Another a very big change was made in SDL 3.3 and SDL3_mixer 3.1. But it works with SDL 3.2.28 and SDL_mixer 3.0.0. This code does not work anymore with PySDL 0.9.9b1: play-sound-by-click-pysdl3-python.zip (352.0 KB)
# pip install PySDL3
# py main.py
import ctypes
import os
os.environ["SDL_MAIN_USE_CALLBACKS"] = "1"
os.environ["SDL_RENDER_DRIVER"] = "opengl"
import sdl3
renderer = ctypes.POINTER(sdl3.SDL_Renderer)()
window = ctypes.POINTER(sdl3.SDL_Window)()
wave = ctypes.POINTER(sdl3.Mix_Chunk)()
@sdl3.SDL_AppInit_func
def SDL_AppInit(appstate, argc, argv):
global wave
if not sdl3.SDL_Init(sdl3.SDL_INIT_VIDEO | sdl3.SDL_INIT_AUDIO):
sdl3.SDL_Log("Couldn't initialize SDL: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
if not sdl3.SDL_CreateWindowAndRenderer("Play a sound by click using PySDL3".encode(), 380, 380, 0, window, renderer):
sdl3.SDL_Log("Couldn't create window/renderer: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
sdl3.SDL_SetRenderVSync(renderer, 1) # Turn on vertical sync
spec = sdl3.SDL_AudioSpec()
spec.freq = sdl3.MIX_DEFAULT_FREQUENCY
spec.format = sdl3.MIX_DEFAULT_FORMAT
spec.channels = sdl3.MIX_DEFAULT_CHANNELS
# Open the audio device
if not sdl3.Mix_OpenAudio(0, spec):
sdl3.SDL_Log("Couldn't open an audio device: %s".encode() % SDL_GetError())
return sdl3.SDL_APP_FAILURE
# Load the WAV file
soundFilePath = "./assets/audio/picked-coin-echo-2.wav".encode();
wave = sdl3.Mix_LoadWAV(soundFilePath);
if not wave:
sdl3.SDL_Log("Couldn't open the file: %s".encode() % soundFilePath)
sdl3.SDL_Log("Error: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE;
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppEvent_func
def SDL_AppEvent(appstate, event):
if sdl3.SDL_DEREFERENCE(event).type == sdl3.SDL_EVENT_QUIT:
return sdl3.SDL_APP_SUCCESS
elif sdl3.SDL_DEREFERENCE(event).type == sdl3.SDL_EVENT_MOUSE_BUTTON_DOWN:
# Play the sound effect
sdl3.Mix_PlayChannel(-1, wave, 0);
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppIterate_func
def SDL_AppIterate(appstate):
sdl3.SDL_SetRenderDrawColor(renderer, 210, 220, 210, sdl3.SDL_ALPHA_OPAQUE)
sdl3.SDL_RenderClear(renderer)
sdl3.SDL_RenderPresent(renderer)
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppQuit_func
def SDL_AppQuit(appstate, result):
global wave
# SDL will clean up the window/renderer for us
if wave:
sdl3.Mix_FreeChunk(wave)
wave = None
If you try to run it with PySDL 0.9.9b1 you will see this error:
wave = ctypes.POINTER(sdl3.Mix_Chunk)()
^^^^^^^^^^^^^^
AttributeError: module 'sdl3' has no attribute 'Mix_Chunk'
But this example works with SDL 3.2.28 and SDL3_mixer 3.0.0 in C: GitHub repository. You can test it in the browser (WASM) - click on the canvas to hear a sound. Console output for C app:
Compiled SDL3 version: 3.2.28
Linked SDL3 version: 3.3.4
SDL3_mixer version: 3.0.0
So, I have fixed the Play a sound by click example in Python: play-sound-pysdl-3.3.2-python.zip (352.4 KB)
# pip install PySDL3
# py main.py
import ctypes
import os
os.environ["SDL_MAIN_USE_CALLBACKS"] = "1"
os.environ["SDL_RENDER_DRIVER"] = "opengl"
import sdl3
renderer = ctypes.POINTER(sdl3.SDL_Renderer)()
window = ctypes.POINTER(sdl3.SDL_Window)()
mixer = ctypes.POINTER(sdl3.MIX_Mixer)()
audio = ctypes.POINTER(sdl3.MIX_Audio)()
@sdl3.SDL_AppInit_func
def SDL_AppInit(appstate, argc, argv):
global mixer, audio
sdl3.SDL_SetHint(b"SDL_MIXER_DISABLE_DRFLAC", b"1")
sdl3.SDL_SetHint(b"SDL_MIXER_DISABLE_DRMP3", b"1")
if not sdl3.SDL_Init(sdl3.SDL_INIT_VIDEO): # | sdl3.SDL_INIT_AUDIO
sdl3.SDL_Log("Couldn't initialize SDL: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
title = "Play a sound by click using PySDL3".encode()
if not sdl3.SDL_CreateWindowAndRenderer(title, 380, 380, 0, window, renderer):
sdl3.SDL_Log("Couldn't create window/renderer: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
sdl3.SDL_SetRenderVSync(renderer, 1) # Turn on vertical sync
# Get the compiled SDL version
compiled = sdl3.SDL_VERSION
compiled_major = sdl3.SDL_VERSIONNUM_MAJOR(compiled)
compiled_minor = sdl3.SDL_VERSIONNUM_MINOR(compiled)
compiled_micro = sdl3.SDL_VERSIONNUM_MICRO(compiled)
print(f"Compiled SDL version: {compiled_major}.{compiled_minor}.{compiled_micro}")
# Get the linked SDL version
linked = sdl3.SDL_GetVersion()
linked_major = sdl3.SDL_VERSIONNUM_MAJOR(linked)
linked_minor = sdl3.SDL_VERSIONNUM_MINOR(linked)
linked_micro = sdl3.SDL_VERSIONNUM_MICRO(linked)
print(f"Linked SDL version: {linked_major}.{linked_minor}.{linked_micro}")
v = sdl3.MIX_Version()
major = sdl3.SDL_VERSIONNUM_MAJOR(v)
minor = sdl3.SDL_VERSIONNUM_MINOR(v)
micro = sdl3.SDL_VERSIONNUM_MICRO(v)
msg = f"SDL3_mixer version: {major}.{minor}.{micro}".encode("utf-8")
sdl3.SDL_Log(msg)
if not sdl3.MIX_Init():
sdl3.SDL_Log("MIX_Init failed: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_FAILURE
mixer = sdl3.MIX_CreateMixerDevice(sdl3.SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, None)
if not mixer:
err = sdl3.SDL_GetError().decode("utf-8")
sdl3.SDL_Log(f"Couldn't create mixer: {err}".encode())
return sdl3.SDL_APP_FAILURE
mixerspec = sdl3.SDL_AudioSpec()
sdl3.MIX_GetMixerFormat(mixer, ctypes.byref(mixerspec))
fmt_name = sdl3.SDL_GetAudioFormatName(mixerspec.format).decode('utf-8')
msg = f"Mixer is format {fmt_name}, {mixerspec.channels} channels, {mixerspec.freq} frequency".encode()
sdl3.SDL_Log(msg)
# --- Print available audio decoders ---
sdl3.SDL_Log(b"Available MIXER decoders:")
num_decoders = sdl3.MIX_GetNumAudioDecoders()
if num_decoders < 0:
err = sdl3.SDL_GetError().decode("utf-8")
msg = f" - [error ({err})]".encode("utf-8")
sdl3.SDL_Log(msg)
elif num_decoders == 0:
sdl3.SDL_Log(b" - [none]")
else:
for i in range(num_decoders):
decoder_name = sdl3.MIX_GetAudioDecoder(i).decode("utf-8")
msg = f" - {decoder_name}".encode("utf-8")
sdl3.SDL_Log(msg)
# --- Load audio file ---
audiofname = b"./assets/audio/picked-coin-echo-2.wav" # bytes for SDL
audio = sdl3.MIX_LoadAudio(mixer, audiofname, False)
if not audio:
err = sdl3.SDL_GetError().decode("utf-8")
msg = f"Failed to load '{audiofname.decode()}' ({err})".encode("utf-8")
sdl3.SDL_Log(msg)
else:
audiospec = sdl3.SDL_AudioSpec()
sdl3.MIX_GetAudioFormat(audio, ctypes.byref(audiospec))
fmt_name = sdl3.SDL_GetAudioFormatName(audiospec.format).decode("utf-8")
channels_text = "" if audiospec.channels == 1 else "s"
msg = f"{audiofname.decode()}: {fmt_name}, {audiospec.channels} channel{channels_text}, {audiospec.freq} freq".encode("utf-8")
sdl3.SDL_Log(msg)
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppEvent_func
def SDL_AppEvent(appstate, event):
if sdl3.SDL_DEREFERENCE(event).type == sdl3.SDL_EVENT_QUIT:
return sdl3.SDL_APP_SUCCESS
elif sdl3.SDL_DEREFERENCE(event).type == sdl3.SDL_EVENT_MOUSE_BUTTON_DOWN:
# Play the sound effect
if not sdl3.MIX_PlayAudio(mixer, audio):
sdl3.SDL_Log("Failed to play audio: %s".encode() % sdl3.SDL_GetError())
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppIterate_func
def SDL_AppIterate(appstate):
sdl3.SDL_SetRenderDrawColor(renderer, 210, 220, 210, sdl3.SDL_ALPHA_OPAQUE)
sdl3.SDL_RenderClear(renderer)
sdl3.SDL_RenderPresent(renderer)
return sdl3.SDL_APP_CONTINUE
@sdl3.SDL_AppQuit_func
def SDL_AppQuit(appstate, result):
global audio, mixer, renderer, window
if audio:
sdl3.MIX_DestroyAudio(audio)
audio = None
if mixer:
sdl3.MIX_DestroyMixer(mixer)
mixer = None
if renderer:
sdl3.SDL_DestroyRenderer(renderer)
renderer = None
if window:
sdl3.SDL_DestroyWindow(window)
window = None
sdl3.MIX_Quit()
sdl3.SDL_Quit()
Console output:
Compiled SDL version: 3.3.2
Linked SDL version: 3.3.2
SDL3_mixer version: 3.1.0
C version with SDL 3.3.4:
Console output:
Compiled SDL version: 3.3.4
Linked SDL3 version: 3.3.4
SDL3_mixer version: 3.1.0