/// <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_stopWorker = false;
            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

            while (!m_stopWorker && m_waveChannel.Position < m_waveChannel.Length)
            {
                lock (PropertiesLock)
                {
                    m_waveChannel.Volume = m_volume;
                }

                #region Read samples from file

                // Change current play position
                lock (CurrentPlayTimeLock)
                {
                    if (m_newPlayTimeRequested)
                    {
                        m_waveChannel.CurrentTime = m_newPlayTime;

                        m_newPlayTimeRequested = false;
                    }
                }

                // *** 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

                lock (PropertiesLock)
                {
                    if (m_timeStretchProfileChanged)
                    {
                        ApplySoundTouchTimeStretchProfile();

                        m_timeStretchProfileChanged = false;
                    }
                }

                #endregion

                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)
                        {
                            SetSoundSharpValues();

                            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);
                            }

                            samplesProcessed = m_soundTouchSharp.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);
                        }
                    }

                    #endregion

                    #region Handle Loop & Cue
                    loop = this.Loop;
                    if (loop)
                    {
                        m_waveChannel.CurrentTime = this.StartMarker;

                        WaitForCue();
                    }
                    else
                    {
                        break;
                    }

                    #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
        }
Beispiel #2
0
        /// <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_stopWorker = false;
            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

            while (!m_stopWorker && m_waveChannel.Position < m_waveChannel.Length)
            {
                lock (PropertiesLock)
                {
                    m_waveChannel.Volume = m_volume;
                }

                #region Read samples from file

                // Change current play position
                lock (CurrentPlayTimeLock)
                {
                    if (m_newPlayTimeRequested)
                    {
                        m_waveChannel.CurrentTime = m_newPlayTime;

                        m_newPlayTimeRequested = false;
                    }
                }

                // *** 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

                lock (PropertiesLock)
                {
                    if (m_timeStretchProfileChanged)
                    {
                        ApplySoundTouchTimeStretchProfile();

                        m_timeStretchProfileChanged = false;
                    }
                }

                #endregion

                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)
                        {
                            SetSoundSharpValues();

                            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);
                            }

                            samplesProcessed = m_soundTouchSharp.ReceiveSamples(convertOutputBuffer.Floats, outBufferSizeFloats);
                        }
                    }

                    #endregion

                    #region Handle Loop & Cue
                    loop = this.Loop;
                    if (loop)
                    {
                        m_waveChannel.CurrentTime = this.StartMarker;

                        WaitForCue();
                    }
                    else
                    {
                        break;
                    }

                    #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
        }