欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 运维知识 > Android >内容正文

Android

audiorecord怎么释放_Android 开发 AudioRecord音频录制

发布时间:2023/12/19 Android 42 豆豆
生活随笔 收集整理的这篇文章主要介绍了 audiorecord怎么释放_Android 开发 AudioRecord音频录制 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

前言

Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。

实现流程

获取权限

初始化获取每一帧流的Size

初始化音频录制AudioRecord

开始录制与保存录制音频文件

停止录制

给音频文件添加头部信息,并且转换格式成wav

释放AudioRecord,录制流程完毕

获取权限

如果是Android5.0以上,以上3个权限需要动态授权

初始化获取每一帧流的Size

privateInteger mRecordBufferSize;private voidinitMinBufferSize(){//获取每一帧的字节流大小

mRecordBufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO

, AudioFormat.ENCODING_PCM_16BIT);

}

第一个参数sampleRateInHz 采样率(赫兹),方法注释里有说明

只能在4000到192000的范围内取值

在AudioFormat类里

public static final int SAMPLE_RATE_HZ_MIN = 4000; 最小4000

public static final int SAMPLE_RATE_HZ_MAX = 192000; 最大192000

第二个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。

在AudioFormat类录public static final int CHANNEL_IN_LEFT = 0x4;//左声道

public static final int CHANNEL_IN_RIGHT = 0x8;//右声道

public static final int CHANNEL_IN_FRONT = 0x10;//前声道

public static final int CHANNEL_IN_BACK = 0x20;//后声道

public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;

public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;

public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;

public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;

public static final int CHANNEL_IN_PRESSURE = 0x400;

public static final int CHANNEL_IN_X_AXIS = 0x800;

public static final int CHANNEL_IN_Y_AXIS = 0x1000;

public static final int CHANNEL_IN_Z_AXIS = 0x2000;

public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;

public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;

public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;//单声道

public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);//立体声道(左右声道)

第三个参数audioFormat 音频格式 表示音频数据的格式。

注意!一般的手机设备可能只支持 16位PCM编码,如果其他的都会报错为坏值.

public static final int ENCODING_PCM_16BIT = 2; //16位PCM编码

public static final int ENCODING_PCM_8BIT = 3; //8位PCM编码

public static final int ENCODING_PCM_FLOAT = 4; //4位PCM编码

public static final int ENCODING_AC3 = 5;

public static final int ENCODING_E_AC3 = 6;

public static final int ENCODING_DTS = 7;

public static final int ENCODING_DTS_HD = 8;

public static final int ENCODING_MP3 = 9; //MP3编码 此格式可能会因为不设备不支持报错

public static final int ENCODING_AAC_LC = 10;

public static final int ENCODING_AAC_HE_V1 = 11;

public static final int ENCODING_AAC_HE_V2 = 12;

初始化音频录制AudioRecord

privateAudioRecord mAudioRecord;private voidinitAudioRecord(){

mAudioRecord= newAudioRecord(MediaRecorder.AudioSource.MIC

,8000, AudioFormat.CHANNEL_IN_MONO

, AudioFormat.ENCODING_PCM_16BIT

, mRecordBufferSize);

}

第一个参数audioSource 音频源   这里选择使用麦克风:MediaRecorder.AudioSource.MIC

第二个参数sampleRateInHz 采样率(赫兹)  与前面初始化获取每一帧流的Size保持一致

第三个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。   与前面初始化获取每一帧流的Size保持一致

第四个参数audioFormat 音频格式  表示音频数据的格式。  与前面初始化获取每一帧流的Size保持一致

第五个参数缓存区大小,就是上面我们配置的AudioRecord.getMinBufferSize

开始录制与保存录制音频文件

private booleanmWhetherRecord;privateFile pcmFile;private voidstartRecord(){

pcmFile= new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord.pcm");

mWhetherRecord= true;new Thread(newRunnable() {

@Overridepublic voidrun() {

mAudioRecord.startRecording();//开始录制

FileOutputStream fileOutputStream= null;try{

fileOutputStream= newFileOutputStream(pcmFile);byte[] bytes = new byte[mRecordBufferSize];while(mWhetherRecord){

mAudioRecord.read(bytes,0, bytes.length);//读取流

fileOutputStream.write(bytes);

fileOutputStream.flush();

}

Log.e(TAG,"run: 暂停录制");

mAudioRecord.stop();//停止录制

fileOutputStream.flush();

fileOutputStream.close();

addHeadData();//添加音频头部信息并且转成wav格式

} catch(FileNotFoundException e) {

e.printStackTrace();

mAudioRecord.stop();

}catch(IOException e) {

e.printStackTrace();

}

}

}).start();

}

