Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion buildconfig/stubs/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
pg_stub_excludes = ['.flake8']

# SDL3 only!
if sdl_api != 3
pg_stub_excludes += ['_audio.pyi']
endif

install_subdir(
'pygame',
exclude_files: '.flake8',
exclude_files: pg_stub_excludes,
install_dir: pg_dir,
strip_directory: true,
install_tag: 'pg-tag',
Expand Down
3 changes: 3 additions & 0 deletions buildconfig/stubs/mypy_allow_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ pygame\.pypm
pygame\._sdl2\.mixer
pygame\.sysfont.*
pygame\.docs.*

# Remove me when we're checking stubs for SDL3!
pygame\._audio
164 changes: 164 additions & 0 deletions buildconfig/stubs/pygame/_audio.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from collections.abc import Callable
from typing import TypeVar

from pygame.typing import FileLike
from typing_extensions import Buffer

# TODO: Support SDL3 stubchecking without failing when on SDL2 builds
# Right now this module is unconditionally skipped in mypy_allow_list.txt

def init() -> None: ...

# def quit() -> None: ...
def get_init() -> bool: ...
def get_current_driver() -> str: ...
def get_drivers() -> list[str]: ...
def get_playback_devices() -> list[AudioDevice]: ...
def get_recording_devices() -> list[AudioDevice]: ...

# def mix_audio(dst: Buffer, src: Buffer, format: AudioFormat, volume: float) -> None: ...
def load_wav(file: FileLike) -> tuple[AudioSpec, bytes]: ...

# def convert_samples(
# src_spec: AudioSpec, src_data: Buffer, dst_spec: AudioSpec
# ) -> bytes: ...

DEFAULT_PLAYBACK_DEVICE: AudioDevice
DEFAULT_RECORDING_DEVICE: AudioDevice

# T = TypeVar("T")
# stream_callback = Callable[[T, AudioStream, int, int], None]
# post_mix_callback = Callable[[T, AudioStream, Buffer], None]
# iteration_callback = Callable[[T, AudioDevice, bool], None]

class AudioFormat:
@property
def bitsize(self) -> int: ...
@property
def bytesize(self) -> int: ...
@property
def is_float(self) -> bool: ...
@property
def is_int(self) -> bool: ...
@property
def is_big_endian(self) -> bool: ...
@property
def is_little_endian(self) -> bool: ...
@property
def is_signed(self) -> bool: ...
@property
def is_unsigned(self) -> bool: ...
@property
def name(self) -> str: ...
@property
def silence_value(self) -> bytes: ...
def __index__(self) -> int: ...
def __repr__(self) -> str: ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think implementing __eq__ would be handy too, so users could say spec.format == U8 or something like that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is handled by Python’s default identity comparison, because AudioFormats are singletons.

UNKNOWN: AudioFormat
U8: AudioFormat
S8: AudioFormat
S16LE: AudioFormat
S16BE: AudioFormat
S32LE: AudioFormat
S32BE: AudioFormat
F32LE: AudioFormat
F32BE: AudioFormat
S16: AudioFormat
S32: AudioFormat
F32: AudioFormat

class AudioSpec:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be a dataclass?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe in the first draft it was, but when I actually went to write it I wanted understandable full control over all of the runtime consequences. Like I made the fields immutable. So for the typing and implementation I don’t see an advantage to making it into a dataclass.

def __init__(self, format: AudioFormat, channels: int, frequency: int) -> None: ...
@property
def format(self) -> AudioFormat: ...
@property
def channels(self) -> int: ...
@property
def frequency(self) -> int: ...
@property
def framesize(self) -> int: ...
def __repr__(self) -> str: ...

class AudioDevice:
def open(self, spec: AudioSpec | None = None) -> LogicalAudioDevice: ...
# def open_stream(
# self,
# spec: AudioSpec | None,
# callback: stream_callback | None,
# userdata: T | None,
# ) -> AudioStream: ...
@property
def is_playback(self) -> bool: ...
@property
def name(self) -> str: ...
# Need something for https://wiki.libsdl.org/SDL3/SDL_GetAudioDeviceFormat
@property
def channel_map(self) -> list[int] | None: ...

class LogicalAudioDevice(AudioDevice):
def pause(self) -> None: ...
def resume(self) -> None: ...
@property
def paused(self) -> bool: ...
@property
def gain(self) -> float: ...
@gain.setter
def gain(self, value: float) -> None: ...
# def set_iteration_callbacks(
# self,
# start: iteration_callback | None,
# end: iteration_callback | None,
# userdata: T,
# ) -> None: ...
# def set_post_mix_callback(
# self, callback: post_mix_callback | None, userdata: T
# ) -> None: ...

class AudioStream:
def __init__(self, src_spec: AudioSpec, dst_spec: AudioSpec) -> None: ...
def bind(self, device: LogicalAudioDevice) -> None: ...
def unbind(self) -> None: ...
def clear(self) -> None: ...
def flush(self) -> None: ...
@property
def num_available_bytes(self) -> int: ...
@property
def num_queued_bytes(self) -> int: ...
def get_data(self, size: int) -> bytes: ...
def put_data(self, data: Buffer) -> None: ...
def pause_device(self) -> None: ...
def resume_device(self) -> None: ...
@property
def device_paused(self) -> bool: ...
@property
def device(self) -> LogicalAudioDevice | None: ...
@property
def src_spec(self) -> AudioSpec: ...
@src_spec.setter
def src_spec(self, value: AudioSpec) -> None: ...
@property
def dst_spec(self) -> AudioSpec: ...
@dst_spec.setter
def dst_spec(self, value: AudioSpec) -> None: ...
@property
def gain(self) -> float: ...
@gain.setter
def gain(self, value: float) -> None: ...
@property
def frequency_ratio(self) -> float: ...
@frequency_ratio.setter
def frequency_ratio(self, value: float) -> None: ...
# def set_input_channel_map(self, channel_map: list[int] | None) -> None: ...
# def get_input_channel_map(self) -> list[int] | None: ...
# def set_output_channel_map(self, channel_map: list[int] | None) -> None: ...
# def get_output_channel_map(self) -> list[int] | None: ...
def lock(self) -> None: ...
def unlock(self) -> None: ...
# def set_get_callback(
# self, callback: stream_callback | None, userdata: T
# ) -> None: ...
# def set_put_callback(
# self, callback: stream_callback | None, userdata: T
# ) -> None: ...
def __repr__(self) -> str: ...
Loading
Loading