4

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

例如,如果音高设置为 2,那么音乐听起来会高一个八度,播放速度会提高一倍,持续时间会减半。我真正改变的是播放速度,但我需要实时交互。

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

我研究了许多 python 音频包,但我还没有找到一个可以改变当前正在播放的声音的音高的包。我有多个版本的 Python,所以对包支持的版本没有要求。我正在 Windows 7 上开发它。

有什么建议么?

4

4 回答 4

2

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

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

这里有一些奇怪的行为,我不知道如何解决。如果音调当前很低,那么在音调改变之前大约有 2 秒的延迟。但是,对于高音,音高会实时变化。(随着音高变低,延迟平滑增加)。如果soundOutput.getLeft() < 0.2. 也就是说,如果缓冲区上剩余的声音量小于0.2秒。因此,不应有任何延误。为了进行故障排除,我包含了写入soundOutput.getLeft()文件的代码。它往往始终保持在或非常接近于 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, Python 2.6 使用 PyGame 1.9.1 , python 2.6使用NumPy 1.3.0 , python 2.6使用PyMedia 1.3.7.3 , python 2.6使用scikits.samplerate 0.3.1。请注意,scikits.samplerate 与 NumPy 1.4 或更高版本冲突,其中一个包(我忘记是哪个包)需要setuptools

于 2010-10-22T17:44:09.307 回答
1

听起来好像您想即时重新采样音频。

也许您可以尝试使用scikits.samplerate模块。它使用Secret Rabbit Code 库

于 2010-10-20T23:26:02.747 回答
0

scikits.samplerate 0.3.1 需要设置工具

如果您不这样做,您将不断收到错误 ImportError: No module named pkg_resources

于 2013-03-12T07:18:12.233 回答
0

您可能想查看使用wxPython创建媒体播放器,并研究该SetPlaybackRate()功能。wxWidget 文档在这里

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

于 2010-10-22T02:08:56.880 回答