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

来源:互联网 发布:阿里云客服待遇怎么算 编辑:程序博客网 时间:2024/05/17 06:56

很多唱歌类型的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


1 0
原创粉丝点击