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
11 changes: 11 additions & 0 deletions .github/workflows/build-sdl3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,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
8 changes: 4 additions & 4 deletions buildconfig/download_win_prebuilt.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ def get_urls(x86=True, x64=True):
'ea302368159ca64056b2519b3a624525f625220e',
],
[
'https://github.com/libsdl-org/SDL_mixer/releases/download/prerelease-3.1.2/SDL3_mixer-devel-3.1.2-VC.zip',
'055992eb135197e74644bea65fdc8f8f0838b4ed'
'https://github.com/libsdl-org/SDL_mixer/releases/download/release-3.2.0/SDL3_mixer-devel-3.2.0-VC.zip',
'1d3b0d49a2af69ca8287f1ee6d024e960e28c4e8'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
]
])
if x86:
Expand Down Expand Up @@ -250,12 +250,12 @@ def copy(src, dst):
copy(
os.path.join(
temp_dir,
'SDL3_mixer-devel-3.1.2-VC/SDL3_mixer-3.1.2'
'SDL3_mixer-devel-3.2.0-VC/SDL3_mixer-3.2.0'
),
os.path.join(
move_to_dir,
prebuilt_dir,
'SDL3_mixer-3.1.2'
'SDL3_mixer-3.2.0'
)
)
copy(
Expand Down
2 changes: 1 addition & 1 deletion buildconfig/stubs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pg_stub_excludes = ['.flake8']

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

install_subdir(
Expand Down
1 change: 1 addition & 0 deletions buildconfig/stubs/mypy_allow_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ pygame\.docs.*

# Remove me when we're checking stubs for SDL3!
pygame\._audio
pygame\._sdl3_mixer
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) -> None: ...
# 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) -> None: ...
# 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,
ms: int = -1,
preferred_mixer: Mixer | None = None,
) -> 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) -> None: ...
@property
def frequency_ratio(self) -> float: ...
@frequency_ratio.setter
def frequency_ratio(self, value: float) -> None: ...
# 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 @@ -215,7 +215,7 @@ if plat == 'win'

sdl_ver = (sdl_api == 3) ? '3.4.0' : '2.32.10'
sdl_image_ver = (sdl_api == 3) ? '3.4.0' : '2.8.10'
sdl_mixer_ver = '2.8.1'
sdl_mixer_ver = (sdl_api == 3) ? '3.2.0' : '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
11 changes: 2 additions & 9 deletions src_c/_base_audio.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#include "pygame.h"
#include "pgcompat.h"
#include "structmember.h"
#include "_base_audio.h"

// Useful heap type example @
// https://github.com/python/cpython/blob/main/Modules/xxlimited.c

// ***************************************************************************
// OVERALL DEFINITIONS
// OVERALL DEFINITIONS (Also see header file)
// ***************************************************************************

typedef struct {
Expand All @@ -17,14 +18,6 @@ typedef struct {

#define GET_STATE(x) (audio_state *)PyModule_GetState(x)

typedef struct {
PyObject_HEAD SDL_AudioDeviceID devid;
} PGAudioDeviceStateObject;

typedef struct {
PyObject_HEAD SDL_AudioStream *stream;
} PGAudioStreamStateObject;

#define AUDIO_INIT_CHECK(module) \
if (!(GET_STATE(module))->audio_initialized) { \
return RAISE(pgExc_SDLError, "audio not initialized"); \
Expand Down
16 changes: 16 additions & 0 deletions src_c/_base_audio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef _PG_BASE_AUDIO_H
#define _PG_BASE_AUDIO_H

#include "pygame.h"

/* SHARED DEFINITIONS FOR OTHER MODULES TO BE ABLE TO SEE. */

typedef struct {
PyObject_HEAD SDL_AudioDeviceID devid;
} PGAudioDeviceStateObject;

typedef struct {
PyObject_HEAD SDL_AudioStream *stream;
} PGAudioStreamStateObject;

#endif
Loading
Loading