您的位置:首页 > 移动开发 > Android开发

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

2016-05-24 15:37 477 查看
近期需要做音轨合成这样一个功能,何为音轨合成,说白了就是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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  音轨合成