Skip to content

Commit 72212bc

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

File tree

3 files changed

+185
-53
lines changed

3 files changed

+185
-53
lines changed

docs/audio.rst

+159-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,159 @@ 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+
--------------
266+
267+
.. py:class::
268+
AudioRecording(duration, rate=7812)
269+
270+
The ``AudioRecording`` object contains audio data and the sampling rate
271+
associated to it.
241272

242-
On micro:bit V1 the constructor does not take any arguments,
243-
and an AudioFrame instance is always 32 bytes.
273+
The size of the internal buffer will depend on the ``rate``
274+
(number of samples per second) and ``duration`` parameters.
244275

245-
:param duration: (**V2**) Indicates how many milliseconds of audio this
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:: track(start_ms=0, end_ms=-1)
296+
297+
Create an `AudioTrack <#audio.AudioTrack>`_ instance from a portion of
298+
the data in this ``AudioRecording`` instance.
299+
300+
:param start_ms: Where to start of the track in milliseconds.
301+
:param end_ms: The end of the track in milliseconds.
302+
If the default value of ``-1`` is provided it will end the track
303+
at the end of the AudioRecording.
304+
305+
When an AudioRecording is used to record data from the microphone,
306+
increasing the sampling rate increases the sound quality,
307+
but it also increases the amount of memory used.
308+
309+
During playback, increasing the sampling rate speeds up the sound
310+
and decreasing the sample rate slows it down.
311+
312+
AudioTrack
313+
----------
314+
315+
An ``AudioTrack`` can be created from an ``AudioRecording`` or ``bytearray``
316+
and individual bytes can be accessed and modified like elements in a list.
317+
318+
This class is useful to modify the audio data in an ``AudioRecording`` or
319+
to create precisely sized audio data buffers for sending and receiving them
320+
via communication protocols like radio or serial.
321+
322+
.. py:class::
323+
AudioTrack(buffer, rate=7812)
324+
325+
The ``AudioTrack`` object points to the data provided by the input buffer,
326+
which can be an ``AudioRecording``, or a buffer-like object like a
327+
``bytearray``.
328+
It also contains its own sampling rate, so multiple ``AudioTrack``
329+
instances pointing to the same buffer can have different rates and won't
330+
affect the rate of the original buffer.
254331

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.
332+
:param buffer: The buffer containing the audio data.
333+
:param rate: The sampling rate at which data will be stored
334+
via the microphone, or played via the ``audio.play()`` function.
335+
336+
.. py:function:: set_rate(sample_rate)
337+
338+
Configure the sampling rate associated with the data in the
339+
``AudioTrack`` instance.
260340

261341
:param sample_rate: The sample rate to set.
262342

263343
.. py:function:: get_rate()
264344
265-
(**V2 only**) Return the configured sampling rate for this
266-
``AudioFrame`` instance.
345+
Return the configured sampling rate for this
346+
``AudioTrack`` instance.
267347

268348
:return: The configured sample rate.
269349

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

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
415+
The ``audio`` module can consumes an iterable (sequence, like list or tuple, or
416+
generator) of ``AudioFrame`` instances, each 32 samples at 7812.5 Hz,
417+
which take just over 4 milliseconds to play each frame
295418
(1/7812.5 * 32 = 0.004096 = 4096 microseconds).
296419

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
420+
The function ``play`` fully copies all data from each ``AudioFrame`` before it
421+
calls ``next()`` for the next frame, so a sound source can use the same
299422
``AudioFrame`` repeatedly.
300423

301424
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-13
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,24 @@ 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

160162
.. py:function:: record_into(buffer, rate=7812, 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.
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.
166173
:param rate: Number of samples to capture per second.
174+
This will overwrite the rate set in the buffer object.
167175
:param wait: When set to ``True`` it blocks until the recording is
168176
done, if it is set to ``False`` it will run in the background.
177+
:returns: An ``AudioTrack`` which ends where the recording ended.
169178

170179
.. py:function:: is_recording()
171180
@@ -263,18 +272,18 @@ An example of recording and playback with a display animation::
263272
RECORDING_RATE = 3906
264273
RECORDING_MS = 5000
265274

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

268277
while True:
269278
if button_a.is_pressed():
270-
microphone.record_into(my_recording, rate=RECORDING_RATE, wait=False)
279+
clipped_recording = microphone.record_into(my_recording, wait=False)
271280
display.show([mouth_open, mouth_closed], loop=True, wait=False, delay=150)
272281
while button_a.is_pressed() and microphone.is_recording():
273282
sleep(50)
274283
microphone.stop_recording()
275284
display.clear()
276285
if button_b.is_pressed():
277-
audio.play(my_recording, wait=False)
286+
audio.play(clipped_recording, wait=False)
278287
while audio.is_playing():
279288
x = accelerometer.get_x()
280289
audio.set_rate(scale(x, (-1000, 1000), (2250, 11000)))

0 commit comments

Comments
 (0)