From e7ff8261885843b2e701affb427d8f68d9183ee2 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Mon, 20 Jun 2022 11:22:41 +0100 Subject: [PATCH 1/7] docs: Add Sound Effects documentation. --- docs/audio.rst | 282 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 246 insertions(+), 36 deletions(-) diff --git a/docs/audio.rst b/docs/audio.rst index 3d48d9c4e..5a41c563a 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -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 ` **V2**. You can connect wired headphones or +:doc:`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 +: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.Effect(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. + - ``Effect``: A sound effect, or an iterable of sound effects, created + via the :py:meth:`audio.Effect` 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. @@ -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)``. @@ -83,8 +86,215 @@ 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:: + Effect(preset=None, freq_start=400, freq_end=200, duration=500, vol_start=100, vol_end=255, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LINEAR) + + An ``Effect`` 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 = Effect(duration=1000)``, and then + change its attributes ``my_effect.duration = 500``. + + :param preset: An existing Effect instance to use as a base, its values + are cloned in the new instance, and any additional arguments provided + overwrite the base values. + :param freq_start: Start Frequency in Hertz (Hz), eg: ``400`` + :param freq_end: End Frequency in Hertz (Hz), eg: ``2000`` + :param duration: Duration of the sound (ms), eg: ``500`` + :param vol_start: Start volume value, range 0-255, eg: ``120`` + :param vol_end: End volume value, range 0-255, eg: ``255`` + :param wave: Type of wave shape, one of these values: ``WAVE_SINE``, + ``WAVE_SAWTOOTH``, ``WAVE_TRIANGLE``, ``WAVE_SQUARE``, + ``WAVE_NOISE`` (randomly generated noise). + :param fx: Effect to add on the sound, one of the following values: + ``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``None``. + :param interpolation: The type of curve between the start and end + frequencies, different wave shapes have different rates of change + in frequency. One of the following values: ``INTER_LINEAR``, + ``INTER_CURVE``, ``INTER_LOG``. + + .. py:attribute:: freq_start + + Start Frequency in Hertz (Hz) + + .. py:attribute:: freq_end + + End Frequency in Hertz (Hz) + + .. py:attribute:: duration + + Duration of the sound (ms), eg: ``500`` + + .. py:attribute:: vol_start + + Start volume value, range 0-255, eg: ``120`` + + .. py:attribute:: vol_end + + End volume value, range 0-255, eg: ``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:: interpolation + + The type of curve between the start and end + frequencies, different wave shapes have different rates of change + in frequency. One of the following values: ``INTER_LINEAR``, + ``INTER_CURVE``, ``INTER_LOG``. + +The arguments used to create any Sound Effect, including the built in effects, +can be inspected by looking at each of the Effect 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 ` you can inspect the built +in Effects:: + + >>> audio.Effect.CROAK + Effect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., interpolation=...) + +The built in Effects are immutable, so they cannot be changed. Trying to modify +a built in Effect will throw an exception:: + + >>> audio.Effect.CLICK.duration = 1000 + Traceback (most recent call last): + File "", line 1, in + TypeError: effect cannot be modified + +But a new one can be created like this:: + + >>> click_clone = Effect(audio.Effect.CLICK) + >>> click_clone.duration = 1000 + >>> + +Built in Sound Effects +---------------------- + +Some pre-created Sound Effects are already available as examples. These can +be played directly ``audio.play(audio.Effect.SQUEAK)``, or used as a base to +create new effects ``audio.Effect(audio.Effect.SQUEAK, duration=2000)``. + +* ``audio.Effect.SQUEAK`` +* ``audio.Effect.WARBLE`` +* ``audio.Effect.CHIRP`` +* ``audio.Effect.CROAK`` +* ``audio.Effect.CLICK`` + +Sound Effects Example +--------------------- + +:: + + from microbit import * + + # Play a built in Sound Effect + audio.play(audio.Effect.CHIRP) + + # Create a Sound Effect and immediately play it + audio.play(Effect( + freq_start=400, + freq_end=2000, + duration=500, + vol_start=100, + vol_end=255, + wave=audio.WAVE_TRIANGLE, + fx=audio.FX_VIBRATO, + interpolation=audio.LOG + )) + + # Play a Sound Effect instance, modify an attribute, and play it again + my_effect = Effect( + preset=audio.CHIRP + 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 + audio.play(audio.Effect.WARBLE) + my_modified_effect = Effect(audio.Effect.WARBLE, duration=1000) + audio.play(my_modified_effect) + + # Use sensor data to modify and play the existing Sound Effect instance + while True: + my_effect.freq_start=accelerometer.get_x() + my_effect.freq_end=accelerometer.get_y() + audio.play(my_effect) + + if button_a.is_pressed(): + # On button A play an effect and once it's done show an image + audio.play(audio.Effect.CHIRP) + display.show(Image.DUCK) + sleep(500) + elif button_b.is_pressed(): + # On button B play an effect while showing an image + audio.play(audio.Effect.CLICK, wait=False) + display.show(Image.SQUARE) + sleep(500) + + +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. @@ -104,11 +314,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). +32k cycles in micro:bit V1 or 128k in V2, so should be plenty). -Example -======= +AudioFrame Example +------------------ .. include:: ../examples/waveforms.py :code: python From b89aa5d0f53cba0147e665da880c945f229b9246 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Fri, 1 Jul 2022 19:55:54 +0100 Subject: [PATCH 2/7] docs: Change proposed audio.Effect() to audio.SoundEffect() --- docs/audio.rst | 61 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/docs/audio.rst b/docs/audio.rst index 5a41c563a..8d64c685f 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -20,7 +20,7 @@ There are three different kinds of audio sources that can be played using the 2. `Sound Effects <#sound-effects-v2>`_ (**V2**), a way to create custom sounds by configuring its parameters:: - my_effect = audio.Effect(freq_start=400, freq_end=2500, duration=500) + 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) @@ -45,8 +45,8 @@ Functions - ``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. - - ``Effect``: A sound effect, or an iterable of sound effects, created - via the :py:meth:`audio.Effect` class + - ``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 @@ -114,17 +114,17 @@ Sound Effects **V2** ==================== .. py:class:: - Effect(preset=None, freq_start=400, freq_end=200, duration=500, vol_start=100, vol_end=255, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LINEAR) + SoundEffect(preset=None, freq_start=400, freq_end=200, duration=500, vol_start=100, vol_end=255, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LINEAR) - An ``Effect`` instance represents a sound effect, composed by a set of + 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 = Effect(duration=1000)``, and then - change its attributes ``my_effect.duration = 500``. + can first create an effect ``my_effect = SoundEffect(duration=1000)``, + and then change its attributes ``my_effect.duration = 500``. - :param preset: An existing Effect instance to use as a base, its values + :param preset: An existing SoundEffect instance to use as a base, its values are cloned in the new instance, and any additional arguments provided overwrite the base values. :param freq_start: Start Frequency in Hertz (Hz), eg: ``400`` @@ -180,29 +180,29 @@ Sound Effects **V2** in frequency. One of the following values: ``INTER_LINEAR``, ``INTER_CURVE``, ``INTER_LOG``. -The arguments used to create any Sound Effect, including the built in effects, -can be inspected by looking at each of the Effect instance attributes, or by -converting the instance into a string (which can be done via ``str()`` +The arguments used to create any Sound Effect, including the built in ones, +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 ` you can inspect the built in Effects:: - >>> audio.Effect.CROAK - Effect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., interpolation=...) + >>> audio.SoundEffect.CROAK + SoundEffect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., interpolation=...) The built in Effects are immutable, so they cannot be changed. Trying to modify -a built in Effect will throw an exception:: +a built in SoundEffect will throw an exception:: - >>> audio.Effect.CLICK.duration = 1000 + >>> audio.SoundEffect.CLICK.duration = 1000 Traceback (most recent call last): File "", line 1, in TypeError: effect cannot be modified But a new one can be created like this:: - >>> click_clone = Effect(audio.Effect.CLICK) + >>> click_clone = SoundEffect(audio.SoundEffect.CLICK) >>> click_clone.duration = 1000 >>> @@ -210,14 +210,15 @@ Built in Sound Effects ---------------------- Some pre-created Sound Effects are already available as examples. These can -be played directly ``audio.play(audio.Effect.SQUEAK)``, or used as a base to -create new effects ``audio.Effect(audio.Effect.SQUEAK, duration=2000)``. +be played directly ``audio.play(audio.SoundEffect.SQUEAK)``, +or used as a base to create new effects +``audio.SoundEffect(audio.SoundEffect.SQUEAK, duration=2000)``. -* ``audio.Effect.SQUEAK`` -* ``audio.Effect.WARBLE`` -* ``audio.Effect.CHIRP`` -* ``audio.Effect.CROAK`` -* ``audio.Effect.CLICK`` +* ``audio.SoundEffect.SQUEAK`` +* ``audio.SoundEffect.WARBLE`` +* ``audio.SoundEffect.CHIRP`` +* ``audio.SoundEffect.CROAK`` +* ``audio.SoundEffect.CLICK`` Sound Effects Example --------------------- @@ -227,10 +228,10 @@ Sound Effects Example from microbit import * # Play a built in Sound Effect - audio.play(audio.Effect.CHIRP) + audio.play(audio.SoundEffect.CHIRP) # Create a Sound Effect and immediately play it - audio.play(Effect( + audio.play(audio.SoundEffect( freq_start=400, freq_end=2000, duration=500, @@ -242,7 +243,7 @@ Sound Effects Example )) # Play a Sound Effect instance, modify an attribute, and play it again - my_effect = Effect( + my_effect = audio.SoundEffect( preset=audio.CHIRP freq_start=400, freq_end=2000, @@ -253,8 +254,8 @@ Sound Effects Example # You can also create a new effect based on an existing one, and modify # any of its characteristics via arguments - audio.play(audio.Effect.WARBLE) - my_modified_effect = Effect(audio.Effect.WARBLE, duration=1000) + audio.play(audio.SoundEffect.WARBLE) + my_modified_effect = SoundEffect(audio.SoundEffect.WARBLE, duration=1000) audio.play(my_modified_effect) # Use sensor data to modify and play the existing Sound Effect instance @@ -265,12 +266,12 @@ Sound Effects Example if button_a.is_pressed(): # On button A play an effect and once it's done show an image - audio.play(audio.Effect.CHIRP) + audio.play(audio.SoundEffect.CHIRP) display.show(Image.DUCK) sleep(500) elif button_b.is_pressed(): # On button B play an effect while showing an image - audio.play(audio.Effect.CLICK, wait=False) + audio.play(audio.SoundEffect.CLICK, wait=False) display.show(Image.SQUARE) sleep(500) From ce19f55b4a16f2507e3d3e277a77dcfccf988892 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Fri, 1 Jul 2022 20:01:37 +0100 Subject: [PATCH 3/7] docs: Update the proposed default values for audio.SoundEffect(). --- docs/audio.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/audio.rst b/docs/audio.rst index 8d64c685f..9559ba56f 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -114,7 +114,7 @@ Sound Effects **V2** ==================== .. py:class:: - SoundEffect(preset=None, freq_start=400, freq_end=200, duration=500, vol_start=100, vol_end=255, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LINEAR) + SoundEffect(preset=None, freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LOG) An ``SoundEffect`` instance represents a sound effect, composed by a set of parameters configured via the constructor or attributes. From c0a2909120496aa33117d07573c5cd54d18fad60 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Tue, 12 Jul 2022 10:51:56 +0100 Subject: [PATCH 4/7] docs: Move the SoundEffects constants from `audio` to `audio.SoundEffect`. --- docs/audio.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/audio.rst b/docs/audio.rst index 9559ba56f..14930b914 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -209,6 +209,8 @@ But a new one can be created like this:: Built in Sound Effects ---------------------- +⚠️ WARNING: These have not been created/implemented yet + Some pre-created Sound Effects are already available as examples. These can be played directly ``audio.play(audio.SoundEffect.SQUEAK)``, or used as a base to create new effects @@ -237,9 +239,9 @@ Sound Effects Example duration=500, vol_start=100, vol_end=255, - wave=audio.WAVE_TRIANGLE, - fx=audio.FX_VIBRATO, - interpolation=audio.LOG + wave=audio.SoundEffect.WAVE_TRIANGLE, + fx=audio.SoundEffect.FX_VIBRATO, + interpolation=audio.SoundEffect.INTER_LOG )) # Play a Sound Effect instance, modify an attribute, and play it again @@ -315,7 +317,7 @@ 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 -32k cycles in micro:bit V1 or 128k in V2, so should be plenty). +32000 cycles in micro:bit V1 or 128000 in V2, so should be plenty). AudioFrame Example From 63fe6a005f6bed0fade1ff0cd98fbcbf926d3c55 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Tue, 9 Aug 2022 19:53:35 +0100 Subject: [PATCH 5/7] docs: Remove SoundEffect `preset` constructor parameter, and add `copy()` method. --- docs/audio.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/audio.rst b/docs/audio.rst index 14930b914..4ccc5b068 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -23,7 +23,7 @@ There are three different kinds of audio sources that can be played using the 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. `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() @@ -114,7 +114,7 @@ Sound Effects **V2** ==================== .. py:class:: - SoundEffect(preset=None, freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LOG) + SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LOG) An ``SoundEffect`` instance represents a sound effect, composed by a set of parameters configured via the constructor or attributes. @@ -124,9 +124,6 @@ Sound Effects **V2** can first create an effect ``my_effect = SoundEffect(duration=1000)``, and then change its attributes ``my_effect.duration = 500``. - :param preset: An existing SoundEffect instance to use as a base, its values - are cloned in the new instance, and any additional arguments provided - overwrite the base values. :param freq_start: Start Frequency in Hertz (Hz), eg: ``400`` :param freq_end: End Frequency in Hertz (Hz), eg: ``2000`` :param duration: Duration of the sound (ms), eg: ``500`` @@ -142,6 +139,10 @@ Sound Effects **V2** in frequency. One of the following values: ``INTER_LINEAR``, ``INTER_CURVE``, ``INTER_LOG``. + .. py:function:: copy() + + :returns: A copy of the SoundEffect. + .. py:attribute:: freq_start Start Frequency in Hertz (Hz) @@ -189,7 +190,7 @@ function, or by using a function that does the conversion automatically like For example, with the :doc:`REPL ` you can inspect the built in Effects:: - >>> audio.SoundEffect.CROAK + >>> print(audio.SoundEffect.CROAK) SoundEffect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., interpolation=...) The built in Effects are immutable, so they cannot be changed. Trying to modify @@ -198,11 +199,11 @@ a built in SoundEffect will throw an exception:: >>> audio.SoundEffect.CLICK.duration = 1000 Traceback (most recent call last): File "", line 1, in - TypeError: effect cannot be modified + TypeError: SoundEffect cannot be modified -But a new one can be created like this:: +But a new one can be created using the ``copy()`` method:: - >>> click_clone = SoundEffect(audio.SoundEffect.CLICK) + >>> click_clone = audio.SoundEffect.CLICK.copy() >>> click_clone.duration = 1000 >>> @@ -213,8 +214,7 @@ Built in Sound Effects Some pre-created Sound Effects are already available as examples. These can be played directly ``audio.play(audio.SoundEffect.SQUEAK)``, -or used as a base to create new effects -``audio.SoundEffect(audio.SoundEffect.SQUEAK, duration=2000)``. +or they can be cloned as a base to create new effects. * ``audio.SoundEffect.SQUEAK`` * ``audio.SoundEffect.WARBLE`` @@ -257,7 +257,8 @@ Sound Effects Example # You can also create a new effect based on an existing one, and modify # any of its characteristics via arguments audio.play(audio.SoundEffect.WARBLE) - my_modified_effect = SoundEffect(audio.SoundEffect.WARBLE, duration=1000) + my_modified_effect = audio.SoundEffect.WARBLE.copy() + my_modified_effect.duration=1000 audio.play(my_modified_effect) # Use sensor data to modify and play the existing Sound Effect instance From eac0497ddc0dab6613d332f94a112f9bc0b45a6d Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Mon, 15 Aug 2022 11:28:53 +0100 Subject: [PATCH 6/7] docs: Change SoundEffect() `interporlation` parameter to `shape`. --- docs/audio.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/audio.rst b/docs/audio.rst index 4ccc5b068..7f8e9df51 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -114,7 +114,7 @@ Sound Effects **V2** ==================== .. py:class:: - SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, interpolation=INTER_LOG) + SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, shape=SHAPE_LOG) An ``SoundEffect`` instance represents a sound effect, composed by a set of parameters configured via the constructor or attributes. @@ -134,10 +134,10 @@ Sound Effects **V2** ``WAVE_NOISE`` (randomly generated noise). :param fx: Effect to add on the sound, one of the following values: ``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``None``. - :param interpolation: The type of curve between the start and end + :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: ``INTER_LINEAR``, - ``INTER_CURVE``, ``INTER_LOG``. + in frequency. One of the following values: ``SHAPE_LINEAR``, + ``SHAPE_CURVE``, ``SHAPE_LOG``. .. py:function:: copy() @@ -174,12 +174,12 @@ Sound Effects **V2** Effect to add on the sound, one of the following values: ``FX_TREMOLO``, ``FX_VIBRATO``, ``FX_WARBLE``, or ``None``. - .. py:attribute:: interpolation + .. py:attribute:: shape - The type of curve between the start and end + 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: ``INTER_LINEAR``, - ``INTER_CURVE``, ``INTER_LOG``. + in frequency. One of the following values: ``SHAPE_LINEAR``, + ``SHAPE_CURVE``, ``SHAPE_LOG``. The arguments used to create any Sound Effect, including the built in ones, can be inspected by looking at each of the SoundEffect instance attributes, @@ -191,7 +191,7 @@ For example, with the :doc:`REPL ` you can inspect the built in Effects:: >>> print(audio.SoundEffect.CROAK) - SoundEffect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., interpolation=...) + SoundEffect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., shape=...) The built in Effects are immutable, so they cannot be changed. Trying to modify a built in SoundEffect will throw an exception:: @@ -241,7 +241,7 @@ Sound Effects Example vol_end=255, wave=audio.SoundEffect.WAVE_TRIANGLE, fx=audio.SoundEffect.FX_VIBRATO, - interpolation=audio.SoundEffect.INTER_LOG + shape=audio.SoundEffect.SHAPE_LOG )) # Play a Sound Effect instance, modify an attribute, and play it again From 8cfea23019881df94e7c5fc54cef410d46121fd5 Mon Sep 17 00:00:00 2001 From: Carlos Pereira Atencio Date: Tue, 6 Sep 2022 18:45:43 +0100 Subject: [PATCH 7/7] docs: Remove mentions of SoundEffect presets, add ranges, example to file. --- docs/audio.rst | 133 ++++++++++----------------------------- examples/soundeffects.py | 53 ++++++++++++++++ 2 files changed, 87 insertions(+), 99 deletions(-) create mode 100644 examples/soundeffects.py diff --git a/docs/audio.rst b/docs/audio.rst index 7f8e9df51..57cff0d42 100644 --- a/docs/audio.rst +++ b/docs/audio.rst @@ -114,7 +114,7 @@ Sound Effects **V2** ==================== .. py:class:: - SoundEffect(freq_start=500, freq_end=2500, duration=500, vol_start=255, vol_end=0, wave=WAVE_SQUARE, fx=None, shape=SHAPE_LOG) + 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. @@ -124,20 +124,21 @@ Sound Effects **V2** 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), eg: ``400`` - :param freq_end: End Frequency in Hertz (Hz), eg: ``2000`` - :param duration: Duration of the sound (ms), eg: ``500`` - :param vol_start: Start volume value, range 0-255, eg: ``120`` - :param vol_end: End volume value, range 0-255, eg: ``255`` + :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). + ``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 ``None``. + ``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``. + ``SHAPE_CURVE``, ``SHAPE_LOG``. Default: ``SHAPE_LOG`` .. py:function:: copy() @@ -145,23 +146,24 @@ Sound Effects **V2** .. py:attribute:: freq_start - Start Frequency in Hertz (Hz) + Start frequency in Hertz (Hz), a number between ``0`` and ``9999``. .. py:attribute:: freq_end - End Frequency in Hertz (Hz) + End frequency in Hertz (Hz), a number between ``0`` and ``9999```. .. py:attribute:: duration - Duration of the sound (ms), eg: ``500`` + Duration of the sound in milliseconds, a number between ``0`` and + ``9999``. .. py:attribute:: vol_start - Start volume value, range 0-255, eg: ``120`` + Start volume value, a number between ``0`` and ``255``. .. py:attribute:: vol_end - End volume value, range 0-255, eg: ``255`` + End volume value, a number between ``0`` and ``255``. .. py:attribute:: wave @@ -181,103 +183,36 @@ Sound Effects **V2** in frequency. One of the following values: ``SHAPE_LINEAR``, ``SHAPE_CURVE``, ``SHAPE_LOG``. -The arguments used to create any Sound Effect, including the built in ones, +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 ` you can inspect the built -in Effects:: - - >>> print(audio.SoundEffect.CROAK) - SoundEffect(freq_start=..., freq_end=..., duration=..., vol_start=..., vol_end=..., wave=..., fx=..., shape=...) - -The built in Effects are immutable, so they cannot be changed. Trying to modify -a built in SoundEffect will throw an exception:: - - >>> audio.SoundEffect.CLICK.duration = 1000 - Traceback (most recent call last): - File "", line 1, in - TypeError: SoundEffect cannot be modified +For example, with the :doc:`REPL ` you can inspect the +default SoundEffects:: -But a new one can be created using the ``copy()`` method:: + >>> 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) - >>> click_clone = audio.SoundEffect.CLICK.copy() - >>> click_clone.duration = 1000 - >>> +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:: -Built in Sound Effects ----------------------- - -⚠️ WARNING: These have not been created/implemented yet - -Some pre-created Sound Effects are already available as examples. These can -be played directly ``audio.play(audio.SoundEffect.SQUEAK)``, -or they can be cloned as a base to create new effects. - -* ``audio.SoundEffect.SQUEAK`` -* ``audio.SoundEffect.WARBLE`` -* ``audio.SoundEffect.CHIRP`` -* ``audio.SoundEffect.CROAK`` -* ``audio.SoundEffect.CLICK`` + >>> 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 a built in Sound Effect - audio.play(audio.SoundEffect.CHIRP) - - # Create a 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( - preset=audio.CHIRP - 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 - audio.play(audio.SoundEffect.WARBLE) - my_modified_effect = audio.SoundEffect.WARBLE.copy() - my_modified_effect.duration=1000 - audio.play(my_modified_effect) - - # Use sensor data to modify and play the existing Sound Effect instance - while True: - my_effect.freq_start=accelerometer.get_x() - my_effect.freq_end=accelerometer.get_y() - audio.play(my_effect) - - if button_a.is_pressed(): - # On button A play an effect and once it's done show an image - audio.play(audio.SoundEffect.CHIRP) - display.show(Image.DUCK) - sleep(500) - elif button_b.is_pressed(): - # On button B play an effect while showing an image - audio.play(audio.SoundEffect.CLICK, wait=False) - display.show(Image.SQUARE) - sleep(500) - +.. include:: ../examples/soundeffects.py + :code: python AudioFrame ========== diff --git a/examples/soundeffects.py b/examples/soundeffects.py new file mode 100644 index 000000000..343221dfb --- /dev/null +++ b/examples/soundeffects.py @@ -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)