Android AudioRecord音频采集

环境:compileSDKVersion 30

工具类: android.media.AudioRecord

1. 初始化AudioRecord

private static final int sampleRate = 44100;
private static final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private static final int pcm16bit = ENCODING_PCM_16BIT;
AudioFormat audioFormat = new AudioFormat.Builder()
        .setSampleRate(sampleRate)
        .setChannelMask(channelConfig)
        .setEncoding(pcm16bit)
        .build();

mBufferSize = AudioRecord.getMinBufferSize(
        sampleRate,
        channelConfig,
        pcm16bit);

mAudioRecord = new AudioRecord.Builder()
        .setAudioFormat(audioFormat)
        .setAudioSource(MediaRecorder.AudioSource.MIC)
        .setBufferSizeInBytes(mBufferSize)
        .build();
  • sampleRate 以多大的采样频率来采集音频,用的最多的是44.1kHz采样率,若失败可以用16kHz的重试。SDK中对采样频率的范围有限制。
    public static final int SAMPLE_RATE_HZ_MIN = 4000;
    public static final int SAMPLE_RATE_HZ_MAX = 192000;
  • channelConfig 采集几个声道的声音,可选立体声或单声道,可见立体声也是左右耳采集是伪立体声,性能考虑一般以单声道方式采集。
    public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
    public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
  • audioFormat 采样的量化格式,也即位深度;通常为16bit ENCODING_PCM_16BIT。
  • AudioSource 音频采集的输入源,当前为MIC即通过麦克风输入。
  • bufferSizeInBytes AudioRecord内部音频缓冲区的大小。不同厂商定义不同,越小则产生的延时就越小。AudioRecord有静态方法辅助计算缓冲区大小: AudioRecord#getMinBufferSize内部调用了native层函数获取缓冲区大小。
    static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)

AudioRecord初始化结束可通过AudioRecord#getState判断初始化是否成功,返回STATE_INITIALIZED即成功.

2. 开始采集

        AudioRecord启动采集是生产消费模型,生产者即采集方不断将数据写入缓冲区,消费者不断读取,如果缓冲区大小超过初始化时设置的值,则会有overrun的错误。所以需要启动一个新线程来循环读取。

        AudioRecord采集函数签名

public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)

获取到audioData数组后,可以通过IO写入到本地。

private final class MyRecordThread extends Thread {
    @Override
    public void run() {
        try {
            final File cacheFile = AudioDataCache.Instance.getCacheFile();
            BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(cacheFile));
            while (mState.get()) {
                byte[] audioData = new byte[mBufferSize];
                int read = mAudioRecord.read(audioData, 0, audioData.length);
                if (checkRet(read)) {
                    fos.write(audioData, 0, audioData.length);
                    fos.flush();
                }
            }
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean checkRet(int code) {
        switch (code) {
            case ERROR_INVALID_OPERATION:
            case ERROR_BAD_VALUE:
            case ERROR_DEAD_OBJECT:
                Log.i("Mg", "RecordError: code=" + code);
                return false;
            default:
                return true;
        }
    }
}

        

3. 停止采集

        AudioRecord#stop停止采集。

        AudioRecord#release释放录音器,以便其他设备使用。

        通过AtomicBoolean类型标志位停止读取消费者线程的死循环。注意的是需要在release或stop之后再标志位置为false,否则会有文件写出不完全的问题。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值