Problem writing a 16-bit PCM source file

As a small experimental piece of music, I am trying to program a song in the C standard. The code outputs a raw PCM file that can be imported into Audacity. At the moment, everything works as expected, but I ran into problems when trying to write each sample as 16 bits, and not the current 8-bit I use.

Until recording, the current sample is calculated as a float, and its boundaries are stored within the range of an 8-bit integer. It is then written as an 8-bit integer before repeating the process for the next sample. It works great and plays correctly. The problem arises when I try to write it as a 16-bit raw PCM file - I multiply the float by 256 and copy the result to an integer, after which I use fwrite to write the resulting 16-bit integer. This does not give the expected results when importing, which leads to a very distorted version of the expected.

I added the current code below, since the problem only occurs at the writing stage.

Working 8-bit code:

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

16-bit code does not work:

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

I probably just missed something obvious, but I tried to work for hours. Thanks in advance!

+5
source share
6 answers

I canโ€™t say what exactly is wrong in your code without seeing it, but it will make you enjoy a 16-bit PCM with a 1 kHz sinusoidal frequency opened 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, go to File-> Import-> Raw Data:

Encoding: Signed 16-bit PCM
Byte Order: Small-endian
Channels: 1 channel (mono)
Sampling Rate: 8000

Import.

+4
source

I would suggest that looking at the waveform in Audacity would give you some clues.

Have you checked it:

  • is the correctness correct?
  • which you should not use, for example. unsigned integers?
  • Did you mark the file correctly as 16-bit?

I do not know what the expected format for PCM is, but these are all likely candidates for this problem.

+3
source

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

 putc((int) out, fo); 

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

Sure, the compiler will still see that it is something like putc, but this does not work for links. If you declare the pcm variable as a float, then fwrite will write the floating point data instead of what you want. So I will ask the same question: pcm is an integer type?

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

0
source

Go for a limb here, but since you want to have 16-bit values, try the following:

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

Also, make sure you tag your file correctly, i.e. raw PCM, signed 16 bits with the corresponding limb. (editing: this is not applicable for PCM)

0
source

To zombies this topic:

From the WAV Wiki:

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

0
source

You must recount your floating point code to an integer, which means you need to round at some point to avoid adding noise and DC bias 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 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 a standard normalized range from -1.0 to +1.0 for your amplitudes?

0
source

All Articles