public const double HIGHPASS = 7000.0; //HZ public static double[] DistortionMask(int maskLength, double distortionStrength, WaveFormat format) { double lowpass, highpass; double nyquist = SoundProcessing.NyquistRate(format); lowpass = LOWPASS / (nyquist / SoundProcessing.Constants.FFT_LENGTH / 2.0); highpass = HIGHPASS / (nyquist / SoundProcessing.Constants.FFT_LENGTH / 2.0); double[] mask = new double[maskLength]; for (int i = 0; i < maskLength; ++i) { double rampDown = 1.0; if (i < lowpass) { rampDown = MathUtils.Clamp(Math.Sqrt(i), -distortionStrength, distortionStrength); } else if (i > highpass) { rampDown = MathUtils.Clamp(Math.Sqrt(i / 2), -distortionStrength, distortionStrength); } mask[i] = rampDown; } return(mask); }
public static double[] ApplyDistortion(double[] samples, double distortionStrength, WaveFormat fmt) { List <double> processedSamples = new List <double>(); //list containing overlapping sound frames List <double> savedTimeDomain = new List <double>(SoundProcessing.Constants.FFT_FRAME_LENGTH); SoundProcessing.PadZeroes(savedTimeDomain, SoundProcessing.Constants.FFT_FRAME_LENGTH);//pad zeroes to have required container size //build a filter mask specified for the distortion double[] filterMask = FilterMasks.DistortionMask( SoundProcessing.Constants.FREQUENCY_DOMAIN_LENGTH, distortionStrength, fmt ); for (int i = 0; i < samples.Length; i += SoundProcessing.Constants.FFT_FRAME_LENGTH) { //cut out a data frame from samples var data = samples.Slice(i, SoundProcessing.Constants.FFT_LENGTH); //prepare a buffer for FFT var complices = new Complex[SoundProcessing.Constants.FREQUENCY_DOMAIN_LENGTH]; //arrays containing the input samples, the result of FFT and the result of IFFT PinnedArray <double> input = new PinnedArray <double>(data); PinnedArray <Complex> frequencyDomain = new PinnedArray <Complex>(complices); PinnedArray <double> timeDomain = new PinnedArray <double>(data.Length); //convert time domain to frequency domain DFT.FFT(input, frequencyDomain); //mask the frequencies using a mask created earlier for (int j = 0; j < frequencyDomain.Length; ++j) { frequencyDomain[j] *= filterMask[j]; } //mask negative frequencies for (int j = frequencyDomain.Length - 1; j > frequencyDomain.Length / 2; --j) { frequencyDomain[j] *= filterMask[frequencyDomain.Length - j]; } //convert back to time domain DFT.IFFT(frequencyDomain, timeDomain); //overlapping of the frames for (int j = 0; j < SoundProcessing.Constants.OVERLAP; ++j) { timeDomain[j] *= (j + 0.0) / SoundProcessing.Constants.OVERLAP; timeDomain[j] += savedTimeDomain[j] * (1.0 - (j + 0.00) / (double)SoundProcessing.Constants.OVERLAP); //overlap with previous samples to avoid stuttering } //add overlapping sound samples savedTimeDomain.Clear(); for (int j = SoundProcessing.Constants.FFT_FRAME_LENGTH; j < timeDomain.Length; ++j) { savedTimeDomain.Add(MathUtils.Clamp(timeDomain[j], -1.0, 1.0)); } //add the actual samples for (int j = 0; j < SoundProcessing.Constants.FFT_FRAME_LENGTH; ++j) { processedSamples.Add(MathUtils.Clamp(timeDomain[j], -1.0, 1.0)); } //dispose of the PinnedArray objects input.Dispose(); frequencyDomain.Dispose(); timeDomain.Dispose(); } return(processedSamples.ToArray()); }