日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

java audiorecord_Android 录音实现(AudioRecord)

發布時間:2023/12/10 Android 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java audiorecord_Android 录音实现(AudioRecord) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上一篇文章介紹了使用 MediaRecorder 實現錄音功能 Android錄音實現(MediaRecorder) ,下面我們繼續看看使用 AudioRecord 實現錄音功能。

AudioRecord

首先看看Android幫助文檔中對該類的簡單概述: AndioRecord 類的主要功能是讓各種 Java 應用能夠管理音頻資源,以便它們通過此類能夠錄制平臺的聲音輸入硬件所收集的聲音。此功能的實現就是通過 "pulling 同步"(reading讀取)AudioRecord 對象的聲音數據來完成的。在錄音過程中,應用所需要做的就是通過后面三個類方法中的一個去及時地獲取 AudioRecord 對象的錄音數據。 AudioRecord 類提供的三個獲取聲音數據的方法分別是 read(byte[], int, int), read(short[], int, int), read(ByteBuffer, int)。無論選擇使用那一個方法都必須事先設定方便用戶的聲音數據的存儲格式。

開始錄音的時候,一個 AudioRecord 需要初始化一個相關聯的聲音buffer,這個 buffer 主要是用來保存新的聲音數據。這個 buffer 的大小,我們可以在對象構造期間去指定。它表明一個 AudioRecord 對象還沒有被讀取(同步)聲音數據前能錄多長的音(即一次可以錄制的聲音容量)。聲音數據從音頻硬件中被讀出,數據大小不超過整個錄音數據的大小(可以分多次讀出),即每次讀取初始化 buffer 容量的數據。

采集工作很簡單,我們只需要構造一個AudioRecord對象,然后傳入各種不同配置的參數即可。一般情況下錄音實現的簡單流程如下:

音頻源:我們可以使用麥克風作為采集音頻的數據源。

采樣率:一秒鐘對聲音數據的采樣次數,采樣率越高,音質越好。

音頻通道:單聲道,雙聲道等,

音頻格式:一般選用PCM格式,即原始的音頻樣本。

緩沖區大小:音頻數據寫入緩沖區的總數,可以通過AudioRecord.getMinBufferSize獲取最小的緩沖區。(將音頻采集到緩沖區中然后再從緩沖區中讀取)。

代碼實現如下:

import android.media.AudioFormat;

import android.media.AudioRecord;

import android.media.MediaRecorder;

import android.text.TextUtils;

import android.util.Log;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

/**

* 實現錄音

*

* @author chenmy0709

* @version V001R001C01B001

*/

public class AudioRecorder {

//音頻輸入-麥克風

private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;

//采用頻率

//44100是目前的標準,但是某些設備仍然支持22050,16000,11025

//采樣頻率一般共分為22.05KHz、44.1KHz、48KHz三個等級

private final static int AUDIO_SAMPLE_RATE = 16000;

//聲道 單聲道

private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;

//編碼

private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

// 緩沖區字節大小

private int bufferSizeInBytes = 0;

//錄音對象

private AudioRecord audioRecord;

//錄音狀態

private Status status = Status.STATUS_NO_READY;

//文件名

private String fileName;

//錄音文件

private List filesName = new ArrayList<>();

/**

* 類級的內部類,也就是靜態類的成員式內部類,該內部類的實例與外部類的實例

* 沒有綁定關系,而且只有被調用時才會裝載,從而實現了延遲加載

*/

private static class AudioRecorderHolder {

/**

* 靜態初始化器,由JVM來保證線程安全

*/

private static AudioRecorder instance = new AudioRecorder();

}

private AudioRecorder() {

}

public static AudioRecorder getInstance() {

return AudioRecorderHolder.instance;

}

/**

* 創建錄音對象

*/

public void createAudio(String fileName, int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) {

// 獲得緩沖區字節大小

bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,

channelConfig, channelConfig);

audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);

this.fileName = fileName;

}

/**

* 創建默認的錄音對象

*

* @param fileName 文件名

*/

