/*
* @(#)AuParser.java 1.23 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media.parser.audio;
import java.io.IOException;
import javax.media.Time;
import javax.media.Duration;
import javax.media.Track;
import javax.media.BadHeaderException;
import javax.media.protocol.SourceStream;
import javax.media.protocol.PullSourceStream;
import javax.media.protocol.Positionable;
import javax.media.Format;
import javax.media.protocol.ContentDescriptor;
import javax.media.format.AudioFormat;
import com.sun.media.parser.BasicPullParser;
import com.sun.media.parser.BasicTrack;
import com.sun.media.util.SettableTime;
public class AuParser extends BasicPullParser {
private Time duration = Duration.DURATION_UNKNOWN;
private Format format = null;
private Track[] tracks = new Track[1]; // Only 1 track is there for wave
private int numBuffers = 4; // TODO: check
private int bufferSize;
private int dataSize;
private SettableTime mediaTime = new SettableTime(0L);
private int encoding;
private String encodingString;
private int sampleRate;
private int samplesPerBlock;
private int bytesPerSecond;
private int blockSize;
private long minLocation;
private long maxLocation;
private PullSourceStream stream = null;
public final static int AU_SUN_MAGIC = 0x2e736e64;
public final static int AU_SUN_INV_MAGIC = 0x646e732e;
public final static int AU_DEC_MAGIC = 0x2e736400;
public final static int AU_DEC_INV_MAGIC = 0x0064732e;
public final static int AU_ULAW_8 = 1; /* 8-bit ISDN u-law */
public final static int AU_LINEAR_8 = 2; /* 8-bit linear PCM */
public final static int AU_LINEAR_16 = 3; /* 16-bit linear PCM */
public final static int AU_LINEAR_24 = 4; /* 24-bit linear PCM */
public final static int AU_LINEAR_32 = 5; /* 32-bit linear PCM */
public final static int AU_FLOAT = 6; /* 32-bit IEEE floating point */
public final static int AU_DOUBLE = 7; /* 64-bit IEEE floating point */
public final static int AU_ADPCM_G721 = 23; /* 4-bit CCITT g.721 ADPCM */
public final static int AU_ADPCM_G722 = 24; /* CCITT g.722 ADPCM */
public final static int AU_ADPCM_G723_3 = 25; /* CCITT g.723 3-bit ADPCM */
public final static int AU_ADPCM_G723_5 = 26; /* CCITT g.723 5-bit ADPCM */
public final static int AU_ALAW_8 = 27; /* 8-bit ISDN A-law */
private static ContentDescriptor[] supportedFormat = new ContentDescriptor[] {new ContentDescriptor("audio.basic")};
public ContentDescriptor [] getSupportedInputContentDescriptors() {
return supportedFormat;
}
public Track[] getTracks() throws IOException, BadHeaderException {
if (tracks[0] != null)
return tracks;
stream = (PullSourceStream) streams[0];
if (cacheStream != null) {
// Disable jitter buffer during parsing of the header
cacheStream.setEnabledBuffering(false);
}
readHeader();
if (cacheStream != null) {
cacheStream.setEnabledBuffering(true);
}
minLocation = getLocation(stream);
if (dataSize == -1) { // Unknown
maxLocation = Long.MAX_VALUE; // ??
} else {
maxLocation = minLocation + dataSize;
}
// System.out.println("Location after readHeader() is " + minLocation);
// System.out.println("minLocation is " + minLocation);
// System.out.println("maxLocation is " + maxLocation);
tracks[0] = new AuTrack((AudioFormat) format,
/*enabled=*/ true,
new Time(0),
numBuffers,
bufferSize,
minLocation,
maxLocation
);
return tracks;
}
private void /* for now void */ readHeader()
throws IOException, BadHeaderException {
boolean bigEndian;
int magic = readInt(stream, /* bigEndian = */ true);
// System.out.println("Magic is " + Integer.toHexString(magic));
if ( magic == AU_SUN_MAGIC || magic == AU_DEC_MAGIC ) {
bigEndian = true;
} else if ( magic == AU_SUN_INV_MAGIC || magic == AU_DEC_INV_MAGIC ) {
bigEndian = false;
} else {
throw new BadHeaderException("Invalid magic number " +
Integer.toHexString(magic));
}
int headerSize = readInt(stream);
if (headerSize < 24) {
throw new BadHeaderException("AU Parser: header size should be atleast 24 but is "
+ headerSize);
}
dataSize = readInt(stream);
if (dataSize == -1) {
// Unknown DataSize
// System.out.println("Unknown datasize");
long contentLength = stream.getContentLength();
if ( contentLength != SourceStream.LENGTH_UNKNOWN ) {
dataSize = (int) (contentLength - headerSize);
if (dataSize < 0) {
dataSize = -1;
}
}
}
int encoding = readInt(stream);
int sampleSizeInBits;
blockSize = -1;
switch (encoding) {
case AU_ULAW_8:
encodingString = AudioFormat.ULAW;
sampleSizeInBits = 8;
blockSize = 1;
break;
case AU_ALAW_8:
encodingString = AudioFormat.ALAW;
sampleSizeInBits = 8;
blockSize = 1;
break;
case AU_LINEAR_8:
encodingString = AudioFormat.LINEAR;
sampleSizeInBits = 8;
blockSize = 1;
break;
case AU_LINEAR_16:
encodingString = AudioFormat.LINEAR;
sampleSizeInBits = 16;
blockSize = 2;
break;
case AU_LINEAR_24:
encodingString = AudioFormat.LINEAR;
sampleSizeInBits = 24;
blockSize = 3;
break;
case AU_LINEAR_32:
encodingString = AudioFormat.LINEAR;
sampleSizeInBits = 32;
blockSize = 4;
break;
case AU_FLOAT:
encodingString = "float"; // AudioFormat.JAUDIO_FLOAT;
sampleSizeInBits = 32;
blockSize = 4;
break;
case AU_DOUBLE:
encodingString = "double"; // AudioFormat.JAUDIO_DOUBLE;
sampleSizeInBits = 64;
blockSize = 8;
break;
case AU_ADPCM_G721:
encodingString = "??? what adpcm"; // AudioFormat.JAUDIO_G721_ADPCM;
sampleSizeInBits = 4;
break;
case AU_ADPCM_G723_3:
encodingString = "G723_3"; // AudioFormat.JAUDIO_G723_3;
sampleSizeInBits = 3;
break;
case AU_ADPCM_G723_5:
encodingString = "G723_5"; // AudioFormat.JAUDIO_G723_5;
sampleSizeInBits = 5;
break;
default:
throw new BadHeaderException("Unsupported encoding: " +
Integer.toHexString(encoding));
}
int sampleRate = readInt(stream);
if ( sampleRate < 0 )
throw new BadHeaderException("Negative Sample Rate " + sampleRate);
int channels = readInt(stream);
if ( channels < 1 )
throw new BadHeaderException("Number of channels is " + channels);
if (blockSize != -1)
blockSize *= channels;
// System.out.println("blockSize is " + blockSize);
// System.out.println("dataSize is " + dataSize);
// System.out.println("dataSize is " + Integer.toHexString(dataSize));
// System.out.println("sampleRate is " + sampleRate);
skip(stream, headerSize - (6*4));
// bytesPerSecond cannot be negative because of the above checks.
bytesPerSecond = channels * sampleSizeInBits * sampleRate / 8;
int frameSizeInBytes = channels * sampleSizeInBits / 8;
bufferSize = bytesPerSecond;
// System.out.println("bytesPerSecond is " + bytesPerSecond);
// System.out.println("frameSizeInBytes is " + frameSizeInBytes);
// System.out.println("bufferSize is " + bufferSize);
if (dataSize != -1) {
double durationSeconds = (double) dataSize / bytesPerSecond;
duration = new Time(durationSeconds);
}
// System.out.println("duration is " + duration.getSeconds());
boolean signed = true;
format = new AudioFormat(encodingString,
sampleRate,
sampleSizeInBits,
channels,
bigEndian ? AudioFormat.BIG_ENDIAN : AudioFormat.LITTLE_ENDIAN,
signed ? AudioFormat.SIGNED : AudioFormat.UNSIGNED,
frameSizeInBytes * 8,
Format.NOT_SPECIFIED, // No FRAME_RATE specified
Format.byteArray);
// System.out.println("Audio format is " + format);
}
// TODO: Should reset sequence number after a setPosition
// TODO: Optimize
public Time setPosition(Time where, int rounding) {
if (! seekable ) {
return getMediaTime();
}
if (blockSize < 0) {
// System.out.println("ERROR: setPosition not implemented for this encoding "
// + encodingString);
return getMediaTime();
}
long time = where.getNanoseconds();
long newPos;
if (time < 0)
time = 0;
double newPosd = time * bytesPerSecond / 1000000000.0;
double remainder = (newPosd % blockSize);
newPos = (long) (newPosd - remainder);
if (remainder > 0) {
switch (rounding) {
case Positionable.RoundUp:
newPos += blockSize;
break;
case Positionable.RoundNearest:
if (remainder > (blockSize / 2.0))
newPos += blockSize;
break;
}
}
// if ( newPos > maxLocation )
// newPos = maxLocation;
newPos += minLocation;
((BasicTrack) tracks[0]).setSeekLocation(newPos);
if (cacheStream != null) {
synchronized(this) {
// cacheStream.setPosition(where.getNanoseconds());
cacheStream.abortRead();
}
}
return where; // TODO: return the actual time value
}
public Time getMediaTime() {
long location;
long seekLocation = ((BasicTrack) tracks[0]).getSeekLocation();
if (seekLocation != -1)
location = seekLocation - minLocation;
else
location = getLocation(stream) - minLocation;
synchronized(mediaTime) {
mediaTime.set( location / (double) bytesPerSecond );
}
return mediaTime;
}
public Time getDuration() {
return duration;
}
/**
* Returns a descriptive name for the plug-in.
* This is a user readable string.
*/
public String getName() {
return "Parser for AU file format";
}
class AuTrack extends BasicTrack {
private double sampleRate;
private float timePerFrame;
private SettableTime frameToTime = new SettableTime();
AuTrack(AudioFormat format, boolean enabled, Time startTime,
int numBuffers, int bufferSize,
long minLocation, long maxLocation) {
super(AuParser.this,
format, enabled, AuParser.this.duration,
startTime, numBuffers, bufferSize,
AuParser.this.stream, minLocation, maxLocation);
double sampleRate = format.getSampleRate();
int channels = format.getChannels();
int sampleSizeInBits = format.getSampleSizeInBits();
float bytesPerSecond;
float bytesPerFrame;
float samplesPerFrame;
long durationNano = this.duration.getNanoseconds();
}
AuTrack(AudioFormat format, boolean enabled, Time startTime,
int numBuffers, int bufferSize) {
this(format, enabled,
startTime, numBuffers, bufferSize,
0L, Long.MAX_VALUE);
}
}
}
|