![JAR search and dependency download from the Maven repository](/logo.png)
org.restcomm.connect.commons.util.WavFileUtil Maven / Gradle / Ivy
The newest version!
package org.restcomm.connect.commons.util;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.util.Arrays.copyOfRange;
import static javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED;
public class WavFileUtil {
private static Logger logger = LogManager.getLogger(WavFileUtil.class.getName());
private static final int HEAD_LENGTH = 12;
private static final int FORMAT_LENGTH = 24;
private static boolean isWav(byte[] head) {
return ("RIFF".equals(new String(head, 0, 4, ISO_8859_1)) &&
"WAVE".equals(new String(head, 8, 4, ISO_8859_1)));
}
private static void fileTooSmall(byte[] file) throws Exception {
if (file.length < HEAD_LENGTH + FORMAT_LENGTH) {
logger.warn("file is too small, size if {}." + file.length);
throw new Exception();
}
}
private static int headSize() {
return HEAD_LENGTH + FORMAT_LENGTH;
}
/**
* resolve wav file head.
* ChunkID,ChunkSize,Format, everyone 4 bytes.
*/
public static int fileSize(byte[] file) throws Exception {
fileTooSmall(file);
byte[] head = copyOfRange(file, 0, HEAD_LENGTH);
if (isWav(head)) {
return ByteBuffer.wrap(copyOfRange(head, 4, 8))
.order(LITTLE_ENDIAN)
.getInt() + 8;
} else {
logger.warn("file format error: expected {}, actual {}."+
"[82, 73, 70, 70, *, *, *, *, 87, 65, 86, 69]"+
head);
throw new Exception();
}
}
public static AudioFormat fileFormat(byte[] file) throws Exception {
fileTooSmall(file);
byte[] head = copyOfRange(file, 0, HEAD_LENGTH);
if (isWav(head)) {
byte[] format = copyOfRange(file, 12, HEAD_LENGTH + FORMAT_LENGTH);
String chuckID = new String(format, 0, 4, ISO_8859_1);
int chunkSize = ByteBuffer.wrap(copyOfRange(format, 4, 8))
.order(LITTLE_ENDIAN).getInt();
int audioFmt = ByteBuffer.wrap(copyOfRange(format, 8, 10))
.order(LITTLE_ENDIAN).getShort();
int channels = ByteBuffer.wrap(copyOfRange(format, 10, 12))
.order(LITTLE_ENDIAN).getShort();
int sampleRate = ByteBuffer.wrap(copyOfRange(format, 12, 16))
.order(LITTLE_ENDIAN).getInt();
int byteRate = ByteBuffer.wrap(copyOfRange(format, 16, 20))
.order(LITTLE_ENDIAN).getInt();
int frameSize = ByteBuffer.wrap(copyOfRange(format, 20, 22))
.order(LITTLE_ENDIAN).getShort();
int sampleSizeInBits = ByteBuffer.wrap(copyOfRange(format, 22, 24))
.order(LITTLE_ENDIAN).getShort();
return new AudioFormat(PCM_SIGNED, sampleRate,
sampleSizeInBits, channels, frameSize, sampleRate, false);
} else {
logger.warn("file is not a wav.");
throw new Exception();
}
}
public static void merge(final byte[] left, final byte[] right, final String path) throws Exception {
int leftSize = fileSize(left);
int rightSize = fileSize(right);
int mergeSize = mergeSizeField(leftSize, rightSize);
int mergeDataSize = mergeDataSize(leftSize, rightSize);
try (RandomAccessFile file = new RandomAccessFile(path, "rw")) {
file.write(mergeHead(left, mergeSize));
file.write(dataChunkHead(mergeDataSize));
int max = Math.max(leftSize, rightSize);
for (int i = headSize() + 8; i < max + 8; i += 2) {
file.write(read(left, i));
file.write(read(right, i));
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static byte[] read(final byte[] content, int offset) {
if (content.length > offset) {
return copyOfRange(content, offset, offset + 2);
} else {
return "\0\0".getBytes(ISO_8859_1);
}
}
private static int mergeSizeField(int left, int right) {
int max = Math.max(left - 8, right - 8);
return max * 2;
}
private static int mergeDataSize(int left, int right) {
int max = Math.max(left - headSize() - 8, right - headSize() - 8);
return max * 2;
}
private static byte[] mergeHead(final byte[] left, final int mergeSize) throws Exception {
AudioFormat format = fileFormat(left);
ByteBuffer size = ByteBuffer.allocate(4).order(LITTLE_ENDIAN).putInt(mergeSize);
ByteBuffer channels = ByteBuffer.allocate(2).order(LITTLE_ENDIAN).putShort((short) 2);
ByteBuffer sampleRate = ByteBuffer.allocate(4).order(LITTLE_ENDIAN)
.putInt((int) format.getSampleRate());
ByteBuffer byteRate = ByteBuffer.allocate(4).order(LITTLE_ENDIAN)
.putInt((int) format.getSampleRate() * 2 * format.getSampleSizeInBits() / 8);
ByteBuffer blockAlign = ByteBuffer.allocate(2).order(LITTLE_ENDIAN)
.putShort((short) (2 * format.getSampleSizeInBits() / 8));
ByteBuffer bitsPerSample = ByteBuffer.allocate(2).order(LITTLE_ENDIAN)
.putShort((short) format.getSampleSizeInBits());
ByteBuffer head = ByteBuffer.allocate(headSize());
head.put(left, 0, 4);
head.put(size.array());
head.put(left, 8, 14);
head.put(channels.array());
head.put(sampleRate.array());
head.put(byteRate.array());
head.put(blockAlign.array());
head.put(bitsPerSample.array());
return head.array();
}
private static byte[] dataChunkHead(final int length) {
ByteBuffer head = ByteBuffer.allocate(8);
head.put("data".getBytes(ISO_8859_1));
ByteBuffer size = ByteBuffer.allocate(4).order(LITTLE_ENDIAN).putInt(length);
head.put(size.array());
return head.array();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy