using System; using System.Collections.Generic; namespace RubberBand { public class RubberBandStretcher : IDisposable { public enum Options { None = 0, ProcessOffline = 0x00000000, ProcessRealTime = 0x00000001, StretchElastic = 0x00000000, StretchPrecise = 0x00000010, TransientsCrisp = 0x00000000, TransientsMixed = 0x00000100, TransientsSmooth = 0x00000200, DetectorCompound = 0x00000000, DetectorPercussive = 0x00000400, DetectorSoft = 0x00000800, PhaseLaminar = 0x00000000, PhaseIndependent = 0x00002000, ThreadingAuto = 0x00000000, ThreadingNever = 0x00010000, ThreadingAlways = 0x00020000, WindowStandard = 0x00000000, WindowShort = 0x00100000, WindowLong = 0x00200000, SmoothingOff = 0x00000000, SmoothingOn = 0x00800000, FormantShifted = 0x00000000, FormantPreserved = 0x01000000, PitchHighSpeed = 0x00000000, PitchHighQuality = 0x02000000, PitchHighConsistency = 0x04000000, ChannelsApart = 0x00000000, ChannelsTogether = 0x10000000, } public const Options DefaultOptions = Options.None; public const Options PercussiveOptions = Options.WindowShort | Options.PhaseIndependent; IntPtr _rbs; public RubberBandStretcher(int sampleRate, int channels, Options options = DefaultOptions, double initialTimeRatio = 1.0, double initialPitchScale = 1.0) { _rbs = RubberBandNativeMethods.RubberBandStretcher_Create(new IntPtr(sampleRate), new IntPtr(channels), (int)options, initialTimeRatio, initialPitchScale); } public void Dispose() { if (_rbs != IntPtr.Zero) { RubberBandNativeMethods.RubberBandStretcher_Delete(_rbs); _rbs = IntPtr.Zero; } } public void Reset() { RubberBandNativeMethods.RubberBandStretcher_Reset(_rbs); } public void SetTimeRatio(double ratio) { RubberBandNativeMethods.RubberBandStretcher_SetTimeRatio(_rbs, ratio); } public void SetPitchScale(double scale) { RubberBandNativeMethods.RubberBandStretcher_SetPitchScale(_rbs, scale); } public double GetTimeRatio() { return RubberBandNativeMethods.RubberBandStretcher_GetTimeRatio(_rbs); } public double GetPitchScale() { return RubberBandNativeMethods.RubberBandStretcher_GetPitchScale(_rbs); } public long GetLatency() { return RubberBandNativeMethods.RubberBandStretcher_GetLatency(_rbs).ToInt64(); } public void SetTransientsOption(Options options) { RubberBandNativeMethods.RubberBandStretcher_SetTransientsOption(_rbs, (int)options); } public void SetDetectorOption(Options options) { RubberBandNativeMethods.RubberBandStretcher_SetDetectorOption(_rbs, (int)options); } public void SetPhaseOption(Options options) { RubberBandNativeMethods.RubberBandStretcher_SetPhaseOption(_rbs, (int)options); } public void SetFormantOption(Options options) { RubberBandNativeMethods.RubberBandStretcher_SetFormantOption(_rbs, (int)options); } public void SetPitchOption(Options options) { RubberBandNativeMethods.RubberBandStretcher_SetPitchOption(_rbs, (int)options); } public void SetExpectedInputDuration(long samples) { RubberBandNativeMethods.RubberBandStretcher_SetExpectedInputDuration(_rbs, new IntPtr(samples)); } public void SetMaxProcessSize(long samples) { RubberBandNativeMethods.RubberBandStretcher_SetMaxProcessSize(_rbs, new IntPtr(samples)); } public long GetSamplesRequired() { return RubberBandNativeMethods.RubberBandStretcher_GetSamplesRequired(_rbs).ToInt64(); } public void SetKeyFrameMap(SortedDictionary map) { var mappingData = new List(); foreach (var mapping in map) { mappingData.Add(new IntPtr(mapping.Key)); mappingData.Add(new IntPtr(mapping.Value)); } RubberBandNativeMethods.RubberBandStretcher_SetKeyFrameMap(_rbs, mappingData.ToArray(), mappingData.Count / 2); } float[] _studyBuffer = new float[1024]; public void Study(float[][] input, bool final) { for (int i = 1; i < input.Length; i++) if (input[i].Length != input[0].Length) throw new Exception("Invalid input data: Not all channels have the same number of samples"); Study(input, input[0].Length, final); } public void Study(float[][] input, long samples, bool final) { for (int i = 0; i < input.Length; i++) if (input[i].Length < samples) throw new Exception("Invalid input data: Channel " + i + " does not have " + samples + " samples of data"); long totalSamples = input.Length * samples; if (totalSamples > _studyBuffer.Length) _studyBuffer = new float[totalSamples]; for (int i = 0; i < input.Length; i++) Array.Copy(input[i], 0, _studyBuffer, i * samples, samples); RubberBandNativeMethods.RubberBandStretcher_Study(_rbs, _studyBuffer, new IntPtr(samples), input.Length, final); } float[] _processBuffer = new float[1024]; public void Process(float[][] input, bool final) { for (int i = 1; i < input.Length; i++) if (input[i].Length != input[0].Length) throw new Exception("Invalid input data: Not all channels have the same number of samples"); Process(input, input[0].Length, final); } public void Process(float[][] input, long samples, bool final) { for (int i = 0; i < input.Length; i++) if (input[i].Length < samples) throw new Exception("Invalid input data: Channel " + i + " does not have " + samples + " samples of data"); long totalSamples = input.Length * samples; if (totalSamples > _processBuffer.Length) _processBuffer = new float[totalSamples]; for (int i = 0; i < input.Length; i++) Array.Copy(input[i], 0, _processBuffer, i * samples, samples); RubberBandNativeMethods.RubberBandStretcher_Process(_rbs, _processBuffer, new IntPtr(samples), input.Length, final); } public int Available() { return RubberBandNativeMethods.RubberBandStretcher_Available(_rbs); } float[] _outputBuffer = new float[1024]; public long Retrieve(float[][] output) { for (int i = 1; i < output.Length; i++) if (output[i].Length != output[0].Length) throw new Exception("Invalid output buffer: Not all channels have the same number of samples"); return Retrieve(output, output[0].Length); } public long Retrieve(float[][] output, long samples) { for (int i = 0; i < output.Length; i++) if (output[i].Length < samples) throw new Exception("Invalid output buffer: Channel " + i + " does not have enough space for " + samples + " samples"); long totalSamples = output.Length * samples; if (totalSamples > _outputBuffer.Length) _outputBuffer = new float[totalSamples]; long actualSamples = RubberBandNativeMethods.RubberBandStretcher_Retrieve(_rbs, _outputBuffer, new IntPtr(samples), output.Length).ToInt64(); for (int i = 0; i < output.Length; i++) Array.Copy(_outputBuffer, i * samples, output[i], 0, actualSamples); return actualSamples; } public float GetFrequencyCutoff(int n) { return RubberBandNativeMethods.RubberBandStretcher_GetFrequencyCutoff(_rbs, n); } public void SetFrequencyCutoff(int n, float f) { RubberBandNativeMethods.RubberBandStretcher_SetFrequencyCutoff(_rbs, n, f); } public long GetInputIncrement() { return RubberBandNativeMethods.RubberBandStretcher_GetInputIncrement(_rbs).ToInt64(); } public int[] GetOutputIncrements() { long numberOfIncrements = RubberBandNativeMethods.RubberBandStretcher_GetOutputIncrements(_rbs, null, IntPtr.Zero).ToInt64(); int[] buffer = new int[numberOfIncrements]; RubberBandNativeMethods.RubberBandStretcher_GetOutputIncrements(_rbs, buffer, new IntPtr(buffer.Length)); return buffer; } public float[] GetPhaseResetCurve() { long numberOfCurveElements = RubberBandNativeMethods.RubberBandStretcher_GetPhaseResetCurve(_rbs, null, IntPtr.Zero).ToInt64(); float[] buffer = new float[numberOfCurveElements]; RubberBandNativeMethods.RubberBandStretcher_GetPhaseResetCurve(_rbs, buffer, new IntPtr(buffer.Length)); return buffer; } public int[] GetExactTimePoints() { long numberOfTimePoints = RubberBandNativeMethods.RubberBandStretcher_GetExactTimePoints(_rbs, null, IntPtr.Zero).ToInt64(); int[] buffer = new int[numberOfTimePoints]; RubberBandNativeMethods.RubberBandStretcher_GetExactTimePoints(_rbs, buffer, new IntPtr(buffer.Length)); return buffer; } public long GetChannelCount() { return RubberBandNativeMethods.RubberBandStretcher_GetChannelCount(_rbs).ToInt64(); } public void CalculateStretch() { RubberBandNativeMethods.RubberBandStretcher_CalculateStretch(_rbs); } public void SetDebugLevel(int level) { RubberBandNativeMethods.RubberBandStretcher_SetDebugLevel(_rbs, level); } public static void SetDefaultDebugLevel(int level) { RubberBandNativeMethods.RubberBandStretcher_SetDefaultDebugLevel(level); } } }