【多个pcm或wav文件混音】

多个pcm或wav文件混音

设计思路

根据文件的记录音频的开始时间,可以将多个音频文件混合成一个

限制

只针对单通道位深为16位的wav或pcm文件。其它位深可自行修改

实现代码 java


package cn.pt2000.smart.talk.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;

public class WavUtils {

	/**
	 * 混音多个文件
	 * 
	 * @param upFilePath 设备端上行音频文件
	 * @param downFiles  设备端下行音频文件
	 * @throws Exception
	 */
	public static void mixWav(String upFilePath, File[] downFiles) throws Exception {
		if (downFiles.length == 0) {
			return;
		}
		if (downFiles.length == 1) {
			mixWav(upFilePath, downFiles[0], false);
			return;
		}
		orderFileByFileName(downFiles);
		for (int i = 0; i < downFiles.length; i++) {
			if (i == 0) {
				mixWav(upFilePath, downFiles[0], false);
			} else {
				mixWav(upFilePath, downFiles[0], true);
			}
		}
	}

	/**
	 * 合并上下行音频
	 * 
	 * @param upFilePath
	 * @param downFile
	 * @param 是否需要上行数据的前缀
	 * @throws Exception
	 */
	public static void mixWav(String upFilePath, File downFile, boolean skipHead) throws Exception {
		File upFile = new File(upFilePath);
		long upTime = getFileCreateTimeByFileName(upFile.getName());
		long downTime = getFileCreateTimeByFileName(downFile.getName());
		if (upTime > downTime) {
			long cat = upTime - downTime;
			long catSize = cat * 32;
			mixWav(downFile, upFile, catSize, skipHead);
		} else {
			long cat = downTime - upTime;
			long catSize = cat * 32;
			mixWav(upFile, downFile, catSize, skipHead);
		}
	}

	/**
	 * 合并音频文件
	 * 
	 * @param headFile
	 * @param tailFile
	 * @param headerSize 领先的字节数
	 * @param skipHead
	 */
	public static void mixWav(File headFile, File tailFile, long headerSize, boolean skipHead) throws Exception {
		long headLength = headFile.length();
		long tailLength = tailFile.length();
		if (headLength < headerSize) {
			return;
		}
		// 定义输出文件
		String outFilePath = tailFile.getAbsolutePath().replace("up", "mix").replace("down", "mix");
		File outPutFile = new File(outFilePath);
		outPutFile.createNewFile();
		// 创建输出流
		OutputStream os = new FileOutputStream(outPutFile);
		os.write(new byte[44]); // 写入44字节,占位
		// 创建输入流
		InputStream headIs = new FileInputStream(headFile);
		InputStream tailIs = new FileInputStream(tailFile);
		headIs.read(new byte[44]);
		tailIs.read(new byte[44]);
		// 长度都减去44
		headLength -= 44;
		tailLength -= 44;
		if (!skipHead) {
			headIs.readNBytes((int) headerSize);
		}
		headLength = headLength - headerSize;
		long maxLen = headLength > tailLength ? headLength : tailLength;

		for (int i = 0; i < maxLen; i += 2) {
			byte[] data1 = null;
			if (i < headLength) {
				data1 = headIs.readNBytes(2);
			}
			byte[] data2 = null;
			if (i < tailLength) {
				data2 = tailIs.readNBytes(2);
			}
			byte[] mixData = mixData(data1, data2);
			os.write(mixData);
		}
		headIs.close();
		tailIs.close();
		os.close();
		// 修正wav文件参数
		reviseWavFile(outFilePath);
	}

