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

Null Reference Exception at WaveIn Callback

Jul 26, 2011 at 2:38 PM

Hello,

I'm working up a small example similar to what is done in the .NET Voice Recorder.  Just looking to record audio to a wav file at this point.  The program is structured very similar to the voice recorder.  Right now, I launch the application, begin monitoring (capturing samples but not writing to disk), begin recording after a user button press, then stop recording and close the file after a stop button press.  This works well, but after clicking stop I can't record again without relaunching.  I would like to go back into 'monitoring' mode after writing the wav file.  After the stop button press, the recording state goes to stop requested, the last of the samples are captured, then an event fires to set the recording state to stopped.  I need to call StopRecording() on the WaveIn, or else that event will never fire and I'll be stuck in the Requested Stop state.  When I try to BeginMonitoring() again, a NullReference occurs at the WaveIn callback.  Some code:

//Recorder.Stop()
public void Stop()
{
     if(RecordingState == RecordingState.Recording)
     {
          RecordingState = RecordingState.RequestedStop;
          waveIn.StopRecording();
     }
}

//which eventually fires:
void waveIn_RecordingStopped(object sender, EventArgs e)
{
     RecordingState = RecordingState.Stopped;
     waveWriter.Dispose() //recreated on record button press
     BeginMonitoring(0);
}

public void BeginMonitoring(int device)
{
     if(RecordingState != RecordingState.Stopped)throw new InvalidOperation...();
     waveIn.StartRecording();
     RecordingState = RecordingState.Monitoring;
}


The recording state does change to monitoring, and then the exception is thrown.

Stack trace:

   at NAudio.Wave.WaveIn.Callback(IntPtr waveInHandle, WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
   at NAudio.Wave.WaveWindow.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)

 

One option would be to move the WaveIn creation and initialization down into BeginMonitoring() so that  new object is created after each successful recording, but I think it should be possible to keep one WaveIn object open.  Is there some part of WaveIn that doesn't persist between StartRecording() and StopRecording() calls?

Coordinator
Jul 28, 2011 at 12:36 PM

Possibly this is due to starting too soon after stopping. I've seen issues before with WaveOut where the callback messages for the old playback get muddled with the ones for the next. What callback model are you using? Is it NewWindow?

Jul 28, 2011 at 12:52 PM

Yes, NewWindow.  I tried sticking a Thread.Sleep() in the RecordingStopped handler before the call to BeginMonitoring() ...as soon as it tries to start monitoring again I get the exception, so it seems like something elsewhere is getting disposed of after waveIn.StoppedRecording().  Do you think a different callback model would work better?  I went with NewWindow hoping to avoid managing locking issues between threads.

Coordinator
Jul 28, 2011 at 12:53 PM

are you able to debug into NAudio source code and see where the null reference is?

Jul 28, 2011 at 1:11 PM

I don't have the NAudio source, only the .dll.  I'm pretty new to .NET, how should I go about getting into the source?  Would I just add the NAudio project to the solution and replace the reference to the .dll with one to the project?  Thanks.

Coordinator
Jul 28, 2011 at 1:12 PM

latest code can be got by clicking Source Code tab above, and clicking Download

http://naudio.codeplex.com/SourceControl/list/changesets

It requires VS2010 to build

Mark

Jul 28, 2011 at 6:28 PM

Mark, I was able to dig down into the source.  The error is in WaveIn.c.  Does this help?

private void Callback(IntPtr waveInhandle, WaveInterop.WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
{
     if(message==WaveInterop.WaveMessage.WaveInData)
     {
          GCHandle hBuffer = (GCHandle)waveHeader.userData;
          WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;
          //the problem occurs here ^ ...buffer comes back as null
          //so trying to execute the following fails
          if(DataAvailable != null)
          {
               DavaAvailable(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded)); 
          }
     }
          ....

}


Coordinator
Jul 28, 2011 at 8:08 PM

hmm, a strange one. I guess a null check could be put in to avoid the crash, but that would be fixing the symptoms rather than the cause. I'll put it on my list of bugs to try to recreate, but with some holidays coming up I'm afraid it might be a while before I get round to it.

Mark

Jul 28, 2011 at 9:15 PM

If you'd like, I can zip up my project as it is right now and shoot you an email..at least you'll be able to look into it when you have the time.  In the meantime, would simply returning from Callback() if buffer is null have any adverse effects?

Coordinator
Jul 28, 2011 at 9:20 PM

have you tried simply making a new WaveIn in your StartMonitoring function? Would be worth a try

Oct 31, 2011 at 10:55 AM

Hello,

I've had exactly the same problem like zmb99. Debugging took me to the same Line. If it's too hard to find out the cause, it's probably a good idea to fix the symtoms ;)   With the new line I'm able to call waveInStream.StartRecording and waveInStream.StopRecording as often as I like, and everything works fine.

 

        /// <summary>
        /// Called when we get a new buffer of recorded data
        /// </summary>
        private void Callback(IntPtr waveInHandle, WaveInterop.WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
        {
            if (message == WaveInterop.WaveMessage.WaveInData)
            {
                GCHandle hBuffer = (GCHandle)waveHeader.userData;
                WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;

                if (buffer == null)  return; // with this new line, everything works fine

                if (DataAvailable != null)
                {
                    DataAvailable(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded));
                }
                if (recording)
                {...