I have some Kind of Question about FFT (Actually I believe it's more about Androids FFT-Output from Visualizer.getFFT()).
I have created a Music-Player with own Library function for Android including lot's of Stuff (like genres, dynamic playlists and visualisations). Currently I have some trouble with the Visualisations I create when it comes to render a Spectrum of the current AudioStream.
I already read the following Questions (and Answers) to get an Idea of Androids FFT:
What kind of output should I see from getFft?
Android 2.3 Visualizer - Trouble understanding getFft()
Now to my Problem: The Spectrum I get from getFFTs coefficients seems to be somewhat "odd". I notice that the spectrum I render seems to display a lot of "noise" when playing a song so I tried using some test-sounds. One of them is a simple 8khz sound which should result in only one peak in the graph. Unfortunately the result looks like the following:
http://img4.imageshack.us/img4/4181/spectrum8khz.png
The noise appearing at the bottom is flickering all over the width of the graph. The high bars stay in position only flickering slightly in magnitude.
When I use a test-sound moving slowly from 1kHz to 20kHz it looks like the following (at about 2-3kHz):
http://img846.imageshack.us/img846/7373/spectrum3khz1khz20khz.png
The peaks move from left to right and each one is a little faster so over time the distance between the peaks grows. What is not visible is that the peaks come back and go from right to left once they leave the screen on the right (but with smaller magnitude). Also all the peaks join to one big peak at a little more than 0.5 of the screen.
Here is the code I use to retrieve the Data:
for (int i = 1; i < n / 2; i++) {
byte rfk = mRawSpecData[2*i];
byte ifk = mRawSpecData[2*i+1];
float magnitude = (float)Math.sqrt(rfk * rfk + ifk * ifk);
mFormattedSpecData[i-1] = magnitude / 128f;
}
In the code above I mRawSpecData is the result of the Visualisers getFFT() function. The length of the captured Data is 1024. Currently the slope starts at 1 because mRawSpecData[ 0 ] contains the DC and mRawSpecData[ 1 ] contains n/2.
To solve my problem I also tried to fiddle around with DC and phase of the frequency-bin. Thought maybe I had to apply some computation on the magnitudes to "clean" the graph. But I didn't succeed (Maybe because I didn't get what is goind on with DC/phase at all!).
I spent two weeks searching google at the evenings and trying different computations but nothing really helped.
So what is the deal? Am I doing something wrong or leaving something out? After that another question which bothers me is how to scale the magnitudes correctly. My goal is to get values between 0f and 1f.
Thanks a lot
rampage
P.S.: Screenshots where taken via eclipse from a phone running Android 2.3.
P.P.S.: I also checked the sounds with various other players (like winamp) and there i see the correct behaviour of the spectrum.
I have good result by using following calculations,for get a visualizer like this image. I am using nearly 19 images for displaying like this.These are responding by getFFT() method inputs.And be sure to enable both equalizer and visualizer,Otherwise it gives high values and fft values are responded to device volume.Actually i am not able to give explanation of the code because of my english.so i past code here.
VisualizerView:
public class VisualizerView extends View {
private byte[] mBytes;
private float[] mPoints;
Paint mForePaint = new Paint();
// private int width;// height;
private Paint mPaint;
Bitmap mBmpArray[];
int wTilesize;
int hTilesize;
int no_of_colomuns;
private Bitmap peakBitmap;
private float changeFromTop, changeFromLeft;
private int images_drawn_starting_point ;
int magnitudePoints[];
int max[] = new int[34];
int temp[]=new int[32];
private final int[][] images = {
{ R.drawable.blue_fade_1, R.drawable.blue_fade_2,
R.drawable.blue_fade_3, R.drawable.blue_fade_4,
R.drawable.blue_fade_5, R.drawable.blue_fade_6,
R.drawable.blue_fade_7, R.drawable.blue_fade_8,
R.drawable.blue_fade_9, R.drawable.blue_fade_10,
R.drawable.blue_fade_11, R.drawable.blue_fade_12,
R.drawable.blue_fade_13, R.drawable.blue_fade_14,
R.drawable.blue_fade_15, R.drawable.blue_fade_16,
R.drawable.blue_fade_17, R.drawable.blue_fade_18,
R.drawable.blue_fade_19 }};
private final int IMAGES_LENTH = 19;
public VisualizerView(Context context) {
super(context);
mBmpArray = new Bitmap[20];
init();
}
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public VisualizerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
images_drawn_starting_point = h;
int temp;
wTilesize = w / 34;
// hTilesize = h / 30;
temp = h - ((IMAGES_LENTH - 1) );
hTilesize = temp / (IMAGES_LENTH );
no_of_colomuns = ( w / (wTilesize));
magnitudePoints = new int[no_of_colomuns];
changeFromLeft = wTilesize + 3f;//For spacing left
changeFromTop = hTilesize + 2.5f;//For spacing Right
}
public void init() {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(5f);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
int h = canvas.getHeight();
int w = canvas.getWidth();
canvas.drawRect(new Rect(0, 0, w, h), mPaint);
if (mBytes == null) {
return;
}
if (mPoints == null || mPoints.length < mBytes.length * 4) {
mPoints = new float[mBytes.length * 4];
}
double magnitude;
//VisualizerActivity.THEME_COLOR=0
for (int j = 0; j < IMAGES_LENTH; j++)
loadTile(j,getResources().getDrawable(images[VisualizerActivity.THEME_COLOR][j]));
for (int i = 0; i < no_of_colomuns; i++) {
byte rfk = mBytes[2 * i];
byte ifk = mBytes[2 * i + 1];
magnitude = ((rfk * rfk + ifk * ifk));
int dbValue = (int) (10 * Math.log10(magnitude));
magnitude = Math.round(dbValue * 8);
try {
magnitudePoints[i] = (int) magnitude;
} catch (Exception e) {
e.printStackTrace();
}
}
int left;
int top;
int index;
try {
index = 0;
left = 0;
int m = 1;
if (VisualizerActivity.THEME_STYLE == 0)
{
// common
for (int i = 0; i < no_of_colomuns; i++) {
top = images_drawn_starting_point;
index = 18;
for (int j = 0; j < IMAGES_LENTH; j++) {
if (j > magnitudePoints[m] / IMAGES_LENTH)
{
canvas.drawBitmap(mBmpArray[0], left, top, mPaint);
index++;
}
else
{
canvas.drawBitmap(mBmpArray[index--], left, top,
mPaint);
}
top -= changeFromTop;// hTilesize+1.5;
}
m++;
left += changeFromLeft;// wTilesize+2.5;
}
}
} catch (Exception e)
{
e.getMessage();
}
}
public void loadTile(int key, Drawable tile) {
try {
Bitmap bitmap = Bitmap.createBitmap(wTilesize, hTilesize,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
tile.setBounds(0, 0, wTilesize, hTilesize);
tile.draw(canvas);
mBmpArray[key] = bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
public void updateVisualizerWithFft(byte[] bytes) {
if (AudioPlayer.player != null) {
if (AudioPlayer.player.isPlaying()) {
mBytes = bytes;
}
}
invalidate();
}
}
In VisualizerActivity.java:
AudioPlayer.mVisualizer.setCaptureSize(Visualizer
.getCaptureSizeRange()[1]);
AudioPlayer.mVisualizer.setDataCaptureListener(
new Visualizer.OnDataCaptureListener() {
public void onWaveFormDataCapture(
Visualizer visualizer, byte[] bytes,
int samplingRate) {
// mVisualizerView.updateVisualizer(bytes);
}
public void onFftDataCapture(Visualizer visualizer,
byte[] bytes, int samplingRate) {
mVisualizerView.updateVisualizerWithFft(bytes);
}
}, Visualizer.getMaxCaptureRate() / 2, false, true);