Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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', '_sdl3_mixer.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
4 changes: 4 additions & 0 deletions buildconfig/stubs/mypy_allow_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ pygame\.pypm
pygame\._sdl2\.mixer
pygame\.sysfont.*
pygame\.docs.*

# Remove me when we're checking stubs for SDL3!
pygame\._audio
pygame\._sdl3_mixer
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: ...

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:
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: ...
184 changes: 184 additions & 0 deletions buildconfig/stubs/pygame/_sdl3_mixer.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import dataclasses
from collections.abc import Callable
from typing import Any, Type, TypedDict, TypeVar

import _audio as audio
from pygame.typing import FileLike
from typing_extensions import Buffer

def init() -> None: ...

# def quit() -> None: ...
def get_sdl_mixer_version(linked: bool = True) -> tuple[int, int, int]: ...
def ms_to_frames(sample_rate: int, ms: int) -> int: ...
def frames_to_ms(sample_rate: int, frames: int) -> int: ...
def get_decoders() -> list[str]: ...

# T = TypeVar("T")
# track_stopped_callback = Callable[[T, Track], None]
# track_mix_callback = Callable[[T, Track, audio.AudioSpec, Buffer], None]
# group_mix_callback = Callable[[T, Group, audio.AudioSpec, Buffer], None]
# post_mix_callback = Callable[[T, Mixer, audio.AudioSpec, Buffer], None]

class Mixer:
def __init__(
self,
device: audio.AudioDevice = audio.DEFAULT_PLAYBACK_DEVICE,
spec: audio.AudioSpec | None = None,
) -> None: ...
@property
def gain(self) -> float: ...
@gain.setter
def gain(self, value: float): ...
# TODO: implement frame kwargs, implement MIX_PROP_PLAY_FADE_IN_START_GAIN_FLOAT
def play_tag(
self,
tag: str,
loops: int = 0,
max_ms: int = -1,
start_ms: int = 0,
loop_start_ms: int = 0,
fadein_ms: int = 0,
append_silence_ms: int = 0,
) -> None: ...
def stop_tag(self, tag: str, fade_out_ms: int = 0) -> None: ...
def pause_tag(self, tag: str) -> None: ...
def resume_tag(self, tag: str) -> None: ...
def set_tag_gain(self, tag: str, gain: float) -> None: ...
# def get_tag_tracks(self, tag: str) -> list[Track]: ...
def play_audio(self, audio: Audio) -> None: ...
def stop_all_tracks(self, fade_out_ms: int = 0) -> None: ...
def pause_all_tracks(self) -> None: ...
def resume_all_tracks(self) -> None: ...
@property
def spec(self) -> audio.AudioSpec: ...
# @property
# def frequency_ratio(self) -> float: ...
# @frequency_ratio.setter
# def frequency_ratio(self, value: float): ...
# def set_post_mix_callback(
# self, callback: post_mix_callback | None, userdata: T
# ) -> None: ...

# class MemoryMixer(Mixer):
# def __init__(self, spec: audio.AudioSpec) -> None: ...
# def generate(self, buffer: Buffer, buflen: int) -> None: ...

class Audio:
def __init__(
self,
file: FileLike,
predecode: bool = False,
preferred_mixer: Mixer | None = None,
) -> None: ...
# @classmethod
# def from_raw(
# cls, buffer: Buffer, spec: audio.AudioSpec, preferred_mixer: Mixer | None = None
# ) -> Audio: ...
@classmethod
def from_sine_wave(
cls,
hz: int,
amplitude: float,
preferred_mixer: Mixer | None = None,
ms: int = -1,
) -> Audio: ...
@property
def duration_frames(self) -> int | None: ...
@property
def duration_ms(self) -> int | None: ...
# TODO: just infinite? audio.infinite flows better I think.
@property
def duration_infinite(self) -> bool: ...
@property
def spec(self) -> audio.AudioSpec: ...
def ms_to_frames(self, ms: int) -> int: ...
def frames_to_ms(self, frames: int) -> int: ...
def get_metadata(self) -> AudioMetadata: ...

