Naudio,how to tell playback is completed
Asked Answered
C

4

6

I am using the NAudio library to write a simple WinForms audio recorder/player. My problem is how can I tell that playback is completed? I need to close the wave stream after that.

I knew there is a PlaybackStopped event listed below:

wfr = new NAudio.Wave.WaveFileReader(this.outputFilename);
audioOutput = new DirectSoundOut();
WaveChannel32 wc = new NAudio.Wave.WaveChannel32(wfr); 
audioOutput.Init(wc);
audioOutput.PlaybackStopped += new EventHandler<StoppedEventArgs>(audioOutput_PlaybackStopped);
audioOutput.Play();

But this PlaybackStopped event seems can only be triggered by calling audioOutput.stop(), does anyone know how to determine if playback is completed?

I create an open source project for this question, you can find it here: https://code.google.com/p/stack-overflow-questions/

Camelot answered 30/6, 2012 at 9:2 Comment(3)
Are you sure that the PlaybackStopped is not fired after the playback completed? Because from the source that the end of processSamples() in the finally it fires the event...Maimonides
Yes, I am sure. I tested in a new project. Still it produces the same problem. Is it a bug?Camelot
Same if using WaveOut as audio output.Camelot
C
10

The PlaybackStopped event is raised when you either manually stop playing, or the Read method of the IWaveProvider you are using returns 0. The issue here is that WaveChannel32 does not stop returning data when it's source stream ends, so playback never ends. The PadWithZeroes property should be set to false to fix this.

Corpuz answered 1/7, 2012 at 7:52 Comment(1)
What an obscure property. Never would have found this on my own, thank you. Just to add slight clarity 8 years later, the PaddWithZeroes property is in the WaveChannel32, object. This may have been obvious to some, but perhaps not.Audacious
V
1

As @Mark Heath described in addition I want to add coding example of Naudio wich will play a mp3 file in Debug/Sound Folder folder and wait until it isn't finished. Playback is completed can be checked by waveOut.PlaybackState == PlaybackState.Stopped

play_string = @"SOUND/space.mp3";
var reader = new Mp3FileReader(play_string);
var waveOut = new WaveOut(); // or WaveOutEvent()
waveOut.Init(reader);
waveOut.Play();
while (waveOut.PlaybackState != PlaybackState.Stopped) ; // Wait untill the playing isn't finished. 
Verina answered 9/5, 2020 at 11:44 Comment(2)
is this valid @MuhammadAshikuzzaman ? no body give a rate either + or - .Liva
A while loop without Thread.Sleep or Task.Delay in the body is a bad idea. It is a so-called busy wait which makes your program use 100% processor time.Gin
K
0

Here is my code in Visual Basic that works well for me to determine when the playback is done. It uses a loop to keep testing WaveOut.PlaybackState

In the loop, you will notice that a short thread sleep of 50 is used to stop the cpu running away. I picked 50 from experience, you may find another value works better for you through trial and error.

And finally, the loop allows windows.forms events so the user can click the Stop button, with the statement:

System.Windows.Forms.Application.DoEvents

In the code I have supplied below, you can see events being raised - for example:

 RaiseEvent Enable_PlayButton

I have defined events to allow a parent class to enable the Play, Stop, and Record buttons:

  Public Event Enable_PlayButton(Enabled As Boolean)
  Public Event Enable_StopButton(Enabled As Boolean)
  Public Event Enable_RecordButton(Enabled As Boolean)
  Public Event Enable_SaveButton(Enabled As Boolean)
  Public Event Enable_RevertButton(Enabled As Boolean)

  Public Event RecordingChanged(NewRecording As Byte())

In a parent class, I use AddHandler to wire these up to method in the parent class. In the following example, I have methods such as in the first case, "EnablePlay". Likewise for the other events.

    AddHandler mMicrophoneRecorder.Enable_PlayButton, AddressOf EnablePlay
    AddHandler mMicrophoneRecorder.Enable_StopButton, AddressOf EnableStop
    AddHandler mMicrophoneRecorder.Enable_RecordButton, AddressOf EnableRecord
    AddHandler mMicrophoneRecorder.Enable_RevertButton, AddressOf EnableRevert
    AddHandler mMicrophoneRecorder.RecordingChanged, AddressOf MicRecorder_RecordingChanged

In this method, there are 3 fields that are defined elsewhere:

mWaveOut_via_SoundCard - a class-level (field) of WaveOut.

mAudioFile - instantiated here but held at the class level (a field).

mWavRecordingPath - the path to the audio (wav) file being played.

These 3 fields could probably be passed in to this method as parameters if you prefer to inject them. I used fields, because that's just the way my code for this evolved.

Here then, is the code:


Public Sub A_PlayClicked()

  If mWaveOut_via_SoundCard Is Nothing Then
    mWaveOut_via_SoundCard = New WaveOutEvent()
  End If

  If mAudioFile Is Nothing AndAlso mWavRecordingPath <> "" Then
    mAudioFile = New AudioFileReader(mWavRecordingPath)
    mWaveOut_via_SoundCard.Init(mAudioFile)
  End If

  RaiseEvent Enable_StopButton(True)

  mWaveOut_via_SoundCard.Play()

  Dim PlayDone As Boolean = False
  Dim PState As PlaybackState
  
  Do

    'if stopped or finished, mSoundCard will be nothing,
    'so we test that by trying to get PlayBackState 
    '   from the WaveOut object (mSoundCard)

    If mWaveOut_via_SoundCard Is Nothing Then

      PlayDone = True

    Else

      Try

        PState = mWaveOut_via_SoundCard.PlaybackState

      Catch ex As Exception

        'mSoundCard is probably nothing - but 
        'no matter what the problem, for now we will say
        'that PlayDone is true.

        PlayDone = True

      End Try

    End If

    'Okay we got the PlayState, so evaluate and
    'decide whether to continue here:

    If Not PlayDone Then

      If PState <> PlaybackState.Stopped Then

        'let the system do stuff (e.g. user might click Stop button)

        System.Windows.Forms.Application.DoEvents()

        'don't use all the cpu:

        Threading.Thread.Sleep(50)

      Else

        'well it's stopped so we're done:

        PlayDone = True

      End If

    End If

  Loop Until PlayDone = True

  'here we could raiseevent stopped as well

  RaiseEvent Enable_PlayButton(mCanPlay)
  RaiseEvent Enable_RecordButton(True)

End Sub
Kronstadt answered 11/11, 2022 at 17:12 Comment(1)
But this question was asked for C#.Menhir
X
0

Like the NAudio docs say "subscribe to PlaybackStopped to determine when playback has stopped. And you should call Dispose when you are finished with it."

 private void PlaySound(MMDevice endpoint)
    {
        IWavePlayer waveOut = new WasapiOut(endpoint, AudioClientShareMode.Shared, false, 0);
        waveOut.PlaybackStopped += WaveOut_PlaybackStopped;

        ISampleProvider beep = new SignalGenerator() {
            Frequency = 500,
            Gain = 0.2
        }.Take(TimeSpan.FromSeconds(5));
        waveOut.Init(beep);
        waveOut.Volume = (float)settings.Volume;
        waveOut.Play();
        while (waveOut.PlaybackState == PlaybackState.Playing)
        {
            Thread.Sleep(500);
        }
    }

    private void WaveOut_PlaybackStopped(object sender, StoppedEventArgs e)
    {
        IWavePlayer wp = sender as IWavePlayer;
        wp.Dispose();
    }
Ximenez answered 11/4 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.