KoljaB / RealtimeSTT

A robust, efficient, low-latency speech-to-text library with advanced voice activity detection, wake word activation and instant transcription.
MIT License
2.09k stars 190 forks source link

Try reconnection mic. #115

Open sangheonEN opened 2 months ago

sangheonEN commented 2 months ago

https://github.com/user-attachments/assets/3f2f5a93-ba1a-4043-b670-3fb6593b8f66

When I tested it by disconnecting and turning on the microphone connected via Bluetooth like capture, an error occurred and the voice recognition program stopped.

So I am attaching my modified code. Is there anyone who can verify this code and help me update it? Do I have to make pull requests because I don’t know much about GitHub?

image

ERROR COMMENT :

ERROR:root:Error during recording: [Errno -9999] Unanticipated host error ERROR:root:Microphone disconnected. Attempting to reconnect... Process Process-2: Traceback (most recent call last): File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\RealtimeSTT\audio_recorder.py", line 893, in _audio_data_worker data = stream.read(buffer_size) ^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\pyaudio__init__.py", line 570, in read return pa.read_stream(self._stream, num_frames, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: [Errno -9999] Unanticipated host error

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\RealtimeSTT\audio_recorder.py", line 902, in _audio_data_worker if stream is not None and stream.is_active(): ^^^^^^^^^^^^^^^^^^ File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\pyaudio__init__.py", line 508, in is_active return pa.is_stream_active(self._stream) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: Stream not open

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "C:\Users\admin\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\process.py", line 314, in _bootstrap self.run() File "C:\Users\admin\AppData\Local\Programs\Python\Python311\Lib\multiprocessing\process.py", line 108, in run self._target(*self._args, **self._kwargs) File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\RealtimeSTT\audio_recorder.py", line 923, in _audio_data_worker if stream is not None and stream.is_active(): ^^^^^^^^^^^^^^^^^^ File "C:\Users\admin\Desktop\STT\RealtimeSTT\realtimestt_env\Lib\site-packages\pyaudio__init__.py", line 508, in is_active return pa.is_stream_active(self._stream) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OSError: Stream not open

Code I modified : modify "_audio_data_worker" function

@staticmethod
def _audio_data_worker(audio_queue,
                    sample_rate,
                    buffer_size,
                    input_device_index,
                    shutdown_event,
                    interrupt_stop_event,
                    use_microphone):
    """
    Worker method that handles the audio recording process, with exception handling for microphone disconnection.
    """
    audio_interface = pyaudio.PyAudio()
    stream = None

    def start_stream():
        nonlocal stream, input_device_index  # Ensure input_device_index is updated here as well
        while True:
            try:
                # Check and assign the input device index if it is not set
                if input_device_index is None: 
                    default_device = audio_interface.get_default_input_device_info()
                    input_device_index = default_device['index']

                stream = audio_interface.open(
                    rate=sample_rate,
                    format=pyaudio.paInt16,
                    channels=1,
                    input=True,
                    frames_per_buffer=buffer_size,
                    input_device_index=input_device_index
                )
                logging.info("Microphone connected successfully.")
                break  # Exit the loop once the microphone is successfully connected
            except Exception as e:
                logging.error(f"Microphone connection failed: {e}. Retrying...")
                input_device_index = None
                time.sleep(3)  # Wait for 5 seconds before retrying

    # Initialize the stream for the first time
    start_stream()

    logging.debug("Audio recording (pyAudio input stream) initialized successfully")

    try:
        while not shutdown_event.is_set():
            try:
                data = stream.read(buffer_size)
            except OSError as e:
                logging.error(f"Error during recording: {e}")
                if e.errno == pyaudio.paInputOverflowed:
                    logging.warning("Input overflowed. Frame dropped.")
                    continue
                elif e.errno == pyaudio.paUnanticipatedHostError or e.errno == -9999:
                    logging.error("Microphone disconnected. Attempting to reconnect...")
                    # if mic disconnection -> stream stop and close

                    # close()를 호출한 후 다시 스트리밍을 시작하려면, PyAudio.open()을 호출
                    stream.close()

                    input_device_index = None

                    # restream start
                    start_stream()  # Retry to reconnect the microphone
                    continue
                else:
                    logging.error(f"Error during recording: {e}")
                    tb_str = traceback.format_exc()
                    print(f"Traceback: {tb_str}")
                    print(f"Error: {e}")
                    continue

            # If microphone is active, send the data to the audio queue
            if use_microphone.value:
                audio_queue.put(data)

    except KeyboardInterrupt:
        interrupt_stop_event.set()
        logging.debug("Audio data worker process finished due to KeyboardInterrupt")
    finally:
        if stream is not None:
            stream.stop_stream()
            stream.close()
        audio_interface.terminate()