org.jflac.util.WavWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jflac-codec Show documentation
Show all versions of jflac-codec Show documentation
Encoder and Decoder for FLAC files including Java Sound SPI
The newest version!
package org.jflac.util;
/**
* libFLAC - Free Lossless Audio Codec library
* Copyright (C) 2001,2002,2003 Josh Coalson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import org.jflac.ChannelData;
import org.jflac.Constants;
import org.jflac.frame.Frame;
import org.jflac.metadata.StreamInfo;
/**
* Utility class for writing WAV files.
* @author kc7bfi
*/
public class WavWriter {
private static final int MAX_BLOCK_SIZE = 65535;
private long totalSamples;
private int channels;
private int bps;
private int sampleRate;
private byte[] s8buffer = new byte[MAX_BLOCK_SIZE * Constants.MAX_CHANNELS * 4]; /* WATCHOUT: can be up to 2 megs */
private int samplesProcessed = 0;
private int frameCounter = 0;
private DataOutput os;
private LittleEndianDataOutput osLE;
/**
* The constructor.
* @param os The output sream
* @param streamInfo The FLAC stream info
*/
public WavWriter(DataOutput os, StreamInfo streamInfo) {
this.os = os;
this.osLE = new LittleEndianDataOutput(os);
this.totalSamples = streamInfo.getTotalSamples();
this.channels = streamInfo.getChannels();
this.bps = streamInfo.getBitsPerSample();
this.sampleRate = streamInfo.getSampleRate();
}
/**
* The constructor.
* @param os The output sream
*/
public WavWriter(DataOutput os) {
this.os = os;
this.osLE = new LittleEndianDataOutput(os);
}
/**
* The constructor.
* @param os The output sream
* @param streamInfo The FLAC stream info
*/
public WavWriter(OutputStream os, StreamInfo streamInfo) {
this.os = new DataOutputStream(os);
this.osLE = new LittleEndianDataOutput(this.os);
this.totalSamples = streamInfo.getTotalSamples();
this.channels = streamInfo.getChannels();
this.bps = streamInfo.getBitsPerSample();
this.sampleRate = streamInfo.getSampleRate();
}
/**
* The constructor.
* @param os The output sream
* @param totalSamples The total samples to be written
* @param channels Number of channels
* @param bps Number of bits per sample
* @param sampleRate Sample rate per second
*/
public WavWriter(OutputStream os,
Long totalSamples, Integer channels,
Integer bps, Integer sampleRate) {
this.os = new DataOutputStream(os);
this.osLE = new LittleEndianDataOutput(this.os);
this.totalSamples = totalSamples;
this.channels = channels;
this.bps = bps;
this.sampleRate = sampleRate;
}
/**
* The constructor.
* @param os The output sream
*/
public WavWriter(OutputStream os) {
this.os = new DataOutputStream(os);
this.osLE = new LittleEndianDataOutput(this.os);
}
/**
* Write a WAV file header.
* @throws IOException Thrown if error writing to output string.
*/
public void writeHeader() throws IOException {
long dataSize = totalSamples * channels * ((bps + 7) / 8);
if (totalSamples == 0) {
if (!(os instanceof RandomAccessFile)) throw new IOException("Cannot seek in output stream");
}
//if (dataSize >= 0xFFFFFFDC) throw new IOException("ERROR: stream is too big to fit in a single file chunk (Datasize="+dataSize+")");
os.write("RIFF".getBytes());
osLE.writeInt((int) dataSize + 36); // filesize-8
os.write("WAVEfmt ".getBytes());
os.write(new byte[] {0x10, 0x00, 0x00, 0x00}); // chunk size = 16
os.write(new byte[] {0x01, 0x00}); // compression code == 1
osLE.writeShort(channels);
osLE.writeInt(sampleRate);
osLE.writeInt(sampleRate * channels * ((bps + 7) / 8)); // or is it (sample_rate*channels*bps) / 8
osLE.writeShort(channels * ((bps + 7) / 8)); // block align
osLE.writeShort(bps); // bits per sample
os.write("data".getBytes());
osLE.writeInt((int) dataSize); // data size
}
/**
* Write a WAV file header.
* @param streamInfo The FLAC stream info
* @throws IOException Thrown if error writing to output string.
*/
public void writeHeader(StreamInfo streamInfo) throws IOException {
this.totalSamples = streamInfo.getTotalSamples();
this.channels = streamInfo.getChannels();
this.bps = streamInfo.getBitsPerSample();
this.sampleRate = streamInfo.getSampleRate();
writeHeader();
}
/**
* Write a WAV frame record.
* @param frame The FLAC frame
* @param channelData The decoded channel data
* @throws IOException Thrown if error writing to output channel
*/
public void writeFrame(Frame frame, ChannelData[] channelData) throws IOException {
boolean isUnsignedSamples = (bps <= 8);
int wideSamples = frame.header.blockSize;
int wideSample;
int sample;
int channel;
if (wideSamples > 0) {
samplesProcessed += wideSamples;
frameCounter++;
if (bps == 8) {
if (isUnsignedSamples) {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++) {
//System.out.print("("+(int)((byte)(channelData[channel].getOutput()[wideSample] + 0x80))+")");
s8buffer[sample++] = (byte) (channelData[channel].getOutput()[wideSample] + 0x80);
}
} else {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++)
s8buffer[sample++] = (byte) (channelData[channel].getOutput()[wideSample]);
}
os.write(s8buffer, 0, sample);
} else if (bps == 16) {
if (isUnsignedSamples) {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++) {
short val = (short) (channelData[channel].getOutput()[wideSample] + 0x8000);
s8buffer[sample++] = (byte) (val & 0xff);
s8buffer[sample++] = (byte) ((val >> 8) & 0xff);
}
} else {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++) {
short val = (short) (channelData[channel].getOutput()[wideSample]);
s8buffer[sample++] = (byte) (val & 0xff);
s8buffer[sample++] = (byte) ((val >> 8) & 0xff);
}
}
os.write(s8buffer, 0, sample);
} else if (bps == 24) {
if (isUnsignedSamples) {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++) {
int val = (channelData[channel].getOutput()[wideSample] + 0x800000);
s8buffer[sample++] = (byte) (val & 0xff);
s8buffer[sample++] = (byte) ((val >> 8) & 0xff);
s8buffer[sample++] = (byte) ((val >> 16) & 0xff);
}
} else {
for (sample = wideSample = 0; wideSample < wideSamples; wideSample++)
for (channel = 0; channel < channels; channel++) {
int val = (channelData[channel].getOutput()[wideSample]);
s8buffer[sample++] = (byte) (val & 0xff);
s8buffer[sample++] = (byte) ((val >> 8) & 0xff);
s8buffer[sample++] = (byte) ((val >> 16) & 0xff);
}
}
os.write(s8buffer, 0, sample);
}
}
}
/**
* Write the PCM data.
* @param space The PCM data
* @throws IOException Thrown if error writing to file
*/
public void writePCM(ByteData space) throws IOException {
os.write(space.getData(), 0, space.getLen());
}
}