File size: 5,289 Bytes
c127575
32dcdfe
 
990f149
32dcdfe
 
 
 
 
 
 
 
 
990f149
debd261
 
 
 
c127575
32dcdfe
debd261
 
 
 
c127575
32dcdfe
 
 
 
e368e99
 
32dcdfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990f149
32dcdfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990f149
 
c127575
 
 
 
32dcdfe
c127575
 
32dcdfe
c127575
 
32dcdfe
 
 
 
 
 
e368e99
32dcdfe
 
 
c127575
32dcdfe
 
 
 
71c74fb
c127575
32dcdfe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e368e99
32dcdfe
 
e368e99
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import numpy as np
from constants import STREAMER_CLASSES
import gradio as gr
from stt_streamers import DeepgramStreamer
from sdk import SDKWrapper, SDKParams
from ui import LED_DOT_BLACK, LED_DOT_GREEN, LED_DOT_OFF, LED_DOT_RED, LED_DOT_YELLOW

_ENHANCED_TRANSCRIPT = ""
_RAW_TRANSCRIPT = ""

SDK_STREAMING = SDKWrapper()
Streamer_enhanced = None
Streamer_raw = None


def _set_transcript_enhanced(text: str) -> None:
    global _ENHANCED_TRANSCRIPT
    _ENHANCED_TRANSCRIPT = text


def _set_transcript_raw(text: str) -> None:
    global _RAW_TRANSCRIPT
    _RAW_TRANSCRIPT = text


def clear_live_transcripts():
    global _ENHANCED_TRANSCRIPT, _RAW_TRANSCRIPT
    _ENHANCED_TRANSCRIPT = ""
    _RAW_TRANSCRIPT = ""


def render_system_status(status: str):
    if status == "off":
        return LED_DOT_OFF, "Off"
    if status == "init":
        return LED_DOT_YELLOW, "Initializing..."
    if status == "ready":
        return LED_DOT_GREEN, "Ready"
    if status == "error":
        return LED_DOT_RED, "Error. Please refresh the page."
    raise ValueError(f"Invalid status: {status}")

def shutdown_streamers():
    global Streamer_enhanced, Streamer_raw
    try:
        if Streamer_enhanced is not None:
            Streamer_enhanced.shutdown()
        if Streamer_raw is not None:
            Streamer_raw.shutdown()
    except Exception as e:
        print(f"Error shutting down streamers: {e}")
    finally:
        Streamer_enhanced = None
        Streamer_raw = None


def set_stt_streamer(sample_rate: int, stt_model: str):
    global Streamer_enhanced, Streamer_raw
    StreamerCls = STREAMER_CLASSES.get(stt_model, DeepgramStreamer)
    try:
        Streamer_enhanced = StreamerCls(
            fs_hz=sample_rate,
            stream_name="Enhanced",
            on_update=_set_transcript_enhanced,
        )
        Streamer_raw = StreamerCls(
            fs_hz=sample_rate,
            stream_name="Raw",
            on_update=_set_transcript_raw,
        )
    except Exception as e:
        Streamer_enhanced = None
        Streamer_raw = None
        raise RuntimeError(f"Error initializing STT streamer '{stt_model}': {e}")

def _to_float32_mono(y: np.ndarray) -> np.ndarray:
    y = np.asarray(y)
    if y.ndim > 1:
        y = y.mean(axis=1)
    if y.dtype == np.int16:
        y = y.astype(np.float32) / 32768.0
    else:
        y = y.astype(np.float32)
    return np.asarray(y, dtype=np.float32).flatten()


def _ensure_initialized(sr: int, streaming_sr, stt_model: str, enhancement_level: float):
    streamer_cls = STREAMER_CLASSES[stt_model]
    needs_init = (
        streaming_sr is None
        or streaming_sr != sr
        or Streamer_enhanced is None
        or Streamer_raw is None
        or not isinstance(Streamer_enhanced, streamer_cls)
        or not isinstance(Streamer_raw, streamer_cls)
    )

    if not needs_init:
        if SDK_STREAMING.enhancement_level != enhancement_level:
            SDK_STREAMING.change_enhancement_level(enhancement_level)
        return streaming_sr, *render_system_status("ready")

    try:
        shutdown_streamers()
        sdk_params = SDKParams(
            sample_rate=sr,
            enhancement_level=enhancement_level
        )
        SDK_STREAMING.init_processor(sdk_params)
        set_stt_streamer(sr, stt_model)
        return sr, *render_system_status("ready")
    except Exception as e:
        gr.Warning(f"Streaming process failed: {e}")
        return None, *render_system_status("error")


def stream_step(audio_stream, streaming_sr, stt_model, enhancement_level, input_gain_db):
    if audio_stream is None:
        return streaming_sr, *render_system_status("off"), _ENHANCED_TRANSCRIPT, _RAW_TRANSCRIPT, LED_DOT_OFF

    sr, chunk = audio_stream
    if chunk is None:
        return streaming_sr, *render_system_status("off"), _ENHANCED_TRANSCRIPT, _RAW_TRANSCRIPT, LED_DOT_OFF

    enhancement_level_float = enhancement_level / 100.0

    streaming_sr, system_led, system_text = _ensure_initialized(
        sr=sr,
        streaming_sr=streaming_sr,
        stt_model=stt_model,
        enhancement_level=enhancement_level_float,
    )

    try:
        y = _to_float32_mono(chunk)

        if input_gain_db and input_gain_db > 0:
            gain_linear = np.float32(10.0 ** (float(input_gain_db) / 20.0))
            y = np.clip(y * gain_linear, -1.0, 1.0).astype(np.float32)

        enhanced_chunk_16k, vad_detected = SDK_STREAMING.process_with_vad(y)
        enhanced_chunk_16k = np.asarray(enhanced_chunk_16k, dtype=np.float32).flatten()

        Streamer_raw.process_chunk(y)
        Streamer_enhanced.process_chunk(enhanced_chunk_16k)

        vad_led = LED_DOT_GREEN if vad_detected else LED_DOT_BLACK
        return streaming_sr, system_led, system_text, _ENHANCED_TRANSCRIPT, _RAW_TRANSCRIPT, vad_led

    except Exception as e:
        gr.Warning(f"Streaming process failed: {e}")
        err_led, err_text = render_system_status("error")
        return streaming_sr, err_led, err_text, _ENHANCED_TRANSCRIPT, _RAW_TRANSCRIPT, LED_DOT_OFF


def on_start_recording():
    clear_live_transcripts()
    led, text = render_system_status("init")
    return "", "", led, text


def on_stop_recording():
    led, text = render_system_status("off")
    return led, led, text, None