我使用NI数据采集模块以48ksps“现场”捕获了几个Gb样本数据。我想从这些数据创建一个WAV文件。
我之前使用MATLAB加载数据,将其标准化为16位PCM范围,然后将其写为WAV文件。然而,MATLAB对文件大小进行了抨击,因为它完成了“内存中”的所有内容。
理想情况下,我会在C ++或C中执行此操作,(C#是一个选项),或者如果有现有实用程序,我会使用它。是否有一种简单的方法(即现有的库)来获取原始PCM缓冲区,指定采样率,位深度,并将其打包成WAV文件?
要处理大型数据集,它需要能够以块的形式附加数据,因为不一定可以将整个数据集读入内存。
我知道我可以使用格式规范从头开始这样做,但我不想重新发明轮子,或者花时间修复bug,如果我能帮助它的话。
答案 0 :(得分:3)
有趣的是,我发现了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;
}
答案 1 :(得分:2)
我认为您可以使用libsox。
答案 2 :(得分:1)
我刚才在Mathworks的文件交换网站上遇到了一个名为WAVAPPEND的函数。我从来没有使用它,所以我不确定它是否有效或适合你想要做的事情,但也许它对你有用。
答案 3 :(得分:1)
好的......我在这里已经晚了5年......但我只是为自己做了这件事,并想把解决方案放在那里!
在matlab中编写大型wav文件时,内存不足也存在同样的问题。我通过编辑matlab wavwrite函数解决了这个问题,因此它使用memmap
而不是存储在RAM中的变量从硬盘驱动器中提取数据,然后将其保存为新函数。这将为您省去很多麻烦,因为您不必担心从头开始编写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变量读作一个全局变量,而不是将值传递给函数并占用你的RAM,然后你可以做一些这样的改动:
替换写入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');
我可以验证这种方法是有效的,因为我刚刚用编辑过的wavwrite函数写了一个800mB的4通道wav文件,当matlab通常会为我编写大于200mb的wav文件时抛出out of memmory
错误。
答案 4 :(得分:0)
C#将是一个不错的选择。 FileStreams易于使用,可用于以块的形式读取和写入数据。此外,读取WAV文件头是一项相对复杂的任务(您必须搜索RIFF块等),但写它们是蛋糕(您只需填写标题结构并在开头写入它)该文件)。
有很多库可以进行这样的转换,但我不确定它们是否可以处理您所谈论的巨大数据量。即使他们这样做,您可能仍然需要做一些编程工作来将较小的原始数据块提供给这些库。
对于编写自己的方法,归一化并不困难,甚至从48ksps到44.1ksps的重采样也相对简单(假设你不介意线性插值)。你也可以更好地控制输出,因此创建一组较小的WAV文件会更容易,而不是一个巨大的WAV文件。
答案 5 :(得分:0)
当前的Windows SDK音频捕获示例从麦克风捕获数据并将捕获的数据保存到.WAV文件中。代码远非最佳,但它应该可以工作。
请注意,RIFF文件(.WAV文件为RIFF文件)的大小限制为4G。