日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Android用AudioRecord進行錄音

瀏覽:176日期:2022-09-21 11:21:55

在音視頻開發(fā)中,錄音當然是必不可少的。首先我們要學會單獨的錄音功能,當然這里說的錄音是指用AudioRecord來錄音,讀取錄音原始數(shù)據(jù),讀到的就是所謂的PCM數(shù)據(jù)。對于錄音來說,最重要的幾個參數(shù)要搞明白:

1、simpleRate采樣率,采樣率就是采樣頻率,每秒鐘記錄多少個樣本。

2、channelConfig通道配置,其實就是所謂的單通道,雙通道之類的,AudioFormat.CHANNEL_IN_MONO單通道,AudioFormat.CHANNEL_IN_STEREO雙通道,這里只列了這兩種,還有其它的,可自行查閱。

3、audioFormat音頻格式,其實就是采樣的精度,每個樣本的位數(shù),AudioFormat.ENCODING_PCM_8BIT每個樣本占8位,AudioFormat.ENCODING_PCM_16BIT每個樣本占16位,這里也只用了這兩個,別的沒研究。

在學習過程中會用到的一些參數(shù),我這里封裝了一個類,如下

public class AudioParams { enum Format { SINGLE_8_BIT, DOUBLE_8_BIT, SINGLE_16_BIT, DOUBLE_16_BIT } private Format format; int simpleRate; AudioParams(int simpleRate, Format f) { this.simpleRate = simpleRate; this.format = f; } AudioParams(int simpleRate, int channelCount, int bits) { this.simpleRate = simpleRate; set(channelCount, bits); } int getBits() { return (format == Format.SINGLE_8_BIT || format == Format.DOUBLE_8_BIT) ? 8 : 16; } int getEncodingFormat() { return (format == Format.SINGLE_8_BIT || format == Format.DOUBLE_8_BIT) ? AudioFormat.ENCODING_PCM_8BIT : AudioFormat.ENCODING_PCM_16BIT; } int getChannelCount() {return (format == Format.SINGLE_8_BIT || format == Format.SINGLE_16_BIT) ? 1 : 2;} int getChannelConfig() { return (format == Format.SINGLE_8_BIT || format == Format.SINGLE_16_BIT) ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO; } int getOutChannelConfig() { return (format == Format.SINGLE_8_BIT || format == Format.SINGLE_16_BIT) ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO; } void set(int channelCount, int bits) { if ((channelCount != 1 && channelCount != 2) || (bits != 8 && bits != 16)) { throw new IllegalArgumentException('不支持其它格式 channelCount=$channelCount bits=$bits'); } if (channelCount == 1) { if (bits == 8) {format = Format.SINGLE_8_BIT; } else {format = Format.SINGLE_16_BIT; } } else { if (bits == 8) {format = Format.DOUBLE_8_BIT; } else {format = Format.DOUBLE_16_BIT; } } }}

這里固定使用了單通道8位,雙通道8位,單通道16位,雙通道16位,所以用了枚舉來限制。

為了方便把錄音數(shù)據(jù)拿出來顯示、存儲,這里寫了一個回調方法如下

public interface RecordCallback { /** * 數(shù)據(jù)回調 * * @param bytes 數(shù)據(jù) * @param len 數(shù)據(jù)有效長度,-1時表示數(shù)據(jù)結束 */ void onRecord(byte[] bytes, int len); }

有了這些參數(shù),現(xiàn)在就可以錄音了,先看一下樣例

