音视频学习:AudioRecord的简单使用

Android  音视频  2020年7月28日 pm12:02发布4年前 (2020)更新 91es.com站长
97 0 0

前言

相关文章摘抄过,但由于不是自己写的或者敲过代码,后面就忘了。

本站主要简单的介绍AudioRecord的使用。

好记性不如烂笔头

正文

Android录音的流程:

  1. 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
  2. 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
  3. 开始录音
  4. 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流。
  5. 关闭数据流
  6. 停止录音

AudioRecord参数简介

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes)
  1. audioSource 音频采集的输入源。
public static final int MIC = 1;   //表示手机麦克风输入
  1. sampleRateInHz采样率

录音设备1S内对声音信号的采集次数,单位Hz

目前44100Hz是唯一可以保证兼容所有Android手机的采样率。

  1. channelConfig 通道数的配置

AudioFormat中有如下定义:

public static final int CHANNEL_IN_LEFT = 0x4;
public static final int CHANNEL_IN_RIGHT = 0x8;
public static final int CHANNEL_IN_FRONT = 0x10;
//单通道
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
//双通道
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
  1. audioFormat 音频数据位宽配置

用来配置数据位宽,有如下选项:

public static final int ENCODING_PCM_16BIT = 2;
public static final int ENCODING_PCM_8BIT = 3;
  1. bufferSizeInBytes 音频缓冲区大小

该值不能低于一帧音频帧的大小。

计算方式:

int size = 采样率 * 采样时间 * 位宽 * 通道数

其中采样时间一般取2.5ms~120ms,具体取多少由厂商或者应用决定。

每一帧采样的时间越短,产生的延时越小,但碎片化的数据也会越多。

这个值有接口直接获取,不需要我们计算。

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)

AudioRecord的主要方法

//开始录制
audioRecord.startRecording();
//停止录制
audioRecord.stop();
//读取录音数据
audioRecord.read(bytes,0,bytes.length);

代码片段

权限配置和获取
  1. AndroidManifest.xml 配置需要的权限
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
  1. 动态申请权限

由于Android 6.0后,权限申请严格,因此需要手动申请权限:

在Activity中主动申请

    //2 是requestCode
    PermissionUtils.requestPermission(this, 2);


    //重写(下面只是打印日志)
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        Log.d(TAG, "onRequestPermissionsResult requestCode : " + requestCode);
        if (requestCode == 2) {
            for (int i = 0; i < permissions.length; i++) {
                Log.d(TAG, "onRequestPermissionsResult : " + permissions[i] + " grantResults : " + grantResults[i]);
            }
        }
    }
public class PermissionUtils {

    private static final String TAG = MyApp.TAG + PermissionUtils.class.getSimpleName();

    /**
     * 需要申请的权限
     */
    public static final String REQUEST_MANIFEST_PERMISSION[] = new String[]{
            Manifest.permission.RECORD_AUDIO
    };

    /**
     * Android 6.0后非系统应用需要申请权限
     *
     * @param activity
     * @param requestCode
     */
    public static void requestPermission(Activity activity, int requestCode) {
        if (null == activity) {
            Log.d(TAG, "requestPermission null : ");
            return;
        }
        Log.d(TAG, "requestPermission requestCode : " + requestCode);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ActivityCompat.requestPermissions(activity, REQUEST_MANIFEST_PERMISSION, requestCode);
        }
        return;
    }
}
AudioRecord封装代码

下面是封装的RecordUtils代码。

public class RecordUtils {

    private static String TAG = MyApp.TAG + RecordUtils.class.getSimpleName();

    private static final int DEFAULT_SOURCE = MediaRecorder.AudioSource.MIC;  //麦克风
    private static final int DEFAULT_RATE = 44100;    //采样率
    private static final int DEFAULT_CHANNEL = AudioFormat.CHANNEL_IN_STEREO;   //双通道(左右声道)
    private static final int DEFAULT_FORMAT = AudioFormat.ENCODING_PCM_16BIT;   //数据位宽16位

    private static AudioRecord mAudioRecord = null;
    private static int mMinBufferSize = 0;
    private static boolean isRecording = false;

    private static RecordThread mRecordThread = null;

    private static IRecordBufferListener mIRecordBufferListener = null;


