Reduce the volume of a Wav audio file using C

Luca P. picture Luca P. · Jun 3, 2013 · Viewed 7.3k times · Source

I am writing a C program for editing a Wav audio file. I have loaded all file datas in an array of unsigned integer values (UINT16_T).

Now, i would like to reduce the volume of the file. I thought it was enough to decrease the value (of a certain percentage) of the single values. But if i do that, i obtain an audio file with noise (I think I understand is called "static" or "click noise")

Why? Which is the right procedure?

Thank You!

This is the piece of code affected:

    FILE* fp;
    FILE* fp2;

    /*Size of my file*/
    #define BUFFER_SIZE 28242852

    /*Array with file data*/
    unsigned char *buffer;

    /*Array used for converting two bytes in an unsigned int*/
    unsigned char uintBytes[2];

    /*The unsigned int obtained*/
    uint16_t * conv;

    /*The new value calculated*/
    uint16_t nuovoValore;

    /*Array used for the reverse conversion, form UINT to bytes*/
    unsigned char* nuovoValArray;



    for(i=44; i<BUFFER_SIZE;i++){

    if(i%2==0){

        /*I read 2 bytes form the array and "convert" it in an unsigned int*/
        uintBytes[0]=buffer[i];
        uintBytes[1]=buffer[i+1];

        conv=(uint16_t *) &uintBytes[0];

          /*Calculate the new value (-30%) to write in the new file*/

        nuovoValore= *conv - ((float)*conv*30/100);
                  if(nuovoValore<0) nuovoValore=0;

                     nuovoValArray=malloc(2);
         memset(nuovoValArray,'\0',2);
        nuovoValArray=(unsigned char*)&nuovoValore;


            /*Write the two bytes of the new file*/
        fwrite(&nuovoValArray[0], 1, 1, fp2); 
        fwrite(&nuovoValArray[1], 1, 1, fp2);


    }
}

Answer

tay10r picture tay10r · Jun 4, 2013

To keep things simple, check all the specs of your audio file before compiling your program. A plain .wav file has the following attributes:

  • No compression (Audio format would be PCM)
  • 16 bit samples
  • Mono-channel (although your program might work with stereo)

So make sure the audio file you're parsing contains these attributes. Once you have verified that these attributes are common to your audio file, then you can begin testing. If your file does not contain these attributes, you may want to consider getting Audacity, or something similar, to make test .wav files.

Your code is a little strange. First you cast the data as a char, then to int, and then into float. That's going to give you some serious errors. All of these data types are different in size. Float also has a completely different binary format. A int of value 65 may be a float of -34564.23 (or something like that). Just use int16_t.

I also see that you've opened two files for your code - don't bother, since it makes the code bigger. Keep your code as simple as you can until it does what you want - then add the auxiliary attributes.

Also, on your fwrites you've written fwrite (&nuovoValArray[0], 1, 1, fp2) but it should be fwrite (&nuovoValArray[0], 2, 1, fp2) since the size of int16_t is 2 bytes and not 1.

When it comes to reducing the volume of the file, here's a general approach that should work:

  • Get sample samp[i] (16-bit or 2 bytes)
  • Reduce the volume: samp[i] -= (int16_t) (samp[i] * percent);
  • Increment i
  • Repeat

Here's a snippet of code that might help:

// open file
// read into char * fileBuffer

int sampleCount = ((fileSize - dataOffset) / sizeof (int16_t)); 
int16_t * samp  = (int16_t *) &fileBuffer[dataOffset];
float percent   = 0.6f;

for (int i = 0; i < sampleCount; i++){
    samp[i] -= (int16_t) (samp[i] * percent); // Should work +/- values
}

// save file

I previously had written an applications that graphs .wav files for waveform analysis. All I had to read to learn the file format was this page - it should help you as well.