电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

Android用AudioRecord進(jìn)行錄音

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

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

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

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

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

在學(xué)習(xí)過程中會(huì)用到的一些參數(shù),我這里封裝了一個(gè)類,如下

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ù)拿出來顯示、存儲(chǔ),這里寫了一個(gè)回調(diào)方法如下

public interface RecordCallback { /** * 數(shù)據(jù)回調(diào) * * @param bytes 數(shù)據(jù) * @param len 數(shù)據(jù)有效長度,-1時(shí)表示數(shù)據(jù)結(jié)束 */ 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對(duì)象 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ù)回調(diào)到外部if (read > 0 && callback != null) { callback.onRecord(buffer, read);} } if (callback != null) {// len 為-1時(shí)表示結(jié)束callback.onRecord(buffer, -1);recording = false; } //釋放資源 release(); }); recordThread.start(); }

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

拿到pcm數(shù)據(jù)以后,如果直接保存到文件是無法直接播放的,因?yàn)檫@只是一堆數(shù)據(jù),沒有任何格式說明,如果想讓普通播放器可以播放,需要在文件中加入文件頭,來告訴播放器這個(gè)數(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ù)幾個(gè)參數(shù)設(shè)置一下文件頭,然后直接寫入錄音采集到的pcm數(shù)據(jù),就可被正常播放了。wav文件頭格式定義,可點(diǎn)擊這里查看或自行百度。

如果想要通過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 { // 因?yàn)樵谇懊嬉呀?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)建文件,先寫入文件頭,由于暫時(shí)我們不知道會(huì)錄多長,有多少pcm數(shù)據(jù),長度先用0表示,等錄音結(jié)束后,通過seek(int)方法重新寫入文件頭信息,也可以先把pcm數(shù)據(jù)保存到臨時(shí)文件,然后再寫入到一個(gè)新的文件中,這里就不舉例說明了。

最后放入完整類的代碼

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 { // 因?yàn)樵谇懊嬉呀?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對(duì)象 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ù)回調(diào)到外部if (read > 0 && callback != null) { callback.onRecord(buffer, read);} } if (callback != null) {// len 為-1時(shí)表示結(jié)束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ù)回調(diào) * * @param bytes 數(shù)據(jù) * @param len 數(shù)據(jù)有效長度,-1時(shí)表示數(shù)據(jù)結(jié)束 */ void onRecord(byte[] bytes, int len); }}

如有不對(duì)之處還請(qǐng)?jiān)u論指正

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

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 电动高尔夫球车|电动观光车|电动巡逻车|电动越野车厂家-绿友机械集团股份有限公司 | 耐驰泵阀管件制造-耐驰泵阀科技(天津)有限公司 | 河南mpp电力管_mpp电力管生产厂家_mpp电力电缆保护管价格 - 河南晨翀实业 | 钢制暖气片散热器_天津钢制暖气片_卡麦罗散热器厂家 | 凝胶成像系统(wb成像系统)百科-上海嘉鹏| 免费分销系统 — 分销商城系统_分销小程序开发 -【微商来】 | 苏州伊诺尔拆除公司_专业酒店厂房拆除_商场学校拆除_办公楼房屋拆除_家工装拆除拆旧 | 立式矫直机_卧式矫直机-无锡金矫机械制造有限公司 | 济南冷库安装-山东冷库设计|建造|冷库维修-山东齐雪制冷设备有限公司 | MES系统-WMS系统-MES定制开发-制造执行MES解决方案-罗浮云计算 | 地源热泵一体机,地源热泵厂家-淄博汇能环保设备有限公司 | Pos机办理_个人商户免费POS机申请-拉卡拉办理网 | 消泡剂_水处理消泡剂_切削液消泡剂_涂料消泡剂_有机硅消泡剂_广州中万新材料生产厂家 | 充气膜专家-气膜馆-PTFE膜结构-ETFE膜结构-商业街膜结构-奥克金鼎 | 广东高华家具-公寓床|学生宿舍双层铁床厂家【质保十年】 | 热镀锌槽钢|角钢|工字钢|圆钢|H型钢|扁钢|花纹板-天津千百顺钢铁贸易有限公司 | 土壤墒情监测站_土壤墒情监测仪_土壤墒情监测系统_管式土壤墒情站-山东风途物联网 | 工业胀紧套_万向节联轴器_链条-规格齐全-型号选购-非标订做-厂家批发价格-上海乙谛精密机械有限公司 | 污水/卧式/潜水/钻井/矿用/大型/小型/泥浆泵,价格,参数,型号,厂家 - 安平县鼎千泵业制造厂 | 不锈钢/气体/液体玻璃转子流量计(防腐,选型,规格)-常州天晟热工仪表有限公司【官网】 | 深圳装修_店面装修设计_餐厅设计_装修全包价格-尚泰装饰设计 | 建筑工程资质合作-工程资质加盟分公司-建筑资质加盟 | 论文查重_免费论文查重_知网学术不端论文查重检测系统入口_论文查重软件 | 除尘布袋_液体过滤袋_针刺毡滤料-杭州辉龙过滤技术有限公司 | 直线模组_滚珠丝杆滑台_模组滑台厂家_万里疆科技 | 安徽合肥格力空调专卖店_格力中央空调_格力空调总经销公司代理-皖格制冷设备 | 杭州成人高考_浙江省成人高考网上报名| 安全光栅|射频导纳物位开关|音叉料位计|雷达液位计|两级跑偏开关|双向拉绳开关-山东卓信机械有限公司 | Honsberg流量计-Greisinger真空表-气压计-上海欧臻机电设备有限公司 | 锻造液压机,粉末冶金,拉伸,坩埚成型液压机定制生产厂家-山东威力重工官方网站 | 细砂提取机,隔膜板框泥浆污泥压滤机,螺旋洗砂机设备,轮式洗砂机械,机制砂,圆锥颚式反击式破碎机,振动筛,滚筒筛,喂料机- 上海重睿环保设备有限公司 | 沈阳庭院景观设计_私家花园_别墅庭院设计_阳台楼顶花园设计施工公司-【沈阳现代时园艺景观工程有限公司】 | 吹塑加工_大型吹塑加工_滚塑代加工-莱力奇吹塑加工有限公司 | 细沙回收机-尾矿干排脱水筛设备-泥石分离机-建筑垃圾分拣机厂家-青州冠诚重工机械有限公司 | J.S.Bach 圣巴赫_高端背景音乐系统_官网 | 黑龙江京科脑康医院-哈尔滨精神病医院哪家好_哈尔滨精神科医院排名_黑龙江精神心理病专科医院 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 苏州防水公司_厂房屋面外墙防水_地下室卫生间防水堵漏-苏州伊诺尔防水工程有限公司 | 蓝莓施肥机,智能施肥机,自动施肥机,水肥一体化项目,水肥一体机厂家,小型施肥机,圣大节水,滴灌施工方案,山东圣大节水科技有限公司官网17864474793 | 冷库安装厂家_杭州冷库_保鲜库建设-浙江克冷制冷设备有限公司 | 热熔胶网膜|pes热熔网膜价格|eva热熔胶膜|热熔胶膜|tpu热熔胶膜厂家-苏州惠洋胶粘制品有限公司 |