Skylar Kennedy
Skylar Kennedy

Reputation: 367

NAudio is not reading MemoryStream containing wav data

I'm trying to use NAudio to record 8 seconds of streaming audio, store it in System.IO.MemoryStream, and then play it back right after. To do this, I'm running the following code:

using System.IO;
using NAudio.Wave;

public class AudioCapture{

    static WaveFileWriter wri;
    static bool stopped = false;

    public AudioCapture(){
        //record from loopback
        IWaveIn waveIn = new WasapiLoopbackCapture();
        waveIn.DataAvailable += waveIn_DataAvailable;
        waveIn.RecordingStopped += waveIn_RecordingStopped;

        //write loopback capture to MemoryStream
        Stream sos = new MemoryStream();
        wri = new WaveFileWriter(sos, waveIn.WaveFormat);

        //record 8 seconds of streaming audio
        waveIn.StartRecording();
        System.Threading.Thread.Sleep(8000);
        waveIn.StopRecording();

        //flush and dispose objects
        wri.Flush();
        waveIn.Dispose();
        wri.Dispose();

        //play back recorded audio
        WaveOut waveOut = new WaveOut();
        WaveFileReader waveFileReader = new WaveFileReader(sos);
        waveOut.Init(waveFileReader);
        waveOut.Play();

    }

    static void waveIn_RecordingStopped(object sender, StoppedEventArgs e){
        stopped = true;
    }

    static void waveIn_DataAvailable(object sender, WaveInEventArgs e){
        if (wri != null) wri.Write(e.Buffer, 0, e.BytesRecorded);
    }

}

I get the following exception telling me that the stream could not be read:

Activated   Event   Time    Duration    Thread
    Exception: Exception thrown: 'System.ArgumentException' in NAudio.dll 
("Stream was not readable."). Exception thrown: 'System.ArgumentException' in 
NAudio.dll ("Stream was not readable.") 17.86s      [17404] <No Name> 

I'm not quite sure what the issue is as WaveFileWriter clearly accepts MemoryStream according to the documentation. Is there some conversion that I'm missing? Any help would be appreciated. Thanks!

For clarification: The error seems to be at this line:

WaveFileReader waveFileReader = new WaveFileReader(sos);

Upvotes: 1

Views: 3381

Answers (2)

Mark Heath
Mark Heath

Reputation: 49482

There is no need to use WaveFileReader or WaveFileWriter in this scenario. Just write the audio received to the MemoryStream as it arrives:

static void waveIn_DataAvailable(object sender, WaveInEventArgs e){
    if (sos != null) sos.Write(e.Buffer, 0, e.BytesRecorded);
}

And then when you've finished and are ready to play, rewind the MemoryStream and play using a RawSourceWaveStream:

//play back recorded audio
var waveOut = new WaveOut();
sos.Position = 0;
var waveFileReader = new RawSourceWaveStream(sos, waveIn.WaveFormat);
waveOut.Init(waveFileReader);
waveOut.Play();

Upvotes: 1

Evk
Evk

Reputation: 101443

WaveFileWriter is (unfortunately) designed in a way that it always disposes stream it writes to when you dispose WaveFileWriter. It could have accepted some flag to control this behavior, but it does not. So if you need to use that stream later you need to either not dispose WaveFileWriter too early, or clone that closed memory stream. If you choose to not dispose writer - you need to rewind memory stream back to position 0, so that reading can start from the beginning (after writing position is at the end):

wri.Flush();
waveIn.Dispose();
// wri.Dispose(); < too early
sos.Seek(0, SeekOrigin.Begin);

If you want to use a clone - rewind is not needed:

wri.Flush();
waveIn.Dispose();
wri.Dispose();
sos = new MemoryStream(sos.ToArray());

Drawback of cloning is that it creates unnecessary copy of the whole byte array (ToArray call).

Upvotes: 3

Related Questions