Home How do I read an audio file into an array in C#
Reply: 2

How do I read an audio file into an array in C#

Bluehand
1#
Bluehand Published in 2017-12-05 21:21:35Z

I am trying to read a WAV file into a buffer array in c# but am having some problems. I am using a file stream to manage the audio file. Here is what I have...

FileStream WAVFile = new FileStream(@"test.wav", FileMode.Open);

        //Buffer for the wave file...
        BinaryReader WAVreader = new BinaryReader(WAVFile);

        //Read information from the header.
        chunkID = WAVreader.ReadInt32();
        chunkSize = WAVreader.ReadInt32();
        RiffFormat = WAVreader.ReadInt32();

        ...

        channels = WAVreader.ReadInt16();
        samplerate = WAVreader.ReadInt32();
        byteRate = WAVreader.ReadInt32();
        blockAllign = WAVreader.ReadInt16();
        bitsPerSample = WAVreader.ReadInt16();

        dataID = WAVreader.ReadInt32();
        dataSize = WAVreader.ReadInt32();

The above is reading data from the WAV file header. Then I have this:

musicalData = WAVreader.ReadBytes(dataSize);

...to read the actual sample data but this is only 26 bytes for 60 seconds of audio. Is this correct?

How would I go about converting the byte[] array to double[]?

LB2
2#
LB2 Reply to 2017-12-05 21:40:59Z

It's been a good 10-15 years since I touched WAVE file processing, but unlike the first impression that most people get about wave files as simple fixed-size header followed by PCM encoded audio data, WAVE files are a bit more complex RIFF format files.

Instead of re-engineering RIFF file processing and various cases, I would suggest to use interop and call on APIs that deal with RIFF file format.

You can see example of how to open and get data buffer (and meta information about what buffer is) in this example. It's in C++, but it shows use of mmioOpen, mmioRead, mmioDescend, mmioAscend APIs that you would need to use to get your hands on a proper audio buffer.

Horkrine
3#
Horkrine Reply to 2017-12-05 21:44:21Z

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++;
    }
}

If you wanted to use plugins, then 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 personally try to avoid using third-party libraries as much as I can. But the option is still there if you'd like the code to look better and easier to handle.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.3098 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO