Pure Python wrapper around SDL2 libraries for cross-platform multimedia development
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive audio playback, recording, and mixing capabilities through SDL2 audio and SDL2_mixer integration. PySDL2 provides both low-level audio device control and high-level audio mixing functions.
Low-level audio device functions for playback and recording.
def SDL_GetNumAudioDevices(iscapture: int) -> int:
"""
Get number of available audio devices.
Parameters:
- iscapture: 0 for playback devices, 1 for recording devices
Returns:
Number of available devices
"""
def SDL_GetAudioDeviceName(index: int, iscapture: int) -> bytes:
"""
Get name of audio device.
Parameters:
- index: device index
- iscapture: 0 for playback, 1 for recording
Returns:
Device name as bytes
"""
def SDL_OpenAudioDevice(device: bytes, iscapture: int, desired: SDL_AudioSpec,
obtained: SDL_AudioSpec, allowed_changes: int) -> int:
"""
Open audio device for playback or recording.
Parameters:
- device: device name (None for default)
- iscapture: 0 for playback, 1 for recording
- desired: desired audio specification
- obtained: actual audio specification (may differ from desired)
- allowed_changes: allowed changes to audio spec
Returns:
Audio device ID (>= 2) on success, 0 on failure
"""
def SDL_CloseAudioDevice(dev: int) -> None:
"""Close audio device."""
def SDL_PauseAudioDevice(dev: int, pause_on: int) -> None:
"""Pause or unpause audio device."""
def SDL_GetAudioDeviceStatus(dev: int) -> int:
"""Get audio device status."""Audio format constants and specification structure.
# Audio format constants
SDL_AUDIO_MASK_BITSIZE: int = 0xFF
SDL_AUDIO_MASK_DATATYPE: int = (1 << 8)
SDL_AUDIO_MASK_ENDIAN: int = (1 << 12)
SDL_AUDIO_MASK_SIGNED: int = (1 << 15)
# Specific audio formats
AUDIO_U8: int = 0x0008 # Unsigned 8-bit
AUDIO_S8: int = 0x8008 # Signed 8-bit
AUDIO_U16LSB: int = 0x0010 # Unsigned 16-bit little-endian
AUDIO_S16LSB: int = 0x8010 # Signed 16-bit little-endian
AUDIO_U16MSB: int = 0x1010 # Unsigned 16-bit big-endian
AUDIO_S16MSB: int = 0x9010 # Signed 16-bit big-endian
AUDIO_U16: int = AUDIO_U16LSB
AUDIO_S16: int = AUDIO_S16LSB
AUDIO_S32LSB: int = 0x8020 # Signed 32-bit little-endian
AUDIO_S32MSB: int = 0x9020 # Signed 32-bit big-endian
AUDIO_S32: int = AUDIO_S32LSB
AUDIO_F32LSB: int = 0x8120 # 32-bit floating point little-endian
AUDIO_F32MSB: int = 0x9120 # 32-bit floating point big-endian
AUDIO_F32: int = AUDIO_F32LSB
class SDL_AudioSpec:
"""Audio specification structure."""
freq: int # Sample rate (samples per second)
format: int # Audio format (AUDIO_S16, etc.)
channels: int # Number of channels (1=mono, 2=stereo)
silence: int # Silence value for audio format
samples: int # Audio buffer size in samples (power of 2)
padding: int # Padding
size: int # Audio buffer size in bytes
callback: ctypes.CFUNCTYPE # Audio callback function
userdata: ctypes.c_void_p # User data for callbackFunctions for converting between audio formats.
def SDL_BuildAudioCVT(cvt: SDL_AudioCVT, src_format: int, src_channels: int, src_rate: int,
dst_format: int, dst_channels: int, dst_rate: int) -> int:
"""Build audio conversion structure."""
def SDL_ConvertAudio(cvt: SDL_AudioCVT) -> int:
"""Convert audio data using conversion structure."""
class SDL_AudioCVT:
"""Audio conversion structure."""
needed: int # Set to 1 if conversion needed
src_format: int # Source audio format
dst_format: int # Destination audio format
rate_incr: float # Rate conversion increment
buf: ctypes.POINTER(ctypes.c_uint8) # Audio buffer
len: int # Original audio buffer length
len_cvt: int # Converted audio buffer length
len_mult: int # Buffer length multiplier
len_ratio: float # Length ratioHigh-level audio mixing functions through SDL2_mixer.
def Mix_OpenAudio(frequency: int, format: int, channels: int, chunksize: int) -> int:
"""
Initialize audio mixer.
Parameters:
- frequency: sample rate (22050, 44100, etc.)
- format: audio format (MIX_DEFAULT_FORMAT recommended)
- channels: number of output channels (1=mono, 2=stereo)
- chunksize: audio buffer size in samples
Returns:
0 on success, -1 on failure
"""
def Mix_CloseAudio() -> None:
"""Close audio mixer."""
def Mix_QuerySpec(frequency: ctypes.POINTER(ctypes.c_int), format: ctypes.POINTER(ctypes.c_uint16),
channels: ctypes.POINTER(ctypes.c_int)) -> int:
"""Query mixer audio format."""
def Mix_AllocateChannels(numchans: int) -> int:
"""Set number of mixing channels."""Functions for loading and playing sound effects.
def Mix_LoadWAV(file: bytes) -> Mix_Chunk:
"""
Load WAV audio file.
Parameters:
- file: path to WAV file as bytes
Returns:
Mix_Chunk object or None on failure
"""
def Mix_LoadWAV_RW(src: SDL_RWops, freesrc: int) -> Mix_Chunk:
"""Load WAV from SDL_RWops."""
def Mix_FreeChunk(chunk: Mix_Chunk) -> None:
"""Free audio chunk."""
def Mix_PlayChannel(channel: int, chunk: Mix_Chunk, loops: int) -> int:
"""
Play audio chunk on channel.
Parameters:
- channel: channel to play on (-1 for first available)
- chunk: audio chunk to play
- loops: number of loops (-1 for infinite)
Returns:
Channel number playing on, -1 on error
"""
def Mix_PlayChannelTimed(channel: int, chunk: Mix_Chunk, loops: int, ticks: int) -> int:
"""Play audio chunk with time limit."""
def Mix_Volume(channel: int, volume: int) -> int:
"""
Set channel volume.
Parameters:
- channel: channel number (-1 for all channels)
- volume: volume level (0-128)
Returns:
Previous volume level
"""
def Mix_Pause(channel: int) -> None:
"""Pause channel."""
def Mix_Resume(channel: int) -> None:
"""Resume channel."""
def Mix_HaltChannel(channel: int) -> int:
"""Stop playing on channel."""
def Mix_Playing(channel: int) -> int:
"""Check if channel is playing."""
def Mix_Paused(channel: int) -> int:
"""Check if channel is paused."""Functions for loading and playing background music.
def Mix_LoadMUS(file: bytes) -> Mix_Music:
"""
Load music file.
Parameters:
- file: path to music file as bytes
Returns:
Mix_Music object or None on failure
"""
def Mix_LoadMUS_RW(src: SDL_RWops, freesrc: int) -> Mix_Music:
"""Load music from SDL_RWops."""
def Mix_FreeMusic(music: Mix_Music) -> None:
"""Free music."""
def Mix_PlayMusic(music: Mix_Music, loops: int) -> int:
"""
Play music.
Parameters:
- music: music to play
- loops: number of loops (-1 for infinite)
Returns:
0 on success, -1 on error
"""
def Mix_FadeInMusic(music: Mix_Music, loops: int, ms: int) -> int:
"""Play music with fade-in effect."""
def Mix_VolumeMusic(volume: int) -> int:
"""
Set music volume.
Parameters:
- volume: volume level (0-128)
Returns:
Previous volume level
"""
def Mix_PauseMusic() -> None:
"""Pause music."""
def Mix_ResumeMusic() -> None:
"""Resume music."""
def Mix_HaltMusic() -> int:
"""Stop music."""
def Mix_FadeOutMusic(ms: int) -> int:
"""Fade out music over time."""
def Mix_PlayingMusic() -> int:
"""Check if music is playing."""
def Mix_PausedMusic() -> int:
"""Check if music is paused."""
def Mix_GetMusicType(music: Mix_Music) -> int:
"""Get music type."""Constants and types for SDL2_mixer.
# Mixer format constants
MIX_DEFAULT_FREQUENCY: int = 22050
MIX_DEFAULT_FORMAT: int = AUDIO_S16LSB
MIX_DEFAULT_CHANNELS: int = 2
MIX_MAX_VOLUME: int = 128
# Mixer initialization flags
MIX_INIT_FLAC: int = 0x00000001
MIX_INIT_MOD: int = 0x00000002
MIX_INIT_MP3: int = 0x00000008
MIX_INIT_OGG: int = 0x00000010
MIX_INIT_MID: int = 0x00000020
MIX_INIT_OPUS: int = 0x00000040
# Music types
MUS_NONE: int = 0
MUS_CMD: int = 1
MUS_WAV: int = 2
MUS_MOD: int = 3
MUS_MID: int = 4
MUS_OGG: int = 5
MUS_MP3: int = 6
MUS_MP3_MAD_UNUSED: int = 7
MUS_FLAC: int = 8
MUS_MODPLUG_UNUSED: int = 9
MUS_OPUS: int = 10
class Mix_Chunk:
"""Audio chunk structure."""
allocated: int # 1 if allocated, 0 if not
abuf: ctypes.POINTER(ctypes.c_uint8) # Audio buffer
alen: int # Audio buffer length
volume: int # Volume (0-128)
class Mix_Music:
"""Opaque music structure."""Advanced audio processing functions.
def Mix_RegisterEffect(chan: int, f: ctypes.CFUNCTYPE, d: ctypes.CFUNCTYPE, arg: ctypes.c_void_p) -> int:
"""Register audio effect function."""
def Mix_UnregisterEffect(channel: int, f: ctypes.CFUNCTYPE) -> int:
"""Unregister audio effect function."""
def Mix_UnregisterAllEffects(channel: int) -> int:
"""Unregister all effects on channel."""
def Mix_SetPostMix(mix_func: ctypes.CFUNCTYPE, arg: ctypes.c_void_p) -> None:
"""Set post-mix callback function."""
def Mix_HookMusic(mix_func: ctypes.CFUNCTYPE, arg: ctypes.c_void_p) -> None:
"""Set music hook callback function."""
def Mix_HookMusicFinished(music_finished: ctypes.CFUNCTYPE) -> None:
"""Set callback for when music finishes."""
def Mix_ChannelFinished(channel_finished: ctypes.CFUNCTYPE) -> None:
"""Set callback for when channel finishes."""import sdl2
import sdl2.sdlmixer
# Initialize SDL2 with audio
sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO)
# Initialize mixer
if sdl2.sdlmixer.Mix_OpenAudio(22050, sdl2.sdlmixer.MIX_DEFAULT_FORMAT, 2, 1024) == -1:
print("Failed to initialize mixer")
exit(1)
# Load sound effect
sound = sdl2.sdlmixer.Mix_LoadWAV(b"sound.wav")
if not sound:
print("Failed to load sound")
exit(1)
# Load background music
music = sdl2.sdlmixer.Mix_LoadMUS(b"music.ogg")
if not music:
print("Failed to load music")
exit(1)
# Play background music
sdl2.sdlmixer.Mix_PlayMusic(music, -1) # Loop forever
# Main loop
running = True
event = sdl2.SDL_Event()
while running:
while sdl2.SDL_PollEvent(event):
if event.type == sdl2.SDL_QUIT:
running = False
elif event.type == sdl2.SDL_KEYDOWN:
if event.key.keysym.sym == sdl2.SDLK_SPACE:
# Play sound effect
sdl2.sdlmixer.Mix_PlayChannel(-1, sound, 0)
# Cleanup
sdl2.sdlmixer.Mix_FreeChunk(sound)
sdl2.sdlmixer.Mix_FreeMusic(music)
sdl2.sdlmixer.Mix_CloseAudio()
sdl2.SDL_Quit()import sdl2
import ctypes
def audio_callback(userdata, stream, length):
"""Audio callback function."""
# Fill stream with audio data
# This example just fills with silence
ctypes.memset(stream, 0, length)
# Initialize SDL2
sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO)
# Set up audio specification
audio_spec = sdl2.SDL_AudioSpec()
audio_spec.freq = 44100
audio_spec.format = sdl2.AUDIO_S16LSB
audio_spec.channels = 2
audio_spec.samples = 1024
audio_spec.callback = audio_callback
audio_spec.userdata = None
# Open audio device
obtained = sdl2.SDL_AudioSpec()
device_id = sdl2.SDL_OpenAudioDevice(None, 0, audio_spec, obtained, 0)
if device_id == 0:
print("Failed to open audio device")
exit(1)
print(f"Opened audio device {device_id}")
print(f"Format: {obtained.format}, Freq: {obtained.freq}, Channels: {obtained.channels}")
# Start audio playback
sdl2.SDL_PauseAudioDevice(device_id, 0)
# Run for a while
sdl2.SDL_Delay(5000)
# Stop and close
sdl2.SDL_PauseAudioDevice(device_id, 1)
sdl2.SDL_CloseAudioDevice(device_id)
sdl2.SDL_Quit()import sdl2
import ctypes
# Initialize SDL2
sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO)
# Set up conversion from 16-bit stereo to 8-bit mono
cvt = sdl2.SDL_AudioCVT()
result = sdl2.SDL_BuildAudioCVT(
cvt,
sdl2.AUDIO_S16LSB, 2, 44100, # Source: 16-bit stereo 44.1kHz
sdl2.AUDIO_U8, 1, 22050 # Dest: 8-bit mono 22.05kHz
)
if result == -1:
print("Audio conversion not possible")
exit(1)
elif result == 0:
print("No conversion needed")
exit(0)
# Allocate buffer for conversion (example with 1024 samples)
original_len = 1024 * 2 * 2 # 1024 samples * 2 channels * 2 bytes per sample
cvt.len = original_len
cvt.buf = (ctypes.c_uint8 * (original_len * cvt.len_mult))()
# Fill buffer with sample data (normally you'd load from file)
# ... fill cvt.buf with audio data ...
# Perform conversion
if sdl2.SDL_ConvertAudio(cvt) == 0:
print(f"Conversion successful. New length: {cvt.len_cvt}")
else:
print("Conversion failed")
sdl2.SDL_Quit()import sdl2
import sdl2.sdlmixer
# Initialize SDL2 and mixer
sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO)
sdl2.sdlmixer.Mix_OpenAudio(44100, sdl2.sdlmixer.MIX_DEFAULT_FORMAT, 2, 1024)
# Load sound
sound = sdl2.sdlmixer.Mix_LoadWAV(b"sound.wav")
# Set channel volume to 50%
sdl2.sdlmixer.Mix_Volume(0, sdl2.sdlmixer.MIX_MAX_VOLUME // 2)
# Play sound on channel 0
channel = sdl2.sdlmixer.Mix_PlayChannel(0, sound, 0)
# Wait for sound to finish
while sdl2.sdlmixer.Mix_Playing(channel):
sdl2.SDL_Delay(100)
# Play with fade-in effect
sdl2.sdlmixer.Mix_FadeInChannelTimed(0, sound, 0, 1000, 5000) # 1s fade-in, 5s total
# Cleanup
sdl2.sdlmixer.Mix_FreeChunk(sound)
sdl2.sdlmixer.Mix_CloseAudio()
sdl2.SDL_Quit()Install with Tessl CLI
npx tessl i tessl/pypi-pysdl2