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 }