5

作为一个小型实验音乐作品,我正在尝试用标准 C 编写一首歌曲。代码输出一个原始 PCM 文件,可以将其导入 Audacity。目前一切都按预期工作,但我在尝试将每个样本写入 16 位而不是我正在使用的当前 8 位时遇到问题。

在写入之前,当前样本被计算为浮点数,其边界几乎保持在有符号 8 位整数的范围内。然后在对下一个样本重复该过程之前将其写入为 8 位整数。这可以正常工作并且可以正常播放。当我尝试将其写为 16 位原始 PCM 文件时会出现问题 - 我将浮点数乘以 256 并将结果复制到一个整数,然后我使用 fwrite 写入生成的 16 位整数。这在导入时没有给出预期的结果,导致我所期望的高度失真的版本。

我在下面添加了有效代码,因为问题只发生在写作阶段。

工作8位代码:

if (out<-127) {out=-128;} else if (out>126) {out=127;}
putc(out,fo);

不工作的 16 位代码:

if (out<-127) {out=-128;} else if (out>126) {out=127;}
pcm=out*256;
fwrite(&pcm,2,1,fo);

我可能只是遗漏了一些明显的东西,但我一直在努力解决几个小时。提前致谢!

4

6 回答 6

4

我无法在没有看到代码的情况下说出您的代码到底出了什么问题,但这将使您成为一个不错的 1 KHz 正弦波 16 位 PCM,可在 Audacity 中打开:

#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358
#endif

int main(void)
{
  FILE* f = fopen("sinewave.pcm", "wb");
  double t;
  for (t = 0; t < 1; t += 1./8000) // 8000 is the sample rate in Hz
  {
    double sample = 15000 * sin(2 * M_PI * 1000 * t); // 1000 Hz sine wave
    short s16 = (short)sample;
    unsigned char c;
    c = (unsigned)s16 % 256;
    fwrite(&c, 1, 1, f);
    c = (unsigned)s16 / 256 % 256;
    fwrite(&c, 1, 1, f);
  }
  fclose(f);
  return 0;
}

在 Audacity 中浏览 File->Import->Raw Data:

编码:有符号 16 位 PCM
字节顺序:Little-endian
通道:1 通道(单声道)
采样率:8000

进口。

于 2012-07-17T11:21:13.747 回答
3

我想看看 Audacity 中的波形会给你一些线索。

你检查过:

  • 字节序正确吗?
  • 你不应该使用例如无符号整数吗?
  • 您已将文件正确标记为 16 位?

我不知道 PCM 的预期格式是什么,但这些都可能是问题的候选者。

于 2010-07-24T15:22:23.943 回答
0

在进行转换时进行类型转换是一种很好的做法。例如,如果out是一个浮点数,那么

putc((int) out, fo);

会让编译器知道你想把你的数字写成整数。

当然,编译器无论如何都会为putc之类的东西弄清楚这一点,但这不适用于引用。如果您将pcm变量声明为浮点数,则 fwrite 将写入浮点数据而不是您想要的数据。所以我会问同样的问题:pcm是整数类型吗?

另一个问题是:你真的需要浮点吗?如果您可以使用小数精度,则可能需要它(同样,您将通过输出为 8 位或 16 位格式而失去该精度),但如果您只对样本进行简单的数学运算,这是一种浪费。因此,您可以通过坚持使用整数类型并在编写时将其转换为 char/int8_t 来简化很多事情。

于 2010-07-24T15:29:56.150 回答
0

在这里尝试一下,但是由于您想要有符号的 16 位值,请尝试以下操作:

int16_t pcm = out * 256;
fwrite(&pcm, sizeof(pcm), 1, fo);

另外,请确保您已正确标记文件,即。原始 PCM 以适当的字节序签名 16 位。(编辑:这不适用于 PCM)

于 2010-07-24T15:33:39.780 回答
0

僵尸这个线程:

来自 WAV 维基:

WAV 格式存在一些不一致:例如,8 位数据是无符号的,而 16 位数据是有符号的

于 2012-07-17T10:55:22.530 回答
0

您必须将代码从浮点重新采样为整数,这意味着您需要在某个点进行舍入以避免向信号添加噪声和 DC 偏移

float sample = out * 256.f;   // amplify to new range.
int pcm32 = (int)floorf(sample + .5f); // round and convert to 32 bit pcm.

// saturate just before conversion if possible, that's always safer.
if (pcm32 > SHRT_MAX) pcm32 = SHRT_MAX;
if (pcm32 < SHRT_MIN) pcm32 = SHRT_MIN;

short int pcm16 = (short int)pcm32;  // keep the lowest 16 bits

您是否考虑过为幅度使用 -1.0 到 +1.0 的标准归一化范围?

于 2019-09-22T00:02:51.907 回答