public void createDefaultAudio(String fileName) {

// 獲得緩沖區字節大小

bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE,

AUDIO_CHANNEL, AUDIO_ENCODING);

audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);

this.fileName = fileName;

status = Status.STATUS_READY;

}

/**

* 開始錄音

*

* @param listener 音頻流的監聽

*/

public void startRecord(final RecordStreamListener listener) {

if (status == Status.STATUS_NO_READY || TextUtils.isEmpty(fileName)) {

throw new IllegalStateException("錄音尚未初始化,請檢查是否禁止了錄音權限~");

}

if (status == Status.STATUS_START) {

throw new IllegalStateException("正在錄音");

}

Log.d("AudioRecorder", "===startRecord===" + audioRecord.getState());

audioRecord.startRecording();

new Thread(new Runnable() {

@Override

public void run() {

writeDataTOFile(listener);

}

}).start();

}

/**

* 暫停錄音

*/

public void pauseRecord() {

Log.d("AudioRecorder", "===pauseRecord===");

if (status != Status.STATUS_START) {

throw new IllegalStateException("沒有在錄音");

} else {

audioRecord.stop();

status = Status.STATUS_PAUSE;

}

}

/**

* 停止錄音

*/

public void stopRecord() {

Log.d("AudioRecorder", "===stopRecord===");

if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {

throw new IllegalStateException("錄音尚未開始");

} else {

audioRecord.stop();

status = Status.STATUS_STOP;

release();

}

}

/**

* 釋放資源

*/

public void release() {

Log.d("AudioRecorder", "===release===");

//假如有暫停錄音

try {

if (filesName.size() > 0) {

List filePaths = new ArrayList<>();

for (String fileName : filesName) {

filePaths.add(FileUtil.getPcmFileAbsolutePath(fileName));

}

//清除

filesName.clear();

//將多個pcm文件轉化為wav文件

mergePCMFilesToWAVFile(filePaths);

} else {

//這里由于只要錄音過filesName.size都會大于0,沒錄音時fileName為null

//會報空指針 NullPointerException

// 將單個pcm文件轉化為wav文件

//Log.d("AudioRecorder", "=====makePCMFileToWAVFile======");

//makePCMFileToWAVFile();

}

} catch (IllegalStateException e) {

throw new IllegalStateException(e.getMessage());

}

if (audioRecord != null) {

audioRecord.release();

audioRecord = null;

}

status = Status.STATUS_NO_READY;

}

/**

* 取消錄音

*/

public void canel() {

filesName.clear();

fileName = null;

if (audioRecord != null) {

audioRecord.release();

audioRecord = null;

}

status = Status.STATUS_NO_READY;

}

/**

* 將音頻信息寫入文件

*

* @param listener 音頻流的監聽

*/

private void writeDataTOFile(RecordStreamListener listener) {

// new一個byte數組用來存一些字節數據,大小為緩沖區大小

byte[] audiodata = new byte[bufferSizeInBytes];

FileOutputStream fos = null;

int readsize = 0;

try {

String currentFileName = fileName;

if (status == Status.STATUS_PAUSE) {

//假如是暫停錄音 將文件名后面加個數字,防止重名文件內容被覆蓋

currentFileName += filesName.size();

}

filesName.add(currentFileName);

File file = new File(FileUtil.getPcmFileAbsolutePath(currentFileName));

if (file.exists()) {

file.delete();

}

fos = new FileOutputStream(file);// 建立一個可存取字節的文件

} catch (IllegalStateException e) {

Log.e("AudioRecorder", e.getMessage());

throw new IllegalStateException(e.getMessage());

} catch (FileNotFoundException e) {

Log.e("AudioRecorder", e.getMessage());

}

//將錄音狀態設置成正在錄音狀態

status = Status.STATUS_START;

while (status == Status.STATUS_START) {

readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);

if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {

try {

fos.write(audiodata);

if (listener != null) {

//用于拓展業務

listener.recordOfByte(audiodata, 0, audiodata.length);

}

} catch (IOException e) {

Log.e("AudioRecorder", e.getMessage());

}

}

}

