wave文件格式解析-java

wave文件格式解析

  • wave的头文件详细
    wave文件格式解析-java_第1张图片

  • 注意事项

    • 在第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;

}

  • wave转换工具类
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;
            }
        }
    }
}


你可能感兴趣的:(java,工具)