/// <summary> /// Processes the sound. /// </summary> private static void Process(SoundTouch <TSampleType, TLongSampleType> pSoundTouch, WavInFile inFile, WavOutFile outFile) { int nSamples; var sampleBuffer = new TSampleType[BUFF_SIZE]; if ((inFile == null) || (outFile == null)) { return; // nothing to do. } int nChannels = inFile.GetNumChannels(); Debug.Assert(nChannels > 0); int buffSizeSamples = BUFF_SIZE / nChannels; // Process samples read from the input file while (!inFile.Eof()) { // Read a chunk of samples from the input file int num = inFile.Read(sampleBuffer, BUFF_SIZE); nSamples = num / inFile.GetNumChannels(); // Feed the samples into SoundTouch processor pSoundTouch.PutSamples(sampleBuffer, nSamples); // Read ready samples from SoundTouch processor & write them output file. // NOTES: // - 'receiveSamples' doesn't necessarily return any samples at all // during some rounds! // - On the other hand, during some round 'receiveSamples' may have more // ready samples than would fit into 'sampleBuffer', and for this reason // the 'receiveSamples' call is iterated for as many times as it // outputs samples. do { nSamples = pSoundTouch.ReceiveSamples(sampleBuffer, buffSizeSamples); outFile.Write(sampleBuffer, nSamples * nChannels); } while (nSamples != 0); } // Now the input file is processed, yet 'flush' few last samples that are // hiding in the SoundTouch's internal processing pipeline. pSoundTouch.Flush(); do { nSamples = pSoundTouch.ReceiveSamples(sampleBuffer, buffSizeSamples); outFile.Write(sampleBuffer, nSamples * nChannels); } while (nSamples != 0); }
public int Read(float[] buffer, int offset, int count) { int samplesRead = 0; while (samplesRead < count) { if (!reachedEndOfSource) { var readFromSource = _sample.Read(sourceReadBuffer, 0, sourceReadBuffer.Length); if (readFromSource > 0) { _soundTouch.PutSamples(sourceReadBuffer, readFromSource / channelCount); } else { reachedEndOfSource = true; // we've reached the end, tell SoundTouch we're done _soundTouch.Flush(); } } var desiredSampleFrames = (count - samplesRead) / channelCount; var received = _soundTouch.ReceiveSamples(soundTouchReadBuffer, desiredSampleFrames) * channelCount; // use loop instead of Array.Copy due to WaveBuffer for (int n = 0; n < received; n++) { buffer[offset + samplesRead++] = soundTouchReadBuffer[n]; } if (received == 0 && reachedEndOfSource) { break; } } return(samplesRead); }
/// <summary> /// Overridden Read function that returns samples processed with SoundTouch. Returns data in same format as /// WaveChannel32 i.e. stereo float samples. /// </summary> /// <param name="buffer">Buffer where to return sample data</param> /// <param name="offset">Offset from beginning of the buffer</param> /// <param name="count">Number of bytes to return</param> /// <returns>Number of bytes copied to buffer</returns> public override int Read(byte[] buffer, int offset, int count) { try { if (ArcaeaSpeedChanger.GlobalVariable.soundParseFormat == SupportedAudioFormat.OGG) { if (ArcaeaSpeedChanger.GlobalVariable.soundParseProgress <= 50) { ArcaeaSpeedChanger.GlobalVariable.soundParseProgress += 2; } } else { ArcaeaSpeedChanger.GlobalVariable.soundParseProgress += 5; } //Console.WriteLine("Change!" + ArcaeaSpeedChanger.GlobalVariable.soundParseProgress); // Iterate until enough samples available for output: // - read samples from input stream // - put samples to SoundStretch processor while (st.AvailableSampleCount < count) { int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length); if (nbytes == 0) { // end of stream. flush final samples from SoundTouch buffers to output if (endReached == false) { endReached = true; // do only once to avoid continuous flushing st.Flush(); } break; } // binary copy data from "byte[]" to "float[]" buffer Buffer.BlockCopy(bytebuffer, 0, floatbuffer, 0, nbytes); st.PutSamples(floatbuffer, (uint)(nbytes / 8)); } // ensure that buffer is large enough to receive desired amount of data out if (floatbuffer.Length < count / 4) { floatbuffer = new float[count / 4]; } // get processed output samples from SoundTouch int numsamples = (int)st.ReceiveSamples(floatbuffer, (uint)(count / 8)); // binary copy data from "float[]" to "byte[]" buffer Buffer.BlockCopy(floatbuffer, 0, buffer, offset, numsamples * 8); return(numsamples * 8); // number of bytes } catch (Exception) { return(0); } }
public int Read(float[] buffer, int offset, int count) { if (playbackRate == 0) // play silence { for (int n = 0; n < count; n++) { buffer[offset++] = 0; } return(count); } if (repositionRequested) { soundTouch.Clear(); repositionRequested = false; } int samplesRead = 0; bool reachedEndOfSource = false; while (samplesRead < count) { if (soundTouch.NumberOfSamplesAvailable == 0) { var readFromSource = sourceProvider.Read(sourceReadBuffer, 0, sourceReadBuffer.Length); if (readFromSource > 0) { soundTouch.PutSamples(sourceReadBuffer, readFromSource / channelCount); } else { reachedEndOfSource = true; // we've reached the end, tell SoundTouch we're done soundTouch.Flush(); } } var desiredSampleFrames = (count - samplesRead) / channelCount; var received = soundTouch.ReceiveSamples(soundTouchReadBuffer, desiredSampleFrames) * channelCount; // use loop instead of Array.Copy due to WaveBuffer for (int n = 0; n < received; n++) { buffer[offset + samplesRead++] = soundTouchReadBuffer[n]; } if (received == 0 && reachedEndOfSource) { break; } } return(samplesRead); }
/// <summary> /// Overridden Read function that returns samples processed with SoundTouch. Returns data in same format as /// WaveChannel32 i.e. stereo float samples. /// </summary> /// <param name="buffer">Buffer where to return sample data</param> /// <param name="offset">Offset from beginning of the buffer</param> /// <param name="count">Number of bytes to return</param> /// <returns>Number of bytes copied to buffer</returns> public override int Read(byte[] buffer, int offset, int count) { try { // Iterate until enough samples available for output: // - read samples from input stream // - put samples to SoundStretch processor while (st.AvailableSampleCount < count) { int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length); if (nbytes == 0) { // end of stream. flush final samples from SoundTouch buffers to output if (endReached == false) { endReached = true; // do only once to avoid continuous flushing st.Flush(); } break; } // binary copy data from "byte[]" to "float[]" buffer Buffer.BlockCopy(bytebuffer, 0, floatbuffer, 0, nbytes); st.PutSamples(floatbuffer, (uint)(nbytes / 8)); } // ensure that buffer is large enough to receive desired amount of data out if (floatbuffer.Length < count / 4) { floatbuffer = new float[count / 4]; } // get processed output samples from SoundTouch int numsamples = (int)st.ReceiveSamples(floatbuffer, (uint)(count / 8)); // binary copy data from "float[]" to "byte[]" buffer for (int i = 0; i < count / 4; i++) { floatbuffer[i + offset] = equaliser.TransformSample(floatbuffer[i + offset]); } Buffer.BlockCopy(floatbuffer, 0, buffer, offset, numsamples * 8); return(numsamples * 8); // number of bytes } catch (Exception exp) { StatusMessage.Write("exception in WaveStreamProcessor.Read: " + exp.Message); return(0); } }
/// <summary> /// Stretches audio, keeps channels and samplerate. /// negatief value slowsdown (makes longer) the audio /// Positief value speedsup (make shorter) the audio /// </summary> public AudioSamples TimeStretch(float[] inputAudioSamples, int inputSampleRate = 44100, int inputChannels = 2, float rateFactor = 0.0f) { // calculate total milliseconds to read int totalmilliseconds = Int32.MaxValue; float[] data = null; int stream = Bass.BASS_StreamCreatePush(inputSampleRate, inputChannels, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero); ThrowIfStreamIsInvalid(stream); BASS_CHANNELINFO channelInfo = Bass.BASS_ChannelGetInfo(stream); Bass.BASS_StreamPutData(stream, inputAudioSamples, inputAudioSamples.Length * 4); SoundTouch <Single, Double> soundTouch = new SoundTouch <Single, Double>(); soundTouch.SetSampleRate(channelInfo.freq); soundTouch.SetChannels(channelInfo.chans); soundTouch.SetTempoChange(0.0f); soundTouch.SetPitchSemiTones(0.0f); soundTouch.SetRateChange(rateFactor); // -1.4f = Radio 538 setting soundTouch.SetSetting(SettingId.UseQuickseek, 0); soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 0); int bufferSize = 2048; float[] buffer = new float[bufferSize]; List <float[]> chunks = new List <float[]>(); int size = 0; int nSamples = 0; while ((float)(size) / channelInfo.freq * 1000 < totalmilliseconds) { // get re-sampled data int bytesRead = Bass.BASS_ChannelGetData(stream, buffer, bufferSize); if (bytesRead <= 0) { break; } nSamples = (bytesRead / 4) / channelInfo.chans; // Feed the samples into SoundTouch processor soundTouch.PutSamples(buffer, nSamples); // Read ready samples from SoundTouch processor & write them output file. // NOTES: // - 'receiveSamples' doesn't necessarily return any samples at all // during some rounds! // - On the other hand, during some round 'receiveSamples' may have more // ready samples than would fit into 'sampleBuffer', and for this reason // the 'receiveSamples' call is iterated for as many times as it // outputs samples. do { nSamples = soundTouch.ReceiveSamples(buffer, (bufferSize / channelInfo.chans)); if (nSamples > 0) { float[] chunk = new float[nSamples * channelInfo.chans]; Array.Copy(buffer, chunk, nSamples * channelInfo.chans); chunks.Add(chunk); size += nSamples * channelInfo.chans; //size of the data } } while (nSamples != 0); } //while // Now the input file is processed, yet 'flush' few last samples that are // hiding in the SoundTouch's internal processing pipeline. soundTouch.Flush(); do { nSamples = soundTouch.ReceiveSamples(buffer, (bufferSize / channelInfo.chans)); if (nSamples > 0) { float[] chunk = new float[nSamples * channelInfo.chans]; Array.Copy(buffer, chunk, nSamples * channelInfo.chans); chunks.Add(chunk); size += nSamples * channelInfo.chans; //size of the data } } while (nSamples != 0); if (size <= 0) { // not enough samples to return the requested data return(null); } // Do bass cleanup Bass.BASS_ChannelStop(stream); Bass.BASS_StreamFree(stream); int start = 0; int end = size; data = new float[size]; int index = 0; // Concatenate foreach (float[] chunk in chunks) { Array.Copy(chunk, 0, data, index, chunk.Length); index += chunk.Length; } // Select specific part of the song if (start != 0 || end != size) { float[] temp = new float[end - start]; Array.Copy(data, start, temp, 0, end - start); data = temp; } // Create audiosamples object AudioSamples audioSamples = new AudioSamples(); audioSamples.Origin = "MEMORY"; audioSamples.Channels = channelInfo.chans; audioSamples.SampleRate = channelInfo.freq; audioSamples.StartInMS = start; audioSamples.DurationInMS = end; audioSamples.Samples = data; return(audioSamples); }
/// <summary> /// Overridden Read function that returns samples processed with SoundTouch. Returns data in same format as /// WaveChannel32 i.e. stereo float samples. /// </summary> /// <param name="buffer">Buffer where to return sample data</param> /// <param name="offset">Offset from beginning of the buffer</param> /// <param name="count">Number of bytes to return</param> /// <returns>Number of bytes copied to buffer</returns> public override int Read(byte[] buffer, int offset, int count) { var normalb4 = normal; currentFrameCount++; try { // Iterate until enough samples available for output: // - read samples from input stream // - put samples to SoundStretch processor while (st.AvailableSamples < count) { int nbytes; if (inputWC32 != null) { nbytes = inputWC32.Read(bytebuffer, 0, bytebuffer.Length); } else { nbytes = inputAFR.Read(bytebuffer, 0, bytebuffer.Length); } if (nbytes == 0) { // end of stream. flush final samples from SoundTouch buffers to output if (EndReached == false) { EndReached = true; // do only once to avoid continuous flushing st.Flush(); } break; } // binary copy data from "byte[]" to "float[]" buffer Buffer.BlockCopy(bytebuffer, 0, floatbuffer, 0, nbytes); var max = GetMaxVolume(floatbuffer); if (max >= options.silent_threshold) { normal = true; st.SetTempoChange((options.sounded_speed - 1) * 100); } else { normal = false; st.SetTempoChange((options.silent_speed - 1) * 100); } st.PutSamples(floatbuffer, (nbytes / 8)); } // ensure that buffer is large enough to receive desired amount of data out if (floatbuffer.Length < count / 4) { floatbuffer = new float[count / 4]; } // get processed output samples from SoundTouch int numsamples = st.ReceiveSamples(floatbuffer, (count / 8)); // binary copy data from "float[]" to "byte[]" buffer Buffer.BlockCopy(floatbuffer, 0, buffer, offset, numsamples * 8); if (normal != normalb4) { OnAudioFrameRendered(); } return(numsamples * 8); // number of bytes } catch (Exception) { return(0); } }
public int Read(byte[] buffer, int offset, int count) { int numRead = 0; // Mismatched formats/interpretations: // // - Source returns raw bytes, lets us interpret. // - SoundTouch takes samples (Int16), counts one frame across all channels as a single sample (one left + one right == one sample). // - When converting to/from bytes, we need to count each channel in a frame as a separate sample (one left + one right == two samples). // - We implement IWaveProvider, the same as source, and are thus expected to return raw bytes. // - We may be asked for a number of bytes that isn't a multiple of the stretcher's output block size. // - We may be provided with source data that isn't a multiple of the stretcher's input block size. // // Hooray! if (_outputExtraBytes.Count > 0) { if (_outputExtraBytes.Count > count) { _outputExtraBytes.CopyTo(0, buffer, offset, count); _outputExtraBytes.RemoveRange(0, count); return(count); } else { _outputExtraBytes.CopyTo(buffer); count -= _outputExtraBytes.Count; numRead += _outputExtraBytes.Count; _outputExtraBytes.Clear(); } } int bytesPerFrame = 2 * _source.WaveFormat.Channels; while (true) { int stretchedFramesToRead = (count + bytesPerFrame - 1) / bytesPerFrame; int stretchedFramesBytes = stretchedFramesToRead * bytesPerFrame; if (stretchedFramesBytes > _stretchedSamples.Length) { stretchedFramesToRead = _stretchedSamples.Length / bytesPerFrame; stretchedFramesBytes = stretchedFramesToRead * bytesPerFrame; } int numberOfFramesRead = _stretcher.ReceiveSamples(_stretchedSamples, stretchedFramesToRead); if (numberOfFramesRead == 0) { int sourceBytesRead = _sourceExtraBytes.Count; if (sourceBytesRead > 0) { _sourceExtraBytes.CopyTo(_sourceBuffer); _sourceExtraBytes.Clear(); } sourceBytesRead += _source.Read(_sourceBuffer, sourceBytesRead, _sourceBuffer.Length - sourceBytesRead); SourceRead?.Invoke(this, EventArgs.Empty); if (sourceBytesRead == 0) { // End of stream, zero pad Array.Clear(buffer, offset, count); //the calling method will handle not filled arrays // if (offset == 0 && count == buffer.Length) // numRead = 0; // //else ////numRead += count; EndOfStream?.Invoke(this, EventArgs.Empty); return(numRead); } int numberOfSourceSamples = (sourceBytesRead / 2 / _source.WaveFormat.Channels) * _source.WaveFormat.Channels; int sourceBytesInSamples = numberOfSourceSamples * 2; if (sourceBytesInSamples < sourceBytesRead) { // We got a misaligned read, stash the bytes we aren't going to process for the next pass. for (int i = sourceBytesInSamples; i < sourceBytesRead; i++) { _sourceExtraBytes.Add(_sourceBuffer[i]); } } for (int i = 0; i < numberOfSourceSamples; i++) { int lo = _sourceBuffer[i + i]; int hi = _sourceBuffer[i + i + 1]; _sourceSamples[i] = unchecked ((short)((hi << 8) | lo)); } _stretcher.PutSamples(_sourceSamples, numberOfSourceSamples / _source.WaveFormat.Channels); } else { int numberOfBytesAvailable = numberOfFramesRead * bytesPerFrame; int numberOfSamplesAvailable = numberOfBytesAvailable / 2; int i; for (i = 0; i < numberOfSamplesAvailable; i++) { if (count == 0) { break; } int sample = _stretchedSamples[i]; unchecked { byte hi = (byte)(sample >> 8); byte lo = (byte)(sample & 0xFF); buffer[offset++] = lo; numRead++; count--; if (count == 0) { _outputExtraBytes.Add(hi); break; } buffer[offset++] = hi; numRead++; count--; } } for (; i < numberOfSamplesAvailable; i++) { int sample = _stretchedSamples[i]; unchecked { byte hi = (byte)(sample >> 8); byte lo = (byte)(sample & 0xFF); _outputExtraBytes.Add(lo); _outputExtraBytes.Add(hi); } } if (count == 0) { return(numRead); } } } }