一. AudioRecord API 详解
AudioRecord 是安卓零碎提供的用于实现录音的性能类。
依据官网文档:
AndioRecord 类的次要性能就是让各自 JAVA 利用可能治理音频资源,以便它们通过这个类录制硬件设施收集的声音。录音过程,利用须要做的就是调用三个类办法中的一个获取声音数据。
1. read(byte[],int,int)
2. read(short[],int,int)
3. read(ByteBuffer,int)
无论应用哪一个办法,都须要事后设定一个 buffer 用于存储还没有来得及被读取的数据。这个 buffer 的大小能够在结构 AudioRecord 对象的时候指定,一旦确定大小,声音数据从音频硬件中被读出,一次读取的数据大小不能超过该 buffer 的容量。
实现 Android 录音的流程为:
- 结构一个 AudioRecord 对象,其中须要的最小录音缓存 buffer 大小能够通过 getMinBufferSize 办法失去,如果 buffer 容量过小,会导致对象结构失败。
- 初始化一个 buffer,该 buffer 大于等于 AudioRecord 对象用于写声音数据的 buffer 大小。
- 开始录音。
- 创立一个数据流,一边从 AudioRecord 中读取声音数据到初始化的 buffer,一边将 buffer 中数据导入数据流。
- 敞开数据流。
- 进行录音。
二. 应用 AudioRecord 实现录音,并生成.wav 文件
2.1 创立一个 AudioRecord 对象
首先要申明一些全局的变量参数:
private AudioRecord audioRecord = null; // 申明 AudioRecord 对象
private int recordBufSize = 0; // 申明 recordBuffer 的大小字段
获取 buffer 的大小,并创立 AudioRecord:
public void createAudioRecord(){recordBufSize = AudioRecord.getMinBufferSize(frequency,channelConfiguration,EncodingBitRate); //audioRecord 能接管的最小的 buffer 大小
audioRecord = new AudioRecord(MediaRecord.AudioSource.MIC,frequency,channelConfiguration,EncodingBitRate,recordBufSize);
}
2.2 初始化一个 buffer
byte data[] = new byte[recordBufSize];
2.3 开始录音
audioRecord.startRecording();
isRecording = true;
2.4 创立一个数据流,一边从 AudioRecord 中读取声音数据到初始化的 buffer,一边将 buffer 中数据导入数据流
FileOutputStream os = null;
try{os = new FileOutputStream(filename);
}catch(FileNotFoundException e){e.printStackTrace();
}
if(null != os)
{while(isRecording){read = audioRecord.read(data,0,recordBufSize);
// 如果读取音频数据没有呈现谬误,就将数据写入到文件
if(AudioRecord.ERROR_INVALID_OPERATION != read){
try{os.write(data)
}catch(IOException e){e.printStackTrace();
}
}
}
try{os.close();
}catch(IOException e){e.printStackTrace();
}
}
2.5 敞开数据流
批改标记位:isRecording 为 false,下面的 while 循环就主动进行了,数据流也就进行流动了,Stream 也就被敞开了。
isRecording = false;
2.6 进行录音
进行录音之后,留神要开释资源。
if(null != audioRecord){audioRecord.stop();
audioRecord.release();
audioRecord = null;
audioRecord =
}
最初:增加权限 WRITE_EXTERNAL_STORAGE、RECORD_AUDIO
三. 注意事项
依照上述流程录制出的音频文件是无奈间接进行播放的 raw 文件,如果想要放入播放器中进行播放,须要退出文件头。
增加 wave 文件头的代码如下:
public class PcmToWavUtil {
/**
* 缓存的音频大小
*/
private int mBufferSize;
/**
* 采样率
*/
private int mSampleRate;
/**
* 声道数
*/
private int mChannel;
/**
* @param sampleRate sample rate、采样率
* @param channel channel、声道
* @param encoding Audio data format、音频格式
*/
PcmToWavUtil(int sampleRate, int channel, int encoding) {
this.mSampleRate = sampleRate;
this.mChannel = channel;
this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, encoding);
}
/**
* pcm 文件转 wav 文件
*
* @param inFilename 源文件门路
* @param outFilename 指标文件门路
*/
public void pcmToWav(String inFilename, String outFilename) {
FileInputStream in;
FileOutputStream out;
long totalAudioLen;
long totalDataLen;
long longSampleRate = mSampleRate;
int channels = mChannel == AudioFormat.CHANNEL_IN_MONO ? 1 : 2;
long byteRate = 16 * mSampleRate * channels / 8;
byte[] data = new byte[mBufferSize];
try {in = new FileInputStream(inFilename);
out = new FileOutputStream(outFilename);
totalAudioLen = in.getChannel().size();
totalDataLen = totalAudioLen + 36;
writeWaveFileHeader(out, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate);
while (in.read(data) != -1) {out.write(data);
}
in.close();
out.close();} catch (IOException e) {e.printStackTrace();
}
}
/**
* 退出 wav 文件头
*/
private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels, long byteRate)
throws IOException {byte[] header = new byte[44];
// RIFF/WAVE header
header[0] = 'R';
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);
//WAVE
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
// 'fmt' chunk
header[12] = 'f';
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
// 4 bytes: size of 'fmt' chunk
header[16] = 16;
header[17] = 0;
header[18] = 0;
header[19] = 0;
// format = 1
header[20] = 1;
header[21] = 0;
header[22] = (byte) channels;
header[23] = 0;
header[24] = (byte) (longSampleRate & 0xff);
header[25] = (byte) ((longSampleRate >> 8) & 0xff);
header[26] = (byte) ((longSampleRate >> 16) & 0xff);
header[27] = (byte) ((longSampleRate >> 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);
// block align
header[32] = (byte) (2 * 16 / 8);
header[33] = 0;
// bits per sample
header[34] = 16;
header[35] = 0;
//data
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
out.write(header, 0, 44);
}
}
四. MediaRecorder 和 AudioRecord
Andorid SDK 提供了两套音视频采集的 API,别离为 MediaRecorder 和 AudioRecord,前者是一个更加下层的 API,它能够间接把手机麦克风录入的音频数据进行压缩编码(如 AMR、MP3 等)并存成文件,而后者更靠近底层,可能更加自在灵便的管制,能够失去原始的一帧帧 PCM 音频数据。 如果想要简略的做一个录音机,录制成能够播放的音频文件,则推举应用 MediaRecord,而如果想要对音频做进一步解决、或者采纳第三方的编码库进行压缩、以及网络传输等利用,则倡议应用 AudioRecord。 其实 MediaRecord 底层也是调用了 AudioRecord 与 Android Framework 层的 AudioFlinger 进行交互的。 直播中实时采集音频天然要有 AudioRecord。
五. 源码
http://github.com/renhui/Audi…