346 lines
10 KiB
C#
346 lines
10 KiB
C#
/*
|
|
rubberband-sharp
|
|
|
|
A consumer of the rubberband DLL that wraps the RubberBandStretcher
|
|
object exposed by the DLL in a managed .NET type.
|
|
|
|
Copyright 2018-2019 Jonathan Gilbert
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use, copy,
|
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
|
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Jonathan Gilbert
|
|
shall not be used in advertising or otherwise to promote the sale,
|
|
use or other dealings in this Software without prior written
|
|
authorization.
|
|
*/
|
|
|
|
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<long, long> map)
|
|
{
|
|
var mappingData = new List<IntPtr>();
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|