This project has moved and is read-only. For the latest updates, please go here.

WaveOutEvent buffers is null

Jul 24, 2012 at 10:57 PM
Edited Jul 24, 2012 at 11:06 PM

Every once in a while when I close my application while using WaveOutEvent i get a NullReferenceException from the DoPlayback() function.  It turns out this.buffers is null.

Screenshot from VS2010, when Exception happens
screenshot


A bandaid to fix the problem I now check for the null value in DoPlayback();

private void DoPlayback()
{
            if (this.buffers == null || this.waveStream == null)
                return;

            TimeSpan waitTime = TimeSpan.FromSeconds((double)this.buffers[0].BufferSize / (this.waveStream.WaveFormat.AverageBytesPerSecond * 2));

.........

}


I dont get a good stack trace back for some reason, I assume the call to DoPlayback()  is still in the ThreadPool  after I dispose of WaveOutEvent?

ThreadPool.QueueUserWorkItem((state) => PlaybackThread(), null);

Maybe the cure (not bandaid) would be to "un-queue" the thread form the pool on disposal?  not sure if that is possible or makes any sense...

I hope this helps, or maybe I am just doing something wrong...

Thanks,
Brandon Hansen, KG6YPI

ps.. I noticed my callsign (kg6ypi) got into the readme file,  you can attach my name to it also if you wish, thanks.

Jul 30, 2012 at 2:55 PM

hi, thanks. It is a bit odd - almost as though you are starting to play at the exact instant you shut down? But I will add your fix and update the readme

Mark

Aug 23, 2012 at 4:58 AM
Edited Aug 23, 2012 at 6:57 AM

Thanks for adding this into the code, its working great! Now I have changed some of my Dispose methods in my forms to clean up WaveOutEvent/WaveInEvent objects by disposing of them properly. This ends up in a new exception.

I get an WaveHeaderUnprepared calling waveOutWrite.  See below.

NAudio

I was able to fix this by editing WriteToWaveOut() method in WaveOutBuffer.cs to check and see if hWaveOut handle has been set to IntPtr.Zero. See below..

private void WriteToWaveOut()
        {
            MmResult result;

            lock (waveOutLock)
            {
                if (hWaveOut == IntPtr.Zero)
                    return;

                result = WaveInterop.waveOutWrite(hWaveOut, header, Marshal.SizeOf(header));
            }
            if (result != MmResult.NoError)
            {
                throw new MmException(result, "waveOutWrite");
            }

            GC.KeepAlive(this);
        }

It seems as if this lock is waiting for the Dispose method to Unprepare the headers, then continuing. So this null or IntPtr.Zero check, inside the lock, solved the issue. Dispose locks and unprepares headers, see below..

protected void Dispose(bool disposing)
        {
            ...
                lock (waveOutLock)
                {
                    WaveInterop.waveOutUnprepareHeader(hWaveOut, header, Marshal.SizeOf(header));
                }
                hWaveOut = IntPtr.Zero;
            }
        }

I assume that's where "hWaveOut" is put into the state to form the Exception.

Thanks for all your work!

Brandon

Sep 5, 2012 at 5:21 PM

this exception should no longer be unhandled in the latest code as we catch any exceptions thrown in buffer.OnDone