For those of you who don’t know me, I have a unique skill where I can speak backwards phonetically.
Although it’s quite a useless skill, there is a Guinness World Record for it which I’m trying to beat.
To train for this, I needed to make a backwards player app so I could test myself.
Of course there are already many free apps out there, but instead of just downloading one of those, I thought I’d reinvent the wheel, and learn heaps in the process.
The format of a .wav file
According to http://www.topherlee.com/software/pcm-tut-wavformat.html, a .wav file is made up of a header and audio data. The header looks like this:
The header provides metadata to do with the audio file. Some parts of the metadata are essential for us to know if we are to reverse the file.
After the header is the audio data, which is basically the actual values of the audio signal’s amplitudes which make up the sound.
So now we know that a wav file has metadata, and audio data, how do we go about reversing the sound?
What exactly do we need to reverse?
We definitely don’t want to reverse the header, since this contains the metadata to do with the file, so all we need to reverse is the audio data, which comes after the header.
Audio data and samples
Unfortunately, we can’t simply create a new file with a copy of the header, and then a copy of the audio data in reverse.
This is because a wav file has things called “samples”. If each sample was only one byte, then we could simply copy the header into a new byte array, then use a for loop to copy each element of the audio data, in reverse, into the new byte array, then create a new wav file based on this new byte array.
What we need to reverse are these “samples”.
What makes up a sample?
If we want to reverse the samples, we need to know what exactly makes up a sample, ie. how many bytes are used to represent each sample?
According to the documentation for a wav file, the “bits per sample” is stored in the wav file’s byte array at index 34 and 35.
With the wav file I was using for testing, I had a value of 16 in index 34, and 0 in 35.
According to http://soundfile.sapp.org/doc/WaveFormat/, if your first 4 bytes contain the string “RIFF”, as opposed to “RIFX”, then the file’s byte order is little-endian.
This means the value in index 35 of my wav file’s byte array is the most significant byte, and the byte in index 34 is the least significant byte.
So to get the bits per sample, we need to read the number as 0 followed by 16, which looks like this in binary, 0000000010000, which is 16 in decimal.
We now know that there are 16 bits (or 2 bytes), per sample.
To reverse the sound of a wav file, we need to do the following:
- Get the byte array that makes up the audio file.
- Extract the bytes that make up the header.
- Extract the audio data samples and reverse them.
- Create a new byte array and copy in the header from step 2, and the reversed samples from step 3.
- Write this new byte array to a file.
This is now available as a NuGet package.
First we need to read the file’s file stream into a byte array, and then close the file stream:
(All the following code samples are available on my GitHub page).
Now we need to extract the header from the byte array we read from the file stream in step 1.
Here I’m using a static variable from a class called
I’ll show you this class in its entirety later on, for now you just need to know that its value is 44.
Now comes the hard part, reversing the samples.
As we mentioned earlier, because my audio file has 2 bytes per sample, we can’t just use a for loop to iterate through the forwards byte array, in reverse, copying each element into the new byte array.
If we did this, we would not only be reversing the samples, but reversing each sample’s bytes which would result in completely wrong amplitude values.
To work out how to design the for loop, let’s begin with an easy example, a byte array with only 10 elements and 2 bytes per sample:
As you can see from the diagram above, our for loop needs to count in a very odd way.
To count like this, we need to start from the very end of the array.
We then need to work our way backwards through the array, but not per index, rather per sample, copying in each sample’s bytes into our new byte array, preserving the sample’s forwards order of bytes.
The variable called sampleIdentifier is used to help us go backwards, sample by sample, through the array.
The if statement:
if (i != 0 && i % bytesPerSample == 0), helps determine if we are at an even, or an odd index.
If we’re at an odd index, then we need to go one index forwards in our reverse for loop to get the last byte from the sample.
This is quite hard to explain, so the best way to understand what is going on is to examine the code below, and step through it in debug mode to see what exactly is happening.
The second last step is to create a new byte array to store our new values. We need to copy in the header bytes, and the reversed samples.
The final step is easy, just write the new byte array to a file, and you’re done!
The entire code
You can view the code on my GitHub page.
I’ve written the code so that you can run it from any project type. You just need to call