RealTimeSTT 0.3.4__tar.gz → 0.3.5__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.4
3
+ Version: 0.3.5
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
@@ -55,7 +55,7 @@ https://github.com/user-attachments/assets/797e6552-27cd-41b1-a7f3-e5cbc72094f5
55
55
 
56
56
  ### Updates
57
57
 
58
- Latest Version: v0.3.4
58
+ Latest Version: v0.3.5
59
59
 
60
60
  See [release history](https://github.com/KoljaB/RealtimeSTT/releases).
61
61
 
@@ -28,7 +28,7 @@ https://github.com/user-attachments/assets/797e6552-27cd-41b1-a7f3-e5cbc72094f5
28
28
 
29
29
  ### Updates
30
30
 
31
- Latest Version: v0.3.4
31
+ Latest Version: v0.3.5
32
32
 
33
33
  See [release history](https://github.com/KoljaB/RealtimeSTT/releases).
34
34
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: RealtimeSTT
3
- Version: 0.3.4
3
+ Version: 0.3.5
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
@@ -55,7 +55,7 @@ https://github.com/user-attachments/assets/797e6552-27cd-41b1-a7f3-e5cbc72094f5
55
55
 
56
56
  ### Updates
57
57
 
58
- Latest Version: v0.3.4
58
+ Latest Version: v0.3.5
59
59
 
60
60
  See [release history](https://github.com/KoljaB/RealtimeSTT/releases).
61
61
 
@@ -10,7 +10,7 @@ RealtimeSTT.egg-info/dependency_links.txt
10
10
  RealtimeSTT.egg-info/entry_points.txt
11
11
  RealtimeSTT.egg-info/requires.txt
12
12
  RealtimeSTT.egg-info/top_level.txt
13
- server/__init__.py
14
- server/install_packages.py
15
- server/stt_cli_client.py
16
- server/stt_server.py
13
+ RealtimeSTT_server/__init__.py
14
+ RealtimeSTT_server/install_packages.py
15
+ RealtimeSTT_server/stt_cli_client.py
16
+ RealtimeSTT_server/stt_server.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ stt = RealtimeSTT_server.stt_cli_client:main
3
+ stt-server = RealtimeSTT_server.stt_server:main
@@ -0,0 +1,2 @@
1
+ RealtimeSTT
2
+ RealtimeSTT_server
@@ -1,90 +1,21 @@
1
- """
2
- Speech-to-Text (STT) Client CLI for WebSocket Server Interaction
3
-
4
- This command-line interface (CLI) allows interaction with the Speech-to-Text (STT) WebSocket server. It connects to the server via control and data WebSocket URLs to facilitate real-time speech transcription, control the server, and manage various parameters related to the STT process.
5
-
6
- The client can be used to start recording audio, set or retrieve STT parameters, and interact with the server using commands. Additionally, the CLI can disable real-time updates or run in debug mode for detailed output.
7
-
8
- ### Features:
9
- - Connects to STT WebSocket server for real-time transcription and control.
10
- - Supports setting and retrieving parameters via the command line.
11
- - Allows calling server methods (e.g., start/stop recording).
12
- - Option to disable real-time updates during transcription.
13
- - Debug mode available for verbose logging.
14
-
15
- ### Starting the Client:
16
- You can start the client using the command `stt` and optionally pass configuration options or commands for interacting with the server.
17
-
18
- ```bash
19
- stt [OPTIONS]
20
- ```
21
-
22
- ### Available Parameters:
23
- - `--control-url` (default: "ws://localhost:8011"): The WebSocket URL for server control commands.
24
-
25
- - `--data-url` (default: "ws://localhost:8012"): The WebSocket URL for sending audio data and receiving transcription updates.
26
-
27
- - `--debug`: Enable debug mode, which prints detailed logs to stderr.
28
-
29
- - `--nort` or `--norealtime`: Disable real-time output of transcription results.
30
-
31
- - `--set-param PARAM VALUE`: Set a recorder parameter (e.g., silero_sensitivity, beam_size, etc.). This option can be used multiple times to set different parameters.
32
-
33
- - `--get-param PARAM`: Retrieve the value of a specific recorder parameter. This option can be used multiple times to retrieve different parameters.
34
-
35
- - `--call-method METHOD [ARGS]`: Call a method on the recorder with optional arguments. This option can be used multiple times for different methods.
36
-
37
- ### Example Usage:
38
- 1. **Start the client with default settings:**
39
- ```bash
40
- stt
41
- ```
42
-
43
- 2. **Set a recorder parameter (e.g., set Silero sensitivity to 0.1):**
44
- ```bash
45
- stt --set-param silero_sensitivity 0.1
46
- ```
47
-
48
- 3. **Retrieve the value of a recorder parameter (e.g., get the current Silero sensitivity):**
49
- ```bash
50
- stt --get-param silero_sensitivity
51
- ```
52
-
53
- 4. **Call a method on the recorder (e.g., start the microphone input):**
54
- ```bash
55
- stt --call-method set_microphone
56
- ```
57
-
58
- 5. **Run in debug mode:**
59
- ```bash
60
- stt --debug
61
- ```
62
-
63
- ### WebSocket Interface:
64
- - **Control WebSocket**: Used for sending control commands like setting parameters or invoking methods.
65
- - **Data WebSocket**: Used for sending audio data for real-time transcription and receiving transcription results.
66
-
67
- The client can be used to send audio data to the server for transcription and to control the behavior of the server remotely.
68
- """
69
-
70
- import os
71
- import sys
72
- import pyaudio
73
- import numpy as np
1
+ from urllib.parse import urlparse
74
2
  from scipy import signal
75
- import logging
3
+ from queue import Queue
4
+ import numpy as np
5
+ import subprocess
6
+ import threading
76
7
  import websocket
77
8
  import argparse
78
- import json
79
- import threading
80
- import time
9
+ import pyaudio
10
+ import logging
81
11
  import struct
82
12
  import socket
83
- import subprocess
84
13
  import shutil
85
- from urllib.parse import urlparse
86
14
  import queue
87
- from queue import Queue
15
+ import json
16
+ import time
17
+ import sys
18
+ import os
88
19
 
89
20
  os.environ['ALSA_LOG_LEVEL'] = 'none'
90
21
 
@@ -108,8 +39,9 @@ class STTWebSocketClient:
108
39
  self.control_url = control_url
109
40
  self.data_url = data_url
110
41
  self.control_ws = None
111
- self.data_ws = None
112
- self.is_running = False
42
+ self.data_ws_app = None
43
+ self.data_ws_connected = None # WebSocket object that will be used for sending
44
+ self.is_running = True
113
45
  self.debug = debug
114
46
  self.file_output = file_output
115
47
  self.last_text = ""
@@ -154,13 +86,13 @@ class STTWebSocketClient:
154
86
  self.control_ws_thread.start()
155
87
 
156
88
  # Connect to data WebSocket
157
- self.data_ws = websocket.WebSocketApp(self.data_url,
158
- on_message=self.on_data_message,
159
- on_error=self.on_error,
160
- on_close=self.on_close,
161
- on_open=self.on_data_open)
89
+ self.data_ws_app = websocket.WebSocketApp(self.data_url,
90
+ on_message=self.on_data_message,
91
+ on_error=self.on_error,
92
+ on_close=self.on_close,
93
+ on_open=self.on_data_open)
162
94
 
163
- self.data_ws_thread = threading.Thread(target=self.data_ws.run_forever)
95
+ self.data_ws_thread = threading.Thread(target=self.data_ws_app.run_forever)
164
96
  self.data_ws_thread.daemon = False # Set to False to ensure proper shutdown
165
97
  self.data_ws_thread.start()
166
98
 
@@ -182,7 +114,7 @@ class STTWebSocketClient:
182
114
 
183
115
  def on_data_open(self, ws):
184
116
  self.debug_print("Data WebSocket connection opened.")
185
- self.is_running = True
117
+ self.data_ws_connected = ws # Store the connected websocket object for sending data
186
118
  self.start_recording()
187
119
 
188
120
  def on_error(self, ws, error):
@@ -208,8 +140,57 @@ class STTWebSocketClient:
208
140
  if os.name == 'nt': # Windows
209
141
  subprocess.Popen('start /min cmd /c stt-server', shell=True)
210
142
  else: # Unix-like systems
211
- subprocess.Popen(['stt-server'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True)
212
- print("STT server start command issued. Please wait a moment for it to initialize.", file=sys.stderr)
143
+ terminal_emulators = [
144
+ 'gnome-terminal',
145
+ 'x-terminal-emulator',
146
+ 'konsole',
147
+ 'xfce4-terminal',
148
+ 'lxterminal',
149
+ 'xterm',
150
+ 'mate-terminal',
151
+ 'terminator',
152
+ 'tilix',
153
+ 'alacritty',
154
+ 'urxvt',
155
+ 'eterm',
156
+ 'rxvt',
157
+ 'kitty',
158
+ 'hyper'
159
+ ]
160
+
161
+ terminal = None
162
+ for term in terminal_emulators:
163
+ if shutil.which(term):
164
+ terminal = term
165
+ break
166
+
167
+ if terminal:
168
+ terminal_exec_options = {
169
+ 'x-terminal-emulator': ['--'],
170
+ 'gnome-terminal': ['--'],
171
+ 'mate-terminal': ['--'],
172
+ 'terminator': ['--'],
173
+ 'tilix': ['--'],
174
+ 'konsole': ['-e'],
175
+ 'xfce4-terminal': ['-e'],
176
+ 'lxterminal': ['-e'],
177
+ 'alacritty': ['-e'],
178
+ 'xterm': ['-e'],
179
+ 'rxvt': ['-e'],
180
+ 'urxvt': ['-e'],
181
+ 'eterm': ['-e'],
182
+ 'kitty': [],
183
+ 'hyper': ['--command']
184
+ }
185
+
186
+ exec_option = terminal_exec_options.get(terminal, None)
187
+ if exec_option is not None:
188
+ subprocess.Popen([terminal] + exec_option + ['stt-server'], start_new_session=True)
189
+ print(f"STT server started in a new terminal window using {terminal}.", file=sys.stderr)
190
+ else:
191
+ print(f"Unsupported terminal emulator '{terminal}'. Please start the STT server manually.", file=sys.stderr)
192
+ else:
193
+ print("No supported terminal emulator found. Please start the STT server manually.", file=sys.stderr)
213
194
 
214
195
  def ensure_server_running(self):
215
196
  if not self.is_server_running():
@@ -230,14 +211,11 @@ class STTWebSocketClient:
230
211
  return False
231
212
  return True
232
213
 
233
- # Handle control messages like set_parameter, get_parameter, etc.
234
214
  def on_control_message(self, ws, message):
235
215
  try:
236
216
  data = json.loads(message)
237
- # Handle server response with status
238
217
  if 'status' in data:
239
218
  if data['status'] == 'success':
240
- # print(f"Server Response: {data.get('message', '')}")
241
219
  if 'parameter' in data and 'value' in data:
242
220
  print(f"Parameter {data['parameter']} = {data['value']}")
243
221
  elif data['status'] == 'error':
@@ -249,19 +227,16 @@ class STTWebSocketClient:
249
227
  except Exception as e:
250
228
  self.debug_print(f"Error processing control message: {e}")
251
229
 
252
- # Handle real-time transcription and full sentence updates
253
230
  def on_data_message(self, ws, message):
254
231
  try:
255
232
  data = json.loads(message)
256
- # Handle real-time transcription updates
257
- if data.get('type') == 'realtime':
233
+ message_type = data.get('type')
234
+ if message_type == 'realtime':
258
235
  if data['text'] != self.last_text:
259
236
  self.last_text = data['text']
260
237
  if not self.norealtime:
261
238
  self.update_progress_bar(self.last_text)
262
-
263
- # Handle full sentences
264
- elif data.get('type') == 'fullSentence':
239
+ elif message_type == 'fullSentence':
265
240
  if self.file_output:
266
241
  sys.stderr.write('\r\033[K')
267
242
  sys.stderr.write(data['text'])
@@ -273,6 +248,16 @@ class STTWebSocketClient:
273
248
  self.finish_progress_bar()
274
249
  print(f"{data['text']}")
275
250
  self.stop()
251
+ elif message_type in {
252
+ 'vad_detect_start',
253
+ 'vad_detect_stop',
254
+ 'recording_start',
255
+ 'recording_stop',
256
+ 'wakeword_detected',
257
+ 'wakeword_detection_start',
258
+ 'wakeword_detection_end',
259
+ 'transcription_start'}:
260
+ pass # Known message types, no action needed
276
261
 
277
262
  else:
278
263
  self.debug_print(f"Unknown data message format: {data}")
@@ -317,10 +302,11 @@ class STTWebSocketClient:
317
302
  self.finish_progress_bar()
318
303
  self.is_running = False
319
304
  self.stop_event.set()
305
+ self.debug_print("Stopping client and cleaning up resources.")
320
306
  if self.control_ws:
321
307
  self.control_ws.close()
322
- if self.data_ws:
323
- self.data_ws.close()
308
+ if self.data_ws_connected:
309
+ self.data_ws_connected.close()
324
310
 
325
311
  # Join threads to ensure they finish before exiting
326
312
  if self.control_ws_thread:
@@ -357,7 +343,7 @@ class STTWebSocketClient:
357
343
  metadata_json = json.dumps(metadata)
358
344
  metadata_length = len(metadata_json)
359
345
  message = struct.pack('<I', metadata_length) + metadata_json.encode('utf-8') + audio_data
360
- self.data_ws.send(message, opcode=websocket.ABNF.OPCODE_BINARY)
346
+ self.data_ws_connected.send(message, opcode=websocket.ABNF.OPCODE_BINARY)
361
347
  except Exception as e:
362
348
  self.debug_print(f"Error sending audio data: {e}")
363
349
  break # Exit the recording loop
@@ -389,10 +375,10 @@ class STTWebSocketClient:
389
375
  frames_per_buffer=CHUNK,
390
376
  input_device_index=self.input_device_index,
391
377
  )
392
- self.debug_print(f"Audio recording initialized successfully at {self.device_sample_rate} Hz")
378
+ self.debug_print(f"Audio recording initialized successfully at {self.device_sample_rate} Hz, device index {self.input_device_index}")
393
379
  return True
394
380
  except Exception as e:
395
- self.debug_print(f"Failed to initialize audio stream at {self.device_sample_rate} Hz: {e}")
381
+ self.debug_print(f"Failed to initialize audio stream at {self.device_sample_rate} Hz, device index {self.input_device_index}: {e}")
396
382
  return False
397
383
 
398
384
  except Exception as e:
@@ -443,9 +429,7 @@ class STTWebSocketClient:
443
429
  self.command_thread.start()
444
430
 
445
431
  def command_processor(self):
446
- # print(f"Starting command processor")
447
432
  self.debug_print(f"Starting command processor")
448
- #while self.is_running and not self.stop_event.is_set():
449
433
  while not self.stop_event.is_set():
450
434
  try:
451
435
  command = self.commands.get(timeout=0.1)
@@ -455,14 +439,12 @@ class STTWebSocketClient:
455
439
  self.get_parameter(command['parameter'])
456
440
  elif command['type'] == 'call_method':
457
441
  self.call_method(command['method'], command.get('args'), command.get('kwargs'))
458
- except queue.Empty: # Use queue.Empty instead of Queue.Empty
459
- continue # Queue was empty, just loop again
442
+ except queue.Empty:
443
+ continue
460
444
  except Exception as e:
461
445
  self.debug_print(f"Error in command processor: {e}")
462
- # finally:
463
- #print(f"Leaving command processor")
464
- self.debug_print(f"Leaving command processor")
465
446
 
447
+ self.debug_print(f"Leaving command processor")
466
448
 
467
449
  def add_command(self, command):
468
450
  self.commands.put(command)
@@ -502,7 +484,6 @@ def main():
502
484
  if args.set_param:
503
485
  for param, value in args.set_param:
504
486
  try:
505
- # Attempt to parse the value to the appropriate type
506
487
  if '.' in value:
507
488
  value = float(value)
508
489
  else:
@@ -552,3 +533,4 @@ def main():
552
533
 
553
534
  if __name__ == "__main__":
554
535
  main()
536
+
@@ -68,7 +68,7 @@ The server will broadcast real-time transcription updates to all connected clien
68
68
 
69
69
  extended_logging = True
70
70
  send_recorded_chunk = False
71
- log_incoming_chunks = False
71
+ log_incoming_chunks = True
72
72
  stt_optimizations = False
73
73
 
74
74
 
@@ -259,23 +259,23 @@ def on_transcription_start(loop):
259
259
  })
260
260
  asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
261
261
 
262
- def on_realtime_transcription_update(text, loop):
263
- # Send real-time transcription updates to the client
264
- text = preprocess_text(text)
265
- message = json.dumps({
266
- 'type': 'realtime_update',
267
- 'text': text
268
- })
269
- asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
270
-
271
- def on_recorded_chunk(chunk, loop):
272
- if send_recorded_chunk:
273
- bytes_b64 = base64.b64encode(chunk.tobytes()).decode('utf-8')
274
- message = json.dumps({
275
- 'type': 'recorded_chunk',
276
- 'bytes': bytes_b64
277
- })
278
- asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
262
+ # def on_realtime_transcription_update(text, loop):
263
+ # # Send real-time transcription updates to the client
264
+ # text = preprocess_text(text)
265
+ # message = json.dumps({
266
+ # 'type': 'realtime_update',
267
+ # 'text': text
268
+ # })
269
+ # asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
270
+
271
+ # def on_recorded_chunk(chunk, loop):
272
+ # if send_recorded_chunk:
273
+ # bytes_b64 = base64.b64encode(chunk.tobytes()).decode('utf-8')
274
+ # message = json.dumps({
275
+ # 'type': 'recorded_chunk',
276
+ # 'bytes': bytes_b64
277
+ # })
278
+ # asyncio.run_coroutine_threadsafe(audio_queue.put(message), loop)
279
279
 
280
280
  # Define the server's arguments
281
281
  def parse_arguments():
@@ -346,7 +346,7 @@ def parse_arguments():
346
346
  parser.add_argument('--data_port', type=int, default=8012,
347
347
  help='The port number used for the data WebSocket connection. Data connections are used to send audio data and receive transcription updates in real time. Default is port 8012.')
348
348
 
349
- parser.add_argument('--wake_words', type=str, default="Jarvis",
349
+ parser.add_argument('--wake_words', type=str, default="",
350
350
  help='Specify the wake word(s) that will trigger the server to start listening. For example, setting this to "Jarvis" will make the system start transcribing when it detects the wake word "Jarvis". Default is "Jarvis".')
351
351
 
352
352
  parser.add_argument('--wake_words_sensitivity', type=float, default=0.5,
@@ -358,7 +358,7 @@ def parse_arguments():
358
358
  parser.add_argument('--wake_word_activation_delay', type=float, default=20,
359
359
  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.')
360
360
 
361
- parser.add_argument('--wakeword_backend', type=str, default='pvporcupine',
361
+ parser.add_argument('--wakeword_backend', type=str, default='none',
362
362
  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".')
363
363
 
364
364
  parser.add_argument('--openwakeword_model_paths', type=str, nargs='*',
@@ -619,7 +619,7 @@ async def main_async():
619
619
  'on_wakeword_detection_start': make_callback(loop, on_wakeword_detection_start),
620
620
  'on_wakeword_detection_end': make_callback(loop, on_wakeword_detection_end),
621
621
  'on_transcription_start': make_callback(loop, on_transcription_start),
622
- 'on_recorded_chunk': make_callback(loop, on_recorded_chunk),
622
+ # 'on_recorded_chunk': make_callback(loop, on_recorded_chunk),
623
623
  'no_log_file': True, # Disable logging to file
624
624
  'use_extended_logging': args.use_extended_logging,
625
625
  }
@@ -1,34 +1,34 @@
1
- import setuptools
2
-
3
- with open("README.md", "r") as fh:
4
- long_description = fh.read()
5
-
6
- # Read requirements.txt
7
- with open('requirements.txt') as f:
8
- requirements = f.read().splitlines()
9
-
10
- setuptools.setup(
11
- name="RealtimeSTT",
12
- version="0.3.4",
13
- author="Kolja Beigel",
14
- author_email="kolja.beigel@web.de",
15
- description="A fast Voice Activity Detection and Transcription System",
16
- long_description=long_description,
17
- long_description_content_type="text/markdown",
18
- url="https://github.com/KoljaB/RealTimeSTT",
19
- packages=setuptools.find_packages(),
20
- classifiers=[
21
- "Programming Language :: Python :: 3",
22
- "License :: OSI Approved :: MIT License",
23
- "Operating System :: OS Independent",
24
- ],
25
- python_requires='>=3.6',
26
- install_requires=requirements,
27
- 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",
28
- entry_points={
29
- 'console_scripts': [
30
- 'stt-server=server.stt_server:main',
31
- 'stt=server.stt_cli_client:main',
32
- ],
33
- },
34
- )
1
+ import setuptools
2
+
3
+ with open("README.md", "r") as fh:
4
+ long_description = fh.read()
5
+
6
+ # Read requirements.txt
7
+ with open('requirements.txt') as f:
8
+ requirements = f.read().splitlines()
9
+
10
+ setuptools.setup(
11
+ name="RealtimeSTT",
12
+ version="0.3.5",
13
+ author="Kolja Beigel",
14
+ author_email="kolja.beigel@web.de",
15
+ description="A fast Voice Activity Detection and Transcription System",
16
+ long_description=long_description,
17
+ long_description_content_type="text/markdown",
18
+ url="https://github.com/KoljaB/RealTimeSTT",
19
+ packages=setuptools.find_packages(include=["RealtimeSTT", "RealtimeSTT_server"]),
20
+ classifiers=[
21
+ "Programming Language :: Python :: 3",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ ],
25
+ python_requires='>=3.6',
26
+ install_requires=requirements,
27
+ 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",
28
+ entry_points={
29
+ 'console_scripts': [
30
+ 'stt-server=RealtimeSTT_server.stt_server:main',
31
+ 'stt=RealtimeSTT_server.stt_cli_client:main',
32
+ ],
33
+ },
34
+ )
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- stt = server.stt_cli_client:main
3
- stt-server = server.stt_server:main
@@ -1,2 +0,0 @@
1
- RealtimeSTT
2
- server
File without changes
File without changes