public void startRecord(AudioParams params, RecordCallback callback) { int simpleRate = params.simpleRate; int channelConfig = params.getChannelConfig(); int audioFormat = params.getEncodingFormat(); // 根據(jù)AudioRecord提供的api拿到最小緩存大小 int bufferSize = AudioRecord.getMinBufferSize(simpleRate, channelConfig, audioFormat); //創(chuàng)建Record對象 record = new AudioRecord(MediaRecorder.AudioSource.MIC, simpleRate, channelConfig, audioFormat, bufferSize); recordThread = new Thread(() -> { byte[] buffer = new byte[bufferSize]; record.startRecording(); recording = true; while (recording) {int read = record.read(buffer, 0, bufferSize);// 將數(shù)據(jù)回調到外部if (read > 0 && callback != null) { callback.onRecord(buffer, read);} } if (callback != null) {// len 為-1時表示結束callback.onRecord(buffer, -1);recording = false; } //釋放資源 release(); }); recordThread.start(); }

這個方法就是簡單的采集音頻數(shù)據(jù),這個數(shù)據(jù)就是最原始的pcm數(shù)據(jù)。

拿到pcm數(shù)據(jù)以后,如果直接保存到文件是無法直接播放的,因為這只是一堆數(shù)據(jù),沒有任何格式說明,如果想讓普通播放器可以播放,需要在文件中加入文件頭,來告訴播放器這個數(shù)據(jù)的格式,這里是直接保存成wav格式的數(shù)據(jù)。下面就是加入wav格式文件頭的方法

private static byte[] getWaveFileHeader(int totalDataLen, int sampleRate, int channelCount, int bits) { byte[] header = new byte[44]; // RIFF/WAVE header header[0] = ’R’; header[1] = ’I’; header[2] = ’F’; header[3] = ’F’; int fileLength = totalDataLen + 36; header[4] = (byte) (fileLength & 0xff); header[5] = (byte) (fileLength >> 8 & 0xff); header[6] = (byte) (fileLength >> 16 & 0xff); header[7] = (byte) (fileLength >> 24 & 0xff); //WAVE header[8] = ’W’; header[9] = ’A’; header[10] = ’V’; header[11] = ’E’; // ’fmt ’ chunk header[12] = ’f’; header[13] = ’m’; header[14] = ’t’; header[15] = ’ ’; // 4 bytes: size of ’fmt ’ chunk header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0; // pcm format = 1 header[20] = 1; header[21] = 0; header[22] = (byte) channelCount; header[23] = 0; header[24] = (byte) (sampleRate & 0xff); header[25] = (byte) (sampleRate >> 8 & 0xff); header[26] = (byte) (sampleRate >> 16 & 0xff); header[27] = (byte) (sampleRate >> 24 & 0xff); int byteRate = sampleRate * bits * channelCount / 8; header[28] = (byte) (byteRate & 0xff); header[29] = (byte) (byteRate >> 8 & 0xff); header[30] = (byte) (byteRate >> 16 & 0xff); header[31] = (byte) (byteRate >> 24 & 0xff); // block align header[32] = (byte) (channelCount * bits / 8); header[33] = 0; // bits per sample header[34] = (byte) bits; header[35] = 0; //data header[36] = ’d’; header[37] = ’a’; header[38] = ’t’; header[39] = ’a’; header[40] = (byte) (totalDataLen & 0xff); header[41] = (byte) (totalDataLen >> 8 & 0xff); header[42] = (byte) (totalDataLen >> 16 & 0xff); header[43] = (byte) (totalDataLen >> 24 & 0xff); return header; }

根據(jù)幾個參數(shù)設置一下文件頭,然后直接寫入錄音采集到的pcm數(shù)據(jù),就可被正常播放了。wav文件頭格式定義,可點擊這里查看或自行百度。

如果想要通過AudioRecord錄音直接保存到文件,可參考下面方法

public void startRecord(String filePath, AudioParams params, RecordCallback callback) { int channelCount = params.getChannelCount(); int bits = params.getBits(); final boolean storeFile = filePath != null && !filePath.isEmpty(); startRecord(params, (bytes, len) -> { if (storeFile) {if (file == null) { File f = new File(filePath); if (f.exists()) { f.delete(); } try { file = new RandomAccessFile(f, 'rw'); file.write(getWaveFileHeader(0, params.simpleRate, channelCount, bits)); } catch (IOException e) { e.printStackTrace(); }}if (len > 0) { try { file.write(bytes, 0, len); } catch (IOException e) { e.printStackTrace(); }} else { try { // 因為在前面已經(jīng)寫入頭信息,所以這里要減去頭信息才是數(shù)據(jù)的長度 int length = (int) file.length() - 44; file.seek(0); file.write(getWaveFileHeader(length, params.simpleRate, channelCount, bits)); file.close(); } catch (IOException e) { e.printStackTrace(); }} } if (callback != null) {callback.onRecord(bytes, len); } }); }

先通過RandomAccessFile創(chuàng)建文件,先寫入文件頭,由于暫時我們不知道會錄多長,有多少pcm數(shù)據(jù),長度先用0表示,等錄音結束后,通過seek(int)方法重新寫入文件頭信息,也可以先把pcm數(shù)據(jù)保存到臨時文件,然后再寫入到一個新的文件中,這里就不舉例說明了。

最后放入完整類的代碼

package cn.sskbskdrin.record.audio;import android.media.AudioRecord;import android.media.MediaRecorder;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;/** * @author sskbskdrin * @date 2019/April/3 */public class AudioRecordManager { private AudioParams DEFAULT_FORMAT = new AudioParams(8000, 1, 16); private AudioRecord record; private Thread recordThread; private boolean recording = false; private RandomAccessFile file; public void startRecord(String filePath, RecordCallback callback) { startRecord(filePath, DEFAULT_FORMAT, callback); } public void startRecord(String filePath, AudioParams params, RecordCallback callback) { int channelCount = params.getChannelCount(); int bits = params.getBits(); final boolean storeFile = filePath != null && !filePath.isEmpty(); startRecord(params, (bytes, len) -> { if (storeFile) {if (file == null) { File f = new File(filePath); if (f.exists()) { f.delete(); } try { file = new RandomAccessFile(f, 'rw'); file.write(getWaveFileHeader(0, params.simpleRate, channelCount, bits)); } catch (IOException e) { e.printStackTrace(); }}if (len > 0) { try { file.write(bytes, 0, len); } catch (IOException e) { e.printStackTrace(); }} else { try { // 因為在前面已經(jīng)寫入頭信息,所以這里要減去頭信息才是數(shù)據(jù)的長度 int length = (int) file.length() - 44; file.seek(0); file.write(getWaveFileHeader(length, params.simpleRate, channelCount, bits)); file.close(); } catch (IOException e) { e.printStackTrace(); }} } if (callback != null) {callback.onRecord(bytes, len); } }); } public void startRecord(AudioParams params, RecordCallback callback) { int simpleRate = params.simpleRate; int channelConfig = params.getChannelConfig(); int audioFormat = params.getEncodingFormat(); // 根據(jù)AudioRecord提供的api拿到最小緩存大小 int bufferSize = AudioRecord.getMinBufferSize(simpleRate, channelConfig, audioFormat); //創(chuàng)建Record對象 record = new AudioRecord(MediaRecorder.AudioSource.MIC, simpleRate, channelConfig, audioFormat, bufferSize); recordThread = new Thread(() -> { byte[] buffer = new byte[bufferSize]; record.startRecording(); recording = true; while (recording) {int read = record.read(buffer, 0, bufferSize);// 將數(shù)據(jù)回調到外部if (read > 0 && callback != null) { callback.onRecord(buffer, read);} } if (callback != null) {// len 為-1時表示結束callback.onRecord(buffer, -1);recording = false; } //釋放資源 release(); }); recordThread.start(); } public void stop() { recording = false; } public void release() { recording = false; if (record != null) { record.stop(); record.release(); } record = null; file = null; recordThread = null; } private static byte[] getWaveFileHeader(int totalDataLen, int sampleRate, int channelCount, int bits) { byte[] header = new byte[44]; // RIFF/WAVE header header[0] = ’R’; header[1] = ’I’; header[2] = ’F’; header[3] = ’F’; int fileLength = totalDataLen + 36; header[4] = (byte) (fileLength & 0xff); header[5] = (byte) (fileLength >> 8 & 0xff); header[6] = (byte) (fileLength >> 16 & 0xff); header[7] = (byte) (fileLength >> 24 & 0xff); //WAVE header[8] = ’W’; header[9] = ’A’; header[10] = ’V’; header[11] = ’E’; // ’fmt ’ chunk header[12] = ’f’; header[13] = ’m’; header[14] = ’t’; header[15] = ’ ’; // 4 bytes: size of ’fmt ’ chunk header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0; // pcm format = 1 header[20] = 1; header[21] = 0; header[22] = (byte) channelCount; header[23] = 0; header[24] = (byte) (sampleRate & 0xff); header[25] = (byte) (sampleRate >> 8 & 0xff); header[26] = (byte) (sampleRate >> 16 & 0xff); header[27] = (byte) (sampleRate >> 24 & 0xff); int byteRate = sampleRate * bits * channelCount / 8; header[28] = (byte) (byteRate & 0xff); header[29] = (byte) (byteRate >> 8 & 0xff); header[30] = (byte) (byteRate >> 16 & 0xff); header[31] = (byte) (byteRate >> 24 & 0xff); // block align header[32] = (byte) (channelCount * bits / 8); header[33] = 0; // bits per sample header[34] = (byte) bits; header[35] = 0; //data header[36] = ’d’; header[37] = ’a’; header[38] = ’t’; header[39] = ’a’; header[40] = (byte) (totalDataLen & 0xff); header[41] = (byte) (totalDataLen >> 8 & 0xff); header[42] = (byte) (totalDataLen >> 16 & 0xff); header[43] = (byte) (totalDataLen >> 24 & 0xff); return header; } public interface RecordCallback { /** * 數(shù)據(jù)回調 * * @param bytes 數(shù)據(jù) * @param len 數(shù)據(jù)有效長度,-1時表示數(shù)據(jù)結束 */ void onRecord(byte[] bytes, int len); }}

如有不對之處還請評論指正

以上就是Android用AudioRecord進行錄音的詳細內容,更多關于Android AudioRecord的資料請關注好吧啦網(wǎng)其它相關文章!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
丝瓜av网站精品一区二区| 91日韩免费| 久久影院资源站| 天堂精品久久久久| 午夜久久美女| 热三久草你在线| 日韩精选在线| 久久不射网站| 欧美少妇精品| 精品一区91| 国产亚洲福利| 国产精品v亚洲精品v日韩精品| 老鸭窝毛片一区二区三区| 免费在线看一区| 亚洲自啪免费| 不卡一区2区| 91精品观看| 精品成人免费一区二区在线播放| 国产精品伦一区二区| 日本aⅴ免费视频一区二区三区| 免费久久99精品国产自在现线| 欧美好骚综合网| 国产精品国产三级国产在线观看| 91麻豆精品| 蜜臀久久久久久久| 久久亚洲影院| 日韩国产欧美一区二区三区| 日本在线不卡视频一二三区| 日韩午夜视频在线| 亚洲精品高潮| 婷婷综合一区| 91成人福利| 免费在线看一区| 亚洲一级淫片| 久久av超碰| 激情国产在线| 国产精品88久久久久久| 日日夜夜免费精品| 国产精品一区二区三区四区在线观看| 91在线成人| 欧美国产美女| 亚洲综合日韩| 欧美视频一区| 青青青免费在线视频| 亚洲欧洲另类| 久久精品 人人爱| 亚洲黄色免费av| 成人片免费看| 精品国产美女a久久9999| 欧美中文日韩| 欧美日本三区| 特黄毛片在线观看| 欧美日韩精品一本二本三本| 亚洲人成精品久久久| 日本精品一区二区三区在线观看视频 | 欧美亚洲国产一区| 亚洲人成高清| 麻豆成人av在线| 黄色成人精品网站| 91成人精品观看| 中文在线中文资源| 亚洲欧美日本视频在线观看| 国产精品三级| 婷婷精品视频| 欧美日韩网址| 亚洲高清不卡| 国产另类在线| 久久裸体视频| 久久精品99久久久| 国产一区欧美| 国产欧美91| 亚洲第一精品影视| 69堂精品视频在线播放| 久久国产主播| 国产乱码精品一区二区三区亚洲人| 日韩欧美1区| 日韩黄色在线观看| 欧美丝袜一区| 久久中文在线| 伊人久久大香伊蕉在人线观看热v| 精品久久美女| 中文精品电影| 91一区二区| 91成人精品在线| 好吊一区二区三区| 成人亚洲欧美| 你懂的国产精品永久在线| 日韩午夜av| 亚洲最新无码中文字幕久久| 美腿丝袜在线亚洲一区| 在线看片日韩| 黄色在线一区| 丝袜诱惑一区二区| 久久成人高清| 日本国产欧美| 久久不射网站| 亚洲精品午夜av福利久久蜜桃| 久草精品视频| 日韩av不卡在线观看| 亚洲一级在线| 亚洲成人精品| 美腿丝袜在线亚洲一区| 免费视频一区二区| 欧美亚洲国产激情| 精品资源在线| 91p九色成人| 免费视频国产一区| 国产三级精品三级在线观看国产| 天堂а√在线最新版中文在线| 国产精品v日韩精品v欧美精品网站| 午夜国产精品视频免费体验区| 精品国产一区二区三区噜噜噜| 99国产精品自拍| 日韩欧美在线中字| 国产精品亚洲产品| 日韩在线视频一区二区三区| 日韩中文视频| 精品一区二区三区免费看 | 美女精品在线观看| 亚洲伦乱视频| 欧美成人aaa| 日本成人在线不卡视频| 樱桃视频成人在线观看| 另类综合日韩欧美亚洲| 日韩一区二区三免费高清在线观看| 久久视频一区| 免费视频一区二区三区在线观看| 国产亚洲毛片在线| 欧美成人基地| 精品视频一区二区三区四区五区 | 久久男人天堂| 国产日韩一区| 男女性色大片免费观看一区二区| 国产99久久| 日韩中文在线播放| 国产理论在线| 精品三级在线| 久久99久久人婷婷精品综合| 日本不卡一区二区| 中文字幕日韩欧美精品高清在线| 一本一道久久a久久精品蜜桃| 午夜影院一区| 精品精品国产三级a∨在线| 国产欧美日韩精品高清二区综合区 | 国产三级精品三级在线观看国产| 亚洲伊人精品酒店| 亚洲视频播放| 午夜久久久久| 亚洲欧美日韩高清在线| 美女精品在线| 久久国产精品99国产| 亚洲小说欧美另类婷婷| 久久人人97超碰国产公开结果| 日韩免费视频| 久久天堂精品| 婷婷久久一区| 久久久久亚洲| 日韩和的一区二在线| 国产不卡人人| 欧美一级鲁丝片| 另类中文字幕国产精品| 午夜精品成人av| 欧美 日韩 国产一区二区在线视频 | 免费观看在线综合色| 免费视频一区二区| 蜜桃视频一区二区三区 | 在线综合亚洲| 亚洲欧美网站在线观看| 亚洲啊v在线免费视频| 日韩1区2区日韩1区2区| 日本综合精品一区| 欧美午夜三级| 国产精品日韩精品中文字幕| 国产精品久久久久久久免费观看| 日韩电影免费网站| 久久精品免费一区二区三区 | 久久先锋影音| 日韩国产精品久久久久久亚洲| 青青伊人久久| 精品视频一区二区三区在线观看 | 久久三级毛片| 免费高潮视频95在线观看网站| 99久久亚洲精品蜜臀| 国产精品毛片| 日韩精品欧美精品| 欧美黄页在线免费观看| 国产精品久久久久久久久久10秀| 久久蜜桃精品| 日韩影院免费视频| 91成人在线网站| 首页国产精品| 欧美在线亚洲综合一区| 国产精品美女久久久| 无码日韩精品一区二区免费| 国产精品高清一区二区| 欧美私人啪啪vps| 五月天综合网站| 日韩av午夜在线观看| 成年男女免费视频网站不卡| 中文日韩在线|