wave文件格式解析
-
wave的头文件详细
-
注意事项
- 在第13个字节开始,不一定是fmt(0x666d7420)需要定位fmt的位置
- 在fmt结束后有可能有一段文件描述,需要定位data位置
-
wave格式实体类
package io.renren.modules.scl.util;
import lombok.Data;
/**
* WAVE文件格式
*
* @author 大兵
* @date 2018-08-03 11:27
**/
@Data
public class WaveFormat {
/**
* data标识结束位置
*/
private Long dataFlag;
/**
* fmt标识结束位置
*/
private Long fmtFlag;
/**
* 数据类型是否为浮点数
*/
private boolean floatFlag;
//需要读取的参数
/**
* 采样率
*/
private long samplingRate;
/**
* 通道数
*/
private int channelNumber;
/**
* 采样位数
*/
private int bitsPer;
/**
* 每秒数据量
*/
private long byteRate;
/**
* 块大小
*/
private int blockAlign;
/**
* 头文件长度
*/
private long headerLength;
/**
* 帧长
*/
private long frameLength;
/**
* 原始信号大小
*/
private long fileSize;
/**
* 时长(精确到毫秒数)
*/
private long duration;
/**
* 数据大小
*/
private long dataSize;
}
package io.renren.modules.scl.util;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Objects;
/**
* Wave工具类(重构)
*
* @author 大兵
* @date 2018-08-03 11:38
**/
public class WaveUtil {
/**
* fmt标识
*/
private static final long FMT_MAGIC = 0x666D7420;
/**
* data标识
*/
private static final long DATA_MAGIC = 0x64617461;
/**
* riff标识
*/
private static final long RIFF_MAGIC = 0x52494646;
/**
* wav类型
*/
private static final long WAVE_MAGIC = 0x57415645;
/**
* 读取wave头文件
* 不关闭流
*/
public static WaveFormat readFormat(RandomAccessFile raf) throws IOException {
WaveFormat waveFormat = new WaveFormat();
//判断是否是wave文件
if (isWave(raf)) {
throw new RuntimeException("这不是个wave文件");
}
//查找fmt标识位置
waveFormat.setFmtFlag(findFmtFlag(raf));
if (waveFormat.getFmtFlag() == null) {
throw new RuntimeException("未找到fmt标识符");
}
//查找data标识位置
waveFormat.setDataFlag(findDataFlag(raf));
if (waveFormat.getDataFlag() == null) {
throw new RuntimeException("未找到data标识符");
}
raf.seek(waveFormat.getFmtFlag() + 4);
waveFormat.setFloatFlag(Unsigned.shortToLittle(raf.readShort()) == 3);
waveFormat.setChannelNumber(Unsigned.shortToLittle(raf.readShort()));
waveFormat.setSamplingRate(Unsigned.intToLittle(raf.readInt()));
waveFormat.setByteRate(Unsigned.intToLittle(raf.readInt()));
waveFormat.setBlockAlign(Unsigned.shortToLittle(raf.readShort()));
waveFormat.setBitsPer(Unsigned.shortToLittle(raf.readShort()));
waveFormat.setFileSize(raf.length());
waveFormat.setHeaderLength(waveFormat.getDataFlag() + 4);
waveFormat.setDataSize(raf.length() - waveFormat.getHeaderLength());
waveFormat.setFrameLength(waveFormat.getDataSize() / waveFormat.getBlockAlign());
waveFormat.setDuration((long) ((float) waveFormat.getFrameLength() * 1000 / (float) waveFormat.getSamplingRate()));
return waveFormat;
}
/**
* wave格式化为64位Double浮点数
*
* @param formFile 源文件
* @param toFile 转换后文件
*/
public static void waveTo64BitDouble(File formFile, File toFile) throws IOException {
RandomAccessFile fraf = new RandomAccessFile(formFile, "r");
FileChannel ffc = fraf.getChannel();
RandomAccessFile traf = new RandomAccessFile(toFile, "rw");
FileChannel tfc = traf.getChannel();
WaveFormat waveFormat = readFormat(fraf);
tfc.write(ByteBuffer.allocate(44));
boolean floatFlag = waveFormat.isFloatFlag();
int bitsper = waveFormat.getBitsPer();
long headerLength = waveFormat.getHeaderLength();
//8位
if (bitsper == 8) {
wave8To64(ffc, tfc, headerLength);
waveFormat.setBlockAlign(waveFormat.getBlockAlign() * 8);
waveFormat.setByteRate(waveFormat.getByteRate() * 8);
}
//16位
if (bitsper == 16) {
wave16To64(ffc, tfc, headerLength);
waveFormat.setBlockAlign(waveFormat.getBlockAlign() * 4);
waveFormat.setByteRate(waveFormat.getByteRate() * 4);
}
//24位整数
if (bitsper == 24) {
wave24To64(ffc, tfc, headerLength);
waveFormat.setBlockAlign(waveFormat.getBlockAlign() / 3 * 8);
waveFormat.setByteRate(waveFormat.getByteRate() / 3 * 8);
}
//32位浮点数
if (bitsper == 32 && floatFlag) {
wav32FloatTo64(ffc, tfc, headerLength);
waveFormat.setBlockAlign(waveFormat.getBlockAlign() * 2);
waveFormat.setByteRate(waveFormat.getByteRate() * 2);
}
//32位整数
if (bitsper == 32 && !floatFlag) {
wav32to64(ffc, tfc, headerLength);
waveFormat.setBlockAlign(waveFormat.getBlockAlign() * 2);
waveFormat.setByteRate(waveFormat.getByteRate() * 2);
}
//64位整数
if (bitsper == 64 && !floatFlag) {
wav64to64(ffc, tfc, headerLength);
}
//64位浮点数
if (bitsper == 64 && floatFlag) {
wav64DoubleTo64(ffc, tfc, headerLength);
}
waveFormat.setBitsPer(64);
waveFormat.setFloatFlag(true);
waveFormat.setFileSize(tfc.size() - 8);
waveFormat.setDataSize(tfc.size() - 44);
writeHeader(waveFormat, traf);
tfc.close();
traf.close();
ffc.close();
fraf.close();
}
/**
* 8位wave文件转换为64位
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
* @throws IOException io异常
*/
public static void wave8To64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
byte[] bytes = new byte[4096];
double[] doubles = new double[4096];
int read;
while ((read = ffc.read(fbb)) != -1) {
fbb.position(0);
fbb.get(bytes).clear();
for (int i = 0; i < bytes.length; i++) {
doubles[i] = ((bytes[i] & 0xff) - 128) / 128f;
}
tbb.position(0);
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//最后一次
if (read != fbb.capacity()) {
double[] dt = new double[read];
System.arraycopy(doubles, 0, dt, 0, read);
ByteBuffer byteBuffer = ByteBuffer.allocate(read * 4);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
} else {
//写入文件
tfc.write(tbb);
}
}
}
/**
* 16位转64位浮点
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wave16To64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096 * 2);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
short[] shorts = new short[4096];
double[] doubles = new double[4096];
double temp = Short.MAX_VALUE;
int read;
while ((read = ffc.read(fbb)) != -1) {
//读取short
fbb.position(0);
fbb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
//转成double
for (int i = 0; i < shorts.length; i++) {
doubles[i] = shorts[i] / temp;
}
tbb.position(0);
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//最后一次
if (read != fbb.capacity()) {
double[] dt = new double[read / 2];
System.arraycopy(doubles, 0, dt, 0, read / 2);
ByteBuffer byteBuffer = ByteBuffer.allocate(read * 4);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
} else {
//写入文件
tfc.write(tbb);
}
}
}
/**
* 24位整数转换为64位double
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wave24To64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096 * 3);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
byte[] bytes = new byte[4096 * 3];
double[] doubles = new double[4096];
int read;
while ((read = ffc.read(fbb)) != -1) {
fbb.position(0);
fbb.get(bytes);
fbb.clear();
byteToDouble(bytes, doubles);
if (read == fbb.capacity()) {
tbb.rewind();
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//写入
tfc.write(tbb);
} else {
//最后一次
double[] dt = new double[read / 3];
ByteBuffer byteBuffer = ByteBuffer.allocate(read * 8 / 3);
System.arraycopy(doubles, 0, dt, 0, read / 4);
byteBuffer.position(0);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
}
}
}
/**
* 32浮点转换成64位浮点
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wav32FloatTo64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096 * 4);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
float[] floats = new float[4096];
double[] doubles = new double[4096];
int read;
while ((read = ffc.read(fbb)) != -1) {
//读取float
fbb.position(0);
fbb.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().get(floats);
//转换成double
for (int i = 0; i < floats.length; i++) {
doubles[i] = floats[i];
}
if (read == fbb.capacity()) {
tbb.position(0);
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//写入
tfc.write(tbb);
} else {
//最后一次
double[] dt = new double[read / 4];
ByteBuffer byteBuffer = ByteBuffer.allocate(read * 2);
System.arraycopy(doubles, 0, dt, 0, read / 4);
byteBuffer.position(0);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
}
}
}
/**
* 32整数转换成64位浮点
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wav32to64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096 * 4);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
int[] ints = new int[4096];
double[] doubles = new double[4096];
double intMax = Integer.MAX_VALUE;
int read;
while ((read = ffc.read(fbb)) != -1) {
fbb.rewind();
fbb.order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get(ints);
for (int i = 0; i < ints.length; i++) {
doubles[i] = ints[i] / intMax;
}
if (read == fbb.capacity()) {
tbb.rewind();
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//写入
tfc.write(tbb);
} else {
double[] dt = new double[read / 4];
ByteBuffer byteBuffer = ByteBuffer.allocate(read * 2);
System.arraycopy(doubles, 0, dt, 0, read / 4);
byteBuffer.rewind();
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
}
}
}
/**
* 64整数转换成64位浮点
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wav64to64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
ByteBuffer fbb = ByteBuffer.allocate(4096 * 8);
ByteBuffer tbb = ByteBuffer.allocate(4096 * 8);
long[] longs = new long[4096];
double[] doubles = new double[4096];
double longMax = Long.MAX_VALUE;
int read;
while ((read = ffc.read(fbb)) != -1) {
fbb.rewind();
fbb.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer().get(longs);
for (int i = 0; i < longs.length; i++) {
doubles[i] = longs[i] / longMax;
}
if (read == fbb.capacity()) {
tbb.rewind();
tbb.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(doubles);
//写入
tfc.write(tbb);
} else {
double[] dt = new double[read / 8];
System.arraycopy(doubles, 0, dt, 0, dt.length);
ByteBuffer byteBuffer = ByteBuffer.allocate(read);
byteBuffer.rewind();
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer().put(dt);
tfc.write(byteBuffer);
}
}
}
/**
* 64位浮点转换成64位浮点
*
* @param ffc 入通道
* @param tfc 出通道
* @param headerLength 头文件长度
*/
private static void wav64DoubleTo64(FileChannel ffc, FileChannel tfc, long headerLength) throws IOException {
ffc.position(headerLength);
tfc.transferFrom(ffc, 44, ffc.size() - headerLength);
}
/**
* 对已经预留了44位的wave文件进行充填头文件
*
* @param waveFormat wave格式对象
* @param raf 随机读写对象
*/
public static void writeHeader(WaveFormat waveFormat, RandomAccessFile raf) throws IOException {
raf.seek(0);
raf.writeInt((int) RIFF_MAGIC);
//如果长度大于4g
if (waveFormat.getFileSize() / 2 < Integer.MAX_VALUE) {
writeLittleInteger((int) waveFormat.getFileSize(), raf);
} else {
writeLittleInteger(-1, raf);
}
raf.writeInt((int) WAVE_MAGIC);
raf.writeInt((int) FMT_MAGIC);
raf.writeInt(0x10000000);
if (waveFormat.isFloatFlag()) {
writeLittleShort((short) 3, raf);
} else {
writeLittleShort((short) 1, raf);
}
writeLittleShort((short) waveFormat.getChannelNumber(), raf);
writeLittleInteger((int) waveFormat.getSamplingRate(), raf);
writeLittleInteger((int) waveFormat.getByteRate(), raf);
writeLittleShort((short) waveFormat.getBlockAlign(), raf);
writeLittleShort((short) waveFormat.getBitsPer(), raf);
raf.writeInt((int) DATA_MAGIC);
//如果长度大于4g
if (waveFormat.getDataSize() / 2 < Integer.MAX_VALUE) {
writeLittleInteger((int) waveFormat.getDataSize(), raf);
} else {
writeLittleInteger(-1, raf);
}
}
/**
* 把short以小端方式写入文件
*/
private static void writeLittleShort(short s, RandomAccessFile raf) throws IOException {
raf.write(s);
raf.write(s >>> 8);
}
/**
* 把int以小端方式写入文件
*/
private static void writeLittleInteger(int i, RandomAccessFile raf) throws IOException {
raf.write(i);
raf.write(i >>> 8);
raf.write(i >>> 16);
raf.write(i >>> 24);
}
/**
* 判断是否是wave文件
*/
public static boolean isWave(RandomAccessFile raf) throws IOException {
//如果wave长度小于44
if (raf.length() < 44) {
return false;
}
raf.seek(8);
int data = raf.readInt();
//如果第8-12为不是wave标识则这不是wave文件
return data == DATA_MAGIC;
}
/**
* 查找fmt标识结束位置
*
* @param raf 随机读写类
* @return fmt标识结束位置,如果为null则没有找到
*/
private static Long findFmtFlag(RandomAccessFile raf) throws IOException {
Long fmtFlag = null;
long pos = 12;
long length = raf.length();
while (Objects.isNull(fmtFlag)) {
raf.seek(pos);
if (FMT_MAGIC == raf.readInt()) {
fmtFlag = pos + 4;
}
pos += 2;
if (pos >= length) {
return null;
}
}
return fmtFlag;
}
/**
* 查找data标识结束位置
*
* @param raf 随机读写类
* @return data标识结束位置,如果为null则没有找到
*/
private static Long findDataFlag(RandomAccessFile raf) throws IOException {
Long dataFlag = null;
long temp = 20;
while (Objects.isNull(dataFlag)) {
raf.seek(temp);
if (DATA_MAGIC == raf.readInt()) {
dataFlag = temp + 4;
}
temp += 2;
if (temp >= raf.length()) {
return null;
}
}
return dataFlag;
}
/**
* 根据字节读取文件
*
* @param rdf 随机文件流
* @param pos 开始的位置
* @param length 读取长度
* @return 字节数组
*/
public static byte[] readByte(RandomAccessFile rdf, long pos, int length) throws IOException {
rdf.seek(pos);
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
result[i] = rdf.readByte();
}
return result;
}
/**
* 24位wave转64位专用
* 字节数组转换为double数组
* 长度变为字节数组的1/3
*
* @param bytes 字节数组
*/
private static void byteToDouble(byte[] bytes, double[] doubles) {
for (int i = 0; i < bytes.length; i++) {
if (i != 0 && (i % 3 == 2)) {
doubles[(i + 1) / 3 - 1] = (bytes[i - 2] & 0xFF | (bytes[i - 1] & 0xFF) << 8 | bytes[i] << 16) / 8388608f;
}
}
}
}