TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

AudioRecordの問題(解決)

AndroidのAudioRecordの問題 - TadaoYamaokaの開発日記で、

03-28 21:17:52.498    1081-1097/com.tadaoyamaoka.recordsample W/AudioRecord﹕ obtainBuffer timed out (is the CPU pegged?) user=00065320, server=00065320

というエラーがでると書いたが、勘違いをしていたためということがわかった。

record.setPositionNotificationPeriod(FRAME_BUFFER_SIZE);

の引数のFRAME_BUFFER_SIZEを、AudioRecord.getMinBufferSize()で取得したバッファサイズより小さくしていたことが原因だった。

shortでのサイズなので、AudioRecord.getMinBufferSize() / 2とすれば、
obtainBuffer timed outが発生しなくなった。

なんで勘違いしてたんだろう。

実機(200SH)でFRAME_BUFFER_SIZEは3840だったので、
11.48FPS(87ms間隔)で取得できる。
実際安定して取得できていた。
OpenSL ESを使わなくても結構行けそうである。


AndroidエミュレータだとFRAME_BUFFER_SIZEは320だったが、
137FPSのはずが、実際はかなり遅い。
setPositionNotificationPeriod()で設定の間隔通りに呼ばれるというわけではなさそう。



以下に動くようになったサンプルコードを貼っておきます。

package com.tadaoyamaoka.recordsample;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.Timer;
import java.util.TimerTask;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder holder;
    private AudioRecord record;
    private Timer timer = new Timer(true);
    Handler handler = new Handler();

    private int count = 0;
    private int draw_count = 0;

    // 定数
    private static final int AUDIO_SAMPLE_FREQ = 44100;
    private static final int AUDIO_BUFFER_SIZE = AudioRecord.getMinBufferSize(
            AUDIO_SAMPLE_FREQ, AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
    private static final int FRAME_BUFFER_SIZE = AUDIO_BUFFER_SIZE / 2 ;

    private short data[] = new short[FRAME_BUFFER_SIZE];

    public MySurfaceView(Context context) {
        super(context);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        holder = getHolder();
        holder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // AudioRecord作成
        record = new AudioRecord(MediaRecorder.AudioSource.MIC,
                AUDIO_SAMPLE_FREQ, AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT, AUDIO_BUFFER_SIZE);
        record.setRecordPositionUpdateListener(new AudioRecord.OnRecordPositionUpdateListener() {
            // フレームごとの処理
            @Override
            public void onPeriodicNotification(AudioRecord recorder) {
                count++;
                recorder.read(data, 0, FRAME_BUFFER_SIZE);
            }

            @Override
            public void onMarkerReached(AudioRecord recorder) {
            }
        });

        record.setPositionNotificationPeriod(FRAME_BUFFER_SIZE);

        // 録音開始
        record.startRecording();

        record.read(data, 0, FRAME_BUFFER_SIZE);

        // 描画用タイマー開始
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        draw();
                    }
                });
            }
        }, 1000/30, 1000/30); // 30FPS
    }

    private void draw() {
        draw_count++;
        //Log.d("debug", String.format("%d", count));

        Canvas canvas = holder.lockCanvas();
        if (canvas != null) {
            canvas.drawColor(Color.BLACK);
            Paint paint = new Paint();
            paint.setTextSize(30);
            paint.setColor(Color.WHITE);
            canvas.drawText(String.format("%d", count), 100, 100, paint);
            canvas.drawText(String.format("%d", draw_count), 100, 200, paint);
            holder.unlockCanvasAndPost(canvas);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        timer.cancel();
        timer = null;

        record.stop();
        record.release();
        record = null;
    }
}