Skip to content

Commit

Permalink
docs: Add initial proposal for recording & playback API.
Browse files Browse the repository at this point in the history
  • Loading branch information
microbit-carlos committed May 19, 2023
1 parent 1c3fc31 commit d430151
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 9 deletions.
71 changes: 68 additions & 3 deletions docs/audio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,25 @@ a speaker to pin 0 and GND on the edge connector to hear the sounds.
The ``audio`` module can be imported as ``import audio`` or accessed via
the ``microbit`` module as ``microbit.audio``.

There are three different kinds of audio sources that can be played using the
There are four different kinds of audio sources that can be played using the
:py:meth:`audio.play` function:

1. `Built in sounds <#built-in-sounds-v2>`_ (**V2**),
e.g. ``audio.play(Sound.HAPPY)``

2. `Sound Effects <#sound-effects-v2>`_ (**V2**), a way to create custom sounds
by configuring its parameters::

my_effect = audio.SoundEffect(freq_start=400, freq_end=2500, duration=500)
audio.play(my_effect)

3. `Audio Frames <#audioframe>`_, an iterable (like a list or a generator)
3. `AudioBuffer <#audiobuffer>`_ (**V2**), a generic buffer for audio that can
be used to record sound from the micro:bit V2 built-in microphone::

my_audio_buffer = microphone.record()
audio.play(my_audio_buffer)

4. `Audio Frames <#audioframe>`_, an iterable (like a list or a generator)
of Audio Frames, which are lists of 32 samples with values from 0 to 255::

square_wave = audio.AudioFrame()
Expand All @@ -40,13 +47,16 @@ Functions
Play the audio source to completion.

:param source: There are three types of data that can be used as a source:
:param source: There are four types of data that can be used as a source:

- ``Sound``: The ``microbit`` module contains a list of
built-in sounds, e.g. ``audio.play(Sound.TWINKLE)``. A full list can
be found in the `Built in sounds <#built-in-sounds-v2>`_ section.
- ``SoundEffect``: A sound effect, or an iterable of sound effects,
created via the :py:meth:`audio.SoundEffect` class
- ``AudioBuffer``: An audio buffer, or an iterable of audio buffers,
created via the :py:meth:`audio.AudioBuffer` class or
:doc:`microphone.record() <microphone>` function
- ``AudioFrame``: An iterable of ``AudioFrame`` instances as described
in the `AudioFrame Technical Details <#id2>`_ section

Expand Down Expand Up @@ -215,6 +225,61 @@ Sound Effects Example
.. include:: ../examples/soundeffects.py
:code: python


Audio Buffer **V2**
===================

.. py:class::
AudioBuffer(duration=3000, rate=11000)

Create a buffer to contain audio data and its sampling rate.

The sampling rate is configured via constructor or instance attribute,
and is used by the ``microphone.record_into()`` and
``audio.play()`` functions to configure the recording and playback rates.

For audio recording, reducing the number of samples recorded per second
will reduce the size of the data buffer, but also reduce the sound quality.
And increasing the sampling rate increases the buffer size and sound
quality.

For audio playback, reducing the sampling rate compared with the recording
rate, will slow down the audio. And increasing the playback rate
will speed it up.

The size of the buffer will be determined by the samples per second
and the ``duration`` configured when creating a new instance.

:param duration: Indicates in milliseconds, how much sound data the buffer
can contained at the configured ``data_rate``.
:param rate: Sampling rate of for the data in the buffer. This value is
used for recording and playback, and can be edited as an attribute.

.. py:function:: copy()
:returns: A copy of the Audio Buffer.

.. py:attribute:: rate
The sampling rate for the data inside the buffer.
TODO: Indicate range of valid values here.

Audio Buffer Example
--------------------

::

my_buffer = audio.AudioBuffer(duration=5000)
microphone.record_into(my_buffer)
audio.play(my_buffer)

# A smaller buffer can be generated with the same duration by using
# a lower sampling rate
smaller_buffer = audio.AudioBuffer(duration=5000, rate=5500)
microphone.record_into(my_buffer)
audio.play(my_buffer)


AudioFrame
==========

