Android 录音详解(一)—— MediaRecorder、AudioRecord、生成wav格式、边录边播

越来越多的 APP 都用到了手机的录音功能,比如搜索、聊天、输入、K歌等...

本系列详细介绍 Android 中录音功能的实现,包括:

Android 录音详解(四)—— 录音添加背景音乐

Android 中的录音主要有两种方式 MediaRecorder 和 AudioRecord,下面分别介绍:

MediaRecorder(基于文件)

可以录制音、视频;

封装了录制、编码、压缩、线程等功能,直接生成可播放的音频文件;

优点:封装度高,操作简单

缺点:编码格式有限,.aac  .amr  .3gp,但是没有 mp3、wav 格式。

AudioRecord(基于字节流)

只能录制音频;

输出的是 PCM 的声音数据,如果保存成文件是不能直接播放的,需要编码;

可以捕获音频流,边录制边处理,比如编码、变声、添加背景音乐。

优点:更灵活

缺点:需自行处理编码、开线程等工作

应用场景:语音聊天、汤姆猫、K歌...

PCM:

Pulse Code Modulation(脉冲编码调制),是对连续变化的模拟信号进行抽样、量化和编码产生的数字信号。

它不是一种音频格式,它是声音文件的元数据,也就是声音的内容,没有文件头。经过某种格式的压缩、编码算法处理以后,再加上这种格式的文件头,才是这种格式的音频文件。

音频参数:

采样频率:

自然界的声音转换成数字格式时,要对它进行采样,每秒钟采样的次数就是采样率。就好比电影的1秒24帧画面。最常用:44.1kHz。

采样位数:

一个采样样本用多少位二进制数编码,最常用:16位。

声道数:

分为单声道和双声道,双声道又叫立体声,双声道音频文件比单声道大一倍。

比特率(码率):

每秒钟音频文件所占的 bit 数。单位 :kbps(每秒千比特数)。比特率(原始音频 PCM) = 采样频率 * 采样位数 * 声道数,这是未经压缩的比特率,压缩后会远小于这个值。

采用44.1kHz采样频率、16位采样位数、双声道编码的原始音频 PCM 比特率为:1411.2 kbps 。而最常见的 mp3 格式的比特率为:128kbps,约 1MB/分钟。

编码格式:

将原始音频 PCM 采用特定压缩算法处理后,加上文件头,所保存成的文件的格式。例如 mp3、wav、aac...

编码格式:

mp3

是当今最流行的一种数字音频编码和有损压缩格式,就是将 PCM 通过算法进行压缩,常规 mp3 文件约为1MB/分钟。

aac

是 mp3 的下一代格式,也是有损压缩,相对于mp3,aac 格式的音频更佳,文件更小。ios 平台也支持,跨平台性好。

wav

最流行的非压缩数据格式,微软开发。

amr

压缩比比较大,但相对其他的压缩格式质量比较差,多用于人声,通话录音。

riff

一种文件描述的格式,wav文件就采用了riff描述,前面44字节就是 riff 描述内容,就是文件头。

MediaRecorder

首先在 AndroidManifest 配置文件中添加录音权限:

当然 Android 6.0 以上还要动态获取权限,具体实现方式请百度之。

// 录音对象声明

private MediaRecorder mRecorder;

private void startRecording() {

// 创建录音对象

mRecorder = new MediaRecorder();

// 设置声音来源 MIC 即手机麦克风

mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

// 设置音频格式 aac

mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);

// 设置录音文件

mRecorder.setOutputFile(getExternalCacheDir() + "/demo.aac");

// 设置编码器

mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

try {

// 准备录音

mRecorder.prepare();

} catch (IOException e) {

e.printStackTrace();

}

// 开始录音

mRecorder.start();

}

录音是耗时操作,但是,由于 MediaRecorder 已经封装了线程,故以上代码放在主线程即可。

MediaRecorder 是很占资源的,使用完毕需要释放掉:

private void stopRecording() {

// 停止录音

mRecorder.stop();

// 释放资源

mRecorder.release();

// 引用置空

mRecorder = null;

}

AudioRecord

首先在 AndroidManifest 配置文件中添加录音权限:

当然 Android 6.0 以上还要动态获取权限,具体实现方式请百度之。

// 录音状态

private boolean isRecording = true;

