我在读取 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>
我可能只是把转换错了,但似乎无法找出哪里。大家有什么建议吗?
谢谢
编辑:
这就是现在显示的内容(在图表中):
这就是它应该显示的内容:
这里有几个问题:
假设采用 little-endian 架构,您的代码看起来更像这样(非常接近 Carl 的答案):
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);
}
对于big-endian架构:
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。
(首先关于小端/大端。WAV 只是一种容器格式,其中编码的数据可以是无数格式。大多数编解码器都是无损的(MPEG Layer-3 又名 MP3,是的,流可以“打包”成 WAV、各种 CCITT 和其他编解码器)。您假设您处理某种 PCM 格式,您看到的是 RAW 格式的实际波形,没有对其进行无损转换。取决于生成流的编解码器。 RIFF WAV 文件中格式参数的字节序是否得到保证?)
还有一个问题是,一个 PCM 样本是否是线性比例采样整数,或者其背后是否存在某种比例、对数比例或其他变换。我遇到的常规 PCM wav 文件是简单的线性比例样本,但我并不从事音频录制或制作行业。
解决方案的路径:
确保正确解释和处理循环迭代器变量及其大小。看起来大小告诉你有多少字节。您将获得正好一半的短整数样本。请注意,Bjorn 的解决方案因此正确地将 i 增加了 2。
我的工作代码是
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);
}
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);
}
您可能想要更多类似这样的东西:
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 位值转换为有符号值,然后将其写入无符号变量。你应该看看它是否是你想要的。
输入 #0,wav,来自 'SampleAudio.wav':持续时间:00:01:06.36,比特率:256 kb/sStream #0:0:音频:pcm_s16le ([1][0][0][0] / 0x0001)、8000 Hz、立体声、s16、256 kb/s**[
1.
[`
##
***Heading***
##
`
]2**