很多唱歌类型的APP(比如唱吧),都在寻求实现好的用户体验,其中一个就是能够一边边播放歌曲一边唱(声音会被录下来),有人便想进一步实现播放录的声音(就像拿着麦克风唱歌的感觉,自己可以听见自己的声音),这个想法是好的,也可行。

具体方案是:MediaPlayer播放音乐,MediaRecorder录音并保存; AudioTrack和AudioRecord两个配合实现实时语音流的记录和同步播放(可认为是轻量级的MediaPlayer和MediaRecorder,细节还有有差别请自行检索)

但是要是你去下载唱吧,却发现它并没有实现“可以听见自己唱歌的声音”的效果,经过我在做一个项目中的尝试、研究发现,原来android4.3(API18)以上不支持MediaRecorder与AudioRecord的共用,即:只要同时使用两者,则只能实现存储录音文件(MediaRecorder),不能实时获取语音流(AudioRecord)。

具体代码可见下方:

public class MainActivity extends Activity{private MediaPlayer mediaPlayer;private MediaRecorder mediaRecorder;private AudioTrack audioTrack;private AudioRecord audioRecord;private int recBufSize, playBufSize;private static final int sampleRateInHz = 44100;private static final int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_STEREO;private static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initMediaPlayer();initMediaRecord();initAudioTrack();initAudioRecord();new RecordPlayThread().start();}/*** 初始化记录音频流资源*/private void initAudioRecord() {recBufSize = audioRecord.getMinBufferSize(sampleRateInHz,channelConfig, audioFormat);audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRateInHz, channelConfig, audioFormat, recBufSize);}/*** 初始化播放音频流资源*/private void initAudioTrack() {playBufSize = audioTrack.getMinBufferSize(sampleRateInHz,channelConfig, audioFormat);audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz,channelConfig, audioFormat, playBufSize,AudioTrack.MODE_STREAM);}//音乐播放路径:需要在手机里的"1yzz"文件夹中放一个名为"test1.mp3"的文件(可自行修改)private String playpath1 = Environment.getExternalStorageDirectory()+ "/1yzz/test1.mp3";  /*** 初始化音乐播放*/private void initMediaPlayer(){if (mediaPlayer!=null){mediaPlayer.stop();mediaPlayer.release();mediaPlayer = null;}mediaPlayer = new MediaPlayer();try {mediaPlayer.setDataSource(playpath1);mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mediaPlayer.setLooping(true);mediaPlayer.prepare();mediaPlayer.start();} catch (Exception e) {e.printStackTrace();}}private String recordpath; private File audioFile;private boolean isrecording;/*** 初始化录音*/private void initMediaRecord() {if (mediaRecorder != null) {mediaRecorder.stop();mediaRecorder.release();mediaRecorder = null;}SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");String date = sDateFormat.format(new java.util.Date());//录音文件保存路径:手机中的"1yzz"文件夹(可自行修改)String Fpath = Environment.getExternalStorageDirectory() + "/1yzz";File file = new File(Fpath);if (!file.exists()) {file.mkdir();}//录音文件命名方式:"时间"+"record"+".mp3"(可自行修改)recordpath = Fpath + "/" + date + "record.mp3";audioFile = new File(recordpath);mediaRecorder = new MediaRecorder();mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//从麦克风采集mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);mediaRecorder.setAudioSamplingRate(44100);  //采样率mediaRecorder.setAudioChannels(1);          //单声道mediaRecorder.setAudioEncodingBitRate(128000);//比特率mediaRecorder.setOutputFile(audioFile.getAbsolutePath());mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//声音编码格式try {isrecording = true;mediaRecorder.prepare();mediaRecorder.start();} catch (Exception e) {e.printStackTrace();}}/*** 边录音边播放线程*/class RecordPlayThread extends Thread {public void run() {byte[] data = new byte[recBufSize];int num = 0;audioRecord.startRecording();audioTrack.play();while (isrecording) {num = audioRecord.read(data, 0, recBufSize);byte[] tmpBuf = new byte[num];System.arraycopy(data, 0, tmpBuf, 0, num);audioTrack.write(tmpBuf, 0, tmpBuf.length);}}}@Overridepublic void onDestroy() {isrecording = false;if (mediaPlayer != null) {mediaPlayer.stop();mediaPlayer.release();mediaPlayer = null;}if (mediaRecorder != null) {mediaRecorder.stop();mediaRecorder.release();mediaRecorder = null;}if (audioTrack!=null) {audioTrack.stop();audioTrack.release();audioTrack = null;}if (audioRecord!=null) {audioRecord.stop();audioRecord.release();audioRecord = null;}super.onDestroy();}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK ) { finish();return true;} else {return super.onKeyDown(keyCode, event);}}}

注意需要添加以下权限:

 <uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

在4.3以下运行以上代码,发现可以播放歌曲、录音、同步播放自己声音,体验很好(具体参数也可以调整)

可是在4.3(API18)以及以上的手机就会发现:不能播放自己的声音!(不会抛错,功放只播歌曲而没有自己从麦克风唱歌的声音);经调试可以发现,若在MediaRecorder使用时,要从AudioRecord获取语音流是获取不到的(读出来是0),也就是说,受API的限制,两者资源不能同时使用(可能是考虑到资源调用安全性问题,深层原因待进一步研究,水平有限望见谅)

简单说明:AudioRecord可以从麦克风记录短时长的语音流,之后AudioTrack可以从中读取出来并通过功放播放出来;若只用以上两个资源而不使用MediaPlayer和MediaRecorder,则能够实现一个麦克风加功放的简单效果(这时因为未使用MediaRecorder,在4.3以上也是可以正常实现的,因为4.3只限制MediaRecorder与AudioRecord的同时使用,只使用其中一个是没问题的)

