Open microbit-carlos opened 2 months ago
Doing continuous and smooth playback/recording is a good goal to achieve. And having a queue as suggested above is a neat way to do it. There could be instead a method like audio.wait_finished_playing()
, but calling that before the next audio.play()
will probably lead to small gaps in the output. So a queue is a good idea.
Bit actually I think the feature is orthogonal to the existing wait
keyword argument. Because you may want to queue and block. So consider instead a new keyword argument called queue
:
audio.play(frame1, wait=False) # start playing and return
audio.play(frame2, queue=True, wait=False) # wait for previous frame, then play
audio.play(frame3, queue=True) # wait for previous frame, then play and block
The queue
argument means "wait for any existing playback/recording to finish and then immediately start this next one".
But, you can already do gapless playback by passing in a generator to the play
function. The audio subsystem will continue to get frames from the generator until the generator is exhausted. This is a very flexible way of generating audio frames. For example:
def generator():
yield audioframe_1
yield audioframe_2
audio.play(generator())
Or a continuous streaming example:
frames = collections.deque((), 4) # holds a queue of frames to play
def generator():
blank_frame = audio.AudioFrame()
while True:
if not frames:
# no frames ready, just play a blank frame
yield blank_frame
else:
# pop a frame and play it
yield frame.popleft()
# start the playback
audio.play(generator(), wait=False)
# generate audio frames
while True:
frames.append(get_frame_from_radio())
It might be a good idea to extend this generator functionality to microphone recording, where the generator yields the next frame to record into. When the generator is exhausted the recording stops.
Multiple subsequent calls to
audio.play(buffer, wait=False)
andmicrophone.record_into(buffer, wait=False)
will cancel the current playback/recording and start the new one immediately.This is very likely the user expectation, and if we wanted to block until the previous call finishes we can always wait with the
audio.is_playing()
andmicrohone.is_recording()
functions.However, when first building a programmes using
wait=False
, we found ourselves in situations where "blocking with a queue of 1" was useful. This is the approached we ended up following in CODAL as well for some of the async audio functionality.For example, to illustrate what I mean I'll change the
wait
parameter with a new value:So, for a loop like this one:
We might end up having to do something like:
And we think that some of the audio clicks we hear when trying to constantly transmit and play audio data via radio (walkie-talkie projects) might be produced between the
while is_recording()
and therecord_into()
, or in the case of playback, betweenwhile is_playing()
andaudio.play()
.