RealTimeSTT 0.3.0__tar.gz → 0.3.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: RealtimeSTT
3
- Version: 0.3.0
3
+ Version: 0.3.1
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
@@ -15,22 +15,30 @@ License-File: LICENSE
15
15
  Requires-Dist: PyAudio==0.2.14
16
16
  Requires-Dist: faster-whisper==1.0.3
17
17
  Requires-Dist: pvporcupine==1.9.5
18
- Requires-Dist: webrtcvad==2.0.10
18
+ Requires-Dist: webrtcvad-wheels==2.0.14
19
19
  Requires-Dist: halo==0.0.31
20
- Requires-Dist: torch==2.3.1
21
- Requires-Dist: torchaudio==2.3.1
22
- Requires-Dist: scipy==1.12.0
20
+ Requires-Dist: torch
21
+ Requires-Dist: torchaudio
22
+ Requires-Dist: scipy==1.14.1
23
23
  Requires-Dist: websockets==v12.0
24
- Requires-Dist: openwakeword==0.6.0
25
-
24
+ Requires-Dist: websocket-client==1.8.0
25
+ Requires-Dist: openwakeword>=0.4.0
26
+ Requires-Dist: numpy<2.0.0
26
27
 
27
28
  # RealtimeSTT
29
+ [![PyPI](https://img.shields.io/pypi/v/RealtimeSTT)](https://pypi.org/project/RealtimeSTT/)
30
+ [![Downloads](https://static.pepy.tech/badge/RealtimeSTT)](https://pepy.tech/project/KoljaB/RealtimeSTT)
31
+ [![GitHub release](https://img.shields.io/github/release/KoljaB/RealtimeSTT.svg)](https://GitHub.com/KoljaB/RealtimeSTT/releases/)
32
+ [![GitHub commits](https://badgen.net/github/commits/KoljaB/RealtimeSTT)](https://GitHub.com/Naereen/KoljaB/RealtimeSTT/commit/)
33
+ [![GitHub forks](https://img.shields.io/github/forks/KoljaB/RealtimeSTT.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/KoljaB/RealtimeSTT/network/)
34
+ [![GitHub stars](https://img.shields.io/github/stars/KoljaB/RealtimeSTT.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/KoljaB/RealtimeSTT/stargazers/)
28
35
 
29
36
  *Easy-to-use, low-latency speech-to-text library for realtime applications*
30
37
 
31
38
  ## New
32
39
 
33
- Custom wake words with [OpenWakeWord](#openwakeword). Thanks to the [developers](https://github.com/dscripka/openWakeWord) of this!
40
+ - AudioToTextRecorderClient class, which automatically starts a server if none is running and connects to it. The class shares the same interface as AudioToTextRecorder, making it easy to upgrade or switch between the two. (Work in progress, most parameters and callbacks of AudioToTextRecorder are already implemented into AudioToTextRecorderClient, but not all. Also the server can not handle concurrent (parallel) requests yet.)
41
+ - reworked CLI interface ("stt-server" to start the server, "stt" to start the client, look at "server" folder for more info)
34
42
 
35
43
  ## About the Project
36
44
 
@@ -47,12 +55,49 @@ https://github.com/user-attachments/assets/797e6552-27cd-41b1-a7f3-e5cbc72094f5
47
55
 
48
56
  ### Updates
49
57
 
50
- Latest Version: v0.3.0
58
+ Latest Version: v0.3.1
51
59
 
52
60
  See [release history](https://github.com/KoljaB/RealtimeSTT/releases).
53
61
 
54
62
  > **Hint:** *Since we use the `multiprocessing` module now, ensure to include the `if __name__ == '__main__':` protection in your code to prevent unexpected behavior, especially on platforms like Windows. For a detailed explanation on why this is important, visit the [official Python documentation on `multiprocessing`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing-programming).*
55
63
 
64
+ ## Quick Examples
65
+
66
+ ### Print everything being said:
67
+
68
+ ```python
69
+ from RealtimeSTT import AudioToTextRecorder
70
+ import pyautogui
71
+
72
+ def process_text(text):
73
+ print(text)
74
+
75
+ if __name__ == '__main__':
76
+ print("Wait until it says 'speak now'")
77
+ recorder = AudioToTextRecorder()
78
+
79
+ while True:
80
+ recorder.text(process_text)
81
+ ```
82
+
83
+ ### Type everything being said:
84
+
85
+ ```python
86
+ from RealtimeSTT import AudioToTextRecorder
87
+ import pyautogui
88
+
89
+ def process_text(text):
90
+ pyautogui.typewrite(text + " ")
91
+
92
+ if __name__ == '__main__':
93
+ print("Wait until it says 'speak now'")
94
+ recorder = AudioToTextRecorder()
95
+
96
+ while True:
97
+ recorder.text(process_text)
98
+ ```
99
+ *Will type everything being said into your selected text box*
100
+
56
101
  ### Features
57
102
 
58
103
  - **Voice Activity Detection**: Automatically detects when you start and stop speaking.
@@ -183,6 +228,19 @@ recorder.stop()
183
228
  print(recorder.text())
184
229
  ```
185
230
 
231
+ #### Standalone Example:
232
+
233
+ ```python
234
+ from RealtimeSTT import AudioToTextRecorder
235
+
236
+ if __name__ == '__main__':
237
+ recorder = AudioToTextRecorder()
238
+ recorder.start()
239
+ input("Press Enter to stop recording...")
240
+ recorder.stop()
241
+ print("Transcription: ", recorder.text())
242
+ ```
243
+
186
244
  ### Automatic Recording
187
245
 
188
246
  Recording based on voice activity detection.
@@ -192,8 +250,19 @@ with AudioToTextRecorder() as recorder:
192
250
  print(recorder.text())
193
251
  ```
194
252
 
253
+ #### Standalone Example:
254
+
255
+ ```python
256
+ from RealtimeSTT import AudioToTextRecorder
257
+
258
+ if __name__ == '__main__':
259
+ with AudioToTextRecorder() as recorder:
260
+ print("Transcription: ", recorder.text())
261
+ ```
262
+
195
263
  When running recorder.text in a loop it is recommended to use a callback, allowing the transcription to be run asynchronously:
196
264
 
265
+
197
266
  ```python
198
267
  def process_text(text):
199
268
  print (text)
@@ -202,6 +271,21 @@ while True:
202
271
  recorder.text(process_text)
203
272
  ```
204
273
 
274
+ #### Standalone Example:
275
+
276
+ ```python
277
+ from RealtimeSTT import AudioToTextRecorder
278
+
279
+ def process_text(text):
280
+ print(text)
281
+
282
+ if __name__ == '__main__':
283
+ recorder = AudioToTextRecorder()
284
+
285
+ while True:
286
+ recorder.text(process_text)
287
+ ```
288
+
205
289
  ### Wakewords
206
290
 
207
291
  Keyword activation before detecting voice. Write the comma-separated list of your desired activation keywords into the wake_words parameter. You can choose wake words from these list: alexa, americano, blueberry, bumblebee, computer, grapefruits, grasshopper, hey google, hey siri, jarvis, ok google, picovoice, porcupine, terminator.
@@ -213,6 +297,18 @@ print('Say "Jarvis" then speak.')
213
297
  print(recorder.text())
214
298
  ```
215
299
 
300
+ #### Standalone Example:
301
+
302
+ ```python
303
+ from RealtimeSTT import AudioToTextRecorder
304
+
305
+ if __name__ == '__main__':
306
+ recorder = AudioToTextRecorder(wake_words="jarvis")
307
+
308
+ print('Say "Jarvis" to start recording.')
309
+ print(recorder.text())
310
+ ```
311
+
216
312
  ### Callbacks
217
313
 
218
314
  You can set callback functions to be executed on different events (see [Configuration](#configuration)) :
@@ -228,6 +324,22 @@ recorder = AudioToTextRecorder(on_recording_start=my_start_callback,
228
324
  on_recording_stop=my_stop_callback)
229
325
  ```
230
326
 
327
+ #### Standalone Example:
328
+
329
+ ```python
330
+ from RealtimeSTT import AudioToTextRecorder
331
+
332
+ def start_callback():
333
+ print("Recording started!")
334
+
335
+ def stop_callback():
336
+ print("Recording stopped!")
337
+
338
+ if __name__ == '__main__':
339
+ recorder = AudioToTextRecorder(on_recording_start=start_callback,
340
+ on_recording_stop=stop_callback)
341
+ ```
342
+
231
343
  ### Feed chunks
232
344
 
233
345
  If you don't want to use the local microphone set use_microphone parameter to false and provide raw PCM audiochunks in 16-bit mono (samplerate 16000) with this method:
@@ -236,6 +348,20 @@ If you don't want to use the local microphone set use_microphone parameter to fa
236
348
  recorder.feed_audio(audio_chunk)
237
349
  ```
238
350
 
351
+ #### Standalone Example:
352
+
353
+ ```python
354
+ from RealtimeSTT import AudioToTextRecorder
355
+
356
+ if __name__ == '__main__':
357
+ recorder = AudioToTextRecorder(use_microphone=False)
358
+ with open("audio_chunk.pcm", "rb") as f:
359
+ audio_chunk = f.read()
360
+
361
+ recorder.feed_audio(audio_chunk)
362
+ print("Transcription: ", recorder.text())
363
+ ```
364
+
239
365
  ### Shutdown
240
366
 
241
367
  You can shutdown the recorder safely by using the context manager protocol:
@@ -245,12 +371,25 @@ with AudioToTextRecorder() as recorder:
245
371
  [...]
246
372
  ```
247
373
 
374
+
248
375
  Or you can call the shutdown method manually (if using "with" is not feasible):
249
376
 
250
377
  ```python
251
378
  recorder.shutdown()
252
379
  ```
253
380
 
381
+ #### Standalone Example:
382
+
383
+ ```python
384
+ from RealtimeSTT import AudioToTextRecorder
385
+
386
+ if __name__ == '__main__':
387
+ with AudioToTextRecorder() as recorder:
388
+ [...]
389
+ # or manually shutdown if "with" is not used
390
+ recorder.shutdown()
391
+ ```
392
+
254
393
  ## Testing the Library
255
394
 
256
395
  The test subdirectory contains a set of scripts to help you evaluate and understand the capabilities of the RealtimeTTS library.
@@ -445,7 +584,7 @@ Shoutout to [Steven Linn](https://github.com/stevenlafl) for providing docker su
445
584
 
446
585
  ## License
447
586
 
448
- MIT
587
+ [MIT](https://github.com/KoljaB/RealtimeSTT?tab=MIT-1-ov-file)
449
588
 
450
589
  ## Author
451
590
 
@@ -1,36 +1,17 @@
1
- Metadata-Version: 2.1
2
- Name: RealtimeSTT
3
- Version: 0.3.0
4
- Summary: A fast Voice Activity Detection and Transcription System
5
- Home-page: https://github.com/KoljaB/RealTimeSTT
6
- Author: Kolja Beigel
7
- Author-email: kolja.beigel@web.de
8
- Keywords: real-time,audio,transcription,speech-to-text,voice-activity-detection,VAD,real-time-transcription,ambient-noise-detection,microphone-input,faster_whisper,speech-recognition,voice-assistants,audio-processing,buffered-transcription,pyaudio,ambient-noise-level,voice-deactivity
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.6
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Requires-Dist: PyAudio==0.2.14
16
- Requires-Dist: faster-whisper==1.0.3
17
- Requires-Dist: pvporcupine==1.9.5
18
- Requires-Dist: webrtcvad==2.0.10
19
- Requires-Dist: halo==0.0.31
20
- Requires-Dist: torch==2.3.1
21
- Requires-Dist: torchaudio==2.3.1
22
- Requires-Dist: scipy==1.12.0
23
- Requires-Dist: websockets==v12.0
24
- Requires-Dist: openwakeword==0.6.0
25
-
26
-
27
1
  # RealtimeSTT
2
+ [![PyPI](https://img.shields.io/pypi/v/RealtimeSTT)](https://pypi.org/project/RealtimeSTT/)
3
+ [![Downloads](https://static.pepy.tech/badge/RealtimeSTT)](https://pepy.tech/project/KoljaB/RealtimeSTT)
4
+ [![GitHub release](https://img.shields.io/github/release/KoljaB/RealtimeSTT.svg)](https://GitHub.com/KoljaB/RealtimeSTT/releases/)
5
+ [![GitHub commits](https://badgen.net/github/commits/KoljaB/RealtimeSTT)](https://GitHub.com/Naereen/KoljaB/RealtimeSTT/commit/)
6
+ [![GitHub forks](https://img.shields.io/github/forks/KoljaB/RealtimeSTT.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/KoljaB/RealtimeSTT/network/)
7
+ [![GitHub stars](https://img.shields.io/github/stars/KoljaB/RealtimeSTT.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/KoljaB/RealtimeSTT/stargazers/)
28
8
 
29
9
  *Easy-to-use, low-latency speech-to-text library for realtime applications*
30
10
 
31
11
  ## New
32
12
 
33
- Custom wake words with [OpenWakeWord](#openwakeword). Thanks to the [developers](https://github.com/dscripka/openWakeWord) of this!
13
+ - AudioToTextRecorderClient class, which automatically starts a server if none is running and connects to it. The class shares the same interface as AudioToTextRecorder, making it easy to upgrade or switch between the two. (Work in progress, most parameters and callbacks of AudioToTextRecorder are already implemented into AudioToTextRecorderClient, but not all. Also the server can not handle concurrent (parallel) requests yet.)
14
+ - reworked CLI interface ("stt-server" to start the server, "stt" to start the client, look at "server" folder for more info)
34
15
 
35
16
  ## About the Project
36
17
 
@@ -47,12 +28,49 @@ https://github.com/user-attachments/assets/797e6552-27cd-41b1-a7f3-e5cbc72094f5
47
28
 
48
29
  ### Updates
49
30
 
50
- Latest Version: v0.3.0
31
+ Latest Version: v0.3.1
51
32
 
52
33
  See [release history](https://github.com/KoljaB/RealtimeSTT/releases).
53
34
 
54
35
  > **Hint:** *Since we use the `multiprocessing` module now, ensure to include the `if __name__ == '__main__':` protection in your code to prevent unexpected behavior, especially on platforms like Windows. For a detailed explanation on why this is important, visit the [official Python documentation on `multiprocessing`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing-programming).*
55
36
 
37
+ ## Quick Examples
38
+
39
+ ### Print everything being said:
40
+
41
+ ```python
42
+ from RealtimeSTT import AudioToTextRecorder
43
+ import pyautogui
44
+
45
+ def process_text(text):
46
+ print(text)
47
+
48
+ if __name__ == '__main__':
49
+ print("Wait until it says 'speak now'")
50
+ recorder = AudioToTextRecorder()
51
+
52
+ while True:
53
+ recorder.text(process_text)
54
+ ```
55
+
56
+ ### Type everything being said:
57
+
58
+ ```python
59
+ from RealtimeSTT import AudioToTextRecorder
60
+ import pyautogui
61
+
62
+ def process_text(text):
63
+ pyautogui.typewrite(text + " ")
64
+
65
+ if __name__ == '__main__':
66
+ print("Wait until it says 'speak now'")
67
+ recorder = AudioToTextRecorder()
68
+
69
+ while True:
70
+ recorder.text(process_text)
71
+ ```
72
+ *Will type everything being said into your selected text box*
73
+
56
74
  ### Features
57
75
 
58
76
  - **Voice Activity Detection**: Automatically detects when you start and stop speaking.
@@ -183,6 +201,19 @@ recorder.stop()
183
201
  print(recorder.text())
184
202
  ```
185
203
 
204
+ #### Standalone Example:
205
+
206
+ ```python
207
+ from RealtimeSTT import AudioToTextRecorder
208
+
209
+ if __name__ == '__main__':
210
+ recorder = AudioToTextRecorder()
211
+ recorder.start()
212
+ input("Press Enter to stop recording...")
213
+ recorder.stop()
214
+ print("Transcription: ", recorder.text())
215
+ ```
216
+
186
217
  ### Automatic Recording
187
218
 
188
219
  Recording based on voice activity detection.
@@ -192,8 +223,19 @@ with AudioToTextRecorder() as recorder:
192
223
  print(recorder.text())
193
224
  ```
194
225
 
226
+ #### Standalone Example:
227
+
228
+ ```python
229
+ from RealtimeSTT import AudioToTextRecorder
230
+
231
+ if __name__ == '__main__':
232
+ with AudioToTextRecorder() as recorder:
233
+ print("Transcription: ", recorder.text())
234
+ ```
235
+
195
236
  When running recorder.text in a loop it is recommended to use a callback, allowing the transcription to be run asynchronously:
196
237
 
238
+
197
239
  ```python
198
240
  def process_text(text):
199
241
  print (text)
@@ -202,6 +244,21 @@ while True:
202
244
  recorder.text(process_text)
203
245
  ```
204
246
 
247
+ #### Standalone Example:
248
+
249
+ ```python
250
+ from RealtimeSTT import AudioToTextRecorder
251
+
252
+ def process_text(text):
253
+ print(text)
254
+
255
+ if __name__ == '__main__':
256
+ recorder = AudioToTextRecorder()
257
+
258
+ while True:
259
+ recorder.text(process_text)
260
+ ```
261
+
205
262
  ### Wakewords
206
263
 
207
264
  Keyword activation before detecting voice. Write the comma-separated list of your desired activation keywords into the wake_words parameter. You can choose wake words from these list: alexa, americano, blueberry, bumblebee, computer, grapefruits, grasshopper, hey google, hey siri, jarvis, ok google, picovoice, porcupine, terminator.
@@ -213,6 +270,18 @@ print('Say "Jarvis" then speak.')
213
270
  print(recorder.text())
214
271
  ```
215
272
 
273
+ #### Standalone Example:
274
+
275
+ ```python
276
+ from RealtimeSTT import AudioToTextRecorder
277
+
278
+ if __name__ == '__main__':
279
+ recorder = AudioToTextRecorder(wake_words="jarvis")
280
+
281
+ print('Say "Jarvis" to start recording.')
282
+ print(recorder.text())
283
+ ```
284
+
216
285
  ### Callbacks
217
286
 
218
287
  You can set callback functions to be executed on different events (see [Configuration](#configuration)) :
@@ -228,6 +297,22 @@ recorder = AudioToTextRecorder(on_recording_start=my_start_callback,
228
297
  on_recording_stop=my_stop_callback)
229
298
  ```
230
299
 
300
+ #### Standalone Example:
301
+
302
+ ```python
303
+ from RealtimeSTT import AudioToTextRecorder
304
+
305
+ def start_callback():
306
+ print("Recording started!")
307
+
308
+ def stop_callback():
309
+ print("Recording stopped!")
310
+
311
+ if __name__ == '__main__':
312
+ recorder = AudioToTextRecorder(on_recording_start=start_callback,
313
+ on_recording_stop=stop_callback)
314
+ ```
315
+
231
316
  ### Feed chunks
232
317
 
233
318
  If you don't want to use the local microphone set use_microphone parameter to false and provide raw PCM audiochunks in 16-bit mono (samplerate 16000) with this method:
@@ -236,6 +321,20 @@ If you don't want to use the local microphone set use_microphone parameter to fa
236
321
  recorder.feed_audio(audio_chunk)
237
322
  ```
238
323
 
324
+ #### Standalone Example:
325
+
326
+ ```python
327
+ from RealtimeSTT import AudioToTextRecorder
328
+
329
+ if __name__ == '__main__':
330
+ recorder = AudioToTextRecorder(use_microphone=False)
331
+ with open("audio_chunk.pcm", "rb") as f:
332
+ audio_chunk = f.read()
333
+
334
+ recorder.feed_audio(audio_chunk)
335
+ print("Transcription: ", recorder.text())
336
+ ```
337
+
239
338
  ### Shutdown
240
339
 
241
340
  You can shutdown the recorder safely by using the context manager protocol:
@@ -245,12 +344,25 @@ with AudioToTextRecorder() as recorder:
245
344
  [...]
246
345
  ```
247
346
 
347
+
248
348
  Or you can call the shutdown method manually (if using "with" is not feasible):
249
349
 
250
350
  ```python
251
351
  recorder.shutdown()
252
352
  ```
253
353
 
354
+ #### Standalone Example:
355
+
356
+ ```python
357
+ from RealtimeSTT import AudioToTextRecorder
358
+
359
+ if __name__ == '__main__':
360
+ with AudioToTextRecorder() as recorder:
361
+ [...]
362
+ # or manually shutdown if "with" is not used
363
+ recorder.shutdown()
364
+ ```
365
+
254
366
  ## Testing the Library
255
367
 
256
368
  The test subdirectory contains a set of scripts to help you evaluate and understand the capabilities of the RealtimeTTS library.
@@ -445,7 +557,7 @@ Shoutout to [Steven Linn](https://github.com/stevenlafl) for providing docker su
445
557
 
446
558
  ## License
447
559
 
448
- MIT
560
+ [MIT](https://github.com/KoljaB/RealtimeSTT?tab=MIT-1-ov-file)
449
561
 
450
562
  ## Author
451
563
 
@@ -0,0 +1,2 @@
1
+ from .audio_recorder import AudioToTextRecorder
2
+ from .audio_recorder_client import AudioToTextRecorderClient
@@ -29,7 +29,6 @@ Author: Kolja Beigel
29
29
  from typing import Iterable, List, Optional, Union
30
30
  import torch.multiprocessing as mp
31
31
  import torch
32
- from typing import List, Union
33
32
  from ctypes import c_bool
34
33
  from openwakeword.model import Model
35
34
  from scipy.signal import resample
@@ -49,6 +48,7 @@ import platform
49
48
  import pyaudio
50
49
  import logging
51
50
  import struct
51
+ import base64
52
52
  import queue
53
53
  import halo
54
54
  import time
@@ -121,8 +121,9 @@ class TranscriptionWorker:
121
121
  time.sleep(TIME_SLEEP)
122
122
 
123
123
  def run(self):
124
- system_signal.signal(system_signal.SIGINT, system_signal.SIG_IGN)
125
- __builtins__['print'] = self.custom_print
124
+ if __name__ == "__main__":
125
+ system_signal.signal(system_signal.SIGINT, system_signal.SIG_IGN)
126
+ # __builtins__['print'] = self.custom_print
126
127
 
127
128
  logging.info(f"Initializing faster_whisper main transcription model {self.model_path}")
128
129
 
@@ -171,7 +172,7 @@ class TranscriptionWorker:
171
172
  except Exception as e:
172
173
  logging.error(f"General error in processing queue item: {e}")
173
174
  finally:
174
- __builtins__['print'] = print # Restore the original print function
175
+ # __builtins__['print'] = print # Restore the original print function
175
176
  self.conn.close()
176
177
  self.stdout_pipe.close()
177
178
  self.shutdown_event.set() # Ensure the polling thread will stop
@@ -539,6 +540,7 @@ class AudioToTextRecorder:
539
540
  self.start_recording_event = threading.Event()
540
541
  self.stop_recording_event = threading.Event()
541
542
  self.last_transcription_bytes = None
543
+ self.last_transcription_bytes_b64 = None
542
544
  self.initial_prompt = initial_prompt
543
545
  self.suppress_tokens = suppress_tokens
544
546
  self.use_wake_words = wake_words or wakeword_backend in {'oww', 'openwakeword', 'openwakewords'}
@@ -786,6 +788,10 @@ class AudioToTextRecorder:
786
788
  maxlen=int((self.sample_rate // self.buffer_size) *
787
789
  self.pre_recording_buffer_duration)
788
790
  )
791
+ self.last_words_buffer = collections.deque(
792
+ maxlen=int((self.sample_rate // self.buffer_size) *
793
+ 0.3)
794
+ )
789
795
  self.frames = []
790
796
 
791
797
  # Recording control flags
@@ -894,7 +900,9 @@ class AudioToTextRecorder:
894
900
  import pyaudio
895
901
  import numpy as np
896
902
  from scipy import signal
897
- system_signal.signal(system_signal.SIGINT, system_signal.SIG_IGN)
903
+
904
+ if __name__ == '__main__':
905
+ system_signal.signal(system_signal.SIGINT, system_signal.SIG_IGN)
898
906
 
899
907
  def get_highest_sample_rate(audio_interface, device_index):
900
908
  """Get the highest supported sample rate for the specified device."""
@@ -984,13 +992,16 @@ class AudioToTextRecorder:
984
992
  stream = initialize_audio_stream(audio_interface, input_device_index, device_sample_rate, chunk_size)
985
993
  if stream is not None:
986
994
  logging.debug(f"Audio recording initialized successfully at {device_sample_rate} Hz, reading {chunk_size} frames at a time")
995
+ # logging.error(f"Audio recording initialized successfully at {device_sample_rate} Hz, reading {chunk_size} frames at a time")
987
996
  return True
988
997
  except Exception as e:
989
- logging.warning(f"Failed to initialize audio stream at {device_sample_rate} Hz: {e}")
998
+ logging.warning(f"Failed to initialize audio23 stream at {device_sample_rate} Hz: {e}")
990
999
  continue
1000
+
1001
+
991
1002
 
992
1003
  # If we reach here, none of the sample rates worked
993
- raise Exception("Failed to initialize audio stream with all sample rates.")
1004
+ raise Exception("Failed to initialize audio stream12 with all sample rates.")
994
1005
 
995
1006
  except Exception as e:
996
1007
  logging.exception(f"Error initializing pyaudio audio recording: {e}")
@@ -1198,7 +1209,7 @@ class AudioToTextRecorder:
1198
1209
  if self.transcribe_count == 0:
1199
1210
  logging.debug("Adding transcription request, no early transcription started")
1200
1211
  start_time = time.time() # Start timing
1201
- self.parent_transcription_pipe.send((self.audio, self.language))
1212
+ self.parent_transcription_pipe.send((audio_copy, self.language))
1202
1213
  self.transcribe_count += 1
1203
1214
 
1204
1215
  while self.transcribe_count > 0:
@@ -1212,7 +1223,8 @@ class AudioToTextRecorder:
1212
1223
  segments, info = result
1213
1224
  self.detected_language = info.language if info.language_probability > 0 else None
1214
1225
  self.detected_language_probability = info.language_probability
1215
- self.last_transcription_bytes = audio_copy
1226
+ self.last_transcription_bytes = copy.deepcopy(audio_copy)
1227
+ self.last_transcription_bytes_b64 = base64.b64encode(self.last_transcription_bytes.tobytes()).decode('utf-8')
1216
1228
  transcription = self._preprocess_output(segments)
1217
1229
  end_time = time.time() # End timing
1218
1230
  transcription_time = end_time - start_time
@@ -1377,6 +1389,17 @@ class AudioToTextRecorder:
1377
1389
 
1378
1390
  return self
1379
1391
 
1392
+ def listen(self):
1393
+ """
1394
+ Puts recorder in immediate "listen" state.
1395
+ This is the state after a wake word detection, for example.
1396
+ The recorder now "listens" for voice activation.
1397
+ Once voice is detected we enter "recording" state.
1398
+ """
1399
+ self.listen_start = time.time()
1400
+ self._set_state("listening")
1401
+ self.start_recording_on_voice_activity = True
1402
+
1380
1403
  def feed_audio(self, chunk, original_sample_rate=16000):
1381
1404
  """
1382
1405
  Feed an audio chunk into the processing pipeline. Chunks are
@@ -1453,14 +1476,14 @@ class AudioToTextRecorder:
1453
1476
  logging.debug('Terminating reader process')
1454
1477
 
1455
1478
  # Give it some time to finish the loop and cleanup.
1456
- if self.use_microphone:
1479
+ if self.use_microphone.value:
1457
1480
  self.reader_process.join(timeout=10)
1458
1481
 
1459
- if self.reader_process.is_alive():
1460
- logging.warning("Reader process did not terminate "
1461
- "in time. Terminating forcefully."
1462
- )
1463
- self.reader_process.terminate()
1482
+ if self.reader_process.is_alive():
1483
+ logging.warning("Reader process did not terminate "
1484
+ "in time. Terminating forcefully."
1485
+ )
1486
+ self.reader_process.terminate()
1464
1487
 
1465
1488
  logging.debug('Terminating transcription process')
1466
1489
  self.transcript_process.join(timeout=10)
@@ -1521,6 +1544,7 @@ class AudioToTextRecorder:
1521
1544
  logging.debug('Debug: Trying to get data from audio queue')
1522
1545
  try:
1523
1546
  data = self.audio_queue.get(timeout=0.01)
1547
+ self.last_words_buffer.append(data)
1524
1548
  except queue.Empty:
1525
1549
  if self.use_extended_logging:
1526
1550
  logging.debug('Debug: Queue is empty, checking if still running')
@@ -1818,10 +1842,6 @@ class AudioToTextRecorder:
1818
1842
 
1819
1843
  if self.use_extended_logging:
1820
1844
  logging.debug('Debug: Handling non-wake word scenario')
1821
- if not self.use_wake_words:
1822
- self.listen_start = time.time()
1823
- self._set_state("listening")
1824
- self.start_recording_on_voice_activity = True
1825
1845
  else:
1826
1846
  if self.use_extended_logging:
1827
1847
  logging.debug('Debug: Setting failed_stop_attempt to True')