Android音频处理之通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能

时间:2021-05-20

音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下Android他提供的录音类,实际上他有两个,一个是MediaRecorder,还有一个就是我们今天要用到的AudioRecord,那他们有什么区别呢?

一.区别

MediaRecorder和AudioRecord都可以录制音频,区别是MediaRecorder录制的音频文件是经过压缩后的,需要设置编码器。并且录制的音频文件可以用系统自带的Music播放器播放。

而AudioRecord录制的是PCM格式的音频文件,需要用AudioTrack来播放,AudioTrack更接近底层。

PCM可能更加可以理解为音频的源文件

二.优缺点

AudioRecord

主要是实现边录边播以及对音频的实时处理,这个特性让他更适合在语音方面有优势

优点:语音的实时处理,可以用代码实现各种音频的封装

缺点:输出是PCM格式文件,如果保存成音频文件,是不能够被播放器播放的,所以必须先写代码实现数据编码以及压缩

MediaRecorder

已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有,aac,amr,3gp等

优点:集成,直接调用相关接口即可,代码量小

缺点:无法实时处理音频;输出的音频格式不是很多,例如没有输出mp3格式文件

三.准备工作

我们要实现的是一个实时的去录音,播放,停止等功能的测试案例,那我们肯定要准备点什么,比如说,我这里先创建一个项目——PCMSample

然后写个布局

layout_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="10dp"><Buttonandroid:id="@+id/startAudio"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/button_bg"android:text="开始录音"android:textColor="@android:color/white"/><Buttonandroid:id="@+id/stopAudio"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginTop="5dp"android:background="@drawable/button_bg"android:enabled="false"android:text="停止录音"android:textColor="@android:color/white"/><Buttonandroid:id="@+id/playAudio"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/button_bg"android:enabled="false"android:text="播放音频"android:textColor="@android:color/white"/><Buttonandroid:id="@+id/deleteAudio"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:background="@drawable/button_bg"android:text="删除PCM"android:textColor="@android:color/white"/><ScrollViewandroid:id="@+id/mScrollView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_marginTop="5dp"android:layout_weight="1"><TextViewandroid:id="@+id/tv_audio_succeess"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="初始化完成...."android:textColor="@color/colorAccent"/></ScrollView></LinearLayout>

可以预览一下

这里我给按钮加了一个扁平的效果,实际上写了一个xml,很简单

button_bg.xml

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_pressed="true"><shape><corners android:radius="30dp"/><solid android:color="@color/colorPrimary"/></shape></item><item android:state_pressed="false"><shape><corners android:radius="30dp"/><solid android:color="@color/colorPrimaryDark"/></shape></item></selector>

好的,回到正题,我们这里有四个按钮,分别是开始。停止,播放,和删除,我们就是要实现这四个功能,在此之前,我们还需要做的事情就是添加权限,因为我们要录音和写内存卡文件,所有需要这两个权限即可

<!--录音--><uses-permission android:name="android.permission.RECORD_AUDIO" /><!--读取SD卡--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

这里初始化什么的就不说了,我们直接进入正题

四.开始录音

开始录音的话,这里,我们定义一个变量isRecording去控制,这样就比较好结束了,而且要注意的是,录音是不能放在UI线程的,你懂的,所以我们可以写一个开始录音的方法

//开始录音public void StartRecord() {Log.i(TAG,"开始录音");//16K采集率int frequency = 16000;//格式int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;//16Bitint audioEncoding = AudioFormat.ENCODING_PCM_16BIT;//生成PCM文件file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/reverseme.pcm");Log.i(TAG,"生成文件");//如果存在,就先删除再创建if (file.exists())file.delete();Log.i(TAG,"删除文件");try {file.createNewFile();Log.i(TAG,"创建文件");} catch (IOException e) {Log.i(TAG,"未能创建");throw new IllegalStateException("未能创建" + file.toString());}try {//输出流OutputStream os = new FileOutputStream(file);BufferedOutputStream bos = new BufferedOutputStream(os);DataOutputStream dos = new DataOutputStream(bos);int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize);short[] buffer = new short[bufferSize];audioRecord.startRecording();Log.i(TAG, "开始录音");isRecording = true;while (isRecording) {int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);for (int i = 0; i < bufferReadResult; i++) {dos.writeShort(buffer[i]);}}audioRecord.stop();dos.close();} catch (Throwable t) {Log.e(TAG, "录音失败");}}

首先,这里我们了解一下采样率,编码,音频流等基本的概念,剩下的大多是读写流的操作了,我们通过创建一个AudioRecord去写pcm文件,定义一个while循环,用我们刚才定义的isRecording控制,所以,我们的点击事件就

case R.id.startAudio:Thread thread = new Thread(new Runnable() {@Overridepublic void run() {StartRecord();Log.e(TAG,"start");}});thread.start();printLog("开始录音");ButtonEnabled(false, true, false);break;

这里要注意一下thread.start();开启线程,同时打印出log,具体代码如下

//打印logprivate void printLog(final String resultString) {tv_audio_succeess.post(new Runnable() {@Overridepublic void run() {tv_audio_succeess.append(resultString + "\n");mScrollView.fullScroll(ScrollView.FOCUS_DOWN);}});}

这里,我为了防止ANR,所以控制了一下按钮的焦点

//获取/失去焦点private void ButtonEnabled(boolean start, boolean stop, boolean play) {startAudio.setEnabled(start);stopAudio.setEnabled(stop);playAudio.setEnabled(play);}

好的,我们运行一下

看起来没什么变化,但是你去内存卡中就会发现多了一个pcm文件

当然,你只是点击启动录音是不会生成这个pcm文件的,你需要点击停止停止录音的按钮

