从原始波形数据中检测特定频率/音调

发布于 2024-10-14 07:14:10 字数 254 浏览 8 评论 0原文

我正在读取来自麦克风的原始波流。
(这部分的工作原理是我可以将其发送到扬声器并获得良好的回声。)

为简单起见,假设我想检测波形数据中的 DTMF 音调。实际上,我想检测任何频率,而不仅仅是 DTMF 中的频率。但我总是知道我在寻找哪个频率。

我尝试过通过 FFT 运行它,但如果我想要高精度的检测(假设它只存在 20 毫秒),它似乎不是很有效。我可以以大约 200 毫秒的精度检测到它。

关于算法我有哪些选择? 是否有任何 .Net 库?

I am reading a raw wave stream coming from the microphone.
(This part works as I can send it to the speaker and get a nice echo.)

For simplicity lets say I want to detect a DTMF-tone in the wave data. In reality I want to detect any frequency, not just those in DTMF. But I always know which frequency I am looking for.

I have tried running it through FFT, but it doesn't seem very efficient if I want high accuracy in the detection (say it is there for only 20 ms). I can detect it down to an accuracy of around 200 ms.

What are my options with regards to algorithms?
Are there any .Net libs for it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

难理解 2024-10-21 07:14:10

如果您想检测 DTMF 等特定频率,您可能需要查看 Goertzel 算法输入。 Sourceforge 上有一个基于该算法的 C# DTMF 生成器/检测器 库。

You may want to look at the Goertzel algorithm if you're trying to detect specific frequencies such as DTMF input. There is a C# DTMF generator/detector library on Sourceforge based on this algorithm.

疾风者 2024-10-21 07:14:10

那里有非常好的 Goertzel 实现。 C# 修改:

private double GoertzelFilter(float[] samples, double freq, int start, int end)
    {
        double sPrev = 0.0;
        double sPrev2 = 0.0;
        int i;
        double normalizedfreq = freq / SIGNAL_SAMPLE_RATE;
        double coeff = 2 * Math.Cos(2 * Math.PI * normalizedfreq);
        for (i = start; i < end; i++)
        {
            double s = samples[i] + coeff * sPrev - sPrev2;
            sPrev2 = sPrev;
            sPrev = s;
        }
        double power = sPrev2 * sPrev2 + sPrev * sPrev - coeff * sPrev * sPrev2;
        return power;
    }

对我来说非常有用。

Very nice implementation of Goertzel is there. C# modification:

private double GoertzelFilter(float[] samples, double freq, int start, int end)
    {
        double sPrev = 0.0;
        double sPrev2 = 0.0;
        int i;
        double normalizedfreq = freq / SIGNAL_SAMPLE_RATE;
        double coeff = 2 * Math.Cos(2 * Math.PI * normalizedfreq);
        for (i = start; i < end; i++)
        {
            double s = samples[i] + coeff * sPrev - sPrev2;
            sPrev2 = sPrev;
            sPrev = s;
        }
        double power = sPrev2 * sPrev2 + sPrev * sPrev - coeff * sPrev * sPrev2;
        return power;
    }

Works great for me.

拔了角的鹿 2024-10-21 07:14:10

假设典型的 DTMF 频率为 200Hz - 1000Hz。然后您必须根据 4 到 20 个周期来检测信号。我猜 FFT 不会给你带来任何帮助,因为你只会检测到 50Hz 频率的倍数:这是 FFT 的内置功能,增加样本数量不会解决你的问题。你必须做一些更聪明的事情。

对数据进行线性最小二乘拟合

h(t) = A cos (omega t) + B sin (omega t)

最好的办法是针对给定的 omega(DTMF 频率之一) 。有关详细信息(特别是如何设置统计显着性水平)和链接,请参阅文学。

Let's say that typical DTMF frequency is 200Hz - 1000Hz. Then you'd have to detect a signal based on between 4 and 20 cycles. FFT will not get you anywhere I guess, since you'll detect only multiples of 50Hz frequencies: this is a built in feature of FFT, increasing the number of samples will not solve your problem. You'll have to do something more clever.

Your best shot is to linear least-square fit your data to

h(t) = A cos (omega t) + B sin (omega t)

for a given omega (one of the DTMF frequencies). See this for details (in particular how to set a statistical significance level) and links to the litterature.

心凉怎暖 2024-10-21 07:14:10

我发现这是 Goertzel 的一个简单实现。还没有让它工作(寻找错误的频率?),但我想我无论如何都会分享它。它是从 此网站

        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 found this as a simple implementation of Goertzel. Haven't gotten it to work yet (looking for wrong frequency?), but I thought I'd share it anywas. It is copied from this site.

        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)));
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文