Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android音頻處理之通過AudioRecord去保存PCM文件進行錄制,播放,停止,刪除功能

Android音頻處理之通過AudioRecord去保存PCM文件進行錄制,播放,停止,刪除功能

編輯:關於Android編程

音頻這方面很博大精深,我這裡肯定講不了什麼高級的東西,最多也只是一些基礎類知識,首先,我們要介紹一下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文件進行錄制,播放,停止,刪除功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved