org.jcodec.codecs.wav.WavHeader.java Source code

Java tutorial

Introduction

Here is the source code for org.jcodec.codecs.wav.WavHeader.java

Source

package org.jcodec.codecs.wav;

import static org.jcodec.codecs.wav.ReaderLE.readShort;
import static org.jcodec.codecs.wav.ReaderLE.readInt;
import static org.jcodec.codecs.wav.StringReader.readString;
import static org.jcodec.codecs.wav.WriterLE.writeInt;
import static org.jcodec.codecs.wav.WriterLE.writeShort;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.sound.sampled.AudioFormat;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;
import org.jcodec.common.model.ChannelLabel;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * @author The JCodec project
 * 
 */
public class WavHeader {

    public static class FmtChunkExtended extends FmtChunk {
        short cbSize;
        short bitsPerCodedSample;
        int channelLayout;
        int guid;

        public FmtChunkExtended(FmtChunk fmtChunk, short cbSize, short bitsPerCodedSample, int channelLayout,
                int guid) {
            super(fmtChunk);
            this.cbSize = cbSize;
            this.bitsPerCodedSample = bitsPerCodedSample;
            this.channelLayout = channelLayout;
            this.guid = guid;
        }

        public static FmtChunk read(InputStream input) throws IOException {
            FmtChunk fmtChunk = FmtChunk.read(input);
            return new FmtChunkExtended(fmtChunk, readShort(input), readShort(input), readInt(input),
                    readInt(input));
        }

        public void write(OutputStream out) throws IOException {
            super.write(out);

            writeShort(out, cbSize);
            writeShort(out, bitsPerCodedSample);
            writeInt(out, channelLayout);
            writeInt(out, guid);
        }

        public int size() {
            return super.size() + 12;
        }

        public ChannelLabel[] getLabels() {
            List<ChannelLabel> labels = new ArrayList<ChannelLabel>();
            for (int i = 0; i < mapping.length; i++) {
                if ((channelLayout & (1 << i)) != 0)
                    labels.add(mapping[i]);
            }
            return labels.toArray(new ChannelLabel[0]);
        }
    }

