Bad timing when playing audio files with PyGame

2019-09-14 13:35发布

When I play a sound every 0.5 second with PyGame:

import pygame, time

pygame.mixer.init()
s = pygame.mixer.Sound("2.wav")

for i in range(8):
  pygame.mixer.Channel(i).play(s)
  time.sleep(0.5)

it doesn't respect the timing correctly at all.

It's like there are pause of 0.2 sec than 0.7 sec then 0.2 sec again, it's very irregular.


Notes:

  • I know that time.sleep() is not the most accurate in the world, but even with the more accurate solutions from here, the problem is still present

  • Tested on a RaspberryPi

  • The problem is still there if I play many different files s[i].play(), with i in a big range. So the problem doesn't come from the fact it tries to replay the same file

2条回答
爷、活的狠高调
2楼-- · 2019-09-14 14:03

Here is the reason:

enter image description here

Even if we decrease the audio buffer to the minimum supported by the soundcard (1024 or 512 samples instead of pygame's default 4096), the differences will still be there, making irregulat what should be a "metronome beat".

I'll update with a working solution as soon as I find one. (I have a few ideas in this direction).

查看更多
手持菜刀,她持情操
3楼-- · 2019-09-14 14:22

As you wrote in your own answer, the reason for the timing problems very likely is the fact that the audio callback runs decoupled from the rest of the application.

The audio backend typically has some kind of a clock which is accessible from both inside the callback function and outside of it. I see two possible solutions:

  1. use a library that allows you to implement the callback function yourself, calculate the starting times of your sounds inside the callback function, compare those times with the current time of the "audio clock" and write your sound to the output at the appropriate position in the output buffer.

  2. use a library that allows you to specify the exact time (in terms of the "audio clock") when to start playing your sounds. This library would do the steps of the previous point for you.

For the first option, you could use the sounddevice module. The callback function (which you'll have to implement) will get an argument named time, which has an attribute time.outputBufferDacTime, which is a floating point value specifying the time (in seconds) when the first sample of the output buffer will be played back.

Full disclosure: I'm the author of the sounddevice module, so my recommendation is quite biased.

Quite recently, I've started working on the rtmixer module, which can be used for the second option. Please note that this is in very early development state, so use it with caution. With this module, you don't have to write a callback function, you can use the function rtmixer.Mixer.play_buffer() to play an audio buffer at a specified time (in seconds). For reference, you can get the current time from rtmixer.Mixer.time.

查看更多
登录 后发表回答