From 92215207e039ce69f14a075c8f7f885239fa2008 Mon Sep 17 00:00:00 2001 From: Matt Hillsdon Date: Wed, 20 Mar 2024 11:41:22 +0000 Subject: [PATCH] WIP recording playback APIs These are not final and we have outstanding queries. --- lang/en/typeshed/stdlib/microbit/audio.pyi | 49 +++++++++++++-- .../typeshed/stdlib/microbit/microphone.pyi | 63 +++++++++++++++++++ 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/lang/en/typeshed/stdlib/microbit/audio.pyi b/lang/en/typeshed/stdlib/microbit/audio.pyi index 3b1e327..d537ff3 100644 --- a/lang/en/typeshed/stdlib/microbit/audio.pyi +++ b/lang/en/typeshed/stdlib/microbit/audio.pyi @@ -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. @@ -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:: @@ -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. diff --git a/lang/en/typeshed/stdlib/microbit/microphone.pyi b/lang/en/typeshed/stdlib/microbit/microphone.pyi index 68e8f3a..5dc255e 100644 --- a/lang/en/typeshed/stdlib/microbit/microphone.pyi +++ b/lang/en/typeshed/stdlib/microbit/microphone.pyi @@ -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 @@ -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. + """ + ... \ No newline at end of file