Файл WAV из захваченных образцов данных PCM

У меня есть несколько гигабайт образцов данных, собранных «в полевых условиях» со скоростью 48 кбит / с с использованием модуля NI Data Acquisition. Я хочу создать из этих данных файл WAV.

Я сделал это ранее, используя MATLAB, чтобы загрузить данные, нормализовать их до 16-битного диапазона PCM, а затем записать их как файл WAV. Однако MATLAB упирается в размер файла, поскольку он все делает «в памяти».

В идеале я бы сделал это на C ++ или C (C # - вариант), или, если есть существующая утилита, я бы ее использовал. Есть ли простой способ (например, существующая библиотека) взять необработанный буфер PCM, указать частоту дискретизации, битовую глубину и упаковать его в файл WAV?

Чтобы обрабатывать большой набор данных, необходимо иметь возможность добавлять данные по частям, поскольку не обязательно будет возможно прочитать весь набор в память.

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


person Clifford    schedule 22.09.2009    source источник
comment
Сколько битов на выборку в ваших необработанных данных, и нужно ли вам это пересчитать до 44,1 кбит / с? Формат WAV поддерживает 48 кбит / с.   -  person MusiGenesis    schedule 22.09.2009
comment
Спасибо всем. 48Ksps следует сохранить. Данные, захваченные NI, представляют собой напряжение с плавающей запятой, но изначально были дискретизированы с частотой дискретизации 14 бит. Я использую 16 бит для сохранения целостности сигнала. Это не было напрямую проблемой программирования, хотя я использовал программирование для решения проблемы. Данные представляют собой набор тестов для встроенного приложения обработки сигналов (настоящая проблема программирования). Мне просто нужен портативный способ воссоздания исходного сигнала. В конце концов, я просто масштабировал образцы до +/- 1.0, а не измерения напряжения, и использовал sox для создания файла wav непосредственно из данных с плавающей запятой.   -  person Clifford    schedule 23.09.2009


Ответы (6)


Я думаю, вы можете использовать для этого libsox.

person hlovdal    schedule 22.09.2009
comment
Похоже, что мне нужно. Я надеюсь, что смогу построить его, не загрязняя свой компьютер Cygwin. Я лучше буду использовать виртуальную машину Linux! - person Clifford; 22.09.2009
comment
ретрансляция предварительно скомпилированных двоичных файлов на cygwin; вам действительно нужна библиотека C или достаточно вызвать sox из командной строки? - person Christoph; 22.09.2009
comment
На данный момент вызываю sox напрямую, если мне больше нравится. - person Clifford; 22.09.2009
comment
Спасибо, я использовал sox.exe, чтобы добиться в итоге того, что мне нужно. - person Clifford; 22.09.2009

Интересно, что я обнаружил ошибку при синтаксическом разборе кода stackoverflow, он не поддерживает символ \ в конце строки, как вы видите ниже, грустно

//stolen from OGG Vorbis pcm to wav conversion rountines, sorry
#define VERSIONSTRING "OggDec 1.0\n"

static int quiet = 0;
static int bits = 16;
static int endian = 0;
static int raw = 0;
static int sign = 1;
unsigned char headbuf[44];  /* The whole buffer */







#define WRITE_U32(buf, x) *(buf)     = (unsigned char)((x)&0xff);\
                          *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\
                          *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\
                          *((buf)+3) = (unsigned char)(((x)>>24)&0xff);

#define WRITE_U16(buf, x) *(buf)     = (unsigned char)((x)&0xff);\
                          *((buf)+1) = (unsigned char)(((x)>>8)&0xff);

/*
 * Some of this based on ao/src/ao_wav.c
 */
static int
write_prelim_header (FILE * out, int channels, int samplerate)
{

  int knownlength = 0;

  unsigned int size = 0x7fffffff;
  // int channels = 2;
  // int samplerate = 44100;//change this to 48000
  int bytespersec = channels * samplerate * bits / 8;
  int align = channels * bits / 8;
  int samplesize = bits;

  if (knownlength)
    size = (unsigned int) knownlength;

  memcpy (headbuf, "RIFF", 4);
  WRITE_U32 (headbuf + 4, size - 8);
  memcpy (headbuf + 8, "WAVE", 4);
  memcpy (headbuf + 12, "fmt ", 4);
  WRITE_U32 (headbuf + 16, 16);
  WRITE_U16 (headbuf + 20, 1);  /* format */
  WRITE_U16 (headbuf + 22, channels);
  WRITE_U32 (headbuf + 24, samplerate);
  WRITE_U32 (headbuf + 28, bytespersec);
  WRITE_U16 (headbuf + 32, align);
  WRITE_U16 (headbuf + 34, samplesize);
  memcpy (headbuf + 36, "data", 4);
  WRITE_U32 (headbuf + 40, size - 44);

  if (fwrite (headbuf, 1, 44, out) != 44)
    {
      printf ("ERROR: Failed to write wav header: %s\n", strerror (errno));
      return 1;
    }

  return 0;
}

static int
rewrite_header (FILE * out, unsigned int written)
{
  unsigned int length = written;

  length += 44;

  WRITE_U32 (headbuf + 4, length - 8);
  WRITE_U32 (headbuf + 40, length - 44);
  if (fseek (out, 0, SEEK_SET) != 0)
    {
      printf ("ERROR: Failed to seek on seekable file: %s\n",
          strerror (errno));
      return 1;
    }

  if (fwrite (headbuf, 1, 44, out) != 44)
    {
      printf ("ERROR: Failed to write wav header: %s\n", strerror (errno));
      return 1;
    }
  return 0;
}
person Mandrake    schedule 21.10.2009
comment
наконец, я могу добавить комментарии, спасибо, и исправить ошибку кода, у меня есть много кода C для публикации здесь - person Mandrake; 21.10.2009
comment
исправлена ​​ошибка, и обратите внимание, что в wav-файле есть действительные данные после позиции 44, в то время как в некоторых случаях инструменты Microsoft, которые обрабатывают wav-файлы, могут начинаться с позиции 60, а затем искать позицию данных в wav-файле для запуска в правильной позиции - person Mandrake; 25.10.2009

Некоторое время назад я наткнулся на функцию под названием WAVAPPEND на сайте обмена файлами Mathworks. У меня никогда не было времени использовать его, поэтому я не уверен, работает ли он или подходит для того, что вы пытаетесь сделать, но, возможно, он будет вам полезен.

person mtrw    schedule 22.09.2009
comment
Спасибо, я думаю, это будет полезно в будущем. - person Clifford; 22.09.2009

Хорошо ... Я здесь опоздал на 5 лет ... но я сделал это для себя и хотел предложить решение!

У меня была такая же проблема с нехваткой памяти при записи больших файлов wav в Matlab. Я обошел это, отредактировав функцию wavwrite в matlab, чтобы она извлекала данные с вашего жесткого диска, используя memmap вместо переменных, хранящихся в ОЗУ, а затем сохраняла их как новую функцию. Это избавит вас от многих проблем, так как вам не нужно беспокоиться о работе с заголовками при написании файла wav с нуля, и вам не понадобятся никакие внешние приложения.

1) введите edit wavwrite, чтобы увидеть код функции, затем сохраните его копию как новую функцию.

