2025, Dec 06 19:00

simpleaudio.play() freezes Python loops on 3.12.8/3.13.1 with v1.0.4: root cause and simpleaudio-312compat fix

Fix Python loops freezing on simpleaudio.play(): in 3.12.8/3.13.1 with simpleaudio 1.0.4, access violation at wait_done. Use simpleaudio-312compat 1.0.4.

When a tight loop suddenly stops as soon as you trigger audio playback, it looks like a logic error. In this case it isn’t. The loop halts only when the play call is present, and removing that single line makes the loop run to completion. The root cause turns out to be a specific interaction between the Python runtime and the audio library version.

Minimal example that seems to “freeze” the loop

The behavior is reproducible with a small loop that fires a .play() on every iteration and sleeps briefly between triggers.

import simpleaudio, time, math
counter = 0
pulse = simpleaudio.WaveObject.from_wave_file('metronome2.wav')
while counter < 20:
    pulse.play()
    time.sleep(0.5)
    counter = counter + 1
    print(counter)

With the play call in place, the loop stops progressing. Remove it, and the loop completes as expected. Removing the sleep call can also change the outcome, which further points away from a pure control-flow mistake and toward the audio layer.

What actually causes the failure

The failure is due to the combination of Python 3.12.8 or 3.13.1 with simpleaudio 1.0.4. In that setup, running code that plays a wave and synchronizes on completion fails with an access violation at the wait_done call. That explains why the loop appears to exit or hang as soon as audio playback is involved.

Fix that works in practice

Use simpleaudio-312compat version 1.0.4 instead of the regular simpleaudio package. The import line does not change; the module is still imported as simpleaudio. With the compat package in place, the same logic runs correctly.

import simpleaudio
clip = simpleaudio.WaveObject.from_wave_file('hal2.wav')
turn = 0
while turn < 20:
    turn += 1
    handle = clip.play()
    handle.wait_done()
    print("turn:", turn)

This validates that the issue isn’t in the loop or counters but in the library/runtime pairing. The compat build addresses the crash while allowing the same import path and API usage.

Why you want this on your radar

Audio in timing-sensitive code is often wrapped in loops, so stability of .play() in that context is essential. Version skew between a library and the interpreter can mimic control-flow bugs and waste time in the wrong layer of the stack. Knowing that Python 3.12.8 or 3.13.1 combined with simpleaudio 1.0.4 can produce an access violation helps avoid misdiagnosis and points straight to a known workaround.

Conclusion

If a loop stalls only when simpleaudio.play() is invoked, consider the environment first. On Python 3.12.8 or 3.13.1 with simpleaudio 1.0.4, switch to simpleaudio-312compat 1.0.4 while keeping the same import. If you rely on synchronized playback, calling wait_done on the returned object is a viable pattern once the compat package is in place. Keeping an eye on exact interpreter and package versions saves time and prevents chasing phantom logic errors.