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

音频这方面很博大精深,我这里肯定讲不了什么高级的东西,最多也只是一些基础类知识,首先,我们要介绍一下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>

可以预览一下

这里我给按钮加了一个扁平的效果,实际上写了一个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);
}

好的,我们运行一下

看起来没什么变化,但是你去内存卡中就会发现多了一个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文件进行录制,播放,停止,删除功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android使用VideoView播放本地视频和网络视频的方法

    1.效果展示 2.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="matc

  • Android提高之MediaPlayer播放网络音频的实现方法

    前面有文章曾经地介绍过MediaPlayer的基本用法,这里就更加深入地讲解MediaPlayer的在线播放功能.本文主要实现MediaPlayer在线播放音频的功能,由于在线视频播放比在线音频播放复杂,因此先介绍在线音频播放的实现,这样可以帮助大家逐步深入了解MediaPlayer的在线播放功能. 先来看看本文程序运行的结果如下图所示: main.xml的源码如下: <?xml version="1.0" encoding="utf-8"?> <

  • android实用工具类分享(获取内存/检查网络/屏幕高度/手机分辨率)

    复制代码 代码如下: public class CommonUtil { public static boolean hasSDCard() { String status = Environment.getExternalStorageState();  return status.equals(Environment.MEDIA_MOUNTED); } /**  * 获取最大内存  *   * @return  */ public static long getMaxMemory() { r

  • Android截取视频帧并转化为Bitmap示例

    MainActivity如下: 复制代码 代码如下: package cn.testmediametadataretriever; import java.io.File; import java.io.FileOutputStream; import android.media.MediaMetadataRetriever; import android.os.Bundle; import android.os.Environment; import android.app.Activity;

  • Android音频录制MediaRecorder之简易的录音软件实现代码

    使用MediaRecorder的步骤:1.创建MediaRecorder对象2.调用MediRecorder对象的setAudioSource()方法设置声音的来源,一般传入MediaRecorder.MIC3.调用MediaRecorder对象的setOutputFormat()设置所录制的音频文件的格式4.调用MediaRecorder对象的setAudioRncoder().setAudioEncodingBitRate(int bitRate).setAudioSamlingRate(i

  • 19个Android常用工具类汇总

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.SerializeUtils.

  • Android提高之MediaPlayer播放网络视频的实现方法

    前面讲解了MediaPlayer播放网络音频,主要介绍了MediaPlayer关于网络音频的缓冲和进度条控制的方法,本文再来讲解一下MediaPlayer播放网络视频的方法.播放网络视频比播放网络音频需要多一个SurfaceView而已,在已经熟悉了MediaPlayer播放网络音频之后,相信大家对本文所述的播放网络视频也能很快地掌握. 先来看看本文程序运行截图,如下所示: 本文程序的视频来自http://daily3gp.com,大家可以替换程序中的视频链接,试试其他影片. main.xml的

  • Android开发之多媒体文件获取工具类实例【音频,视频,图片等】

    本文实例讲述了Android开发之多媒体文件获取工具类.分享给大家供大家参考,具体如下: package com.android.ocr.util; import java.io.File; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import

  • android多媒体类VideoView使用方法详解

    一.概述 VideoView类将视频的显示和控制集于一身,我们可以借助它完成一个简易的视频播放器.VideoView和MediaPlayer也比较相似. 二.VideoView的使用方法 它主要有以下几种常用方法 步骤: 1.指定视频文件的路径, 2.接下来调用start()方法就可以开始播放视频,pause()方法就会暂停播放,resume()方法就会重新播放 注:获取视频文件也需要运行时权限,所有相关逻辑也需要写.       最后不要忘记在AndroidManifest.xml文件中声明用

  • Android获取SD卡上图片和视频缩略图的小例子

    如何判断文件呢? 可以通过Cursor遍历数据库,对比INTERNAL_CONTENT_URI字段的值,这是一个Uri,这里保存着Android手机SD卡上的多媒体文件完整路径. [java] 复制代码 代码如下: Uri originalUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;                 //若为视频则为MediaStore.Video.Media.EXTERNAL_CONTENT_URI;          

  • Android使用MediaRecorder类进行录制视频

    我们提醒大家使用MediaRecorder录音录像的设置代码步骤一定要按照API指定的顺序来设置,否则报错 步骤为: 1.设置视频源,音频源,即输入源 2.设置输出格式 3.设置音视频的编码格式 一.首先看布局文件,这里有一个SurfaceView,这是一个绘制容器,可以直接从内存或者DMA等硬件接口取得图像数据, <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tool

随机推荐