Skip to content

Commit

Permalink
WIP recording playback APIs
Browse files Browse the repository at this point in the history
These are not final and we have outstanding queries.
  • Loading branch information
microbit-matt-hillsdon committed Mar 20, 2024
1 parent 506c92f commit 9221520
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 5 deletions.
49 changes: 44 additions & 5 deletions lang/en/typeshed/stdlib/microbit/audio.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ from ..microbit import MicroBitDigitalPin, Sound, pin0
from typing import ClassVar, Iterable, Union

def play(
source: Union[Iterable[AudioFrame], Sound, SoundEffect],
source: Union[AudioFrame, Iterable[AudioFrame], Sound, SoundEffect],
wait: bool = True,
pin: MicroBitDigitalPin = pin0,
return_pin: Union[MicroBitDigitalPin, None] = None,
) -> None:
"""Play a built-in sound, sound effect or custom audio frames.
"""Play a built-in sound, sound effect or audio samples using ``AudioFrame``.
Example: ``audio.play(Sound.GIGGLE)``
:param source: A built-in ``Sound`` such as ``Sound.GIGGLE``, a ``SoundEffect`` or sample data as an iterable of ``AudioFrame`` objects.
:param source: A built-in ``Sound`` such as ``Sound.GIGGLE``, a ``SoundEffect`` or sample data as an ``AudioFrame`` object or an iterable of ``AudioFrame`` objects.
:param wait: If ``wait`` is ``True``, this function will block until the sound is complete.
: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 Down Expand Up @@ -136,10 +136,18 @@ class SoundEffect:
"""

class AudioFrame:
"""An ``AudioFrame`` object is a list of 32 samples each of which is a unsigned byte
"""An ``AudioFrame`` object is a list of 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.
The number of samples in an AudioFrame will depend on the
``rate`` (number of samples per second) and ``duration`` parameters.
The total number of samples will always be a round up multiple of 32.
On micro:bit V1 the constructor does not take any arguments,
and an AudioFrame instance is always 32 bytes.
For example, playing 32 samples at 7812 Hz takes just over 4 milliseconds
(1/7812.5 * 32 = 0.004096 = 4096 microseconds).
Example::
Expand All @@ -148,6 +156,37 @@ class AudioFrame:
frame[i] = 252 - i * 8
"""

def __init__(
self,
duration: int = -1,
rate: int = 7812
):
"""Create a new ``AudioFrame``.
Example: ``my_recording = AudioFrame(duration=5000)``
:param duration: Indicates how many milliseconds of audio this instance can store (V2 only).
:param rate: The sampling rate at which data will be stored via the microphone, or played via the ``audio.play()`` function (V2 only).
"""

def set_rate(self, sample_rate: int) -> None:
"""Configure the sampling rate associated with the data in the
``AudioFrame`` instance (V2 only).
For recording from the microphone, increasing the sampling rate
increases the sound quality, but reduces the length of audio it
can store.
During playback, increasing the sampling rate speeds up the sound
and decreasing it slows it down.
"""

def get_rate(self) -> int:
"""Get the sampling rate associated with the data in the
``AudioFrame`` instance (V2 only).
:returns: The configured sampling rate for this ``AudioFrame`` instance.
"""

def copyfrom(self, other: AudioFrame) -> None:
"""Overwrite the data in this ``AudioFrame`` with the data from another ``AudioFrame`` instance.
Expand Down
63 changes: 63 additions & 0 deletions lang/en/typeshed/stdlib/microbit/microphone.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from typing import Optional, Tuple
from ..microbit import SoundEvent
from ..microbit.audio import AudioFrame

def current_event() -> Optional[SoundEvent]:
"""Get the last recorded sound event
Expand Down Expand Up @@ -68,3 +69,65 @@ def sound_level() -> int:
:return: A representation of the sound pressure level in the range 0 to 255.
"""
...

def record(duration: int = 3000, rate: int = 7812) -> AudioFrame:
"""Record sound into an ``AudioFrame`` 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 both memory consumption and sound
quality.
If there isn't enough memory available a ``MemoryError`` will be raised.
:param duration: How long to record in milliseconds.
:param rate: Number of samples to capture per second.
:returns: An ``AudioFrame`` with the sound samples.
"""
...

def record_into(buffer: AudioFrame, rate: int = 7812, wait: bool = True) -> None:
"""Record sound into an existing ``AudioFrame`` until it is filled,
or the ``stop_recording()`` function is called.
:param buffer: An ``AudioFrame`` to record 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.
"""
...

def is_recording() -> bool:
"""Checks whether the microphone is currently recording.
:returns: ``True`` if the microphone is currently recording sound, or
``False`` otherwise.
"""
...

def stop_recording() -> None:
"""Stops a recording running in the background.
"""
...

SENSITIVITY_LOW: float;
"""Low microphone sensitivity."""

SENSITIVITY_MEDIUM: float;
"""Medium microphone sensitivity."""

SENSITIVITY_HIGH: float;
"""High microphone sensitivity."""


def set_sensitivity(gain: float) -> None:
"""Configure the microphone sensitivity.
The default sensitivity is ``microphone.SENSITIVITY_MEDIUM``.
:param gain: The microphone gain. Use ``microphone.SENSITIVITY_LOW``, ``microphone.SENSITIVITY_MEDIUM``, ``microphone.SENSITIVITY_HIGH``, or a value between these levels.
"""
...

0 comments on commit 9221520

Please sign in to comment.