我的目标是使用 Python 在计算机游戏环境中播放具有以下要求的声音。
获取一些输入 WAV 文件并随机将音高改变为原始的 +/- 50%。使用 PyDub 更改采样率似乎是一种简单的方法。
播放声音。
能够快速调用此函数,以便在实际播放中长短持续时间的声音重叠。
我花了超过 24 个工作小时来寻找满足所有这些要求的方法。我以前在 Visual Basic 中做过这个,我对它在 Python 中的难度感到惊讶。
到目前为止,这是我所知道的:
PyGame.Mixer 可以同时播放重叠的声音,但它必须以相同的采样率播放它们。似乎没有办法改变音高。
PyDub 可以通过改变采样率来改变音高,但它不能用它的基本播放来播放重叠的声音。而且,我必须将输出的声音写入文件,然后立即将其加载回来,这感觉很浪费。
WinSound 可以播放 PyDub 的不同采样率的声音,但不能同时播放,甚至不能使用线程。
Playsound 包不适用于 python 3.6。
如果我使用 Threading,PyAudio 可以在并发播放的同时播放 PyDub 的可变采样率声音,但是,它不会超过几次,它会导致可怕的内存问题,从而迅速导致 Python 崩溃。
我的问题:如何在不引起问题的情况下实现上述 3 个目标?
这是迄今为止我得到的最好结果(这是 PyAudio 版本,如果测试超过一次或两次会导致崩溃):
from pydub import AudioSegment
from random import random, seed
from time import sleep
import os
import threading
import pyaudio
import wave
def PlayAsyncWithRandPitch(WavPath):
MyBaseFilename = os.path.basename(WavPath)
sound = AudioSegment.from_file(WavPath, format="wav")
seed()
octaves = ((random()-0.50))
print("random octave factor for this sound is: "+str(octaves))
print("current sound frame rate:"+str(sound.frame_rate))
new_sample_rate = int(sound.frame_rate * (2.0 ** octaves))
print("new sound frame rate:"+str(new_sample_rate))
newpitchsound = sound._spawn(sound.raw_data, overrides={'frame_rate': new_sample_rate})
MyTotalNewPath = os.getcwd()+"\\Soundfiles\\Temp\\Mod_"+MyBaseFilename
newpitchsound.export(MyTotalNewPath, format="wav")
SoundThread = threading.Thread(target=PAPlay, args=(MyTotalNewPath,))
SoundThread.start()
#=======================================================================================
#This function is just code for playing a sound in PyAudio
def PAPlay(filename):
CHUNK = 1024
wf = wave.open(filename, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
return
if __name__ == "__main__":
#Example sounds to test if more than one can play at once
PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\RifleMiss.WAV')
sleep(0.2)
PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\splash.wav')
sleep(0.2)
PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\sparkhit1.WAV')
sleep(5.0)
预先感谢您的帮助!