Android多媒体(一) 音轨合成 我用双手成就你的梦想

近期需要做音轨合成这样一个功能,何为音轨合成,说白了就是N个音频文件合成一个,同时播放N个声音。然而网上各种找代码,并没有一个能用的,最后终于找到一个外国大神写的合音工具类,稍加修改便成了自己的东西,现在分享出来,方便大家使用。


模拟器没办法录音,所以这里先只提供一张效果图


我这里做的效果是点击开始录音开始录制声音,停止后将该录音文件存储在sd卡,并在中间的ListViw显示名字,点击item选中多条后,即可开始合音

先来说一下步骤,

1.录音并将录音文件存储在sd卡下

2.解码

3.合音

从大的方向来讲,要实现这里的效果只需以上三步


我这里使用的MediaRecorder录音,首先,点击按钮后开始录音:

MediaRecorder mRecorder = new MediaRecorder();
mRecorder.reset();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置声音来源
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 设置所录制的音视频文件的格式
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);// 设置所录制的声音的编码格式
mRecorder.setAudioEncodingBitRate(96000);// 比特率
mRecorder.setAudioChannels(2);// 通道
mRecorder.setAudioSamplingRate(44100);// 采样率
mRecorder.setOutputFile(recordFile.getAbsolutePath() + "/record"
		+ count + ".mp4");// 设置录制的音频文件的保存位置
try {
	mRecorder.prepare();
} catch (IOException e) {
	e.printStackTrace();
}
mRecorder.start();


这里需要注意的是:

1.要合音的文件格式必须相同,比如必须都是MP4或都是MP3,否则合出来的音会像卡带了一样

2.采样率、通道、精度必须相同,有一个参数不同都会影响到合出来的效果

3.做的时候还遇到一个问题,设置采样率、通道、比特率的代码必须放在setOutputFormat后面,如果放在setOutputFormat前面,合出来的音也是有问题的。至于这个为什么, 我暂时也没有找到答案,当时因为这个问题困扰了很久,无意间放到setOutputFormat后面才意外成功


录音结束后,将文件名字添加到List中,来充当ListView的数据

mRecorder.stop();
mRecorder = null;
RecordBean bean = new RecordBean();
bean.setId(count);
bean.setName(count + ".mp4");
bean.setState(0);
bean.setPath(recordFile.getAbsolutePath() + "/record" + count + ".mp4");
list.add(bean);
adapter.setList(list);

接下来,就是选择录音的文件并进行解码,将解码后的文件存在sd卡,合音时,将解码后的文件进行合音。

解码:

/**
	 * 解码
	 * 
	 */
	class DecodeTask extends AsyncTask<Void, Double, Boolean> {

		private String fileUrl;
		private int position;

		DecodeTask(String url, int p) {
			fileUrl = url;
			position = p;
		}

		@Override
		protected Boolean doInBackground(Void... params) {
			try {
				// 解码后的路径
				String decodeFilePath = decodeFile.getAbsolutePath()
						+ "/decode" + MD5Util.getMD5Str(fileUrl);
				// 将解码后的路径保存在list中,方便后面取值
				list.get(position).setDecodePath(decodeFilePath);
				// 解码
				AudioDecoder audioDec = AudioDecoder
						.createDefualtDecoder(fileUrl);
				fileUrl = decodeFilePath;
				audioDec.decodeToFile(decodeFilePath);
				return true;
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}
		}

		@Override
		protected void onPostExecute(Boolean result) {
			super.onPostExecute(result);
			dialog.cancel();
		}
	}


合音:

/**
	 * 合音
	 * 
	 */
	class MixTask extends AsyncTask<Void, Double, Boolean> {

		private int size;

		MixTask(int num) {
			size = num;
		}

		@Override
		protected Boolean doInBackground(Void... params) {
			String rawAudioFile = null;

			// 将需要合音的音频解码后的文件放到数组里
			File[] rawAudioFiles = new File[size];
			StringBuilder sbMix = new StringBuilder();
			int index = 0;

			for (int i = 0; i < list.size(); i++) {
				if (1 == list.get(i).getState()) {
					rawAudioFiles[index++] = new File(list.get(i)
							.getDecodePath());
					sbMix.append(i + "");
				}
			}

			// 最终合音的路径
			final String mixFilePath = mixFile.getAbsolutePath() + "/mix"
					+ MD5Util.getMD5Str(sbMix.toString());

			// 下面的都是合音的代码
			try {
				MultiAudioMixer audioMixer = MultiAudioMixer.createAudioMixer();

				audioMixer.setOnAudioMixListener(new OnAudioMixListener() {

					FileOutputStream fosRawMixAudio = new FileOutputStream(
							mixFilePath);

					@Override
					public void onMixing(byte[] mixBytes) throws IOException {
						fosRawMixAudio.write(mixBytes);
					}

					@Override
					public void onMixError(int errorCode) {
						try {
							if (fosRawMixAudio != null)
								fosRawMixAudio.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}

					@Override
					public void onMixComplete() {
						try {
							if (fosRawMixAudio != null)
								fosRawMixAudio.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}

				});
				audioMixer.mixAudios(rawAudioFiles);
				rawAudioFile = mixFilePath;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			AudioEncoder accEncoder = AudioEncoder
					.createAccEncoder(rawAudioFile);
			String finalMixPath = mixFile.getAbsolutePath() + "/finalMix.aac";
			accEncoder.encodeToFile(finalMixPath);
			return true;
		}

		@Override
		protected void onPostExecute(Boolean result) {
			super.onPostExecute(result);
			Toast.makeText(getApplicationContext(), "合音成功", Toast.LENGTH_SHORT)
					.show();
			dialog.cancel();
		}
	}


最后合成的声音,可以在sdcard/merge/mix/中找到,叫finalMix.aac,可以点击播放试一下

Demo中只是实现该操作,有些东西没有加,如判断sd卡、判断权限是否关闭等,这些实际操作中切勿忘记自行加上



由于所需要的类还是比较多的,这里不能贴上全部代码,有需要的可自行下载Demo:http://download.csdn.net/detail/qq_18612815/9529622

Demo中重要的地方我都加了注释,基本都能看得懂

本篇讲的是音轨合成,接下来的几篇会增加音轨拼接、音轨与视频合成,有需要的加个关注,方便get


刚刚发现了一个bug,Demo中  RecordWithRecordActivity  199行应改为 new MixTask(num).execute();,之前写demo时候测试把num写成2固定死了,所以如果选择超过2个文件就会崩...


转载请注明来自http://blog.csdn.net/qq_18612815


  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值