Android多媒体1————AudioRecord实现录音功能
一.前言
在百度实习的时候,导师让我负责语音助手SDK相关的东西,所以就一直想对其进行整理总结,就先从最基础,最简单AudioRecord类开始。
二.官方文档中的AudioRecord
想要了解一个类的具体说明,首先要看看官方文档:
1.概述
AndioRecordlei类的主要功能是让各种JAVA应用管理音频资源,方便从平台的音频输入硬件录制音频。此功能的实现是通过“pulling”(读取)AndioRecord对象的声音数据来完成。在录制过程中,应用所需要的是通过后面三个类方法中的一个区及时获取AudioRecord对象的录音数据。
开始录音时,AudioRecord需要一个初始化相关联的声音buffer,这个buffer主要用来存储新的声音数据,这个buffer的大小,我们可以在对象的构造期间去指定,它表明一个AudioRecord对象还没用被读取(同步)声音数据前能录多长的音。声音数据从音频硬件被读取出来,数据大小不超过整个录音数据的大小(可以分多次读取),即每次读取初始化buffer容量的数据。
2.实现Android录音的流程
- 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小通过getMinBufferSize得到,如果buffer容量过小,将导致对象构造的失败
- 初始化一个buffer,该buffer大于等于AudioRecord对象用于声音数据的buffer大小
- 开始录音
- 创建一个数据流,一边从AudioRecord中读取数据初始化的buffer,一边将buffer中的数据导入数据流中
- 关闭数据流
- 停止录音
三.相关的API
1.构造方法
AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
参数简介:
- audioSource:指的是从哪里采集音频。这里我们当然是从麦克风采集音频,所以此参数的值为MIC
- sampleRateInHz:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。例如要采集低质量的音频就可以使用4000、8000等低采样率。
- channelConfig:android支持双声道和单道声,MONO单声道,STEREO立体声
- audioFormat:采集来的数据使用PCM编码,Android支持的采用大小为16bit或者8bit。采样越大,信息量越多,音质也越高可以在getMinBufferSize()查看。
- bufferSizeInBytes:采集数据需要缓存区的大小,如果不知道最小需要的大小
2.公共方法
- int getAudioFormat():返回配置的音频数据格式
- int getAudioSessionId():返回音频会话 id。
- int getAudioSource():返回音频录制源。
- int getChannelConfiguration():返回配置的通道配置。
- int getChannelCount():返回配置的通道数。
- static int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat):返回成功创建 audio 记录对象所需的最小缓冲区大小 (以字节为单位)。
- int getNotificationMarkerPosition():返回以框架表示的通知标记位置。
- int getPositionNotificationPeriod():返回以框架表示的通知更新期间。
- int getRecordingState():返回音频记录实例的录制状态。
- int getSampleRate():返回以 hz 为单位的配置音频数据采样率
- int getState():返回音频记录实例的状态。
- int read(short[] audioData, int offsetInShorts, int sizeInShorts):从音频硬件读取音频数据, 以便录制到缓冲区中。
- int read(byte[] audioData, int offsetInBytes, int sizeInBytes):从音频硬件读取音频数据, 以便录制到缓冲区中。
- int read(ByteBuffer audioBuffer, int sizeInBytes):从音频硬件读取音频数据, 以便录制到缓冲区中。
- void release():释放本机AudioRecord资源
- int setNotificationMarkerPosition(int markerInFrames)
- int setPositionNotificationPeriod(int periodInFrames)
- void setRecordPositionUpdateListener(AudioRecord.OnRecordPositionUpdateListener listener, Handler handler):设置AudioRecord通知的侦听器,当达到先前设置的标记时,或每次定期记录头位置更新时。
- void startRecording():从AudioRecord实例开始记录。
- void startRecording(MediaSyncEvent syncEvent):当指定的同步事件在指定的音频会话上发生时,从AudioRecord实例开始记录。
- void stop():停止记录
四.使用示例
public class AudioRecordImpl {
private static final String TAG = "AudioRecordImpl";
// 采样率
private static final int SAMPLE_RATE_HZ = 16000;
private static final int BUFFER_SIZE = 640;
private AudioRecord audioRecord;
private Thread recordThread;
//采样频率
private int mAudioSampleRate = SAMPLE_RATE_HZ;
//音频源
private int mAudioSource = MediaRecorder.AudioSource.MIC;
//编码大小
private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
//选择声道
private int mAudioChannel = AudioFormat.CHANNEL_IN_MONO;
// 用于数据统计
private volatile boolean isQueryLog;
private volatile boolean isStartRecord = false;
public AudioRecordImpl() {
setAudio(SAMPLE_RATE_HZ, MediaRecorder.AudioSource.MIC, AudioFormat.ENCODING_PCM_16BIT,
AudioFormat.CHANNEL_IN_MONO);
}
//默认设置
public void setAudio(int sampleRate, int source, int format, int channel) {
mAudioSampleRate = sampleRate;
mAudioSource = source;
mAudioFormat = format;
mAudioChannel = channel;
}
//开始录音
public v startRecord() {
stopRecord();
if (recordThread != null) {
try {
//t.join()方法阻塞调用此方法的线程(calling thread),
// 直到线程t完成,此线程再继续
recordThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//设置线程优先级,android.os.Process.THREAD_PRIORITY_AUDIO-标准音乐播放使用的线程优先级
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
//获取最小buffer
final int bufferSize = AudioRecord.getMinBufferSize(
mAudioSampleRate, mAudioChannel, mAudioFormat);
try {
// 防止某些手机崩溃
//创建audioRecord对象
audioRecord = new AudioRecord(mAudioSource, mAudioSampleRate,
mAudioChannel, mAudioFormat, bufferSize * 10);
} catch (Exception e) {
e.printStackTrace();
LogUtil.dcf(TAG, "new AudioRecord() IllegalStateException ", e);
}
isStartRecord = true;
recordThread = new Thread() {
@Override
public void run() {
super.run();
try {
// 防止某些手机崩溃
if (audioRecord != null) {
audioRecord.startRecording();
}
} catch (IllegalStateException e) {
e.printStackTrace();
LogUtil.dcf(TAG, "startRecording IllegalStateException ", e);
isStartRecord = false;
}
isQueryLog = true;
while (isStartRecord) {
try {
//读取语音数据
byte[] buffer = new byte[BUFFER_SIZE];
int readBytes = audioRecord.read(buffer, 0, buffer.length);
if (readBytes > 0) {
//处理语音数据
fireData(buffer);
}
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.ecf(TAG, "audio recorder exception," + e);
isStartRecord = false;
}
}
// 释放资源
try {
if (audioRecord != null) {
audioRecord.release();
LogUtil.ic(TAG, "audioRecorder release ");
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.wcf(TAG, "stop and release IllegalStateException ", e);
} finally {
audioRecord = null;
}
}
};
recordThread.start();
}
//停止录音
public void stopRecord() {
LogUtil.dc(TAG, "stopRecord");
isStartRecord = false;
}
//释放资源
public void release() {
//释放资源
LogUtil.dc(TAG, "release");
stopRecord();
}
}