RealTimeSTT 0.3.100__tar.gz → 0.3.101__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.
Files changed (22) hide show
  1. {realtimestt-0.3.100/realtimestt.egg-info → realtimestt-0.3.101}/PKG-INFO +3 -4
  2. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT/audio_recorder.py +69 -37
  3. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT/audio_recorder_client.py +17 -9
  4. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT_server/stt_server.py +4 -9
  5. {realtimestt-0.3.100 → realtimestt-0.3.101/realtimestt.egg-info}/PKG-INFO +3 -4
  6. {realtimestt-0.3.100 → realtimestt-0.3.101}/realtimestt.egg-info/requires.txt +2 -3
  7. {realtimestt-0.3.100 → realtimestt-0.3.101}/requirements.txt +2 -3
  8. {realtimestt-0.3.100 → realtimestt-0.3.101}/setup.py +1 -1
  9. {realtimestt-0.3.100 → realtimestt-0.3.101}/LICENSE +0 -0
  10. {realtimestt-0.3.100 → realtimestt-0.3.101}/MANIFEST.in +0 -0
  11. {realtimestt-0.3.100 → realtimestt-0.3.101}/README.md +0 -0
  12. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT/__init__.py +0 -0
  13. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT/audio_input.py +0 -0
  14. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT/warmup_audio.wav +0 -0
  15. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT_server/__init__.py +0 -0
  16. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT_server/install_packages.py +0 -0
  17. {realtimestt-0.3.100 → realtimestt-0.3.101}/RealtimeSTT_server/stt_cli_client.py +0 -0
  18. {realtimestt-0.3.100 → realtimestt-0.3.101}/realtimestt.egg-info/SOURCES.txt +0 -0
  19. {realtimestt-0.3.100 → realtimestt-0.3.101}/realtimestt.egg-info/dependency_links.txt +0 -0
  20. {realtimestt-0.3.100 → realtimestt-0.3.101}/realtimestt.egg-info/entry_points.txt +0 -0
  21. {realtimestt-0.3.100 → realtimestt-0.3.101}/realtimestt.egg-info/top_level.txt +0 -0
  22. {realtimestt-0.3.100 → realtimestt-0.3.101}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: realtimestt
3
- Version: 0.3.100
3
+ Version: 0.3.101
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
@@ -17,11 +17,10 @@ Requires-Dist: webrtcvad-wheels==2.0.14
17
17
  Requires-Dist: halo==0.0.31
18
18
  Requires-Dist: torch
19
19
  Requires-Dist: torchaudio
20
- Requires-Dist: scipy==1.15.1
20
+ Requires-Dist: scipy==1.15.2
21
21
  Requires-Dist: openwakeword>=0.4.0
22
- Requires-Dist: websockets==14.2
22
+ Requires-Dist: websockets==15.0.1
23
23
  Requires-Dist: websocket-client==1.8.0
24
- Requires-Dist: numpy<2.0.0
25
24
  Requires-Dist: soundfile==0.13.1
26
25
  Dynamic: author
27
26
  Dynamic: author-email
@@ -279,7 +279,7 @@ class AudioToTextRecorder:
279
279
  on_vad_detect_stop=None,
280
280
 
281
281
  # Wake word parameters
282
- wakeword_backend: str = "pvporcupine",
282
+ wakeword_backend: str = "",
283
283
  openwakeword_model_paths: str = None,
284
284
  openwakeword_inference_framework: str = "onnx",
285
285
  wake_words: str = "",
@@ -641,6 +641,7 @@ class AudioToTextRecorder:
641
641
  self.early_transcription_on_silence = early_transcription_on_silence
642
642
  self.use_extended_logging = use_extended_logging
643
643
  self.faster_whisper_vad_filter = faster_whisper_vad_filter
644
+ self.awaiting_speech_end = False
644
645
 
645
646
  # ----------------------------------------------------------------------------
646
647
  # Named logger configuration
