我正在研究播放音乐的Python程序。一个功能是一个滑块,用户可以向上或向下拖动以在播放时改变音乐的音高。
例如,如果音高设置为2,那么音乐将发出一个八度音高,它将播放两倍的速度,并且它将持续一半的时间。所有我真正改变的是播放速度,但我需要实时交互式地进行。
可以在here找到在Flash中实现此功能的一个很好的示例。 (需要一点点加载,耐心等待。)
我已经研究了许多python音频包,但我还没有找到一个可以改变当前正在播放的声音的音高。我有多个版本的Python,因此不需要包支持的版本。我正在Windows 7上开发它。
有什么建议吗?
答案 0 :(得分:3)
在Craig McQueen的帮助下,我创建了一个概念验证程序。
此程序播放名为“music.wav”的 mono 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()
尝试让我使用的所有软件包很好地协同工作是一件痛苦的事。我正在使用Python 2.6.6,PyGame 1.9.1 for python 2.6,NumPy 1.3.0 for python 2.6,PyMedia 1.3.7.3 for python 2.6和scikits.samplerate 0.3.1 for python 2.6。请注意,scikits.samplerate与NumPy 1.4或更高版本冲突,其中一个包(我忘记了哪一个)需要setuptools
答案 1 :(得分:1)
听起来好像你想要动态重新采样音频。
也许您可以尝试使用scikits.samplerate模块。它使用Secret Rabbit Code library。
答案 2 :(得分:0)
您可能希望查看使用wxPython到create a media player,并调查SetPlaybackRate()
函数。 wxWidget docs here
所有平台都不支持SetPlaybackRate()
功能,我自己也没有尝试过,看看它是否完全符合您的要求,以及它的工作原理与否。
答案 3 :(得分:0)
设置工具
如果你不这样做,你会不断收到错误ImportError:没有名为pkg_resources的模块