写入 16 位原始 PCM 文件时出现问题

发布于 2024-09-10 22:07:30 字数 700 浏览 9 评论 0原文

作为一个小型实验音乐作品,我尝试用标准 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);

我可能只是错过了一些明显的东西,但我已经尝试了几个小时来解决它。提前致谢!

As a small experimental music piece I am attempting to program a song in standard C. The code outputs a raw PCM file which can be imported into Audacity. At the moment everything works as expected, but I'm encountering problems when trying to write each sample as 16 bit as opposed to the current 8 bit I am using.

Up until the point of being written, the current sample is calculated as a float, and its bounds are kept pretty much within the range of a signed 8 bit integer. It is then written as a 8 bit integer before repeating the process for the next sample. This works fine and plays properly. The problem occurs when I try to write it as a 16bit raw PCM file - I multiply the float by 256 and copy the result to a integer, whereupon I use fwrite to write the resulting 16bit integer. This does not give the expected results when imported, resulting in a highly distorted version of what I was expecting.

I've added the valid code below, since the problem occurs only at the writing stage.

Working 8bit code:

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

Not working 16bit code:

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

I'm probably just missing something obvious, but I've been trying to work it out for hours. Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

清音悠歌 2024-09-17 22:07:30

如果没有看到代码,我无法判断代码中到底出了什么问题,但这将使您在 Audacity 中可以打开一个漂亮的 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;
}

通过文件 -> 导入 -> 原始数据导航:

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

导入。

I can't tell what exactly is wrong in your code without seeing it, but this will make you a nice 1 KHz sine wave 16-bit PCM openable in 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;
}

In Audacity navigate through File->Import->Raw Data:

Encoding: Signed 16-bit PCM
Byte Order: Little-endian
Channels: 1 Channel (Mono)
Sample rate: 8000

Import.

很糊涂小朋友 2024-09-17 22:07:30

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

您检查过:

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

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

I would imagine looking at the waveform in Audacity would've given you some clues.

Have you checked:

  • the endianness is correct?
  • that you're not supposed to be using e.g. unsigned integers?
  • you've correctly marked the file as 16-bit?

I don't know what the expected format is for PCM, but these are all likely candidates for the problem.

我最亲爱的 2024-09-17 22:07:30

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

putc((int) out, fo);

会让编译器知道您要将数字写为整数。

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

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

It's good practice to do type casts when doing conversions. For example, if out is a float, then

putc((int) out, fo);

will let the compiler know that you want to write your number as an integer.

Sure, the compiler will figure that out anyway for something like putc, but this doesn't work for referencing. If you declare the pcm variable as a float, then fwrite will write floating point data instead of what you want. So i'll ask the same question: is pcm an integer type?

Another question is: do you really need floating point here? You might need it if you can use the decimal precision (then again, you'll lose that precision by outputting into an 8-bit or 16-bit format), but it's a waste if you only do simple math with your samples. Therefore you can simplify things a lot by sticking to an integer type, and converting that to a char/int8_t when writing.

眼睛会笑 2024-09-17 22:07:30

这里有点冒险,但由于您想要带符号的 16 位值,请尝试以下操作:

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

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

Going on a limb here, but since you want signed 16-bit values, try this:

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

Also, make sure you've marked your file correctly, ie. raw PCM signed 16 bit with the appropriate endian-ness. (edit: this isn't applicable for PCM)

╰ゝ天使的微笑 2024-09-17 22:07:30

僵尸这个线程:

来自 WAV Wiki:

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

To zombie this thread:

From the WAV Wiki:

There are some inconsistencies in the WAV format: for example, 8-bit data is unsigned while 16-bit data is signed

愁以何悠 2024-09-17 22:07:30

您必须将代码从浮点重新采样为整数,这意味着您需要在某个点进行舍入以避免向信号添加噪声和 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 的标准归一化范围?

You have to resample your code from floating point to integer, which means you need to round at some point to avoid adding noise and DC offset to your signal

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

Have you considered using the standard normalized range of -1.0 to +1.0 for your amplitudes?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文