Play same file multiple times using WaveOutEvent

Feb 22, 2013 at 10:26 AM
Edited Feb 22, 2013 at 10:30 AM
Hello all,

I am using WaveOutEvent and AudioFileReader class to play my audio files. I wanted to provide a functionality where user can play a given file for specified duration. Here there is a scenario where audio file duration could be less than the duration specified by user. So I want to replay the same file again and again until user specified duration completes. I tried following ways:
  1. Create WaveOutEvent instance, initialize it, play file, on PlayingStopped dispose this instance, create new WaveOutEvent instance, initialize it, play file, ... until duration completes. [But continuously creating and disposing WaveOutEvent instance throws AccessViolationException after some time]
  2. Create WaveOutEvent instance, initialize it, play file, on PlayingStopped initalize it again, play file, ... until duration completes, and then dispose WaveOutEvent. [But this way is raising MMException with AlreadyAllocated result]
Is there any other way to do so?
If we set the Position of AudioFileReader to zero and then call WaveOutEvent.Play then its not working! Why so?

Thanks.
Coordinator
Feb 25, 2013 at 4:20 PM
if you need to keep replaying the same file, I'd open the output device only once, and reposition the input file to the begining. Don't call initialize twice on the same instance of WaveOutEvent though - this isn;t a supported workflow in NAudio at the moment. I don't know why repositioning to the start should not work for you. You can test by creating an AudioFileReader, reading some data with Read, then setting Position =0, and do another Read. It should return >0 bytes.
Feb 26, 2013 at 7:37 AM
Edited Feb 26, 2013 at 7:39 AM
Hello sir,
I tried using AudioFileReader in the way that you suggested and it is working as you said. That means re-positioning to the start should work for me.

So I was trying to find out what is going wrong and I found that when you call Play for the second time, the PlaybackThread waits infinitely at callbackEvent.WaitOne() call. Now this might be because when 1st PlaybackThread completes reading all the buffers and exits, callbackEvent must be remaining in non signaled state. Is it the case? and if yes then Should we call callbackEvent.Set() before exiting from the PlaybackThread?

I have one more doubt, how come callbackEvent.WaitOne() is returning immediately for the first time? (I have this doubt because you have initialized callbackEvent with initial state as non signaled!)
Coordinator
Feb 26, 2013 at 3:00 PM
can you try the fix suggested here, and see if that resolves it for you?
http://naudio.codeplex.com/discussions/357995#post844154

It's something on my TODO list for NAudio 1.7, but I've not got round to putting it in yet.

Mark
Feb 27, 2013 at 5:11 AM
I will try the suggested fix but could you please help me to clear my previous doubt (last para in my previous post).
Feb 27, 2013 at 5:21 AM
Edited Feb 27, 2013 at 5:31 AM
I tried the suggested fix, its working fine but we will force callbackEvent to go in signaled state each time when a new PlaybackThread is started which is not necessary if Stop was called previously (because stop is already calling callbackEvent.Set()).

What if we call it as follows:
    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));
        while (playbackState != PlaybackState.Stopped)
        {
            if (callbackEvent.WaitOne())
            {
                // requeue any buffers returned to us
                if (playbackState == PlaybackState.Playing)
                {
                    int queued = 0;
                    foreach (var buffer in buffers)
                    {
                        if (buffer.InQueue || buffer.OnDone())
                        {
                            queued++;
                        }
                    }
                    if (queued == 0)
                    {
                        // we got to the end
                        this.playbackState = PlaybackState.Stopped;

                        callbackEvent.Set();          <<<<<<< calling Set here
                    }
                }
            }
        }
    }
Coordinator
Feb 27, 2013 at 11:59 AM
sounds like a good suggestion. will try to get something like this into the next NAudio
Coordinator
Feb 5 at 11:16 AM
sorry it's taken a while, but this is now in the latest NAudio sourcecode