RealTimeSTT 0.3.93__tar.gz → 0.3.94__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {realtimestt-0.3.93 → realtimestt-0.3.94}/PKG-INFO +4 -4
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT/audio_input.py +81 -14
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT/audio_recorder.py +101 -22
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/PKG-INFO +4 -4
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/requires.txt +3 -3
- {realtimestt-0.3.93 → realtimestt-0.3.94}/setup.py +1 -1
- {realtimestt-0.3.93 → realtimestt-0.3.94}/LICENSE +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/README.md +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT/__init__.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT/audio_recorder_client.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/SOURCES.txt +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/dependency_links.txt +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/entry_points.txt +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT.egg-info/top_level.txt +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT_server/__init__.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT_server/install_packages.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT_server/stt_cli_client.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/RealtimeSTT_server/stt_server.py +0 -0
- {realtimestt-0.3.93 → realtimestt-0.3.94}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: RealtimeSTT
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.94
|
|
4
4
|
Summary: A fast Voice Activity Detection and Transcription System
|
|
5
5
|
Home-page: https://github.com/KoljaB/RealTimeSTT
|
|
6
6
|
Author: Kolja Beigel
|
|
@@ -13,15 +13,15 @@ Requires-Python: >=3.6
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: PyAudio==0.2.14
|
|
16
|
-
Requires-Dist: faster-whisper==1.1.
|
|
16
|
+
Requires-Dist: faster-whisper==1.1.1
|
|
17
17
|
Requires-Dist: pvporcupine==1.9.5
|
|
18
18
|
Requires-Dist: webrtcvad-wheels==2.0.14
|
|
19
19
|
Requires-Dist: halo==0.0.31
|
|
20
20
|
Requires-Dist: torch
|
|
21
21
|
Requires-Dist: torchaudio
|
|
22
|
-
Requires-Dist: scipy==1.
|
|
22
|
+
Requires-Dist: scipy==1.15.1
|
|
23
23
|
Requires-Dist: openwakeword>=0.4.0
|
|
24
|
-
Requires-Dist: websockets==14.
|
|
24
|
+
Requires-Dist: websockets==14.2
|
|
25
25
|
Requires-Dist: websocket-client==1.8.0
|
|
26
26
|
Requires-Dist: numpy<2.0.0
|
|
27
27
|
|
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
from colorama import init, Fore, Style
|
|
2
|
+
from scipy.signal import butter, filtfilt, resample_poly
|
|
2
3
|
import pyaudio
|
|
3
4
|
import logging
|
|
4
|
-
import time
|
|
5
5
|
|
|
6
|
+
DESIRED_RATE = 16000
|
|
7
|
+
CHUNK_SIZE = 1024
|
|
8
|
+
AUDIO_FORMAT = pyaudio.paInt16
|
|
9
|
+
CHANNELS = 1
|
|
6
10
|
|
|
7
11
|
class AudioInput:
|
|
8
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
input_device_index: int = None,
|
|
15
|
+
debug_mode: bool = False,
|
|
16
|
+
target_samplerate: int = DESIRED_RATE,
|
|
17
|
+
chunk_size: int = CHUNK_SIZE,
|
|
18
|
+
audio_format: int = AUDIO_FORMAT,
|
|
19
|
+
channels: int = CHANNELS,
|
|
20
|
+
resample_to_target: bool = True,
|
|
21
|
+
):
|
|
22
|
+
|
|
9
23
|
self.input_device_index = input_device_index
|
|
10
24
|
self.debug_mode = debug_mode
|
|
11
25
|
self.audio_interface = None
|
|
12
26
|
self.stream = None
|
|
13
27
|
self.device_sample_rate = None
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.
|
|
17
|
-
self.
|
|
18
|
-
self.
|
|
19
|
-
self.DESIRED_RATE = 16000
|
|
28
|
+
self.target_samplerate = target_samplerate
|
|
29
|
+
self.chunk_size = chunk_size
|
|
30
|
+
self.audio_format = audio_format
|
|
31
|
+
self.channels = channels
|
|
32
|
+
self.resample_to_target = resample_to_target
|
|
20
33
|
|
|
21
34
|
def get_supported_sample_rates(self, device_index):
|
|
22
35
|
"""Test which standard sample rates are supported by the specified device."""
|
|
@@ -31,8 +44,8 @@ class AudioInput:
|
|
|
31
44
|
if self.audio_interface.is_format_supported(
|
|
32
45
|
rate,
|
|
33
46
|
input_device=device_index, # Changed to input_device
|
|
34
|
-
input_channels=max_channels, # Changed to input_channels
|
|
35
|
-
input_format=self.
|
|
47
|
+
input_channels=max_channels, # Changed to input_channels
|
|
48
|
+
input_format=self.audio_format, # Changed to input_format
|
|
36
49
|
):
|
|
37
50
|
supported_rates.append(rate)
|
|
38
51
|
except:
|
|
@@ -108,18 +121,18 @@ class AudioInput:
|
|
|
108
121
|
if self.debug_mode:
|
|
109
122
|
print(f"Actual selected device index: {actual_device_index}")
|
|
110
123
|
self.input_device_index = actual_device_index
|
|
111
|
-
self.device_sample_rate = self._get_best_sample_rate(actual_device_index, self.
|
|
124
|
+
self.device_sample_rate = self._get_best_sample_rate(actual_device_index, self.target_samplerate)
|
|
112
125
|
|
|
113
126
|
if self.debug_mode:
|
|
114
127
|
print(f"Setting up audio on device {self.input_device_index} with sample rate {self.device_sample_rate}")
|
|
115
128
|
|
|
116
129
|
try:
|
|
117
130
|
self.stream = self.audio_interface.open(
|
|
118
|
-
format=self.
|
|
119
|
-
channels=self.
|
|
131
|
+
format=self.audio_format,
|
|
132
|
+
channels=self.channels,
|
|
120
133
|
rate=self.device_sample_rate,
|
|
121
134
|
input=True,
|
|
122
|
-
frames_per_buffer=self.
|
|
135
|
+
frames_per_buffer=self.chunk_size,
|
|
123
136
|
input_device_index=self.input_device_index,
|
|
124
137
|
)
|
|
125
138
|
if self.debug_mode:
|
|
@@ -135,6 +148,60 @@ class AudioInput:
|
|
|
135
148
|
self.audio_interface.terminate()
|
|
136
149
|
return False
|
|
137
150
|
|
|
151
|
+
def lowpass_filter(self, signal, cutoff_freq, sample_rate):
|
|
152
|
+
"""
|
|
153
|
+
Apply a low-pass Butterworth filter to prevent aliasing in the signal.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
signal (np.ndarray): Input audio signal to filter
|
|
157
|
+
cutoff_freq (float): Cutoff frequency in Hz
|
|
158
|
+
sample_rate (float): Sampling rate of the input signal in Hz
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
np.ndarray: Filtered audio signal
|
|
162
|
+
|
|
163
|
+
Notes:
|
|
164
|
+
- Uses a 5th order Butterworth filter
|
|
165
|
+
- Applies zero-phase filtering using filtfilt
|
|
166
|
+
"""
|
|
167
|
+
# Calculate the Nyquist frequency (half the sample rate)
|
|
168
|
+
nyquist_rate = sample_rate / 2.0
|
|
169
|
+
|
|
170
|
+
# Normalize cutoff frequency to Nyquist rate (required by butter())
|
|
171
|
+
normal_cutoff = cutoff_freq / nyquist_rate
|
|
172
|
+
|
|
173
|
+
# Design the Butterworth filter
|
|
174
|
+
b, a = butter(5, normal_cutoff, btype='low', analog=False)
|
|
175
|
+
|
|
176
|
+
# Apply zero-phase filtering (forward and backward)
|
|
177
|
+
filtered_signal = filtfilt(b, a, signal)
|
|
178
|
+
return filtered_signal
|
|
179
|
+
|
|
180
|
+
def resample_audio(self, pcm_data, target_sample_rate, original_sample_rate):
|
|
181
|
+
"""
|
|
182
|
+
Filter and resample audio data to a target sample rate.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
pcm_data (np.ndarray): Input audio data
|
|
186
|
+
target_sample_rate (int): Desired output sample rate in Hz
|
|
187
|
+
original_sample_rate (int): Original sample rate of input in Hz
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
np.ndarray: Resampled audio data
|
|
191
|
+
|
|
192
|
+
Notes:
|
|
193
|
+
- Applies anti-aliasing filter before resampling
|
|
194
|
+
- Uses polyphase filtering for high-quality resampling
|
|
195
|
+
"""
|
|
196
|
+
if target_sample_rate < original_sample_rate:
|
|
197
|
+
# Downsampling with low-pass filter
|
|
198
|
+
pcm_filtered = self.lowpass_filter(pcm_data, target_sample_rate / 2, original_sample_rate)
|
|
199
|
+
resampled = resample_poly(pcm_filtered, target_sample_rate, original_sample_rate)
|
|
200
|
+
else:
|
|
201
|
+
# Upsampling without low-pass filter
|
|
202
|
+
resampled = resample_poly(pcm_data, target_sample_rate, original_sample_rate)
|
|
203
|
+
return resampled
|
|
204
|
+
|
|
138
205
|
def read_chunk(self):
|
|
139
206
|
"""Read a chunk of audio data"""
|
|
140
207
|
return self.stream.read(self.CHUNK, exception_on_overflow=False)
|
|
@@ -120,7 +120,7 @@ class TranscriptionWorker:
|
|
|
120
120
|
data = self.conn.recv()
|
|
121
121
|
self.queue.put(data)
|
|
122
122
|
except Exception as e:
|
|
123
|
-
logging.error(f"Error receiving data from connection: {e}")
|
|
123
|
+
logging.error(f"Error receiving data from connection: {e}", exc_info=True)
|
|
124
124
|
else:
|
|
125
125
|
time.sleep(TIME_SLEEP)
|
|
126
126
|
|
|
@@ -162,6 +162,7 @@ class TranscriptionWorker:
|
|
|
162
162
|
try:
|
|
163
163
|
audio, language = self.queue.get(timeout=0.1)
|
|
164
164
|
try:
|
|
165
|
+
logging.debug(f"Transcribing audio with language {language}")
|
|
165
166
|
if self.batch_size > 0:
|
|
166
167
|
segments, info = model.transcribe(
|
|
167
168
|
audio,
|
|
@@ -184,7 +185,7 @@ class TranscriptionWorker:
|
|
|
184
185
|
logging.debug(f"Final text detected with main model: {transcription}")
|
|
185
186
|
self.conn.send(('success', (transcription, info)))
|
|
186
187
|
except Exception as e:
|
|
187
|
-
logging.error(f"General error in transcription: {e}")
|
|
188
|
+
logging.error(f"General error in transcription: {e}", exc_info=True)
|
|
188
189
|
self.conn.send(('error', str(e)))
|
|
189
190
|
except queue.Empty:
|
|
190
191
|
continue
|
|
@@ -193,7 +194,7 @@ class TranscriptionWorker:
|
|
|
193
194
|
logging.debug("Transcription worker process finished due to KeyboardInterrupt")
|
|
194
195
|
break
|
|
195
196
|
except Exception as e:
|
|
196
|
-
logging.error(f"General error in processing queue item: {e}")
|
|
197
|
+
logging.error(f"General error in processing queue item: {e}", exc_info=True)
|
|
197
198
|
finally:
|
|
198
199
|
__builtins__['print'] = print # Restore the original print function
|
|
199
200
|
self.conn.close()
|
|
@@ -567,6 +568,8 @@ class AudioToTextRecorder:
|
|
|
567
568
|
self.sample_rate = sample_rate
|
|
568
569
|
self.recording_start_time = 0
|
|
569
570
|
self.recording_stop_time = 0
|
|
571
|
+
self.last_recording_start_time = 0
|
|
572
|
+
self.last_recording_stop_time = 0
|
|
570
573
|
self.wake_word_detect_time = 0
|
|
571
574
|
self.silero_check_time = 0
|
|
572
575
|
self.silero_working = False
|
|
@@ -590,6 +593,8 @@ class AudioToTextRecorder:
|
|
|
590
593
|
self.stream = None
|
|
591
594
|
self.start_recording_event = threading.Event()
|
|
592
595
|
self.stop_recording_event = threading.Event()
|
|
596
|
+
self.backdate_stop_seconds = 0.0
|
|
597
|
+
self.backdate_resume_seconds = 0.0
|
|
593
598
|
self.last_transcription_bytes = None
|
|
594
599
|
self.last_transcription_bytes_b64 = None
|
|
595
600
|
self.initial_prompt = initial_prompt
|
|
@@ -860,6 +865,7 @@ class AudioToTextRecorder:
|
|
|
860
865
|
0.3)
|
|
861
866
|
)
|
|
862
867
|
self.frames = []
|
|
868
|
+
self.last_frames = []
|
|
863
869
|
|
|
864
870
|
# Recording control flags
|
|
865
871
|
self.is_recording = False
|
|
@@ -925,7 +931,7 @@ class AudioToTextRecorder:
|
|
|
925
931
|
logging.info("KeyboardInterrupt in read from stdout detected, exiting...")
|
|
926
932
|
break
|
|
927
933
|
except Exception as e:
|
|
928
|
-
logging.error(f"Unexpected error in read from stdout: {e}")
|
|
934
|
+
logging.error(f"Unexpected error in read from stdout: {e}", exc_info=True)
|
|
929
935
|
logging.error(traceback.format_exc()) # Log the full traceback here
|
|
930
936
|
break
|
|
931
937
|
time.sleep(0.1)
|
|
@@ -1074,7 +1080,7 @@ class AudioToTextRecorder:
|
|
|
1074
1080
|
return stream
|
|
1075
1081
|
|
|
1076
1082
|
except Exception as e:
|
|
1077
|
-
logging.error(f"Microphone connection failed: {e}. Retrying...")
|
|
1083
|
+
logging.error(f"Microphone connection failed: {e}. Retrying...", exc_info=True)
|
|
1078
1084
|
input_device_index = None
|
|
1079
1085
|
time.sleep(3) # Wait before retrying
|
|
1080
1086
|
continue
|
|
@@ -1190,7 +1196,7 @@ class AudioToTextRecorder:
|
|
|
1190
1196
|
if e.errno == pyaudio.paInputOverflowed:
|
|
1191
1197
|
logging.warning("Input overflowed. Frame dropped.")
|
|
1192
1198
|
else:
|
|
1193
|
-
logging.error(f"OSError during recording: {e}")
|
|
1199
|
+
logging.error(f"OSError during recording: {e}", exc_info=True)
|
|
1194
1200
|
# Attempt to reinitialize the stream
|
|
1195
1201
|
logging.error("Attempting to reinitialize the audio stream...")
|
|
1196
1202
|
|
|
@@ -1308,13 +1314,60 @@ class AudioToTextRecorder:
|
|
|
1308
1314
|
if (self.stop_recording_event.wait(timeout=0.02)):
|
|
1309
1315
|
break
|
|
1310
1316
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1317
|
+
frames = self.frames
|
|
1318
|
+
if len(frames) == 0:
|
|
1319
|
+
frames = self.last_frames
|
|
1320
|
+
|
|
1321
|
+
# Calculate samples needed for backdating resume
|
|
1322
|
+
samples_to_keep = int(self.sample_rate * self.backdate_resume_seconds)
|
|
1323
|
+
|
|
1324
|
+
# First convert all current frames to audio array
|
|
1325
|
+
full_audio_array = np.frombuffer(b''.join(frames), dtype=np.int16)
|
|
1326
|
+
full_audio = full_audio_array.astype(np.float32) / INT16_MAX_ABS_VALUE
|
|
1327
|
+
|
|
1328
|
+
# Calculate how many samples we need to keep for backdating resume
|
|
1329
|
+
if samples_to_keep > 0:
|
|
1330
|
+
samples_to_keep = min(samples_to_keep, len(full_audio))
|
|
1331
|
+
# Keep the last N samples for backdating resume
|
|
1332
|
+
frames_to_read_audio = full_audio[-samples_to_keep:]
|
|
1333
|
+
|
|
1334
|
+
# Convert the audio back to int16 bytes for frames
|
|
1335
|
+
frames_to_read_int16 = (frames_to_read_audio * INT16_MAX_ABS_VALUE).astype(np.int16)
|
|
1336
|
+
frame_bytes = frames_to_read_int16.tobytes()
|
|
1337
|
+
|
|
1338
|
+
# Split into appropriate frame sizes (assuming standard frame size)
|
|
1339
|
+
FRAME_SIZE = 2048 # Typical frame size
|
|
1340
|
+
frames_to_read = []
|
|
1341
|
+
for i in range(0, len(frame_bytes), FRAME_SIZE):
|
|
1342
|
+
frame = frame_bytes[i:i + FRAME_SIZE]
|
|
1343
|
+
if frame: # Only add non-empty frames
|
|
1344
|
+
frames_to_read.append(frame)
|
|
1345
|
+
else:
|
|
1346
|
+
frames_to_read = []
|
|
1347
|
+
|
|
1348
|
+
# Process backdate stop seconds
|
|
1349
|
+
samples_to_remove = int(self.sample_rate * self.backdate_stop_seconds)
|
|
1350
|
+
|
|
1351
|
+
if samples_to_remove > 0:
|
|
1352
|
+
if samples_to_remove < len(full_audio):
|
|
1353
|
+
self.audio = full_audio[:-samples_to_remove]
|
|
1354
|
+
logging.debug(f"Removed {samples_to_remove} samples "
|
|
1355
|
+
f"({samples_to_remove/self.sample_rate:.3f}s) from end of audio")
|
|
1356
|
+
else:
|
|
1357
|
+
self.audio = np.array([], dtype=np.float32)
|
|
1358
|
+
logging.debug("Cleared audio (samples_to_remove >= audio length)")
|
|
1359
|
+
else:
|
|
1360
|
+
self.audio = full_audio
|
|
1361
|
+
logging.debug(f"No samples removed, final audio length: {len(self.audio)}")
|
|
1362
|
+
|
|
1314
1363
|
self.frames.clear()
|
|
1364
|
+
self.last_frames.clear()
|
|
1365
|
+
self.frames.extend(frames_to_read)
|
|
1366
|
+
|
|
1367
|
+
# Reset backdating parameters
|
|
1368
|
+
self.backdate_stop_seconds = 0.0
|
|
1369
|
+
self.backdate_resume_seconds = 0.0
|
|
1315
1370
|
|
|
1316
|
-
# Reset recording-related timestamps
|
|
1317
|
-
self.recording_stop_time = 0
|
|
1318
1371
|
self.listen_start = 0
|
|
1319
1372
|
|
|
1320
1373
|
self._set_state("inactive")
|
|
@@ -1388,7 +1441,7 @@ class AudioToTextRecorder:
|
|
|
1388
1441
|
logging.error(f"Transcription error: {result}")
|
|
1389
1442
|
raise Exception(result)
|
|
1390
1443
|
except Exception as e:
|
|
1391
|
-
logging.error(f"Error during transcription: {str(e)}")
|
|
1444
|
+
logging.error(f"Error during transcription: {str(e)}", exc_info=True)
|
|
1392
1445
|
raise e
|
|
1393
1446
|
|
|
1394
1447
|
def _process_wakeword(self, data):
|
|
@@ -1476,7 +1529,16 @@ class AudioToTextRecorder:
|
|
|
1476
1529
|
return self.transcribe()
|
|
1477
1530
|
|
|
1478
1531
|
|
|
1479
|
-
def
|
|
1532
|
+
def format_number(self, num):
|
|
1533
|
+
# Convert the number to a string
|
|
1534
|
+
num_str = f"{num:.10f}" # Ensure precision is sufficient
|
|
1535
|
+
# Split the number into integer and decimal parts
|
|
1536
|
+
integer_part, decimal_part = num_str.split('.')
|
|
1537
|
+
# Take the last two digits of the integer part and the first two digits of the decimal part
|
|
1538
|
+
result = f"{integer_part[-2:]}.{decimal_part[:2]}"
|
|
1539
|
+
return result
|
|
1540
|
+
|
|
1541
|
+
def start(self, frames = None):
|
|
1480
1542
|
"""
|
|
1481
1543
|
Starts recording audio directly without waiting for voice activity.
|
|
1482
1544
|
"""
|
|
@@ -1498,7 +1560,10 @@ class AudioToTextRecorder:
|
|
|
1498
1560
|
self.wakeword_detected = False
|
|
1499
1561
|
self.wake_word_detect_time = 0
|
|
1500
1562
|
self.frames = []
|
|
1563
|
+
if frames:
|
|
1564
|
+
self.frames = frames
|
|
1501
1565
|
self.is_recording = True
|
|
1566
|
+
|
|
1502
1567
|
self.recording_start_time = time.time()
|
|
1503
1568
|
self.is_silero_speech_active = False
|
|
1504
1569
|
self.is_webrtc_speech_active = False
|
|
@@ -1510,9 +1575,19 @@ class AudioToTextRecorder:
|
|
|
1510
1575
|
|
|
1511
1576
|
return self
|
|
1512
1577
|
|
|
1513
|
-
def stop(self
|
|
1578
|
+
def stop(self,
|
|
1579
|
+
backdate_stop_seconds: float = 0.0,
|
|
1580
|
+
backdate_resume_seconds: float = 0.0,
|
|
1581
|
+
):
|
|
1514
1582
|
"""
|
|
1515
1583
|
Stops recording audio.
|
|
1584
|
+
|
|
1585
|
+
Args:
|
|
1586
|
+
- backdate_stop_seconds (float, default="0.0"): Specifies the number of
|
|
1587
|
+
seconds to backdate the stop time. This is useful when the stop
|
|
1588
|
+
command is issued after the actual stop time.
|
|
1589
|
+
- backdate_resume_seconds (float, default="0.0"): Specifies the number
|
|
1590
|
+
of seconds to backdate the time relistening is initiated.
|
|
1516
1591
|
"""
|
|
1517
1592
|
|
|
1518
1593
|
# Ensure there's a minimum interval
|
|
@@ -1525,6 +1600,9 @@ class AudioToTextRecorder:
|
|
|
1525
1600
|
return self
|
|
1526
1601
|
|
|
1527
1602
|
logging.info("recording stopped")
|
|
1603
|
+
self.last_frames = copy.deepcopy(self.frames)
|
|
1604
|
+
self.backdate_stop_seconds = backdate_stop_seconds
|
|
1605
|
+
self.backdate_resume_seconds = backdate_resume_seconds
|
|
1528
1606
|
self.is_recording = False
|
|
1529
1607
|
self.recording_stop_time = time.time()
|
|
1530
1608
|
self.is_silero_speech_active = False
|
|
@@ -1533,6 +1611,9 @@ class AudioToTextRecorder:
|
|
|
1533
1611
|
self.start_recording_event.clear()
|
|
1534
1612
|
self.stop_recording_event.set()
|
|
1535
1613
|
|
|
1614
|
+
self.last_recording_start_time = self.recording_start_time
|
|
1615
|
+
self.last_recording_stop_time = self.recording_stop_time
|
|
1616
|
+
|
|
1536
1617
|
if self.on_recording_stop:
|
|
1537
1618
|
self.on_recording_stop()
|
|
1538
1619
|
|
|
@@ -1607,7 +1688,6 @@ class AudioToTextRecorder:
|
|
|
1607
1688
|
return
|
|
1608
1689
|
|
|
1609
1690
|
print("\033[91mRealtimeSTT shutting down\033[0m")
|
|
1610
|
-
# logging.debug("RealtimeSTT shutting down")
|
|
1611
1691
|
|
|
1612
1692
|
# Force wait_audio() and text() to exit
|
|
1613
1693
|
self.is_shut_down = True
|
|
@@ -1736,7 +1816,7 @@ class AudioToTextRecorder:
|
|
|
1736
1816
|
data = self.audio_queue.get()
|
|
1737
1817
|
|
|
1738
1818
|
except BrokenPipeError:
|
|
1739
|
-
logging.error("BrokenPipeError _recording_worker")
|
|
1819
|
+
logging.error("BrokenPipeError _recording_worker", exc_info=True)
|
|
1740
1820
|
self.is_running = False
|
|
1741
1821
|
break
|
|
1742
1822
|
|
|
@@ -1813,11 +1893,11 @@ class AudioToTextRecorder:
|
|
|
1813
1893
|
|
|
1814
1894
|
except struct.error:
|
|
1815
1895
|
logging.error("Error unpacking audio data "
|
|
1816
|
-
"for wake word processing.")
|
|
1896
|
+
"for wake word processing.", exc_info=True)
|
|
1817
1897
|
continue
|
|
1818
1898
|
|
|
1819
1899
|
except Exception as e:
|
|
1820
|
-
logging.error(f"Wake word processing error: {e}")
|
|
1900
|
+
logging.error(f"Wake word processing error: {e}", exc_info=True)
|
|
1821
1901
|
continue
|
|
1822
1902
|
|
|
1823
1903
|
if self.use_extended_logging:
|
|
@@ -2044,7 +2124,7 @@ class AudioToTextRecorder:
|
|
|
2044
2124
|
except Exception as e:
|
|
2045
2125
|
logging.debug('Debug: Caught exception in main try block')
|
|
2046
2126
|
if not self.interrupt_stop_event.is_set():
|
|
2047
|
-
logging.error(f"Unhandled exeption in _recording_worker: {e}")
|
|
2127
|
+
logging.error(f"Unhandled exeption in _recording_worker: {e}", exc_info=True)
|
|
2048
2128
|
raise
|
|
2049
2129
|
|
|
2050
2130
|
if self.use_extended_logging:
|
|
@@ -2111,7 +2191,7 @@ class AudioToTextRecorder:
|
|
|
2111
2191
|
logging.warning("Realtime transcription timed out")
|
|
2112
2192
|
continue
|
|
2113
2193
|
except Exception as e:
|
|
2114
|
-
logging.error(f"Error in realtime transcription: {str(e)}")
|
|
2194
|
+
logging.error(f"Error in realtime transcription: {str(e)}", exc_info=True)
|
|
2115
2195
|
continue
|
|
2116
2196
|
else:
|
|
2117
2197
|
# Perform transcription and assemble the text
|
|
@@ -2145,7 +2225,6 @@ class AudioToTextRecorder:
|
|
|
2145
2225
|
if self.is_recording and time.time() - \
|
|
2146
2226
|
self.recording_start_time > self.init_realtime_after_seconds:
|
|
2147
2227
|
|
|
2148
|
-
# logging.debug('Starting realtime transcription')
|
|
2149
2228
|
self.realtime_transcription_text = realtime_text
|
|
2150
2229
|
self.realtime_transcription_text = \
|
|
2151
2230
|
self.realtime_transcription_text.strip()
|
|
@@ -2225,7 +2304,7 @@ class AudioToTextRecorder:
|
|
|
2225
2304
|
time.sleep(TIME_SLEEP)
|
|
2226
2305
|
|
|
2227
2306
|
except Exception as e:
|
|
2228
|
-
logging.error(f"Unhandled exeption in _realtime_worker: {e}")
|
|
2307
|
+
logging.error(f"Unhandled exeption in _realtime_worker: {e}", exc_info=True)
|
|
2229
2308
|
raise
|
|
2230
2309
|
|
|
2231
2310
|
def _is_silero_speech(self, chunk):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: RealtimeSTT
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.94
|
|
4
4
|
Summary: A fast Voice Activity Detection and Transcription System
|
|
5
5
|
Home-page: https://github.com/KoljaB/RealTimeSTT
|
|
6
6
|
Author: Kolja Beigel
|
|
@@ -13,15 +13,15 @@ Requires-Python: >=3.6
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: PyAudio==0.2.14
|
|
16
|
-
Requires-Dist: faster-whisper==1.1.
|
|
16
|
+
Requires-Dist: faster-whisper==1.1.1
|
|
17
17
|
Requires-Dist: pvporcupine==1.9.5
|
|
18
18
|
Requires-Dist: webrtcvad-wheels==2.0.14
|
|
19
19
|
Requires-Dist: halo==0.0.31
|
|
20
20
|
Requires-Dist: torch
|
|
21
21
|
Requires-Dist: torchaudio
|
|
22
|
-
Requires-Dist: scipy==1.
|
|
22
|
+
Requires-Dist: scipy==1.15.1
|
|
23
23
|
Requires-Dist: openwakeword>=0.4.0
|
|
24
|
-
Requires-Dist: websockets==14.
|
|
24
|
+
Requires-Dist: websockets==14.2
|
|
25
25
|
Requires-Dist: websocket-client==1.8.0
|
|
26
26
|
Requires-Dist: numpy<2.0.0
|
|
27
27
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
PyAudio==0.2.14
|
|
2
|
-
faster-whisper==1.1.
|
|
2
|
+
faster-whisper==1.1.1
|
|
3
3
|
pvporcupine==1.9.5
|
|
4
4
|
webrtcvad-wheels==2.0.14
|
|
5
5
|
halo==0.0.31
|
|
6
6
|
torch
|
|
7
7
|
torchaudio
|
|
8
|
-
scipy==1.
|
|
8
|
+
scipy==1.15.1
|
|
9
9
|
openwakeword>=0.4.0
|
|
10
|
-
websockets==14.
|
|
10
|
+
websockets==14.2
|
|
11
11
|
websocket-client==1.8.0
|
|
12
12
|
numpy<2.0.0
|
|
@@ -9,7 +9,7 @@ with open('requirements.txt') as f:
|
|
|
9
9
|
|
|
10
10
|
setuptools.setup(
|
|
11
11
|
name="RealtimeSTT",
|
|
12
|
-
version="0.3.
|
|
12
|
+
version="0.3.94",
|
|
13
13
|
author="Kolja Beigel",
|
|
14
14
|
author_email="kolja.beigel@web.de",
|
|
15
15
|
description="A fast Voice Activity Detection and Transcription System",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|