0

GC が原因でアプリケーションのパフォーマンスに問題があり、何が起こっているのかを理解する経験があまりありません。詳しい状況はこちら。

STFT(簡単な説明)を適用して、オーディオをリアルタイムで処理するアプリケーションを構築しようとしています。

基本的に、各バッファーを取得し (私の場合、最小バッファー サイズは 1148 バイトです)、ウィンドウ関数を適用して、フレームのマトリックスを取得します。次に、各フレームで FFT を適用します。最後に、周波数と時間に基づいて、各フレームにゲインを適用できます。次に、バッファーの変更されたバージョンを取得するためにパスを戻します。

私のサンプリング周波数は 8000Hz なので、各バッファーは 1148/8000 = 144ms 未満で処理を行う必要があります。使ってSystem.currentTimeMillis()みたところ、バッファの処理に通常70~100ミリ秒かかるとのことで、問題ありませんでした。

しかし、問題はガベージ コレクターにあります。下のスクリーンショットからわかるように、メモリがいっぱいのようです。GCアクションにより、オーディオが時々割れます。

私のアプリケーションのログ

問題は、次の 2 つの点に気付いたことです。

  • FFT と IFFT を実行せず、フレームをそのままにしておくと、CG_CONCURRENT メッセージは表示されません。これは、FFT が大量のデータ (複素数の配列) を生成するためです。

  • すべての操作は別のスレッドで行われます。そのため、アプリケーションを起動した直後に DDMS の観点から手動で CG を作成しようとしました。以下のコードからわかるように、onCreate メソッドでは、アプリケーションはレイアウトのみをロードします。ヒープ ダンプを見て、Leaks Suspect レポートを生成しました。ほとんどのメモリは、クラス 'android.content.res.Resources' と 'android.graphics.Bitmap' によって占有されています.. (スクリーンショットはこちら)

アプリケーション起動後のヒープ使用量

リークレポートの円グラフ

提案はありますか?私のメモリが最初にすでに90%使用されているのは奇妙だと思います..また、私のニーズを満たすためにヒープを少し増やすことはできません

コード

MainActivity.java

