private void ProcessWave()
        {
            const int bufferSize = 1024 * 10;

            byte[] inputBuffer         = new byte[bufferSize * sizeof(float)];
            byte[] soundTouchOutBuffer = new byte[bufferSize * sizeof(float)];

            ByteAndFloatsConverter convertInputBuffer = new ByteAndFloatsConverter {
                Bytes = inputBuffer
            };
            ByteAndFloatsConverter convertOutputBuffer = new ByteAndFloatsConverter {
                Bytes = soundTouchOutBuffer
            };

            byte[] buffer = new byte[bufferSize];
            _stopWorker = false;
            while (!_stopWorker && _waveChannel.Position < _waveChannel.Length)
            {
                int bytesRead = _waveChannel.Read(convertInputBuffer.Bytes, 0, convertInputBuffer.Bytes.Length);
                //bytesRead = _waveChannel.Read(buffer, 0, BUFFER_SIZE);
                //bytesRead = _reader.Read(convertInputBuffer.Bytes, 0, convertInputBuffer.Bytes.Length);


                int floatsRead = bytesRead / ((sizeof(float)) * _waveChannel.WaveFormat.Channels);
                _soundTouch.PutSamples(convertInputBuffer.Floats, (uint)floatsRead);

                uint receivecount;

                do
                {
                    if (WaitingMode)
                    {
                        SetSoundTouchValues();
                    }

                    uint outBufferSizeFloats = (uint)convertOutputBuffer.Bytes.Length /
                                               (uint)(sizeof(float) * _waveChannel.WaveFormat.Channels);

                    receivecount = _soundTouch.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);

                    if (receivecount > 0)
                    {
                        _provider.AddSamples(convertOutputBuffer.Bytes, 0,
                                             (int)receivecount * sizeof(float) * _reader.WaveFormat.Channels, _reader.CurrentTime);

                        while (_provider.BuffersCount > 3)
                        {
                            Thread.Sleep(10);
                        }
                    }
                } while (!_stopWorker && receivecount != 0);
            }

            _reader.Close();
        }
        private void ProcessWave()
        {
            //MsToBytes(Latency);
            byte[] inputBuffer         = new byte[BUFFER_SIZE * sizeof(float)];
            byte[] soundTouchOutBuffer = new byte[BUFFER_SIZE * sizeof(float)];

            ByteAndFloatsConverter convertInputBuffer = new ByteAndFloatsConverter {
                Bytes = inputBuffer
            };
            ByteAndFloatsConverter convertOutputBuffer = new ByteAndFloatsConverter {
                Bytes = soundTouchOutBuffer
            };

            byte[] buffer    = new byte[BUFFER_SIZE];
            bool   finished  = false;
            int    bytesRead = 0;

            stopWorker = false;
            while (!stopWorker && waveChannel.Position < waveChannel.Length)
            {
                //bytesRead = waveChannel.Read(buffer, 0, BUFFER_SIZE);
                bytesRead = waveChannel.Read(convertInputBuffer.Bytes, 0, convertInputBuffer.Bytes.Length);
                //bytesRead = reader.Read(convertInputBuffer.Bytes, 0, convertInputBuffer.Bytes.Length);

                SetSoundSharpValues();

                int floatsRead = bytesRead / ((sizeof(float)) * waveChannel.WaveFormat.Channels);
                soundTouch.PutSamples(convertInputBuffer.Floats, (uint)floatsRead);

                uint receivecount;

                do
                {// 榨干SoundTouch里面的数据
                    uint outBufferSizeFloats = (uint)convertOutputBuffer.Bytes.Length / (uint)(sizeof(float) * waveChannel.WaveFormat.Channels);

                    receivecount = soundTouch.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);

                    #region Test: write buffers into test.mp3
                    //waveFileWriter.Write(convertOutputBuffer.Bytes, 0, convertOutputBuffer.Bytes.Length);
                    //bool finish = false;
                    //if (finish)
                    //{
                    //    waveFileWriter.Close();
                    //}
                    #endregion

                    if (receivecount > 0)
                    {
                        provider.AddSamples(convertOutputBuffer.Bytes, 0, (int)receivecount * sizeof(float) * reader.WaveFormat.Channels, reader.CurrentTime);;
                        //provider.AddSamples(convertOutputBuffer.Bytes, 0, convertOutputBuffer.Bytes.Length, reader.CurrentTime); ;

                        while (provider.BuffersCount > 3)
                        {
                            Thread.Sleep(10);
                        }
                    }

                    //if (finished && bytesRead == 0)
                    //{
                    //    break;
                    //}
                } while (!stopWorker && receivecount != 0);
            }

            reader.Close();
        }
        /// <summary>
        /// The heart of Practice# audio processing:
        /// 1. Reads chunks of uncompressed samples from the input file
        /// 2. Processes the samples using SoundTouch 
        /// 3. Receive process samples from SoundTouch
        /// 4. Play the processed samples with NAudio
        /// 
        /// Also handles logic required for dynamically changing values on-the-fly of: Volume, Loop, Cue, Current Play Position.
        /// </summary>
        private void ProcessAudio()
        {
            m_logger.Debug("ProcessAudio() started");

            #region Setup
            WaveFormat format = m_waveChannel.WaveFormat;
            int bufferSecondLength = format.SampleRate * format.Channels;
            byte[] inputBuffer = new byte[BufferSamples * sizeof(float)];
            byte[] soundTouchOutBuffer = new byte[BufferSamples * sizeof(float)];

            ByteAndFloatsConverter convertInputBuffer = new ByteAndFloatsConverter { Bytes = inputBuffer };
            ByteAndFloatsConverter convertOutputBuffer = new ByteAndFloatsConverter { Bytes = soundTouchOutBuffer };
            uint outBufferSizeFloats = (uint)convertOutputBuffer.Bytes.Length / (uint)(sizeof(float) * format.Channels);

            int bytesRead;
            int floatsRead;
            uint samplesProcessed = 0;
            int bufferIndex = 0;
            TimeSpan actualEndMarker = TimeSpan.Zero;
            bool loop;

            #endregion

            bool isWaitForCue = (Cue.TotalSeconds > 0);

            while (!m_stopWorker && m_waveChannel.Position < m_waveChannel.Length)
            {
                #region Handle Volume Change request
                if (m_volumeChanged) // Double checked locking
                {
                    lock (PropertiesLock)
                    {
                        if (m_volumeChanged)
                        {
                            m_waveChannel.Volume = m_volume;
                            m_volumeChanged = false;
                        }
                    }
                }
                #endregion

                #region Handle New play position request

                TimeSpan newPlayTime = TimeSpan.MinValue;
                if (m_newPlayTimeRequested) // Double checked locking
                {
                    lock (NewPlayTimeLock)
                    {
                        if (m_newPlayTimeRequested)
                        {
                            m_logger.Debug("newPlayTimeRequested: " + m_newPlayTime);
                            if (m_newPlayTime == m_startMarker)
                            {
                                isWaitForCue = true;
                            }

                            newPlayTime = m_newPlayTime;
                            m_newPlayTimeRequested = false;
                        }
                    }
                }

                #endregion

                #region Wait for Cue

                if (isWaitForCue)
                {
                    isWaitForCue = false;
                    WaitForCue();
                }

                #endregion

                #region [Only when new play position requested] Change current play position

                if (newPlayTime != TimeSpan.MinValue)
                {
                    // Perform the change of play position outside of the lock() block, to avoid dead locks
                    m_waveOutDevice.Pause();
                    m_waveChannel.CurrentTime = newPlayTime;
                    m_soundTouchSharp.Clear();
                    m_waveChannel.Flush();
                    m_inputProvider.Flush();
                    m_waveOutDevice.Play();
                    continue;
                }

                #endregion

                #region Read samples from file

                // *** Read one chunk from input file ***
                bytesRead = m_waveChannel.Read(convertInputBuffer.Bytes, 0, convertInputBuffer.Bytes.Length);
                // **************************************

                floatsRead = bytesRead / ((sizeof(float)) * format.Channels);

                #endregion

                #region Apply DSP Effects (Equalizer, Vocal Removal, etc.)

                ApplyDSPEffects(convertInputBuffer.Floats, floatsRead);

                #endregion

                #region Apply Time Stretch Profile

                // Double checked locking
                if (m_timeStretchProfileChanged)
                {
                    lock (PropertiesLock)
                    {
                        if (m_timeStretchProfileChanged)
                        {
                            ApplySoundTouchTimeStretchProfile();

                            m_timeStretchProfileChanged = false;
                        }
                    }
                }

                #endregion

                #region Handle End Marker
                actualEndMarker = this.EndMarker;
                loop = this.Loop;

                if (!loop || actualEndMarker == TimeSpan.Zero)
                    actualEndMarker = m_waveChannel.TotalTime;

                if (m_waveChannel.CurrentTime > actualEndMarker)
                {
                    #region Flush left over samples

                    // ** End marker reached **
                    // Now the input buffer is processed, 'flush' some last samples that are
                    // hiding in the SoundTouch's internal processing pipeline.
                    m_soundTouchSharp.Clear();
                    m_inputProvider.Flush();
                    m_waveChannel.Flush();

                    if (!m_stopWorker)
                    {
                        while (!m_stopWorker && samplesProcessed != 0)
                        {
                            samplesProcessed = m_soundTouchSharp.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);

                            if (samplesProcessed > 0)
                            {
                                TimeSpan currentBufferTime = m_waveChannel.CurrentTime;
                                m_inputProvider.AddSamples(convertOutputBuffer.Bytes, 0, (int)samplesProcessed * sizeof(float) * format.Channels, currentBufferTime);
                            }
                        }
                    }

                    #endregion

                    #region Perform Loop
                    loop = this.Loop;
                    if (loop)
                    {
                        m_soundTouchSharp.Clear();
                        m_waveChannel.Flush();
                        m_waveChannel.CurrentTime = this.StartMarker;
                        isWaitForCue = (Cue.TotalSeconds > 0);
                        continue;
                    }
                    else
                    {
                        // Exit playback gracefully
                        break;
                    }

                    #endregion
                }
                #endregion

                #region Put samples in SoundTouch

                SetSoundSharpValues();

                // ***                    Put samples in SoundTouch                   ***
                m_soundTouchSharp.PutSamples(convertInputBuffer.Floats, (uint)floatsRead);
                // **********************************************************************

                #endregion

                #region Receive & Play Samples
                // Receive samples from SoundTouch
                do
                {
                    // ***                Receive samples back from SoundTouch            ***
                    // *** This is where Time Stretching and Pitch Changing are actually done *********
                    samplesProcessed = m_soundTouchSharp.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);
                    // **********************************************************************

                    if (samplesProcessed > 0)
                    {
                        TimeSpan currentBufferTime = m_waveChannel.CurrentTime;

                        // ** Play samples that came out of SoundTouch by adding them to AdvancedBufferedWaveProvider - the buffered player 
                        m_inputProvider.AddSamples(convertOutputBuffer.Bytes, 0, (int)samplesProcessed * sizeof(float) * format.Channels, currentBufferTime);
                        // **********************************************************************

                        // Wait for queue to free up - only then add continue reading from the file
                        // >> Note: when paused, loop runs infinitely
                        while (!m_stopWorker && m_inputProvider.GetQueueCount() > BusyQueuedBuffersThreshold)
                        {
                            Thread.Sleep(10);
                        }
                        bufferIndex++;
                    }
                } while (!m_stopWorker && samplesProcessed != 0);
                #endregion
            } // while

            #region Stop PlayBack

            m_logger.Debug("ProcessAudio() finished - stop playback");
            m_waveOutDevice.Stop();
            // Stop listening to PlayPositionChanged events
            m_inputProvider.PlayPositionChanged -= new EventHandler(inputProvider_PlayPositionChanged);

            // Fix to current play time not finishing up at end marker (Wave channel uses positions)
            if (!m_stopWorker && CurrentPlayTime < actualEndMarker)
            {
                lock (CurrentPlayTimeLock)
                {
                    m_currentPlayTime = actualEndMarker;
                }
            }

            // Clear left over buffers
            m_soundTouchSharp.Clear();

            // Playback status changed to -> Stopped
            ChangeStatus(Statuses.Stopped);
            #endregion
        }