Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1be9e87
docs: Update the docs to include the V2 API (#692)
microbit-mark Dec 3, 2020
71d5eef
Docs: update for V2 neopixel module (#694)
microbit-mark Dec 18, 2020
353e6e1
Docs: Speaker update and play() pin arguments (#695)
microbit-mark Dec 18, 2020
5357b8e
V2 docs: Remove is_on() from speaker. (#696)
microbit-carlos Dec 21, 2020
f9a37b4
V2 docs: Add pin set_touch_mode() (#701)
microbit-mark Jan 8, 2021
070e590
docs: Update Memory layout, Partial Flashing, Building for V2, BLE (#…
microbit-sam Feb 3, 2021
5774623
docs: Include V2 features in tutorials (#709)
microbit-mark Feb 24, 2021
c967053
docs: dev guide updates (#711)
microbit-mark Mar 16, 2021
eab72dd
docs: Indicate pin_speaker is only for PWM (#715)
microbit-mark Apr 7, 2021
79aced6
docs: Fix typo in neopixel, mumber -> number (#727)
microbit-matt-hillsdon Sep 23, 2021
8bc1fe8
docs: Add V2 microbit.set_volume() info. (#742)
microbit-carlos Apr 25, 2022
a870ff4
docs: Add note about possible 250kbit/sec radio rate in V2. (#744)
microbit-carlos Apr 29, 2022
8007c9e
docs: Add data logging API (#720)
microbit-mark Sep 5, 2022
8c107e9
docs: Add Power Management documentation. (#754)
microbit-carlos Sep 5, 2022
1e4265a
docs: Add Sound Effects documentation. (#753)
microbit-carlos Sep 6, 2022
44c2c1a
docs: Add missing `days` parameter to `microbit.run_every`. (#767)
microbit-carlos Sep 12, 2022
0d6f5b7
docs: Rename wave->waveform from SoundEffect. (#765)
microbit-carlos Sep 21, 2022
a538cb8
docs: Update Power Mgm to change run_every behaviour. (#769)
microbit-carlos Oct 24, 2022
202fab2
docs: Overwrite configued version always be '2' for v2-docs.
microbit-carlos Nov 15, 2022
8c42897
docs: Update URL to download arm-none-eabi-gcc. (#787)
microbit-carlos Dec 6, 2022
905577a
docs: Improve microphone description, add more info for `set_threshol…
microbit-carlos Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs: Add Sound Effects documentation. (#753)
* docs: Add Sound Effects documentation.

* docs: Change proposed audio.Effect() to audio.SoundEffect()

* docs: Update the proposed default values for audio.SoundEffect().

* docs: Move the SoundEffects constants from `audio` to `audio.SoundEffect`.

* docs: Remove SoundEffect `preset` constructor parameter, and add `copy()` method.

* docs: Change SoundEffect() `interporlation` parameter to `shape`.

* docs: Remove mentions of SoundEffect presets, add ranges, example to file.
  • Loading branch information
microbit-carlos committed Sep 16, 2024
commit 1e4265aba613463251c60db1625d1eaa606dbc2a
221 changes: 185 additions & 36 deletions docs/audio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,57 @@ Audio
This module allows you play sounds with the micro:bit.

By default sound output will be via the edge connector on pin 0 and the
:doc:`built-in speaker <speaker>` **V2**. You can connect wired headphones or
:doc:`built-in speaker <speaker>` (**V2**). You can connect wired headphones or
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
: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)
of Audio Frames, which are lists of 32 samples with values from 0 to 255::

square_wave = audio.AudioFrame()
for i in range(16):
square_wave[i] = 0
square_wave[i + 16] = 255
audio.play([square_wave] * 64)


Functions
=========

.. py:function:: play(source, wait=True, pin=pin0, return_pin=None)

Play the source to completion.
Play the audio source to completion.

:param source: ``Sound``: The ``microbit`` module contains a list of
built-in sounds that your can pass to ``audio.play()``.
:param source: There are three 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
- ``AudioFrame``: An iterable of ``AudioFrame`` instances as described
in the `AudioFrame Technical Details <#id2>`_ section

``AudioFrame``: The source agrument can also be an iterable
of ``AudioFrame`` elements as described below.
:param wait: If ``wait`` is ``True``, this function will block until the
source is exhausted.

:param pin: An optional argument to specify the output pin can be used to
override the default of ``pin0``. If we do not want any sound to play
we can use ``pin=None``.

:param return_pin: specifies a differential edge connector pin to connect
to an external speaker instead of ground. This is ignored for the **V2**
revision.
Expand All @@ -41,34 +69,9 @@ Functions

Stops all audio playback.

Classes
=======

.. py:class::
AudioFrame

An ``AudioFrame`` object is a list of 32 samples each of which is an unsigned byte
(whole number between 0 and 255).

It takes just over 4 ms to play a single frame.

.. py:function:: copyfrom(other)

Overwrite the data in this ``AudioFrame`` with the data from another
``AudioFrame`` instance.

:param other: ``AudioFrame`` instance from which to copy the data.


Using audio
===========

You will need a sound source, as input to the ``play`` function. You can use
the built-in sounds **V2** from the ``microbit`` module, ``microbit.Sound``, or
generate your own, like in ``examples/waveforms.py``.

Built-in sounds **V2**
----------------------
======================

The built-in sounds can be called using ``audio.play(Sound.NAME)``.

Expand All @@ -83,8 +86,154 @@ The built-in sounds can be called using ``audio.play(Sound.NAME)``.
* ``Sound.TWINKLE``
* ``Sound.YAWN``

Sounds Example
--------------

::

from microbit import *

while True:
if button_a.is_pressed() and button_b.is_pressed():
# When pressing both buttons only play via the edge connector
audio.play(Sound.HELLO, pin=pin0)
elif button_a.is_pressed():
# On button A play a sound and when it's done show an image
audio.play(Sound.HAPPY)
display.show(Image.HAPPY)
elif button_b.is_pressed():
# On button B play a sound and show an image at the same time
audio.play(Sound.TWINKLE, wait=False)
display.show(Image.BUTTERFLY)

sleep(500)
display.clear()


Sound Effects **V2**
====================

.. py:class::
SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=FX_NONE, shape=SHAPE_LOG)

An ``SoundEffect`` instance represents a sound effect, composed by a set of
parameters configured via the constructor or attributes.

All the parameters are optional, with default values as shown above, and
they can all be modified via attributes of the same name. For example, we
can first create an effect ``my_effect = SoundEffect(duration=1000)``,
and then change its attributes ``my_effect.duration = 500``.

:param freq_start: Start frequency in Hertz (Hz), default: ``500``
:param freq_end: End frequency in Hertz (Hz), default: ``2500``
:param duration: Duration of the sound (ms), default: ``500``
:param vol_start: Start volume value, range 0-255, default: ``255``
:param vol_end: End volume value, range 0-255, default: ``0``
:param wave: Type of wave shape, one of these values: ``WAVE_SINE``,
``WAVE_SAWTOOTH``, ``WAVE_TRIANGLE``, ``WAVE_SQUARE``,
``WAVE_NOISE`` (randomly generated noise). Default: ``WAVE_SQUARE``
:param fx: Effect to add on the sound, one of the following values:
``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``FX_NONE``.
Default: ``FX_NONE``
:param shape: The type of the interpolation curve between the start and end
frequencies, different wave shapes have different rates of change
in frequency. One of the following values: ``SHAPE_LINEAR``,
``SHAPE_CURVE``, ``SHAPE_LOG``. Default: ``SHAPE_LOG``

.. py:function:: copy()

:returns: A copy of the SoundEffect.

.. py:attribute:: freq_start

Start frequency in Hertz (Hz), a number between ``0`` and ``9999``.

.. py:attribute:: freq_end

End frequency in Hertz (Hz), a number between ``0`` and ``9999```.

.. py:attribute:: duration

Duration of the sound in milliseconds, a number between ``0`` and
``9999``.

.. py:attribute:: vol_start

Start volume value, a number between ``0`` and ``255``.

.. py:attribute:: vol_end

End volume value, a number between ``0`` and ``255``.

.. py:attribute:: wave

Type of wave shape, one of these values: ``WAVE_SINE``,
``WAVE_SAWTOOTH``, ``WAVE_TRIANGLE``, ``WAVE_SQUARE``,
``WAVE_NOISE`` (randomly generated noise).

.. py:attribute:: fx

Effect to add on the sound, one of the following values:
``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``None``.

.. py:attribute:: shape

The type of interpolation curve between the start and end
frequencies, different wave shapes have different rates of change
in frequency. One of the following values: ``SHAPE_LINEAR``,
``SHAPE_CURVE``, ``SHAPE_LOG``.

The arguments used to create any Sound Effect,
can be inspected by looking at each of the SoundEffect instance attributes,
or by converting the instance into a string (which can be done via ``str()``
function, or by using a function that does the conversion automatically like
``print()``).

For example, with the :doc:`REPL </devguide/repl>` you can inspect the
default SoundEffects::

>>> print(audio.SoundEffect())
SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=FX_NONE, shape=SHAPE_LOG)

This format is "human readable", which means it is easy for us to read,
and it looks very similar to the code needed to create that SoundEffect,
but it's not quite right. The ``repr()`` function can be used to create a
string of Python code that can be stored or transferred
(you could transmit sounds via micro:bit radio!) and be executed with the
``eval()`` function::

>>> from audio import SoundEffect
>>> sound_code = repr(SoundEffect())
>>> print(sound_code)
SoundEffect(500, 2500, 500, 255, 0, 3, 0, 18)
>>> eval("audio.play({})".format(sound_code))

Sound Effects Example
---------------------

.. include:: ../examples/soundeffects.py
:code: python

AudioFrame
==========

.. py:class::
AudioFrame

An ``AudioFrame`` object is a list of 32 samples each of which is an unsigned byte
(whole number between 0 and 255).

It takes just over 4 ms to play a single frame.

.. py:function:: copyfrom(other)

Overwrite the data in this ``AudioFrame`` with the data from another
``AudioFrame`` instance.

:param other: ``AudioFrame`` instance from which to copy the data.

Technical Details
=================
-----------------

.. note::
You don't need to understand this section to use the ``audio`` module.
Expand All @@ -104,11 +253,11 @@ samples. When reading reaches the start or the mid-point of the buffer, it
triggers a callback to fetch the next ``AudioFrame`` which is then copied into
the buffer. This means that a sound source has under 4ms to compute the next
``AudioFrame``, and for reliable operation needs to take less 2ms (which is
32000 cycles, so should be plenty).
32000 cycles in micro:bit V1 or 128000 in V2, so should be plenty).


Example
=======
AudioFrame Example
------------------

.. include:: ../examples/waveforms.py
:code: python
53 changes: 53 additions & 0 deletions examples/soundeffects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from microbit import *

# Play the default Sound Effect
audio.play(audio.SoundEffect())

# Create a new Sound Effect and immediately play it
audio.play(audio.SoundEffect(
freq_start=400,
freq_end=2000,
duration=500,
vol_start=100,
vol_end=255,
wave=audio.SoundEffect.WAVE_TRIANGLE,
fx=audio.SoundEffect.FX_VIBRATO,
shape=audio.SoundEffect.SHAPE_LOG
))

# Play a Sound Effect instance, modify an attribute, and play it again
my_effect = audio.SoundEffect(
freq_start=400,
freq_end=2000,
)
audio.play(my_effect)
my_effect.duration = 1000
audio.play(my_effect)

# You can also create a new effect based on an existing one, and modify
# any of its characteristics via arguments
my_modified_effect = my_effect.copy()
my_modified_effect.wave = audio.SoundEffect.WAVE_NOISE
audio.play(my_modified_effect)

# Use sensor data to modify and play an existing Sound Effect instance
my_effect.duration = 600
while True:
# int() might be temporarily neededhttps://github.com/microbit-foundation/micropython-microbit-v2/issues/121
my_effect.freq_start = int(scale(accelerometer.get_x(), from_=(-2000, 2000), to=(0, 9999)))
my_effect.freq_end = int(scale(accelerometer.get_y(), from_=(-2000, 2000), to=(0, 9999)))
audio.play(my_effect)

if button_a.is_pressed():
# Button A silences the micro:bit
speaker.off()
display.show(Image("09090:00000:00900:09990:00900"))
sleep(500)
elif button_b.is_pressed():
# On button B re-enable speaker & play an effect while showing an image
speaker.on()
audio.play(audio.SoundEffect(), wait=False)
display.show(Image.MUSIC_QUAVER)
sleep(500)

sleep(150)