Beispiel #1
0
        /// <summary>
        /// Simulates a realtime audio stream by generating a sine wave in realtime and ingesting it into the FIFO buffer.
        /// </summary>
        private static void StartSineWaveRealtimeGenerator(AudioProperties audioProperties, IAudioWriterStream targetStream)
        {
            // Create a stream that generates 1 second of a sine wave
            var sineWaveStream = new SineGeneratorStream(audioProperties.SampleRate, 440, new TimeSpan(0, 0, 1));

            // Store the sine wave in a buffer
            // We can concatenate this buffer over and over again to create an infinitely long sine wave
            var sineWaveBuffer = new byte[sineWaveStream.Length];
            var bytesRead      = sineWaveStream.Read(sineWaveBuffer, 0, sineWaveBuffer.Length);

            if (bytesRead < sineWaveBuffer.Length)
            {
                throw new Exception("incomplete buffer read");
            }

            Task.Factory.StartNew(() =>
            {
                // Each realtime second, write the 1-second sine wave to the target stream to
                // simulate an infinitely long realtime sine wave stream.
                //
                // For low-latency processing use-cases, writes would ideally be shorter and happen
                // more frequently to keep the delay between input and output of the FIFO stream
                // as low as possible.
                while (true)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("Writing 1 second into buffer");
                    targetStream.Write(sineWaveBuffer, 0, sineWaveBuffer.Length);
                    _dataGenerated += sineWaveBuffer.Length;
                }
            });
        }
Beispiel #2
0
        public void MonoOverlapAddRectangle()
        {
            var properties   = new AudioProperties(1, 44100, 32, AudioFormat.IEEE);
            var sourceStream = new SineGeneratorStream(44100, 440, TimeSpan.FromSeconds(1));
            var targetStream = new MemoryWriterStream(new System.IO.MemoryStream(), properties);

            // Rectangular window (samples unchanged) with no overlap should just reconstruct the original stream
            int windowSize = 100;
            int hopSize    = 100;
            var window     = WindowType.Rectangle;

            var sw  = new StreamWindower(sourceStream, windowSize, hopSize, window);
            var ola = new OLA(targetStream, windowSize, hopSize);

            var frameBuffer = new float[windowSize];

            while (sw.HasNext())
            {
                sw.ReadFrame(frameBuffer);
                ola.WriteFrame(frameBuffer);
            }
            ola.Flush();

            Assert.AreEqual(sourceStream.Length, targetStream.Length);

            // Validate ola target stream content
            sourceStream.Position = targetStream.Position = 0;
            long similarFloats = StreamUtil.CompareFloats(sourceStream, targetStream);

            Assert.AreEqual(sourceStream.Length / sourceStream.SampleBlockSize, similarFloats);
        }
        public void CheckMultiSourceRandomSeeking()
        {
            var stream1 = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var stream2 = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var cc      = new ConcatenationStream(stream1, stream2);

            int seekOffset = 200 * stream1.SampleBlockSize;

            // Check seek into first segment
            cc.Position = seekOffset;
            Assert.AreEqual(seekOffset, cc.Position);
            var bytesRead = StreamUtil.ReadAllAndCount(cc);

            Assert.AreEqual(cc.Length - seekOffset, bytesRead);

            // Check seek into second segment
            cc.Position = stream1.Length + seekOffset;
            Assert.AreEqual(stream1.Length + seekOffset, cc.Position);
            bytesRead = StreamUtil.ReadAllAndCount(cc);
            Assert.AreEqual(cc.Length - stream1.Length - seekOffset, bytesRead);

            // Check seek back into first segment
            cc.Position = seekOffset;
            Assert.AreEqual(seekOffset, cc.Position);
            bytesRead = StreamUtil.ReadAllAndCount(cc);
            Assert.AreEqual(cc.Length - seekOffset, bytesRead);
        }
        public void CheckMultiSourceSeekToEnd()
        {
            var stream1 = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var stream2 = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var cc      = new ConcatenationStream(stream1, stream2);

            // Seek to the end where zero bytes are expected to be read
            cc.Position = cc.Length;
            var bytesRead = StreamUtil.ReadAllAndCount(cc);

            Assert.AreEqual(0, bytesRead);
        }
        public void CheckSeek()
        {
            var sine = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));

            int seek = 100 * 4;

            sine.Position = seek;
            Assert.AreEqual(sine.Position, seek);

            long bytesRead = StreamUtil.ReadAllAndCount(sine);

            Assert.AreEqual(sine.Length - seek, bytesRead);
        }
        public void CheckSingleSourceLength()
        {
            var stream = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var cc     = new ConcatenationStream(stream);

            // Ensure that the lengths match
            Assert.AreEqual(stream.Length, cc.Length);

            var bytesRead = StreamUtil.ReadAllAndCount(cc);

            // Ensure that the read bytes equals the length
            Assert.AreEqual(stream.Length, bytesRead);
        }
