Android 开发 AudioRecord音频录制
<h1><span style="color: rgba(0, 128, 128, 1)">版权声明</span></h1><p>本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/10969824.html</p>
<div>本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。</div>
<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p> Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。</p>
<p> </p>
<h1><span style="color: rgba(0, 128, 128, 1)">实现流程</span></h1>
<ol>
<li>获取权限</li>
<li>初始化获取每一帧流的Size</li>
<li>初始化音频录制AudioRecord</li>
<li>开始录制与保存录制音频文件</li>
<li>停止录制</li>
<li>给音频文件添加头部信息,并且转换格式成wav</li>
<li>释放AudioRecord,录制流程完毕</li>
</ol>
<h1><span style="color: rgba(0, 128, 128, 1)">获取权限</span></h1>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">音频录制权限 </span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.RECORD_AUDIO"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">读取和写入存储权限</span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.WRITE_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.READ_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/></span></pre>
</div>
<p><span style="color: rgba(255, 0, 0, 1)">如果是Android5.0以上,以上3个权限需要动态授权<br></span></p>
<h1><span style="color: rgba(0, 128, 128, 1)">初始化获取每一帧流的Size</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Integer mRecordBufferSize;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> initMinBufferSize(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取每一帧的字节流大小</span>
mRecordBufferSize = AudioRecord.getMinBufferSize(8000<span style="color: rgba(0, 0, 0, 1)">
, AudioFormat.CHANNEL_IN_MONO
, AudioFormat.ENCODING_PCM_16BIT);
}</span></pre>
</div>
<h3><span style="color: rgba(0, 0, 0, 1)">第一个参数<span style="font-family: "DejaVu Sans Mono"">sampleRateInHz 采样率(赫兹)<span style="font-family: "DejaVu Sans Mono"">,方法注释里有说明</span></span></span></h3>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">只能在<span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">4000</span></span></span></span></span></span></span>到<span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">192000</span></span></span></span></span></span></span></span></span>的范围内取值</span></span></span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">在<span style="font-family: "DejaVu Sans Mono"">AudioFormat类里<br><span style="font-family: "DejaVu Sans Mono"">public static final int SAMPLE_RATE_HZ_MIN = 4000; 最小<span style="font-family: "DejaVu Sans Mono"">4000<br><span style="font-family: "DejaVu Sans Mono"">public static final int SAMPLE_RATE_HZ_MAX = 192000; 最大<span style="font-family: "DejaVu Sans Mono"">192000<br><br></span></span></span></span></span></span></span></span></span></p>
<h3><span style="color: rgba(0, 0, 0, 1)">第二个参数<span style="font-family: "DejaVu Sans Mono"">channelConfig 声道配置 描述音频声道的配置<span style="font-family: "DejaVu Sans Mono"">,例如左声道<span style="font-family: "DejaVu Sans Mono"">/右声道<span style="font-family: "DejaVu Sans Mono"">/前声道<span style="font-family: "DejaVu Sans Mono"">/后声道。<br></span></span></span></span></span></span></h3>
<p><span style="color: rgba(0, 0, 0, 1)">在<span style="font-family: "DejaVu Sans Mono"">AudioFormat类录<span style="font-family: "DejaVu Sans Mono""><br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_LEFT = 0x4;//左声道<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_RIGHT = 0x8;//右声道<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_FRONT = 0x10;//前声道<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_BACK = 0x20;//后声道<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_PRESSURE = 0x400;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_X_AXIS = 0x800;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_Y_AXIS = 0x1000;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_Z_AXIS = 0x2000;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;//单声道<br><span style="font-family: "DejaVu Sans Mono"">public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);//立体声道<span style="font-family: "DejaVu Sans Mono"">(左右声道<span style="font-family: "DejaVu Sans Mono"">)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<p> </p>
<h3><span style="color: rgba(0, 0, 0, 1)">第三个参数<span style="font-family: "DejaVu Sans Mono"">audioFormat 音频格式 表示音频数据的格式。</span></span></h3>
<p><strong><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono"">注意!一般的手机设备可能只支持 <span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">16位<span style="font-family: "DejaVu Sans Mono"">PCM编码,如果其他的都会报错为坏值.</span></span></span></span></span></span></strong></p>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_PCM_16BIT = 2; //16位<span style="font-family: "DejaVu Sans Mono"">PCM编码<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_PCM_8BIT = 3; //8位<span style="font-family: "DejaVu Sans Mono"">PCM编码<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_PCM_FLOAT = 4; //4位<span style="font-family: "DejaVu Sans Mono"">PCM编码<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_AC3 = 5;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_E_AC3 = 6;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_DTS = 7;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_DTS_HD = 8;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_MP3 = 9; //MP3编码 此格式可能会因为不设备不支持报错<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_AAC_LC = 10;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_AAC_HE_V1 = 11;<br><span style="font-family: "DejaVu Sans Mono"">public static final int ENCODING_AAC_HE_V2 = 12;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<h1><span style="color: rgba(0, 128, 128, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">初始化音频录制AudioRecord</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> AudioRecord mAudioRecord;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> initAudioRecord(){
mAudioRecord </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AudioRecord(MediaRecorder.AudioSource.MIC
, </span>8000<span style="color: rgba(0, 0, 0, 1)">
, AudioFormat.CHANNEL_IN_MONO
, AudioFormat.ENCODING_PCM_16BIT
, mRecordBufferSize);
}</span></pre>
</div>
<ul>
<li>第一个参数audioSource 音频源 这里选择使用麦克风:MediaRecorder.AudioSource.MIC</li>
<li>第二个参数sampleRateInHz 采样率(赫兹) 与前面初始化获取每一帧流的Size保持一致</li>
<li>第三个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。 与前面初始化获取每一帧流的Size保持一致</li>
<li>第四个参数audioFormat 音频格式 表示音频数据的格式。 与前面初始化获取每一帧流的Size保持一致</li>
<li>第五个参数缓存区大小,就是上面我们配置的AudioRecord.getMinBufferSize </li>
</ul>
<h1><span style="font-family: "DejaVu Sans Mono"; color: rgba(0, 128, 128, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">开始录制与<span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">保存录制音频文件</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> mWhetherRecord;
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> File pcmFile;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> startRecord(){
pcmFile </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(AudioRecordActivity.<span style="color: rgba(0, 0, 255, 1)">this</span>.getExternalCacheDir().getPath(),"audioRecord.pcm"<span style="color: rgba(0, 0, 0, 1)">);
mWhetherRecord </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Runnable() {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() {
mAudioRecord.startRecording();<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 128, 0, 1)">//开始<span style="color: rgba(0, 128, 0, 1)">录制</span></span></span>
FileOutputStream fileOutputStream </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
fileOutputStream </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FileOutputStream(pcmFile);
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] bytes = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> (mWhetherRecord){
mAudioRecord.read(bytes, </span>0<span style="color: rgba(0, 0, 0, 1)">, bytes.length);<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 128, 0, 1)">//<span style="color: rgba(0, 128, 0, 1)">读取流</span></span></span>
fileOutputStream.write(bytes);
fileOutputStream.flush();
}
Log.e(TAG, </span>"run: 暂停录制"<span style="color: rgba(0, 0, 0, 1)"> );
mAudioRecord.stop();<span style="color: rgba(0, 128, 0, 1)">//<span style="color: rgba(0, 128, 0, 1)">停止录制</span></span>
fileOutputStream.flush();
fileOutputStream.close();
addHeadData();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">添加音频头部信息并且转成wav格式</span>
} <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (FileNotFoundException e) {
e.printStackTrace();
mAudioRecord.stop();
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
e.printStackTrace();
}
}
}).start();
}</span></pre>
</div>
<p>这里说明一下为什么用布尔值,来关闭录制.有些小伙伴会发现<span style="color: rgba(0, 0, 0, 1)">AudioRecord</span>是可以获取到录制状态的.那么肯定有人会用状态来判断while是否还需要处理流.这种是错误的做法.因为MIC属于硬件层任何硬件的东西都是异步的而且会有很大的延时.所以回调的状态也是有延时的,有时候流没了,但是状态还是显示为正在录制.</p>
<h1><span style="color: rgba(0, 128, 128, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">停止录制</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></h1>
<p><span style="color: rgba(0, 0, 0, 1)"> 就是调用<strong>mAudioRecord.stop();</strong>方法来停止录制,但是因为我在上面的保存流后做了调用停止视频录制,所以我这里只需要切换布尔值就可以关闭音频录制</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> stopRecord(){
mWhetherRecord </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)"><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono""><span style="font-family: "DejaVu Sans Mono"">给音频文件添加头部信息,并且转换格式成wav</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></h1>
<p>音频录制完成后,这个时候去存储目录找到音频文件部分,会提示无法播放文件.其实是因为没有加入音频头部信息.一般通过麦克风采集的录音数据都是PCM格式的,即不包含头部信息,播放器无法知道音频采样率、位宽等参数,导致无法播放,显然是非常不方便的。pcm转换成wav,我们只需要在pcm的文件起始位置加上至少44个字节的WAV头信息即可。</p>
<div>
<div><strong>偏移地址 命名 内容</strong></div>
<div>00-03 ChunkId "RIFF"</div>
<div>04-07 ChunkSize 下个地址开始到文件尾的总字节数(此Chunk的数据大小)</div>
<div>08-11 fccType "WAVE"</div>
<div>12-15 SubChunkId1 "fmt ",最后一位空格。</div>
<div>16-19 SubChunkSize1 一般为16,表示fmt Chunk的数据块大小为16字节</div>
<div>20-21 FormatTag 1:表示是PCM 编码</div>
<div>22-23 Channels 声道数,单声道为1,双声道为2</div>
<div>24-27 SamplesPerSec 采样率</div>
<div>28-31 BytesPerSec 码率 :采样率 * 采样位数 * 声道个数,bytePerSecond = sampleRate * (bitsPerSample / 8) * channels</div>
<div>32-33 BlockAlign 每次采样的大小:位宽*声道数/8</div>
<div>34-35 BitsPerSample 位宽</div>
<div>36-39 SubChunkId2 "data"</div>
<div>40-43 SubChunkSize2 音频数据的长度</div>
<div>44-... data 音频数据</div>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> addHeadData(){
pcmFile </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(AudioRecordActivity.<span style="color: rgba(0, 0, 255, 1)">this</span>.getExternalCacheDir().getPath(),"audioRecord.pcm"<span style="color: rgba(0, 0, 0, 1)">);
handlerWavFile </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(AudioRecordActivity.<span style="color: rgba(0, 0, 255, 1)">this</span>.getExternalCacheDir().getPath(),"audioRecord_handler.wav"<span style="color: rgba(0, 0, 0, 1)">);
PcmToWavUtil pcmToWavUtil </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> PcmToWavUtil(8000<span style="color: rgba(0, 0, 0, 1)">,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
pcmToWavUtil.pcmToWav(pcmFile.toString(),handlerWavFile.toString());
}</span></pre>
</div>
<h3>写入头部信息的工具类</h3>
<p>注意输入File和输出File不能同一个,因为没有做缓存.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> PcmToWavUtil {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> String TAG = "PcmToWavUtil"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 缓存的音频大小
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> mBufferSize;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 采样率
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> mSampleRate;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 声道数
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> mChannel;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> sampleRate sample rate、采样率
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> channel channel、声道
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> encoding Audio data format、音频格式
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
PcmToWavUtil(</span><span style="color: rgba(0, 0, 255, 1)">int</span> sampleRate, <span style="color: rgba(0, 0, 255, 1)">int</span> channel, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> encoding) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.mSampleRate =<span style="color: rgba(0, 0, 0, 1)"> sampleRate;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.mChannel =<span style="color: rgba(0, 0, 0, 1)"> channel;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.mBufferSize =<span style="color: rgba(0, 0, 0, 1)"> AudioRecord.getMinBufferSize(mSampleRate, mChannel, encoding);
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* pcm文件转wav文件
*
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> inFilename 源文件路径
* </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> outFilename 目标文件路径
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> pcmToWav(String inFilename, String outFilename) {
FileInputStream in;
FileOutputStream out;
</span><span style="color: rgba(0, 0, 255, 1)">long</span> totalAudioLen;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">总录音长度</span>
<span style="color: rgba(0, 0, 255, 1)">long</span> totalDataLen;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">总数据长度</span>
<span style="color: rgba(0, 0, 255, 1)">long</span> longSampleRate =<span style="color: rgba(0, 0, 0, 1)"> mSampleRate;
</span><span style="color: rgba(0, 0, 255, 1)">int</span> channels = mChannel == AudioFormat.CHANNEL_IN_MONO ? 1 : 2<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">long</span> byteRate = 16 * mSampleRate * channels / 8<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] data = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
in </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FileInputStream(inFilename);
out </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FileOutputStream(outFilename);
totalAudioLen </span>=<span style="color: rgba(0, 0, 0, 1)"> in.getChannel().size();
totalDataLen </span>= totalAudioLen + 36<span style="color: rgba(0, 0, 0, 1)">;
writeWaveFileHeader(out, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate);
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (in.read(data) != -1<span style="color: rgba(0, 0, 0, 1)">) {
out.write(data);
out.flush();
}
Log.e(TAG, </span>"pcmToWav: 停止处理"<span style="color: rgba(0, 0, 0, 1)">);
in.close();
out.close();
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
e.printStackTrace();
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 加入wav文件头
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> writeWaveFileHeader(FileOutputStream out, <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> totalAudioLen,
</span><span style="color: rgba(0, 0, 255, 1)">long</span> totalDataLen, <span style="color: rgba(0, 0, 255, 1)">long</span> longSampleRate, <span style="color: rgba(0, 0, 255, 1)">int</span> channels, <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> byteRate)
</span><span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IOException {
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] header = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> RIFF/WAVE header</span>
header = 'R'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>1] = 'I'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>2] = 'F'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>3] = 'F'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>4] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (totalDataLen & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>5] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen >> 8) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>6] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen >> 16) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>7] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen >> 24) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">WAVE</span>
header = 'W'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>9] = 'A'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>10] = 'V'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>11] = 'E'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 'fmt ' chunk</span>
header = 'f'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>13] = 'm'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>14] = 't'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>15] = ' '<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 4 bytes: size of 'fmt ' chunk</span>
header = 16<span style="color: rgba(0, 0, 0, 1)">;
header[</span>17] = 0<span style="color: rgba(0, 0, 0, 1)">;
header[</span>18] = 0<span style="color: rgba(0, 0, 0, 1)">;
header[</span>19] = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> format = 1</span>
header = 1<span style="color: rgba(0, 0, 0, 1)">;
header[</span>21] = 0<span style="color: rgba(0, 0, 0, 1)">;
header[</span>22] = (<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">) channels;
header[</span>23] = 0<span style="color: rgba(0, 0, 0, 1)">;
header[</span>24] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (longSampleRate & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>25] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate >> 8) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>26] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate >> 16) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>27] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate >> 24) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>28] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (byteRate & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>29] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate >> 8) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>30] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate >> 16) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>31] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate >> 24) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> block align</span>
header = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (2 * 16 / 8<span style="color: rgba(0, 0, 0, 1)">);
header[</span>33] = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> bits per sample</span>
header = 16<span style="color: rgba(0, 0, 0, 1)">;
header[</span>35] = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">data</span>
header = 'd'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>37] = 'a'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>38] = 't'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>39] = 'a'<span style="color: rgba(0, 0, 0, 1)">;
header[</span>40] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (totalAudioLen & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>41] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen >> 8) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>42] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen >> 16) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
header[</span>43] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen >> 24) & 0xff<span style="color: rgba(0, 0, 0, 1)">);
out.write(header, </span>0, 44<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">释放AudioRecord,录制流程完毕</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">调用release()方法释放资源</span></p>
<div class="cnblogs_code">
<pre>mAudioRecord.release();</pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)">最后你就可以在指定目录下找到音频文件播放了</span></p>
<h1> </h1>
<h1><span style="color: rgba(0, 128, 128, 1)">最后介绍下其他API</span></h1>
<h3><strong><span style="color: rgba(0, 128, 128, 1)">获取AudioRecord初始化状态</span></strong></h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getState() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mState;
}</span></pre>
</div>
<pre class="highlighter-hljs" data-dark-theme="true"> </pre>
<p>注意!这里是<strong>初始化状态</strong>,不是录制状态,它只会返回2个状态</p>
<ul>
<li>AudioRecord#STATE_INITIALIZED //已经初始化</li>
<li>AudioRecord#STATE_UNINITIALIZED //没有初始化</li>
</ul>
<h3><strong><span style="color: rgba(0, 128, 128, 1)">获取AudioRecord录制状态</span></strong></h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getRecordingState() {
</span><span style="color: rgba(0, 0, 255, 1)">synchronized</span><span style="color: rgba(0, 0, 0, 1)"> (mRecordingStateLock) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mRecordingState;
}
}</span></pre>
</div>
<pre class="highlighter-hljs" data-dark-theme="true"> </pre>
<p>返回录制状态,它只返回2个状态</p>
<ul>
<li>AudioRecord#RECORDSTATE_STOPPED //停止录制</li>
<li>AudioRecord#RECORDSTATE_RECORDING //正在录制</li>
</ul>
<pre class="highlighter-hljs" data-dark-theme="true"><code> </code></pre>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
<div style="text-align: center">
<p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/10969824.html </p>
<div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/10969824.html
頁:
[1]