android - 使用 androids 可视化器类获取可变频率范围

标签 android kotlin fft frequency visualizer

我想获取智能手机播放的声音的某些频率范围的值,以便我可以通过蓝牙将它们转发到可视化这些范围的设备。这些范围是:
0-63Hz
63-160Hz
160-400Hz
400-1000Hz
1000-2.500Hz
2.500-6.250Hz
6.250-16.000Hz

Audio Session ID 为 0,因此我可以使用智能手机播放的任何声音。

我发现的是可视化器类,我认为我可以使用 getFft 方法来实现它。虽然看起来我只能将频率分成与捕获率相同大小的部分?或者我完全误解了这里的某些东西?我尝试仅使用采样率作为捕获率,这样我就会为每个频率提供一个值,但它只会再次将捕获率设置为 1024。
或者也许这个类不是我想要的? 我想我可能完全错过了这里的要点,所以欢迎任何帮助或解释(或另一个库的推荐)。

        val visualizer = Visualizer(0)
        visualizer.scalingMode = 0

        visualizer.setDataCaptureListener(object : Visualizer.OnDataCaptureListener {
            override fun onWaveFormDataCapture(
                vis: Visualizer,
                bytes: ByteArray,
                samplingRate: Int
            ) {

            }

            override fun onFftDataCapture(
                visualizer: Visualizer?,
                fft: ByteArray?,
                samplingRate: Int
            ) {
                //if frequency <=63 do something
                //else if frequency <=160 do something ...
            }

        }, Visualizer.getMaxCaptureRate() / 2, false, true)
        visualizer.enabled = true


最佳答案

FFT 计算的数学原理是固有的,它将产生大小均匀且计数等于样本大小一半的频率“桶”,并上升到样本大小一半的频率速度。 (FFT 实际上会生成等于样本大小的存储桶,但 Android 的 Visualizer 会在提供结果之前转储后半部分,因为它们包含前半部分的反射,因此对于可视化没有用处。)

根据硬件功能和普通的旧物理原理,允许的捕获大小和捕获率范围将非常有限。而且,这两个属性成反比。如果您的捕获尺寸很大,那么您的捕获率就必须很小。音频以均匀定时幅度的流的形式产生(其中间隔是采样率)。为简单起见,假设音频流仅为 1024 Hz,每秒产生 1024 个幅度。如果您的捕获速率为每秒 1 个,则每次捕获时您将收集全部 1024 个幅度,因此您的捕获大小为 1024。如果您的捕获速率为每秒 2 个,则每次捕获时您将收集 512 个幅度,因此您的捕获大小为 512。

注意,我不确定您设置的捕获大小是否与 setDataCaptureListener 中使用的捕获率成反比,它是否忽略您设置的大小或实际重复/删除数据。我总是使用 Visualizer.getMaxCaptureRate() 作为捕获率。

您可以做的(并且不会精确)是对适当的范围进行平均,尽管我认为您需要在平均之前将对数函数应用于幅度,否则结果看起来不会很好。在将它们可视化之前,您肯定需要在某个时刻对幅度应用对数函数,以便可视化工具对查看者有意义。

因此,选择捕获大小后,您可以准备用于收集结果的范围。

private val targetEndpoints = listOf(0f, 63f, 160f, 400f, 1000f, 2500f, 6250f, 16000f)
private val DESIRED_CAPTURE_SIZE = 1024 // A typical value, has worked well for me
private lateinit var frequencyOrdinalRanges: List<IntRange>
//...

val captureSizeRange = Visualizer.getCaptureSizeRange().let { it[0]..it[1] }
val captureSize = DESIRED_CAPTURE_SIZE.coerceIn(captureSizeRange)
visualizer.captureSize = captureSize
val samplingRate = visualizer.samplingRate
frequencyOrdinalRanges = targetEndpoints.zipWithNext { a, b ->
        val startOrdinal = 1 + (captureSize * a / samplingRate).toInt()
        // The + 1 omits the DC offset in the first range, and the overlap for remaining ranges
        val endOrdinal = (captureSize * b / samplingRate).toInt()
        startOrdinal..endOrdinal
    }

然后在你的监听器中

override fun onFftDataCapture(
    visualizer: Visualizer,
    fft: ByteArray,
    samplingRate: Int
) {
    val output = FloatArray(frequencyOrdinalRanges.size)
    for ((frequencyOrdinalRange, i) in frequencyOrdinalRanges.withIndex) {
        var logMagnitudeSum = 0f
        for (k in ordinalRange) {
            val fftIndex = k * 2
            logMagnitudeSum += log10(hypot(fft[fftIndex].toFloat(), fft[fftIndex + 1].toFloat()))
        }
        output[i] = logMagnitudeSum / (ordinalRange.last - ordinalRange.first + 1)
    }
    // If you want magnitude to be on a 0..1 scale, you can divide it by log10(hypot(127f, 127f))
    // Do something with output
}

我没有测试以上任何内容,因此可能会有错误。只是想传达策略。

关于android - 使用 androids 可视化器类获取可变频率范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60527292/

相关文章:

android - Android Studio 2.2 更新后任务 ':app:processDebugManifest' 执行失败

android - “没有找到权限撤销的内容提供者 :file:///data/local on sencha touch app

java - 如何在 Spring Data 中使用自定义数据库方言?

python - 在 Python 中通过 Scipy 创建带通滤波器?

c++ - FFT和IFFT的长度

java - 寻找合适的设计模式

android - 如何在混合应用程序中安全地创建登录系统(phonegap/cordova)

operator-overloading - Kotlin - 函数的调用运算符重载

java - 显示具有多个航点的两点之间的路线 Android Here Map Lite Edition

algorithm - Rutkowska 的位反转算法