【问题标题】:Change the pitch (and speed) of audio during playback in Python在 Python 中播放期间更改音频的音高(和速度)
【发布时间】:2010-10-20 20:27:26
【问题描述】:

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

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

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

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

有什么建议吗?

【问题讨论】:

    标签: python windows audio pitch


    【解决方案1】:

    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.6PyGame 1.9.1 for python 2.6NumPy 1.3.0 for python 2.6PyMedia 1.3.7.3 for python 2.6scikits.samplerate 0.3.1 for python 2.6。请注意,scikits.samplerate 与 NumPy 1.4 或更高版本冲突,其中一个包(我忘记是哪个)需要setuptools

    【讨论】:

    • 我无法安装 pymedia。看起来像一个旧/过时的包。这个问题有什么解决办法吗?
    【解决方案2】:

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

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

    【讨论】:

    • only thing that scikits.samplerate does 是将一个 numpy 数组重新采样到另一个数组中。我知道如果我采用 44100 Hz 的声音,将其重新采样到 22050 Hz,然后以 44100 Hz 播放,它会高出一个八度。但现在我需要一种播放声音并即时重新采样的方法,这是原始问题的重要组成部分。
    • 我假设你已经知道如何在 Python 中实现音频样本的基本播放。
    • 对不起,让我重新表述一下我的陈述。我需要一种播放声音的方法,让我可以即时进行重新采样。我会检查 python 文档,但我不知道有什么方法可以做到这一点。
    • 您目前使用什么模块/框架来播放音频?
    • 到目前为止,我一直在使用Pygame mixer,但由于主要的质量问题,我想放弃它。我愿意尝试任何建议的模块。
    【解决方案3】:

    您可能想看看使用wxPythoncreate a media player,并研究SetPlaybackRate() 函数。 wxWidget docs here.

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

    【讨论】:

      【解决方案4】:

      scikits.samplerate 0.3.1 需要设置工具

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

      【讨论】:

      • 只是一个更新,如果你没有安装 setuptools 并且是最新的,它现在会抛出这个错误:>>> from scikits.samplerate import resample Traceback(最近一次调用最后一次):文件“ ”,第 1 行,在 文件中“scikits/samplerate/__init__.py”,第 6 行,在 from _samplerate import resample, available_convertors, src_version_str, \ ImportError: No module named _samplerate
      猜你喜欢
      • 1970-01-01
      • 2018-08-12
      • 2011-01-21
      • 1970-01-01
      • 2023-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多