WAV-файл из захваченных образцов данных PCM - PullRequest
4 голосов
/ 22 сентября 2009

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

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

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

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

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

Ответы [ 6 ]

3 голосов
/ 21 октября 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;
}
2 голосов
/ 22 сентября 2009

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

1 голос
/ 05 июля 2015

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

У меня была такая же проблема с нехваткой памяти при записи больших файлов WAV в Matlab. Я справился с этим, отредактировав функцию matlab wavwrite так, чтобы она извлекала данные с вашего жесткого диска, используя 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 в wavfile

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

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

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

1 голос
/ 22 сентября 2009

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

0 голосов
/ 25 сентября 2009

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

Обратите внимание, что файлы RIFF (файлы .WAV являются файлами RIFF) имеют размер не более 4 ГБ.

0 голосов
/ 22 сентября 2009

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

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

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

...