245 lines
7.7 KiB
Java
245 lines
7.7 KiB
Java
|
/*
|
||
|
* AiffAudioFileReader.java
|
||
|
*
|
||
|
* This file is part of Tritonus: http://www.tritonus.org/
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
|
||
|
* Copyright (c) 1999 by Matthias Pfisterer
|
||
|
*
|
||
|
*
|
||
|
* 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.DataInputStream;
|
||
|
import java.io.File;
|
||
|
import java.io.InputStream;
|
||
|
import java.io.IOException;
|
||
|
|
||
|
import javax.sound.sampled.AudioFormat;
|
||
|
import javax.sound.sampled.AudioFileFormat;
|
||
|
import javax.sound.sampled.AudioInputStream;
|
||
|
import javax.sound.sampled.AudioSystem;
|
||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||
|
|
||
|
import org.tritonus.share.sampled.file.TAudioFileFormat;
|
||
|
import org.tritonus.share.sampled.file.TAudioFileReader;
|
||
|
import org.tritonus.share.TDebug;
|
||
|
|
||
|
|
||
|
/** Class for reading AIFF and AIFF-C files.
|
||
|
*
|
||
|
* @author Florian Bomers
|
||
|
* @author Matthias Pfisterer
|
||
|
*/
|
||
|
public class AiffAudioFileReader extends TAudioFileReader
|
||
|
{
|
||
|
private static final int READ_LIMIT = 1000;
|
||
|
|
||
|
|
||
|
|
||
|
public AiffAudioFileReader()
|
||
|
{
|
||
|
super(READ_LIMIT);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
private void skipChunk(DataInputStream dataInputStream, int chunkLength, int chunkRead)
|
||
|
throws IOException {
|
||
|
chunkLength-=chunkRead;
|
||
|
if (chunkLength>0) {
|
||
|
dataInputStream.skip(chunkLength + (chunkLength % 2));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private AudioFormat readCommChunk(DataInputStream dataInputStream, int chunkLength)
|
||
|
throws IOException, UnsupportedAudioFileException {
|
||
|
|
||
|
int nNumChannels = dataInputStream.readShort();
|
||
|
if (nNumChannels <= 0) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"not an AIFF file: number of channels must be positive");
|
||
|
}
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Found "+nNumChannels+" channels.");
|
||
|
}
|
||
|
// ignored: frame count
|
||
|
dataInputStream.readInt();
|
||
|
int nSampleSize = dataInputStream.readShort();
|
||
|
float fSampleRate = (float) readIeeeExtended(dataInputStream);
|
||
|
if (fSampleRate <= 0.0) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"not an AIFF file: sample rate must be positive");
|
||
|
}
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Found framerate "+fSampleRate);
|
||
|
}
|
||
|
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
|
||
|
int nRead=18;
|
||
|
if (chunkLength>nRead) {
|
||
|
int nEncoding=dataInputStream.readInt();
|
||
|
nRead+=4;
|
||
|
if (nEncoding==AiffTool.AIFF_COMM_PCM) {
|
||
|
// PCM - nothing to do
|
||
|
}
|
||
|
else if (nEncoding==AiffTool.AIFF_COMM_ULAW) {
|
||
|
// ULAW
|
||
|
encoding=AudioFormat.Encoding.ULAW;
|
||
|
nSampleSize=8;
|
||
|
}
|
||
|
else if (nEncoding==AiffTool.AIFF_COMM_IMA_ADPCM) {
|
||
|
encoding = new AudioFormat.Encoding("IMA_ADPCM");
|
||
|
nSampleSize=4;
|
||
|
}
|
||
|
else {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"Encoding 0x"+Integer.toHexString(nEncoding)
|
||
|
+" of AIFF file not supported");
|
||
|
}
|
||
|
}
|
||
|
/* In case of IMA ADPCM, frame size is 0.5 bytes (since it is
|
||
|
always mono). A value of 1 as frame size would be wrong.
|
||
|
Handling of frame size 0 in defined nowhere. So the best
|
||
|
solution is to set the frame size to unspecified (-1).
|
||
|
*/
|
||
|
int nFrameSize = (nSampleSize == 4) ?
|
||
|
AudioSystem.NOT_SPECIFIED :
|
||
|
calculateFrameSize(nSampleSize, nNumChannels);
|
||
|
if (TDebug.TraceAudioFileReader) { TDebug.out("calculated frame size: " + nFrameSize); }
|
||
|
skipChunk(dataInputStream, chunkLength, nRead);
|
||
|
AudioFormat format = new AudioFormat(encoding,
|
||
|
fSampleRate,
|
||
|
nSampleSize,
|
||
|
nNumChannels,
|
||
|
nFrameSize,
|
||
|
fSampleRate,
|
||
|
true);
|
||
|
return format;
|
||
|
}
|
||
|
|
||
|
private void readVerChunk(DataInputStream dataInputStream, int chunkLength)
|
||
|
throws IOException, UnsupportedAudioFileException {
|
||
|
if (chunkLength<4) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"Corrput AIFF file: FVER chunk too small.");
|
||
|
}
|
||
|
int nVer=dataInputStream.readInt();
|
||
|
if (nVer!=AiffTool.AIFF_FVER_TIME_STAMP) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"Unsupported AIFF file: version not known.");
|
||
|
}
|
||
|
skipChunk(dataInputStream, chunkLength, 4);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
|
||
|
throws UnsupportedAudioFileException, IOException
|
||
|
{
|
||
|
if (TDebug.TraceAudioFileReader) {TDebug.out("AiffAudioFileReader.getAudioFileFormat(InputStream, long): begin"); }
|
||
|
DataInputStream dataInputStream = new DataInputStream(inputStream);
|
||
|
int nMagic = dataInputStream.readInt();
|
||
|
if (nMagic != AiffTool.AIFF_FORM_MAGIC) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"not an AIFF file: header magic is not FORM");
|
||
|
}
|
||
|
int nTotalLength = dataInputStream.readInt();
|
||
|
nMagic = dataInputStream.readInt();
|
||
|
boolean bIsAifc;
|
||
|
if (nMagic == AiffTool.AIFF_AIFF_MAGIC) {
|
||
|
bIsAifc = false;
|
||
|
} else if (nMagic == AiffTool.AIFF_AIFC_MAGIC) {
|
||
|
bIsAifc = true;
|
||
|
} else {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"unsupported IFF file: header magic neither AIFF nor AIFC");
|
||
|
}
|
||
|
boolean bFVerFound=!bIsAifc;
|
||
|
boolean bCommFound=false;
|
||
|
boolean bSSndFound=false;
|
||
|
AudioFormat format=null;
|
||
|
int nDataChunkLength=0;
|
||
|
|
||
|
// walk through the chunks
|
||
|
// chunks may be in any order. However, in this implementation, SSND must be last
|
||
|
while (!bFVerFound || !bCommFound || !bSSndFound) {
|
||
|
nMagic = dataInputStream.readInt();
|
||
|
int nChunkLength = dataInputStream.readInt();
|
||
|
switch (nMagic) {
|
||
|
case AiffTool.AIFF_COMM_MAGIC:
|
||
|
format=readCommChunk(dataInputStream, nChunkLength);
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Read COMM chunk with length "+nChunkLength);
|
||
|
}
|
||
|
bCommFound=true;
|
||
|
break;
|
||
|
case AiffTool.AIFF_FVER_MAGIC:
|
||
|
if (!bFVerFound) {
|
||
|
readVerChunk(dataInputStream, nChunkLength);
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Read FVER chunk with length "+nChunkLength);
|
||
|
}
|
||
|
bFVerFound=true;
|
||
|
} else {
|
||
|
skipChunk(dataInputStream, nChunkLength, 0);
|
||
|
}
|
||
|
break;
|
||
|
case AiffTool.AIFF_SSND_MAGIC:
|
||
|
if (!bCommFound || !bFVerFound) {
|
||
|
throw new UnsupportedAudioFileException(
|
||
|
"cannot handle AIFF file: SSND not last chunk");
|
||
|
}
|
||
|
bSSndFound=true;
|
||
|
nDataChunkLength=nChunkLength-8;
|
||
|
// 8 information bytes of no interest
|
||
|
dataInputStream.skip(8);
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Found SSND chunk with length "+nChunkLength);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (TDebug.TraceAudioFileReader) {
|
||
|
TDebug.out("Skipping unknown chunk: "
|
||
|
+Integer.toHexString(nMagic));
|
||
|
}
|
||
|
skipChunk(dataInputStream, nChunkLength, 0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO: length argument has to be in frames
|
||
|
AudioFileFormat audioFileFormat = new TAudioFileFormat(
|
||
|
bIsAifc ? AudioFileFormat.Type.AIFC : AudioFileFormat.Type.AIFF,
|
||
|
format,
|
||
|
nDataChunkLength / format.getFrameSize(),
|
||
|
nTotalLength + 8);
|
||
|
if (TDebug.TraceAudioFileReader) {TDebug.out("AiffAudioFileReader.getAudioFileFormat(InputStream, long): end"); }
|
||
|
return audioFileFormat;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*** AiffAudioFileReader.java ***/
|