使用 androids visualizer 获取可变频率范围 class
Getting variable frequency ranges with androids visualizer class
我想获取智能手机播放的声音的特定频率范围的值,以便我可以通过蓝牙将它们转发到可视化这些范围的设备。这些范围是:
0-63Hz
63-160Hz
160-400赫兹
400-1000赫兹
1000-2.500Hz
2.500-6.250Hz
6.250-16.000Hz
音频会话 ID 为 0,因此我可以使用智能手机播放的任何声音。
我发现的是可视化工具 class,我认为我可以使用 getFft 方法实现它。虽然看起来我只能将频率分成具有捕获率的相同大小的部分?或者我在这里完全误解了什么?我尝试只使用采样率作为捕获率,所以我会为每个频率设置一个值,但它只会再次将捕获率设置为 1024。
或者这 class 可能不是我想要的想?
我想我可能完全忽略了这里的要点,所以欢迎任何帮助或解释(或其他图书馆的推荐)。
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 的数学方法固有的是,它会产生大小均匀的频率 "buckets",计数等于样本大小的一半,并上升到一个频率是采样率的一半。 (FFT 实际上会生成等于样本大小的桶,但 Android 的可视化工具会继续并在交付结果之前转储后半部分,因为它们包含前半部分的反映,因此对可视化没有用。 )
根据硬件功能和普通的旧物理学,允许的捕获大小和捕获率的范围非常有限。而且,这两个属性是成反比的。如果捕获大小很大,则捕获率必须很小。音频以等时幅度的流形式产生(其中间距为 samplingRate
)。为简单起见,假设音频流仅为 1024 Hz,每秒产生 1024 个振幅。如果您的捕获速率为每秒 1,则每次捕获时您将收集所有 1024 个振幅,因此您的捕获大小为 1024。如果您的捕获速率为每秒 2,则每次捕获将收集 512 个振幅,因此您的捕获尺寸为 512.
请注意,我不确定您是否设置了捕获大小并且它与您在 setDataCaptureListener
中使用的捕获率不成反比,它是否忽略了您设置的大小或实际上 repeats/drops 数据。我总是使用 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
}
以上我都没有测试过,所以可能会有错误。只是想传达策略。
我想获取智能手机播放的声音的特定频率范围的值,以便我可以通过蓝牙将它们转发到可视化这些范围的设备。这些范围是:
0-63Hz
63-160Hz
160-400赫兹
400-1000赫兹
1000-2.500Hz
2.500-6.250Hz
6.250-16.000Hz
音频会话 ID 为 0,因此我可以使用智能手机播放的任何声音。
我发现的是可视化工具 class,我认为我可以使用 getFft 方法实现它。虽然看起来我只能将频率分成具有捕获率的相同大小的部分?或者我在这里完全误解了什么?我尝试只使用采样率作为捕获率,所以我会为每个频率设置一个值,但它只会再次将捕获率设置为 1024。
或者这 class 可能不是我想要的想?
我想我可能完全忽略了这里的要点,所以欢迎任何帮助或解释(或其他图书馆的推荐)。
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 的数学方法固有的是,它会产生大小均匀的频率 "buckets",计数等于样本大小的一半,并上升到一个频率是采样率的一半。 (FFT 实际上会生成等于样本大小的桶,但 Android 的可视化工具会继续并在交付结果之前转储后半部分,因为它们包含前半部分的反映,因此对可视化没有用。 )
根据硬件功能和普通的旧物理学,允许的捕获大小和捕获率的范围非常有限。而且,这两个属性是成反比的。如果捕获大小很大,则捕获率必须很小。音频以等时幅度的流形式产生(其中间距为 samplingRate
)。为简单起见,假设音频流仅为 1024 Hz,每秒产生 1024 个振幅。如果您的捕获速率为每秒 1,则每次捕获时您将收集所有 1024 个振幅,因此您的捕获大小为 1024。如果您的捕获速率为每秒 2,则每次捕获将收集 512 个振幅,因此您的捕获尺寸为 512.
请注意,我不确定您是否设置了捕获大小并且它与您在 setDataCaptureListener
中使用的捕获率不成反比,它是否忽略了您设置的大小或实际上 repeats/drops 数据。我总是使用 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
}
以上我都没有测试过,所以可能会有错误。只是想传达策略。