6

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);


    }
}
Luca P.
  • 89
  • 1
  • 7
  • Can you show us how you tried decreasing the volume? For example, if you just blindly subtracted a number, only the top half of the waves would be smaller and the negative wave would be larger. You should also make sure that the file you're dealing with doesn't use any sort of compression – tay10r Jun 03 '13 at 20:43
  • I think WAV files use signed samples. Have you tried loading the values as signed integers? Also, there's a small header; are you parsing that, or modifying it along with the other data: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ – Taylor Brandstetter Jun 03 '13 at 20:47
  • 2
    `UINT16_T` is a dead giveaway. Wav uses sign for 16-bit values, and unsigned for 8-bit values. @Luca, you should show us some of your code. – tay10r Jun 03 '13 at 20:50
  • Thank you, i added the piece of code affected! – Luca P. Jun 03 '13 at 21:22
  • Googling i've found some examples that uses UINT_16...so i did!! :) but maybe this kind of data is right only for the header informations... – Luca P. Jun 03 '13 at 21:28
  • I might be wrong, but from what I've remember from messing with WAV files, what you were messing around was the sampled signals, in express in the frequency domain, not the actual discrete values. – Akatosh Jun 03 '13 at 21:49
  • `((float)*conv*30/100)` - you want to be very careful here with type promotion and evaluation order. This is also an odd way of effecting a 30% reduction. – marko Jun 03 '13 at 21:53
  • You are all very kind, unfortunately i haven't found any complete documentation about this problem, (have you??), and this code is the maximum that i can do. Maybe you can explain me in a somewhat more complete way how i can write the correct code?? It would be just as great if you can provide me some good documentation!! – Luca P. Jun 03 '13 at 22:02
  • @Akatosh I had the same doubt, but i have no i idea of how i could proceed in that way!! – Luca P. Jun 03 '13 at 22:05

1 Answers1

4

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.

tay10r
  • 4,234
  • 2
  • 24
  • 44
  • THANK YOU VERY MUCH, the code works PERFECTLY!!!! My audio file is 16bit PCM, but i must work with a STEREO file. However, your code worked fine anyway. All that different kind of data are now unnecessary. Regarding the two opened files, one is for reading the original file (r), and the other is for writing the new file. Is it incorrect?? – Luca P. Jun 03 '13 at 22:56
  • It's not incorrect opening two files like that, I just was suggesting to open/read/write with one file for simplicity, until your program works. Generally you'll want to create a new file and preserve the original, as you have done. And yes, it should work with mono and stereo. – tay10r Jun 03 '13 at 23:02
  • Yes, i created a new file for preserving the original. Thank you again. – Luca P. Jun 03 '13 at 23:11
  • Couldn't this line (samp[i] -= (int16_t) (samp[i] * percent);) be reduced to samp[i] = (int16_t) (samp[i] * percent); ? – jjxtra Feb 26 '15 at 17:09
  • @PsychoDad yeah, but it means something different. In the expression I used, `1.0f percent` means `100%` reduction of sound and `0.0f` means no reduction in sound. In the expression you mentioned `1.0f` means no reduction and `0.0f` means complete reduction. – tay10r Feb 27 '15 at 01:41