RealTimeSTT 0.1.6__tar.gz → 0.1.7__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.1.6 → RealTimeSTT-0.1.7}/PKG-INFO +10 -5
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/README.md +7 -2
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealTimeSTT.egg-info/PKG-INFO +10 -5
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealTimeSTT.egg-info/requires.txt +2 -2
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealtimeSTT/audio_recorder.py +59 -38
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/setup.py +1 -1
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealTimeSTT.egg-info/SOURCES.txt +0 -0
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealTimeSTT.egg-info/dependency_links.txt +0 -0
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealTimeSTT.egg-info/top_level.txt +0 -0
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/RealtimeSTT/__init__.py +0 -0
- {RealTimeSTT-0.1.6 → RealTimeSTT-0.1.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: RealTimeSTT
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -16,8 +16,8 @@ Requires-Dist: faster-whisper>=0.7.1
|
|
|
16
16
|
Requires-Dist: pvporcupine==1.9.5
|
|
17
17
|
Requires-Dist: webrtcvad>=2.0.10
|
|
18
18
|
Requires-Dist: halo>=0.0.31
|
|
19
|
-
Requires-Dist: torch
|
|
20
|
-
Requires-Dist: torchaudio
|
|
19
|
+
Requires-Dist: torch>=2.0.1
|
|
20
|
+
Requires-Dist: torchaudio>=2.0.2
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
# RealtimeSTT
|
|
@@ -37,9 +37,14 @@ https://github.com/KoljaB/RealtimeSTT/assets/7604638/207cb9a2-4482-48e7-9d2b-072
|
|
|
37
37
|
|
|
38
38
|
### Updates
|
|
39
39
|
|
|
40
|
+
#### v0.1.7
|
|
41
|
+
- Bugfix for Mac OS Installation (multiprocessing / queue.size())
|
|
42
|
+
- KeyboardInterrupt handling (now abortable with CTRL+C)
|
|
43
|
+
- Bugfix for spinner handling (could lead to exception in some cases)
|
|
44
|
+
|
|
40
45
|
#### v0.1.6
|
|
41
|
-
-
|
|
42
|
-
-
|
|
46
|
+
- Implements context manager protocol (recorder can be used in a `with` statement)
|
|
47
|
+
- Bugfix for resource management in shutdown method
|
|
43
48
|
|
|
44
49
|
#### v0.1.5
|
|
45
50
|
|
|
@@ -16,9 +16,14 @@ https://github.com/KoljaB/RealtimeSTT/assets/7604638/207cb9a2-4482-48e7-9d2b-072
|
|
|
16
16
|
|
|
17
17
|
### Updates
|
|
18
18
|
|
|
19
|
+
#### v0.1.7
|
|
20
|
+
- Bugfix for Mac OS Installation (multiprocessing / queue.size())
|
|
21
|
+
- KeyboardInterrupt handling (now abortable with CTRL+C)
|
|
22
|
+
- Bugfix for spinner handling (could lead to exception in some cases)
|
|
23
|
+
|
|
19
24
|
#### v0.1.6
|
|
20
|
-
-
|
|
21
|
-
-
|
|
25
|
+
- Implements context manager protocol (recorder can be used in a `with` statement)
|
|
26
|
+
- Bugfix for resource management in shutdown method
|
|
22
27
|
|
|
23
28
|
#### v0.1.5
|
|
24
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: RealTimeSTT
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -16,8 +16,8 @@ Requires-Dist: faster-whisper>=0.7.1
|
|
|
16
16
|
Requires-Dist: pvporcupine==1.9.5
|
|
17
17
|
Requires-Dist: webrtcvad>=2.0.10
|
|
18
18
|
Requires-Dist: halo>=0.0.31
|
|
19
|
-
Requires-Dist: torch
|
|
20
|
-
Requires-Dist: torchaudio
|
|
19
|
+
Requires-Dist: torch>=2.0.1
|
|
20
|
+
Requires-Dist: torchaudio>=2.0.2
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
# RealtimeSTT
|
|
@@ -37,9 +37,14 @@ https://github.com/KoljaB/RealtimeSTT/assets/7604638/207cb9a2-4482-48e7-9d2b-072
|
|
|
37
37
|
|
|
38
38
|
### Updates
|
|
39
39
|
|
|
40
|
+
#### v0.1.7
|
|
41
|
+
- Bugfix for Mac OS Installation (multiprocessing / queue.size())
|
|
42
|
+
- KeyboardInterrupt handling (now abortable with CTRL+C)
|
|
43
|
+
- Bugfix for spinner handling (could lead to exception in some cases)
|
|
44
|
+
|
|
40
45
|
#### v0.1.6
|
|
41
|
-
-
|
|
42
|
-
-
|
|
46
|
+
- Implements context manager protocol (recorder can be used in a `with` statement)
|
|
47
|
+
- Bugfix for resource management in shutdown method
|
|
43
48
|
|
|
44
49
|
#### v0.1.5
|
|
45
50
|
|
|
@@ -20,7 +20,7 @@ Author: Kolja Beigel
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
from multiprocessing import Process, Pipe,
|
|
23
|
+
from multiprocessing import Process, Pipe, Event, Manager
|
|
24
24
|
import faster_whisper
|
|
25
25
|
import collections
|
|
26
26
|
import numpy as np
|
|
@@ -172,7 +172,8 @@ class AudioToTextRecorder:
|
|
|
172
172
|
self.allowed_latency_limit = ALLOWED_LATENCY_LIMIT
|
|
173
173
|
|
|
174
174
|
self.level = level
|
|
175
|
-
|
|
175
|
+
manager = Manager()
|
|
176
|
+
self.audio_queue = manager.Queue()
|
|
176
177
|
self.buffer_size = BUFFER_SIZE
|
|
177
178
|
self.sample_rate = SAMPLE_RATE
|
|
178
179
|
self.recording_start_time = 0
|
|
@@ -228,13 +229,14 @@ class AudioToTextRecorder:
|
|
|
228
229
|
logging.info(f"Starting RealTimeSTT")
|
|
229
230
|
|
|
230
231
|
# Start transcription process
|
|
232
|
+
self.interrupt_stop_event = Event()
|
|
231
233
|
self.main_transcription_ready_event = Event()
|
|
232
234
|
self.parent_transcription_pipe, child_transcription_pipe = Pipe()
|
|
233
|
-
self.transcript_process = Process(target=AudioToTextRecorder._transcription_worker, args=(child_transcription_pipe, model, self.main_transcription_ready_event, self.shutdown_event))
|
|
235
|
+
self.transcript_process = Process(target=AudioToTextRecorder._transcription_worker, args=(child_transcription_pipe, model, self.main_transcription_ready_event, self.shutdown_event, self.interrupt_stop_event))
|
|
234
236
|
self.transcript_process.start()
|
|
235
237
|
|
|
236
238
|
# Start audio data reading process
|
|
237
|
-
self.reader_process = Process(target=AudioToTextRecorder._audio_data_worker, args=(self.audio_queue, self.sample_rate, self.buffer_size, self.shutdown_event))
|
|
239
|
+
self.reader_process = Process(target=AudioToTextRecorder._audio_data_worker, args=(self.audio_queue, self.sample_rate, self.buffer_size, self.shutdown_event, self.interrupt_stop_event))
|
|
238
240
|
self.reader_process.start()
|
|
239
241
|
|
|
240
242
|
# Initialize the realtime transcription model
|
|
@@ -325,7 +327,7 @@ class AudioToTextRecorder:
|
|
|
325
327
|
|
|
326
328
|
|
|
327
329
|
@staticmethod
|
|
328
|
-
def _transcription_worker(conn, model_path, ready_event, shutdown_event):
|
|
330
|
+
def _transcription_worker(conn, model_path, ready_event, shutdown_event, interrupt_stop_event):
|
|
329
331
|
"""
|
|
330
332
|
Worker method that handles the continuous process of transcribing audio data.
|
|
331
333
|
|
|
@@ -362,25 +364,30 @@ class AudioToTextRecorder:
|
|
|
362
364
|
logging.debug('Faster_whisper main speech to text transcription model initialized successfully')
|
|
363
365
|
|
|
364
366
|
while not shutdown_event.is_set():
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
367
|
+
try:
|
|
368
|
+
if conn.poll(0.5):
|
|
369
|
+
audio, language = conn.recv()
|
|
370
|
+
try:
|
|
371
|
+
segments = model.transcribe(audio, language=language if language else None)[0]
|
|
372
|
+
transcription = " ".join(seg.text for seg in segments).strip()
|
|
373
|
+
conn.send(('success', transcription))
|
|
374
|
+
except faster_whisper.WhisperError as e:
|
|
375
|
+
logging.error(f"Whisper transcription error: {e}")
|
|
376
|
+
conn.send(('error', str(e)))
|
|
377
|
+
except Exception as e:
|
|
378
|
+
logging.error(f"General transcription error: {e}")
|
|
379
|
+
conn.send(('error', str(e)))
|
|
380
|
+
else:
|
|
381
|
+
# If there's no data, sleep for a short while to prevent busy waiting
|
|
382
|
+
time.sleep(0.02)
|
|
383
|
+
except KeyboardInterrupt:
|
|
384
|
+
interrupt_stop_event.set()
|
|
385
|
+
logging.debug('Transcription worker process finished due to KeyboardInterrupt')
|
|
386
|
+
break
|
|
380
387
|
|
|
381
388
|
|
|
382
389
|
@staticmethod
|
|
383
|
-
def _audio_data_worker(audio_queue, sample_rate, buffer_size, shutdown_event):
|
|
390
|
+
def _audio_data_worker(audio_queue, sample_rate, buffer_size, shutdown_event, interrupt_stop_event):
|
|
384
391
|
"""
|
|
385
392
|
Worker method that handles the audio recording process.
|
|
386
393
|
|
|
@@ -433,7 +440,11 @@ class AudioToTextRecorder:
|
|
|
433
440
|
print (f"Error: {e}")
|
|
434
441
|
continue
|
|
435
442
|
|
|
436
|
-
audio_queue.put(data)
|
|
443
|
+
audio_queue.put(data)
|
|
444
|
+
|
|
445
|
+
except KeyboardInterrupt:
|
|
446
|
+
interrupt_stop_event.set()
|
|
447
|
+
logging.debug('Audio data worker process finished due to KeyboardInterrupt')
|
|
437
448
|
finally:
|
|
438
449
|
stream.stop_stream()
|
|
439
450
|
stream.close()
|
|
@@ -463,14 +474,16 @@ class AudioToTextRecorder:
|
|
|
463
474
|
self.start_recording_on_voice_activity = True
|
|
464
475
|
|
|
465
476
|
# Wait until recording starts
|
|
466
|
-
self.
|
|
477
|
+
while not self.interrupt_stop_event.is_set():
|
|
478
|
+
if (self.start_recording_event.wait(timeout=0.5)): break
|
|
467
479
|
|
|
468
480
|
# If recording is ongoing, wait for voice inactivity to finish recording.
|
|
469
481
|
if self.is_recording:
|
|
470
482
|
self.stop_recording_on_voice_deactivity = True
|
|
471
483
|
|
|
472
484
|
# Wait until recording stops
|
|
473
|
-
self.
|
|
485
|
+
while not self.interrupt_stop_event.is_set():
|
|
486
|
+
if (self.stop_recording_event.wait(timeout=0.5)): break
|
|
474
487
|
|
|
475
488
|
# Convert recorded frames to the appropriate audio format.
|
|
476
489
|
audio_array = np.frombuffer(b''.join(self.frames), dtype=np.int16)
|
|
@@ -536,7 +549,7 @@ class AudioToTextRecorder:
|
|
|
536
549
|
|
|
537
550
|
self.wait_audio()
|
|
538
551
|
|
|
539
|
-
if self.is_shut_down:
|
|
552
|
+
if self.is_shut_down or self.interrupt_stop_event.is_set():
|
|
540
553
|
return ""
|
|
541
554
|
|
|
542
555
|
if on_transcription_finished:
|
|
@@ -660,16 +673,23 @@ class AudioToTextRecorder:
|
|
|
660
673
|
# Continuously monitor audio for voice activity
|
|
661
674
|
while self.is_running:
|
|
662
675
|
|
|
663
|
-
|
|
676
|
+
try:
|
|
664
677
|
|
|
665
|
-
# Handle queue overflow
|
|
666
|
-
queue_overflow_logged = False
|
|
667
|
-
while self.audio_queue.qsize() > self.allowed_latency_limit:
|
|
668
|
-
if not queue_overflow_logged:
|
|
669
|
-
logging.warning(f"Audio queue size exceeds latency limit. Current size: {self.audio_queue.qsize()}. Discarding old audio chunks.")
|
|
670
|
-
queue_overflow_logged = True
|
|
671
678
|
data = self.audio_queue.get()
|
|
672
679
|
|
|
680
|
+
# Handle queue overflow
|
|
681
|
+
queue_overflow_logged = False
|
|
682
|
+
while self.audio_queue.qsize() > self.allowed_latency_limit:
|
|
683
|
+
if not queue_overflow_logged:
|
|
684
|
+
logging.warning(f"Audio queue size exceeds latency limit. Current size: {self.audio_queue.qsize()}. Discarding old audio chunks.")
|
|
685
|
+
queue_overflow_logged = True
|
|
686
|
+
data = self.audio_queue.get()
|
|
687
|
+
|
|
688
|
+
except BrokenPipeError:
|
|
689
|
+
print ("BrokenPipeError _recording_worker")
|
|
690
|
+
self.is_running = False
|
|
691
|
+
break
|
|
692
|
+
|
|
673
693
|
if not self.is_recording:
|
|
674
694
|
# Handle not recording state
|
|
675
695
|
|
|
@@ -790,8 +810,9 @@ class AudioToTextRecorder:
|
|
|
790
810
|
|
|
791
811
|
|
|
792
812
|
except Exception as e:
|
|
793
|
-
|
|
794
|
-
|
|
813
|
+
if not self.interrupt_stop_event.is_set():
|
|
814
|
+
logging.error(f"Unhandled exeption in _recording_worker: {e}")
|
|
815
|
+
raise
|
|
795
816
|
|
|
796
817
|
|
|
797
818
|
def _realtime_worker(self):
|
|
@@ -988,23 +1009,23 @@ class AudioToTextRecorder:
|
|
|
988
1009
|
if self.on_vad_detect_start:
|
|
989
1010
|
self.on_vad_detect_start()
|
|
990
1011
|
self._set_spinner("speak now")
|
|
991
|
-
if self.spinner:
|
|
1012
|
+
if self.spinner and self.halo:
|
|
992
1013
|
self.halo._interval = 250
|
|
993
1014
|
elif new_state == "wakeword":
|
|
994
1015
|
if self.on_wakeword_detection_start:
|
|
995
1016
|
self.on_wakeword_detection_start()
|
|
996
1017
|
self._set_spinner(f"say {self.wake_words}")
|
|
997
|
-
if self.spinner:
|
|
1018
|
+
if self.spinner and self.halo:
|
|
998
1019
|
self.halo._interval = 500
|
|
999
1020
|
elif new_state == "transcribing":
|
|
1000
1021
|
if self.on_transcription_start:
|
|
1001
1022
|
self.on_transcription_start()
|
|
1002
1023
|
self._set_spinner("transcribing")
|
|
1003
|
-
if self.spinner:
|
|
1024
|
+
if self.spinner and self.halo:
|
|
1004
1025
|
self.halo._interval = 50
|
|
1005
1026
|
elif new_state == "recording":
|
|
1006
1027
|
self._set_spinner("recording")
|
|
1007
|
-
if self.spinner:
|
|
1028
|
+
if self.spinner and self.halo:
|
|
1008
1029
|
self.halo._interval = 100
|
|
1009
1030
|
elif new_state == "inactive":
|
|
1010
1031
|
if self.spinner and self.halo:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|