Closed microbit-carlos closed 2 months ago
Copying comment https://github.com/microbit-foundation/micropython-microbit-v2/pull/163#issuecomment-2068519700 here:
This needs discussion. copyfrom() actually allows copying from any object with the buffer protocol (eg bytes, memoryview) and they won't necessarily have the used_size attribute.
This method should probably just set used_size to the total bytes copied fro the buffer-like object. The only issue there is copying from another AudioFrame it will copy the total allocated size, not the used_size, of the other AudioFrame.
Yes, I think AudioFrame.copyfrom()
should set used_size
to to the last byte copied from the input argument (any buffer-like object).
So, if we start with a long AudioFrame
that has data all the way to the end, and then we copy a short buffer into the AudioFrame
, then we would end up with the used_size
marker at length of the small buffer.
But I think this makes sense only if we also provide accessors to the used_marker
(https://github.com/microbit-foundation/micropython-microbit-v2/issues/196), otherwise there is "magic" that cannot be changed without odd workarounds (like assigning to itself the last indexable value, e.g audio_frame[len(audio_frame) - 1] = audio_frame[len(audio_frame) - 1]
vs audio_frame.set_marker(len(audio_frame))
).
The question would be, do we special-case copying from another AudioFrame
?
We'd have a few options, for all of these we can consider the following set up code:
af_long = audio.AudioFrame(duration=5000)
af_short = audio.AudioFrame(duration=2000)
microphone.record_into(af_short, wait=False)
sleep(1000) # Record for only 1 second, af_short is only half filled
microphone.stop_recording()
used_size
value
af_long.copyfrom(af_short)
audio.play(af_long) # Plays for 1 second
af_long.copyfrom(af_short)
audio.play(af_long) # Plays for 2 second, 1st second audio data, 2nd second empty
af_long.set_position(af_short.get_position())
audio.play(af_long) # Plays for 1 second
used_marker
af_long.copyfrom(af_short)
audio.play(af_long) # Plays for 1 second
In my opinion, for a user option 1. and 3. make most sense for easy-of-use. Option 2. is not too bad, as it makes sense if you already know about the marker, however I think it's a lot more likely that the users would want 1 second playback instead of 2, so making that default makes sense.
From options 1. and 3. , I'm a lot more inclined on option 1., because option 3. limiting how much data is copied doesn't feel quite right.
This might also need to be discussed with https://github.com/microbit-foundation/micropython-microbit-v2/issues/194 in mind, as one of the suggestions is to add an extra argument to have AudioFrame.copyfrom(buffer, index)
.
af_long = AudioFrame(duration=5000)
af_short = AudioFrame(duration=2000)
while True:
if button_a.is_pressed():
microphone.record_into(af_short, wait=False)
while button_a.is_pressed():
pass
microphone.stop()
af_long.copyfrom(af_short, index=af_long.get_position())
if button_b.is_pressed():
audio.play(large_af)
sleep(200)
So in these cases we would need to make sure that when calculating the user_size
position, it also needs to be offset from the index
point (from AudioFrame.copyfrom(buffer, index)
).
This can be separated in two parts:
copyfrom()
should move the marker based on the size of the data copiedSo, 1. can be implemented already, and 2 will be considered as part of the new proposal:
We've changed the implementation and the new audio objects do not have an "internal marker":
Using the latest version (at the time of writing) of the recording & playback branch: https://github.com/microbit-foundation/micropython-microbit-v2/commit/0b06914c71c18533da90df85230ac198578669bf Hex: https://github.com/microbit-foundation/micropython-microbit-v2/actions/runs/8416237764?pr=163
If we manually edit the last byte in
b
it then does play a short clip: