rockbox/songdbj/org/tritonus/file/AiffAudioOutputStream.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

205 lines
6.4 KiB
Java

/*
* AiffAudioOutputStream.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 AIFF and AIFF-C files.
*
* @author Florian Bomers
*/
public class AiffAudioOutputStream 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 AudioFileFormat.Type m_FileType;
public AiffAudioOutputStream(AudioFormat audioFormat,
AudioFileFormat.Type fileType,
long lLength,
TDataOutputStream dataOutputStream) {
super(audioFormat,
lLength,
dataOutputStream,
lLength == AudioSystem.NOT_SPECIFIED
&& dataOutputStream.supportsSeek());
// AIFF files cannot exceed 2GB
if (lLength != AudioSystem.NOT_SPECIFIED && lLength>0x7FFFFFFFl) {
throw new IllegalArgumentException(
"AIFF files cannot be larger than 2GB.");
}
// IDEA: write AIFF file instead of AIFC when encoding=PCM ?
m_FileType=fileType;
if (!audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)
&& !audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
// only AIFC files can handle non-pcm data
m_FileType=AudioFileFormat.Type.AIFC;
}
}
protected void writeHeader()
throws IOException {
if (TDebug.TraceAudioOutputStream) {
TDebug.out("AiffAudioOutputStream.writeHeader(): called.");
}
AudioFormat format = getFormat();
boolean bIsAifc = m_FileType.equals(AudioFileFormat.Type.AIFC);
long lLength = getLength();
TDataOutputStream dos = getDataOutputStream();
int nCommChunkSize=18;
int nFormatCode=AiffTool.getFormatCode(format);
if (bIsAifc) {
// encoding takes 4 bytes
// encoding name takes at minimum 2 bytes
nCommChunkSize+=6;
}
int nHeaderSize=4 // magic
+8+nCommChunkSize // COMM chunk
+8; // header of SSND chunk
if (bIsAifc) {
// add length for FVER chunk
nHeaderSize+=12;
}
// 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+nHeaderSize>0x7FFFFFFFl) {
lLength=0x7FFFFFFFl-nHeaderSize;
}
// chunks must be on word-boundaries
long lSSndChunkSize=(lLength!=AudioSystem.NOT_SPECIFIED)?
(lLength+(lLength%2)+8):AudioSystem.NOT_SPECIFIED;
// write IFF container chunk
dos.writeInt(AiffTool.AIFF_FORM_MAGIC);
dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)?
((int) (lSSndChunkSize+nHeaderSize)):LENGTH_NOT_KNOWN);
if (bIsAifc) {
dos.writeInt(AiffTool.AIFF_AIFC_MAGIC);
// write FVER chunk
dos.writeInt(AiffTool.AIFF_FVER_MAGIC);
dos.writeInt(4);
dos.writeInt(AiffTool.AIFF_FVER_TIME_STAMP);
} else {
dos.writeInt(AiffTool.AIFF_AIFF_MAGIC);
}
// write COMM chunk
dos.writeInt(AiffTool.AIFF_COMM_MAGIC);
dos.writeInt(nCommChunkSize);
dos.writeShort((short) format.getChannels());
dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)?
((int) (lLength / format.getFrameSize())):LENGTH_NOT_KNOWN);
if (nFormatCode==AiffTool.AIFF_COMM_ULAW) {
// AIFF ulaw states 16 bits for ulaw data
dos.writeShort(16);
} else {
dos.writeShort((short) format.getSampleSizeInBits());
}
writeIeeeExtended(dos, format.getSampleRate());
if (bIsAifc) {
dos.writeInt(nFormatCode);
dos.writeShort(0); // no encoding name
// TODO: write encoding.toString() ??
}
// write header of SSND chunk
dos.writeInt(AiffTool.AIFF_SSND_MAGIC);
// don't use lSSndChunkSize here !
dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)
?((int) (lLength+8)):LENGTH_NOT_KNOWN);
// 8 information bytes of no interest
dos.writeInt(0); // offset
dos.writeInt(0); // blocksize
}
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("AiffOutputStream.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();
}
public void writeIeeeExtended(TDataOutputStream dos, float sampleRate) throws IOException {
// currently, only integer sample rates are written
// TODO: real conversion
// I don't know exactly how much I have to shift left the mantisse for normalisation
// now I do it so that there are any bits set in the first 5 bits
int nSampleRate=(int) sampleRate;
short ieeeExponent=0;
while ((nSampleRate!=0) && (nSampleRate & 0x80000000)==0) {
ieeeExponent++;
nSampleRate<<=1;
}
dos.writeShort(16414-ieeeExponent); // exponent
dos.writeInt(nSampleRate); // mantisse high double word
dos.writeInt(0); // mantisse low double word
}
}
/*** AiffAudioOutputStream.java ***/