说明
-
读取文件头:
- 使用
readHeader
方法读取前 44 字节的文件头。(不同格式文件头不同)
- 使用
-
读取数据部分:
- 使用
readData
方法逐段读取数据部分,并将其写入ByteArrayOutputStream
。
- 使用
-
检查文件头一致性:
- 使用
areHeadersEqual
方法检查所有 WAV 文件的格式是否一致。
- 使用
-
更新文件头:
- 使用
updateWavHeader
方法更新文件头中的数据长度信息。 - 使用
intToByteArray
方法将整数转换为字节数组,以便更新文件头。
- 使用
-
写入合并后的文件:
- 将所有 WAV 文件的内容写入到输出文件中。
注意事项
-
文件头一致性:
- 确保所有 WAV 文件的格式完全一致,包括采样率、通道数、位深度等。
-
数据长度更新:
- 在合并完成后,更新文件头中的数据长度信息,以确保新的 WAV 文件是有效的。
-
输入流读取:
- 使用逐段读取的方式确保完整地读取整个文件内容。
代码
import java.io.*;
import java.util.Arrays;
import java.util.List;
public class WavMerger {
public static void main(String[] args) throws FileNotFoundException {
List<InputStream> inputStreams = Arrays.asList(
new BufferedInputStream(new FileInputStream("1.wav")),
new BufferedInputStream(new FileInputStream("2.wav"))
);
File outputFile = new File("merged.wav");
try {
mergeWavFiles(inputStreams, outputFile);
System.out.println("WAV 文件合并成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void mergeWavFiles(List<InputStream> inputStreams, File outputFile) throws IOException {
try (RandomAccessFile output = new RandomAccessFile(outputFile, "rw")) {
// 创建一个缓冲区来存储整个文件的内容
ByteArrayOutputStream tempBuffer = new ByteArrayOutputStream();
// 读取第一个 WAV 文件的全部内容
InputStream firstInput = inputStreams.get(0);
byte[] header1 = readHeader(firstInput);
byte[] data1 = readData(firstInput);
// 检查其他 WAV 文件的格式是否与第一个一致
byte[] header = header1;
int totalDataLength = data1.length;
for (int i = 1; i < inputStreams.size(); i++) {
InputStream input = inputStreams.get(i);
byte[] currentHeader = readHeader(input);
byte[] currentData = readData(input);
if (!areHeadersEqual(header, currentHeader)) {
throw new IOException("WAV 文件的格式不一致");
}
// 更新总数据长度
totalDataLength += currentData.length;
}
// 更新文件头中的数据长度
updateWavHeader(header, totalDataLength);
// 写入文件头
tempBuffer.write(header);
// 写入所有文件的数据部分
for (InputStream input : inputStreams) {
byte[] data = readData(input);
tempBuffer.write(data);
}
// 将临时缓冲区的内容写入输出文件
byte[] mergedContent = tempBuffer.toByteArray();
output.write(mergedContent);
}
}
private static byte[] readHeader(InputStream in) throws IOException {
byte[] header = new byte[44];
int bytesRead = in.read(header);
if (bytesRead != header.length) {
throw new IOException("无法读取完整的 WAV 文件头");
}
// 重置输入流到文件头之后的第一个字节
((BufferedInputStream) in).mark(Integer.MAX_VALUE); // 标记当前位置
return header;
}
private static byte[] readData(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
// 重置输入流到文件头之后的第一个字节
((BufferedInputStream) in).reset(); // 重置到标记的位置
return baos.toByteArray();
}
private static boolean areHeadersEqual(byte[] header1, byte[] header2) {
// 检查文件头的主要部分(比如采样率、位深度等)是否一致
return Arrays.equals(Arrays.copyOfRange(header1, 0, 4), Arrays.copyOfRange(header2, 0, 4)) &&
Arrays.equals(Arrays.copyOfRange(header1, 8, 12), Arrays.copyOfRange(header2, 8, 12)) &&
Arrays.equals(Arrays.copyOfRange(header1, 20, 22), Arrays.copyOfRange(header2, 20, 22)) &&
Arrays.equals(Arrays.copyOfRange(header1, 22, 24), Arrays.copyOfRange(header2, 22, 24)) &&
Arrays.equals(Arrays.copyOfRange(header1, 24, 28), Arrays.copyOfRange(header2, 24, 28)) &&
Arrays.equals(Arrays.copyOfRange(header1, 34, 36), Arrays.copyOfRange(header2, 34, 36));
}
private static void updateWavHeader(byte[] header, int totalDataLength) {
// 更新 RIFF 区块的大小(文件长度 - 8)
int riffChunkSizeOffset = 4;
int riffChunkSize = 44 + totalDataLength - 8;
byte[] riffChunkSizeBytes = intToByteArray(riffChunkSize);
System.arraycopy(riffChunkSizeBytes, 0, header, riffChunkSizeOffset, 4);
// 更新 数据区块的大小
int dataChunkSizeOffset = 40;
byte[] dataChunkSizeBytes = intToByteArray(totalDataLength);
System.arraycopy(dataChunkSizeBytes, 0, header, dataChunkSizeOffset, 4);
}
private static byte[] intToByteArray(int value) {
return new byte[] {
(byte) (value & 0xFF),
(byte) ((value >> 8) & 0xFF),
(byte) ((value >> 16) & 0xFF),
(byte) ((value >> 24) & 0xFF)
};
}
}