Изменить высоту звука (и скорость) во время воспроизведения в Python - PullRequest
3 голосов
/ 21 октября 2010

Я работаю над программой на Python, которая воспроизводит музыку.Одной из функций будет слайдер, который пользователь может перетаскивать вверх или вниз, чтобы изменить высоту звучания музыки во время воспроизведения.

Например, если высота звука установлена ​​на 2, то музыка будет звучать на одну октаву выше., он будет играть в два раза быстрее и будет длиться вдвое дольше.Все, что я действительно меняю, это скорость воспроизведения, но мне нужно сделать это в интерактивном режиме в режиме реального времени.

Хороший пример этой функции, реализованной во флэш-памяти, можно найти здесь .(Требуется немного времени, чтобы загрузить, наберитесь терпения.)

Я просмотрел много аудио пакетов Python, но не нашел ни одного, который мог бы изменить высоту звука, который воспроизводится в данный момент.У меня есть несколько версий Python, поэтому нет требований к какой версии пакет поддерживает.Я разрабатываю это на Windows 7.

Есть предложения?

Ответы [ 4 ]

3 голосов
/ 22 октября 2010

С помощью Крейга МакКуина я создал программу для проверки концепции.

Эта программа воспроизводит моно 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 , PyGame 1.9.1 для python 2.6 , NumPy 1.3.0 для python 2.6 , PyMedia 1.3.7.3для python 2.6 и scikits. примерная скорость 0.3.1 для python 2.6 .Обратите внимание, что scikits.samplerate конфликтует с NumPy 1.4 или выше, и один из пакетов (я забыл, какой) требует setuptools

1 голос
/ 21 октября 2010

Звучит так, как будто вы хотите сэмплировать звук на лету.

Возможно, вы можете попробовать использовать модуль scikits.samplerate . Используется библиотека Secret Rabbit Code .

0 голосов
/ 12 марта 2013

инструменты настройки требуются для scikits.samplerate 0.3.1

если вы этого не сделаете, вы продолжите получать ошибку ImportError: Нет модуля с именем pkg_resources

0 голосов
/ 22 октября 2010

Возможно, вы захотите посмотреть, используя wxPython - , создайте медиаплеер и исследуйте функцию SetPlaybackRate(). wxWidget docs здесь .

Эта функция SetPlaybackRate() поддерживается не на всех платформах, и я сам не пробовал проверить, выполняет ли она именно то, что вам нужно, и насколько хорошоработает или нет.

...