	/**
	 * 修正wav文件的参数
	 * 
	 * @param filePath
	 * @throws Exception
	 */
	public static void reviseWavFile(String filePath) throws Exception {
		// 修改文件头,生成wav文件
		RandomAccessFile file = new RandomAccessFile(filePath, "rw");
		file.seek(0L);
		file.writeBytes("RIFF");
		file.write(LittleEndianUtils.writeUnsignedInt32(file.length()));
		file.writeBytes("WAVE");
		file.writeBytes("fmt ");
		file.write(LittleEndianUtils.writeUnsignedInt32(16));
		file.write(LittleEndianUtils.writeUnsignedInt16(1));
		file.write(LittleEndianUtils.writeUnsignedInt16(1));
		file.write(LittleEndianUtils.writeUnsignedInt32(16000));
		file.write(LittleEndianUtils.writeUnsignedInt32(32000));
		file.write(LittleEndianUtils.writeUnsignedInt16(2));
		file.write(LittleEndianUtils.writeUnsignedInt16(16));
		file.writeBytes("data");
		file.write(LittleEndianUtils.writeUnsignedInt32(file.length() - 44));
		file.close();
	}

	/**
	 * 根据文件名获取文件的创建时间
	 * 
	 * @param fileName
	 * @return
	 */
	private static long getFileCreateTimeByFileName(String fileName) {
		int index = fileName.indexOf("_");
		String timeStr = fileName.substring(0, index);
		return Long.parseLong(timeStr);
	}

	/**
	 * 将两个wav音频合并
	 * 
	 * @param src1
	 * @param src2
	 * @param dest
	 * @throws Exception
	 */
	public static void mixWav(String src1, String src2, String dest) throws Exception {
		File file1 = new File(src1);
		File file2 = new File(src2);
		long file1Len = file1.length() / 2 - 22;
		long file2Len = file2.length() / 2 - 22;
		long maxLen = file1Len > file2Len ? file1Len : file2Len;
		InputStream is1 = new FileInputStream(src1);
		InputStream is2 = new FileInputStream(src2);

		byte[] head1 = is1.readNBytes(44);
		byte[] head2 = is2.readNBytes(44);

		FileOutputStream os = new FileOutputStream(dest);
		if (file1Len > file2Len) {
			os.write(head1);
		} else {
			os.write(head2);
		}

		for (int i = 0; i < maxLen; i++) {
			byte[] data1 = null;
			if (i < file1Len) {
				data1 = is1.readNBytes(2);
			}
			byte[] data2 = null;
			if (i < file2Len) {
				data2 = is2.readNBytes(2);
			}
			byte[] mixData = mixData(data1, data2);
			os.write(mixData);
		}

		is1.close();
		is2.close();
		os.close();
	}

	/**
	 * 混淆数据
	 * 
	 * @param data
	 * @param data2
	 * @return
	 */
	public static byte[] mixData(byte[] d1, byte[] d2) {
		if (null == d1) {
			d1 = new byte[] { 0, 0 };
		}
		if (null == d2) {
			d2 = new byte[] { 0, 0 };
		}
		int data1 = LittleEndianUtils.readInt16(d1);
		int data2 = LittleEndianUtils.readInt16(d2);
		int temp1 = Math.abs(data1);
		int temp2 = Math.abs(data2);
		int all = temp1 + temp2;
		double result = data1 * (temp1 * 2.0 / all) + data2 * (temp2 * 1.0 / all);
		short tempResult = (short) Math.ceil(result);
		// 防止溢出
		if (tempResult > 32760) {
			tempResult = 32760;
		}
		if (tempResult < -32760) {
			tempResult = -32760;
		}
		return LittleEndianUtils.writeInt16(tempResult);
	}

	/**
	 * 按照文件创建时间先后排序
	 * 
	 * @param files
	 */
	private static void orderFileByFileName(File[] files) {
		for (int i = 0; i < files.length - 1; i++) {// 外层循环控制排序趟数
			for (int j = 0; j < files.length - 1 - i; j++) {// 内层循环控制每一趟排序多少次
				long ct1 = getFileCreateTimeByFileName(files[j].getName());
				long ct2 = getFileCreateTimeByFileName(files[j + 1].getName());
				if (ct1 > ct2) {
					File temp = files[j];
					files[j] = files[j + 1];
					files[j + 1] = temp;
				}
			}
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值