这里说明一下为什么用布尔值,来关闭录制.有些小伙伴会发现AudioRecord是可以获取到录制状态的.那么肯定有人会用状态来判断while是否还需要处理流.这种是错误的做法.因为MIC属于硬件层任何硬件的东西都是异步的而且会有很大的延时.所以回调的状态也是有延时的,有时候流没了,但是状态还是显示为正在录制.

停止录制

就是调用mAudioRecord.stop();方法来停止录制,但是因为我在上面的保存流后做了调用停止视频录制,所以我这里只需要切换布尔值就可以关闭音频录制

private voidstopRecord(){

mWhetherRecord= false;

}

给音频文件添加头部信息,并且转换格式成wav

音频录制完成后,这个时候去存储目录找到音频文件部分,会提示无法播放文件.其实是因为没有加入音频头部信息.一般通过麦克风采集的录音数据都是PCM格式的,即不包含头部信息,播放器无法知道音频采样率、位宽等参数,导致无法播放,显然是非常不方便的。pcm转换成wav,我们只需要在pcm的文件起始位置加上至少44个字节的WAV头信息即可。

偏移地址   命名       内容

00-03   ChunkId       "RIFF"

04-07   ChunkSize      下个地址开始到文件尾的总字节数(此Chunk的数据大小)

08-11   fccType       "WAVE"

12-15   SubChunkId1       "fmt ",最后一位空格。

16-19   SubChunkSize1    一般为16,表示fmt Chunk的数据块大小为16字节

20-21   FormatTag      1:表示是PCM 编码

22-23   Channels         声道数,单声道为1,双声道为2

24-27   SamplesPerSec      采样率

28-31   BytesPerSec     码率 :采样率 * 采样位数 * 声道个数,bytePerSecond = sampleRate * (bitsPerSample / 8) * channels

32-33   BlockAlign       每次采样的大小:位宽*声道数/8

34-35   BitsPerSample     位宽

36-39   SubChunkId2     "data"

40-43   SubChunkSize2      音频数据的长度

44-...   data         音频数据

private voidaddHeadData(){

pcmFile= new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord.pcm");

handlerWavFile= new File(AudioRecordActivity.this.getExternalCacheDir().getPath(),"audioRecord_handler.wav");

PcmToWavUtil pcmToWavUtil= new PcmToWavUtil(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);

pcmToWavUtil.pcmToWav(pcmFile.toString(),handlerWavFile.toString());

}

写入头部信息的工具类

注意输入File和输出File不能同一个,因为没有做缓存.

public classPcmToWavUtil {private static final String TAG = "PcmToWavUtil";/*** 缓存的音频大小*/

private intmBufferSize;/*** 采样率*/

private intmSampleRate;/*** 声道数*/

private intmChannel;/***@paramsampleRate sample rate、采样率

*@paramchannel channel、声道

*@paramencoding Audio data format、音频格式*/PcmToWavUtil(int sampleRate, int channel, intencoding) {this.mSampleRate =sampleRate;this.mChannel =channel;this.mBufferSize =AudioRecord.getMinBufferSize(mSampleRate, mChannel, encoding);

}/*** pcm文件转wav文件

*

*@paraminFilename 源文件路径

*@paramoutFilename 目标文件路径*/

public voidpcmToWav(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= newFileInputStream(inFilename);

out= newFileOutputStream(outFilename);

totalAudioLen=in.getChannel().size();

totalDataLen= totalAudioLen + 36;

writeWaveFileHeader(out, totalAudioLen, totalDataLen,

longSampleRate, channels, byteRate);while (in.read(data) != -1) {

out.write(data);

out.flush();

}

Log.e(TAG,"pcmToWav: 停止处理");

in.close();

out.close();

}catch(IOException e) {

e.printStackTrace();

}

}/*** 加入wav文件头*/

private void writeWaveFileHeader(FileOutputStream out, longtotalAudioLen,long totalDataLen, long longSampleRate, int channels, longbyteRate)throwsIOException {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);

}

}

释放AudioRecord,录制流程完毕

调用release()方法释放资源

mAudioRecord.release();

最后你就可以在指定目录下找到音频文件播放了

最后介绍下其他API

获取AudioRecord初始化状态

public intgetState() {returnmState;

}

注意!这里是初始化状态,不是录制状态,它只会返回2个状态

AudioRecord#STATE_INITIALIZED    //已经初始化

AudioRecord#STATE_UNINITIALIZED  //没有初始化

获取AudioRecord录制状态

public intgetRecordingState() {synchronized(mRecordingStateLock) {returnmRecordingState;

}

}

返回录制状态,它只返回2个状态

AudioRecord#RECORDSTATE_STOPPED    //停止录制

AudioRecord#RECORDSTATE_RECORDING    //正在录制

总结

以上是生活随笔为你收集整理的audiorecord怎么释放_Android 开发 AudioRecord音频录制的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。