欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-01 20:33:28
音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下android他提供的录音类,实际上他有两个,一个是mediarecor...

音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下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"?>
<linearlayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<button
android: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"/>
<button
android: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"/>
<button
android: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"/>
<button
android: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"/>
<scrollview
android:id="@+id/mscrollview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margintop="5dp"
android:layout_weight="1">
<textview
android:id="@+id/tv_audio_succeess"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="初始化完成...."
android:textcolor="@color/coloraccent"/>
</scrollview>
</linearlayout>

可以预览一下

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

这里我给按钮加了一个扁平的效果,实际上写了一个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;
//16bit
int 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() {
@override
public void run() {
startrecord();
log.e(tag,"start");
}
});
thread.start();
printlog("开始录音");
buttonenabled(false, true, false);
break;

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

//打印log
private void printlog(final string resultstring) {
tv_audio_succeess.post(new runnable() {
@override
public 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);
}

好的,我们运行一下

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

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

Android音频处理之通过AudioRecord去保存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;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
initview();
}
//初始化view
private 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);
}
//点击事件
@override
public void onclick(view v) {
switch (v.getid()) {
case r.id.startaudio:
thread thread = new thread(new runnable() {
@override
public 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;
}
}
//打印log
private void printlog(final string resultstring) {
tv_audio_succeess.post(new runnable() {
@override
public 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;
//16bit
int 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文件进行录制,播放,停止,删除功能

这样就可以调试了

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

最后,放一张完整的截图

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

以上所述是小编给大家介绍的android音频处理之通过audiorecord去保存pcm文件进行录制,播放,停止,删除功能,希望对大家有所帮助