C ++ чтение 16-битного файла Wav

У меня проблемы с чтением 16-битного файла .wav. Я прочитал информацию в заголовке, однако преобразование не работает.

Например, в Matlab, если я читаю волновой файл, я получаю данные следующего типа:

 -0.0064, -0.0047,  -0.0051, -0.0036, -0.0046, -0.0059,  -0.0051

Однако в моей программе на C ++ возвращается следующее:

0.960938, -0.00390625, -0.949219, -0.00390625, -0.996094, -0.00390625

Мне нужно, чтобы данные были представлены таким же образом. Теперь для файлов 8 bit .wav я сделал следующее:

uint8_t c;

for(unsigned i=0; (i < size); i++)
{
    c = (unsigned)(unsigned char)(data[i]);
    double t = (c-128)/128.0;
    rawSignal.push_back(t);
}

Однако это сработало, когда я сделал это для 16 бит:

uint16_t c;

for(unsigned i=0; (i < size); i++)
{
   c = (signed)(signed char)(data[i]);
   double t = (c-256)/256.0;
   rawSignal.push_back(t);
}

Не работает и показывает вывод (выше).

Я следую стандартам, найденным здесь

Где data - массив char, а rawSignal - std::vector<double> Я, вероятно, просто неправильно передаю преобразование, но не могу понять, где. У кого-нибудь есть предложения?

Спасибо

РЕДАКТИРОВАТЬ:

Вот что сейчас отображается (в виде графика):

введите описание изображения здесь

Вот что он должен отображать:

введите описание изображения здесь


person Phorce    schedule 12.09.2013    source источник
comment
Поскольку данные представляют собой массив символов, тогда data [i] относится к i-му символу, 8-битному элементу, а не к i-му 16-битному значению.   -  person Jay    schedule 12.09.2013
comment
@ Джей прости ?? Нужно ли мне тогда переупорядочивать данные? Теперь я смущен.   -  person Phorce    schedule 12.09.2013
comment
Если вы указали, что «data» - это массив из 8-битных значений, тогда data [3] извлекает 3-й байт массива. Если у вас есть 16-битные значения в массиве, тогда data [3] не даст вам 3-е 16-битное значение.   -  person Jay    schedule 12.09.2013
comment
@Jay Но data - это просто массив символов. Я не уточняю, что это 8bits или что-то в этом роде .. Например. data = new char[padded_size]; так должно ли это быть 16bit char array?   -  person Phorce    schedule 12.09.2013
comment
@ user1326876 - char - это элемент определенного размера. На большинстве процессоров, с которыми вы столкнетесь, это будет 8 бит, хотя есть исключения (например, некоторые TI DSP).   -  person Chris Stratton    schedule 12.09.2013
comment
В вашем заголовке написано, что это 16-битные данные. 16-битные данные не помещаются в char. Вам нужно рассматривать данные как массив 16-битных элементов со знаком.   -  person ScottMcP-MVP    schedule 13.09.2013


Ответы (5)


Здесь есть несколько проблем:

  • 8-битные WAV-файлы без знака, но 16-битные WAV-файлы со знаком. Следовательно, шаг вычитания, указанный в ответах Карла и Джея, не нужен. Я предполагаю, что они просто скопировали из вашего кода, но они ошибаются.
  • 16-битные волны имеют диапазон от -32 768 до 32 767, а не от -256 до 255, что в любом случае делает умножение, которое вы используете, неверным.
  • 16-битные WAV-файлы - это 2 байта, поэтому вы должны прочитать два байта, чтобы сделать одну выборку, а не одну. Кажется, вы читаете по одному символу за раз. Когда вы читаете байты, вам, возможно, придется поменять их местами, если ваш собственный порядок байтов не является прямым порядком байтов.

Предполагая архитектуру little-endian, ваш код будет выглядеть примерно так (очень близко к ответу Карла):

for (int i = 0; i < size; i += 2)
{
    int c = (data[i + 1] << 8) | data[i];
    double t = c/32768.0;
    rawSignal.push_back(t);
}

для архитектуры с прямым порядком байтов:

for (int i = 0; i < size; i += 2)
{
    int c = (data[i] << 8) | data[i+1];
    double t = c/32768.0;
    rawSignal.push_back(t);
}

