3. AudioRecord录制麦克风音频

任务说明

本案例在 Android 车载应用中使用 AudioRecord 采集麦克风 PCM 数据,写入 .pcm 文件,并通过文件大小与采样参数校验录音是否成功。核心参数:采样率 16000Hz、单声道、16bit PCM。

权限与依赖

在 AndroidManifest.xml 中声明录音权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

Android 6.0+ 需在运行时申请 RECORD_AUDIO。车载系统若为系统应用,也可通过 privapp-permissions 预授权。

核心实现

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class MicPcmRecorder {

    private static final String TAG = "MicPcmRecorder";
    private static final int SAMPLE_RATE = 16000;
    private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
    private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;

    private AudioRecord audioRecord;
    private volatile boolean recording = false;
    private Thread recordThread;

    /** 开始录音,PCM 数据写入 outputFile */
    public boolean start(File outputFile) {
        int minBuf = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL, ENCODING);
        if (minBuf == AudioRecord.ERROR || minBuf == AudioRecord.ERROR_BAD_VALUE) {
            Log.e(TAG, "getMinBufferSize failed");
            return false;
        }
        int bufferSize = minBuf * 2;

        audioRecord = new AudioRecord(
                MediaRecorder.AudioSource.MIC,
                SAMPLE_RATE, CHANNEL, ENCODING, bufferSize);

        if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
            Log.e(TAG, "AudioRecord init failed");
            audioRecord.release();
            audioRecord = null;
            return false;
        }

        recording = true;
        audioRecord.startRecording();

        recordThread = new Thread(() -> writePcm(outputFile, bufferSize));
        recordThread.start();
        return true;
    }

    private void writePcm(File outputFile, int bufferSize) {
        byte[] buffer = new byte[bufferSize];
        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
            while (recording) {
                int read = audioRecord.read(buffer, 0, buffer.length);
                if (read > 0) {
                    fos.write(buffer, 0, read);
                }
            }
            fos.flush();
        } catch (IOException e) {
            Log.e(TAG, "write pcm failed", e);
        }
    }

    /** 停止录音并释放资源 */
    public void stop() {
        recording = false;
        if (recordThread != null) {
            try { recordThread.join(1000); } catch (InterruptedException ignored) {}
            recordThread = null;
        }
        if (audioRecord != null) {
            audioRecord.stop();
            audioRecord.release();
            audioRecord = null;
        }
    }
}

调用与验证

File pcm = new File(getExternalFilesDir(null), "mic_test.pcm");
MicPcmRecorder recorder = new MicPcmRecorder();
if (recorder.start(pcm)) {
    // 录制 3 秒
    new android.os.Handler().postDelayed(recorder::stop, 3000);
}

验证方法:录制 3 秒后检查 mic_test.pcm 文件大小。理论字节数 = 16000 × 2 × 3 = 96000 字节(单声道 16bit)。实际大小接近该值即说明采集与写入正常。可用 adb pull 导出后通过 Audacity「导入原始数据」选择 16000Hz / 单声道 / 16bit 试听。

常见问题

  • 文件为 0 字节:多为未授予 RECORD_AUDIO 或 getState() 未初始化。
  • 播放为噪声:采样率或声道数不匹配,导入时需与代码参数一致。
  • 录音断续:bufferSize 过小,建议取 getMinBufferSize 的 2 倍以上。