Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyAudio Responsive Recording

I've seen the recording tutorial on the PyAudio website for recording a fixed length recording, but I was wondering how I could do the same with a non-fixed recording? Bascially, I want to create buttons to start and end the recording but I haven't found anything on the matter. Any ideas, and I am not looking for an alternative library?

like image 707
Malik Brahimi Avatar asked Feb 11 '23 23:02

Malik Brahimi


1 Answers

Best is to use the non-blocking way of recording, i.e. you provide a callback function that gets called from the moment you start the stream and keeps getting called for every block/buffer that gets processed until you stop the stream.

In that callback function you check for a boolean for example, and when it is true you write the incoming buffer to a datastructure, when it is false you ignore the incoming buffer. This boolean can be set from clicking a button for example.

EDIT: look at the example of wire audio: http://people.csail.mit.edu/hubert/pyaudio/#wire-callback-example The stream is opened with an argument

stream_callback=my_callback

Where my_callback is a regular function declared as

def my_callback(in_data, frame_count, time_info, status)

This function will be called every time a new buffer is available. in_data contains the input, which you want to record. In this example, in_data just gets returned in a tuple together with pyaudio.paContinue. Which means that the incoming buffer from the input device is put/copied back into the output buffer sent the the output device (its the same device, so its actually routing input to output aka wire). See the api docs for a bit more explanation: http://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.PyAudio.open

So in this function you can do something like (this is an extract from some code I've written, which is not complete: I use some functions not depicted. Also I play a sinewave on one channel and noise on the other in 24bit format.):


record_on = False
playback_on = False

recorded_frames = queue.Queue()

def callback_play_sine(in_data, frame_count, time_info, status):
    if record_on:
        global recorded_frames
        recorded_frames.put(in_data)

    if playback_on:
        left_channel_data = mysine.next_block(frame_count) * MAX_INT24 * gain
        right_channel_data = ((np.random.rand(frame_count) * 2) - 1) * MAX_INT24 * gain
        data = interleave_channels(max_nr_of_channels, (left_output_channel, left_channel_data), (right_output_channel, right_channel_data))
        data = convert_int32_to_24bit_bytestream(data)
    else:
        data = np.zeros(frame_count*max_nr_of_channels).tostring()

    if stop_callback:
        callback_flag = pyaudio.paComplete
    else:
        callback_flag = pyaudio.paContinue

    return data, callback_flag

You can then set record_on and playback_on to True or False from another part of your code while the stream is open/running, causing recording and playback to start or stop independently without interrupting the stream. I copy the in_data in a (threadsafe) queue, which is used by another thread to write to disk there, else the queue will get big after a while.

BTW: pyaudio is based on portaudio, which has much more documentation and helpful tips. For example (http://portaudio.com/docs/v19-doxydocs/writing_a_callback.html): the callback function has to finish before a new buffer is presented, else buffers will be lost. So writing to a file inside the callback function usually not a good idea. (though writing to a file gets buffered and I don't know if it blocks when its written to disk eventually)

like image 196
Emile Vrijdags Avatar answered Feb 16 '23 04:02

Emile Vrijdags