我是靠谱客的博主 笨笨耳机,最近开发中收集的这篇文章主要介绍Android-音视频(2):用AudioRecord采集麦克风PCM并保存到文件1.先了解一下录音流程2.源码如下:,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
1.先了解一下录音流程
- 1.定义AudioRecord录音相关参数,如 音频采集源、音频采样率、声道、数据格式、最小录音缓存
//音频采集来源
private static final int mAudioSource = MediaRecorder.AudioSource.MIC; //麦克风
//音频采样率 (MediaRecoder的采样率通常是8000Hz AAC的通常是44100Hz.设置采样率为44100目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
private static final int mSampleRateInHz = 44100;
//声道
private static final int mChannelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO; //单声道
//数据格式 (指定采样的数据的格式和每次采样的大小)
//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
//指定最小录音缓冲区大小
private int mBufferSizeInBytes;
- 2.通过音频采样率、通道、数据格式,计算出最小录音缓冲区的大小
//计算最小录音缓冲区大小
mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat);
- 3.创建AudioRecord,参数为上面的5个
mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,mAudioFormat,mBufferSizeInBytes);
- 4.创建一个文件用于保存音频PCM。
//存储AudioRecord录音PCM数据的文件
private File mRecordingFile;
//录音pcm数据文件路径+文件名
private static final String mFileName = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"audiorecordtest.pcm";
//将PCM数据写入文件的流
private DataOutputStream mDataOutputStream;
//创建录音PCM数据存储的文件
mRecordingFile = new File(mFileName);
if (mRecordingFile.exists()){
mRecordingFile.delete();
}
mRecordingFile.createNewFile();
//初始化流
mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));
- 5.开始录音,先创建一个byte[] 大小为最小录音缓冲区大小,用于写入声音数据,然后录音,同时写入文件。
byte[] buffer = new byte[mBufferSizeInBytes];
//开始录音
mAudioRecord.startRecording();
int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes);
//如果音频数据没有错误,就写入文件
if (AudioRecord.ERROR_INVALID_OPERATION != bufferReadResult && mDataOutputStream != null){
for (int i=0 ; i < bufferReadResult ; i++){
mDataOutputStream.write(buffer[i]);
}
}
- 6.录音完毕,关闭录音及释放相关资源.
mDataOutputStream.close();
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
- 7.这时候音频PCM文件已经产生,但是点击发现不能播放。所以我们需要添加文件头WAV(或其它),使其可以播放。
- 先定义新文件 test.wav的存储路径。
//定义wav文件的位置
String wavFilename = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"test.wav";
File wavFile = new File(wavFilename);
if (wavFile.exists()){
wavFile.delete();
}
wavFile.createNewFile();
- 然后开始将 PCM文件转换成WAV文件
public void pcmTowav(String pcmfilepath , String wavfilepath ) throws IOException {
FileInputStream pcmIn;
FileOutputStream wavOut;
//原始pcm数据大小不含(文件头),添加文件头要用
long pcmLength;
//文件总大小(含文件头),添加文件头要用
long dataLength;
//通道标识(1(单通道)或2(双通道),添加文件头要用)
int channels = (mChannelConfig == AudioFormat.CHANNEL_OUT_MONO ? 1 : 2);
//采样率,添加文件头要用
int sampleRate = mSampleRateInHz;
//信息传输速率=((采样率*通道数*每样值位数) / 8),添加文件头要用
int byteRate = sampleRate*channels*16/8;
byte[] data = new byte[mBufferSizeInBytes];
pcmIn = new FileInputStream(pcmfilepath);
wavOut = new FileOutputStream(wavfilepath);
pcmLength = pcmIn.getChannel().size();
//wav文件头44字节
dataLength = pcmLength+44;
//先写入wav文件头
writeHeader(wavOut , pcmLength , dataLength , sampleRate , channels , byteRate);
//再写入数据
while (pcmIn.read(data)!=-1){
wavOut.write(data);
}
Log.i("TAG","wav文件写入完成");
pcmIn.close();
wavOut.close();
}
- 写入wav文件头的方法如下:
private void writeHeader(FileOutputStream wavOut, long pcmLength, long dataLength, int sampleRate, int channels, int byteRate) throws IOException {
//wave文件头44个字节
byte[] header = new byte[44];
/*0-11字节(RIFF chunk :riff文件描述块)*/
//文件标记为RIFF文件
header[0]='R';
header[1]='I';
header[2]='F';
header[3]='F';
//文件总长度
header[4]= (byte) (dataLength * 0xff); //取一个字节(低8位)
header[5]= (byte) ((dataLength >> 8) * 0xff); //取一个字节 (中8位)
header[6]= (byte) ((dataLength >> 16) * 0xff); //取一个字节 (次8位)
header[7]= (byte) ((dataLength >> 24) * 0xff); //取一个字节 (高8位)
//文件类型标记为WAVE
header[8]='W';
header[9]='A';
header[10]='V';
header[11]='E';
/*13-35字节(fmt chunk : 数据格式信息块)*/
//标记格式块,描述数据格式信息 4个字节
header[12]='f';
header[13]='m';
header[14]='t';
header[15]=' '; //要有一个空格
//格式数据的长度 4个字节
header[16]=16;
header[17]=0;
header[18]=0;
header[19]=0;
//格式类型(如1 是 PCM) 2个字节
header[20]=1;
header[21]=0;
//通道数 1为单声道,2为双声道
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);
//比特率=(采样率* 每样值位数*渠道) / 8。
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
// (每样值位数* 通道)/8 所有通道的一个样值所需比特数 其中的声道数至少为2,小于2的按2算
header[32]= (16 * 2 / 8); //(比如PCM16,双声道的1个Frame等于16*2/8=4字节),
header[33]= 0 ;
//每样值位数(常用16比特或8比特表示样值)
header[34]=16;
header[35]=0;
/*36字节之后 (data chunk : 数据块)*/
//"data"块标记,标记数据节开始。
header[36]='d';
header[37]='a';
header[38]='t';
header[39]='a';
//描述数据节的大小
header[40] = (byte) (pcmLength & 0xff);
header[41] = (byte) ((pcmLength >> 8) & 0xff);
header[42] = (byte) ((pcmLength >> 16) & 0xff);
header[43] = (byte) ((pcmLength >> 24) & 0xff);
//写入文件头
wavOut.write(header,0,44);
}
- 8.这个时候音频wav文件已经产生,可以点击播放了。
2.源码如下:
注意:录音要在线程
Activity.java
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mThread = new Thread(){
@Override
public void run() {
try {
startRecord();
} catch (IOException e) {
e.printStackTrace();
stopRecord();
}
}
};
mThread.start();
}
});
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopRecord();
}
});
addHead.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
addHead();
} catch (IOException e) {
e.printStackTrace();
}
}
});
private void initAudioRecord() throws IOException {
//计算最小录音缓冲区大小
mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat);
//创建AudioRecord。AudioRecord类实际上不会保存捕获的音频,因此需要手动创建文件并保存下载。
mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,mAudioFormat,mBufferSizeInBytes);
//创建录音数据存储的文件
mRecordingFile = new File(mFileName);
if (mRecordingFile.exists()){
mRecordingFile.delete();
}
mRecordingFile.createNewFile();
//初始化流
mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));
}
//开始录音
private void startRecord() throws IOException {
//初始化AudioRecord和存储的文件和流
initAudioRecord();
//检测AudioRecord.getMinBufferSize的参数(采样率,声道,采样精度)是否支持当前的硬件设备
if (mBufferSizeInBytes == AudioRecord.ERROR_BAD_VALUE || mBufferSizeInBytes == AudioRecord.ERROR){
Log.i("TAG","Unable To getMinBufferSize");
}else {
//检测AudioRecord初始化成功
if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){
byte[] buffer = new byte[mBufferSizeInBytes];
//开始录音
mAudioRecord.startRecording();
Log.i("TAG","开始录音");
isRecording = true;
//如果正在录音状态
while (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING && isRecording){
Log.i("TAG","正在录音");
int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes);
//如果音频数据没有错误,就写入文件
if (AudioRecord.ERROR_INVALID_OPERATION != bufferReadResult && mDataOutputStream != null){
for (int i=0 ; i < bufferReadResult ; i++){
mDataOutputStream.write(buffer[i]);
}
}
}
mDataOutputStream.close();
}
}
}
//添加wav头
private void addHead() throws IOException {
//定义wav文件的位置
String wavFilename = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"test.wav";
File wavFile = new File(wavFilename);
if (wavFile.exists()){
wavFile.delete();
}
wavFile.createNewFile();
mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat);
PcmToWavUtil pcmtowavutil = new PcmToWavUtil(mSampleRateInHz,mChannelConfig,mBufferSizeInBytes,mAudioFormat);
//添加wav文件头,并将pcm数据转换成wav文件
pcmtowavutil.pcmTowav(mFileName,wavFilename);
}
//停止录音,释放资源
private void stopRecord() {
isRecording = false;
if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){
mAudioRecord.stop();
Log.i("TAG","录音停止");
}
if (mAudioRecord != null){
mAudioRecord.release();
mAudioRecord = null;
mThread = null;
}
}
PcmToWavUtil.java
public class PcmToWavUtil {
//采样率
private int mSampleRateInHz;
//声道数
private int mChannelConfig;
//最小缓冲区大小
private int mBufferSizeInBytes;
//数据格式
//这里传入了AudioFormat.ENCODING_PCM_16BIT,[所以下面代码中的每样值位数为16,每样值字节为2后面也会用]
private int mAudioFormat;
public PcmToWavUtil(int mSampleRateInHz , int mChannelConfig , int mBufferSizeInBytes,int mAudioFormat){
this.mSampleRateInHz = mSampleRateInHz;
this.mChannelConfig = mChannelConfig;
this.mBufferSizeInBytes = mBufferSizeInBytes;
this.mAudioFormat = mAudioFormat;
}
public void pcmTowav(String pcmfilepath , String wavfilepath ) throws IOException {
FileInputStream pcmIn;
FileOutputStream wavOut;
//原始pcm数据大小不含(文件头),添加文件头要用
long pcmLength;
//文件总大小(含文件头),添加文件头要用
long dataLength;
//通道标识(1(单通道)或2(双通道),添加文件头要用)
int channels = (mChannelConfig == AudioFormat.CHANNEL_OUT_MONO ? 1 : 2);
//采样率,添加文件头要用
int sampleRate = mSampleRateInHz;
//信息传输速率=((采样率*通道数*每样值位数) / 8),添加文件头要用
int byteRate = sampleRate*channels*16/8;
byte[] data = new byte[mBufferSizeInBytes];
pcmIn = new FileInputStream(pcmfilepath);
wavOut = new FileOutputStream(wavfilepath);
pcmLength = pcmIn.getChannel().size();
//wav文件头44字节
dataLength = pcmLength+44;
//先写入wav文件头
writeHeader(wavOut , pcmLength , dataLength , sampleRate , channels , byteRate);
//再写入数据
while (pcmIn.read(data)!=-1){
wavOut.write(data);
}
Log.i("TAG","wav文件写入完成");
pcmIn.close();
wavOut.close();
}
private void writeHeader(FileOutputStream wavOut, long pcmLength, long dataLength, int sampleRate, int channels, int byteRate) throws IOException {
//wave文件头44个字节
byte[] header = new byte[44];
/*0-11字节(RIFF chunk :riff文件描述块)*/
header[0]='R';
header[1]='I';
header[2]='F';
header[3]='F';
header[4]= (byte) (dataLength * 0xff); //取一个字节(低8位)
header[5]= (byte) ((dataLength >> 8) * 0xff); //取一个字节 (中8位)
header[6]= (byte) ((dataLength >> 16) * 0xff); //取一个字节 (次8位)
header[7]= (byte) ((dataLength >> 24) * 0xff); //取一个字节 (高8位)
header[8]='W';
header[9]='A';
header[10]='V';
header[11]='E';
/*13-35字节(fmt chunk : 数据格式信息块)*/
header[12]='f';
header[13]='m';
header[14]='t';
header[15]=' '; //要有一个空格
header[16]=16;
header[17]=0;
header[18]=0;
header[19]=0;
header[20]=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]= (16 * 2 / 8); //
header[33]= 0 ;
header[34]=16;
header[35]=0;
/*36字节之后 (data chunk : 数据块)*/
header[36]='d';
header[37]='a';
header[38]='t';
header[39]='a';
header[40] = (byte) (pcmLength & 0xff);
header[41] = (byte) ((pcmLength >> 8) & 0xff);
header[42] = (byte) ((pcmLength >> 16) & 0xff);
header[43] = (byte) ((pcmLength >> 24) & 0xff);
//写入文件头
wavOut.write(header,0,44);
}
}
最后
以上就是笨笨耳机为你收集整理的Android-音视频(2):用AudioRecord采集麦克风PCM并保存到文件1.先了解一下录音流程2.源码如下:的全部内容,希望文章能够帮你解决Android-音视频(2):用AudioRecord采集麦克风PCM并保存到文件1.先了解一下录音流程2.源码如下:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复