五.停止录音

停止录音很简单,我们控制通过改变写入流就好了

case R.id.stopAudio:isRecording = false;ButtonEnabled(true, false, true);printLog("停止录音");break;

这样才会生成PCM

六播放音频

现在有了PCM我们可以试着去播放了,写一个播放的方法

//播放文件public void PlayRecord() {if(file == null){return;}//读取文件int musicLength = (int) (file.length() / 2);short[] music = new short[musicLength];try {InputStream is = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(is);DataInputStream dis = new DataInputStream(bis);int i = 0;while (dis.available() > 0) {music[i] = dis.readShort();i++;}dis.close();AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,16000, AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT,musicLength * 2,AudioTrack.MODE_STREAM);audioTrack.play();audioTrack.write(music, 0, musicLength);audioTrack.stop();} catch (Throwable t) {Log.e(TAG, "播放失败");}}

正如上面所说,我们播放需要用到AudioTrack,调用他的play方法以及设置一些参数即可

七.删除音频

删除音频只需要删除这个pcm文件就行

//删除文件private void deleFile() {if(file == null){return;}file.delete();printLog("文件删除成功");}

这就是大致的录音逻辑,虽然看起来很简单,但是这正是现在很多语音和音频的最基础部分,特别是语音,如果你从事语音的工作,我相信你会感谢我的!

好了,最后放上完整的代码:

MainActivity

package com.liuguilin.pcmsample;import android.media.AudioFormat;import android.media.AudioManager;import android.media.AudioRecord;import android.media.AudioTrack;import android.media.MediaRecorder;import android.os.Bundle;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.ScrollView;import android.widget.TextView;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;public class MainActivity extends AppCompatActivity implements View.OnClickListener {public static final String TAG = "PCMSample";//是否在录制private boolean isRecording = false;//开始录音private Button startAudio;//结束录音private Button stopAudio;//播放录音private Button playAudio;//删除文件private Button deleteAudio;private ScrollView mScrollView;private TextView tv_audio_succeess;//pcm文件private File file;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}//初始化Viewprivate void initView() {mScrollView = (ScrollView) findViewById(R.id.mScrollView);tv_audio_succeess = (TextView) findViewById(R.id.tv_audio_succeess);printLog("初始化成功");startAudio = (Button) findViewById(R.id.startAudio);startAudio.setOnClickListener(this);stopAudio = (Button) findViewById(R.id.stopAudio);stopAudio.setOnClickListener(this);playAudio = (Button) findViewById(R.id.playAudio);playAudio.setOnClickListener(this);deleteAudio = (Button) findViewById(R.id.deleteAudio);deleteAudio.setOnClickListener(this);}//点击事件@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.startAudio:Thread thread = new Thread(new Runnable() {@Overridepublic void run() {StartRecord();Log.e(TAG,"start");}});thread.start();printLog("开始录音");ButtonEnabled(false, true, false);break;case R.id.stopAudio:isRecording = false;ButtonEnabled(true, false, true);printLog("停止录音");break;case R.id.playAudio:PlayRecord();ButtonEnabled(true, false, false);printLog("播放录音");break;case R.id.deleteAudio:deleFile();break;}}//打印logprivate void printLog(final String resultString) {tv_audio_succeess.post(new Runnable() {@Overridepublic void run() {tv_audio_succeess.append(resultString + "\n");mScrollView.fullScroll(ScrollView.FOCUS_DOWN);}});}//获取/失去焦点private void ButtonEnabled(boolean start, boolean stop, boolean play) {startAudio.setEnabled(start);stopAudio.setEnabled(stop);playAudio.setEnabled(play);}//开始录音public void StartRecord() {Log.i(TAG,"开始录音");//16K采集率int frequency = 16000;//格式int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;//16Bitint audioEncoding = AudioFormat.ENCODING_PCM_16BIT;//生成PCM文件file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/reverseme.pcm");Log.i(TAG,"生成文件");//如果存在,就先删除再创建if (file.exists())file.delete();Log.i(TAG,"删除文件");try {file.createNewFile();Log.i(TAG,"创建文件");} catch (IOException e) {Log.i(TAG,"未能创建");throw new IllegalStateException("未能创建" + file.toString());}try {//输出流OutputStream os = new FileOutputStream(file);BufferedOutputStream bos = new BufferedOutputStream(os);DataOutputStream dos = new DataOutputStream(bos);int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize);short[] buffer = new short[bufferSize];audioRecord.startRecording();Log.i(TAG, "开始录音");isRecording = true;while (isRecording) {int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);for (int i = 0; i < bufferReadResult; i++) {dos.writeShort(buffer[i]);}}audioRecord.stop();dos.close();} catch (Throwable t) {Log.e(TAG, "录音失败");}}//播放文件public void PlayRecord() {if(file == null){return;}//读取文件int musicLength = (int) (file.length() / 2);short[] music = new short[musicLength];try {InputStream is = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(is);DataInputStream dis = new DataInputStream(bis);int i = 0;while (dis.available() > 0) {music[i] = dis.readShort();i++;}dis.close();AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,16000, AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT,musicLength * 2,AudioTrack.MODE_STREAM);audioTrack.play();audioTrack.write(music, 0, musicLength);audioTrack.stop();} catch (Throwable t) {Log.e(TAG, "播放失败");}}//删除文件private void deleFile() {if(file == null){return;}file.delete();printLog("文件删除成功");}}

如果你想去调试这些pcm文件做音频测试的话,我推荐使用Audacity这个软件,可以看到,我直接点击左上角的file-导入-源文件,然后设置16K

这样就可以调试了

最后,放一张完整的截图

以上所述是小编给大家介绍的Android音频处理之通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章