欢迎建议、补充、指正!

demo下载地址 :http://download.csdn.net/detail/duguju/9082813

实现KTV效果:播放歌曲,录音(存储录音文件),并同步播放录音 解释4.3以上audio与media资源冲突问题相关推荐

  1. MIUI13和MIUI12的录音存储位置

    MIUI13和MIUI12的录音存储位置 1.电话录音存储位置 2.自带录音软件存储位置 1.电话录音存储位置 在桌面找到并打开[文件管理],点击[MIUI]文件夹 点击[sound_recorder ...

  2. php 音频顺序播放,MP3播放器音频文件不按顺序播放的解决方法,简单实用

    MP3播放器对于现在的我们来说很多已经是过去了,但是我们总会有用到它的时候:当你听一些录音课程或者评书故事,亦或者使用迷你音响播放歌曲时常会发现拷贝进MP3播放器中的音频文件根本不按照你自己的想象进行 ...

  3. html音乐唱片自动转,HTML5音乐播放器(四):播放列表与播放方式

    发现播放列表和播放方式切换两个功能是连在一起的,单独一个拿出来说不太合适,所以就都一块弄完了.废话不多说,进入主题,功能的逻辑我是这么设计的: 把整个歌曲文件的信息都写在 json 文件里,获取并生成 ...

  4. HTML5音乐播放器(四):播放列表与播放方式

    2019独角兽企业重金招聘Python工程师标准>>> 发现播放列表和播放方式切换两个功能是连在一起的,单独一个拿出来说不太合适,所以就都一块弄完了.废话不多说,进入主题,功能的逻辑 ...

  5. sync-player:使用websocket实现异地同步播放视频

    本文作者:星空无限 原文链接:https://liyangzone.com/2020/09/20/%E5%89%8D%E7%AB%AF/sync-player/ GoEasy已获作者授权转载,GoEa ...

  6. 如何同步播放asf和vga文件的教学视频

    (1)文件夹结构 教学视频很多是在一个网页窗口下显示三个子窗口: 一个是asf的(frmleftup.htm),Media Player播放,一般在左上,为主讲人: 一个是PPT目录(frmleftd ...

  7. c#winform演练 ktv项目 播放歌曲同时显示歌曲名称与歌手

    c#winform演练 ktv项目 播放歌曲同时显示歌曲名称与歌手 为什么需要歌曲对象 歌曲的路径是一个字符串 如何知道一个路径对应哪一首歌? 用对象最合适 设置对象的name为歌名 对象的src为路 ...

  8. 上传声音 微信小程序_微信小程序录音文件保存,播放

    最近做微信小程序需要把录音文件保存起来然后在另一个地方再播放,录音用的是wx.startRecord接口返回的是录音文件的临时路径,开始以为是和选择图片保存图片没什么两样的操作,结果和后端同事一起折腾 ...

  9. 什么录音软件可以录制电脑内部播放的声音

    什么录音软件可以录制电脑内部播放的声音 说起录音,我们应该都不陌生,多多少少都会使用到录音这个功能,不管是商业还是日常的工作学习中,录音已经成为人们生活中不可或缺的一项小功能,可能很多人使用录音这个功 ...

最新文章

  1. “中台不就是微服务吗?有啥区别?”
  2. swiper 定义放多少张图片_swiper轮播问题之二:默认显示3张图片,中间显示全部两边显示部分...
  3. 用浏览器测试Get与Post Webservice,Post一直报405错误,而Get能够成功的原因与解决方法
  4. 使用Electron制作一个快速搜索应用(入门向)
  5. UA MATH567 高维统计II 随机向量2 各向同性的随机向量
  6. 计算机监控检修维护规程,DL/T 1009-2016 水电厂计算机监控系统运行及维护规程
  7. 【转】ABP源码分析三十二:ABP.SignalR
  8. (三)slatstack配置管理
  9. OpenHarmony开源鸿蒙学习入门-应用开发之使用eTS语法示例项目讲解
  10. ARM版本ubuntu安装PL2303驱动
  11. python做图片美化_Python实现简单的照片磨皮(照片智能磨皮) 最新免费版
  12. Qt(c++)调用python一直报错slot、hypot等
  13. 【报告分享】2021小红书投放运营指南书-小红书(附下载)
  14. android 出生日期设置,android-选择出生日期对话框
  15. 肖秀荣教授:就考研政治来说
  16. 【MySQL】JDBC编程
  17. mysql instr locate_mysql函数之一:INSTR、LOCATE、POSITION VS LIKE
  18. 线下实体店结合VR全景,让虚拟购物更加真实
  19. 基于点云数据提取道路标线的思路
  20. HDU6305: RMQ Similar Sequence 题解

热门文章

  1. 海康、大华、星邦网络摄像头的 RTSP协议 地址与格式
  2. 技术分享| 基于 Etcd 的分布式锁实现原理及方案
  3. Loj 6053(EES筛法)
  4. # beta冲刺(7/7)
  5. java五子棋页面设计编程_Java程序设计:五子棋(一)——编写界面
  6. 【邀请函】谷歌云平台 (GCP) 入门培训:核心基础架构
  7. 水平导航栏+导航栏跟随+导航栏下划线滑动效果
  8. 为西门子 SIMATIC IOT2050 部署 OPCUA 服务器,并 POST 三菱CNC生产过程数据至 MES
  9. PMP 第1章 项目与项目经理
  10. 春色迷人 | 江南风景水彩画欣赏