Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read the data in a wav file to an array

Tags:

c#

file-io

audio

I need to get all the samples of a wav file into an array (or two if you need to do that to keep the stereo) so that I can apply some modifications to them. I was wondering if this is easily done (preferably without external libraries). I have no experience with reading in sound files, so I don't know much about the subject.

like image 252
annonymously Avatar asked Jan 06 '12 06:01

annonymously


People also ask

How do I view the contents of a WAV file?

Windows and Mac are both capable of opening WAV files. For Windows, if you double-click a WAV file, it will open using Windows Media Player. For Mac, if you double-click a WAV, it will open using iTunes or Quicktime. If you're on a system without these programs installed, then consider third-party software.

Does WAV have metadata?

FLAC, AIFF and MP3 formats take full advantage of metadata whereas WAV files only allow a little metadata input, so not as helpful for browsing your library. It's important if you want to locate and play music around your home. Network players like our CXN use metadata to browse and play your music.

What data does a WAV file contain?

Though a WAV file can contain compressed audio, the most common WAV audio format is uncompressed audio in the linear pulse-code modulation (LPCM) format. LPCM is also the standard audio coding format for audio CDs, which store two-channel LPCM audio sampled at 44,100 Hz with 16 bits per sample.


2 Answers

This code should do the trick. It converts a wave file to a normalized double array (-1 to 1), but it should be trivial to make it an int/short array instead (remove the /32768.0 bit and add 32768 instead). The right[] array will be set to null if the loaded wav file is found to be mono.

I can't claim it's completely bullet proof (potential off-by-one errors), but after creating a 65536 sample array, and creating a wave from -1 to 1, none of the samples appear to go 'through' the ceiling or floor.

// convert two bytes to one double in the range -1 to 1 static double bytesToDouble(byte firstByte, byte secondByte) {     // convert two bytes to one short (little endian)     short s = (secondByte << 8) | firstByte;     // convert to range from -1 to (just below) 1     return s / 32768.0; }  // Returns left and right double arrays. 'right' will be null if sound is mono. public void openWav(string filename, out double[] left, out double[] right) {     byte[] wav = File.ReadAllBytes(filename);      // Determine if mono or stereo     int channels = wav[22];     // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels      // Get past all the other sub chunks to get to the data subchunk:     int pos = 12;   // First Subchunk ID from 12 to 16      // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))     while(!(wav[pos]==100 && wav[pos+1]==97 && wav[pos+2]==116 && wav[pos+3]==97)) {         pos += 4;         int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;         pos += 4 + chunkSize;     }     pos += 8;      // Pos is now positioned to start of actual sound data.     int samples = (wav.Length - pos)/2;     // 2 bytes per sample (16 bit sound mono)     if (channels == 2) samples /= 2;        // 4 bytes per sample (16 bit stereo)      // Allocate memory (right will be null if only mono sound)     left = new double[samples];     if (channels == 2) right = new double[samples];     else right = null;      // Write to double array/s:     int i=0;     while (pos < length) {         left[i] = bytesToDouble(wav[pos], wav[pos + 1]);         pos += 2;         if (channels == 2) {             right[i] = bytesToDouble(wav[pos], wav[pos + 1]);             pos += 2;         }         i++;     } } 
like image 123
Dan W Avatar answered Oct 21 '22 03:10

Dan W


Assuming your WAV file contains 16 bit PCM (which is the most common), you can use NAudio to read it out into a byte array, and then copy that into an array of 16 bit integers for convenience. If it is stereo, the samples will be interleaved left, right.

using (WaveFileReader reader = new WaveFileReader("myfile.wav")) {     Assert.AreEqual(16, reader.WaveFormat.BitsPerSample, "Only works with 16 bit audio");     byte[] buffer = new byte[reader.Length];     int read = reader.Read(buffer, 0, buffer.Length);     short[] sampleBuffer = new short[read / 2];     Buffer.BlockCopy(buffer, 0, sampleBuffer, 0, read); } 

I know you wanted to avoid third party libraries, but if you want to be sure to cope with WAV files with extra chunks, I suggest avoiding approaches like just seeking 44 bytes into the file.

like image 22
Mark Heath Avatar answered Oct 21 '22 02:10

Mark Heath