# class Group:
# def __init__(self, mixer: Mixer) -> None: ...
# @property
# def mixer(self) -> Mixer: ...
# def set_post_mix_callback(
# self, callback: group_mix_callback | None, userdata: Type[T]
# ) -> None: ...

class Track:
def __init__(self, mixer: Mixer) -> None: ...
# Potential idea?
# set_source(self, source: Audio | audio.AudioStream | FileLike | None) -> None: ...
# get_source for filestream could be difficult to implement in a reasonable way.
def set_audio(self, audio: Audio | None) -> None: ...
def get_audio(self) -> Audio | None: ...
def set_audiostream(self, audiostream: audio.AudioStream | None) -> None: ...
def get_audiostream(self) -> audio.AudioStream | None: ...
def set_filestream(self, file: FileLike) -> None: ...
# TODO: implement MIX_PROP_PLAY_FADE_IN_START_GAIN_FLOAT
def play(
self,
loops: int = 0,
max_frame: int = -1,
max_ms: int = -1,
start_frame: int = 0,
start_ms: int = 0,
loop_start_frame: int = 0,
loop_start_ms: int = 0,
fadein_frames: int = 0,
fadein_ms: int = 0,
append_silence_frames: int = 0,
append_silence_ms: int = 0,
) -> None: ...
@property
def mixer(self) -> Mixer: ...
def add_tag(self, tag: str) -> None: ...
def remove_tag(self, tag: str) -> None: ...
# def get_tags(self) -> list[str]: ...
# def set_group(self, group: Group | None) -> None: ...
def set_playback_position(self, frames: int) -> None: ...
def get_playback_position(self) -> int: ...
def get_remaining_frames(self) -> int | None: ...
def ms_to_frames(self, ms: int) -> int: ...
def frames_to_ms(self, frames: int) -> int: ...
def stop(self, fade_out_frames: int = 0) -> None: ...
def pause(self) -> None: ...
def resume(self) -> None: ...
@property
def playing(self) -> bool: ...
@property
def paused(self) -> bool: ...
@property
def loops(self) -> int: ...
@property
def gain(self) -> float: ...
@gain.setter
def gain(self, value: float): ...
@property
def frequency_ratio(self) -> float: ...
@frequency_ratio.setter
def frequency_ratio(self, value: float): ...
# def set_output_channel_map(self, channel_map: list[int] | None) -> None: ...
def set_stereo(self, gains: tuple[float, float] | None) -> None: ...
def set_3d_position(self, position: tuple[float, float, float] | None) -> None: ...
def get_3d_position(self) -> tuple[float, float, float]: ...
# def set_stopped_callback(
# self, callback: track_stopped_callback | None, userdata: T
# ) -> None: ...
# def set_raw_callback(
# self, callback: track_mix_callback | None, userdata: T
# ) -> None: ...

# class AudioDecoder:
# def __init__(self, file: FileLike) -> None: ...
# @property
# def spec(self) -> audio.AudioSpec: ...
# def decode(buffer: Buffer, spec: audio.AudioSpec) -> int: ...

@dataclasses.dataclass(frozen=True)
class AudioMetadata:
title: str | None
artist: str | None
album: str | None
copyright: str | None
track_num: int | None
total_tracks: int | None
1 change: 0 additions & 1 deletion dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

SDL3_ARGS = [
"-Csetup-args=-Dsdl_api=3",
"-Csetup-args=-Dmixer=disabled",
]
COVERAGE_ARGS = ["-Csetup-args=-Dcoverage=true"]

Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ if plat == 'win' and host_machine.cpu_family().startswith('x86')
# consider meson wraps? Hopefully can also get the same build path as below
sdl_ver = (sdl_api == 3) ? '3.4.0' : '2.32.10'
sdl_image_ver = (sdl_api == 3) ? '3.4.0' : '2.8.8'
sdl_mixer_ver = '2.8.1'
sdl_mixer_ver = (sdl_api == 3) ? '3.1.2' : '2.8.1'
sdl_ttf_ver = (sdl_api == 3) ? '3.2.2' : '2.24.0'

arch_suffix = 'x' + host_machine.cpu_family().substring(-2)
Expand Down
Loading
Loading