Этот код не протестирован, поэтому, пожалуйста, LMK, если он не работает.

person Bjorn Roche    schedule 12.09.2013
comment
Спасибо за ваш ответ. Я проверил вашу работу по обработке сигнала / обработки звука, и она впечатляет :) Я попробую это сделать, как только у меня будет возможность. У меня большая проблема ... Он не считывает правильный размер для 16-битных аудиофайлов, но для 8-битных файлов ... Я проверю позже, хотя спасибо :) - person Phorce; 13.09.2013
comment
Нет, не работает. Пробовал как с прямым, так и с прямым порядком байтов - person Phorce; 13.09.2013
comment
Он не считывает правильный размер для 16-битных аудиофайлов, но он предназначен для 8-битных файлов, поэтому вы неправильно читаете заголовок. Возможно, вы начинаете считывать данные не с того байта. - person Bjorn Roche; 13.09.2013
comment
Кроме того, на какой архитектуре вы работаете? Возможно, вам также стоит включить свой код для чтения заголовка. - person Bjorn Roche; 13.09.2013
comment
Наконец-то работает :) Мне нужно было поспать ага! Ваше здоровье. Кроме того, есть ли способ проверить, имеет ли человек архитектуру с прямым или обратным порядком байтов? - person Phorce; 13.09.2013
comment
Большинство компиляторов создают макросы для обеспечения порядка байтов. - person Bjorn Roche; 13.09.2013
comment
Еще раз большое спасибо за вашу помощь :) Я прочитаю это !! - person Phorce; 13.09.2013
comment
Это не сработало .. Кажется, так давно! Я создал новый вопрос: stackoverflow.com/ questions / 21345689 / есть небольшая разница в результатах, и я понятия не имею, почему это происходит. Если у вас будет время, не могли бы вы взглянуть ... Спасибо - person Phorce; 25.01.2014
comment
Да, это необъективно. Если вам нужно знать, как это работает, вы должны задать новый вопрос. - person Bjorn Roche; 20.07.2014

(Прежде всего, о прямом порядке байтов / обратном порядке байтов. WAV - это просто формат контейнера, данные, закодированные в нем, могут быть в бесчисленных форматах. Большинство кодеков работают без потерь (MPEG Layer-3, также известный как MP3, да, поток может быть «упакован» в WAV, различные CCITT и другие кодеки). Вы предполагаете, что имеете дело с каким-то форматом PCM, в котором вы видите реальную волну в формате RAW, для него не было выполнено преобразование без потерь. Порядок байтов зависит от на кодеке, создавшем поток. порядок байтов параметров формата гарантирован в файлах RIFF WAV?)

Также возникает вопрос, является ли один образец PCM в линейном масштабе дискретизированным целым числом или за ним стоит какое-то масштабирование, логарифмическая шкала или другое преобразование. Обычные файлы WAV PCM, с которыми я сталкивался, были простыми семплами в линейном масштабе, но я не работаю в звукозаписывающей или производственной индустрии.

Итак, путь к вашему решению:

  1. Убедитесь, что вы имеете дело с обычным файлом RIFF WAV с кодировкой 16 бит PCM.
  2. При чтении потока всегда считывайте два байта (char) за раз и преобразуйте два символа в 16-битный короткий. Люди показали это до меня.
  3. Форма волны, которую вы показываете, ясно указывает на то, что вы либо неправильно оценили частоту (или у вас только один монофонический канал вместо стерео). Потому что частота дискретизации (44,1 кГц, 22 кГц, 11 кГц, 8 кГц и т. Д.) Так же важна, как и разрешение (8 бит, 16 бит, 24 бит и т. Д.). Может быть, в первом случае у вас были стереоданные. Вы можете читать это как моно, вы можете этого не замечать. Во втором случае, если у вас есть монофонические данные, у вас закончатся сэмплы на полпути к чтению данных. Судя по вашим графикам, именно это и происходит. Кстати о другой причине: более низкое разрешение дискретизации (а 16 бит также ниже) часто сочетается с более низкой частотой дискретизации. Так что, если ваши входные данные - это время записи, и вы думаете, что у вас есть данные с частотой 22 кГц, но на самом деле это всего лишь 11 кГц, тогда вы снова выйдете на полпути от фактических сэмплов и прочитаете мусор в памяти. Так что либо одно из этих.

