在Python中播放时更改音频的音调(和速度)

在Python中播放时更改音频的音调(和速度),python,windows,audio,pitch,Python,Windows,Audio,Pitch,我正在开发一个播放音乐的Python程序。其中一个功能是一个滑块,用户可以在播放音乐时上下拖动来改变音调 例如,如果音高设置为2,则音乐的音高将提高一个八度,播放速度将提高一倍,持续时间将延长一半。我真正改变的是播放速度,但我需要以实时交互方式进行更改 可以找到在flash中实现此功能的一个很好的示例。(加载需要一点时间,请耐心。) 我已经研究了很多python音频包,但是我还没有找到一个可以改变当前播放的声音的音调的包。我有多个Python版本,因此不需要包支持的版本。我在Windows7上开

我正在开发一个播放音乐的Python程序。其中一个功能是一个滑块,用户可以在播放音乐时上下拖动来改变音调

例如,如果音高设置为2,则音乐的音高将提高一个八度,播放速度将提高一倍,持续时间将延长一半。我真正改变的是播放速度,但我需要以实时交互方式进行更改

可以找到在flash中实现此功能的一个很好的示例。(加载需要一点时间,请耐心。)

我已经研究了很多python音频包,但是我还没有找到一个可以改变当前播放的声音的音调的包。我有多个Python版本,因此不需要包支持的版本。我在Windows7上开发这个


有什么建议吗?

听起来好像你想动态地对音频重新采样


也许您可以尝试使用该模块。它使用了。

您可能希望了解如何使用,并研究该函数

并非所有平台都支持该函数,我自己也没有尝试过,看看它是否完全符合您的要求,以及它的工作情况如何。

在的帮助下,我创建了一个概念验证程序

该程序播放一个名为“music.wav”(与程序位于同一文件夹中)的单声道wav文件,并显示一个短而宽的窗口。在窗口中单击并拖动时,音乐的音调会发生变化。窗口的左侧低两个八度,右侧高两个八度

这里有一些奇怪的行为,我不知道如何修复。如果当前音高较低,则在音高改变之前大约有2秒的延迟。但是,对于高音,音高会实时变化。(随着俯仰变低,延迟平稳增加)。如果
soundOutput.getLeft()<0.2
,我只会向缓冲区添加更多声音。也就是说,如果缓冲器上剩余的声音量小于0.2秒。因此,不应拖延。对于疑难解答,我包含了将
soundOutput.getLeft()写入文件的代码。它倾向于始终保持在0或非常接近0

将读取的帧减少到
waveRead.readframes(100)
会减少延迟,但也会使声音起伏。增加读取的帧数会显著增加延迟

import os, sys, wave, pygame, numpy, pymedia.audio.sound, scikits.samplerate

class Window:
    def __init__(self, width, height, minOctave, maxOctave):
        """
        width, height: the width and height of the screen.
        minOctave, maxOctave: the highest and lowest pitch changes. 0 is no change.
        """
        self.minOctave = minOctave
        self.maxOctave = maxOctave
        self.width = width
        self.mouseDown = False
        self.ratio = 1.0 # The resampling ratio
        waveRead = wave.open(os.path.join(sys.path[0], "music.wav"), 'rb')
        sampleRate = waveRead.getframerate()
        channels = waveRead.getnchannels()
        soundFormat = pymedia.audio.sound.AFMT_S16_LE
        soundOutput = pymedia.audio.sound.Output(sampleRate, channels, soundFormat)
        pygame.init()
        screen = pygame.display.set_mode((width, height), 0)
        screen.fill((255, 255, 255))
        pygame.display.flip()
        fout = open(os.path.join(sys.path[0], "musicdata.txt"), 'w') # For troubleshooting
        byteString = waveRead.readframes(1000) # Read at most 1000 samples from the file.
        while len(byteString) != 0:
            self.handleEvent(pygame.event.poll()) # This does not wait for an event.
            fout.write(str(soundOutput.getLeft()) + "\n") # For troubleshooting
            if soundOutput.getLeft() < 0.2: # If there is less than 0.2 seconds left in the sound buffer.
                array = numpy.fromstring(byteString, dtype=numpy.int16)
                byteString = scikits.samplerate.resample(array, self.ratio, "sinc_fastest").astype(numpy.int16).tostring()
                soundOutput.play(byteString)
                byteString = waveRead.readframes(500) # Read at most 500 samples from the file.
        waveRead.close()
        return

    def handleEvent(self, event):
        if event.type == pygame.QUIT or (event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE):
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            self.mouseDown = True
            self.setRatio(event.pos)
        if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            self.mouseDown = False
        if event.type == pygame.MOUSEMOTION and self.mouseDown:
            self.setRatio(event.pos)
        return None

    def setRatio(self, point):
        self.ratio = 2 ** -(self.minOctave + point[0] * (self.maxOctave - self.minOctave) / float(self.width))
        print(self.ratio)