try {

if (fos != null) {

fos.close();// 關閉寫入流

}

} catch (IOException e) {

Log.e("AudioRecorder", e.getMessage());

}

}

/**

* 將pcm合并成wav

*

* @param filePaths

*/

private void mergePCMFilesToWAVFile(final List filePaths) {

new Thread(new Runnable() {

@Override

public void run() {

if (PcmToWav.mergePCMFilesToWAVFile(filePaths, FileUtil.getWavFileAbsolutePath(fileName))) {

//操作成功

} else {

//操作失敗

Log.e("AudioRecorder", "mergePCMFilesToWAVFile fail");

throw new IllegalStateException("mergePCMFilesToWAVFile fail");

}

fileName = null;

}

}).start();

}

/**

* 將單個pcm文件轉化為wav文件

*/

private void makePCMFileToWAVFile() {

new Thread(new Runnable() {

@Override

public void run() {

if (PcmToWav.makePCMFileToWAVFile(FileUtil.getPcmFileAbsolutePath(fileName), FileUtil.getWavFileAbsolutePath(fileName), true)) {

//操作成功

} else {

//操作失敗

Log.e("AudioRecorder", "makePCMFileToWAVFile fail");

throw new IllegalStateException("makePCMFileToWAVFile fail");

}

fileName = null;

}

}).start();

}

/**

* 獲取錄音對象的狀態

*

* @return

*/

public Status getStatus() {

return status;

}

/**

* 獲取本次錄音文件的個數

*

* @return

*/

public int getPcmFilesCount() {

return filesName.size();

}

/**

* 錄音對象的狀態

*/

public enum Status {

//未開始

STATUS_NO_READY,

//預備

STATUS_READY,

//錄音

STATUS_START,

//暫停

STATUS_PAUSE,

//停止

STATUS_STOP

}

}

AudioRecorder 錄音聲音數據從音頻硬件中被讀出,編碼格式為 PCM格式,但 PCM語音數據,如果保存成音頻文件,是不能夠被播放器播放的,所以必須先寫代碼實現數據編碼以及壓縮。下面實現 PCM 語音數據轉為 WAV 文件。

/**

* 將一個pcm文件轉化為wav文件

* @param pcmPath pcm文件路徑

* @param destinationPath 目標文件路徑(wav)

* @param deletePcmFile 是否刪除源文件

* @return

*/

public static boolean makePCMFileToWAVFile(String pcmPath, String destinationPath, boolean deletePcmFile) {

byte buffer[] = null;

int TOTAL_SIZE = 0;

File file = new File(pcmPath);

if (!file.exists()) {

return false;

}

TOTAL_SIZE = (int) file.length();

// 填入參數,比特率等等。這里用的是16位單聲道 8000 hz

WaveHeader header = new WaveHeader();

// 長度字段 = 內容的大小(TOTAL_SIZE) +

// 頭部字段的大小(不包括前面4字節的標識符RIFF以及fileLength本身的4字節)

header.fileLength = TOTAL_SIZE + (44 - 8);

header.FmtHdrLeth = 16;

header.BitsPerSample = 16;

header.Channels = 2;

header.FormatTag = 0x0001;

header.SamplesPerSec = 8000;

header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);

header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;

header.DataHdrLeth = TOTAL_SIZE;

byte[] h = null;

try {

h = header.getHeader();

} catch (IOException e1) {

Log.e("PcmToWav", e1.getMessage());

return false;

}

if (h.length != 44) // WAV標準,頭部應該是44字節,如果不是44個字節則不進行轉換文件

return false;

// 先刪除目標文件

File destfile = new File(destinationPath);

if (destfile.exists())

destfile.delete();

// 合成的pcm文件的數據,寫到目標文件

try {

buffer = new byte[1024 * 4]; // Length of All Files, Total Size

InputStream inStream = null;

OutputStream ouStream = null;

ouStream = new BufferedOutputStream(new FileOutputStream(

destinationPath));

ouStream.write(h, 0, h.length);

inStream = new BufferedInputStream(new FileInputStream(file));

int size = inStream.read(buffer);

while (size != -1) {

ouStream.write(buffer);

size = inStream.read(buffer);

}

inStream.close();

ouStream.close();

} catch (FileNotFoundException e) {

Log.e("PcmToWav", e.getMessage());

return false;

} catch (IOException ioe) {

Log.e("PcmToWav", ioe.getMessage());

return false;

}

