Модификатор файла WAV с использованием Python

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

import codecs, wave

#convert a number to its two's complemented value (For positive it is equal itself)
def convert_to_twos(value, wid_len=16):
    if value < 0 :
        value = value + (1 << wid_len)
    return value

#receive the value of a two's complemented number.
def twos_back_value(value, wid_len=16):
    if value & (1 << wid_len -1):
        value = value - (1 << wid_len)
    return value

#opening files
input_file = wave.open(r"<address of input wave file>", 'r')
output_file = wave.open(r"<an address for output wave file>", 'w')

#Get input file parameters and set them to the output file after modifing the channel number.
out_params = [None, None, None, None, None, None]
in_params = input_file.getparams()
out_params[0] = 1 # I want to have a mono type wave file in output. so I set the channels = 1
out_params[1] = in_params[1] #Frame Width
out_params[2] = in_params[2] #Sample Rate
out_params[3] = in_params[3] #Number of Frames
out_params[4] = in_params[4] #Type
out_params[5] = in_params[5] #Compressed or not
output_file.setparams(out_params)

#reading frames from first file and storing in the second file
for frame in range(out_params[2]):
    value = int(codecs.getencoder('hex')(input_file.readframes(1))[0][:4], 16) #converting first two bytes of each frame (let assume each channel has two bytes frame length) to int (from byte string).
    t_back_value = twos_back_value( value ,out_params[1]*8)
    new_value = int(t_back_value * 1)
    new_twos = convert_to_twos(new_value, out_params[1]*8)
    to_write = new_twos.to_bytes((new_twos.bit_length() + 7) // 8, 'big')
    output_file.writeframes(to_write)


#closing files
input_file.close()
output_file.close()

Проблема в том, что когда я запускаю указанную выше программу и играю в выходной файл, я слышу только шум и ничего больше! (Пока жду тот же файл только в одноканальном режиме!)

Обновление:

У меня что-то странное. Согласно документации, функция readframes(n) Читает и возвращает не более n кадров звука в виде строки байтов. Поэтому я ожидаю, что эта функция вернет только шестнадцатеричные значения. Но на самом деле я вижу некоторые странные нешестнадцатеричные значения:

read_frame = input_file.readframes(1)
print (read_frame)
print (codecs.getencoder('hex')(read_frame)[0])
print ("")

выше кода, в цикле for верните это:

b'\xe3\x00\xc7\xf5'
b'e300c7f5'

b'D\xe8\xa1\xfd'
b'44e8a1fd'

b'\xde\x08\xb2\x1c'
b'de08b21c'

b'\x17\xea\x10\xe9'
b'17ea10e9'

b'{\xf7\xbc\xf5'
b'7bf7bcf5'

b'*\xf6K\x08'
b'2af64b08'

Как видите, в read_frame! есть несколько нешестнадцатеричных значений. (*, }, D, ... например). Что это?


person Ebrahim Ghasemi    schedule 23.11.2015    source источник
comment
Какой модификации файла WAV вы пытаетесь добиться? то есть для каждого кадра, что должно быть на выходе?   -  person Martin Evans    schedule 23.11.2015
comment
@MartinEvans Я хочу изменить RMS (среднеквадратичный) файлов WAV. Это часть проекта психологического эксперимента. Например, я хочу иметь новую версию предыдущего файла с RMS, равным половине первого файла.   -  person Ebrahim Ghasemi    schedule 23.11.2015
comment
@MartinEvans Проверьте обновление, пожалуйста.   -  person Ebrahim Ghasemi    schedule 23.11.2015


Ответы (1)


Значения, которые вы видите, представляют собой четыре байта для каждого кадра, а именно два байта для первого канала и два байта для второго канала. Для моно WAV вы увидите только два байта.

Следующий подход должен направить вас на правильный путь. Вам нужно использовать библиотеку Python struct для преобразования ваши двоичные значения кадра в целые числа со знаком. Затем вы можете манипулировать ими по мере необходимости. В моем примере я просто умножаю на 2/3:

import wave
import codecs
import struct

#opening files
input_file = wave.open(r"sample.wav", 'rb')
output_file = wave.open(r"sample_out.wav", 'wb')

#Get input file parameters and set them to the output file after modifing the channel number.
in_params = list(input_file.getparams())

out_params = in_params[:]
out_params[0] = 1
output_file.setparams(out_params)

nchannels, sampwidth, framerate, nframes, comptype, compname = in_params
format = '<{}h'.format(nchannels)

#reading frames from first file and storing in the second file
for index in range(nframes):
    frame = input_file.readframes(1)
    data = struct.unpack(format, frame)
    value = data[0]     # first (left) channel only
    value = (value * 2) // 3    # apply a simple function to each value
    output_file.writeframes(struct.pack('<h', value))

#closing files
input_file.close()
output_file.close()

Учтите, что обработка волнового файла кадр за раз будет мучительно медленной. Его можно ускорить, уменьшив количество вызовов до writeframes.