Beispiel #7
0
        public void MonoOverlapAddHann()
        {
            var                properties   = new AudioProperties(1, 44100, 32, AudioFormat.IEEE);
            IAudioStream       sourceStream = new SineGeneratorStream(44100, 440, TimeSpan.FromSeconds(1));
            IAudioWriterStream targetStream = new MemoryWriterStream(new System.IO.MemoryStream(), properties);

            // 50% overlap with a Hann window is an optimal combination, Hann window is optimally uneven with a 1 as middle point
            int windowSize = 21;
            int hopSize    = 11;
            var window     = WindowType.Hann;

            // Adjust source length to window/hop size so no samples remain at the end
            // (a potential last incomplete frame is not returned by the stream windower)
            // With remaining samples, the source and target length cannot be compared
            sourceStream = new CropStream(sourceStream, 0,
                                          sourceStream.Length
                                          - (sourceStream.Length - windowSize * sourceStream.SampleBlockSize)
                                          % (hopSize * sourceStream.SampleBlockSize));

            var sw  = new StreamWindower(sourceStream, windowSize, hopSize, window);
            var ola = new OLA(targetStream, windowSize, hopSize);

            var frameBuffer = new float[windowSize];

            while (sw.HasNext())
            {
                sw.ReadFrame(frameBuffer);
                ola.WriteFrame(frameBuffer);
            }
            ola.Flush();

            Assert.AreEqual(sourceStream.Length, targetStream.Length);

            // Validate ola target stream content
            // Crop the streams to ignore windowed start and end when no overlap-add is performed and content definitely differs

            var sourceStreamCropped = new CropStream(sourceStream,
                                                     hopSize * sourceStream.SampleBlockSize * 2,
                                                     sourceStream.Length - hopSize * sourceStream.SampleBlockSize);

            var targetStreamCropped = new CropStream(targetStream,
                                                     hopSize * sourceStream.SampleBlockSize * 2,
                                                     sourceStream.Length - hopSize * sourceStream.SampleBlockSize);

            sourceStreamCropped.Position = targetStreamCropped.Position = 0;

            long similarFloats = StreamUtil.CompareFloats(sourceStreamCropped, targetStreamCropped);

            Assert.AreEqual(sourceStreamCropped.Length / sourceStreamCropped.SampleBlockSize, similarFloats);
        }
        public void CheckLength()
        {
            int  sampleRate     = 44100;
            int  seconds        = 1;
            long expectedLength = sampleRate * seconds * 4; // 4 bytes per 32bit ieee sample

            var sine = new SineGeneratorStream(sampleRate, 440, new TimeSpan(0, 0, seconds));

            Assert.AreEqual(expectedLength, sine.Length);

            long bytesRead = StreamUtil.ReadAllAndCount(sine);

            Assert.AreEqual(expectedLength, bytesRead);
        }
        public void CheckSingleSourceSeeking()
        {
            var stream = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
            var cc     = new ConcatenationStream(stream);

            int seek = 200 * stream.SampleBlockSize;

            cc.Position = seek;

            // Ensure that the position is correct
            Assert.AreEqual(seek, cc.Position);

            var bytesRead = StreamUtil.ReadAllAndCount(cc);

            // Ensure that the expected remaining bytes match the seek position
            Assert.AreEqual(cc.Length - seek, bytesRead);
        }
Beispiel #10
0
        public void TransformForwardAndBackward()
        {
            int size     = 4096;
            var pffft    = new PFFFT(size, Transform.Real);
            var dataIn   = new float[size];
            var dataOut  = new float[size];
            var dataBack = new float[size];

            var generator = new SineGeneratorStream(4096, 100, TimeSpan.FromSeconds(1));

            generator.Read(dataIn, 0, size);

            pffft.Forward(dataIn, dataOut);
            pffft.Backward(dataOut, dataBack);

            CollectionAssert.AreEqual(dataIn, dataBack, new FFTComparer());
        }
Beispiel #11
0
        /// <summary>
        /// Calculates a normalization offset for the current window function. Every window function leads to
        /// a different FFT result peak value, and since the peak value of each windowed FFT means 0dB, this
        /// offset can be used to adjust the calculated dB values.
        /// </summary>
        private void CalculateWindowFunctionNormalizationOffset()
        {
            if (windowFunction == null)
            {
                windowFunctionNormalizationDecibelOffset = 0;
            }
            else
            {
                SineGeneratorStream sine   = new SineGeneratorStream(1024, 16, new TimeSpan(0, 0, 1));
                float[]             input  = new float[WindowSize];
                float[]             output = new float[input.Length / 2];
                WindowFunction      wf     = WindowUtil.GetFunction(windowFunction.Type, input.Length);

                sine.Read(input, 0, input.Length);
                wf.Apply(input);
                fft.Forward(input);
                int   maxIndex = FFTUtil.Results(input, output);
                float maxValue = output[maxIndex];
                windowFunctionNormalizationDecibelOffset = 1f - maxValue;
            }
        }
 public void CheckMultiSourceMatchingFormat()
 {
     var stream1 = new SineGeneratorStream(44100, 440, new TimeSpan(0, 0, 1));
     var stream2 = new SineGeneratorStream(96000, 440, new TimeSpan(0, 0, 1));
     var cc      = new ConcatenationStream(stream1, stream2);
 }
