繁体   English   中英

C ++读取16位Wav文件

[英]C++ reading 16bit Wav file

我在读取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);
}

但是,当我为16bit执行此操作时,这很有用:

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);
}

不起作用,并显示输出(上方)。

我遵循这里找到的标准

我的datachar数组,而rawSignalstd::vector<double>我可能只是在处理转换错误,但似乎找不到位置。 有人有什么建议吗?

谢谢

编辑:

这是现在显示的内容(在图中):

在此输入图像描述

这是它应该显示的内容:

在此输入图像描述

这里有一些问题:

  • 8位wavs是无符号的,但是16位wavs是有符号的。 因此,Carl和Jay的答案中给出的减法步骤是不必要的。 我认为他们只是从你的代码中复制,但他们错了。
  • 16位波的范围从-32,768到32,767,而不是-256到255,这使得你使用的乘法无论如何都是错误的。
  • 16位wavs是2个字节,因此你必须读取两个字节来制作一个样本,而不是一个。 您似乎一次只能阅读一个角色。 读取字节时,如果您的本地字节序不是little-endian,则可能必须交换它们。

假设是一个低端架构,您的代码将看起来像这样(非常接近卡尔的答案):

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);
}

对于大端架构:

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如果不起作用。

(首先关于little-endian / big-endian-ness。WAV只是一种容器格式,其中编码的数据可以是无数格式。大多数编解码器都是无损的(MPEG Layer-3又称MP3,是的,流可以“打包”成WAV,各种CCITT和其他编解码器。。你假设你处理某种PCM格式,你看到RAW格式的实际波形,没有进行无损转换。字节顺序取决于在编解码器上生成流。 是否在RIFF WAV文件中保证格式参数的字节顺序?

这也是一个问题,如果一个PCM样本是线性比例采样整数或者它背后有一些缩放,对数比例或其他转换。 我遇到的常规PCM wav文件是简单的线性比例样本,但是我不在音频录制或制作行业工作。

所以你的解决方案的路径:

  1. 确保您正在处理常规的16位PCM编码RIFF WAV文件。
  2. 在读取流时,始终一次读取两个字节(char)并将两个字符转换为16位短。 人们在我面前展示了这一点
  3. 您显示的波形清楚地表明您要么没有很好地估计频率 (或者您只有一个单声道而不是立体声 )。 因为采样率(44.1kHz,22KHz,11KHz,8kHz等)与分辨率(8位,16位,24位等)一样重要。 也许在第一种情况下,您有立体声数据。 您可以单声道阅读它,您可能不会注意到它。 在第二种情况下,如果您具有单声道数据,则在读取数据的一半过程中将用尽样本。 根据您的图表,这似乎就是这种情况。 谈论另一个原因:较低的采样分辨率(以及16位也较低)通常与较低的采样率配对。 因此,如果您的输入数据是录制时间,并且您认为您有22kHz的数据,但它实际上只有11kHz,那么您将从实际样本中途耗尽并读入内存垃圾。 所以这些中的任何一个。

确保您正确解释和处理循环迭代器变量和大小。 看起来大小告诉您有多少字节。 你只有一半的短整数样本。 注意,正因为如此,Bjorn的解将i正确地增加了2。

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位值转换为有符号值,然后将其写入无符号变量。 你应该看一下,看看它是不是你想要的。

我的工作代码是

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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM