不可名状的夹心饼干 發表於 2019-6-3 20:20:00

Android 开发 AudioRecord音频录制

<h1><span style="color: rgba(0, 128, 128, 1)">版权声明</span></h1>
<p>本文来自博客园,作者:观心静&nbsp;,转载请注明原文链接: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>&nbsp;</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)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)">音频录制权限 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)">读取和写入存储权限</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">/&gt;</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: &quot;DejaVu Sans Mono&quot;">sampleRateInHz 采样率(赫兹)<span style="font-family: &quot;DejaVu Sans Mono&quot;">,方法注释里有说明</span></span></span></h3>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">只能在<span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">4000</span></span></span></span></span></span></span>到<span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">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: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">在<span style="font-family: &quot;DejaVu Sans Mono&quot;">AudioFormat类里<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int SAMPLE_RATE_HZ_MIN = 4000; 最小<span style="font-family: &quot;DejaVu Sans Mono&quot;">4000<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int SAMPLE_RATE_HZ_MAX = 192000; 最大<span style="font-family: &quot;DejaVu Sans Mono&quot;">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: &quot;DejaVu Sans Mono&quot;">channelConfig 声道配置 描述音频声道的配置<span style="font-family: &quot;DejaVu Sans Mono&quot;">,例如左声道<span style="font-family: &quot;DejaVu Sans Mono&quot;">/右声道<span style="font-family: &quot;DejaVu Sans Mono&quot;">/前声道<span style="font-family: &quot;DejaVu Sans Mono&quot;">/后声道。<br></span></span></span></span></span></span></h3>
<p><span style="color: rgba(0, 0, 0, 1)">在<span style="font-family: &quot;DejaVu Sans Mono&quot;">AudioFormat类录<span style="font-family: &quot;DejaVu Sans Mono&quot;"><br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_LEFT = 0x4;//左声道<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_RIGHT = 0x8;//右声道<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_FRONT = 0x10;//前声道<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_BACK = 0x20;//后声道<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_PRESSURE = 0x400;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_X_AXIS = 0x800;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_Y_AXIS = 0x1000;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_Z_AXIS = 0x2000;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;//单声道<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);//立体声道<span style="font-family: &quot;DejaVu Sans Mono&quot;">(左右声道<span style="font-family: &quot;DejaVu Sans Mono&quot;">)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>&nbsp;</p>
<h3><span style="color: rgba(0, 0, 0, 1)">第三个参数<span style="font-family: &quot;DejaVu Sans Mono&quot;">audioFormat 音频格式 表示音频数据的格式。</span></span></h3>
<p><strong><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;">注意!一般的手机设备可能只支持 <span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">16位<span style="font-family: &quot;DejaVu Sans Mono&quot;">PCM编码,如果其他的都会报错为坏值.</span></span></span></span></span></span></strong></p>
<p><span style="color: rgba(0, 0, 0, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_PCM_16BIT = 2; //16位<span style="font-family: &quot;DejaVu Sans Mono&quot;">PCM编码<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_PCM_8BIT = 3; //8位<span style="font-family: &quot;DejaVu Sans Mono&quot;">PCM编码<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_PCM_FLOAT = 4; //4位<span style="font-family: &quot;DejaVu Sans Mono&quot;">PCM编码<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_AC3 = 5;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_E_AC3 = 6;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_DTS = 7;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_DTS_HD = 8;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_MP3 = 9; //MP3编码 此格式可能会因为不设备不支持报错<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_AAC_LC = 10;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">public static final int ENCODING_AAC_HE_V1 = 11;<br><span style="font-family: &quot;DejaVu Sans Mono&quot;">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: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">初始化音频录制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 音频源&nbsp;&nbsp; 这里选择使用麦克风:MediaRecorder.AudioSource.MIC</li>
<li>第二个参数sampleRateInHz 采样率(赫兹)&nbsp; 与前面初始化获取每一帧流的Size保持一致</li>
<li>第三个参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。&nbsp;&nbsp; 与前面初始化获取每一帧流的Size保持一致</li>
<li>第四个参数audioFormat 音频格式&nbsp; 表示音频数据的格式。&nbsp; 与前面初始化获取每一帧流的Size保持一致</li>
<li>第五个参数缓存区大小,就是上面我们配置的AudioRecord.getMinBufferSize&nbsp;</li>
</ul>
<h1><span style="font-family: &quot;DejaVu Sans Mono&quot;; color: rgba(0, 128, 128, 1)"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">开始录制与<span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">保存录制音频文件</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: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">停止录制</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)">&nbsp;就是调用<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: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;"><span style="font-family: &quot;DejaVu Sans Mono&quot;">给音频文件添加头部信息,并且转换格式成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     &nbsp; "fmt ",最后一位空格。</div>
<div>16-19   SubChunkSize1    一般为16,表示fmt Chunk的数据块大小为16字节</div>
<div>20-21   FormatTag      1:表示是PCM 编码</div>
<div>22-23   Channels       &nbsp; 声道数,单声道为1,双声道为2</div>
<div>24-27   SamplesPerSec    &nbsp; 采样率</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   &nbsp;&nbsp; 音频数据的长度</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 &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>5] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen &gt;&gt; 8) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>6] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen &gt;&gt; 16) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>7] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalDataLen &gt;&gt; 24) &amp; 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 &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>25] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate &gt;&gt; 8) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>26] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate &gt;&gt; 16) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>27] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((longSampleRate &gt;&gt; 24) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>28] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) (byteRate &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>29] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate &gt;&gt; 8) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>30] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate &gt;&gt; 16) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>31] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((byteRate &gt;&gt; 24) &amp; 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 &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>41] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen &gt;&gt; 8) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>42] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen &gt;&gt; 16) &amp; 0xff<span style="color: rgba(0, 0, 0, 1)">);
      header[</span>43] = (<span style="color: rgba(0, 0, 255, 1)">byte</span>) ((totalAudioLen &gt;&gt; 24) &amp; 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>&nbsp;</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">&nbsp;</pre>
<p>注意!这里是<strong>初始化状态</strong>,不是录制状态,它只会返回2个状态</p>
<ul>
<li>AudioRecord#STATE_INITIALIZED&nbsp;   //已经初始化</li>
<li>AudioRecord#STATE_UNINITIALIZED&nbsp; //没有初始化</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">&nbsp;</pre>
<p>返回录制状态,它只返回2个状态</p>
<ul>
<li>AudioRecord#RECORDSTATE_STOPPED&nbsp;&nbsp;&nbsp; //停止录制</li>
<li>AudioRecord#RECORDSTATE_RECORDING&nbsp;&nbsp;&nbsp; //正在录制</li>
</ul>
<pre class="highlighter-hljs" data-dark-theme="true"><code>&nbsp;</code></pre>
<p>&nbsp;</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]
查看完整版本: Android 开发 AudioRecord音频录制