在java中录制音频并确定实时是不是播放了x频率的音调,如果是这样的话

Posted

技术标签:

【中文标题】在java中录制音频并确定实时是不是播放了x频率的音调,如果是这样的话【英文标题】:record audio in java and determine real time if a tone of x frequency was played if so do something在java中录制音频并确定实时是否播放了x频率的音调,如果是这样的话 【发布时间】:2013-09-06 07:13:52 【问题描述】:

我希望能够使用 java 检测预定频率的音调。我正在做的是播放音调(音调的频率因用户输入而异),我试图检测音调是否具有特定频率。如果是,我执行某个方法。根据我的阅读,我需要 FFT,但我不确定如何在 java 中实现它。似乎有很多关于如何做到这一点的文档,但是有哪些文档涉及查看音频文件而不是实时分析。我不需要将音频保存到文件中,只需确定是否以及何时录制了频率为 x 的音调。

理想情况下,我希望以 44KHz 的采样率进行录制,并在确定是否检测到音调后,确定何时以 +-3ms 的精度检测到音调。但是,只要不荒谬(即+100ms),低于此的精度是可以接受的。从我查到的资料中,我大致知道我需要做什么,但我需要帮助将它们联系在一起。使用伪代码它看起来大致是这样的(我认为)

请注意,我大概知道在 +-1 秒内可以检测到满足频率的音调

for(i = 0, i < 440000 * 2, i++)//*2 because of expected appearance interval;may change
    record sound sample
    fft(sound sample)
    if(frequencySoundSample > x)
        do something
        return
    

播放音调时会有相当大的背景噪音。然而音调会有一个非常高的频率,比如 15-22KHz,所以我相信通过简单地寻找录音机何时检测到一个非常高的频率,我可以确定它是我的音调(音调也将以高幅度可能为 0.5 秒或 1 秒)。我知道不会有其他高频声音作为背景噪音(我预计背景频率可能高达 5KHz)。

那我有两个问题。我提供的伪代码是否足以满足我的需求?如果不是,或者如果有更好的方法可以做到这一点,我完全赞成。其次,我将如何在java中实现它?我知道我需要做什么,但我无法将所有这些联系在一起。我对 java 相当不错,但我不熟悉与音频有关的语法,而且我对 fft 没有任何经验。请明确并提供带有 cmets 的代码。我一直在试图解决这个问题,我只需要看到它都捆绑在一起。谢谢。

编辑

我知道使用像我这样的 for 循环不会产生我想要的频率。更多的是粗略地展示我想要的东西。也就是说,随着时间的推移,同时记录、执行 fft 和测试频率。

【问题讨论】:

【参考方案1】:

如果您只是在寻找特定频率,那么基于 FFT 的方法对于您的特定应用来说可能不是一个好的选择,原因有二:

    这太过分了——你计算整个频谱只是为了检测一个点的幅度

    要获得 3 毫秒的起始检测分辨率,您需要在连续 FFT 之间进行较大的重叠,这将需要比仅处理连续样本块更多的 CPU 带宽

检测单音是否存在的更好选择是Goertzel algorithm(又名 Goertzel 过滤器)。它实际上是在单个频域 bin 上评估的 DFT,并广泛用于音调检测。它比 FFT 的计算成本低得多,实现起来非常简单,并且您可以在每个样本上测试它的输出,因此没有分辨率问题(除了物理定律规定的问题)。您需要对输出的幅度进行低通滤波,然后使用某种阈值检测来确定音调的开始时间。

请注意,关于音调检测和使用 Goertzel 算法(例如 Precise tone onset/duration measurement?)的 SO 上已有许多有用的问题和答案 - 我建议您将这些问题与 Wikipedia 条目一起作为一个好的起点阅读。

【讨论】:

【参考方案2】:

我实际上正在从事一个类似的项目,其中包含音高检测,Java 也是如此。如果你想使用 FFT,你可以通过这些步骤来完成。 Java 有很多库可以让你轻松完成这个过程。

首先,您需要读入声音文件。这可以使用 Java Sound 来完成。它是一个内置库,具有可以轻松录制声音的功能。可以在here 找到示例。默认采样率为 44,100 KHz(CD 质量)。这些示例可以让您从播放实际音调到表示音调的双字节数组。

其次,您应该使用 JTransforms 进行 FFT。这是对一组样本进行 FFT 的an example。

FFT 为您提供的数组长度是您传递给它的样本数组长度的两倍。您需要通过两个 FFT 数组,因为该数组的每个部分都表示为一个虚构的和一个真实的部分。用 sqrt(im^2 + re^2) 计算这个数组每个部分的大小。然后,找出哪个幅度最大。该幅度的索引对应于您正在寻找的频率。

请记住,您不会对整个声音部分进行 FFT。您将声音分成块,然后对每个块进行 FFT。块可以重叠以获得更高的准确性,但这应该不是问题,因为您只是在寻找预定的音符。如果您想提高性能,您还可以在执行此操作之前对每个块进行窗口化。

获得所有 FFT 后,它们应确认某个频率,然后您可以对照所需的注释进行检查。

如果您想尝试将其可视化,我建议您使用 JFreeChart。这是另一个可以轻松绘制图形的库。

【讨论】:

以上是关于在java中录制音频并确定实时是不是播放了x频率的音调,如果是这样的话的主要内容,如果未能解决你的问题,请参考以下文章

来自 Ruby Web 应用程序的实时音频录制/播放

录制实时流媒体音频

如何在 Java 中同步 TargetDataLine 和 SourceDataLine(同步音频录制和播放)

将录制的音频从浏览器流式传输到服务器

在 Android 中是不是可以录制 MediaPlayer 播放的内容?

来自麦克风的实时音频播放。 C#