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