AAOS系列之(七) --- AudioRecord录音逻辑分析(一)
一文讲透AAOS架构,点到为止不藏私📌 这篇帖子给大家分析下 AudioRecord的初始化1. 场景介绍:在 AAOS 的 Framework 开发中,录音模块几乎是每个项目都会涉及的重要组成部分。无论是语音控制、车内对讲(同行者模式),还是集成科大讯飞等语音识别引擎,都高度依赖系统 Framework 层向 App 层提供稳定、可用的录音数据。在实现录音功能时,有几个关键参数必须严格设置:采样率、通道数和音频格式。其中,采样率尤为关键。如果设置不当,录制下来的音频数据可能无法被正常解码和回放,从而导致语音引擎识别失败,严重影响用户体验。因此,在平台开发阶段就需要明确并统一这些参数配置,确保整个语音链路的稳定性和兼容性。APP端录音的基础代码如下:// 音频获取源privateintaudioSource=MediaRecorder.AudioSource.MIC;// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025privatestaticintsampleRateInHz=16000;// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道privatestaticintchannelConfig=AudioFormat.CHANNEL_IN_STEREO;// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。privatestaticintaudioFormat=AudioFormat.ENCODING_PCM_16BIT;privateintbufferSizeInBytes=0;privateAudioRecordaudioRecord;privatevoidcreatAudioRecord(){// true: 内建APP使用的录音方式; false:三方APP使用的录音方式.booleanisBuild=true;if(isBuild){// MIC1+MIC2+REF1+REF2bufferSizeInBytes=5120;finalAudioFormataudioFormat1=newAudioFormat.Builder().setEncoding(AudioFormat.ENCODING_PCM_16BIT).setSampleRate(sampleRateInHz).setChannelIndexMask(0xf)// 设置了这个参数, 采集的就是4通道的数据.build();audioRecord=newAudioRecord.Builder().setAudioFormat(audioFormat1).build();}else{//channelConfig = AudioFormat.CHANNEL_IN_STEREO | AudioFormat.CHANNEL_IN_FRONT_BACK;// 获得缓冲区字节大小bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);Log.d(TAG,"creatAudioRecord: bufferSizeInBytes = "+bufferSizeInBytes);audioRecord=newAudioRecord(audioSource,sampleRateInHz,channelConfig,audioFormat,bufferSizeInBytes);}调用到AudioRecord.java的构造方法,常规APP,比如Hicar,微信, 都是通过标准的API 来调用录音的接口://channelConfig = AudioFormat.CHANNEL_IN_STEREO | AudioFormat.CHANNEL_IN_FRONT_BACK;// 获得缓冲区字节大小bufferSizeInBytes=AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);Log.d(TAG,"creatAudioRecord: bufferSizeInBytes = "+bufferSizeInBytes);// 调用AudioRecord的构造方法.audioRecord=newAudioRecord(audioSource,sampleRateInHz,channelConfig,audioFormat,bufferSizeInBytes);接下来, 我们来看下AudioRecord.java这个类的方法:1.调用AudioRecord 的构造方法:publicclassAudioRecordimplementsAudioRouting,MicrophoneDirection,AudioRecordingMonitor,AudioRecordingMonitorClient{// 标准的APP接口publicAudioRecord(intaudioSource,intsampleRateInHz,intchannelConfig,intaudioFormat,intbufferSizeInBytes)throwsIllegalArgumentException{this((newAudioAttributes.Builder()).setInternalCapturePreset(audioSource).build(),(newAudioFormat.Builder())// 2.这里有个将channelConfig 转换成ChannelMask的流程..setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,true/*allow legacy configurations*/)).setEncoding(audioFormat).setSampleRate(sampleRateInHz).build(),bufferSizeInBytes,AudioManager.AUDIO_SESSION_ID_GENERATE);}}2.调用getChannelMaskFromLegacyConfig(),将channelConfig转换成ChannelMask:// 调用这个方法, 我们传入的参数是:CHANNEL_IN_STEREO = 12;privatestaticintgetChannelMaskFromLegacyConfig(intinChannelConfig,booleanallowLegacyConfig){intmask;switch(inChannelConfig){caseAudioFormat.CHANNEL_IN_DEFAULT:// AudioFormat.CHANNEL_CONFIGURATION_DEFAULTcaseAudioFormat.CHANNEL_IN_MONO:caseAudioFormat.CHANNEL_CONFIGURATION_MONO:mask=AudioFormat.CHANNEL_IN_MONO;break;// 传入立体声通道,设置mask为CHANNEL_IN_STEREO,没有改变caseAudioFormat.CHANNEL_IN_STEREO:caseAudioFormat.CHANNEL_CONFIGURATION_STEREO:mask=AudioFormat.CHANNEL_IN_STEREO;break;case(AudioFormat.CHANNEL_IN_FRONT|AudioFormat.CHANNEL_IN_BACK):mask=inChannelConfig;break;default:thrownewIllegalArgumentException("Unsupported channel configuration.");}if(!allowLegacyConfig((inChannelConfig==AudioFormat.CHANNEL_CONFIGURATION_MONO)||(inChannelConfig==AudioFormat.CHANNEL_CONFIGURATION_STEREO))){// only happens with the constructor that uses AudioAttributes and AudioFormatthrownewIllegalArgumentException("Unsupported deprecated configuration.");}// 返回mask, 这里的值就是CHANNEL_IN_STEREO = 12returnmask;}3.调用setChannelMask(),把计算后的mask值设置给AudioRecorder:这个方法没有什么复杂的逻辑,只是把channelMask直接保存到成员变量mChannelMaskpublic@NonNullBuildersetChannelMask(intchannelMask){if(channelMask==CHANNEL_INVALID){thrownewIllegalArgumentException("Invalid zero channel mask");}elseif(/* channelMask != 0 */mChannelIndexMask!=0