Python: изменить высоту звука аудиофайла

это мой первый пост в стеке. До сих пор этот сайт был очень полезным, но я новичок и мне нужно четкое объяснение моей проблемы, связанной со звуком со сдвигом высоты тона в Python. У меня установлены текущие модули: numpy, scipy, pygame и scikits "samplerate" api.

Моя цель — взять стереофайл и воспроизвести его с разной высотой звука за как можно меньшее количество шагов. В настоящее время я загружаю файл в массив с помощью pygame.sndarray, затем применяю преобразование частоты дискретизации с помощью scikits.samplerate.resample, а затем преобразую вывод обратно в звуковой объект для воспроизведения с помощью pygame. Проблема в том, что из моих динамиков выходит мусорный звук. Конечно, я пропускаю несколько шагов (помимо того, что ничего не знаю о математике и аудио).

Спасибо.

import time, numpy, pygame.mixer, pygame.sndarray
from scikits.samplerate import resample

pygame.mixer.init(44100,-16,2,4096)

# choose a file and make a sound object
sound_file = "tone.wav"
sound = pygame.mixer.Sound(sound_file)

# load the sound into an array
snd_array = pygame.sndarray.array(sound)

# resample. args: (target array, ratio, mode), outputs ratio * target array.
# this outputs a bunch of garbage and I don't know why.
snd_resample = resample(snd_array, 1.5, "sinc_fastest")

# take the resampled array, make it an object and stop playing after 2 seconds.
snd_out = pygame.sndarray.make_sound(snd_resample)
snd_out.play()
time.sleep(2)

person hilmers    schedule 14.12.2011    source источник


Ответы (4)


Ваша проблема в том, что pygame работает с массивами numpy.int16, но вызов resample возвращает массив numpy.float32:

>>> snd_array.dtype
dtype('int16')
>>> snd_resample.dtype
dtype('float32')

Вы можете преобразовать результат resample в numpy.int16, используя astype:

>>> snd_resample = resample(snd_array, 1.5, "sinc_fastest").astype(snd_array.dtype)

С этой модификацией ваш скрипт Python хорошо воспроизводит файл tone.wav с более низким тоном и более низкой скоростью.

person kasyc    schedule 21.12.2011
comment
О, чувак, я не знаю, как тебя отблагодарить. Нет, подождите, я могу отправить вам деньги на пиво через PayPal, если вы примете мое предложение. Я провел бесчисленные часы в поисках решения. Это фантастика. - person hilmers; 24.12.2011
comment
Рад видеть, что вам понравился мой ответ :) Вам не нужно ничего мне предлагать, ваш вопрос был интересным, и я также узнал кое-что из него! - person kasyc; 03.01.2012
comment
Можно ли сохранить измененный объект pygame.micer.Sound в виде звукового файла, а не воспроизводить его? - person trevorKirkby; 11.01.2014
comment
Я бы сделал это с помощью модуля SciPy io.wavefile: docs.scipy.org/ doc/scipy/reference/io.html - person kasyc; 19.01.2014

Лучше всего, вероятно, использовать python audiere.

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

http://audiere.sourceforge.net/home.php

person Jere    schedule 15.12.2011
comment
Спасибо, Audiere был моим первым выбором, но я не смог заставить его make работать без ошибок. Я не слишком силен в этом деле, поэтому мне приходится браться за работу, учитывая мои ограниченные навыки. - person hilmers; 16.12.2011
comment
Это выглядит хорошо, работает ли это для python 2.7? Вроде для python 2.2 - person trevorKirkby; 22.12.2013

Скорее всего, scikits.samplerate.resample «думает», что ваш звук находится в формате, отличном от 16-битного стерео. Проверьте документацию по scikits.samplerate, чтобы узнать, где выбрать правильный аудиоформат в вашем массиве. Если бы он передискретизировал 16-битный звук, обрабатывая его как 8-битный мусор, это то, что получится.

person jsbueno    schedule 14.12.2011

Из документации scikits.samplerate.resample:

Если вход имеет ранг 1, используются все данные, и предполагается, что они получены из монофонического сигнала. Если ранг равен 2, числовые столбцы будут считаться числом каналов.

Поэтому я думаю, что вам нужно сделать что-то вроде этого, чтобы передать стереоданные в resample в ожидаемом формате:

snd_array = snd_array.reshape((-1,2))

snd_resample = resample(snd_array, 1.5, "sinc_fastest")

snd_resample = snd_resample.reshape(-1) # Flatten it out again
person Ichigo Jam    schedule 15.12.2011
comment
Спасибо. Я попробовал ваше предложение, но выходит тот же результат. - person hilmers; 20.12.2011