Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
11 changes: 11 additions & 0 deletions .github/workflows/build-sdl3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ jobs:
cmake --build . --config Release --parallel
sudo cmake --install . --config Release

- name: Install SDL3_mixer
if: matrix.os != 'windows-latest'
run: |
git clone https://github.com/libsdl-org/SDL_mixer
cd SDL_mixer
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --config Release --parallel
sudo cmake --install . --config Release

- name: Build with SDL3
run: python3 dev.py build --sdl3

Expand Down
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

from pygame 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
Loading
Loading