Убедитесь, что вы правильно интерпретируете и обрабатываете переменную итератора цикла и размер. Кажется, размер говорит о том, сколько у вас байтов. У вас будет ровно вдвое меньше коротких целочисленных выборок. Обратите внимание, что решение Бьорна правильно увеличивает i на 2 из-за этого.

person Csaba Toth    schedule 12.09.2013
comment
Спасибо за ответ :) Может быть, это проблема, почему я не возвращаю правильный размер для 16-битного файла .wav, когда правильный размер для 8-битного файла .wav возвращает правильный размер? Должен ли я быть size / 2? - person Phorce; 13.09.2013
comment
Размер потока в заголовке, вероятно, правильный, и если он говорит, что аудиопоток, содержащийся в файле wav, равен x, тогда у вас должно быть x байта (в противном случае ваш файл будет усечен). Я говорю, что с 16-битными сэмплами PCM размер x, но у вас x / 2 количество сэмплов в этих данных. Если в вашем цикле указано i += 2, то в этом отношении у вас все в порядке. Но вы видите, что ваш график становится странным ровно в половине матча. Что-то пошло не так. Нам нужна дополнительная информация, чтобы рассказать вам, что не так. - person Csaba Toth; 13.09.2013

Мой рабочий код

int8_t* buffer = new int8_t[size];
/*
  HERE buffer IS FILLED
*/
for (int i = 0; i < size; i += 2)
{
    int16_t c = ((unsigned char)buffer[i + 1] << 8) | (unsigned char)buffer[i];
    double t = c/32768.0;
    rawSignal.push_back(t);
}
person carimus    schedule 30.12.2015

16-битное количество дает вам диапазон от -32 768 до 32 767, а не от -256 до 255 (это всего 9 бит). Использовать:

for (int i = 0; i < size; i += 2)
{
    c = (data[i + 1] << 8) + data[i]; // WAV files are little-endian
    double t = (c - 32768)/32768.0;
    rawSignal.push_back(t);
}
person Carl Norum    schedule 12.09.2013
comment
Спасибо за ответ. Это дает мне: 0.992493 0.992584 0.992218 0.983398 0.994781 Что не так по сравнению с результатом в MatLab? - person Phorce; 12.09.2013
comment
Что вы имеете в виду под словом logs извините? Не могли бы вы привести мне пример - person Phorce; 12.09.2013
comment
Я имею в виду распечатать некоторые данные и посмотреть, считываете ли вы байты, которые находятся в файле. - person Carl Norum; 12.09.2013
comment
Да, я сделал. Вся информация в заголовке верна. Я распечатываю data[i] вроде тоже нормально. ммм, это странно. - person Phorce; 12.09.2013
comment
Не уверен в правильности вычитания, поскольку файлы .wav обычно подписываются. Также имеет значение, являются ли данные 8-битного типа со знаком или без знака, поскольку нежелательное знаковое расширение менее значимого байта может вызвать проблемы. - person Chris Stratton; 12.09.2013
comment
вычитание неверно для 16-битных файлов. смотри мой ответ. - person Bjorn Roche; 13.09.2013

Возможно, вам захочется чего-то большего:

uint16_t c;
for(unsigned i=0; (i < size); i++)
{
   // get a 16 bit pointer to the array
   uint16_t* p = (uint16_t*)data;
   // get the i-th element
   c = *( p + i );
   // convert to signed? I'm guessing this is what you want
   int16_t cs = (int16_t)c;
   double t = (cs-256)/256.0;
   rawSignal.push_back(t);
}

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

person Jay    schedule 12.09.2013
comment
Спасибо за ваш ответ. Это вроде работает, однако взгляните на мою исходную публикацию, я загрузил 2 графика. Я не понимаю, откуда взялся блок .. Но это близко к тому, что я хочу :) Надеюсь, вы мне поможете - person Phorce; 12.09.2013
comment
Это неверно по крайней мере по двум причинам: нет необходимости вычитать 256, а 256 - неправильный коэффициент масштабирования для 16 бит. Кроме того, он предполагает архитектуру с прямым порядком байтов. За арифметикой указателя сложно следить ... не уверен, что это правильно. - person Bjorn Roche; 13.09.2013