public void TestReverse() { //Arrange var test = _signal.Copy(); var reversed = _signal.Copy().Samples; Array.Reverse(reversed); //Act test.Reverse(); //Assert Assert.That(test.Samples, Is.EqualTo(reversed)); }
/// <summary> /// Decimation preceded by low-pass filtering /// </summary> /// <param name="signal"></param> /// <param name="factor"></param> /// <param name="filter"></param> /// <returns></returns> public DiscreteSignal Decimate(DiscreteSignal signal, int factor, FirFilter filter = null) { if (factor == 1) { return(signal.Copy()); } var filterSize = factor > MinResamplingFilterOrder / 2 ? 2 * factor + 1 : MinResamplingFilterOrder; var lpFilter = filter; if (filter == null) { lpFilter = DesignFilter.FirLp(filterSize, 0.5f / factor); signal = lpFilter.ApplyTo(signal); } var output = new float[signal.Length / factor]; var pos = 0; for (var i = 0; i < output.Length; i++) { output[i] = signal[pos]; pos += factor; } return(new DiscreteSignal(signal.SamplingRate / factor, output)); }
/// <summary> /// Interpolation followed by low-pass filtering /// </summary> /// <param name="signal"></param> /// <param name="factor"></param> /// <param name="filter"></param> /// <returns></returns> public DiscreteSignal Interpolate(DiscreteSignal signal, int factor, FirFilter filter = null) { if (factor == 1) { return(signal.Copy()); } var output = new float[signal.Length * factor]; var pos = 0; for (var i = 0; i < signal.Length; i++) { output[pos] = factor * signal[i]; pos += factor; } var lpFilter = filter; if (filter == null) { var filterSize = factor > MinResamplingFilterOrder / 2 ? 2 * factor + 1 : MinResamplingFilterOrder; lpFilter = DesignFilter.FirLp(filterSize, 0.5f / factor); } return(lpFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * factor, output))); }
/// <summary> /// Time stretching with auto-derived parameters /// </summary> /// <param name="signal">Signal</param> /// <param name="stretch">Stretch factor (ratio)</param> /// <param name="algorithm">Algorithm for TSM (optional)</param> /// <returns>Time stretched signal</returns> public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, TsmAlgorithm algorithm = TsmAlgorithm.PhaseVocoderPhaseLocking) { if (Math.Abs(stretch - 1.0) < 1e-10) { return(signal.Copy()); } IFilter stretchFilter; var frameSize = MathUtils.NextPowerOfTwo(1024 * signal.SamplingRate / 16000); switch (algorithm) { case TsmAlgorithm.PhaseVocoder: stretchFilter = new PhaseVocoder(stretch, frameSize / 10, frameSize); break; case TsmAlgorithm.PhaseVocoderPhaseLocking: stretchFilter = new PhaseLockingVocoder(stretch, frameSize / 8, frameSize); break; case TsmAlgorithm.PaulStetch: stretchFilter = new PaulStretch(stretch, frameSize / 10, frameSize * 4); break; default: stretchFilter = new Wsola(stretch); break; } return(stretchFilter.ApplyTo(signal, FilteringMethod.Auto)); }
/// <summary> /// Time stretching /// </summary> /// <param name="signal">Signal</param> /// <param name="stretch">Stretch factor (scale)</param> /// <param name="fftSize">Size of FFT</param> /// <param name="hopSize">Hop size</param> /// <param name="algorithm">Algorithm for TSM</param> /// <returns></returns> public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, int fftSize = 1024, int hopSize = -1, TsmAlgorithm algorithm = TsmAlgorithm.Wsola) { if (Math.Abs(stretch - 1.0) < 1e-10) { return(signal.Copy()); } var hopAnalysis = hopSize > 0 ? hopSize : fftSize / 4; IFilter stretchFilter; switch (algorithm) { case TsmAlgorithm.PhaseVocoder: stretchFilter = new PhaseVocoder(stretch, hopAnalysis, fftSize); break; default: stretchFilter = new Wsola(stretch, fftSize); break; } return(stretchFilter.ApplyTo(signal, FilteringOptions.Auto)); }
/// <summary> /// Time stretching with parameters set by user /// </summary> /// <param name="signal">Signal</param> /// <param name="stretch">Stretch factor (ratio)</param> /// <param name="windowSize">Window size (for vocoders - FFT size)</param> /// <param name="hopSize">Hop size</param> /// <param name="algorithm">Algorithm for TSM (optional)</param> /// <returns>Time stretched signal</returns> public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, int windowSize, int hopSize, TsmAlgorithm algorithm = TsmAlgorithm.PhaseVocoderPhaseLocking) { if (Math.Abs(stretch - 1.0) < 1e-10) { return(signal.Copy()); } IFilter stretchFilter; switch (algorithm) { case TsmAlgorithm.PhaseVocoder: stretchFilter = new PhaseVocoder(stretch, hopSize, windowSize); break; case TsmAlgorithm.PhaseVocoderPhaseLocking: stretchFilter = new PhaseLockingVocoder(stretch, hopSize, windowSize); break; case TsmAlgorithm.PaulStetch: stretchFilter = new PaulStretch(stretch, hopSize, windowSize); break; default: stretchFilter = new Wsola(stretch, windowSize, hopSize); break; } return(stretchFilter.ApplyTo(signal, FilteringMethod.Auto)); }
/// <summary> /// Changes RMS relatively to input <paramref name="signal"/>. /// </summary> /// <param name="signal">Signal</param> /// <param name="rmsDb">RMS change in decibels, e.g. -6dB - decrease RMS twice</param> public static DiscreteSignal ChangeRms(DiscreteSignal signal, double rmsDb) { var modified = signal.Copy(); ChangeRms(modified.Samples, rmsDb); return(modified); }
/// <summary> /// Normalizes RMS. /// </summary> /// <param name="signal">Signal</param> /// <param name="rmsDb">RMS in decibels (dBFS), e.g. -6dB, -18dB, -26dB, etc.</param> public static DiscreteSignal NormalizeRms(DiscreteSignal signal, double rmsDb) { var normalized = signal.Copy(); NormalizeRms(normalized.Samples, rmsDb); return(normalized); }
/// <summary> /// Normalizes peak level. /// </summary> /// <param name="signal">Signal</param> /// <param name="peakDb">Peak level in decibels (dBFS), e.g. -1dB, -3dB, etc.</param> public static DiscreteSignal NormalizePeak(DiscreteSignal signal, double peakDb) { var normalized = signal.Copy(); NormalizePeak(normalized.Samples, peakDb); return(normalized); }
/// <summary> /// Time stretching /// </summary> /// <param name="signal"></param> /// <param name="stretch"></param> /// <param name="fftSize"></param> /// <param name="hopSize"></param> /// <returns></returns> public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, int fftSize = 1024, int hopSize = -1) { if (Math.Abs(stretch - 1.0) < 1e-10) { return(signal.Copy()); } var hopAnalysis = hopSize > 0 ? hopSize : fftSize / 4; var hopSynthesis = (int)(hopAnalysis * stretch); var vocoder = new PhaseVocoder(hopAnalysis, hopSynthesis, fftSize); return(vocoder.ApplyTo(signal)); }
/// <summary> /// Simple resampling as the combination of interpolation and decimation. /// </summary> /// <param name="signal"></param> /// <param name="up"></param> /// <param name="down"></param> /// <param name="filter"></param> /// <returns></returns> public DiscreteSignal ResampleUpDown(DiscreteSignal signal, int up, int down, FirFilter filter = null) { if (up == down) { return(signal.Copy()); } var newSamplingRate = signal.SamplingRate * up / down; if (up > 20 && down > 20) { return(Resample(signal, newSamplingRate, filter)); } var output = new float[signal.Length * up]; var pos = 0; for (var i = 0; i < signal.Length; i++) { output[pos] = up * signal[i]; pos += up; } var lpFilter = filter; if (filter == null) { var factor = Math.Max(up, down); var filterSize = factor > MinResamplingFilterOrder / 2 ? 8 * factor + 1 : MinResamplingFilterOrder; lpFilter = DesignFilter.FirLp(filterSize, 0.5f / factor); } var upsampled = lpFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * up, output)); output = new float[upsampled.Length / down]; pos = 0; for (var i = 0; i < output.Length; i++) { output[i] = upsampled[pos]; pos += down; } return(new DiscreteSignal(newSamplingRate, output)); }
/// <summary> /// Simple resampling (as the combination of interpolation and decimation). /// </summary> /// <param name="signal"></param> /// <param name="newSamplingRate"></param> /// <returns></returns> public static DiscreteSignal Resample(DiscreteSignal signal, int newSamplingRate) { if (newSamplingRate == signal.SamplingRate) { return(signal.Copy()); } var gcd = MathUtils.Gcd(signal.SamplingRate, newSamplingRate); var up = newSamplingRate / gcd; var down = signal.SamplingRate / gcd; if (up > 20 && down > 20) { return(ResampleUpDown(signal, up, down)); } var output = new float[signal.Length * up]; var pos = 0; for (var i = 0; i < signal.Length; i++) { output[pos] = up * signal[i]; pos += up; } var factor = Math.Max(up, down); var filterSize = factor > MinResamplingFilterOrder / 2 ? 8 * factor + 1 : MinResamplingFilterOrder; var lpFilter = DesignFilter.FirLp(filterSize, 0.5f / factor); var upsampled = lpFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * up, output)); output = new float[upsampled.Length / down]; pos = 0; for (var i = 0; i < output.Length; i++) { output[i] = upsampled[pos]; pos += down; } return(new DiscreteSignal(newSamplingRate, output)); }
/// <summary> /// Band-limited resampling /// </summary> /// <param name="signal"></param> /// <param name="newSamplingRate"></param> /// <param name="filter"></param> /// <param name="order"></param> /// <returns></returns> public DiscreteSignal Resample(DiscreteSignal signal, int newSamplingRate, FirFilter filter = null, int order = 15) { if (signal.SamplingRate == newSamplingRate) { return(signal.Copy()); } var g = (float)newSamplingRate / signal.SamplingRate; var input = signal.Samples; var output = new float[(int)(input.Length * g)]; if (g < 1 && filter == null) { filter = new FirFilter(DesignFilter.FirWinLp(MinResamplingFilterOrder, g / 2)); input = filter.ApplyTo(signal).Samples; } var step = 1 / g; for (var n = 0; n < output.Length; n++) { var x = n * step; for (var i = -order; i < order; i++) { var j = (int)Math.Floor(x) - i; if (j < 0 || j >= input.Length) { continue; } var t = x - j; float w = (float)(0.5 * (1.0 + Math.Cos(t / order * Math.PI))); // Hann window float sinc = (float)MathUtils.Sinc(t); // Sinc function output[n] += w * sinc * input[j]; } } return(new DiscreteSignal(newSamplingRate, output)); }
/// <summary> /// Method implements tube distortion effect /// </summary> /// <param name="signal"></param> /// <param name="filteringOptions"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringOptions filteringOptions = FilteringOptions.Auto) { var maxAmp = signal.Samples.Max(s => Math.Abs(s)); if (Math.Abs(maxAmp) < 1e-10) { return(signal.Copy()); } IEnumerable <float> tempZ; if (Math.Abs(Q) < 1e-10) { tempZ = signal.Samples.Select(s => { var q = Gain * s / maxAmp; return(Math.Abs(q - Q) < 1e-10 ? 1.0f / Dist : (float)(q / (1 - Math.Exp(-Dist * q)))); }); } else { tempZ = signal.Samples.Select(s => { var q = Gain * s / maxAmp; return(Math.Abs(q - Q) < 1e-10 ? (float)(1.0 / Dist + Q / (1 - Math.Exp(Dist * Q))) : (float)((q - Q) / (1 - Math.Exp(-Dist * (q - Q))) + Q / (1 - Math.Exp(Dist * Q)))); }); } var maxZ = tempZ.Max(z => Math.Abs(z)); var tempY = tempZ.Zip(signal.Samples, (z, x) => Mix * z * maxAmp / maxZ + (1 - Mix) * x); var maxY = tempY.Max(y => Math.Abs(y)); var output = tempY.Select(y => y * maxAmp / maxY); return(_outputFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate, output))); }
/// <summary> /// Method implements simple distortion effect /// </summary> /// <param name="signal"></param> /// <param name="filteringOptions"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringOptions filteringOptions = FilteringOptions.Auto) { var maxAmp = signal.Samples.Max(s => Math.Abs(s)); if (Math.Abs(maxAmp) < 1e-8) { return(signal.Copy()); } var tempZ = signal.Samples.Select(s => { var q = Gain * s / maxAmp; return(Math.Sign(q) * (1 - Math.Exp(-Math.Abs(q)))); }); var maxZ = tempZ.Max(z => Math.Abs(z)); var tempY = tempZ.Zip(signal.Samples, (z, x) => Mix * z * maxAmp / maxZ + (1 - Mix) * x); var maxY = tempY.Max(y => Math.Abs(y)); var output = tempY.Select(y => (float)(y * maxAmp / maxY)); return(new DiscreteSignal(signal.SamplingRate, output)); }