public static IEnumerable<float[]> DownSample(IEnumerable<float[]> samplesEnumerator, int numChannels, int windowSize, Action<float[]> highSampleCallback)
        {
            var fft = new MathNet.Numerics.Transformations.ComplexFourierTransformation ();

            var determineVolumeSource = new Complex[windowSize];
            for (var sampleCtr = 0; sampleCtr < windowSize; sampleCtr++)
                determineVolumeSource[sampleCtr] = new Complex(1, 0);

            fft.TransformForward(determineVolumeSource);

            var determineVolumeDestination = new Complex[]
            {
                determineVolumeSource[0],
                Complex.FromModulusArgument(0,0),
                Complex.FromModulusArgument(0,0),
                Complex.FromModulusArgument(0,0)
            };

            fft.TransformBackward(determineVolumeDestination);

            var dcMultiplier = determineVolumeDestination[0].Modulus;

            var ratio = (1d / Convert.ToDouble(windowSize)) * 2d * Math.PI;
            for (var sampleCtr = 0; sampleCtr < windowSize; sampleCtr++)
            {
                var sampleCtrDouble = Convert.ToDouble(sampleCtr);
                determineVolumeSource[sampleCtr] = new Complex(Math.Cos(sampleCtrDouble * ratio), 0);
            }

            fft.TransformForward(determineVolumeSource);

            determineVolumeDestination = new Complex[]
            {
                Complex.FromModulusArgument(0,0),
                determineVolumeSource[1],
                Complex.FromModulusArgument(0,0),
                determineVolumeSource[determineVolumeSource.Length - 1],
            };

            fft.TransformBackward(determineVolumeDestination);

            var midMultiplier = determineVolumeDestination.Select(s => s.Modulus).Max();

            /*// This only works when windowsize is 8
            for (var sampleCtr = 0; sampleCtr < windowSize; sampleCtr++)
            {
                var abs = sampleCtr % 2 == 1 ? 1 : 0;
                var sign = sampleCtr % 4 > 1 ? 1 : -1;
                determineVolumeSource[sampleCtr] = new Complex(abs * sign, 0);
            }*/

            ratio = (1d / Convert.ToDouble(windowSize)) * 4d * Math.PI;
            for (var sampleCtr = 0; sampleCtr < windowSize; sampleCtr++)
            {
                var sampleCtrDouble = Convert.ToDouble(sampleCtr);
                determineVolumeSource[sampleCtr] = new Complex(Math.Cos(sampleCtrDouble * ratio), 0);
            }

            fft.TransformForward(determineVolumeSource);

            determineVolumeDestination = new Complex[]
            {
                Complex.FromModulusArgument(0,0),
                Complex.FromModulusArgument(0,0),
                determineVolumeSource[2],
                Complex.FromModulusArgument(0,0),
            };

            fft.TransformBackward(determineVolumeDestination);

            var highMultiplier = determineVolumeDestination.Select(s => s.Modulus).Max();

            var sampleQueue = new Queue<float[]>();
            for (var sampleCtr = 0; sampleCtr < windowSize / 4; sampleCtr++)
                sampleQueue.Enqueue(new float[numChannels]);

            var maxOriginalSample = 0.0;
            var maxLowFrequencySample = 0.0;

            using (var sampleEnumerator = samplesEnumerator.GetEnumerator())
            {
                int samplesFromSource;

                do
                {
                    samplesFromSource = 0;

                    while (sampleQueue.Count < windowSize)
                    {
                        if (sampleEnumerator.MoveNext())
                        {
                            sampleQueue.Enqueue(sampleEnumerator.Current.ToArray());
                            samplesFromSource++;
                        }
                        else
                            sampleQueue.Enqueue(new float[numChannels]);
                    }

                    var simplified = new float[][]
                    {
                        new float[numChannels],
                        new float[numChannels]
                    };

                    var filtered = new float[windowSize / 2][];
                    for (var sampleCtr = 0; sampleCtr < windowSize / 2; sampleCtr++)
                        filtered[sampleCtr] = new float[numChannels];

                    for (var channelCtr = 0; channelCtr < numChannels; channelCtr++)
                    {
                        maxOriginalSample = Math.Max(
                            maxOriginalSample,
                            sampleQueue.Select(s => Math.Abs(s[channelCtr])).Max());

                        var samplesToTransform = sampleQueue.Select (s => new Complex (s [channelCtr], 0)).ToArray ();

                        fft.TransformForward (samplesToTransform);

                        var samplesToTransformBack = new Complex[]
                        {
                            samplesToTransform[0],
                            samplesToTransform[1],
                            samplesToTransform[2],
                            samplesToTransform[samplesToTransform.Length - 1]
                        };

                        samplesToTransformBack[0].Modulus /= dcMultiplier;
                        samplesToTransformBack[1].Modulus /= midMultiplier;
                        samplesToTransformBack[2].Modulus /= highMultiplier;
                        samplesToTransformBack[3].Modulus /= midMultiplier;

                        fft.TransformBackward(samplesToTransformBack);

                        simplified[0][channelCtr] = Convert.ToSingle(samplesToTransformBack[1].Real);
                        simplified[1][channelCtr] = Convert.ToSingle(samplesToTransformBack[2].Real);

                        for (var filterCtr = 3; filterCtr < (samplesToTransform.Length - 1); filterCtr++)
                            samplesToTransform[filterCtr] = Complex.FromModulusArgument(0, 0);

                        fft.TransformBackward(samplesToTransform);
                        for (var sampleCtr = 0; sampleCtr < windowSize / 2; sampleCtr++)
                            filtered[sampleCtr][channelCtr] = Convert.ToSingle(samplesToTransform[sampleCtr + (windowSize / 4)].Real);
                    }

                    maxLowFrequencySample = Math.Max(
                        maxLowFrequencySample,
                        simplified.SelectMany(s => s).Select(Math.Abs).Max());

                    foreach (var samples in simplified)
                        yield return samples;

                    foreach (var samples in filtered)
                        highSampleCallback(samples);

                    while (sampleQueue.Count > windowSize / 2)
                        sampleQueue.Dequeue();

                } while (samplesFromSource > (windowSize / 4));
            }

            /*Console.WriteLine("Loudest sample in source: {0}", maxOriginalSample);
            Console.WriteLine("Loudest sample in low frequency: {0}", maxLowFrequencySample);
            //Console.WriteLine("Loudest sample in high frequency: {0}", maxHighFrequencySample);
            Console.WriteLine();*/
        }
