Android 的 FFT 输出(可视化工具)的频谱不好?

发布于 2024-10-20 12:39:31 字数 1970 浏览 6 评论 0原文

我有一些关于 FFT 的问题(实际上我相信这更多是关于 Visualizer.getFFT() 的 Android FFT 输出)。

我为 Android 创建了一个带有自己的库功能的音乐播放器,其中包括很多东西(如流派、动态播放列表和可视化)。目前,在渲染当前音频流的频谱时,我创建的可视化遇到了一些问题。

我已经阅读了以下问题(和答案)来了解 Android FFT:

我应该从 getFft 看到什么样的输出?

Android 2.3 Visualizer - 难以理解 getFft()

现在我的问题是:我从 getFFTs 系数获得的频谱似乎有些“奇怪”。我注意到,在播放歌曲时,我渲染的频谱似乎显示出很多“噪音”,因此我尝试使用一些测试声音。其中之一是简单的 8khz 声音,它应该只在图表中产生一个峰值。不幸的是,结果如下所示:

http://img4.imageshack.us/img4/ 4181/spectrum8khz.png

底部出现的噪声在图表的整个宽度上闪烁。高条保持在原位,仅在幅度上轻微闪烁。

当我使用从 1kHz 缓慢移动到 20kHz 的测试声音时,它看起来如下(大约 2-3kHz):

http://img846.imageshack.us/img846/7373/spectrum3khz1khz20khz.png

峰值从左向右移动,每个峰值都快一点,因此随着时间的推移,峰值之间的距离会增加。不可见的是,一旦峰值离开右侧屏幕(但幅度较小),它们就会返回并从右向左移动。此外,所有峰都在略大于屏幕 0.5 的位置处连接到一个大峰。

以下是我用来检索数据的代码:

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;
}

在上面的代码中,我的 mRawSpecData 是 Visualisers getFFT() 函数的结果。捕获的数据的长度是1024。当前斜率从1开始,因为mRawSpecData[0]包含DC并且mRawSpecData[1]包含n/2。

为了解决我的问题,我还尝试摆弄频率仓的直流和相位。我想也许我必须对幅度进行一些计算才能“清理”图表。但我没有成功(也许是因为我根本不明白直流/相位是怎么回事!)。

我花了两周的时间在晚上搜索谷歌并尝试不同的计算,但没有任何真正的帮助。

那么到底是怎么回事呢?我是否做错了什么或遗漏了什么? 之后另一个困扰我的问题是如何正确缩放幅度。 我的目标是获得 0f 和 1f 之间的值。

非常感谢

Rampage

P.S.:屏幕截图是通过 Eclipse 从运行 Android 2.3 的手机上获取的。

PPS:我还用其他各种播放器(例如 winamp)检查了声音,在那里我看到了频谱的正确行为。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

蒗幽 2024-10-27 12:39:31

通过使用以下计算,我得到了很好的结果,以获得像 this 图像这样的可视化工具。
我使用近 19 个图像来显示这样的内容。这些图像通过 getFFT() 方法输入进行响应。并且一定要启用均衡器和可视化器,否则它会给出高值,并且 fft 值会响应设备音量。实际上我无法由于我的英语,所以对代码进行解释。所以我在这里粘贴了代码。

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();

    }

}

在 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);

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文