Reputation: 367
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
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
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