Управлением визуализацией на светодиодной матрице будет заниматься Android приложение, в задачи которого входит сам захват аудио, преобразование данных захвата и отправка на ADK плату, к которой и подключена светодиодная матрица. В этой части будет рассмотрен захват и преобразование.
Немного теории
В пакете android.media.audiofx находится класс Visualizer, в котором есть метод getFft, позволяющий получить частоты захвата проигрываемого аудио контента:
public int getFft (byte[] fft)
Since: API Level 9Returns a frequency capture of currently playing audio content.This method must be called when the Visualizer is enabled.The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of the sampling rate returned bygetSamplingRate()
. The capture returns the real and imaginary parts of a number of frequency points equal to half of the capture size plus one.Note: only the real part is returned for the first point (DC) and the last point (sampling frequency / 2).The layout in the returned byte array is as follows:
- n is the capture size returned by getCaptureSize()
- Rfk, Ifk are respectively the real and imaginary parts of the kth frequency component
- If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is: (k*Fs)/(n/2)
Index
0
1
2
3
4
5
...
n - 2
n - 1
Data
Rf0
Rf(n/2)
Rf1
If1
Rf2
If2
...
Rf(n-1)/2
If(n-1)/2
Parameters
fft array of bytes where the FFT should be returned
Если я все правильно понял, то результат выглядит в виде массива байт, содержащий реальные и мнимые части амплитуды для каждой частоты. Первый и второй элементы содержат только реальные части и соответствуют первой и последней точке. Амплитудой будет являться сумма квадратов реальной и мнимой части. Таким образом можно отбросить первые два элемента, а с остальными произвести преобразование по формуле:
Mk = Rfk*Rfk + Ifk*Ifk
Частота дискретизации (sampling rate) Fs = 44100 Гц. Размер захвата (capture size) n = 1024 (для тестового приложения).
Значение частоты для k элемента вычисляется по формуле:
Fk = (k*Fs)/(n/2)
Т.е. в нашем случае F1 = 86 Гц, F2 = 172 Гц ... F32 =2756.25 Гц ... F512 = 44100 Гц.
Однако наибольший интерес представляют частоты до 5 КГц, именно в этом диапазоне находятся музыкальные звуки. Так как у нас матрица позволяет отобразить 32 значения, то частота для 32-го элемента должно быть приблизительно равна 5 КГц, а для этого нам надо правильно вычислить размер захвата n.
n = 2*(32*44100) / 5000 = 564.48 ≈ 564
Теперь F1 ≈ 156 Гц, F2 ≈ 312 Гц ... F32 ≈ 5004 Гц.
Android
За основу взято приложение AudioFxDemo из ApiDemos пакета com.example.android.apis.media в примерах Android SDK. Код выложен в свободный доступ, предложения и исправления приветствуются.
Часть кода, отвечающего за захват:
Затем высчитываются первые 32 значения (диапазон 156 Гц - 5004 Гц) и отрисовываются на канвасе.
Результат выглядит так:
На данный момент результат меня не совсем удовлетворяет - слабо просматривается связь между визуализацией с реально играющей композицией.
Часть кода, отвечающего за захват:
mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,int samplingRate) {}
public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
mVisualizerView.updateVisualizer(bytes);
}
}, Visualizer.getMaxCaptureRate(), false, true);
Затем высчитываются первые 32 значения (диапазон 156 Гц - 5004 Гц) и отрисовываются на канвасе.
Результат выглядит так:
На данный момент результат меня не совсем удовлетворяет - слабо просматривается связь между визуализацией с реально играющей композицией.
А почему матрица позволяет отразить только 32 значения? И как конкретно посчитать амплитуду для например 250 Гц, если я вас правильно понял, то выбираем элемент массива, который близок к искомой частоте, и потом считаем для его значения амплитуду?
ReplyDeleteВ моем случае в дальнейшем использовалась светодиодная матрица размером 32х16, отсюда и 32 значения.
ReplyDeleteЧтобы максимально близко подобраться к 250Гц нужно увеличить размер захвата (setCaptureSize), тогда частоты будут идти плотнее, найти номер этого элемента по формуле и в элементе массива с данным индексом с будет находиться величина быстрого преобразования Фурье (амплитуда).
А по какой формуле искать номер элемента?
ReplyDeleteФормула из статьи, Fk = (k*Fs)/(n/2)
ReplyDeleteИз нее надо найти k.
Fk = 250
Fs = 44100
n = 1024 (для примера)
Выходит что номер элемента ~ 3
Спасибо, занимательно. получилось написать визуализацию звука.
ReplyDelete