Expand Down
118 changes: 112 additions & 6 deletions docs/microphone.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Microphone **V2**
.. py:module:: microbit.microphone
This object lets you access the built-in microphone available on the
micro:bit **V2**. It can be used to respond to sound. The microphone input
is located on the front of the board alongside a microphone activity LED,
which is lit when the microphone is in use.
micro:bit **V2**. It can be used to record and respond to sound.
The microphone input is located on the front of the board alongside a
microphone activity LED, which is lit when the microphone is in use.

.. image:: microphone.png
:width: 300px
Expand All @@ -28,6 +28,25 @@ accessible via variables in ``microbit.SoundEvent``:
- ``microbit.SoundEvent.LOUD``: Represents the transition of sound events,
from ``quiet`` to ``loud`` like clapping or shouting.

Recording
=========

TODO:
* Describe the feature.
* Indicate how the sampling rate relates to recording quality.
* Indicate how changing the sampling rate on the fly affects playback speed.
* What happens if the user changes the sampling rate while recording?

::

from microbit import *

while True:
if button_a.is_pressed():
my_recording = microphone.record(duration=5000)
audio.play(my_recording)
sleep(200)

Functions
=========

Expand Down Expand Up @@ -70,11 +89,60 @@ Functions
* **return**: a representation of the sound pressure level in the range 0 to
255.

.. py:function:: record(duration=3000, rate=11000, wait=True)
Record sound for the amount of time indicated by ``duration`` at the
sampling rate indicated by ``rate``.

The amount of memory consumed is directly related to the length of the
recording and the sampling rate. The higher these values, the more memory
it will use.

A lower sampling rate will reduce memory consumption and sound quality.

If there isn't enough memory available a ``MemoryError`` will be raised.

:param duration: How much time to record in milliseconds.
:param rate: Number of samples to capture per second.
:param wait: When set to ``True`` it blocks until the recording is
done, if it is set to ``False`` it will run in the background.
:returns: An ``AudioBuffer``, configured at the provided ``duration``
and ``rate``, with the sound data.

.. py:function:: record_into(buffer, rate=11000, wait=True)
Record sound into an existing ``AudioBuffer``.

:param buffer: An ``AudioBuffer`` to record the microphone sound.
:param rate: Number of samples to capture per second.
:param wait: When set to ``True`` it blocks until the recording is
done, if it is set to ``False`` it will run in the background.

Example
=======
.. py:function:: is_recording()
An example that runs through some of the functions of the microphone API::
:returns: ``True`` if the microphone is currently recording sound, or
``False`` otherwise.

.. py:function:: stop_recording()
Stops an a recording running in the background.

.. py:function:: set_sensitivity(gain)
Configure the microphone sensitivity to one of these three levels:
``microphone.SENSITIVITY_LOW``, ``microphone.SENSITIVITY_MEDIUM``,
``microphone.SENSITIVITY_HIGH``.

These constants correspond to a number, and any values between these
constants are valid arguments

:param gain: Microphone gain.

Examples
========

An example that runs through some of the functions of the microphone
Sound Events API::

# Basic test for microphone. This test should update the display when
# Button A is pressed and a loud or quiet sound *is* heard, printing the
Expand Down Expand Up @@ -122,3 +190,41 @@ An example that runs through some of the functions of the microphone API::
display.clear()
print(sound)
sleep(500)


An example of recording and playback with a display animation::

from microbit import *

talk_open = Image(
"09090:"
"00000:"
"09990:"
"90009:"
"09990"
)
talk_closed = Image(
"09090:"
"00000:"
"00000:"
"99999:"
"00000"
)

my_recording = audio.AudioBuffer(duration=5000, rate=5500)

while True:
if button_a.is_pressed():
microphone.record_into(my_recording, rate=5500, wait=False)
display.show([talk_open, talk_closed], loop=True, wait=False, delay=150)
while button_a.is_pressed():
sleep(50)
display.show(mouth_open, loop=False) # workaround issue #150
display.clear()
if button_b.is_pressed():
audio.play(my_recording, wait=False)
while audio.is_playing():
x = accelerometer.get_x()
my_recording.rate = scale(x, (-1000, 1000), (2250, 11000))
sleep(50)
sleep(100)

0 comments on commit d430151

Please sign in to comment.