9fee0ec4ca
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
205 lines
6.4 KiB
Java
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 ***/
|