Android音频开发及录音文件的配置
一、Android音频开发(一):音频基础知识二、Android音频开发(二):录制音频(WAV及MP3格式)三、Android音频开发(三):使用ExoPlayer播放音频四、Android音频开发(四):音频播放模式五、Android音频开发(五):感应(息屏/亮屏)管理
附GitHub源码:MultimediaExplore
(相关资料图)
首先看下音频录制跟播放效果简图:
CSDN不支持本地视频上传,我就先上传了一张截图:
上面是录音:长按即可录音,支持声波动画,右滑删除等。支持录制pcm、wav、mp3格式音频。
下面是播放:点击左边扬声器icon,开始播放刚录制的本地音频文件【也支持在线音频播放】,支持播放进度,支持切换播放模式(听筒/扬声器/耳机)等。
一、音频录制权限:
无论在做开发任何功能之前,总得先添加及申请相关权限,后续的工作才能正常进行下去。音频录制所需权限如下,而且要在代码中动态申请这些敏感权限,同意后才能正常录制:
二、录音文件的配置:
通过第一节讲到音频的基础概念可知,在录制音频前应先进行录制的相关配置,它直接决定了录音文件的音频质量、文件大小、音频格式等。
/** * 录音音频的相关配置 */ private void initConfig() { recordConfig = new RecordConfig(); //采样位宽 recordConfig.setEncodingConfig(AudioFormat.ENCODING_PCM_16BIT); //录音格式 recordConfig.setFormat(RecordConfig.RecordFormat.MP3); // recordConfig.setFormat(RecordConfig.RecordFormat.WAV); //采样频率 recordConfig.setSampleRate(16000); String recordDir = String.format(Locale.getDefault(), "%s/Record/zhongyao/", Environment.getExternalStorageDirectory().getAbsolutePath()); //存储目录 recordConfig.setRecordDir(recordDir); AudioRecordManager.getInstance().setCurrentConfig(recordConfig); }
三、音频录制:
音频录制类主要有两个封装类:分别是AudioRecorder 、AudioRecordManager。
AudioRecorder:主要是使用系统的AudioRecord来进行录音。并把录制好的音频文件进行合并,转码等,生成我们所需的音频文件。该文件是全局单例的,保证音频录制类只有一个实例。
AudioRecordManager:对AudioRecorder的封装管理,与外界交互均通过此类来完成,包括录音的各种生命周期控制调用等。减少了外界与AudioRecorder的直接交互,已达到对录音类的更好的管理,此类也是一个全局单例类。
1、录音对象初始化:
这里主要根据之前的录音配置,生成 bufferSizeInBytes【缓冲区字节大小】,和audioRecord对象。
/** * 创建默认的录音对象 */ public void prepareRecord() { // 获得缓冲区字节大小 if (bufferSizeInBytes == 0) { bufferSizeInBytes = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(), currentConfig.getChannelConfig(), currentConfig.getEncodingConfig()); } if (audioRecord == null) { audioRecord = new AudioRecord(AUDIO_INPUT, currentConfig.getSampleRate(), currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSizeInBytes); } audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_PREPARE; }
2、录制wav音频文件:
wav音频文件是无损的,所以音质会接近原生,但也正是因为是无损的,所以wav音频文件几乎没有压缩,相对来说会比较大。
录制wav音频得先进行录制采用,获得pcm文件,然后把pcm文件合并,最后再转成wav音频文件。
(1)开始录制pcm文件:
private void startPcmRecorder() { audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START; notifyState(); Logger.d(TAG, "开始录制 Pcm"); FileOutputStream fos = null; try { fos = new FileOutputStream(tmpFile); audioRecord.startRecording(); byte[] byteBuffer = new byte[bufferSizeInBytes]; while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) { int end = audioRecord.read(byteBuffer, 0, byteBuffer.length); notifyData(byteBuffer); fos.write(byteBuffer, 0, end); fos.flush(); } audioRecord.stop(); files.add(tmpFile); if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_STOP) { makeFile(); } else { Logger.d(TAG, "取消录制..."); } } catch (Exception e) { Logger.e(e, TAG, e.getMessage()); notifyError("录音失败"); } finally { try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) { audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_IDLE; notifyState(); Logger.d(TAG, "录音结束"); } }
(2)合并生成的多个pcm文件:
/** * 合并pcm文件 */ private void mergePcmFile() { boolean mergeSuccess = mergePcmFiles(resultFile, files); if (!mergeSuccess) { notifyError("合并失败"); } }
(3)将合并好的pcm文件转成wav文件:
/** * 添加Wav头文件 */ private void makeWav() { if (!FileUtil.isFile(resultFile) || resultFile.length() == 0) { return; } byte[] header = WavUtils.generateWavFileHeader((int) resultFile.length(), currentConfig.getSampleRate(), currentConfig.getChannelCount(), currentConfig.getEncoding()); WavUtils.writeHeader(resultFile, header); }
3、录制MP3音频文件
相比WAV音频文件而言,MP3音频文件,就更加常见,商业上使用的也比较多,就是因为MP3音频时经过压缩的,文件大小只有WAV的十二分之一,但是音质上几乎没有较大的差异性。当对音质没有极高要求的情况下,如录音文件,MP3格式是极好的选择。
(1)开始录制音频缓存:
这里有开启一个线程Mp3EncodeThread,将录音产生的字节数组byteBuffer不断的进行编解码生成MP3文件。
private void startMp3Recorder() { audioRecordStatus = AudioRecordStatus.AUDIO_RECORD_START; notifyState(); try { audioRecord.startRecording(); short[] byteBuffer = new short[bufferSizeInBytes]; while (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_START) { int end = audioRecord.read(byteBuffer, 0, byteBuffer.length); if (mp3EncodeThread != null) { mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end)); } notifyData(ByteUtils.toBytes(byteBuffer)); } audioRecord.stop(); } catch (Exception e) { Logger.e(e, TAG, e.getMessage()); notifyError("录音失败"); } if (audioRecordStatus != AudioRecordStatus.AUDIO_RECORD_PAUSE) { if (audioRecordStatus == AudioRecordStatus.AUDIO_RECORD_CANCEL) { deleteMp3Encoded(); } else { stopMp3Encoded(); } } else { Logger.d(TAG, "暂停"); } }
(2)MP3音频编解码:
Android原生的音频录制,支持直接生成WAV文件,但其实是不支持直接生成MP3文件的。这里对应MP3编解码,主要用到了开源的 libmp3lame.so 这个音频编解码库。以下是lame编解码方法及Mp3Encoder类:
MP3编解码方法:
private void lameData(ChangeBuffer changeBuffer) { if (changeBuffer == null) { return; } short[] buffer = changeBuffer.getData(); int readSize = changeBuffer.getReadSize(); if (readSize > 0) { int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer); if (encodedSize < 0) { Logger.e(TAG, "Lame encoded size: " + encodedSize); } try { os.write(mp3Buffer, 0, encodedSize); } catch (IOException e) { Logger.e(e, TAG, "Unable to write to file"); } } }
Mp3Encoder类:
public class Mp3Encoder { static { System.loadLibrary("mp3lame"); } public native static void close(); public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf); public native static int flush(byte[] mp3buf); public native static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality); public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) { init(inSampleRate, outChannel, outSampleRate, outBitrate, 7); }}
(3)生成libmp3lame.so:
本项目提供的源码中有lame的jni源码,如果想生成libmp3lame.so文件,供自己的项目使用,那么需要对Mp3Encoder.c 和Mp3Encoder.h文件进行修改,然后通过ndk build 生成该so文件。
修改的内容主要是把文件中的Mp3Encoder路径改成自己项目中的Mp3Encoder的路径,否则会找不到相关的native方法。
另外一般情况下,cpu类型支持 armeabi-v7a 、arm64-v8a 两种即可,如果想支持其他的可在Application.mk中添加。
上面文件修改完毕,通过ndk-build指令即可编译生成对应的 libmp3lame.so 文件。
将so不同CPU类型的文件放置 jniLibs 下,或者通过sourceSets配置的路径下,如:
sourceSets.main { jni.srcDirs = []//disable automatic ndk-build call jniLibs.srcDirs = ["libs"] }
即可进行MP3音频格式的录制。
四、音频录制管理【AudioRecordManager】:
通过全局单例模式的AudioRecorderManager与业务进行交互,即保证了音频录制实例的单一性,又能较好的对音频生命周期等进行较好的管理。
/** * Create by zhongyao on 2021/8/18 * Description:音频录制管理类 */public class AudioRecordManager { private static final String TAG = "AudioRecordManager"; private AudioRecordManager() { } public static AudioRecordManager getInstance() { return AudioRecordManagerHolder.instance; } public static class AudioRecordManagerHolder { public static AudioRecordManager instance = new AudioRecordManager(); } public void setCurrentConfig(RecordConfig recordConfig) { AudioRecorder.getInstance().setCurrentConfig(recordConfig); } public RecordConfig getCurrentConfig() { return AudioRecorder.getInstance().getCurrentConfig(); } /** * 录音状态监听回调 */ public void setRecordStateListener(RecordStateListener listener) { AudioRecorder.getInstance().setRecordStateListener(listener); } /** * 录音数据监听回调 */ public void setRecordDataListener(RecordDataListener listener) { AudioRecorder.getInstance().setRecordDataListener(listener); } /** * 录音可视化数据回调,傅里叶转换后的频域数据 */ public void setRecordFftDataListener(RecordFftDataListener recordFftDataListener) { AudioRecorder.getInstance().setRecordFftDataListener(recordFftDataListener); } /** * 录音文件转换结束回调 */ public void setRecordResultListener(RecordResultListener listener) { AudioRecorder.getInstance().setRecordResultListener(listener); } /** * 录音音量监听回调 */ public void setRecordSoundSizeListener(RecordSoundSizeListener listener) { AudioRecorder.getInstance().setRecordSoundSizeListener(listener); } public void setStatus(AudioRecordStatus curAudioRecordStatus) { switch (curAudioRecordStatus) { case AUDIO_RECORD_IDLE: break; case AUDIO_RECORD_PREPARE: AudioRecorder.getInstance().prepareRecord(); break; case AUDIO_RECORD_START: AudioRecorder.getInstance().startRecord(); break; case AUDIO_RECORD_PAUSE: AudioRecorder.getInstance().pauseRecord(); break; case AUDIO_RECORD_STOP: AudioRecorder.getInstance().stopRecord(); break; case AUDIO_RECORD_CANCEL: AudioRecorder.getInstance().cancelRecord(); break; case AUDIO_RECORD_RELEASE: AudioRecorder.getInstance().releaseRecord(); break; default: break; } } public AudioRecordStatus getStatus() { return AudioRecorder.getInstance().getAudioRecordStatus(); } public String getAudioPath() { return AudioRecorder.getInstance().getAudioPath(); }}
标签: 音频文件
相关推荐:
最新新闻:
- 苹果手机为什么充不进电?苹果手机充电充不进去电的原因及解决方法-每日动态
- 三星e728报价是多少?三星首款天线折叠手机E728评测-环球消息
- Android音频开发及录音文件的配置
- 当前热议!【管理-固件升级】从DD-WRT固件刷到Tomato的升级方案
- 可以搜附近人的交友软件有哪些?有哪些靠谱的交友软件?
- 如何查询appInfo、appName?appInfo查询方法_当前关注
- Windows、Mac、Unix、Linux什么是iOS?-报道
- 世界观天下!GIS开发Georver:共享地理空间数据的开源服务器
- SP2的系统怎么操作?如何使用SP2的系统文件?
- dv机哪个好?索尼FD-AX100E热门款型推荐
- Android直播新思路:4.1Android推流到服务器:要闻速递
- QQ.COM二级域名大全 腾讯旗下产品大全
- 每日消息!泛域名证书不起作用?主机绑定多个域名就可以了
- 当前看点!cad怎么导出jpg图片格式?cad导出jpg图片格式的方法
- 带你走一遍自定义View之旅——效果图
- 【每日一练】什么是预编译?何时需要编译?
- 在pycharm中运行代码时显示下面错误如何解决?
- 3999元起!长虹最新手机三款介绍评测来了
- 模板文件css.htm制作教程 Discuz!模板文件的简单讲解
- 如何在12306网上订火车票官网退票?12306网上订火车票官网退票教程|当前最新
- 华为mate40Pro如何开启颜色反转?开启颜色反转方式:今日最新
- vuejs之Vue2.x中的父组件数据传递至子组件的方法
- 天天最新:在Ubuntu18.04、Deep终端中实现输入密码回显星号
- 天天简讯:在访问网页中弹出“脚本错误”的提示框 如何解决?
- 《绝地求生》主播打造3A级开放世界生存游戏新作 世界观热点
- 今年夏季《忍者小子》将改编成四人合作动作游戏
- 环球新动态:性价比绝了!中兴小方糖双频千兆路由器99元
- 不怕偷 哈罗电动车2373元
- 长江存储颗粒!梵想S500PRO 2TB M.2固态硬盘低至579元|环球观察
- 又曝大Bug!Windows 11会导致SSD速度降低
- 清仓大促销!AMD R5-5500板U套装低至789元
- 每日资讯:Roguelike策略卡牌游戏《河口蟾蜍》面向PC公布
- Epic喜+1:策略游戏《战锤40K:角斗士之战争圣器》 下周送《终极象棋》
- 《红霞岛》公布剧情预告 5月2日正式发售 快讯
- 阿斯报:维尼修斯将视频出庭指证种族主义辱骂他的马洛卡球迷:要闻
- 【环球新视野】《迪士尼竞速风暴》将于4/18以抢先体验形式推出
- 2023年科隆展将迎来一个大厂回归 有更多公司参加-天天亮点
- 湖北省黄石市市场监管局抽检食品42批次 1批次不合格
- 环保少女偷删世界末日预言推文 曾称人类会被毁灭_全球今亮点
- 外媒:《GTA6》应加入私服 因为《GTAOL》私服很火
- 【全球独家】DLSS3普及速度更快:《暗黑4》《极限竞速:地平线5》和虚幻5.2
- 英国有6家游戏公司支持微软收购 只有索尼不支持
- 全球观速讯丨小米智能鱼缸399元开售 支持半年免换水 远程喂鱼
- 本田召回近45万辆汽车 安全带自己会开
- 特斯拉Model 3改装120寸大轮毂:暴力测试来了
- 天禄科技(301045):关于召开2023年第三次临时股东大会,审议《关于公司符合向特定对象发行股票条件的议案》等议案
- 观速讯丨现实版GTA?美国劫匪偷直升机因操作失误当场坠毁
- 《最终幻想起源:天堂的陌生人》4月登陆Steam-全球观点
- 张译新剧《他是谁》豆瓣大量好评!张译演技绝了-简讯
- 确有其事还是开玩笑?刘德华将导演成龙吴京电影 世界热头条
- 环球今日报丨立省195元!16GB内存条崩盘价174元
- “绵阳造”上海行 长虹激光携光影耀申城
- 卡普空《恐龙浩劫》公测开启 残暴恐龙大军来袭
- 当前视讯!游民专访周董...的卫冕电竞战队Jteam
- 环球微速讯:《生化危机4:重制版》艾达王篇和佣兵模式或以DLC形式推出
- 文心一言秒变毕加索 看看百度AI画的青春校园女孩
- 环球聚焦:透明键盘超炫酷!戴尔背光机械键盘直降400元入手
- 调教Siri!苹果入局人工智能_环球微头条
- 王者荣耀体验服爆料:新增姐弟关系,基友关系改名
- 优势互补、携手共赢|极智嘉(Geek+)合作九州云智
- 每日观察!提前尝鲜!参与京东“先人一步”提前到手Find X6系列新机
- 环球精选!手游太赚钱!去年苹果手游收入几乎等于Xbox总收入
- 当前关注:11bit推出超豪华捆绑包 工作室所有作品打包卖95元
- 文心一言已有650家合作伙伴接入:内测并不完美
- 天天观速讯丨《机动战士高达:激战任务2》下载量突破500万 官方举办感谢活动
- Steam2023春季促销有哪些游戏?Steam春促2023哪些游戏打折?
- 感觉还不错 《生化危机4重制版》第一人称mod 世界实时
- 10股特大单净流入资金超2亿元_今日热闻
- 天天最新:看看有没有你的手机!MIUI 14第三批正式发布,共有18款手机机型在名单中
- 品质永不过时,A.O.史密斯加力打造智能舒适家居放心服务:天天热闻
- 对信息泄露和定位跟踪说“再见”,TD Tech F4智防手机护你安全出行
- 全球即时看!幼升小护眼台灯首选,看状元郎宽面学习台灯如何凭实力出圈
- 脑机接口产业技术路线图研究项目专家研讨会在沪顺利召开:天天热讯
- 山雨欲来风满楼 友谊时光新游《浮生忆玲珑》PV首曝
- 天天快消息!《最后生还者》艾莉声优盛赞小熊女演技:没人比她更适合这个角色