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 VarispeedSampleProvider(ISampleProvider sourceProvider, int readDurationMilliseconds, SoundTouchProfile soundTouchProfile) { soundTouch = new SoundTouch(); // explore what the default values are before we change them: //Debug.WriteLine(String.Format("SoundTouch Version {0}", soundTouch.VersionString)); //Debug.WriteLine("Use QuickSeek: {0}", soundTouch.GetUseQuickSeek()); //Debug.WriteLine("Use AntiAliasing: {0}", soundTouch.GetUseAntiAliasing()); SetSoundTouchProfile(soundTouchProfile); this.sourceProvider = sourceProvider; soundTouch.SetSampleRate(WaveFormat.SampleRate); channelCount = WaveFormat.Channels; soundTouch.SetChannels(channelCount); sourceReadBuffer = new float[(WaveFormat.SampleRate * channelCount * (long)readDurationMilliseconds) / 1000]; soundTouchReadBuffer = new float[sourceReadBuffer.Length * 10]; // support down to 0.1 speed }
/// <summary> /// Initializes the audio renderer. /// Call the Play Method to start reading samples. /// </summary> private void Initialize() { Destroy(); // Release the audio device always upon exiting if (Application.Current is Application app) { app.Dispatcher?.BeginInvoke(new Action(() => { app.Exit += OnApplicationExit; })); } // Enumerate devices. The default device is the first one so we check // that we have more than 1 device (other than the default stub) var hasAudioDevices = MediaElement.RendererOptions.UseLegacyAudioOut ? LegacyAudioPlayer.EnumerateDevices().Count > 1 : DirectSoundPlayer.EnumerateDevices().Count > 1; // Check if we have an audio output device. if (hasAudioDevices == false) { WaitForReadyEvent.Complete(); HasFiredAudioDeviceStopped = true; this.LogWarning(Aspects.AudioRenderer, "No audio device found for output."); return; } // Initialize the SoundTouch Audio Processor (if available) AudioProcessor = (SoundTouch.IsAvailable == false) ? null : new SoundTouch(); if (AudioProcessor != null) { AudioProcessor.SetChannels(Convert.ToUInt32(WaveFormat.Channels)); AudioProcessor.SetSampleRate(Convert.ToUInt32(WaveFormat.SampleRate)); } // Initialize the Audio Device AudioDevice = MediaElement.RendererOptions.UseLegacyAudioOut ? new LegacyAudioPlayer(this, MediaElement.RendererOptions.LegacyAudioDevice?.DeviceId ?? -1) as IWavePlayer : new DirectSoundPlayer(this, MediaElement.RendererOptions.DirectSoundDevice?.DeviceId ?? DirectSoundPlayer.DefaultPlaybackDeviceId); // Create the Audio Buffer SampleBlockSize = Constants.AudioBytesPerSample * Constants.AudioChannelCount; var bufferLength = WaveFormat.ConvertMillisToByteSize(2000); // 2-second buffer AudioBuffer = new CircularBuffer(bufferLength); AudioDevice.Start(); }
public SoundTouchWaveStream(IWaveProvider source) { if (source.WaveFormat.BitsPerSample != 16) { throw new FormatException("Can't process bit depth of " + source.WaveFormat.BitsPerSample); } _source = source; _sourceSamples = new short[32768]; _sourceBuffer = new byte[_sourceSamples.Length * 2]; _stretchedSamples = new short[32768]; _stretcher = new SoundTouch <short, long>(); _stretcher.SetSampleRate(_source.WaveFormat.SampleRate); _stretcher.SetChannels(_source.WaveFormat.Channels); _tempo = 1.0; }
private void initializeBuffers() { // example: 44.1 kHz (44,100 samples per second) * 16 bits per sample / 8 bits per byte * 2 channels (stereo) // blockAlign is number of bytes per frame (samples required for all channels) uint fastPlaySamplesPerSecond = m_CurrentAudioPCMFormat.SampleRate; // (int)Math.Round(m_CurrentAudioPCMFormat.SampleRate * m_FastPlayFactor); uint byteRate = fastPlaySamplesPerSecond * m_CurrentAudioPCMFormat.BlockAlign; // (m_CurrentAudioPCMFormat.BitDepth / 8) * m_CurrentAudioPCMFormat.NumberOfChannels; int pcmDataBufferSize = (int)Math.Round(byteRate * REFRESH_INTERVAL_MS / 1000.0); pcmDataBufferSize -= pcmDataBufferSize % m_CurrentAudioPCMFormat.BlockAlign; m_PcmDataBufferLength = pcmDataBufferSize; if (m_PcmDataBuffer == null) { Console.WriteLine("ALLOCATING m_PcmDataBuffer"); m_PcmDataBuffer = new byte[m_PcmDataBufferLength]; } else if (m_PcmDataBuffer.Length < m_PcmDataBufferLength) { Console.WriteLine("m_PcmDataBuffer.resize"); Array.Resize(ref m_PcmDataBuffer, m_PcmDataBufferLength); } int dxBufferSize = (int)Math.Round(byteRate * (20 * REFRESH_INTERVAL_MS) / 1000.0); // ONE SECOND when 50ms refresh internal // 0.500 == 500ms dxBufferSize -= dxBufferSize % m_CurrentAudioPCMFormat.BlockAlign; //m_CircularBufferRefreshChunkSize = (int)(byteRate * 0.200); //200ms //m_CircularBufferRefreshChunkSize -= m_CircularBufferRefreshChunkSize % m_CurrentAudioPCMFormat.BlockAlign; #if USE_SHARPDX WaveFormat waveFormat = new WaveFormat((int)m_CurrentAudioPCMFormat.SampleRate, 16, m_CurrentAudioPCMFormat.NumberOfChannels); #else WaveFormat waveFormat = new WaveFormat(); waveFormat.FormatTag = WaveFormatTag.Pcm; waveFormat.Channels = (short)m_CurrentAudioPCMFormat.NumberOfChannels; waveFormat.SamplesPerSecond = (int)m_CurrentAudioPCMFormat.SampleRate; waveFormat.BitsPerSample = (short)m_CurrentAudioPCMFormat.BitDepth; waveFormat.BlockAlign = (short)m_CurrentAudioPCMFormat.BlockAlign; waveFormat.AverageBytesPerSecond = (int)byteRate; #endif #if USE_SHARPDX SoundBufferDescription bufferDescription = new SoundBufferDescription(); bufferDescription.Flags = BufferFlags.GetCurrentPosition2 | //BufferFlags.ControlPositionNotify | BufferFlags.ControlVolume | BufferFlags.ControlFrequency | BufferFlags.GlobalFocus; #else BufferDescription bufferDescription = new BufferDescription(); bufferDescription.ControlVolume = true; bufferDescription.ControlFrequency = true; bufferDescription.GlobalFocus = true; #endif bufferDescription.BufferBytes = dxBufferSize; bufferDescription.Format = waveFormat; if (OutputDevice == null) { Console.WriteLine("/// OutputDevice NULL, attempting reset..."); List <OutputDevice> outputDevices = OutputDevices; Console.WriteLine("/// OutputDevices: " + outputDevices.Count); SetOutputDevice(m_OutputDeviceControl, "dummy"); } #if USE_SHARPDX m_CircularBuffer = new SecondarySoundBuffer(OutputDevice.Device, bufferDescription); #else m_CircularBuffer = new SecondaryBuffer(bufferDescription, OutputDevice.Device); #endif #if USE_SOUNDTOUCH if (m_SoundTouch == null) { m_SoundTouch = new SoundTouch <TSampleType, TLongSampleType>(); m_SoundTouch.SetSetting(SettingId.UseAntiAliasFilter, 1); // Speech optimised m_SoundTouch.SetSetting(SettingId.SequenceDurationMs, 40); m_SoundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15); m_SoundTouch.SetSetting(SettingId.OverlapDurationMs, 8); } //m_SoundTouch.Flush(); m_SoundTouch.Clear(); //AudioLib.SampleRate.Hz22050 if (m_CurrentAudioPCMFormat.SampleRate > 22050) { m_SoundTouch.SetSetting(SettingId.UseQuickseek, 1); } else { m_SoundTouch.SetSetting(SettingId.UseQuickseek, 0); } m_SoundTouch.SetSampleRate((int)m_CurrentAudioPCMFormat.SampleRate); m_SoundTouch.SetChannels((int)m_CurrentAudioPCMFormat.NumberOfChannels); #endif //USE_SOUNDTOUCH FastPlayFactor = m_FastPlayFactor; // reset }
/// <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); }