【问题标题】:Play Sounds of Varying Varying Pitch Asynchronously and Concurrently异步并同时播放不同音高的声音
【发布时间】:2017-12-07 06:52:30
【问题描述】:

我的目标是使用 Python 在计算机游戏环境中播放满足以下要求的声音。

  1. 获取一些输入的 WAV 文件并随机将音高更改为原始的 +/- 50%。使用 PyDub 更改采样率似乎是一种简单的方法。

  2. 播放声音。

  3. 能够快速调用此函数,使实际播放中长短时长的声音重叠。

我已经花费了超过 24 个工作小时来寻找满足所有这些要求的方法。我以前在 Visual Basic 中做过这个,我对它在 Python 中的难度感到惊讶。

这是我目前所知道的:

  1. PyGame.Mixer 可以同时播放重叠的声音,但它必须以相同的采样率播放它们。似乎没有办法改变音高。

  2. PyDub 可以通过改变采样率来改变音高,但它不能通过基本播放来播放重叠的声音。而且,我必须将输出的声音写入文件然后立即将其加载回来,这感觉很浪费。

  3. WinSound 可以播放 PyDub 的可变采样率声音,但不能同时播放,甚至不能使用线程。

  4. Playsound 包不适用于 python 3.6。

  5. 如果我使用 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)

提前感谢您的热心帮助!

【问题讨论】:

  • 将您的音频剪辑预处理为各种采样率排列,以便您在运行时直接渲染它们
  • 我不知道该怎么做。不过谢谢你的回复,我会考虑的:)

标签: python multithreading audio pitch sample-rate


【解决方案1】:

多亏了又一个小时的谷歌搜索,我找到了一个关于 PyDub 的晦涩注释来解决这个问题。有一种方法可以实际更改采样率,但“实际上不是”更改采样率。这叫花栗鼠法。

https://github.com/jiaaro/pydub/issues/157#issuecomment-252366466

我真的不假装理解这里的细微差别,但似乎这个概念是“取一个声音,设置采样率到某个修改值,然后转换采样率恢复到传统的 44,100 HZ 值。”

他们给出了这个效果很好的例子:

from pydub import AudioSegment
sound = AudioSegment.from_file('./test/data/test1.mp3')
# shift the pitch up by half an octave (speed will increase proportionally)
octaves = 0.5
new_sample_rate = int(sound.frame_rate * (2.0 ** octaves))
# keep the same samples but tell the computer they ought to be played at the 
# new, higher sample rate. This file sounds like a chipmunk but has a weird sample rate.
chipmunk_sound = sound._spawn(sound.raw_data, overrides={'frame_rate': new_sample_rate})
# now we just convert it to a common sample rate (44.1k - standard audio CD) to 
# make sure it works in regular audio players. Other than potentially losing audio quality (if
# you set it too low - 44.1k is plenty) this should now noticeable change how the audio sounds.
chipmunk_ready_to_export = chipmunk_sound.set_frame_rate(44100)

这对我来说没有多大意义,但它确实有效 :) 希望这对那里的人有所帮助。

【讨论】:

    【解决方案2】:

    这种方法似乎有点可疑。我在link 上向 C++ 人员解释了如何使用 Java 进行变速。

    主要思想是使用线性插值从样本之间获取值,并以非 1 比 1 的速率遍历样本数据。如果您要 150% 并且需要样本 0,则样本 1.5 (介于 1 和 2 之间),插入值。

    【讨论】:

    • 感谢您的回复!事实是我不知道如何使用 Java 或 c++。我下面的方法在 Python 中运行速度很快,所以我会使用它,直到我听得更好。
    • 算法要点与代码无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    相关资源
    最近更新 更多