This project has moved. For the latest updates, please go here.

newbie question

Nov 16, 2009 at 9:18 AM
Edited Nov 16, 2009 at 9:23 AM

Hello all,

For my first little project I just want to read a mono, 16-bit, 44.1 kHz WAV file and get the PCM data as an array of doubles where -1.0 <= y <= +1.0, just like MATLAB's wavread function.

Maybe there's a more direct way to do it, but so far I tried this:

WaveStream readerStream = CreateInputStream("idiot.wav");
Wave32To16Stream pcm = new Wave32To16Stream(readerStream);
int bitsPerSample = pcm.WaveFormat.BitsPerSample; // 16. good.
int sampleRate = pcm.WaveFormat.SampleRate; // 44100. good.
string encoding = pcm.WaveFormat.Encoding.ToString(); // Pcm. good.
int channels = pcm.WaveFormat.Channels; // 2.  why not 1 ???

(One odd thing is I noticed is that "channels" equals 2 even though the input file is mono.  Not sure why.)

So anyway, do I now have to read two bytes at a time to get the 16-bit samples?  Or is there some easier way to get my array of doubles?

Thank you!

 

Coordinator
Nov 16, 2009 at 9:22 AM

I would guess that CreateInputStream is converting to stereo 32 bit audio.

Also, if you want doubles, I would skip using the Wave32To16Stream, as 32 bit floating point is easy to convert to doubles.

Have a look at the WaveBuffer class which has some clever tricks allowing you to convert from arrays of byte[] to arrays of short[] or float[] (depending on your need)

Mark

Nov 16, 2009 at 10:47 AM

thanks Mark, yeah my CreateInputStream method was indeed converting to stereo.  fixed.

So I think I have found a solution that works (although I haven't actually verified the data yet).
Anyway, let me know if you see any glaring problems with doing it this way:

WaveFileReader readerStream = new WaveFileReader("idiot.wav");
double[] samples = new double[readerStream.Length / 2]; // 16-bits per sample
double sample = 0.0;
int i = 0;
while (readerStream.TryReadDouble(ref sample) && i < readerStream.Length / 2)
{
    samples[i++] = sample;
}

mucho thanks!

 

Coordinator
Nov 16, 2009 at 1:47 PM

Should work, although you need to be aware that TryReadDouble probably won't remain much longer (trying to keep the main interface clean). TryReadFloat will remain, and you can simply cast its return value to a double (or create your own derived WaveFileReader that includes the old TryReadDouble method).

Mark

Nov 19, 2009 at 3:42 PM
Edited Nov 19, 2009 at 3:44 PM

Hi,

I want to make something similar except I use the WaveIn class. It doesn't support something like "TryReadDouble" so that I had to look for that method and write it for my own. This is what I got:

 

void waveInStream_DataAvailable(object sender, WaveInEventArgs e)
{
   double[] dBuffer = new double[e.BytesRecorded/2];
   byte[] buffer = new byte[2];
            
   for (int i = 0; i < e.BytesRecorded; i+=2)
   {
      buffer[0] = e.Buffer[i];
      buffer[1] = e.Buffer[i + 1];

      dBuffer[i / 2] = BitConverter.ToInt16(buffer, 0) / 32768.0;              
   }

   // do something with the dBuffer...
}

 

Not very nice at all because only 16 bits sampling is supported but it's a beginning and it works fine.

 

The only question: Where the hell does the number "32768.0" come from?

You wrote in the source code of NAudio: // for the benefit of oggencoder we divide by 32768.0f;

How does it work?

Nov 19, 2009 at 4:50 PM

In a 16-bit system, you've got a total of 65536 possible values.  If you put half of those below zero, use one for zero itself, and the remaining above zero, you get a range of -32768 <= y <= 32767.  Dividing by 32768 converts the range of sample values to [-1.00000 <= y <~ +0.99997].  My assumption is that the OggEncoder is looking for the -1 to 1 range.