When using pygame
audio playback, I notice high latency (>100 ms):
import pygame
pygame.init()
pygame.mixer.init()
sounda = pygame.mixer.Sound("test.wav")
def callback()
sounda.play()
# callback is called by another function, but I could measure a high latency (> 100ms)
Is pygame
the cause of the latency ? More generally, is low latency audio playback possible with Python ?
Example of application : play some .wav files when MIDI messages arrive from a MIDI keyboard. (I want to code a very very basic music sampler). Of course, the latency will highly depend on the audio interface (ASIO or not ASIO, etc.), but I now want to analyse if low additionnal latency is possible with Python, and if so, which modules are preferred for this purpose.
Probably not.
Pygame is just a wrapper around SDL. In some areas—like this one—it's a very thin wrapper.
But SDL—or, rather,
SDL_mixer
—could easily be the problem.So, you'll probably need to learn a bit about SDL to use
pygame
for audio beyond the usual gaming-style needs. Audio with SDL is a nice overview, although it seems a bit out of date.The first thing to consider is what audio driver you're using. For example, on many linux systems, ALSA can't do low-latency sound, which means that anything you write that ultimately talks to ALSA can't do it either. And if your system is set to use
esd
or some other sound daemon if possible and fall back if necessary, you obviously don't want that here. So, if something like that is your problem, you will have to configureSDL_mixer
to use a different driver.Assuming the driver can handle it, it definitely is possible to do low-latency sound with
pygame.mixer
/SDL_mixer
. But it may not work out of the box.The first thing you'll want to do is pick a smaller buffer size than the default.
Also note that
SDL_mixer
will re-encode your sounds automatically behind your back if they're not in the same sample rate/etc. as the target, which not only adds a bit of latency for the CPU work, it also means the real buffer size has nothing to do with the one you think you're using…An alternative to this is to go around
pygame.mixer
/SDL_mixer
, do the mixing yourself, and go right topygame.sound
/SDL_sound
. That will still have the same driver issues, but anything that's caused bySDL_mixer
(like re-encoding) goes away.If you can't get pygame/SDL to do what you want (e.g., because the only drivers it supports on your system all suck), you will have to use a different library. PythonInMusic on the wiki has hundreds of links, and you can also search PyPI. However, you might want to start from the other side—find a C audio library you want to use, then search for Python bindings for it. For example, pyAudio is a relatively thin wrapper around PortAudio, so it rocks if PortAudio's portability, configurability, and performance requirements meet your needs and its API fits your design, but it sucks otherwise.
Another place things can go wrong is in your code.
That's obviously not the issue in your case, because all you're doing is giving
pygame.mixer
a pre-made sound. But if you decide you need to, say, pre-convert the sounds and feed buffers intopygame.sound
, you may run into the issue that Python is slow at looping and slow at arithmetic.By "slow" I mean on the order of microseconds. Looping once per 20ms buffer is not a problem. Looping once per sample might be. If you're doing any processing, you should consider using NumPy or a dedicated audio library to do the grunt work rather than pure Python.