NAudio WaspiLoopback Goertzel

2019-08-27 08:26发布

问题:

Update #2:

I kept messing more with the code, my only issue is that it is detecting frequencies when sound is present through the microphone but the algorithm is not working at all. I ask it for 500Hz and it'd output a power level even tough there was NO sound at that frequency. Code below (I have seen that this post haven't had much attention, thus why I'm updating constantly)

    private void button3_Click(object sender, EventArgs e)
    {
        waveIn = new WasapiLoopbackCapture();
        waveIn.DataAvailable += OnDtAvailable;
        waveIn.StartRecording();
    }

    void OnDtAvailable(object sender, WaveInEventArgs e)
    {
        byte[] buffer = e.Buffer;
        int bytesRecorded = e.BytesRecorded;
        int bufferIncrement = waveIn.WaveFormat.BlockAlign;

        for (int index = 0; index < bytesRecorded; index += bufferIncrement)
        {
            float[] samples = new float[buffer.Length];
            Buffer.BlockCopy(buffer, index, samples, 0, bufferIncrement);

            double d1 = CalculateGoertzel(samples, 1336, 44100);
            double c2 = CalculateGoertzel(samples, 770, 48000);
            Console.WriteLine(index + ": Frequency 1336Hz - " + d1 + " Frequency 770Hz - " + c2);
        }
    }

    private static double CalculateGoertzel(float[] samples, double frequency, int sampleRate)
    {
        var normalizedFrequency = Math.Round(frequency * samples.Length / sampleRate);
        var w = (2.0 * Math.PI / samples.Length) * normalizedFrequency;
        var cosine = Math.Cos(w);
        var sine = Math.Sin(w);
        var coeff = 2.0 * cosine;

        var q0 = 0.0;
        var q1 = 0.0;
        var q2 = 0.0;

        foreach (var sample in samples)
        {
            q0 = coeff * q1 - q2 + sample;
            q2 = q1;
            q1 = q0;
        }

        return Math.Sqrt(q1 * q1 + q2 * q2 - q1 * q2 * coeff);
    }

Update #1:

I have been messing around with the code and have finally managed to get something that can work without any errors but it is VERY inaccurate, can anyone show me a better way to do it (code of the 'working' version is below)?

    private void button3_Click(object sender, EventArgs e)
    {
        waveIn = new WasapiLoopbackCapture();
        waveIn.DataAvailable += OnDataAvailable;
        waveIn.StartRecording();
    }

    void OnDataAvailable(object sender, WaveInEventArgs e)
    {
        byte[] buffer = e.Buffer;

        if ((CalculateGoertzel(buffer, 1633.0, 48000) > 84) && (CalculateGoertzel(buffer, 697.0, 48000) > 84))
        {
            DateTime dt = DateTime.Now;
            Console.WriteLine(dt.ToString("mm:ss:fff - ") + "a");
        }
    }

   public static double CalculateGoertzel(byte[] sample, double frequency, int samplerate)
    {
        double Skn, Skn1, Skn2;
        Skn = Skn1 = Skn2 = 0;
        for (int i = 0; i < sample.Length; i++)
        {
            Skn2 = Skn1;
            Skn1 = Skn;
            Skn = 2 * Math.Cos(2 * Math.PI * frequency / samplerate) * Skn1 - Skn2 + sample[i];
        }
        double WNk = Math.Exp(-2 * Math.PI * frequency / samplerate);
        return 20 * Math.Log10(Math.Abs((Skn - WNk * Skn1)));
    }

I am trying to create a solution which can get frequency power that is playing on a computer via its microphone (This is similar to DTMF as my project plans to have multiple frequencies too).

Below you will find the code I am trying this with which is giving the error message:

Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.

I can't seem to figure out why its giving an out of bounds error -- probably because I'm very new to DSP and NAudio (I have tried for hours - I've also tried Googling and searching other stackoverflow posts, I've found one that helped a bit and that's how I got to where I am now).

To download the 525Hz 50 second tone file that I am playing back to my default microphone from Audacity's WASPI playback option click here.

Any help in getting this to work will be greatly appreciated.

    public static double CalculateGoertzel(short[] sample, double frequency, int samplerate)
    {
        double Skn, Skn1, Skn2;
        Skn = Skn1 = Skn2 = 0;
        for (int i = 0; i < sample.Length; i++)
        {
            Skn2 = Skn1;
            Skn1 = Skn;
            Skn = 2 * Math.Cos(2 * Math.PI * frequency / samplerate) * Skn1 - Skn2 + sample[i];
        }
        double WNk = Math.Exp(-2 * Math.PI * frequency / samplerate);
        return 20 * Math.Log10(Math.Abs((Skn - WNk * Skn1)));
    }

    private void button3_Click(object sender, EventArgs e)
    {
        waveIn = new WasapiLoopbackCapture();
        waveIn.DataAvailable += OnDtAvailable;
        waveIn.StartRecording();
    }
    void OnDtAvailable(object sender, WaveInEventArgs e)
    {
        byte[] buffer = e.Buffer;
        int bytesRecorded = e.BytesRecorded;
        int bufferIncrement = waveIn.WaveFormat.BlockAlign;

        for (int index = 0; index < bytesRecorded; index += bufferIncrement)
        {
            short[] sampleBuffer = new short[buffer.Length];
            Buffer.BlockCopy(buffer, index, sampleBuffer, 0, bytesRecorded);
            Console.WriteLine(CalculateGoertzel(sampleBuffer, 525, 48000));
        }
    }

回答1:

BlockCopy copies the number of bytes specified from source to dest with the last number being the number of bytes to copy, so instead (I think) what you want is instead

Buffer.BlockCopy(buffer, index, sampleBuffer, 0, bufferIncrement);

what you're doing is copying the entire source array to the destination over and over. Although I'm not sure why you wouldn't just want to do the entire copy at once (which is what your first method did without the loop)?