private void startRecording(){

// 耗时操作要开线程

new Thread(){

@Override

public void run() {

// 音源

int audioSource = MediaRecorder.AudioSource.MIC;

// 采样率

int sampleRate = 44100;

// 声道数

int channelConfig = AudioFormat.CHANNEL_IN_STEREO;//双声道

// 采样位数

int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

// 获取最小缓存区大小

int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

// 创建录音对象

AudioRecord audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, minBufferSize);

try {

// 创建随机读写流

RandomAccessFile raf = new RandomAccessFile(getExternalCacheDir() + "/demo.wav", "rw");

// 留出文件头的位置

raf.seek(44);

byte[] buffer = new byte[minBufferSize];

// 录音中

audioRecord.startRecording();

isRecording = true;

while (isRecording) {

int readSize = audioRecord.read(buffer, 0, minBufferSize);

raf.write(buffer,0,readSize);

}

// 录音停止

audioRecord.stop();

audioRecord.release();

// 写文件头

WriteWaveFileHeader(raf, raf.length(),sampleRate,2,sampleRate*16*2/8);

raf.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}.start();

}

/**

* 为 wav 文件添加文件头,前提是在头部预留了 44字节空间

*

* @param raf

* 随机读写流

* @param fileLength

* 文件总长

* @param sampleRate

* 采样率

* @param channels

* 声道数量

* @param byteRate

* 码率 = 采样率 * 采样位数 * 声道数 / 8

* @throws IOException

*/

private void WriteWaveFileHeader(RandomAccessFile raf, long fileLength, long sampleRate, int channels, long byteRate) throws IOException {

long totalDataLen = fileLength + 36;

byte[] header = new byte[44];

header[0] = 'R'; // RIFF/WAVE header

header[1] = 'I';

header[2] = 'F';

header[3] = 'F';

header[4] = (byte) (totalDataLen & 0xff);

header[5] = (byte) ((totalDataLen >> 8) & 0xff);

header[6] = (byte) ((totalDataLen >> 16) & 0xff);

header[7] = (byte) ((totalDataLen >> 24) & 0xff);

header[8] = 'W';

header[9] = 'A';

header[10] = 'V';

header[11] = 'E';

header[12] = 'f'; // 'fmt ' chunk

header[13] = 'm';

header[14] = 't';

header[15] = ' ';

header[16] = 16; // 4 bytes: size of 'fmt ' chunk

header[17] = 0;

header[18] = 0;

header[19] = 0;

header[20] = 1; // format = 1

header[21] = 0;

header[22] = (byte) channels;

header[23] = 0;

header[24] = (byte) (sampleRate & 0xff);

header[25] = (byte) ((sampleRate >> 8) & 0xff);

header[26] = (byte) ((sampleRate >> 16) & 0xff);

header[27] = (byte) ((sampleRate >> 24) & 0xff);

header[28] = (byte) (byteRate & 0xff);

header[29] = (byte) ((byteRate >> 8) & 0xff);

header[30] = (byte) ((byteRate >> 16) & 0xff);

header[31] = (byte) ((byteRate >> 24) & 0xff);

header[32] = (byte) (2 * 16 / 8); // block align

header[33] = 0;

header[34] = 16; // bits per sample

header[35] = 0;

header[36] = 'd';

header[37] = 'a';

header[38] = 't';

header[39] = 'a';

header[40] = (byte) (fileLength & 0xff);

header[41] = (byte) ((fileLength >> 8) & 0xff);

header[42] = (byte) ((fileLength >> 16) & 0xff);

header[43] = (byte) ((fileLength >> 24) & 0xff);

raf.seek(0);

raf.write(header, 0, 44);

}

以下是停止录音的方法:

private void stopRecording() {

// 停止录音

isRecording = false;

}

边录边播(扩音器)

AudioRecord + AudioTrack

// 录音状态

private boolean isRecording = true;

private void start() {

// 耗时操作要开线程

new Thread() {

@Override

public void run() {

// 音源

int audioSource = MediaRecorder.AudioSource.MIC;

// 采样率

int sampleRate = 8000;

// 声道数

int channelConfig = AudioFormat.CHANNEL_IN_STEREO;//双声道

// 采样位数

int audioFormat = AudioFormat.ENCODING_PCM_8BIT;

// 获取录音最小缓存区大小

int recorderBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

// 创建录音对象

AudioRecord audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, recorderBufferSize);

// 音频类型

int streamType = AudioManager.STREAM_MUSIC;

// 静态音频还是音频流

int mode = AudioTrack.MODE_STREAM;

// 获取播放最小缓存区大小

int playerBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);

// 创建播放对象

AudioTrack audioTrack = new AudioTrack(streamType, sampleRate, channelConfig, audioFormat, playerBufferSize, mode);

// 缓存区

byte[] buffer = new byte[recorderBufferSize];

// 录音中

audioTrack.play();

audioRecord.startRecording();

isRecording = true;

while (isRecording) {

audioRecord.read(buffer, 0, recorderBufferSize);

audioTrack.write(buffer, 0, buffer.length);

}

// 录音停止

audioRecord.stop();

audioTrack.stop();

audioRecord.release();

audioTrack.release();

}

}.start();

}