    /**
     * start record
     */
    public static void startRecord() {
        mMinBufferSize = AudioRecord.getMinBufferSize(DEFAULT_RATE, DEFAULT_CHANNEL, DEFAULT_FORMAT);
        Log.d(TAG, "startRecord  mMinBufferSize : " + mMinBufferSize);
        if (mMinBufferSize == AudioRecord.ERROR_BAD_VALUE) {
            Log.d(TAG, "startRecord  error 1 ");
            return;
        }
        mAudioRecord = new AudioRecord(DEFAULT_SOURCE, DEFAULT_RATE, DEFAULT_CHANNEL,
                DEFAULT_FORMAT, mMinBufferSize);
        if (null == mAudioRecord || mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
            Log.d(TAG, "startRecord  error 2 ");
            return;
        }
        //启动录音
        mAudioRecord.startRecording();
        //设置录音标志位
        isRecording = true;
        //使用线程读取录音数据
        mRecordThread = new RecordThread();
        mRecordThread.start();
        return;
    }

    /**
     * stop record
     */
    public static void stopRecord() {
        Log.d(TAG, "stopRecord mAudioRecord : " + mAudioRecord);
        isRecording = false;
        //暂停录音线程
        if (null != mRecordThread) {
            try {
                mRecordThread.join();
                mRecordThread = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //停止录音
        if (null != mAudioRecord) {
            Log.d(TAG, "stopRecord getRecordingState() : " + mAudioRecord.getRecordingState());
            mAudioRecord.stop();
            mAudioRecord.release();
            mAudioRecord = null;
            Log.d(TAG, "stopRecord: ");
        }
        return;
    }


    /**
     * 录音线程
     */

    private static class RecordThread extends Thread {

        @Override
        public void run() {
            super.run();
            byte[] buffer = null;
            while (isRecording) {
                buffer = new byte[mMinBufferSize];
                int result = mAudioRecord.read(buffer, 0, buffer.length);
                Log.d(TAG, "startRecord result : " + result + " isRecording : " + isRecording);
            }
        }
    }

}

参考文章

  1. 音视频学习系列第(二)篇---音频采集和播放
  2. Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件

 历史上的今天

  1. 2023: JNI之引用简介(0条评论)
  2. 2022: [代码片段]GradientTextView渐变的TextView(0条评论)
  3. 2019: 梁实秋:排队(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

Android中获得 LayoutInflater 实例的三种方式

以下文章对LayoutInflater总结的不错,摘抄于此,部分内容稍微改动。在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而f...

[转]android NTP时间同步

推荐使用 极客导航:极客导航(http://www.91es.com/jike.html)相关文件:frameworks/base/services/java/com/android/server/SystemServer.javaframeworks/base/services/j...

史铁生:爱情是孤独的证明

我知道一位现代女性,她说只要她的丈夫是爱她的,她丈夫的性对象完全可以不限于她,她说她能理解,她说她自己并不喜欢这样但是她能理解她的丈夫,她说:“只要他爱我,只要他仍然是爱我的,只要他对别人不是爱,他只爱我。”可是,当那男人真的有了另外的性对象而且这样的事情慢慢多起来时,这位现代女性还是陷入了痛苦。...

Kotlin变量的类型转换简介

前言简单记录一下Kotlin中变量类型的转换,记录于此,方便自己查阅。正文如果将一种数据类型的值赋给另一种不同的数据类型的变量时,则需要进行数据类型转换。根据转换方式的不同,数据类型转换可分为两种:智能类型转换和强制类型转换。类型检查在类型转换前,为了避免异常,可以通过is来进行类型检...

木心 :至少,每天要看书

不要讲文学是崇高伟大的。文学可爱。大家课后不要放弃文学。文学是人学。至少,每天要看书,开始读书,要浅,浅到刚开始就可以居高临下。一上来听勃拉姆斯第一交响乐,你会淹死。一开始听《圣母颂》、《军队进行曲》,很好。我小时候听这些,后来到杭州听贝多芬的《月光奏鸣曲》,居然完全不懂,对西方,一开始从基督教...

系统应用MediaButton的使用方式

前言这个是Android高版本注册监听MediaButton的使用,没有测试,至于测试普通应用,会重新整理一篇文章。不想误人子弟,文章大部分设置为私人查阅,抱歉正文隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读参考文章这个是从同事那拷贝的,没有测试。