def main():
    Window(768, 100, -2.0, 2.0)

if __name__ == '__main__':
    main()
导入操作系统、系统、wave、pygame、numpy、pymedia.audio.sound、scikits.samplerate
类窗口:
定义初始(自、宽度、高度、最小八度、最大八度):
"""
宽度,高度:屏幕的宽度和高度。
最小八度,最大八度:最高和最低音高变化。0是不变的。
"""
self.minOctave=minOctave
self.maxOctave=maxOctave
self.width=宽度
self.mouseDown=False
self.ratio=1.0#重采样率
waveRead=wave.open(os.path.join(sys.path[0],“music.wav”),“rb”)
sampleRate=waveRead.getframerate()
channels=waveRead.getnchannels()
soundFormat=pymedia.audio.sound.AFMT_S16_LE
soundOutput=pymedia.audio.sound.Output(采样器、频道、soundFormat)
pygame.init()
screen=pygame.display.set_模式((宽度、高度),0)
屏幕填充((255、255、255))
pygame.display.flip()
fout=open(os.path.join(sys.path[0],“musicdata.txt”),“w')#用于故障排除
byteString=waveRead.readframes(1000)#从文件中最多读取1000个样本。
而len(byteString)!=0:
self.handleEvent(pygame.event.poll())#这不会等待事件。
用于故障排除的fout.write(str(soundOutput.getLeft())+“\n”)#
if soundOutput.getLeft()<0.2:#如果声音缓冲区中剩余的时间少于0.2秒。
array=numpy.fromstring(byteString,dtype=numpy.int16)
byteString=scikits.samplerate.resample(数组,self.ratio,“sinc_faster”).astype(numpy.int16).tostring()
soundOutput.play(byteString)
byteString=waveRead.readframes(500)#从文件中最多读取500个样本。
waveRead.close()
返回
def handleEvent(自身、事件):
如果event.type==pygame.QUIT或(event.type==pygame.KEYUP和event.key==pygame.K_ESCAPE):
sys.exit()
如果event.type==pygame.MOUSEBUTTONDOWN和event.button==1:
self.mouseDown=True
自设置比率(事件位置)
如果event.type==pygame.MOUSEBUTTONUP和event.button==1:
self.mouseDown=False
如果event.type==pygame.MOUSEMOTION和self.mouseDown:
自设置比率(事件位置)
一无所获
def设定比(自身、点):
self.ratio=2**-(self.minOctave+点[0]*(self.maxOctave-self.minOctave)/float(self.width))
打印(自身比率)
def main():
窗口(768100,-2.0,2.0)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()

让我使用的所有软件包都能很好地协同工作是一件痛苦的事情。我正在使用、、和。请注意,scikits.samplerate与NumPy 1.4或更高版本冲突,并且scikits.samplerate 0.3.1需要其中一个软件包(我忘了是哪一个)需要安装工具


如果不这样做,您将不断收到错误提示:没有名为pkg_resources的模块

将一个numpy数组重新采样为另一个。我知道如果我把一个44100赫兹的声音,重新采样到22050赫兹,然后在44100赫兹播放,它会高出一个八度。但是现在我需要一种方法来播放声音并实时进行重采样,这是原始问题的一个重要部分。我假设您已经知道如何用Python实现音频样本的基本播放。对不起,让我重新表述一下我的说法。我需要一种方法来播放声音,使我能够进行动态重采样。我将