写入 PCM 数据时的文件 (.wav) 持续时间 @16KBps
Posted
技术标签:
【中文标题】写入 PCM 数据时的文件 (.wav) 持续时间 @16KBps【英文标题】:File (.wav) duration while writing PCM data @16KBps 【发布时间】:2013-12-19 15:10:13 【问题描述】:我正在 @16KBps 文件中写入一些无声 PCM 数据。此文件为 .wav 格式。为此,我有以下代码:
#define DEFAULT_BITRATE 16000
long LibGsmManaged:: addSilence ()
char silenceBuf[DEFAULT_BITRATE];
if (fout)
for (int i = 0; i < DEFAULT_BITRATE; i++)
silenceBuf[i] = '\0';
fwrite(silenceBuf, sizeof(silenceBuf), 1, fout);
return ftell(fout);
更新: 这是我写标题的方式
void LibGsmManaged::write_wave_header( )
if(fout)
fwrite("RIFF", 4, 1, fout);
total_length_pos = ftell(fout);
write_int32(0);
fwrite("WAVE", 4, 1, fout);
fwrite("fmt ",4, 1, fout);
write_int32(16);
write_int16(1);
write_int16(1);
write_int32(8000);
write_int32(16000);
write_int16(2);
write_int16(16);
fwrite("data",4,1,fout);
data_length_pos = ftell(fout);
write_int32(0);
else
std::cout << "File pointer not correctly initialized";
void LibGsmManaged::write_int32( int value)
if(fout)
fwrite( (const char*)&value, sizeof(value), 1, fout);
else
std::cout << "File pointer not correctly initialized";
我在我的 ios 设备上使用 NSTimer 以 1.0 秒的间隔运行此代码。所以 AFAIK,如果我运行 60 秒,我应该得到一个 file.wav,播放时应该显示 60 秒作为其持续时间(再次 AFAIK)。但在实际测试中,它显示几乎两倍的持续时间,即 2 分钟。 (大约)。我还测试过,当我将 DEFAULT_BITRATE 更改为 8000 时,文件持续时间几乎是正确的。
我无法确定这里发生了什么。我在这里错过了什么不好的东西吗?我希望我的代码没有错。
【问题讨论】:
您不是在向数据写入实际的零,而是在写入'0'
字符。也许你想要'\0'
? (不是说这会影响长度,只是指出来)
文件头包含音频数据大小的注释 - 请参阅 this diagram 中的 Subchunk2Size。你能展示你如何将标题写入文件吗?
@simonc 抱歉回复晚了。在那之后我就昏倒了。请查看更新后的问题。
@Xymotech 非常感谢您指出错误。我已经纠正了。请查看更新后的代码
-1 是为了什么?至少有礼貌地给出理由。
【参考方案1】:
您正在尝试做的事情(编写您自己的 WAV 文件)应该完全可行。这就是好消息。但是,我对您的确切参数和约束有点困惑,就像 cmets 中的许多其他人一样,这就是他们一直试图充实细节的原因。
您想将原始、未压缩、无声的 PCM 写入 WAV 文件。好的。 PCM 数据需要多宽? 您正在创建要写入文件的字符数组。 char 是一个 8 位字节。那是你要的吗?如果是这样,那么您需要使用 0x80 (128) 的静默中心点。 WAV 文件中的 8 位 PCM 是无符号的,即 0..255,而 128 是无符号的。
如果您打算存储静默 16 位数据,那将是有符号数据,因此中心点(-32768 和 32767 之间)为 0。此外,它将以小端字节格式存储。但既然是静音(全为 0),那没关系。
您的问题标题表明(第一句重申)您希望以 16 kbps 的速度写入数据。 您确定要 16 kbps 的原始音频吗?即每秒 16 kbps 或每秒 16000 位。根据您是编写 8 位还是 16 位 PCM 样本,这仅允许 2000 或 1000 Hz 音频,这可能不是您想要的。 您的意思是 16 kHz 音频吗? 16 kHz 音频转换为每秒 16000 个音频样本,这更符合您的代码。再说一次,您的代码提到了 GSM (LibGsmManaged
),所以也许您正在寻找 16 kbps 的音频。但我假设我们正在沿着原始 PCM 路线前进。
你事先知道你需要写多少秒的音频吗?这让这个过程变得非常简单。您可能已经注意到,WAV 标头在一些地方需要长度信息。您可以提前写下(如果您知道值)或稍后填写(如果您写的是不确定的金额)。
假设您正在将 2 秒的原始、单声道、16000 Hz、16 位 PCM 写入 WAV 文件。中心点为 0x0000。
WAV编写过程:
-
写信
'RIFF'
写入 32 位文件大小,即 36(标头大小 - 前 8 个字节)+ 64000(有关该数字,请参见第 12 步)
写'WAVEfmt '
(带空格)
写入 32 位格式标头大小 (16)
写入 16 位音频格式(1 表示原始 PCM 音频)
写入 16 位通道数(1 因为它是单声道的)
写入 32 位采样率(每秒音频采样数 = 16000)
写入 32 位字节速率(每秒字节数 = 32000)
写入 16 位块对齐(每个样本 2 字节 * 1 通道 = 2)
每个样本写入 16 位比特 (16)
写信'data'
写入 32 位长度的音频负载数据(16000 个样本/秒 * 2 字节/样本 * 2 秒 = 64000 个字节)
写入 64000 字节,全为 0 值
如果您需要编写动态数量的音频数据,请将第 2 步和第 12 步中的长度字段保留为 0,然后在完成编写后返回并填写。我不相信您的原始代码正在正确写入长度字段。一些播放软件可能会忽略这些,而另一些可能不会,因此您可能会得到不同的结果。
希望对您有所帮助!如果您了解 Python,这是我回答的另一个问题,它描述了如何使用 write a WAV file using Python's struct library(在编写上述步骤时,我经常提到该代码片段)。
【讨论】:
现在我意识到 'b' 和 'B' 是如何发挥作用的。我已经更新了我的问题标题。实际上它是@16kBps,即千字节而不是千比特。你的答案会不会不同?您还提到了第 2 点和第 12 点,我需要将其保留为 0,这正是我正在做的事情,因为数据是动态的。这里应该纠正什么吗? 我的答案不一样?使用原始 PCM 数据约束硝酸盐并不是那么有趣。更多的是关于样本大小和频率。您打算使用什么样本量和频率?该信息将影响此处的其他数字。关于文件大小,如前所述,在您完成文件写入并知道您有多少数据后,您需要返回 2 个点(您在步骤 2 和 12 中留空的点)并填写这些数字。 *比特率!限制比特率,而不是硝酸盐。 :-)以上是关于写入 PCM 数据时的文件 (.wav) 持续时间 @16KBps的主要内容,如果未能解决你的问题,请参考以下文章