Beispiel #2
0
        private static TableInfo ConvertTable(float[][] wavetable, Dictionary<int, int> noteToPartials)
        {
            var partials = noteToPartials.Select(x => x.Value).Distinct().OrderByDescending(x => x).ToList();
            var transform = new Transform(wavetable.First().Length);

            var output = new TableInfo();
            output.MidiToTable = new int[128];
            output.RenderedTable = new float[partials.Count][][];

            Action<int, Complex[]> LimitPartials = (max, data) =>
            {
                for (int i = 0; i < data.Length; i++)
                {
                    if (i > max && (data.Length - i) > max)
                    {
                        data[i].Real = 0;
                        data[i].Imag = 0;
                    }
                }
            };

            for (int partialIndex = 0; partialIndex < partials.Count; partialIndex++)
            {
                var partial = partials[partialIndex];
                noteToPartials.Where(x => x.Value == partial).Select(x => x.Key).Foreach(x => output.MidiToTable[x] = partialIndex);
                output.RenderedTable[partialIndex] = new float[wavetable.Length][];

                for (int tableIndex = 0; tableIndex < wavetable.Length; tableIndex++)
                {
                    var table = wavetable[tableIndex];
                    var input = table.Select(x => new Complex(x, 0)).ToArray();
                    var fft = new Complex[input.Length];
                    var ifft = new Complex[input.Length];
                    transform.FFT(input, fft);
                    LimitPartials(partial, fft);

                    transform.IFFT(fft, ifft);
                    var signal = ifft.Select(x => (float)x.Real).ToArray();

                    output.RenderedTable[partialIndex][tableIndex] = signal;
                }
            }

            return output;
        }