音频采样,也可以说是音频录制,本篇通过介绍
AudioTrack的使用方法,来对麦克风进行音频采样
一、音频录制
1.1 MediaRecorder
Android开发中,通常会使用MediaRecorder来对音频进行录制,并最终得到一个编码好的音频文件(一般是录制成AAC编码格式)
MediaRecorder的使用流程一般是:
-
构建
MediaRecorder实例MediaRecorder mediaRecorder = new MediaRecorder() -
设置录制声源(一般是设置为
麦克风)mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC) -
设置编码格式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) -
设置输出格式
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS) -
设置录制路径
mediaRecorder.setOutputFile(path) -
准备
mediaRecorder.prepare() -
开始
mediaRecorder.start() -
结束
mediaRecorder.stop()
可以看到,只需要传入必要的参数,就可以得到一个能在任何支持AAC格式的设备上播放该音频文件,不过,这不是本篇文章的重点,本篇文章的重点是介绍如何录制音频的原始格式,即录制Pcm格式的音频文件
1.2 AudioRecord
录制Pcm格式的音频文件,使用的是AudioRecord,该组件可能在一般的Android开发中很少会用到,不过想要录制Pcm格式的音频文件,一个在应用层的选择,就是AudioRecord
AudioRecord的使用分为以下几个部分
- 开启子线程
- 构建实例
- 开始录制
- 循环从
AudioRecord读取数据,写入文件 - 停止录制,释放资源
下面会详细介绍AudioRecord的录制流程
二、AudioRecord录制流程
2.1 开启子线程
因为后面需要有一个循环不断的读取和写入数据,故将录制相关操作放入子线程中
private static class RecordThread extends Thread {
public RecordThread() {}
}
2.2 配置必要的参数
private static class RecordThread extends Thread {
/**
* pcm录制组件
*/
private AudioRecord audioRecord;
/**
* 文件输出
*/
private FileOutputStream fos;
private final String path;
/**
* 声源(一般是来自麦克风)
*/
private final int audioSource;
/**
* 采样率
*/
private final int sampleRateInHz;
/**
* 声道设置
*/
private final int channelConfig;
/**
* 编码格式
*/
private final int audioFormat;
/**
* 音频缓存buffer
*/
private int bufferSizeInByte;
/**
* 是否停止录制
*/
private boolean isStopRecord = false;
/**
* 构造方法(传入必要的参数)
*/
public RecordThread(String path,
int audioSource,
int sampleRateInHz,
int channelConfig,
int audioFormat
) {
this.path = path;
this.audioSource = audioSource;
this.sampleRateInHz = sampleRateInHz;
this.channelConfig = channelConfig;
this.audioFormat = audioFormat;
}
}
构建AudioRecord需要以下参数
-
audioSource声源,一般写麦克风
MediaRecorder.AudioSource.MIC -
sampleRateInHz采样率,通常会使用
44100 -
channelConfig声道设置,比如单声道
CHANNEL_IN_MONO,双声道CHANNEL_IN_STEREO -
audioFormat编码格式,通常可以选择
ENCODING_PCM_8BIT,也可以选择ENCODING_PCM_16BIT -
bufferSizeInByte该参数表示的是音频缓存的
Buffer字节数,用于读取AudioRecord数据,它是通过AudioRecord.getMinBufferSize方法获取
2.3 初始化
在写完构造方法后,可以重写Thread的run方法
# run()
@Override
public void run() {
super.run();
initIo();
initAudioRecord();
record();
}
run方法里面调用了三个方法,分别是
-
initIo()因为是写入文件,所以需要初始化文件
IO流 -
initAudioRecord()根据构造方法传入的参数,构建
AudioRecord -
record()开始真正的录制
# initIo()
/**
* 初始化IO
*/
private void initIo() {
if (TextUtils.isEmpty(path)) {
return;
}
File file = new File(path);
if (file.exists()) {
file.delete();
}
try {
fos = new FileOutputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
fos = null;
}
}
# initAudioRecord()
/**
* 初始化pcm录制组件
*/
private void initAudioRecord() {
bufferSizeInByte = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInByte);
}
2.4 录制
进入到record()方法时,就开始真正的录制
# record()
/**
* 开始录制
*/
private void record() {
if (audioRecord == null || fos == null) {
return;
}
byte[] data = new byte[bufferSizeInByte];
audioRecord.startRecording();
for (; ; ) {
if (isStopRecord) {
release();
break;
}
int readSize = audioRecord.read(data, 0, bufferSizeInByte);
if (readSize <= 0) {
isStopRecord = true;
continue;
}
try {
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在record()方法中,构建了一个for(;;)形式的死循环,然后在内部会有一个isStopRecord的标志位来调用release()方法,并退出循环
在死循环里,每次都会调用audioRecord.read()方法读取音频数据,接着将数据写入对应的文件中
等手动结束,或者是读取到的数据小于等于0时,则释放资源
# release()
/**
* 释放资源
*/
private void release() {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
if (fos != null) {
try {
fos.close();
fos = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
这一流程结束,即得到录制好了Pcm文件,不过,一般的播放器是没办法播放该文件,那么下一篇文章将介绍如何播放Pcm格式的音频文件
三、GitHub
相关的类均可在GitHub中找到