2) Я изменил переменную y в функции wavwrite из массива, содержащего данные wav, в массив ячеек со строками, указывающими на расположение данных каждого канала, сохраненного на моем жестком диске. Разумеется, сначала используйте fwrite для сохранения ваших wav-данных на жестком диске. В начале функции я преобразовал расположение файлов, хранящихся в y, в переменные memmap и определил количество каналов и выборок следующим образом:

замените эти строки:

% If input is a vector, force it to be a column:
if ndims(y) > 2,
  error(message('MATLAB:audiovideo:wavwrite:invalidInputFormat'));
end
if size(y,1)==1,
   y = y(:);
end
[samples, channels] = size(y);

с этим:

% get num of channels
channels = length(y);

%Convert y from strings pointing to wav data to mammap variables allowing access to the data
for i  = 1:length(y)
   y{i} = memmapfile(y{i},'Writable',false,'Format','int16');
end
samples = length(y{1}.Data);

3) Теперь вы можете редактировать приватную функцию write_wavedat(fid,fmt). Это функция, которая записывает данные WAV. Превратите его во вложенную функцию, чтобы она могла читать вашу переменную y memmap как глобальную переменную, вместо того, чтобы передавать значение функции и занимать вашу оперативную память, тогда вы можете внести некоторые изменения, например:

