Source code for libretro.api.audio

"""
Audio callback and sample rendering types.

Corresponds to :c:type:`retro_audio_callback` and related types in ``libretro.h``.
These types allow cores to push audio samples to the frontend
or to be notified of audio buffer status changes.

.. seealso::

    :class:`.AudioDriver`
        The protocol that uses these types to implement audio output support in libretro.py.

    :mod:`libretro.drivers.audio`
        libretro.py's included :class:`.AudioDriver` implementations.
"""

from ctypes import Structure, c_int16, c_size_t, c_uint
from dataclasses import dataclass

from libretro.ctypes import CBoolArg, CIntArg, TypedFunctionPointer, TypedPointer

retro_audio_sample_t = TypedFunctionPointer[None, [CIntArg[c_int16], CIntArg[c_int16]]]
"""
Render a single stereo audio frame.

Called by the :term:`core` to push one signed 16-bit sample per channel to the frontend.

:param left: Signed 16-bit sample for the left channel.
:param right: Signed 16-bit sample for the right channel.

Corresponds to :c:type:`retro_audio_sample_t` in ``libretro.h``.

.. seealso::

    :meth:`.Core.set_audio_sample`
        The method that exposes this callback to cores.

    :meth:`.AudioDriver.sample`
        The method that implements this callback in libretro.py.
"""

retro_audio_sample_batch_t = TypedFunctionPointer[
    c_size_t, [TypedPointer[c_int16], CIntArg[c_size_t]]
]
"""
Render multiple stereo audio frames at once.

Called by the :term:`core` to push a buffer of interleaved
signed 16-bit left/right samples (i.e. ``[L, R, L, R, ...]``) to the frontend.

:param data: Pointer to a buffer of interleaved signed 16-bit samples.
:param frames: Number of stereo frames in ``data`` (i.e. half the number of samples).
:return: The number of frames that were processed by the frontend.

Corresponds to :c:type:`retro_audio_sample_batch_t` in ``libretro.h``.

.. seealso::

    :meth:`.Core.set_audio_sample_batch`
        The method that exposes this callback to cores.

    :meth:`.AudioDriver.sample_batch`
        The method that implements this callback in libretro.py.
"""

retro_audio_callback_t = TypedFunctionPointer[None, []]
"""
Render audio samples on demand.

Registered by the :term:`core` and called by the :term:`frontend`
when it is ready to receive audio output;
the core should respond by pushing samples through
:c:type:`retro_audio_sample_t` or :c:type:`retro_audio_sample_batch_t`.

.. warning::
    The frontend may invoke this callback from any thread,
    so its implementation must be thread-safe.

Corresponds to :c:type:`retro_audio_callback_t` in ``libretro.h``.

.. seealso::

    :meth:`.AudioDriver.callback`
        The suggested entry point for this registered callback in libretro.py.
"""

retro_audio_set_state_callback_t = TypedFunctionPointer[None, [CBoolArg]]
"""
Notify the core that audio rendering should start or stop.

Registered by the :term:`core` and called by the :term:`frontend`
to indicate whether the audio driver is currently active.

:param enabled: :obj:`True` if the frontend's audio driver is active and ready to receive samples,
    :obj:`False` if it is paused.

Corresponds to :c:type:`retro_audio_set_state_callback_t` in ``libretro.h``.

.. seealso::

    :meth:`.AudioDriver.set_state`
        The suggested entry point for this registered callback in libretro.py.
"""

retro_audio_buffer_status_callback_t = TypedFunctionPointer[
    None, [CBoolArg, CIntArg[c_uint], CBoolArg]
]
"""
Report the frontend's audio buffer occupancy to the core.

Registered by the :term:`core` and called by the :term:`frontend`
right before each frame so the core can react to impending buffer underruns
(for example, by skipping a frame).

:param active: :obj:`True` if the frontend's audio buffer is currently in use,
    :obj:`False` if audio is disabled.
:param occupancy: Audio buffer occupancy as a percentage in the range ``[0, 100]``.
:param underrun_likely: :obj:`True` if the frontend expects an audio buffer
    underrun on the next frame.

Corresponds to :c:type:`retro_audio_buffer_status_callback_t` in ``libretro.h``.

.. seealso::

    :meth:`.AudioDriver.report_buffer_status`
        The suggested entry point for this registered callback in libretro.py.
"""


[docs] @dataclass(init=False, slots=True) class retro_audio_callback(Structure): """ Core-registered callbacks for asynchronous audio rendering. Corresponds to :c:type:`retro_audio_callback` in ``libretro.h``. """ callback: retro_audio_callback_t | None """Called by the frontend to request audio samples from the core.""" set_state: retro_audio_set_state_callback_t | None """Called by the frontend to notify the core whether audio output is active.""" _fields_ = ( ("callback", retro_audio_callback_t), ("set_state", retro_audio_set_state_callback_t), )
[docs] def __deepcopy__(self, _): """ Return a copy of this struct. Intended for use with :func:`copy.deepcopy`. >>> import copy >>> from libretro.api import retro_audio_callback >>> cb = retro_audio_callback() >>> cb_copy = copy.deepcopy(cb) >>> cb_copy == cb True >>> cb_copy is cb False """ return retro_audio_callback(callback=self.callback, set_state=self.set_state)
[docs] @dataclass(init=False, slots=True) class retro_audio_buffer_status_callback(Structure): """ Core-registered callback for audio buffer status reporting. Corresponds to :c:type:`retro_audio_buffer_status_callback` in ``libretro.h``. """ callback: retro_audio_buffer_status_callback_t | None """Called to inform the core of the audio buffer's occupancy.""" _fields_ = (("callback", retro_audio_buffer_status_callback_t),)
[docs] def __call__(self, active: bool, occupancy: int, underrun_likely: bool) -> None: """ Call :attr:`callback` with the given parameters if non-:obj:`None`, otherwise does nothing. :param active: Whether audio is active. :param occupancy: The current audio buffer occupancy. :param underrun_likely: Whether an underrun is likely. >>> from libretro.api import retro_audio_buffer_status_callback >>> cb = retro_audio_buffer_status_callback() >>> cb(True, 50, False) # No-op since callback is None """ if self.callback: self.callback(active, occupancy, underrun_likely)
[docs] def __deepcopy__(self, _): """ Return a copy of this struct with the same callback. Intended for use with :func:`copy.deepcopy`. >>> import copy >>> from libretro.api import retro_audio_buffer_status_callback >>> cb = retro_audio_buffer_status_callback() >>> cb_copy = copy.deepcopy(cb) >>> cb_copy == cb True >>> cb_copy is cb False """ return retro_audio_buffer_status_callback(callback=self.callback)
__all__ = [ "retro_audio_sample_t", "retro_audio_sample_batch_t", "retro_audio_callback_t", "retro_audio_set_state_callback_t", "retro_audio_buffer_status_callback_t", "retro_audio_callback", "retro_audio_buffer_status_callback", ]