Beispiel #13
0
        private void Generate(FFTLibrary fftLib)
        {
            // FFT resources:
            // http://www.mathworks.de/support/tech-notes/1700/1702.html
            // http://stackoverflow.com/questions/6627288/audio-spectrum-analysis-using-fft-algorithm-in-java
            // http://stackoverflow.com/questions/1270018/explain-the-fft-to-me
            // http://stackoverflow.com/questions/6666807/how-to-scale-fft-output-of-wave-file

            /*
             * FFT max value test:
             * 16Hz, 1024 samplerate, 512 samples, rectangle window
             * -> input min/max: -1/1
             * -> FFT max: 256
             * -> FFT result max: ~1
             * -> FFT dB result max: ~1
             *
             * source: "Windowing Functions Improve FFT Results, Part I"
             * => kann zum Normalisieren für Fensterfunktionen verwendet werden
             */

            float frequency       = float.Parse(frequencyTextBox.Text);
            int   ws              = (int)windowSize.Value;
            float frequencyFactor = ws / frequency;
            int   sampleRate      = int.Parse(sampleRateTextBox.Text);

            SineGeneratorStream sine = new SineGeneratorStream(sampleRate, frequency, new TimeSpan(0, 0, 1));

            float[] input = new float[ws];
            sine.Read(input, 0, ws);

            //// add second frequency
            //SineGeneratorStream sine2 = new SineGeneratorStream(44100, 700, new TimeSpan(0, 0, 1));
            //float[] input2 = new float[ws];
            //sine2.Read(input2, 0, ws);
            //for (int x = 0; x < input.Length; x++) {
            //    input[x] += input2[x];
            //    input[x] /= 2;
            //}

            //// add dc offset
            //for (int x = 0; x < input.Length; x++) {
            //    input[x] += 0.5f;
            //}

            inputGraph.Values = input;

            WindowFunction wf = WindowUtil.GetFunction((WindowType)windowTypes.SelectedValue, ws);

            float[] window = (float[])input.Clone();
            wf.Apply(window);

            input2Graph.Values = window;

            float[] fftIO = (float[])window.Clone();

            if (fftLib == FFTLibrary.ExocortexDSP)
            {
                // This function call indirection is needed to allow this method execute even when the
                // Exocortex FFT assembly is missing.
                ApplyExocortexDSP(fftIO);
            }
            else if (fftLib == FFTLibrary.FFTW)
            {
                FFTW.FFTW fftw = new FFTW.FFTW(fftIO.Length);
                fftw.Execute(fftIO);
            }
            else if (fftLib == FFTLibrary.PFFFT)
            {
                PFFFT.PFFFT pffft = new PFFFT.PFFFT(fftIO.Length, PFFFT.Transform.Real);
                pffft.Forward(fftIO, fftIO);
            }

            //// convert real input to complex input with im part set to zero
            //float[] fftIO = new float[ws * 2];
            //int i = 0;
            //for (int j = 0; j < window.Length; j++) {
            //    fftIO[i] = window[j];
            //    i += 2;
            //}
            //Fourier.FFT(fftIO, fftIO.Length / 2, FourierDirection.Forward);

            fftOutputGraph.Values = fftIO;


            float[] fftResult = new float[ws / 2];
            //FFTUtil.Results(fftIO, fftResult);

            // transform complex output to magnitudes
            float sum = 0;
            int   y   = 0;

            for (int x = 0; x < fftIO.Length; x += 2)   //  / 2
            {
                fftResult[y] = FFTUtil.CalculateMagnitude(fftIO[x], fftIO[x + 1]) / ws * 2;
                sum         += fftResult[y++];
            }
            //FFTUtil.Results(fftIO, fftResult);

            //// adjust values for the sum to become 1
            //float sum2 = 0;
            //for (int x = 0; x < fftResult.Length; x++) {
            //    fftResult[x] /= sum;
            //    sum2 += fftResult[x];
            //}
            //Debug.WriteLine("sum / sum2: {0} / {1}", sum, sum2);

            fftNormalizedOutputGraph.Values = fftResult;

            // convert magnitudes to decibel
            float[] fftResultdB = new float[fftResult.Length];
            //for (int x = 0; x < fftResult.Length; x++) {
            //    fftResultdB[x] = (float)VolumeUtil.LinearToDecibel(fftResult[x]);
            //}
            FFTUtil.Results(fftIO, fftResultdB);
            fftdBOutputGraph.Values = fftResultdB;

            summary.Text = String.Format(
                "input min/max: {0}/{1} -> window min/max: {2}/{3} -> fft min/max: {4}/{5} -> result min/max/bins/sum: {6}/{7}/{8}/{9} -> dB min/max: {10:0.000000}/{11:0.000000}",
                input.Min(), input.Max(),
                window.Min(), window.Max(),
                fftIO.Min(), fftIO.Max(),
                fftResult.Min(), fftResult.Max(), fftResult.Length, sum,
                fftResultdB.Min(), fftResultdB.Max());
        }