/// <summary>
 /// Stop playback without flushing buffers
 /// </summary>
 public void Pause()
 {
     if (PlaybackState == EPlaybackState.Playing)
     {
         PlaybackState = EPlaybackState.Paused;
     }
 }
 /// <summary>
 /// Stop playback and flush buffers
 /// </summary>
 public void Stop()
 {
     if (PlaybackState != EPlaybackState.Stopped)
     {
         PlaybackState = EPlaybackState.Stopped;
         Debug.WriteLine("[render]Task ending...");
         playTask?.Wait();
         playTask = null;
         Debug.WriteLine("[render]Task Done.");
     }
 }
        /*
         * private void PlayThread()
         * {
         *  IWaveProvider playbackProvider = this.sourceProvider;
         *  Exception     exception        = null;
         *  try
         *  {
         *      // fill a whole buffer
         *      var bufferFrameCount = audioClient.BufferSize;
         *      //var bytesPerFrame    = outputFormat.Channels * outputFormat.BitsPerSample / 8;
         *
         *      readBuffer       = new byte[bufferFrameCount * outputFormat.BlockAlign];
         *      FillBuffer(playbackProvider, bufferFrameCount);
         *
         *      // Create WaitHandle for sync
         *      //WaitHandle[] waitHandles = new WaitHandle[] { frameEventWaitHandle };
         *
         *      audioClient.Start();
         *
         *      while (PlaybackState != EPlaybackState.Stopped)
         *      {
         *          // If using Event Sync, Wait for notification from AudioClient or Sleep half latency
         *          int indexHandle = 0;
         *          if (isUsingEventSync)
         *          {
         *              //indexHandle = WaitHandle.WaitAny(waitHandles, 3 * latencyMilliseconds, false);
         *              frameEventWaitHandle.WaitOne(3 * latencyMilliseconds);
         *          }
         *          else
         *          {
         *              Thread.Sleep(latencyMilliseconds / 2);
         *          }
         *
         *          // If still playing and notification is ok
         *          if (PlaybackState == EPlaybackState.Playing && indexHandle != WaitHandle.WaitTimeout)
         *          {
         *              // See how much buffer space is available.
         *              int numFramesPadding = 0;
         *              if (isUsingEventSync)
         *              {
         *                  // In exclusive mode, always ask the max = bufferFrameCount = audioClient.BufferSize
         *                  numFramesPadding = (shareMode == EAudioClientShareMode.Shared) ? audioClient.CurrentPadding : 0;
         *              }
         *              else
         *              {
         *                  numFramesPadding = audioClient.CurrentPadding;
         *              }
         *              int numFramesAvailable = bufferFrameCount - numFramesPadding;
         *              if (numFramesAvailable > 0)
         *              {
         *                  FillBuffer(playbackProvider, numFramesAvailable);
         *              }
         *          }
         *      }
         *      Thread.Sleep(latencyMilliseconds / 2);
         *      audioClient.Stop();
         *      if (PlaybackState == EPlaybackState.Stopped)
         *      {
         *          audioClient.Reset();
         *      }
         *  }
         *  catch (Exception e)
         *  {
         *      exception = e;
         *  }
         *  finally
         *  {
         *      //RaisePlaybackStopped(exception);
         *  }
         * }
         */

        /*
         * private void RaisePlaybackStopped(Exception e)
         * {
         *  var handler = PlaybackStopped;
         *  if (handler != null)
         *  {
         *      if (this.syncContext == null)
         *      {
         *          handler(this, new StoppedEventArgs(e));
         *      }
         *      else
         *      {
         *          syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
         *      }
         *  }
         * }
         */

        private void FillBuffer(IWaveProvider playbackProvider, int frameCount)
        {
            IntPtr buffer     = renderClient.GetBuffer(frameCount);
            int    readLength = frameCount * outputFormat.BlockAlign;
            int    read       = playbackProvider.Read(readBuffer, 0, readLength);

            if (read == 0)
            {
                PlaybackState = EPlaybackState.Stopped;
            }
            Marshal.Copy(readBuffer, 0, buffer, read);
            int actualFrameCount = read / outputFormat.BlockAlign;

            /*if (actualFrameCount != frameCount)
             * {
             *  Debug.WriteLine(String.Format("WASAPI wanted {0} frames, supplied {1}", frameCount, actualFrameCount ));
             * }*/
            renderClient.ReleaseBuffer(actualFrameCount, EAudioClientBufferFlags.None);
        }
        /// <summary>
        /// Begin Playback
        /// </summary>
        public void Play()
        {
            if (PlaybackState == EPlaybackState.Playing)
            {
                return;
            }

            if (PlaybackState == EPlaybackState.Paused)
            {
                PlaybackState = EPlaybackState.Playing;
                return;
            }

            Debug.WriteLine("[render]Task starting...");
            playTask = Task.Run(() => {
                IWaveProvider playbackProvider = this.sourceProvider;
                AudioClient client             = this.audioClient;
                Exception exception            = null;

                PlaybackState = EPlaybackState.Playing;

                try
                {
                    // fill a whole buffer
                    var bufferFrameCount = client.BufferSize;
                    var bytesPerFrame    = outputFormat.Channels * outputFormat.BitsPerSample / 8;

                    readBuffer = new byte[bufferFrameCount * bytesPerFrame];
                    //FillBuffer(playbackProvider, bufferFrameCount);

                    client.Start();

                    while (PlaybackState != EPlaybackState.Stopped)
                    {
                        // If using Event Sync, Wait for notification from AudioClient or Sleep half latency
                        if (isUsingEventSync)
                        {
                            //indexHandle = WaitHandle.WaitAny(waitHandles, 3 * latencyMilliseconds, false);
                            frameEventWaitHandle.WaitOne(3 * latencyMilliseconds);
                        }
                        else
                        {
                            Task.Delay(latencyMilliseconds / 2);
                        }

                        // If still playing and notification is ok
                        if (PlaybackState != EPlaybackState.Playing)
                        {
                            continue;
                        }

                        // See how much buffer space is available.
                        int numFramesPadding = 0;
                        if (isUsingEventSync)
                        {
                            // In exclusive mode, always ask the max = bufferFrameCount = audioClient.BufferSize
                            numFramesPadding = (shareMode == EAudioClientShareMode.Shared) ? client.CurrentPadding : 0;
                        }
                        else
                        {
                            numFramesPadding = client.CurrentPadding;
                        }
                        int numFramesAvailable = bufferFrameCount - numFramesPadding;
                        if (numFramesAvailable > 0)
                        {
                            FillBuffer(playbackProvider, numFramesAvailable);
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine("[render]Task catch Exception.");
                    Debug.WriteLine(e.Message);
                    Debug.WriteLine(e.Source);
                    Debug.WriteLine(e.StackTrace);
                    exception = e;
                }
                finally
                {
                    client.Stop();
                    client.Reset();
                    Debug.WriteLine("[render]Task stop detected.");
                    RaisePlaybackStopped(exception);
                }
            });
            Debug.WriteLine("[render]Task started");
        }