Математике расчета БПФ присуще то, что он будет генерировать частотные «сегменты», которые имеют одинаковый размер и имеют счет, равный половине размера выборки и go до частоты, равной половине. частота дискретизации. (FFT на самом деле производит сегменты, равные размеру выборки, но Visualizer Android идет вперед и выдает вторую половину перед выдачей результатов, потому что они содержат отражение первой половины, и поэтому бесполезны для визуализации.)
Будет очень ограниченный диапазон допустимых размеров захвата и скоростей захвата, основанных на аппаратных возможностях и простой старой физике. Кроме того, эти два свойства обратно пропорциональны. Если ваш размер захвата велик, ваш коэффициент захвата должен быть маленьким. Звук воспроизводится в виде потока равномерно синхронизированных амплитуд (где расстояние составляет samplingRate
). Предположим для простоты, что аудиопоток только на частоте 1024 Гц, производящий 1024 амплитуды в секунду. Если ваша скорость захвата составляет 1 в секунду, вы собираете все 1024 из этих амплитуд при каждом захвате, поэтому ваш размер захвата составляет 1024. Если ваша скорость захвата составляет 2 в секунду, вы собираете 512 амплитуд при каждом захвате, поэтому ваш захват размер 512.
Обратите внимание, я точно не знаю, если вы установите размер захвата, и он не будет обратно соответствовать вашей скорости захвата, используемой в setDataCaptureListener
, будет ли он игнорировать установленный вами размер или фактически повторяет / отбрасывает данные. Я всегда использую Visualizer.getMaxCaptureRate()
в качестве скорости захвата.
Что вы можете сделать (и это не будет точно), это усреднить соответствующие диапазоны, хотя я думаю, что вы захотите применить функцию log к Величина, прежде чем вы в среднем, или результаты не будут выглядеть великолепно. Вам определенно необходимо применить функцию логарифмирования к величинам в некоторой точке, прежде чем визуализировать их, чтобы визуализатор имел смысл для зрителя.
Поэтому после выбора размера захвата вы можете подготовить диапазоны для использования для сбора результатов.
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
}
Я не проверял ничего из вышеперечисленного, поэтому могут быть ошибки. Просто пытаюсь донести стратегию.