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 built-in 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 audio.play() function:

  1. Built in sounds (V2), e.g. audio.play(Sound.HAPPY)

  2. Sound Effects (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, 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

audio.play(source, wait=True, pin=pin0, return_pin=None)

Play the audio source to completion.

Parameters:
  • 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 section.
    • SoundEffect: A sound effect, or an iterable of sound effects, created via the audio.SoundEffect() class
    • AudioFrame: An iterable of AudioFrame instances as described in the AudioFrame Technical Details section
  • wait – If wait is True, this function will block until the source is exhausted.
  • 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.
  • return_pin – specifies a differential edge connector pin to connect to an external speaker instead of ground. This is ignored for the V2 revision.
audio.is_playing()
Returns:True if audio is playing, otherwise returns False.
audio.stop()

Stops all audio playback.

Built-in sounds V2

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

  • Sound.GIGGLE
  • Sound.HAPPY
  • Sound.HELLO
  • Sound.MYSTERIOUS
  • Sound.SAD
  • Sound.SLIDE
  • Sound.SOARING
  • Sound.SPRING
  • 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

class audio.SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, waveform=WAVEFORM_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.

Parameters:
  • freq_start – Start frequency in Hertz (Hz), default: 500
  • freq_end – End frequency in Hertz (Hz), default: 2500
  • duration – Duration of the sound (ms), default: 500
  • vol_start – Start volume value, range 0-255, default: 255
  • vol_end – End volume value, range 0-255, default: 0
  • waveform – Type of waveform shape, one of these values: WAVEFORM_SINE, WAVEFORM_SAWTOOTH, WAVEFORM_TRIANGLE, WAVEFORM_SQUARE, WAVEFORM_NOISE (randomly generated noise). Default: WAVEFORM_SQUARE
  • fx – Effect to add on the sound, one of the following values: FX_TREMOLO, FX_VIBRATO, FX_WARBLE, or FX_NONE. Default: FX_NONE
  • 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
copy()
Returns:A copy of the SoundEffect.
freq_start

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

freq_end

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

duration

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

vol_start

Start volume value, a number between 0 and 255.

vol_end

End volume value, a number between 0 and 255.

waveform

Type of waveform shape, one of these values: WAVEFORM_SINE, WAVEFORM_SAWTOOTH, WAVEFORM_TRIANGLE, WAVEFORM_SQUARE, WAVEFORM_NOISE (randomly generated noise).

fx

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

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 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, waveform=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

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,
    waveform=audio.SoundEffect.WAVEFORM_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.waveform = audio.SoundEffect.WAVEFORM_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 needed: https://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():
        # Button B re-enables the speaker & plays an effect while showing an image
        speaker.on()
        audio.play(audio.SoundEffect(), wait=False)
        display.show(Image.MUSIC_QUAVER)
        sleep(500)

    sleep(150)

AudioFrame

class audio.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.

copyfrom(other)

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

Parameters:otherAudioFrame instance from which to copy the data.

Technical Details

Note

You don’t need to understand this section to use the audio module. It is just here in case you wanted to know how it works.

The audio module can consumes an iterable (sequence, like list or tuple, or generator) of AudioFrame instances, each 32 samples at 7812.5 Hz, and uses linear interpolation to output a PWM signal at 32.5 kHz, which gives tolerable sound quality.

The function play fully copies all data from each AudioFrame before it calls next() for the next frame, so a sound source can use the same AudioFrame repeatedly.

The audio module has an internal 64 sample buffer from which it reads 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 in micro:bit V1 or 128000 in V2, so should be plenty).

AudioFrame Example

from microbit import display, sleep, button_a
import audio
import math

def repeated_frame(frame, count):
    for i in range(count):
        yield frame

# Press button A to skip to next wave.
def show_wave(name, frame, duration=1500):
    display.scroll(name + " wave", wait=False,delay=100)
    audio.play(repeated_frame(frame, duration),wait=False)
    for i in range(75):
        sleep(100)
        if button_a.is_pressed():
            display.clear()
            audio.stop()
            break

frame = audio.AudioFrame()

for i in range(len(frame)):
    frame[i] = int(math.sin(math.pi*i/16)*124+128.5)
show_wave("Sine", frame)

triangle = audio.AudioFrame()

QUARTER = len(triangle)//4
for i in range(QUARTER):
    triangle[i] = i*15
    triangle[i+QUARTER] = 248-i*15
    triangle[i+QUARTER*2] = 128-i*15
    triangle[i+QUARTER*3] = i*15+8
show_wave("Triangle", triangle)

square = audio.AudioFrame()

HALF = len(square)//2
for i in range(HALF):
    square[i] = 8
    square[i+HALF] = 248
show_wave("Square", square)
sleep(1000)

for i in range(len(frame)):
    frame[i] = 252-i*8
show_wave("Sawtooth", frame)

del frame

#Generate a waveform that goes from triangle to square wave, reasonably smoothly.
frames = [ None ] * 32
for i in range(32):
    frames[i] = frame = audio.AudioFrame()
    for j in range(len(triangle)):
        frame[j] = (triangle[j]*(32-i) + square[j]*i)>>5

def repeated_frames(frames, count):
    for frame in frames:
        for i in range(count):
            yield frame


display.scroll("Ascending wave", wait=False)
audio.play(repeated_frames(frames, 60))