2
votes

I'm using WaveInEvent of NAudio to record microphone data. It works fine for a while, but after a few times, it stops providing input data- the DataAvailable callback is never called with new data.

I have tried creating a new WaveInEvent each time, but this did not resolve the problem. I've also tried using the WASAPI input, which always called DataAvailable - with zero bytes of data.

How can I record audio from the microphone reliably with NAudio?

Currently, my code looks like this:

StartRecording() {
    microphone = new WaveInEvent();
    microphone.DeviceNumber = 0;
    microphone.WaveFormat = outformat;
    microphone.BufferMilliseconds = 50;
    microphone.DataAvailable += (_, recArgs) =>
    {
        session.OnAudioData(recArgs.Buffer, recArgs.BytesRecorded);
    };
    microphone.StartRecording();
}
StopRecording() {
    if (microphone != null)
    {
        microphone.StopRecording();
        microphone.Dispose();
        microphone = null;
    }        
}

There's no other naudio code in the project except using WaveFormat to describe wave formats.

NAudio throws an access violation exception trying to call WaveInBuffer.Reuse() from a threadpool worker. I'm not sure why this doesn't do something more serious than just drop audio data.

For the condition where I did not recreate the WaveInEvent, I get an MmException instead- invalid handle calling waveInPrepareHeader, in the same place.

Frankly, the fact that I get different results heavily implies that NAudio is doing some funky shit it shouldn't to share state between instances, and looking at the source on Codeplex, I'm not really sure WTF is going on.

1
Can you add some code?VMAtm
Are you getting an exception in the RecordingStopped event? Also, is this a USB capture device? If it is Realtek onboard sound, have you updated drivers?Mark Heath
@MarkHeath: I do get the exception in RecordingStopped and it is a USB capture device.Puppy
I've seen access violations NAudio before when something supposed to be pinned isn't and the GC moves it. But I thought we'd flushed out all such issues a long time ago. Can also be dodgy driver issues, and of course USB devices can become disconnected while in use. Does the stack trace of the exception point to which waveIn API call was generating the access violation?Mark Heath
@MarkHeath: It's StartRecording's lambda -> RecordThread() -> DoRecording() -> WaveInBuffer.Reuse(). If you don't want the GC to move a memory buffer, maybe it would be easier to simply use a native allocator like heapalloc/heapfree to allocate it? The USB device did not become disconnected. It is a standalone microphone with a record button, and it's in response to that button press that the above functions are being called.Puppy

1 Answers

1
votes

It seems that the drivers for the USB microphone do not behave correctly. When the buffer is sent to the user through the WIM_DATA message, it is full. However when waveInUnprepareHeader is called, it's still in the queue, even though it was literally just passed as full. So I think that the drivers for the microphone are ultimately to blame.

I've been looking more closely at the microphone and it seems that this particular unit is actually known to have been damaged.