tags:

views:

229

answers:

3

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!

+1  A: 

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.

Oli Charlesworth
If he's writing a raw PCM file, then there won't be any file format header (so your 3rd point is probably not necessary). The OP would have to choose the format (such as mono/stereo, sampling rate, bit width per sample, etc.) upon importing in Audacity.
stakx
the bits, signed/unsigned and endianness (tried all the types) are selected when importing.
blkrbt
@blkrbt: Are you able to synthesise arbitrary waveforms? Can you generate a full-scale sine wave and look at the waveform in Audacity, to see if it's clipped/wrapped/something else?
Oli Charlesworth
A: 

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.

spyked
pcm is an integer. I've also tried using it as a 16bit integer as was suggested in one of the other responses (same result). I do need to use floats for calculating the sample up to the point of writing it.
blkrbt
A: 

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)

Michael Foukarakis
This gives an identical result as what I posted above.
blkrbt