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

很多唱歌类型的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;
	
	@Override
	protected 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);
  			}

  		}
  	}
	
	@Override
	public 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();
	}
	
	@Override
	public 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


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值