if (deletePcmFile) {

file.delete();

}

Log.i("PcmToWav", "makePCMFileToWAVFile success!" + new SimpleDateFormat("yyyy-MM-dd hh:mm").format(new Date()));

return true;

}

總結:AudioRecorder 錄音相比較 MediaRecorder 使用起來會麻煩一些,但優點也是顯而易見的,AudioRecorder 錄音時直接操縱硬件獲取音頻流數據,該過程是實時處理,可以用代碼實現各種音頻的封裝,同時也可實現暫停功能,關于實現暫停錄音功能今天在這里就不贅述了,推薦大家閱讀 imhxl 博主的分享 http://blog.csdn.net/imhxl/article/details/52190451 。

總結

以上是生活随笔為你收集整理的java audiorecord_Android 录音实现(AudioRecord)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产成人在线视频网站 | 网站黄在线观看 | 狠狠干五月天 | 日本在线免费观看 | 婷婷综合av | 一道本av在线 | 国产精品永久免费观看 | 69亚洲| 欧美日韩3p| 干欧美| 欧美成人精品激情在线观看 | 嫩草视频入口 | 色爱区综合 | 国内偷拍一区 | 公车乳尖揉捏酥软呻吟 | 91大尺度| 北条麻妃99精品青青久久 | 1769国产 | 精久久久久 | 久久av无码精品人妻系列试探 | 婷婷综合精品 | 岛国av免费在线观看 | 性色av免费 | 日韩精品欧美精品 | 九九热最新网址 | 98av视频 | av片免费在线播放 | 真人抽搐一进一出视频 | 欧美福利视频一区 | 国产日韩欧美一区二区 | av人人干| 欧洲自拍一区 | 久久久久女| 越南黄色一级片 | 日日夜夜精品视频免费 | 看黄网站在线 | 久草热在线视频 | 免费无遮挡无码永久在线观看视频 | 日本www视频在线观看 | 四虎4hu永久免费网站影院 | 好吊妞视频在线 | 色在线看 | 久久精品女人毛片国产 | 成人三级电影网站 | 免费黄色的网站 | 天天插天天射 | 学生孕妇videosex性欧美 | 国产一二三区在线 | 美日韩精品视频 | 成人app在线 | 成人欧美一区二区三区黑人动态图 | 黄瓜视频在线免费看 | 中文在线播放 | 亚洲深夜福利视频 | 国产免费福利视频 | 欧美特黄一级视频 | 亚洲第一网站 | 毛片a片免费观看 | 一区二区三区在线免费观看 | 91国语对白 | 精品无码一区二区三区免费 | 国产精彩视频 | 97人妻精品一区二区三区免 | 久久久久97国产 | 夜夜高潮夜夜爽国产伦精品 | 诱夫1v1高h | 黄色a大片 | 欧美做受xxxxxⅹ性视频 | 日韩av在线一区二区三区 | 亚洲一级片在线播放 | 一区二区三区不卡视频 | 国产精品女优 | 黄色国产 | 免费av在线网 | 特级免费毛片 | 欧美不卡视频在线观看 | 亚洲一区二区视频在线观看 | 国产无套粉嫩白浆内谢 | 嫩草影院一区二区 | 免费一区二区三区 | 六月婷婷中文字幕 | 天天艹天天 | 麻豆www| 在线日韩欧美 | 可以在线看黄的网站 | 中文字幕日韩专区 | 91色吧| 四虎免费看黄 | 亚洲成人无码久久 | 成人在线免费观看视频 | 亚洲一区二区三区四区电影 | 免费精品视频在线观看 | 青青草狠狠干 | 黄色免费片 | 国产一区二区三区四区五区在线 | 激情久久视频 | 国产精品久久久毛片 | 在线免费观看你懂的 | 美女黄色在线观看 |