@@ -771,7 +772,7 @@ class AudioToTextRecorder:
771
772
  "transcription model initialized successfully")
772
773
 
773
774
  # Setup wake word detection
774
- if wake_words or wakeword_backend in {'oww', 'openwakeword', 'openwakewords'}:
775
+ if wake_words or wakeword_backend in {'oww', 'openwakeword', 'openwakewords', 'pvp', 'pvporcupine'}:
775
776
  self.wakeword_backend = wakeword_backend
776
777
 
777
778
  self.wake_words_list = [
@@ -1439,41 +1440,15 @@ class AudioToTextRecorder:
1439
1440
  self.shutdown()
1440
1441
  raise # Re-raise the exception after cleanup
1441
1442
 
1442
- def transcribe(self):
1443
- """
1444
- Transcribes audio captured by this class instance using the
1445
- `faster_whisper` model.
1446
-
1447
- Automatically starts recording upon voice activity if not manually
1448
- started using `recorder.start()`.
1449
- Automatically stops recording upon voice deactivity if not manually
1450
- stopped with `recorder.stop()`.
1451
- Processes the recorded audio to generate transcription.
1452
-
1453
- Args:
1454
- on_transcription_finished (callable, optional): Callback function
1455
- to be executed when transcription is ready.
1456
- If provided, transcription will be performed asynchronously,
1457
- and the callback will receive the transcription as its argument.
1458
- If omitted, the transcription will be performed synchronously,
1459
- and the result will be returned.
1460
1443
 
1461
- Returns (if no callback is set):
1462
- str: The transcription of the recorded audio.
1463
-
1464
- Raises:
1465
- Exception: If there is an error during the transcription process.
1466
- """
1467
- self._set_state("transcribing")
1468
- audio_copy = copy.deepcopy(self.audio)
1444
+ def perform_final_transcription(self, audio_bytes=None):
1469
1445
  start_time = 0
1470
1446
  with self.transcription_lock:
1471
-
1472
1447
  try:
1473
1448
  if self.transcribe_count == 0:
1474
1449
  logger.debug("Adding transcription request, no early transcription started")
1475
1450
  start_time = time.time() # Start timing
1476
- self.parent_transcription_pipe.send((audio_copy, self.language))
1451
+ self.parent_transcription_pipe.send((audio_bytes, self.language))
1477
1452
  self.transcribe_count += 1
1478
1453
 
1479
1454
  while self.transcribe_count > 0:
@@ -1493,7 +1468,7 @@ class AudioToTextRecorder:
1493
1468
  segments, info = result
1494
1469
  self.detected_language = info.language if info.language_probability > 0 else None
1495
1470
  self.detected_language_probability = info.language_probability
1496
- self.last_transcription_bytes = copy.deepcopy(audio_copy)
1471
+ self.last_transcription_bytes = copy.deepcopy(audio_bytes)
1497
1472
  self.last_transcription_bytes_b64 = base64.b64encode(self.last_transcription_bytes.tobytes()).decode('utf-8')
1498
1473
  transcription = self._preprocess_output(segments)
1499
1474
  end_time = time.time() # End timing
@@ -1512,6 +1487,43 @@ class AudioToTextRecorder:
1512
1487
  logger.error(f"Error during transcription: {str(e)}", exc_info=True)
1513
1488
  raise e
1514
1489
 
1490
+
1491
+ def transcribe(self):
1492
+ """
1493
+ Transcribes audio captured by this class instance using the
1494
+ `faster_whisper` model.
1495
+
1496
+ Automatically starts recording upon voice activity if not manually
1497
+ started using `recorder.start()`.
1498
+ Automatically stops recording upon voice deactivity if not manually
1499
+ stopped with `recorder.stop()`.
1500
+ Processes the recorded audio to generate transcription.
1501
+
1502
+ Args:
1503
+ on_transcription_finished (callable, optional): Callback function
1504
+ to be executed when transcription is ready.
1505
+ If provided, transcription will be performed asynchronously,
1506
+ and the callback will receive the transcription as its argument.
1507
+ If omitted, the transcription will be performed synchronously,
1508
+ and the result will be returned.
1509
+
1510
+ Returns (if no callback is set):
1511
+ str: The transcription of the recorded audio.
1512
+
1513
+ Raises:
1514
+ Exception: If there is an error during the transcription process.
1515
+ """
1516
+ audio_copy = copy.deepcopy(self.audio)
1517
+ self._set_state("transcribing")
1518
+ if self.on_transcription_start:
1519
+ abort_value = self.on_transcription_start(audio_copy)
1520
+ if not abort_value:
1521
+ return self.perform_final_transcription(audio_copy)
1522
+ return None
1523
+ else:
1524
+ return self.perform_final_transcription(audio_copy)
1525
+
1526
+
1515
1527
  def _process_wakeword(self, data):
1516
1528
  """
1517
1529
  Processes audio data to detect wake words.
@@ -2084,6 +2096,7 @@ class AudioToTextRecorder:
2084
2096
  (time.time() - self.recording_start_time > self.min_length_of_recording):
2085
2097
 
2086
2098
  self.speech_end_silence_start = time.time()
2099
+ self.awaiting_speech_end = True
2087
2100
 
2088
2101
  if self.use_extended_logging:
2089
2102
  logger.debug('Debug: Checking early transcription conditions')
@@ -2104,6 +2117,7 @@ class AudioToTextRecorder:
2104
2117
  self.allowed_to_early_transcribe = False
2105
2118
 
2106
2119
  else:
2120
+ self.awaiting_speech_end = False
2107
2121
  if self.use_extended_logging:
2108
2122
  logger.debug('Debug: Handling speech detection')
2109
2123
  if self.speech_end_silence_start:
@@ -2118,6 +2132,7 @@ class AudioToTextRecorder:
2118
2132
  if self.speech_end_silence_start and time.time() - \
2119
2133
  self.speech_end_silence_start >= \
2120
2134
  self.post_speech_silence_duration:
2135
+
2121
2136
  if self.on_vad_stop:
2122
2137
  self.on_vad_stop()
2123
2138
 
@@ -2151,6 +2166,8 @@ class AudioToTextRecorder:
2151
2166
  logger.debug('Debug: Setting failed_stop_attempt to True')
2152
2167
  failed_stop_attempt = True
2153
2168
 
2169
+ self.awaiting_speech_end = False
2170
+
2154
2171
  if self.use_extended_logging:
2155
2172
  logger.debug('Debug: Checking if recording stopped')
2156
2173
  if not self.is_recording and was_recording:
@@ -2206,6 +2223,8 @@ class AudioToTextRecorder:
2206
2223
  logger.debug('Debug: Exiting _recording_worker method')
2207
2224
 
2208
2225
 
2226
+
2227
+
2209
2228
  def _realtime_worker(self):
2210
2229
  """
2211
2230
  Performs real-time transcription if the feature is enabled.
@@ -2225,14 +2244,29 @@ class AudioToTextRecorder:
2225
2244
  if not self.enable_realtime_transcription:
2226
2245
  return
2227
2246
 
2228
- # Continue running as long as the main process is active
2247
+ # Track time of last transcription
2248
+ last_transcription_time = time.time()
2249
+
2229
2250
  while self.is_running:
2230
2251
 
2231
- # Check if the recording is active
2232
2252
  if self.is_recording:
2233
2253
 
2234
- # Sleep for the duration of the transcription resolution
2235
- time.sleep(self.realtime_processing_pause)
2254
+ # MODIFIED SLEEP LOGIC:
2255
+ # Wait until realtime_processing_pause has elapsed,
2256
+ # but check often so we can respond to changes quickly.
2257
+ while (
2258
+ time.time() - last_transcription_time
2259
+ ) < self.realtime_processing_pause:
2260
+ time.sleep(0.001)
2261
+ if not self.is_running or not self.is_recording:
2262
+ break
2263
+
2264
+ if self.awaiting_speech_end:
2265
+ time.sleep(0.001)
2266
+ continue
2267
+
2268
+ # Update transcription time
2269
+ last_transcription_time = time.time()
2236
2270
 
2237
2271
  # Convert the buffer frames to a NumPy array
2238
2272
  audio_array = np.frombuffer(
@@ -2557,8 +2591,6 @@ class AudioToTextRecorder:
2557
2591
  if self.spinner and self.halo:
2558
2592
  self.halo._interval = 500
2559
2593
  elif new_state == "transcribing":
2560
- if self.on_transcription_start:
2561
- self.on_transcription_start()
2562
2594
  self._set_spinner("transcribing")
2563
2595
  if self.spinner and self.halo:
2564
2596
  self.halo._interval = 50
@@ -257,10 +257,13 @@ class AudioToTextRecorderClient:
257
257
 
258
258
 
259
259
  if self.server_already_running:
260
- self.set_parameter("language", self.language)
261
- # self.set_parameter("model", self.model)
262
-
263
-
260
+ if not self.connection_established.wait(timeout=10):
261
+ print("Server connection not established within 10 seconds.")
262
+ else:
263
+ self.set_parameter("language", self.language)
264
+ print(f"Language set to {self.language}")
265
+ self.set_parameter("wake_word_activation_delay", self.wake_word_activation_delay)
266
+ print(f"Wake word activation delay set to {self.wake_word_activation_delay}")
264
267
 
265
268
  def text(self, on_transcription_finished=None):
266
269
  self.realtime_text = ""
@@ -490,11 +493,16 @@ class AudioToTextRecorderClient:
490
493
  print("STT server start command issued. Please wait a moment for it to initialize.", file=sys.stderr)
491
494
 
492
495
  def is_server_running(self):
493
- parsed_url = urlparse(self.control_url)
494
- host = parsed_url.hostname
495
- port = parsed_url.port or 80
496
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
497
- return s.connect_ex((host, port)) == 0
496
+ try:
497
+ # Attempt a proper WebSocket handshake to the control URL.
498
+ from websocket import create_connection
499
+ ws = create_connection(self.control_url, timeout=3)
500
+ ws.close()
501
+ return True
502
+ except Exception as e:
503
+ if self.debug_mode:
504
+ print(f"Server connectivity check failed: {e}")
505
+ return False
498
506
 
499
507
  def ensure_server_running(self):
500
508
  if not self.is_server_running():
@@ -290,14 +290,12 @@ def text_detected(text, loop):
290
290
  print(f"\r[{timestamp}] {bcolors.OKCYAN}{text}{bcolors.ENDC}", flush=True, end='')
291
291
 
292
292
  def on_recording_start(loop):
293
- # Send a message to the client indicating recording has started
294
293
  message = json.dumps({
295
294
  'type': 'recording_start'
296
295
  })
297
296
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
298
297
 
299
298
  def on_recording_stop(loop):
300
- # Send a message to the client indicating recording has stopped
301
299
  message = json.dumps({
302
300
  'type': 'recording_stop'
303
301
  })
@@ -316,28 +314,24 @@ def on_vad_detect_stop(loop):
316
314
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
317
315
 
318
316
  def on_wakeword_detected(loop):
319
- # Send a message to the client when wake word detection starts
320
317
  message = json.dumps({
321
318
  'type': 'wakeword_detected'
322
319
  })
323
320
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
324
321
 
325
322
  def on_wakeword_detection_start(loop):
326
- # Send a message to the client when wake word detection starts
327
323
  message = json.dumps({
328
324
  'type': 'wakeword_detection_start'
329
325
  })
330
326
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
331
327
 
332
328
  def on_wakeword_detection_end(loop):
333
- # Send a message to the client when wake word detection ends
334
329
  message = json.dumps({
335
330
  'type': 'wakeword_detection_end'
336
331
  })
337
332
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
338
333
 
339
- def on_transcription_start(loop):
340
- # Send a message to the client when transcription starts
334
+ def on_transcription_start(_audio_bytes, loop):
341
335
  message = json.dumps({
342
336
  'type': 'transcription_start'
343
337
  })
@@ -460,8 +454,8 @@ def parse_arguments():
460
454
  parser.add_argument('--wake_word_timeout', type=float, default=5.0,
461
455
  help='Maximum time in seconds that the system will wait for a wake word before timing out. After this timeout, the system stops listening for wake words until reactivated. Default is 5.0 seconds.')
462
456
 
463
- parser.add_argument('--wake_word_activation_delay', type=float, default=20,
464
- help='The delay in seconds before the wake word detection is activated after the system starts listening. This prevents false positives during the start of a session. Default is 0.5 seconds.')
457
+ parser.add_argument('--wake_word_activation_delay', type=float, default=0,
458
+ help='The delay in seconds before the wake word detection is activated after the system starts listening. This prevents false positives during the start of a session. Default is 0 seconds.')
465
459
 
466
460
  parser.add_argument('--wakeword_backend', type=str, default='none',
467
461
  help='The backend used for wake word detection. You can specify different backends such as "default" or any custom implementations depending on your setup. Default is "pvporcupine".')
@@ -776,6 +770,7 @@ async def main_async():
776
770
  'use_main_model_for_realtime': args.use_main_model_for_realtime,
777
771
  'spinner': False,
778
772
  'use_microphone': False,
773
+
779
774
  'on_realtime_transcription_update': make_callback(loop, text_detected),
780
775
  'on_recording_start': make_callback(loop, on_recording_start),
781
776
  'on_recording_stop': make_callback(loop, on_recording_stop),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: realtimestt
3
- Version: 0.3.100
3
+ Version: 0.3.101
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
@@ -17,11 +17,10 @@ Requires-Dist: webrtcvad-wheels==2.0.14
17
17
  Requires-Dist: halo==0.0.31
18
18
  Requires-Dist: torch
19
19
  Requires-Dist: torchaudio
20
- Requires-Dist: scipy==1.15.1
20
+ Requires-Dist: scipy==1.15.2
21
21
  Requires-Dist: openwakeword>=0.4.0
22
- Requires-Dist: websockets==14.2
22
+ Requires-Dist: websockets==15.0.1
23
23
  Requires-Dist: websocket-client==1.8.0
24
- Requires-Dist: numpy<2.0.0
25
24
  Requires-Dist: soundfile==0.13.1
26
25
  Dynamic: author
27
26
  Dynamic: author-email
@@ -5,9 +5,8 @@ webrtcvad-wheels==2.0.14
5
5
  halo==0.0.31
6
6
  torch
7
7
  torchaudio
8
- scipy==1.15.1
8
+ scipy==1.15.2
9
9
  openwakeword>=0.4.0
10
- websockets==14.2
10
+ websockets==15.0.1
11
11
  websocket-client==1.8.0
12
- numpy<2.0.0
13
12
  soundfile==0.13.1
@@ -5,9 +5,8 @@ webrtcvad-wheels==2.0.14
5
5
  halo==0.0.31
6
6
  torch
7
7
  torchaudio
8
- scipy==1.15.1
8
+ scipy==1.15.2
9
9
  openwakeword>=0.4.0
10
- websockets==14.2
10
+ websockets==15.0.1
11
11
  websocket-client==1.8.0
12
- numpy<2.0.0
13
12
  soundfile==0.13.1
@@ -14,7 +14,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
14
14
 
15
15
  setuptools.setup(
16
16
  name="realtimestt",
17
- version="0.3.100",
17
+ version="0.3.101",
18
18
  author="Kolja Beigel",
19
19
  author_email="kolja.beigel@web.de",
20
20
  description="A fast Voice Activity Detection and Transcription System",
File without changes
File without changes
File without changes
File without changes