rockbox/songdbj/org/tritonus/file/WaveAudioOutputStream.java
Michiel Van Der Kolk 9fee0ec4ca Songdb java version, source. only 1.5 compatible
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
2005-07-11 15:42:37 +00:00

201 lines
6.5 KiB
Java

/*
* WaveAudioOutputStream.java
*
* This file is part of Tritonus: http://www.tritonus.org/
*/
/*
* Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
*
*
* This program 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 program 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
|<--- this code is formatted to fit into 80 columns --->|
*/
package org.tritonus.sampled.file;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import org.tritonus.share.TDebug;
import org.tritonus.share.sampled.file.TAudioOutputStream;
import org.tritonus.share.sampled.file.TDataOutputStream;
/**
* AudioOutputStream for Wave files.
*
* @author Florian Bomers
*/
public class WaveAudioOutputStream extends TAudioOutputStream {
// this constant is used for chunk lengths when the length is not known yet
private static final int LENGTH_NOT_KNOWN=-1;
private int formatCode;
public WaveAudioOutputStream(AudioFormat audioFormat,
long lLength,
TDataOutputStream dataOutputStream) {
super(audioFormat,
lLength,
dataOutputStream,
lLength == AudioSystem.NOT_SPECIFIED && dataOutputStream.supportsSeek());
// wave cannot store more than 4GB
if (lLength != AudioSystem.NOT_SPECIFIED
&& (lLength+WaveTool.DATA_OFFSET)>0xFFFFFFFFl) {
if (TDebug.TraceAudioOutputStream) {
TDebug.out("WaveAudioOutputStream: Length exceeds 4GB: "
+lLength+"=0x"+Long.toHexString(lLength)
+" with header="+(lLength+WaveTool.DATA_OFFSET)
+"=0x"+Long.toHexString(lLength+WaveTool.DATA_OFFSET));
}
throw new IllegalArgumentException("Wave files cannot be larger than 4GB.");
}
formatCode = WaveTool.getFormatCode(getFormat());
if (formatCode == WaveTool.WAVE_FORMAT_UNSPECIFIED) {
throw new IllegalArgumentException("Unknown encoding/format for this wave file.");
}
}
protected void writeHeader()
throws IOException {
if (TDebug.TraceAudioOutputStream) {
TDebug.out("WaveAudioOutputStream.writeHeader()");
}
AudioFormat format = getFormat();
long lLength = getLength();
int formatChunkAdd=0;
if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
// space for extra fields
formatChunkAdd+=2;
}
int dataOffset=WaveTool.DATA_OFFSET+formatChunkAdd;
if (formatCode!=WaveTool.WAVE_FORMAT_PCM) {
// space for fact chunk
dataOffset+=4+WaveTool.CHUNK_HEADER_SIZE;
}
// if patching the header, and the length has not been known at first
// writing of the header, just truncate the size fields, don't throw an exception
if (lLength != AudioSystem.NOT_SPECIFIED
&& lLength+dataOffset>0xFFFFFFFFl) {
lLength=0xFFFFFFFFl-dataOffset;
}
// chunks must be on word-boundaries
long lDataChunkSize=lLength+(lLength%2);
TDataOutputStream dos = getDataOutputStream();
// write RIFF container chunk
dos.writeInt(WaveTool.WAVE_RIFF_MAGIC);
dos.writeLittleEndian32((int) ((lDataChunkSize+dataOffset-WaveTool.CHUNK_HEADER_SIZE)
& 0xFFFFFFFF));
dos.writeInt(WaveTool.WAVE_WAVE_MAGIC);
// write fmt_ chunk
int formatChunkSize=WaveTool.FMT_CHUNK_SIZE+formatChunkAdd;
short sampleSizeInBits=(short) format.getSampleSizeInBits();
int decodedSamplesPerBlock=1;
if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
if (format.getFrameSize()==33) {
decodedSamplesPerBlock=320;
} else if (format.getFrameSize()==65) {
decodedSamplesPerBlock=320;
} else {
// how to retrieve this value here ?
decodedSamplesPerBlock=(int) (format.getFrameSize()*(320.0f/65.0f));
}
sampleSizeInBits=0; // MS standard
}
int avgBytesPerSec=((int) format.getSampleRate())/decodedSamplesPerBlock*format.getFrameSize();
dos.writeInt(WaveTool.WAVE_FMT_MAGIC);
dos.writeLittleEndian32(formatChunkSize);
dos.writeLittleEndian16((short) formatCode); // wFormatTag
dos.writeLittleEndian16((short) format.getChannels()); // nChannels
dos.writeLittleEndian32((int) format.getSampleRate()); // nSamplesPerSec
dos.writeLittleEndian32(avgBytesPerSec); // nAvgBytesPerSec
dos.writeLittleEndian16((short) format.getFrameSize()); // nBlockalign
dos.writeLittleEndian16(sampleSizeInBits); // wBitsPerSample
dos.writeLittleEndian16((short) formatChunkAdd); // cbSize
if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
dos.writeLittleEndian16((short) decodedSamplesPerBlock); // wSamplesPerBlock
}
// write fact chunk
if (formatCode!=WaveTool.WAVE_FORMAT_PCM) {
// write "fact" chunk: number of samples
// todo: add this as an attribute or property
// in AudioOutputStream or AudioInputStream
long samples=0;
if (lLength!=AudioSystem.NOT_SPECIFIED) {
samples=lLength/format.getFrameSize()*decodedSamplesPerBlock;
}
// saturate sample count
if (samples>0xFFFFFFFFl) {
samples=(0xFFFFFFFFl/decodedSamplesPerBlock)*decodedSamplesPerBlock;
}
dos.writeInt(WaveTool.WAVE_FACT_MAGIC);
dos.writeLittleEndian32(4);
dos.writeLittleEndian32((int) (samples & 0xFFFFFFFF));
}
// write header of data chunk
dos.writeInt(WaveTool.WAVE_DATA_MAGIC);
dos.writeLittleEndian32((lLength!=AudioSystem.NOT_SPECIFIED)?((int) lLength):LENGTH_NOT_KNOWN);
}
protected void patchHeader()
throws IOException {
TDataOutputStream tdos = getDataOutputStream();
tdos.seek(0);
setLengthFromCalculatedLength();
writeHeader();
}
public void close() throws IOException {
long nBytesWritten=getCalculatedLength();
if ((nBytesWritten % 2)==1) {
if (TDebug.TraceAudioOutputStream) {
TDebug.out("WaveOutputStream.close(): adding padding byte");
}
// extra byte for to align on word boundaries
TDataOutputStream tdos = getDataOutputStream();
tdos.writeByte(0);
// DON'T adjust calculated length !
}
super.close();
}
}
/*** WaveAudioOutputStream.java ***/