Skip to content

Commit d37bfa0

Browse files
docs: Update Recording & Playback to use AudioRecording & AudioTrack.
1 parent 7299d1f commit d37bfa0

File tree

3 files changed

+189
-55
lines changed

3 files changed

+189
-55
lines changed

docs/audio.rst

+163-36
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,22 @@ There are three different kinds of audio sources that can be played using the
2424
my_effect = audio.SoundEffect(freq_start=400, freq_end=2500, duration=500)
2525
audio.play(my_effect)
2626

27-
3. `Audio Frames <#audioframe>`_, an instance or an iterable (like a list or
27+
3. `Audio Recordings <#audiorecording-audiotrack-v2>`_, an object that can
28+
be used to record audio from the microphone::
29+
30+
recording = audio.AudioRecording(duration=4000)
31+
microphone.record_into(recording)
32+
audio.play(recording)
33+
34+
4. `Audio Tracks <#audiorecording-audiotrack-v2>`_, a way to point to a portion
35+
of the data in an ``AudioRecording`` or a ``bytearray`` and/or modify it::
36+
37+
recording = audio.AudioRecording(duration=4000)
38+
microphone.record(recording)
39+
track = AudioTrack(recording)[1000:3000]
40+
audio.play(track)
41+
42+
5. `Audio Frames <#audioframe>`_, an instance or an iterable (like a list or
2843
generator) of Audio Frames, which are lists of samples with values
2944
from 0 to 255::
3045

@@ -49,6 +64,10 @@ Functions
4964
be found in the `Built in sounds <#built-in-sounds-v2>`_ section.
5065
- ``SoundEffect``: A sound effect, or an iterable of sound effects,
5166
created via the :py:meth:`audio.SoundEffect` class
67+
- ``AudioRecording``: An instance of ``AudioRecording`` as described
68+
in the `AudioRecording <#audiorecording-audiotrack-v2>`_ section
69+
- ``AudioTrack``: An instance of ``AudioTrack`` as described in the
70+
`AudioTrack <#audiorecording-audiotrack-v2>`_ section
5271
- ``AudioFrame``: An instance or an iterable of ``AudioFrame``
5372
instances as described in the
5473
`AudioFrame Technical Details <#technical-details>`_ section
@@ -226,47 +245,163 @@ Sound Effects Example
226245
:code: python
227246

228247

229-
AudioFrame
230-
==========
248+
AudioRecording & AudioTrack **V2**
249+
==================================
231250

232-
.. py:class::
233-
AudioFrame(duration=-1, rate=7812)
251+
There are two classes that can contain (or point to) audio data
252+
and an associated sampling rate:
234253

235-
An ``AudioFrame`` object is a list of samples, each of which is an unsigned
236-
byte (whole number between 0 and 255).
254+
- ``AudioRecording`` contains its own buffer, it's initialised with a size
255+
defined in time, and it's the object type that ``microphone.record()``
256+
returns.
257+
- ``AudioTrack`` does not hold its own buffer and instead points to a buffer
258+
externally created. This buffer could be an ``AudioRecording``, or a basic
259+
type like a ``bytearray``. It's similar to a
260+
`memoryview <https://docs.micropython.org/en/v1.9.3/pyboard/reference/speed_python.html#arrays>`_
261+
and it can be used to easily chop a portion of the audio data in the
262+
``AudioRecording`` or to modify its contents.
237263

238-
The number of samples in an AudioFrame will depend on the
239-
``rate`` (number of samples per second) and ``duration`` parameters.
240-
The total number of samples will always be a round up multiple of 32.
264+
AudioRecording
265+
--------------
241266

242-
On micro:bit V1 the constructor does not take any arguments,
243-
and an AudioFrame instance is always 32 bytes.
267+
.. py:class::
268+
AudioRecording(duration, rate=7812)
269+
270+
The ``AudioRecording`` object contains audio data and the sampling rate
271+
associated to it.
244272

245-
:param duration: (**V2**) Indicates how many milliseconds of audio this
273+
The size of the internal buffer will depend on the ``rate``
274+
(number of samples per second) and ``duration`` parameters.
275+
276+
:param duration: Indicates how many milliseconds of audio this
246277
instance can store.
247-
:param rate: (**V2**) The sampling rate at which data will be stored
278+
:param rate: The sampling rate at which data will be stored
248279
via the microphone, or played via the ``audio.play()`` function.
249280

250281
.. py:function:: set_rate(sample_rate)
251282
252-
(**V2 only**) Configure the sampling rate associated with the data
253-
in the ``AudioFrame`` instance.
283+
Configure the sampling rate associated with the data in the
284+
``AudioRecording`` instance.
285+
286+
:param sample_rate: The sample rate to set.
287+
288+
.. py:function:: get_rate()
289+
290+
Return the configured sampling rate for this
291+
``AudioRecording`` instance.
292+
293+
:return: The configured sample rate.
294+
295+
.. py:function:: copy()
296+
297+
:returns: a copy of the ``AudioRecording``.
298+
299+
.. py:function:: track(start_ms=0, end_ms=-1)
300+
301+
Create an `AudioTrack <#audio.AudioTrack>`_ instance from a portion of
302+
the data in this ``AudioRecording`` instance.
303+
304+
:param start_ms: Where to start of the track in milliseconds.
305+
:param end_ms: The end of the track in milliseconds.
306+
If the default value of ``-1`` is provided it will end the track
307+
at the end of the AudioRecording.
308+
309+
When an AudioRecording is used to record data from the microphone,
310+
increasing the sampling rate increases the sound quality,
311+
but it also increases the amount of memory used.
312+
313+
During playback, increasing the sampling rate speeds up the sound
314+
and decreasing the sample rate slows it down.
315+
316+
AudioTrack
317+
----------
318+
319+
An ``AudioTrack`` can be created from an ``AudioRecording`` or ``bytearray``
320+
and individual bytes can be accessed and modified like elements in a list.
321+
322+
This class is useful to modify the audio data in an ``AudioRecording`` or
323+
to create precisely sized audio data buffers for sending and receiving them
324+
via communication protocols like radio or serial.
325+
326+
.. py:class::
327+
AudioTrack(buffer, rate=7812)
328+
329+
The ``AudioTrack`` object points to the data provided by the input buffer,
330+
which can be an ``AudioRecording``, or a buffer-like object like a
331+
``bytearray``.
332+
It also contains its own sampling rate, so multiple ``AudioTrack``
333+
instances pointing to the same buffer can have different rates and won't
334+
affect the rate of the original buffer.
335+
336+
:param buffer: The buffer containing the audio data.
337+
:param rate: The sampling rate at which data will be stored
338+
via the microphone, or played via the ``audio.play()`` function.
254339

255-
For recording from the microphone, increasing the sampling rate
256-
increases the sound quality, but reduces the length of audio it
257-
can store.
258-
During playback, increasing the sampling rate speeds up the sound
259-
and decreasing it slows it down.
340+
.. py:function:: set_rate(sample_rate)
341+
342+
Configure the sampling rate associated with the data in the
343+
``AudioTrack`` instance.
260344

261345
:param sample_rate: The sample rate to set.
262346

263347
.. py:function:: get_rate()
264348
265-
(**V2 only**) Return the configured sampling rate for this
266-
``AudioFrame`` instance.
349+
Return the configured sampling rate for this
350+
``AudioTrack`` instance.
267351

268352
:return: The configured sample rate.
269353

354+
.. py:function:: copyfrom(other)
355+
356+
Overwrite the data in this ``AudioTrack`` with the data from another
357+
``AudioTrack``, ``AudioRecording`` or buffer-like object like
358+
a ``bytes`` or ``bytearray`` instance.
359+
360+
:param other: Buffer-like instance from which to copy the data.
361+
362+
Example
363+
-------
364+
365+
::
366+
367+
from microbit import *
368+
369+
# An AudioRecording holds the audio data
370+
recording = audio.AudioRecording(duration=4000)
371+
372+
# AudioTracks point to a portion of the data in the AudioRecording
373+
# We can obtain the an AudioTrack from the AudioRecording.track() method
374+
first_half = recording.track(end_ms=2000)
375+
# Or we can create an AudioTrack from an AudioRecording and slice it
376+
full_track = audio.AudioTrack(recording)
377+
second_half = full_track[full_track.length() // 2:]
378+
379+
while True:
380+
if button_a.is_pressed():
381+
# We can record directly inside the AudioRecording
382+
microphone.record(recording)
383+
if button_b.is_pressed():
384+
audio.play(recording, wait=False)
385+
# The rate can be changed while playing
386+
first_half.set_rate(
387+
scale(accelerometer.get_x(), from_=(-1000, 1000), to=(3_000, 30_000))
388+
)
389+
if pin_logo.is_touched():
390+
# We can also play the AudioTrack pointing to the AudioRecording
391+
audio.play(first_half)
392+
393+
394+
AudioFrame
395+
==========
396+
397+
.. py:class::
398+
AudioFrame
399+
400+
An ``AudioFrame`` object is a list of 32 samples each of which is an unsigned byte
401+
(whole number between 0 and 255).
402+
403+
It takes just over 4 ms to play a single frame.
404+
270405
.. py:function:: copyfrom(other)
271406
272407
Overwrite the data in this ``AudioFrame`` with the data from another
@@ -281,21 +416,13 @@ Technical Details
281416
You don't need to understand this section to use the ``audio`` module.
282417
It is just here in case you wanted to know how it works.
283418

284-
The ``audio.play()`` function can consume an instance or iterable
285-
(sequence, like list or tuple, or generator) of ``AudioFrame`` instances,
286-
The ``AudioFrame`` default playback rate is 7812 Hz, and can be configured
287-
at any point with the ``AudioFrame.set_rate()`` method.
288-
The ``AudioFrame.set_rate()`` also works while the ``AudioFrame`` is being
289-
played, which will affect the playback speed.
290-
291-
Each ``AudioFrame`` instance is 32 samples by default, but it can be
292-
configured to a different size via constructor parameters.
293-
294-
So, for example, playing 32 samples at 7812 Hz takes just over 4 milliseconds
419+
The ``audio`` module can consumes an iterable (sequence, like list or tuple, or
420+
generator) of ``AudioFrame`` instances, each 32 samples at 7812.5 Hz,
421+
which take just over 4 milliseconds to play each frame
295422
(1/7812.5 * 32 = 0.004096 = 4096 microseconds).
296423

297-
The function ``play()`` fully copies all data from each ``AudioFrame`` before
298-
it calls ``next()`` for the next frame, so a sound source can use the same
424+
The function ``play`` fully copies all data from each ``AudioFrame`` before it
425+
calls ``next()`` for the next frame, so a sound source can use the same
299426
``AudioFrame`` repeatedly.
300427

301428
The ``audio`` module has an internal 64 sample buffer from which it reads

docs/microbit_micropython_api.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ The Microphone is accessed via the `microphone` object::
108108
set_threshold(128)
109109
# Returns a representation of the sound pressure level in the range 0 to 255.
110110
sound_level()
111-
# Record audio into a new `AudioFrame`
112-
record(duration, rate=7812)
113-
# Record audio into an existing `AudioFrame`
114-
record_into(buffer, rate=7812)
111+
# Record audio into a new `AudioRecording`
112+
recording = record(duration, rate=7812)
113+
# Record audio into an existing `AudioRecording`
114+
record_into(recording, rate=7812)
115115
# Returns `True` if the microphone is currently recording audio
116116
is_recording()
117117
# Stop any active audio recording

docs/microphone.rst

+22-15
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ accessible via variables in ``microbit.SoundEvent``:
3131
Recording
3232
=========
3333

34-
The microphone can record audio into an :doc:`AudioFrame <audio>`, which can
35-
then be played with the ``audio.play()`` function.
34+
The microphone can record audio into an :doc:`AudioRecording <audio>`,
35+
which can then be played with the ``audio.play()`` function.
3636

3737
Audio sampling is the process of converting sound into a digital format.
3838
To do this, the microphone takes samples of the sound waves at regular
@@ -41,15 +41,17 @@ intervals. The number of samples recorded per second is known as the
4141
quality, but as more samples are saved, it also consumes more memory.
4242

4343
The microphone sampling rate can be configured during sound recording via
44-
the ``AudioFrame.rate()`` method functions.
44+
the :py:meth:`AudioRecording.set_rate()<audio.AudioRecording.set_rate>` or
45+
:py:meth:`AudioTrack.set_rate()<audio.AudioTrack.set_rate>` methods.
4546

4647
At the other side, the audio playback sampling rate indicates how many samples
4748
are played per second. So if audio is played back with a higher sampling rate
4849
than the rate used during recording, then the audio will sound speeded up.
4950
If the playback sampling rate is twice the recording rate, the sound will take
5051
half the time to be played to completion. Similarly, if the playback rate
51-
is halved, it will play half as many samples per second, and so it will
52-
take twice as long to play the same amount of samples.
52+
is halved, it will play half as many samples per second, so it will
53+
take twice as long to play the same amount of samples, and it will sound
54+
slowed down.
5355

5456
How do you think a voice recording will sound if the playback rate is
5557
increased or decreased? Let's try it out!::
@@ -141,7 +143,7 @@ Functions
141143

142144
.. py:function:: record(duration, rate=7812)
143145
144-
Record sound into an ``AudioFrame`` for the amount of time indicated by
146+
Record sound into an ``AudioRecording`` for the amount of time indicated by
145147
``duration`` at the sampling rate indicated by ``rate``.
146148

147149
The amount of memory consumed is directly related to the length of the
@@ -155,17 +157,22 @@ Functions
155157

156158
:param duration: How long to record in milliseconds.
157159
:param rate: Number of samples to capture per second.
158-
:returns: An ``AudioFrame`` with the sound samples.
160+
:returns: An ``AudioRecording`` with the sound samples.
159161

160-
.. py:function:: record_into(buffer, rate=7812, wait=True)
162+
.. py:function:: record_into(buffer, wait=True)
161163
162-
Record sound into an existing ``AudioFrame`` until it is filled,
163-
or the ``stop_recording()`` function is called.
164+
Record sound into an existing ``AudioRecording`` or ``AudioTrack``
165+
until it is filled, or the ``stop_recording()`` function is called.
164166

165-
:param buffer: An ``AudioFrame`` to record sound.
166-
:param rate: Number of samples to capture per second.
167+
This function also returns an ``AudioTrack`` created from the provided
168+
input buffer, which length matches the recording duration.
169+
This is useful when recording with ``wait`` set to ``False``, and the
170+
recording is stopped before the input buffer is filled.
171+
172+
:param buffer: ``AudioRecording`` or ``AudioTrack`` to record sound into.
167173
:param wait: When set to ``True`` it blocks until the recording is
168174
done, if it is set to ``False`` it will run in the background.
175+
:returns: An ``AudioTrack`` which ends where the recording ended.
169176

170177
.. py:function:: is_recording()
171178
@@ -263,18 +270,18 @@ An example of recording and playback with a display animation::
263270
RECORDING_RATE = 3906
264271
RECORDING_MS = 5000
265272

266-
my_recording = audio.AudioBuffer(duration=RECORDING_MS, rate=RECORDING_RATE)
273+
my_recording = audio.AudioRecording(duration=RECORDING_MS, rate=RECORDING_RATE)
267274

268275
while True:
269276
if button_a.is_pressed():
270-
microphone.record_into(my_recording, rate=RECORDING_RATE, wait=False)
277+
clipped_recording = microphone.record_into(my_recording, wait=False)
271278
display.show([mouth_open, mouth_closed], loop=True, wait=False, delay=150)
272279
while button_a.is_pressed() and microphone.is_recording():
273280
sleep(50)
274281
microphone.stop_recording()
275282
display.clear()
276283
if button_b.is_pressed():
277-
audio.play(my_recording, wait=False)
284+
audio.play(clipped_recording, wait=False)
278285
while audio.is_playing():
279286
x = accelerometer.get_x()
280287
audio.set_rate(scale(x, (-1000, 1000), (2250, 11000)))

0 commit comments

Comments
 (0)