问题
I currently try to repeat a sound every x ms - where x is dependent on an UDP packet I receive via socket - and I decided to use pygame for that. I used this SO answer to repeat something every x ms: https://stackoverflow.com/a/18954902/3475778
But now I have the problem, that the sound is played very irregular and made a minimal-working example where the problem persists:
import pygame
from pygame.locals import *
pygame.mixer.init()
sound = pygame.mixer.Sound('sound.wav')
def play_sound():
sound.stop()
sound.play()
pygame.init()
clock = pygame.time.Clock()
pygame.time.set_timer(USEREVENT+1, 200)
while True:
# clock.tick(30)
for event in pygame.event.get():
if event.type == USEREVENT+1:
play_sound()
Here is the waveform of what I have recorded from the script via Audacity:
You see that for some reason some samples were played longer than the others. Not very nice for some kind of metronome I want to build.
edit UPDATE: It is not a problem of pygame.time.set_timer, because this code doesn't solve the problem and doesn't rely on pygame.time.set_timer:
import pygame
from datetime import datetime
d = datetime.now()
pygame.mixer.init()
sound = pygame.mixer.Sound('horn_short.wav')
pygame.init()
while True:
if (datetime.now() - d).total_seconds() > 0.2:
sound.play()
d = datetime.now()
has the same problem. The Problem is also under Ubuntu 16.04, Python 3.5 64bit (Anaconda) and a fresh installed pygame.
回答1:
Here is an idea for an alternative approach. If the goal is to play a sound in regular intervals, you might get better results if you (dynamically) pad the sound to the desired interval length, and then simply loop it with Sound.play(loops=-1).
If there are just a handful of valid intervals, it might be easiest to store dedicated sound files and load the appropriate one.
Otherwise, the pygame.sndarray module provides access to a numpy array of the raw sound data, which makes it possible to dynamically generate sound objects of the desired length:
import pygame
import numpy
# Helper function
def getResizedSound(sound, seconds):
frequency, bits, channels = pygame.mixer.get_init()
# Determine silence value
silence = 0 if bits < 0 else (2**bits / 2) - 1
# Get raw sample array of original sound
oldArray = pygame.sndarray.array(sound)
# Create silent sample array with desired length
newSampleCount = int(seconds * frequency)
newShape = (newSampleCount,) + oldArray.shape[1:]
newArray = numpy.full(newShape, silence, dtype=oldArray.dtype)
# Copy original sound to the beginning of the
# silent array, clipping the sound if it is longer
newArray[:oldArray.shape[0]] = oldArray[:newArray.shape[0]]
return pygame.mixer.Sound(newArray)
pygame.mixer.init()
pygame.init()
sound = pygame.mixer.Sound('sound.wav')
resizedSound = getResizedSound(sound, 0.2)
resizedSound.play(loops=-1)
while True:
pass
Using just pygame (and numpy), this approach should have a good chance for an accurate playback.
回答2:
For me the thing worked much better, if i did:
pygame.mixer.pre_init(44100, -16, 2, 256)
before any of the pygame init functions.
回答3:
Ok, you should try using the other way to load a sound:
pygame.mixer.music.load(file=file_directory str)
To play the sound use:
pygame.mixer.music.play(loops=*optional* int, start=*optional* float)
Your code may look like this:
import pygame
pygame.init()
pygame.mixer.init()
sound = pygame.mixer.music.load('sound.wav')
def playSound():
sound.pause()
sound.play()
while True:
pass
For me, that worked better but I am on python 3.7.2 . I don't know about python 3.5, but there aren't many difference between 3.5 and 3.7.2. That should work!
来源:https://stackoverflow.com/questions/41321052/pygame-mixer-sound-play-is-irregular-although-fired-regularly