Интерпретация данных WAV - PullRequest
7 голосов
/ 09 февраля 2010

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

Функция wave.getparams возвращает (2 канала, 2 байта, 44100 Гц, 96333 кадра, Без сжатия, Без сжатия). Все это кажется веселым, но затем я попытался напечатать один кадр: '\ xc0 \ xff \ xd0 \ xff', который составляет 4 байта. Я предполагаю, что возможно, что кадр имеет 2 сэмпла, но неоднозначности на этом не заканчиваются.

96333 кадра * 2 сэмпла / кадр * (1 / 44,1 к / с) = 4,3688 с

Однако iTunes сообщает, что время ближе к 2 секундам, а расчеты, основанные на размере файла и битрейте, находятся в пределах 2,7 секунды. Что здесь происходит?

Кроме того, как мне узнать, подписаны ли байты или нет?

Большое спасибо!

Ответы [ 6 ]

18 голосов
/ 09 февраля 2010

Спасибо за вашу помощь! У меня все получилось, и я опубликую решение здесь, чтобы каждый мог использовать его на случай, если это понадобится другой бедной душе:

import wave
import struct

def pcm_channels(wave_file):
    """Given a file-like object or file path representing a wave file,
    decompose it into its constituent PCM data streams.

    Input: A file like object or file path
    Output: A list of lists of integers representing the PCM coded data stream channels
        and the sample rate of the channels (mixed rate channels not supported)
    """
    stream = wave.open(wave_file,"rb")

    num_channels = stream.getnchannels()
    sample_rate = stream.getframerate()
    sample_width = stream.getsampwidth()
    num_frames = stream.getnframes()

    raw_data = stream.readframes( num_frames ) # Returns byte data
    stream.close()

    total_samples = num_frames * num_channels

    if sample_width == 1: 
        fmt = "%iB" % total_samples # read unsigned chars
    elif sample_width == 2:
        fmt = "%ih" % total_samples # read signed 2 byte shorts
    else:
        raise ValueError("Only supports 8 and 16 bit audio formats.")

    integer_data = struct.unpack(fmt, raw_data)
    del raw_data # Keep memory tidy (who knows how big it might be)

    channels = [ [] for time in range(num_channels) ]

    for index, value in enumerate(integer_data):
        bucket = index % num_channels
        channels[bucket].append(value)

    return channels, sample_rate
9 голосов
/ 09 февраля 2010

«Два канала» означает стерео, поэтому нет смысла суммировать длительность каждого канала - так что вы отключены в два раза (2,18 секунды, а не 4,37). Что касается подписи, как объяснено, например, здесь , и я цитирую:

8-битные сэмплы сохраняются как неподписанные байты в диапазоне от 0 до 255. 16-битный образцы хранятся в виде 2-х дополнений целые числа со знаком, от -32768 до 32767.

Это часть спецификаций формата WAV (на самом деле его расширенного RIFF) и, следовательно, не зависит от того, какую библиотеку вы используете для работы с WAV-файлом.

4 голосов
/ 09 февраля 2010

Я знаю, что ответ уже принят, но некоторое время назад я кое-что делал со звуком, и вы должны распаковать волну, делая что-то вроде этого.

pcmdata = wave.struct.unpack("%dh"%(wavedatalength),wavedata)

Также один пакет, который я использовал, назывался PyAudio, хотя мне все еще приходилось использовать пакет wave с ним.

2 голосов
/ 25 июля 2015

Опираясь на этот ответ , вы можете получить хорошее повышение производительности, используя numpy.fromstring или numpy.fromfile . Также см. этот ответ .

Вот что я сделал:

def interpret_wav(raw_bytes, n_frames, n_channels, sample_width, interleaved = True):

    if sample_width == 1:
        dtype = np.uint8 # unsigned char
    elif sample_width == 2:
        dtype = np.int16 # signed 2-byte short
    else:
        raise ValueError("Only supports 8 and 16 bit audio formats.")

    channels = np.fromstring(raw_bytes, dtype=dtype)

    if interleaved:
        # channels are interleaved, i.e. sample N of channel M follows sample N of channel M-1 in raw data
        channels.shape = (n_frames, n_channels)
        channels = channels.T
    else:
        # channels are not interleaved. All samples from channel M occur before all samples from channel M-1
        channels.shape = (n_channels, n_frames)

    return channels

Присвоение новому значению фигуры приведет к ошибке, если для этого потребуется скопировать данные в память. Это хорошо, так как вы хотите использовать данные на месте (используя меньше времени и памяти в целом). Функция ndarray.T также не копирует (т. Е. Возвращает представление), если это возможно, но я не уверен, как вы гарантируете , что она не копирует.

Чтение непосредственно из файла с помощью np.fromfile будет еще лучше, но вам придется пропустить заголовок, используя пользовательский тип dtype. Я еще не пробовал это.

2 голосов
/ 09 февраля 2010

Длительность - это просто количество кадров, деленное на количество кадров в секунду. По вашим данным это: 96333 / 44100 = 2.18 seconds.

2 голосов
/ 09 февраля 2010

Каждая выборка имеет 16 бит и 2 канала, поэтому кадр занимает 4 байта

...