Skip to content

Commit cbc668c

Browse files
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.
1 parent ffcd3a6 commit cbc668c

File tree

2 files changed

+238
-36
lines changed

2 files changed

+238
-36
lines changed

docs/audio.rst

+185-36
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,57 @@ Audio
66
This module allows you play sounds with the micro:bit.
77

88
By default sound output will be via the edge connector on pin 0 and the
9-
:doc:`built-in speaker <speaker>` **V2**. You can connect wired headphones or
9+
:doc:`built-in speaker <speaker>` (**V2**). You can connect wired headphones or
1010
a speaker to pin 0 and GND on the edge connector to hear the sounds.
1111

1212
The ``audio`` module can be imported as ``import audio`` or accessed via
1313
the ``microbit`` module as ``microbit.audio``.
1414

15+
There are three different kinds of audio sources that can be played using the
16+
:py:meth:`audio.play` function:
17+
18+
1. `Built in sounds <#built-in-sounds-v2>`_ (**V2**),
19+
e.g. ``audio.play(Sound.HAPPY)``
20+
2. `Sound Effects <#sound-effects-v2>`_ (**V2**), a way to create custom sounds
21+
by configuring its parameters::
22+
23+
my_effect = audio.SoundEffect(freq_start=400, freq_end=2500, duration=500)
24+
audio.play(my_effect)
25+
26+
3. `Audio Frames <#audioframe>`_, an iterable (like a list or a generator)
27+
of Audio Frames, which are lists of 32 samples with values from 0 to 255::
28+
29+
square_wave = audio.AudioFrame()
30+
for i in range(16):
31+
square_wave[i] = 0
32+
square_wave[i + 16] = 255
33+
audio.play([square_wave] * 64)
34+
35+
1536
Functions
1637
=========
1738

1839
.. py:function:: play(source, wait=True, pin=pin0, return_pin=None)
1940
20-
Play the source to completion.
41+
Play the audio source to completion.
2142

22-
:param source: ``Sound``: The ``microbit`` module contains a list of
23-
built-in sounds that your can pass to ``audio.play()``.
43+
:param source: There are three types of data that can be used as a source:
44+
45+
- ``Sound``: The ``microbit`` module contains a list of
46+
built-in sounds, e.g. ``audio.play(Sound.TWINKLE)``. A full list can
47+
be found in the `Built in sounds <#built-in-sounds-v2>`_ section.
48+
- ``SoundEffect``: A sound effect, or an iterable of sound effects,
49+
created via the :py:meth:`audio.SoundEffect` class
50+
- ``AudioFrame``: An iterable of ``AudioFrame`` instances as described
51+
in the `AudioFrame Technical Details <#id2>`_ section
2452

25-
``AudioFrame``: The source agrument can also be an iterable
26-
of ``AudioFrame`` elements as described below.
2753
:param wait: If ``wait`` is ``True``, this function will block until the
2854
source is exhausted.
55+
2956
:param pin: An optional argument to specify the output pin can be used to
3057
override the default of ``pin0``. If we do not want any sound to play
3158
we can use ``pin=None``.
59+
3260
:param return_pin: specifies a differential edge connector pin to connect
3361
to an external speaker instead of ground. This is ignored for the **V2**
3462
revision.
@@ -41,34 +69,9 @@ Functions
4169
4270
Stops all audio playback.
4371

44-
Classes
45-
=======
46-
47-
.. py:class::
48-
AudioFrame
49-
50-
An ``AudioFrame`` object is a list of 32 samples each of which is an unsigned byte
51-
(whole number between 0 and 255).
52-
53-
It takes just over 4 ms to play a single frame.
54-
55-
.. py:function:: copyfrom(other)
56-
57-
Overwrite the data in this ``AudioFrame`` with the data from another
58-
``AudioFrame`` instance.
59-
60-
:param other: ``AudioFrame`` instance from which to copy the data.
61-
62-
63-
Using audio
64-
===========
65-
66-
You will need a sound source, as input to the ``play`` function. You can use
67-
the built-in sounds **V2** from the ``microbit`` module, ``microbit.Sound``, or
68-
generate your own, like in ``examples/waveforms.py``.
6972

