-
-
Notifications
You must be signed in to change notification settings - Fork 230
Create new _sdl3_mixer module #3692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Starbuck5
wants to merge
10
commits into
pygame-community:main
Choose a base branch
from
Starbuck5:sdl3_mixer_api6
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
6e99e2f
SDL3 _audio module
Starbuck5 78396bb
_audio: use METH_O when possible
Starbuck5 561a5ae
Add bounds checking to device gain
Starbuck5 262f3f9
For some reason I need structmember now
Starbuck5 c5ca2b0
Fix potential overflow, adjust func
Starbuck5 b340b43
Fix 0 byte case and remove unnecessary error block
Starbuck5 371f337
Create new _sdl3_mixer module
Starbuck5 676978c
Address mistakes and job fails
Starbuck5 a4b9505
Implement Audio.from_raw, hopefully fix some things
Starbuck5 2c77509
Fix a few ref handling errors, other errors
Starbuck5 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.