音频采样,也可以说是音频录制,本篇通过介绍
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中找到