Wednesday, August 17, 2011

Android приложение визуализатора. Часть 1


Управлением визуализацией на светодиодной матрице будет заниматься Android приложение, в задачи которого входит сам захват аудио, преобразование данных захвата и отправка на ADK плату, к которой и подключена светодиодная матрица. В этой части будет рассмотрен захват и преобразование.






Немного теории
В пакете android.media.audiofx находится класс Visualizer, в котором есть метод getFft, позволяющий получить частоты захвата проигрываемого аудио контента:

public int getFft (byte[] fft)


Since: API Level 9
Returns 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 by getSamplingRate(). 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
fftarray 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. Код выложен в свободный доступ, предложения и исправления приветствуются.

Часть кода, отвечающего за захват:
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 Гц) и отрисовываются на канвасе.
Результат выглядит так:
На данный момент результат меня не совсем удовлетворяет - слабо просматривается связь между визуализацией с реально играющей композицией.

5 comments:

  1. А почему матрица позволяет отразить только 32 значения? И как конкретно посчитать амплитуду для например 250 Гц, если я вас правильно понял, то выбираем элемент массива, который близок к искомой частоте, и потом считаем для его значения амплитуду?

    ReplyDelete
  2. В моем случае в дальнейшем использовалась светодиодная матрица размером 32х16, отсюда и 32 значения.

    Чтобы максимально близко подобраться к 250Гц нужно увеличить размер захвата (setCaptureSize), тогда частоты будут идти плотнее, найти номер этого элемента по формуле и в элементе массива с данным индексом с будет находиться величина быстрого преобразования Фурье (амплитуда).

    ReplyDelete
  3. А по какой формуле искать номер элемента?

    ReplyDelete
  4. Формула из статьи, Fk = (k*Fs)/(n/2)
    Из нее надо найти k.
    Fk = 250
    Fs = 44100
    n = 1024 (для примера)

    Выходит что номер элемента ~ 3

    ReplyDelete
  5. Спасибо, занимательно. получилось написать визуализацию звука.

    ReplyDelete