停止录音和播放:

private void stop() {

// 停止录音

isRecording = false;

}

本篇介绍了 Android 中两种录音方式的对比,介绍了录音中会涉及到的参数和一些概念。用 MediaRecorder 实现了 aac 格式音频的录制,当然也可以录制 amr、3gp、mp4 格式;其他常用的格式如 wav,就需要用到 AudioRecord,以上也同样实现了,至于更常见的 mp3 格式的录制留在下一篇讲解。由于 AudioRecord 可以很方便的控制字节流,那么实时转码、边录边播功能都得以实现。

android 录音原始文件_Android 录音详解(一)—— MediaRecorder、AudioRecord、生成wav格式、边录边播...相关推荐

  1. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解 (旧版本 | 仅作参考)

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

  2. android+发邮件,Android发送邮件的方法实例详解

    Android发送邮件的方法实例详解 时间:2021-05-20 本文实例讲述了Android发送邮件的方法.分享给大家供大家参考,具体如下: 在android手机中实现发送邮件的功能也是不可缺少的. ...

  3. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) -- TelephonyManager详解 >的1.4小节.  从TelephonyRegistry的大部分方法中:  ...

  4. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.  这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\And ...

  5. Android Telephony分析(二) ---- RegistrantList详解

    前言 本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程.  在Telephony模块中,在RIL.Tracker(ServiceStateTr ...

  6. Android Gradle manifestPlaceholders 占位符详解

    Android Gradle manifestPlaceholders 占位符详解 在实际项目中,AndroidManifest里十几个地方的值是需要动态的改变(生成apk文件的时候).如果每次去改也 ...

  7. Android Loader 异步加载详解二:探寻Loader内部机制

    Android Loader 异步加载详解二:探寻Loader内部机制 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/7025991 ...

  8. Android Loader 异步加载详解一:基础概念

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/70241844 本文出自[赵彦军的博客] Android Loader 异步加载详解 ...

  9. Android 动画之ScaleAnimation应用详解

    本节讲解ScaleAnimation 动画在应用中的实现,有需要的朋友可以参考下 android中提供了4中动画: AlphaAnimation 透明度动画效果 ScaleAnimation 缩放动画 ...

最新文章

  1. 洛谷1216 数字三角形
  2. 【qduoj - 312】寻找唯一的萌妹(卡时)
  3. 有python基础学java_Python基础学习篇
  4. FCPX插件Day of the Dead Titles - 恐怖风格文本动画模板
  5. Microsoft Windows XP SP3安装测试手记
  6. 23种设计模式(十五)接口隔离之适配器
  7. OLAP -- ODS 项目总结 -- BI 中的关键
  8. 我们一般用的正则表达式合集和工具 any-rule
  9. Social Influence as Intrinsic Motivation for Multi-Agent Deep Reinforcement Learning-笔记
  10. 复数矩阵相乘的扩展矩阵计算方法
  11. 简单理解有监督学习、无监督学习、强化学习
  12. Linux:详细的RPM和YUM/DNF管理工具,配置仓库等
  13. 我的世界(9)-服务器地图创建、管理(Multiverse插件)
  14. 旋转卡壳算法求最小外接矩形代码
  15. PHP破解微信图片盗链
  16. Gitlab的branch与Tag的使用
  17. 通过jstack分析cpu问题
  18. aop编程时出现错误信息:java.lang.NoClassDefFoundError
  19. win11恢复win10右键菜单栏
  20. 社交软件实现查看附近的人

热门文章

  1. 问题:wps表格用鼠标滚轮上下移动表格时,不是平滑移动,而是按单元格跳动的。已解决
  2. 数据分布方式:哈希与一致性哈希
  3. ORA-00918:未明确定义列,在mybatis中
  4. java 保存2位小数_java 保留2位小数
  5. iOS开发----开发工具大全
  6. 兔偶得之 全自动雨伞
  7. 最新鸿蒙os系统下载,快手鸿蒙版下载-快手鸿蒙OS系统官方最新版 v9.4.20.19713-114手机乐园...
  8. oracle共享锁和排他锁
  9. 计算机:游戏25关,烧脑吃鸡大作战第25关怎么通关 第25关通关技巧介绍
  10. Unity动画系统-给人物添加动画