7073
Built-in sounds **V2**
71-
----------------------
74+
======================
7275

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

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

89+
Sounds Example
90+
--------------
91+
92+
::
93+
94+
from microbit import *
95+
96+
while True:
97+
if button_a.is_pressed() and button_b.is_pressed():
98+
# When pressing both buttons only play via the edge connector
99+
audio.play(Sound.HELLO, pin=pin0)
100+
elif button_a.is_pressed():
101+
# On button A play a sound and when it's done show an image
102+
audio.play(Sound.HAPPY)
103+
display.show(Image.HAPPY)
104+
elif button_b.is_pressed():
105+
# On button B play a sound and show an image at the same time
106+
audio.play(Sound.TWINKLE, wait=False)
107+
display.show(Image.BUTTERFLY)
108+
109+
sleep(500)
110+
display.clear()
111+
112+
113+
Sound Effects **V2**
114+
====================
115+
116+
.. py:class::
117+
SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=FX_NONE, shape=SHAPE_LOG)
118+
119+
An ``SoundEffect`` instance represents a sound effect, composed by a set of
120+
parameters configured via the constructor or attributes.
121+
122+
All the parameters are optional, with default values as shown above, and
123+
they can all be modified via attributes of the same name. For example, we
124+
can first create an effect ``my_effect = SoundEffect(duration=1000)``,
125+
and then change its attributes ``my_effect.duration = 500``.
126+
127+
:param freq_start: Start frequency in Hertz (Hz), default: ``500``
128+
:param freq_end: End frequency in Hertz (Hz), default: ``2500``
129+
:param duration: Duration of the sound (ms), default: ``500``
130+
:param vol_start: Start volume value, range 0-255, default: ``255``
131+
:param vol_end: End volume value, range 0-255, default: ``0``
132+
:param wave: Type of wave shape, one of these values: ``WAVE_SINE``,
133+
``WAVE_SAWTOOTH``, ``WAVE_TRIANGLE``, ``WAVE_SQUARE``,
134+
``WAVE_NOISE`` (randomly generated noise). Default: ``WAVE_SQUARE``
135+
:param fx: Effect to add on the sound, one of the following values:
136+
``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``FX_NONE``.
137+
Default: ``FX_NONE``
138+
:param shape: The type of the interpolation curve between the start and end
139+
frequencies, different wave shapes have different rates of change
140+
in frequency. One of the following values: ``SHAPE_LINEAR``,
141+
``SHAPE_CURVE``, ``SHAPE_LOG``. Default: ``SHAPE_LOG``
142+
143+
.. py:function:: copy()
144+
145+
:returns: A copy of the SoundEffect.
146+
147+
.. py:attribute:: freq_start
148+
149+
Start frequency in Hertz (Hz), a number between ``0`` and ``9999``.
150+
151+
.. py:attribute:: freq_end
152+
153+
End frequency in Hertz (Hz), a number between ``0`` and ``9999```.
154+
155+
.. py:attribute:: duration
156+
157+
Duration of the sound in milliseconds, a number between ``0`` and
158+
``9999``.
159+
160+
.. py:attribute:: vol_start
161+
162+
Start volume value, a number between ``0`` and ``255``.
163+
164+
.. py:attribute:: vol_end
165+
166+
End volume value, a number between ``0`` and ``255``.
167+
168+
.. py:attribute:: wave
169+
170+
Type of wave shape, one of these values: ``WAVE_SINE``,
171+
``WAVE_SAWTOOTH``, ``WAVE_TRIANGLE``, ``WAVE_SQUARE``,
172+
``WAVE_NOISE`` (randomly generated noise).
173+
174+
.. py:attribute:: fx
175+
176+
Effect to add on the sound, one of the following values:
177+
``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``None``.
178+
179+
.. py:attribute:: shape
180+
181+
The type of interpolation curve between the start and end
182+
frequencies, different wave shapes have different rates of change
183+
in frequency. One of the following values: ``SHAPE_LINEAR``,
184+
``SHAPE_CURVE``, ``SHAPE_LOG``.
185+
186+
The arguments used to create any Sound Effect,
187+
can be inspected by looking at each of the SoundEffect instance attributes,
188+
or by converting the instance into a string (which can be done via ``str()``
189+
function, or by using a function that does the conversion automatically like
190+
``print()``).
191+
192+
For example, with the :doc:`REPL </devguide/repl>` you can inspect the
193+
default SoundEffects::
194+
195+
>>> print(audio.SoundEffect())
196+
SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=FX_NONE, shape=SHAPE_LOG)
197+
198+
This format is "human readable", which means it is easy for us to read,
199+
and it looks very similar to the code needed to create that SoundEffect,
200+
but it's not quite right. The ``repr()`` function can be used to create a
201+
string of Python code that can be stored or transferred
202+
(you could transmit sounds via micro:bit radio!) and be executed with the
203+
``eval()`` function::
204+
205+
>>> from audio import SoundEffect
206+
>>> sound_code = repr(SoundEffect())
207+
>>> print(sound_code)
208+
SoundEffect(500, 2500, 500, 255, 0, 3, 0, 18)
209+
>>> eval("audio.play({})".format(sound_code))
210+
211+
Sound Effects Example
212+
---------------------
213+
214+
.. include:: ../examples/soundeffects.py
215+
:code: python
216+
217+
AudioFrame
218+
==========
219+
220+
.. py:class::
221+
AudioFrame
222+
223+
An ``AudioFrame`` object is a list of 32 samples each of which is an unsigned byte
224+
(whole number between 0 and 255).
225+
226+
It takes just over 4 ms to play a single frame.
227+
228+
.. py:function:: copyfrom(other)
229+
230+
Overwrite the data in this ``AudioFrame`` with the data from another
231+
``AudioFrame`` instance.
232+
233+
:param other: ``AudioFrame`` instance from which to copy the data.
234+
86235
Technical Details
87-
=================
236+
-----------------
88237

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

109258

110-
Example
111-
=======
259+
AudioFrame Example
260+
------------------
112261

113262
.. include:: ../examples/waveforms.py
114263
:code: python

examples/soundeffects.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from microbit import *
2+
3+
# Play the default Sound Effect
4+
audio.play(audio.SoundEffect())
5+
6+
# Create a new Sound Effect and immediately play it
7+
audio.play(audio.SoundEffect(
8+
freq_start=400,
9+
freq_end=2000,
10+
duration=500,
11+
vol_start=100,
12+
vol_end=255,
13+
wave=audio.SoundEffect.WAVE_TRIANGLE,
14+
fx=audio.SoundEffect.FX_VIBRATO,
15+
shape=audio.SoundEffect.SHAPE_LOG
16+
))
17+
18+
# Play a Sound Effect instance, modify an attribute, and play it again
19+
my_effect = audio.SoundEffect(
20+
freq_start=400,
21+
freq_end=2000,
22+
)
23+
audio.play(my_effect)
24+
my_effect.duration = 1000
25+
audio.play(my_effect)
26+
27+
# You can also create a new effect based on an existing one, and modify
28+
# any of its characteristics via arguments
29+
my_modified_effect = my_effect.copy()
30+
my_modified_effect.wave = audio.SoundEffect.WAVE_NOISE
31+
audio.play(my_modified_effect)
32+
33+
# Use sensor data to modify and play an existing Sound Effect instance
34+
my_effect.duration = 600
35+
while True:
36+
# int() might be temporarily neededhttps://github.com/microbit-foundation/micropython-microbit-v2/issues/121
37+
my_effect.freq_start = int(scale(accelerometer.get_x(), from_=(-2000, 2000), to=(0, 9999)))
38+
my_effect.freq_end = int(scale(accelerometer.get_y(), from_=(-2000, 2000), to=(0, 9999)))
39+
audio.play(my_effect)
40+
41+
if button_a.is_pressed():
42+
# Button A silences the micro:bit
43+
speaker.off()
44+
display.show(Image("09090:00000:00900:09990:00900"))
45+
sleep(500)
46+
elif button_b.is_pressed():
47+
# On button B re-enable speaker & play an effect while showing an image
48+
speaker.on()
49+
audio.play(audio.SoundEffect(), wait=False)
50+
display.show(Image.MUSIC_QUAVER)
51+
sleep(500)
52+
53+
sleep(150)

0 commit comments

Comments
 (0)