spatialaudio / python-sounddevice

:sound: Play and Record Sound with Python :snake:
https://python-sounddevice.readthedocs.io/
MIT License
980 stars 145 forks source link

Using sd.InputStream.read and sd.rec at the same time from the same microphone/device #410

Open Terminazor opened 2 years ago

Terminazor commented 2 years ago

Hi,

i have a program with two containers/two python scripts for an AI application. One is using sd.InputStream and the .read() function to continuously read chunks out of the audiostream and analyse it. And the other container is using the sd.rec() function to record/save new training samples, but only when the user gives a signal (aka send a mqtt message). My question is, is it somehow possible to use .read() AND occassionally .rec() with the same input device/microphone?

Relevant part of the analyze.py file:

def read_chunks(frames):
  # Read chunks from the audio_stream where each chunk has length "frames"
  global audio_stream
  while True:
      chunk, overflowed = audio_stream.read(frames)
      # do stuff with chunk

def main():
  #start all relevant threads and keep the main function alive
  audio_stream = sd.InputStream(device = input_device_id, samplerate = sample_rate_of_recording_device, channels = channels, dtype = dtype)
  audio_stream.start()
  thread_read_chunks = threading.Thread(target = read_chunks, args = (frames,))
  thread_read_chunks.start()
  while True:
      # do nothing

Relevant part of recording.py:

def on_message_record_sound(client, userdata, msg):
    myrecording = sd.rec(frames, samplerate = sample_rate_of_recording_device, channels = 1, device = device_to_record, dtype = recording_type)
    sd.wait()  # Wait until recording is finished
    # save myrecording locally

def main():
  mqttsender.client.message_callback_add(mqtt_broker_record_sound, on_message_record_sound)
  while True:
    # do nothing
mgeier commented 2 years ago

is it somehow possible to use .read() AND occassionally .rec() with the same input device/microphone?

Yes, but depending on the host API, the two streams might not be able to exist at the same time.

BTW, using a stream in a threading.Thread is not easy to get right, you can search the issues here for "threading" to see some potential problems.

I normally find it easier to use the "callback" mode. This way, a separate thread it automatically created by the PortAudio library, and in many cases it is not necessary to explicitly create a threading.Thread with Python.

I would try to create a single callback function that takes care of both the analyzing and the on-demand recording.

You can find an example for this in examples/rec_gui.py, where a meter is continuously showing the sound level, but the signal is recorded only on demand.