    static ChannelLabel[] mapping = new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT,
            ChannelLabel.CENTER, ChannelLabel.LFE, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT,
            ChannelLabel.FRONT_CENTER_LEFT, ChannelLabel.FRONT_CENTER_RIGHT, ChannelLabel.REAR_CENTER,
            ChannelLabel.SIDE_LEFT, ChannelLabel.SIDE_RIGHT, ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT,
            ChannelLabel.CENTER, ChannelLabel.FRONT_RIGHT, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_CENTER,
            ChannelLabel.REAR_RIGHT, ChannelLabel.STEREO_LEFT, ChannelLabel.STEREO_RIGHT };

    public static class FmtChunk {
        public short audioFormat;
        public short numChannels;
        public int sampleRate;
        public int byteRate;
        public short blockAlign;
        public short bitsPerSample;

        public FmtChunk() {
            this.audioFormat = 1;
        }

        public FmtChunk(short audioFormat, short numChannels, int sampleRate, int byteRate, short blockAlign,
                short bitsPerSample) {
            this.audioFormat = audioFormat;
            this.numChannels = numChannels;
            this.sampleRate = sampleRate;
            this.byteRate = byteRate;
            this.blockAlign = blockAlign;
            this.bitsPerSample = bitsPerSample;
        }

        public FmtChunk(FmtChunk other) {
            this(other.audioFormat, other.numChannels, other.sampleRate, other.byteRate, other.blockAlign,
                    other.bitsPerSample);
        }

        public static FmtChunk read(InputStream input) throws IOException {
            return new FmtChunk(readShort(input), readShort(input), readInt(input), readInt(input),
                    readShort(input), readShort(input));
        }

        public void write(OutputStream out) throws IOException {
            writeShort(out, audioFormat);
            writeShort(out, numChannels);
            writeInt(out, sampleRate);
            writeInt(out, byteRate);
            writeShort(out, blockAlign);
            writeShort(out, bitsPerSample);
        }

        public int size() {
            return 16;
        }
    }

    public String chunkId;
    public int chunkSize;
    public String format;
    public FmtChunk fmt;
    public int dataOffset;
    public long dataSize;
    public static final int WAV_HEADER_SIZE = 44;

    public WavHeader(String chunkId, int chunkSize, String format, FmtChunk fmt, int dataOffset, long dataSize) {
        this.chunkId = chunkId;
        this.chunkSize = chunkSize;
        this.format = format;
        this.fmt = fmt;
        this.dataOffset = dataOffset;
        this.dataSize = dataSize;
    }

    public WavHeader(WavHeader header) {
        throw new UnsupportedOperationException();
    }

    public WavHeader(WavHeader header, int rate) {
        this(header);
        fmt.sampleRate = rate;
    }

    public static WavHeader stereo48k() {
        return stereo48k(0);
    }

    public static WavHeader stereo48k(long samples) {
        return new WavHeader("RIFF", 40, "WAVE",
                new FmtChunk((short) 1, (short) 2, 48000, 48000 * 2 * 16 / 8, (short) 4, (short) 16), 44,
                calcDataSize(1, 2, samples));
    }

    public static WavHeader mono48k(long samples) {
        return new WavHeader("RIFF", 40, "WAVE",
                new FmtChunk((short) 1, (short) 1, 48000, 48000 * 1 * 16 / 8, (short) 2, (short) 16), 44,
                calcDataSize(1, 2, samples));
    }

    public static WavHeader emptyWavHeader() {
        return new WavHeader("RIFF", 40, "WAVE", new FmtChunk(), 44, 0);
    }

    public static WavHeader read(File file) throws IOException {
        InputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            return read(is);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    public static WavHeader read(InputStream in) throws IOException {
        CountingInputStream cin = new CountingInputStream(in);
        String chunkId = readString(cin, 4);
        int chunkSize = readInt(cin);
        String format = readString(cin, 4);
        FmtChunk fmt = null;

        if (!"RIFF".equals(chunkId) || !"WAVE".equals(format)) {
            return null;
        }
        String fourcc;
        int size = 0;
        do {
            fourcc = readString(cin, 4);
            size = ReaderLE.readInt(cin);
            if ("fmt ".equals(fourcc) && size >= 14 && size <= 1024 * 1024) {
                switch (size) {
                case 16:
                case 18:
                    fmt = FmtChunk.read(cin);
                    StringReader.sureSkip(cin, 2);
                    break;
                case 40:
                    fmt = FmtChunkExtended.read(cin);
                    StringReader.sureSkip(cin, 12);
                    break;
                case 28:
                    fmt = FmtChunkExtended.read(cin);
                    break;
                default:
                    throw new IllegalStateException("Don't know how to handle fmt size: " + size);
                }
            } else if (!"data".equals(fourcc)) {
                StringReader.sureRead(cin, size);
            }
        } while (!"data".equals(fourcc));

        return new WavHeader(chunkId, chunkSize, format, fmt, cin.getCount(), size);
    }

    public static WavHeader multiChannelWav(List<File> wavs) throws IOException {
        return multiChannelWav(wavs.toArray(new File[0]));
    }

    public static WavHeader multiChannelWav(File... wavs) throws IOException {
        WavHeader headers[] = new WavHeader[wavs.length];
        for (int i = 0; i < wavs.length; i++) {
            headers[i] = read(wavs[i]);
        }
        return multiChannelWav(headers);
    }

    /** Takes single channel wavs as input produces multi channel wav */
    public static WavHeader multiChannelWav(WavHeader... wavs) {
        WavHeader w = emptyWavHeader();
        int totalSize = 0;
        for (WavHeader wavHeader : wavs) {
            totalSize += wavHeader.dataSize;
        }
        w.dataSize = totalSize;
        FmtChunk fmt = wavs[0].fmt;
        int bitsPerSample = fmt.bitsPerSample;
        int bytesPerSample = bitsPerSample / 8;
        int sampleRate = (int) fmt.sampleRate;
        w.fmt.bitsPerSample = (short) bitsPerSample;
        w.fmt.blockAlign = (short) (wavs.length * bytesPerSample);
        w.fmt.byteRate = wavs.length * bytesPerSample * sampleRate;
        w.fmt.numChannels = (short) wavs.length;
        w.fmt.sampleRate = sampleRate;
        return w;
    }

    public void write(OutputStream out) throws IOException {
        long chunkSize;
        if (dataSize <= 0xffffffffL) {
            chunkSize = dataSize + 36;
        } else {
            chunkSize = 40;
        }

        out.write("RIFF".getBytes());
        writeInt(out, (int) chunkSize);
        out.write("WAVE".getBytes());

        out.write("fmt ".getBytes());
        writeInt(out, fmt.size());
        fmt.write(out);
        out.write("data".getBytes());
        if (dataSize <= 0xffffffffL) {
            writeInt(out, (int) dataSize);
        } else {
            writeInt(out, 0);
        }
    }

    public void writeExtended(OutputStream out) throws IOException {
        long chunkSize;
        if (dataSize <= 0xffffffffL) {
            chunkSize = dataSize + 36;
        } else {
            chunkSize = 40;
        }

        out.write("RIFF".getBytes());
        writeInt(out, (int) chunkSize);
        out.write("WAVE".getBytes());

        out.write("fmt ".getBytes());
        writeInt(out, fmt.size());
        fmt.write(out);
        out.write("data".getBytes());

        if (dataSize <= 0xffffffffL) {
            writeInt(out, (int) dataSize);
        } else {
            writeInt(out, 0);
        }
    }

    public static long calcDataSize(int numChannels, int bytesPerSample, long samples) {
        return samples * numChannels * bytesPerSample;
    }

    public static WavHeader create(AudioFormat af, int size) {
        WavHeader w = emptyWavHeader();
        w.dataSize = size;
        FmtChunk fmt = new FmtChunk();
        int bitsPerSample = af.getSampleSizeInBits();
        int bytesPerSample = bitsPerSample / 8;
        int sampleRate = (int) af.getSampleRate();
        w.fmt.bitsPerSample = (short) bitsPerSample;
        w.fmt.blockAlign = (short) (af.getFrameSize());
        w.fmt.byteRate = (int) af.getFrameRate() * af.getFrameSize();
        w.fmt.numChannels = (short) af.getChannels();
        w.fmt.sampleRate = (int) af.getSampleRate();
        return w;
    }

    public ChannelLabel[] getChannelLabels() {
        if (fmt instanceof FmtChunkExtended) {
            return ((FmtChunkExtended) fmt).getLabels();
        } else {
            switch (fmt.numChannels) {
            case 1:
                return new ChannelLabel[] { ChannelLabel.MONO };
            case 2:
                return new ChannelLabel[] { ChannelLabel.STEREO_LEFT, ChannelLabel.STEREO_RIGHT };
            case 3:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT,
                        ChannelLabel.REAR_CENTER };
            case 4:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT,
                        ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT };
            case 5:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.CENTER,
                        ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT };
            case 6:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.CENTER,
                        ChannelLabel.LFE, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT };
            case 7:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.CENTER,
                        ChannelLabel.LFE, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT,
                        ChannelLabel.REAR_CENTER };
            case 8:
                return new ChannelLabel[] { ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.CENTER,
                        ChannelLabel.LFE, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT, ChannelLabel.REAR_LEFT,
                        ChannelLabel.REAR_RIGHT };
            default:
                ChannelLabel[] labels = new ChannelLabel[fmt.numChannels];
                Arrays.fill(labels, ChannelLabel.MONO);
                return labels;
            }
        }
    }
}