public AudioStreamModifier(ISampleProvider sample, double rateMult, int pitchDelta) { _sample = sample; WaveFormat = _sample.WaveFormat; _soundTouch = new SoundTouch<float, double>(); channelCount = sample.WaveFormat.Channels; _soundTouch.SetSampleRate(sample.WaveFormat.SampleRate); _soundTouch.SetChannels(channelCount); rateMult = (rateMult - 1) * 100; _soundTouch.SetTempoChange(rateMult); _soundTouch.SetPitchSemiTones(pitchDelta*0.25f); _soundTouch.SetRateChange(1.0f); _soundTouch.SetSetting(SettingId.UseQuickseek, 1); _soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 1); _soundTouch.SetSetting(SettingId.SequenceDurationMs, 40); _soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15); _soundTouch.SetSetting(SettingId.OverlapDurationMs, 8); sourceReadBuffer = new float[(WaveFormat.SampleRate * channelCount * readDurationMilliseconds) / 1000]; soundTouchReadBuffer = new float[sourceReadBuffer.Length * 10]; // support down to 0.1 speed }
public AudioStreamModifier(ISampleProvider sample, double rateMult, int pitchDelta) { _sample = sample; WaveFormat = _sample.WaveFormat; _soundTouch = new SoundTouch <float, double>(); channelCount = sample.WaveFormat.Channels; _soundTouch.SetSampleRate(sample.WaveFormat.SampleRate); _soundTouch.SetChannels(channelCount); rateMult = (rateMult - 1) * 100; _soundTouch.SetTempoChange(rateMult); _soundTouch.SetPitchSemiTones(pitchDelta * 0.25f); _soundTouch.SetRateChange(1.0f); _soundTouch.SetSetting(SettingId.UseQuickseek, 1); _soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 1); _soundTouch.SetSetting(SettingId.SequenceDurationMs, 40); _soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15); _soundTouch.SetSetting(SettingId.OverlapDurationMs, 8); sourceReadBuffer = new float[(WaveFormat.SampleRate * channelCount * readDurationMilliseconds) / 1000]; soundTouchReadBuffer = new float[sourceReadBuffer.Length * 10]; // support down to 0.1 speed }
/// <summary> /// Sets the <c>SoundTouch</c> object up according to input file sound format & command line parameters /// </summary> private static void Setup(SoundTouch <TSampleType, TLongSampleType> pSoundTouch, WavInFile inFile, RunParameters parameters) { int sampleRate = inFile.GetSampleRate(); int channels = inFile.GetNumChannels(); pSoundTouch.SetSampleRate(sampleRate); pSoundTouch.SetChannels(channels); pSoundTouch.SetTempoChange(parameters.TempoDelta); pSoundTouch.SetPitchSemiTones(parameters.PitchDelta); pSoundTouch.SetRateChange(parameters.RateDelta); pSoundTouch.SetSetting(SettingId.UseQuickseek, parameters.Quick); pSoundTouch.SetSetting(SettingId.UseAntiAliasFilter, (parameters.NoAntiAlias == 1) ? 0 : 1); if (parameters.Speech) { // use settings for speech processing pSoundTouch.SetSetting(SettingId.SequenceDurationMs, 40); pSoundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15); pSoundTouch.SetSetting(SettingId.OverlapDurationMs, 8); Console.Error.WriteLine("Tune processing parameters for speech processing."); } // print processing information if (parameters.OutFileName != null) { #if SOUNDTOUCH_INTEGER_SAMPLES Console.Error.WriteLine("Uses 16bit integer sample type in processing.\n"); #else #if !SOUNDTOUCH_FLOAT_SAMPLES #error "Sampletype not defined" #endif Console.Error.WriteLine("Uses 32bit floating point sample type in processing.\n"); #endif // print processing information only if outFileName given i.e. some processing will happen Console.Error.WriteLine("Processing the file with the following changes:"); Console.Error.WriteLine(" tempo change = {0:0.00} %", parameters.TempoDelta); Console.Error.WriteLine(" pitch change = {0} semitones", parameters.PitchDelta); Console.Error.WriteLine(" rate change = {0:0.00} %\n", parameters.RateDelta); Console.Error.Write("Working..."); } else { // outFileName not given Console.Error.WriteLine("Warning: output file name missing, won't output anything.\n"); } Console.Error.Flush(); }
//private static int Main(string[] args) //{ // var parameters = new RunParameters(); // parameters.TempoDelta = 50; // parameters.PitchDelta = 5; // parameters.Speech = true; // var inBytes = System.IO.File.ReadAllBytes("Test.wav"); // var inStream = new MemoryStream(inBytes); // var outStream = new MemoryStream(); // Process(inStream, outStream, parameters); // var outBytes = outStream.ToArray(); // File.WriteAllBytes("Test_Pith_Tempo_ms.wav", outBytes); // return 0; //} public static void Process(Stream inStream, Stream outStream, RunParameters parameters) { var soundTouch = new SoundTouch <TSampleType, TLongSampleType>(); // Open Input file. var inFile = new WavInFile(inStream); //var inFile = new WavInFile("Test.wav"); int bits = inFile.GetNumBits(); int samplerate = inFile.GetSampleRate(); int channels = inFile.GetNumChannels(); // Open output file. var outFile = new WavOutFile(outStream, samplerate, bits, channels); //var outFile = new WavOutFile("Test_Pith_Tempo.wav", samplerate, bits, channels); if (parameters.DetectBpm) { // detect sound BPM (and adjust processing parameters // accordingly if necessary) DetectBpm(inFile, parameters); } // Setup the 'SoundTouch' object for processing the sound int sampleRate = inFile.GetSampleRate(); soundTouch.SetSampleRate(sampleRate); soundTouch.SetChannels(channels); soundTouch.SetTempoChange(parameters.TempoDelta); soundTouch.SetPitchSemiTones(parameters.PitchDelta); soundTouch.SetRateChange(parameters.RateDelta); soundTouch.SetSetting(SettingId.UseQuickseek, parameters.Quick); soundTouch.SetSetting(SettingId.UseAntiAliasFilter, (parameters.NoAntiAlias == 1) ? 0 : 1); if (parameters.Speech) { // use settings for speech processing soundTouch.SetSetting(SettingId.SequenceDurationMs, 40); soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15); soundTouch.SetSetting(SettingId.OverlapDurationMs, 8); } // Process the sound Process(soundTouch, inFile, outFile); if (inFile != null) { inFile.Dispose(); } if (outFile != null) { outFile.Dispose(); } }
/// <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); } }