package com.example.fileoutjava;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {

    static final int BUFFER_FACTOR = 1; 

    DataInputStream dis;
    static final int FREQ = 8000;
    static final int FRAME_LENGHT = 32;
    static final int FRAME_SHIFT = 16;
    boolean isMusicStopped = true;
    AudioTrack at;
    Thread playThread;
    long time;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }


    public void playMusic(View v) {
        if (at == null) {
            Log.d("PLAY MUSIC", "LAUNCHING NEW PLAYER");
            playThread = new Thread(musicPlayerThread);
            playThread.start();
        }    
    }

    public void stopMusic(View v) {
        isMusicStopped = true;
        playThread = null;
    }


    Runnable musicPlayerThread = new Runnable() {       
        public void run() {
            Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

            /* eg: 8000 bytes per second, 1000 bytes = 125 ms */

            InputStream is = null;
            DataInputStream dis = null;

            try {
                is = MainActivity.this.getApplicationContext().getAssets().open("test.wav");
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (is!=null)
                dis = new DataInputStream(is); //dis = new DataInputStream(new BufferedInputStream(is,bSize));

            isMusicStopped = false;

            int min_bSize = AudioTrack.getMinBufferSize(FREQ, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
            int bSize = min_bSize*BUFFER_FACTOR;

            STFT stft = new STFT(FRAME_SHIFT,FRAME_LENGHT,FREQ,bSize);

            at = new AudioTrack(AudioManager.STREAM_MUSIC, FREQ, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bSize, AudioTrack.MODE_STREAM);            
            at.play();

            int count = 0;
            byte[] buffer = new byte[bSize];

            time = System.currentTimeMillis();

            try {
                while (!isMusicStopped && (count = dis.read(buffer, 0, bSize)) >= 0) {


                    Log.d("TIME ELAPSED", ""+(System.currentTimeMillis()-time));
                    time = System.currentTimeMillis();

                    //Windowing
                    stft.frameBuffer(buffer);

                    //fourier transform and inverse
                    stft.fourierAnalysis();

                    // Overlapp-Add
                    stft.buildBuffer(buffer);

                    at.write(buffer, 0, count);
                }

                if (at != null) {
                    at.stop();
                    at.flush();
                    at.release();
                    at = null;
                }

                if (dis != null) {
                    dis.close();
                    dis = null;
                }

                if (is != null) {
                    is.close();
                    is = null;
                }

                if (stft != null) stft = null;

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };


    private void stop() {
        isMusicStopped = true;
        playThread = null;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    protected void onPause() {
        this.stop();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        this.stop();
        super.onDestroy();
    }

}

STFT.java

package com.example.fileoutjava;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import android.util.Log;

import com.badlogic.gdx.audio.analysis.FFT;

public class STFT {

    private int fs, fl; //frame shift and frame length in ms
    private int n_fs, n_fl; //frame shift and length in samples
    private int buf_len; //length of the buffer array (bytes)
    private int data_len; //length of the buffer array (converted to short)
    private int padded_data_len; //put 0 padding before and after the buffer short[] data
    private float n_segs; //number of frames that can be taken from one buffer array
    private float[][] stft_matrix;
    private float[] window; //Hamming coefficient
    private float norm_factor = 0;
    private boolean search_norm_factor = true;
    private FFT fft;
    private int i,j,k; //index for loops
    private ByteBuffer bb;
    private float[] tmp_buf;
    private float[] tmp_fft;
    private float[] tmp_ifft;

    public STFT(int frame_shift, int frame_length, int freq, int buf_len) {
        fs = frame_shift;
        fl = frame_length;
        this.buf_len = buf_len;
        this.data_len = buf_len/2;      

        //compute values from ms to samples
        n_fs = (int) Math.floor(fs*freq/1000);
        n_fl = (int) Math.floor(fl*freq/1000);

        padded_data_len = 2*n_fl + data_len;

        //create coefficients
        window = hamming(n_fl);

        tmp_buf = new float[padded_data_len];
        bb = ByteBuffer.allocateDirect(2);
        bb.order(ByteOrder.LITTLE_ENDIAN);

        //compute how many frames can be extracted from the buffer
        n_segs = 1 + (float) (Math.ceil((this.padded_data_len-n_fl)/n_fs));

        //data matrix: size of frame (with padding from previous frame) * number of segments
        stft_matrix = new float[n_fl][(int)n_segs];

        Log.d("STFT STATS", "BufLen:"+this.buf_len+" // Flen:"+n_fl+" // FSh:"+n_fs+
                " // Nsegs:"+n_segs);

        //Initialize the FFT object
        fft = new FFT(n_fl*2,freq);

        //buffers for FFT data, with zero padding
        tmp_fft= new float[n_fl*2];
        tmp_ifft = new float[n_fl];

        for (int i=0; i<n_fl*2; i++) {
            tmp_fft[i] = 0;
            tmp_ifft[i/2] = 0;
        }


    }

    //frames the whole buffer into the stft matrix
    public void frameBuffer(byte[] buf) {

        //initialize tmp_buffer and add 0 padding
        for (k=0; k<padded_data_len; k++)
            tmp_buf[k] = 0;

        //fill the short[] buffer converting from byte[] buffer

        for (i=0; i<buf_len; i+=2) {
            bb.position(0);
            bb.put(buf[i]);
            bb.put(buf[i+1]);
            tmp_buf[n_fl+i/2] = (float) bb.getShort(0);
        }


        //frame the short[] buffer into the matrix using windowing
        for (j=0; j<n_segs; j++) {
            for (int i=0; i<n_fl; i++) {
                stft_matrix[i][j] = tmp_buf[j*n_fs+i]*window[i];

                //NORMALIZATION FACTOR RETRIEVAL: only the first time
                if (search_norm_factor && (j*n_fs+i) == 512)
                    norm_factor+=window[i];
            }
        }

        if (search_norm_factor)
            norm_factor *= 1.2;
        //retrieve the norm factor only the first time
        search_norm_factor = false;

    }

    //sums all frames from STFT matrix into one buffer
    public void buildBuffer(byte[] output) {

        //initialize tmp_buffer and add 0 padding
        for (k=0; k<padded_data_len; k++)
            tmp_buf[k] = 0;

        //Overlap-Add
        for (j=0; j<n_segs; j++) {
            for (i=0; i<n_fl; i++) {
                tmp_buf[j*n_fs+i] += stft_matrix[i][j];
            }
        }

        //convert from short[] to byte[] (with normalization)
        for (i=0; i<buf_len; i+=2) {
            bb.position(0);
            bb.putShort( (short) (tmp_buf[n_fl+i/2]/norm_factor) );
            output[i] = bb.get(0);
            output[i+1] = bb.get(1);
        }

    }

    //FFT and IFFT of the buffer
    public void fourierAnalysis() {

        for (j=0; j<n_segs;j++) {

            for (i=0; i<n_fl; i++) {
                tmp_fft[i] = stft_matrix[i][j];
            }

            fft.forward(tmp_fft);
            //OPERATIONS ON THE SPECTRUM ?
            fft.inverse(tmp_ifft);


            for (int i=0; i<n_fl; i++) {
                stft_matrix[i][j] = tmp_ifft[i];
            }
        }   
    }


    //utility method for Hamming coefficients
    private float[] hamming(int len){
        float[] win = new float[len];
        for (i=0; i<len; i++){
            win[i] = (float) (0.54-0.46*Math.cos((2*Math.PI*i)/(len-1)));
        }

        return win;
    }

}
4

1 に答える 1