Android AudioRecoder音频采集

1、简介

Android中用于音频采集的API包括两种,MediaRecoder和AudioRecoder。

  • MediaRecoder
    MediaRecorder 是基于 AudioRecorder 的 API(最终还是会创建AudioRecord用来与AudioFlinger进行交互) ,它可以直接将采集到的音频数据转化为执行的编码格式,并保存。这种方案相较于调用系统内置的用用程序,便于开发者在UI界面上布局,而且系统封装的很好,便于使用,唯一的缺点是使用它录下来的音频是经过编码的,没有办法的得到原始的音频。同时MediaRecorder即可用于音频的捕获也可以用于视频的捕获相当的强大。实际开发中没有特殊需求的话,用的是比较多的!
  • AudioRecoder
    AudioRecord 是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据进行处理。AudioRecord这种方式采集最为灵活,使开发者最大限度的处理采集的音频,同时它捕获到的音频是原始音频PCM格式的。像做变声处理的需要就必须要用它收集音频。

2、AudioRecoder的创建

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes)
  • audioSource 该参数是音频采集的来源,定义的值在MediaRecorder.AudioSource类中。MIC表示采集源为手机麦克风。

  • sampleRateInHz 该参数表示采样率。44100Hz是科学家给出的最佳采样频率,并且在Android中也是多所有Android设备兼容最好的采样频率。

  • channelConfig 通道数,也就是声道数。常量定义在AudioFormat类中,CHANNEL_IN_MONO表示单声道,CHANNEL_IN_STEREO表示双声道。注意,之前可以通过CHANNEL_CONFIGURATION_MONO和CHANNEL_CONFIGURATION_STEREO指定,现在已经启用,使用了CHANNEL_IN_MONO和CHANNEL_IN_STEREO。

  • audioFormat 指音频采样精度,或者说是音频位宽。常量定义在AudioFormat类中,可选AudioFormat.ENCODING_PCM_16BIT或AudioFormat.ENCODING_PCM_8BIT等。16位将占用更多的空间,但采集的音频也更加接近原声。AudioFormat.ENCODING_PCM_16BIT目前兼容性更好。

  • bufferSizeInBytes 这个参数是AudioRecoder内部音频缓冲区的大小。该缓冲区的大小不能小于一个“音频帧”的大小。音频帧的计算方法为:int size = 采样率 x 位宽 x 采样时间 x 声道数。
    采样时间是指采集一帧数据所用的时间,它的取值一般在2.5ms~120ms 之间,根据手机厂商等因素决定。每一帧采样的时间越短,产生的延时就越小,但是碎片化的数据也就越多。
    AudioRecoder类中提供了一个静态方法int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)来获取缓冲区大小。音频缓冲区大小必须是“音频帧”大小的2~N倍。这里强烈建议使用该方法去计算缓冲区大小,而不是手动去计算。

3、音频采集

创建好AudioRecoder之后,就可以调用AudioRecord.startRecording()方法来开始采集。这里记得添加录音权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
AudioRecord.stop()方法用于停止录音。
使用完AudioRecoder后记得调用AudioRecoder.release()方法释放掉。
AudioRecoder会将采集的音频数据放到缓存区中。所以我们在采集的同时,需要开启一个新线的线程将采集的数据及时的读取出来。如果读取不及时读出数据会出现overrun。数据读取调用:
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);

4、代码示例

    private static final int SAMPLE_RATE_IN_HZ = 44100;
    private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
    private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

    /**
     * 初始化AudioRecoder
     */
    private void initAudioRecorder() {
        /**
         * sampleRateInHz 指定采集音频的采样频率,比较通用的是44100(44.1kHz),这个值是科学家们通过奈葵斯特采样定理得出的一个人能接受最佳的采样频率值
         * channelConfig 指定AudioRecord采集几个声道的声音,预设值定义在AudioFormat中,常用值有 CHANNEL_CONFIGURATION_MONO(单声道) 和 CHANNEL_CONFIGURATION_STEREO(双声道)
         * audioFormat 指定采样PCM数据的采样格式,预设值定义在也AudioFormat中,常用值有 ENCODING_PCM_8BIT、ENCODING_PCM_16BIT和ENCODING_PCM_FLOAT,值得强调的是ENCODING_PCM_16BIT可以保证兼容大部分Andorid手机
         *
         */
        recordBufSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ, CHANNEL_CONFIG, AUDIO_FORMAT);
        /**
         * bufferSizeInBytes 配置AudioRecord内部的音频数据缓冲区,一般来说缓存区越小,产生的音频延迟也越小;值得注意的是,我们可以利用AudioRecord.getMinBufferSize()这个方法帮我们算出最小的缓存区大小,这个数值最好不要自己计算,毕竟不同厂商可能有不同的缓存区采集实现
         */
        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_IN_HZ, CHANNEL_CONFIG, AUDIO_FORMAT, recordBufSize);

        if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
            mState = 1;
            Log.e(TAG, "onCreate: AudioRecord创建成功,recordBufSize = " + recordBufSize);
        } else {
            Log.e(TAG, "onCreate: AudioRecord创建失败");
        }
    }

    /**
     * 开始录音
     */
    private void startRecord() {
        if (baos != null) {
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            baos = null;
        }
        baos = new ByteArrayOutputStream();
        //开始录音
        audioRecord.startRecording();
        //修改状态为开始录音
        mState = 2;
        //开启线程
        new CollectThread().start();
    }

    /**
     * 停止录音
     */
    private void stopRecord() {
        audioRecord.stop();
        audioRecord.release();
        mState = 0;
    }

    /**
     * 采集线程
     */
    class CollectThread extends Thread {
        @Override
        public void run() {
            int read;
            while (mState == 2) {
                //创建byte数据用户保存录音数据
                byte[] pcmData = new byte[recordBufSize];
                //根据recordBufSize从audioRecord中读取指定长度的数据存入mPcmData
                read = audioRecord.read(pcmData, 0, recordBufSize);
                if (read >= AudioRecord.SUCCESS) {
                    synchronized (AudioRecordActivity.class) {
                        //保存数据到OutputStream
                        try {
                            baos.write(pcmData);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

这里只是介绍了使用AudioRecoder进行音频数据的采集,示例代码只是部分代码。在这里记录一下自己学习的过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容