замените строки, которые записывают данные wav:

if (fwrite(fid, reshape(data',total_samples,1), dtype) ~= total_samples), error(message('MATLAB:audiovideo:wavewrite:failedToWriteSamples')); end

с этим:

%Divide data into smaller packets for writing
       packetSize = 30*(5e5); %n*5e5 = n Mb of space required
       packets = ceil(samples/packetSize);

       % Write data to file!
       for i=1:length(y)
           for j=1:packets
               if j == packets
                    fwrite(fid, y{i}.Data(((j-1)*packetSize)+1:end), dtype);
               else
                    fwrite(fid, y{i}.Data(((j-1)*packetSize)+1:j*packetSize), dtype);
               end
               disp(['...' num2str(floor(100*((i-1)*packets + j)/(packets*channels))) '% done writing file...']);
           end
       end

Это будет постепенно копировать данные из каждой переменной memmap в wav-файл.

4) Так и должно быть! Вы можете оставить остальной код как есть, так как он напишет заголовки за вас. Вот пример того, как вы могли бы написать большой двухканальный wav-файл с помощью этой функции:

wavwriteModified({'c:\wavFileinputCh1' 'c:\wavFileinputCh2'},44100,16,'c:\output2ChanWavFile');

Я могу убедиться, что этот подход работает, так как я только что написал 4-канальный wav-файл 800 МБ с моей отредактированной функцией wavwrite, когда Matlab обычно выдает ошибку out of memmory для записи файлов wav размером более 200 МБ для меня.

person RTbecard    schedule 05.07.2015

Для этого подойдет C #. С FileStreams легко работать, и их можно использовать для чтения и записи данных в кусках. Кроме того, чтение заголовков файлов WAV - относительно сложная задача (вам нужно искать фрагменты RIFF и т. Д.), Но запись их - это торт (вы просто заполняете структуру заголовка и пишете ее в начале файла).

Существует ряд библиотек, которые выполняют подобные преобразования, но я не уверен, что они могут обрабатывать огромные объемы данных, о которых вы говорите. Даже если они это сделают, вам, вероятно, все равно придется выполнить некоторую программную работу, чтобы передать меньшие фрагменты необработанных данных в эти библиотеки.

Для написания собственного метода нормализация несложна, и даже передискретизация с 48 к / с до 44,1 к / с относительно проста (при условии, что вы не против линейной интерполяции). У вас также, вероятно, будет больший контроль над выводом, поэтому было бы проще создать набор меньших файлов WAV, а не один гигантский.

person MusiGenesis    schedule 22.09.2009

Текущие образцы аудиозахвата Windows SDK захватывают данные с микрофона и сохраняют захваченные данные в файл .WAV. Код далек от оптимального, но должен работать.

Обратите внимание, что файлы RIFF (файлы .WAV - это файлы RIFF) ограничены размером 4G.

person ReinstateMonica Larry Osterman    schedule 25.09.2009
comment
Это сигналы основной полосы частот от РЧ-приемника, дискретизированные аналоговым модулем сбора данных. Необходимо сохранить смещение постоянного тока, чего нельзя сделать с микрофонным входом или другим аудиовходом, связанным по переменному току. Данные уже существуют как измерения напряжения с плавающей запятой. Вопрос заключался в упаковке существующих данных в форму, которая могла быть воспроизведена в RF-модулятор для повторяемого тестирования декодера основной полосы частот. Конечные файлы намного меньше, чем исходные, потому что исходные данные представляют собой плавающие данные двойной точности, тогда как преобразованные данные - это 16-битный PCM. - person Clifford; 25.09.2009
comment
Я просто указывал, что образцы содержат код, который создает файл WAV из необработанных данных PCM, не предлагая вам захватить с устройства. - person ReinstateMonica Larry Osterman; 26.09.2009