format содержит формат, необходимый для распаковки двоичных значений. Для 2-канального файла WAV это будет содержать 4 байта. Затем format будет настроено как <hh, что означает, что использование struct.unpack приведет к созданию двух полей, каждое из которых будет содержать целочисленное представление каждого канала. Таким образом, четыре байта становятся списком из двух целых чисел, по одному для каждого канала.

person Martin Evans    schedule 23.11.2015
comment
Спасибо, дорогой Мартин, а ты пробовал? К сожалению, у меня все еще есть шумный выход. Я имею в виду, что я слышу только шум! (Входной файл в порядке) - person Ebrahim Ghasemi; 23.11.2015
comment
Да, я тестировал его на стереофайле WAV. Он создал монофонический файл WAV с уменьшенной амплитудой. Размер файла уменьшился вдвое. Я также воссоздал шум, который вы получали в своей версии. - person Martin Evans; 23.11.2015
comment
Будет ли проблемой, если я заменю nframe коротким числом для тестирования программы? (например, 100 000). И в цикле for, и в переменной out_params. - person Ebrahim Ghasemi; 23.11.2015
comment
Я только что попробовал с nframes // 3, и это сработало. - person Martin Evans; 23.11.2015
comment
Могу я узнать, какой аудиоплеер вы используете? - person Ebrahim Ghasemi; 23.11.2015
comment
И, если возможно, могу ли я получить образец входного файла, пожалуйста? (если да, то мой адрес электронной почты: ebr.ghasemi @ gmail). Большое спасибо. - person Ebrahim Ghasemi; 23.11.2015
comment
Если я напечатаю in_param для своего тестового файла, он покажет [2, 2, 44100, 182522, 'NONE', 'not compressed']. - person Martin Evans; 23.11.2015
comment
Странный! Мой [2, 2, 44100, 9692592, 'NONE', 'not compressed']! Так что с этим проблем нет. Могу я узнать, какая версия Python, какой проигрыватель аудиофайлов и какая операционная система у вас есть? Мой: Python 3.5 - KMPlayer - Win7Ultimate64Bit! - person Ebrahim Ghasemi; 23.11.2015
comment
3.4.3 Anaconda 2.3.0 (32-разрядная версия), а выходной файл WAV нормально воспроизводился в проигрывателе Windows Media и VNC. Я на Windows7 64bit. Я предполагаю, что ваш ввод WAV воспроизводится нормально. Вы точно скопировали образец без изменений? - person Martin Evans; 23.11.2015
comment
Единственное изменение, которое я сделал в вашем коде, это замена имен файлов. (sample.wav заменено моим именем файла.). Я думаю, мне нужно понизить версию Python до 3.4.3 Anaconda. - person Ebrahim Ghasemi; 23.11.2015
comment
Используйте полный путь к выходному файлу. Изменяется ли дата в выходном файле после запуска скрипта? - person Martin Evans; 23.11.2015
comment
Да, дорога нормальная. Я заменил sample.wav на 1.wav. А 1.wav находится в том же каталоге, что и test.py (этот скрипт). И да, после запуска скрипта он успешно создал sample_out.wav. Я печатаю параметры этого нового файла, и это: [1, 2, 44100, 9692592, 'NONE', 'not compressed']. Вроде тоже нормально! Размер моего выходного файла составляет половину размера входного файла (18,4 и 36,9)! Я не знаю, в чем причина проблемы! - person Ebrahim Ghasemi; 23.11.2015
comment
Можно ли прислать мне ваш образец файла? - person Ebrahim Ghasemi; 23.11.2015
comment
Ладно, странно! С вашим образцом файла все в порядке. :( спасибо за ваше время. Если вы позволите мне, я отметил ваш ответ завтра. :) - person Ebrahim Ghasemi; 23.11.2015
comment
Строка value = data[0] не должна быть value = data[0] * 256 + data[1] для файлов с шириной выборки = 2? Я думаю, что для этих файлов каждый канал имеет длину 2 байта в каждом кадре. Нет? - person Ebrahim Ghasemi; 24.11.2015
comment
data[0] уже содержит оба байта для первого канала. struct.unpack сделал это. - person Martin Evans; 24.11.2015
comment
Если ваш порядок байтов отличается, вы можете попробовать удалить < - person Martin Evans; 24.11.2015
comment
Извините, это мой последний вопрос :). Разве мы не должны изменить значения перед умножением, например, на 2/3? (Поскольку отрицательные значения фреймов дополняются до двух). Я имею в виду, что, поскольку некоторые из них дополняются двумя, вывод, похоже, не составляет ровно 2/3 ввода. Я опять ошибаюсь? - person Ebrahim Ghasemi; 24.11.2015
comment
Не требуется, так как в этот момент вы работаете с целыми числами со знаком Python. struct.pack преобразует его обратно в 2-байтовый двоичный файл. - person Martin Evans; 24.11.2015