WavParserpublic class WavParser extends BasicPullParser Only 'fmt ' and 'data' chunks supported.
Not computing header (length of the header) as it is misleading.
You can have for example 'fmt ' chunk 'fact' chunk and 'data'
chunk.
Changes:
WAVE_FORMAT_GSM (0x31) is now WAVE_FORMAT_GSM610 as there is also
an MS GSM
sampleSizeInBits field not applicable for GSM610.
For WAVE_FORMAT_ADPCM, WAVE_FORMAT_DVI_ADPCM and WAVE_FORMAT_GSM610
the extra fields are now read. The useful extra field here is
samples per block. |
Fields Summary |
---|
private Time | duration | private WavAudioFormat | format | private Track[] | tracks | private int | numBuffers | private int | bufferSize | private int | dataSize | private SettableTime | mediaTime | private int | encoding | private String | encodingString | private int | sampleRate | private int | channels | private int | sampleSizeInBits | private int | blockAlign | private int | samplesPerBlock | private long | minLocation | private long | maxLocation | private double | locationToMediaTime | private double | timePerBlockNano | private PullSourceStream | stream | private static ContentDescriptor[] | supportedFormat |
Methods Summary |
---|
public javax.media.Time | getDuration()
return duration;
| public javax.media.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 * locationToMediaTime);
}
return mediaTime;
| public java.lang.String | getName()Returns a descriptive name for the plug-in.
This is a user readable string.
return "Parser for WAV file format";
| public javax.media.protocol.ContentDescriptor[] | getSupportedInputContentDescriptors()
return supportedFormat;
| public javax.media.Track[] | getTracks()
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);
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 WavTrack(format,
/*enabled=*/ true,
new Time(0),
numBuffers,
bufferSize,
minLocation,
maxLocation
);
return tracks;
| private void | readHeader()
String magicRIFF = readString(stream);
// System.out.println("magicRIFF is " + magicRIFF);
if (!(magicRIFF.equals("RIFF"))) {
// System.out.println("Fatal Error: Expected magic string RIFF, got " + magicRIFF);
throw new BadHeaderException("WAVE Parser: expected magic string RIFF, got "
+ magicRIFF);
}
int length = readInt(stream, /* bigEndian = */ false);
// System.out.println("length is " + length);
String magicWAVE = readString(stream);
// System.out.println("magicWAVE is " + magicWAVE);
if (!(magicWAVE.equals("WAVE"))) {
// System.out.println("Fatal Error: Expected magic string WAVE, got " +
// magicWAVE);
throw new BadHeaderException("WAVE Parser: expected magic string WAVE, got "
+ magicWAVE);
}
length += 8; // Add 4 bytes for RIFF and WAVE fields
// Only the required chunks 'fmt ' and 'data' are supported.
// There are no restrictions upon the order of the chunks within a WAVE file,
// with the exception that the Format chunk must precede the Data chunk.
// Skip all chunks until you reach the 'fmt ' chunk
while ( ! (readString(stream)).equals("fmt ") ) {
int size = readInt(stream, /* bigEndian = */ false);
skip(stream, size);
}
// Handle Format chunk 'fmt '
int formatSize = readInt(stream, /*bigEndian = */false);
int remainingFormatSize = formatSize;
// System.out.println("formatSize is " + formatSize);
// formatSize should be atleast 16
if (formatSize < 16) {
// TODO: throw new BadHeaderException
// Is this necessary? Check.
}
encoding = readShort(stream, /* bigEndian = */ false);
encodingString = (String) WavAudioFormat.formatMapper.get(new Integer(encoding));
if (encodingString == null) {
encodingString = "unknown";
}
channels = readShort(stream, /* bigEndian = */ false);
sampleRate = readInt(stream, /* bigEndian = */ false);
int bytesPerSecond = readInt(stream, /* bigEndian = */ false);
blockAlign = readShort(stream, /* bigEndian = */ false);
sampleSizeInBits = readShort(stream, /* bigEndian = */ false);
if (encoding == WavAudioFormat.WAVE_FORMAT_MPEG_LAYER3) {
// Some files have sampleSizeInBits as 0
sampleSizeInBits = 16;
}
samplesPerBlock = -1;
remainingFormatSize -= 16;
{ // Workaround for files with bad headers
/**
* Some files, for e.g TooGood_22_8bit.wav and
* US3_44_Stereo.wav, have inconsistent fmt chunk
* They are uncompressed, yet have fmt size of 18
* and claim extra header size of 40.
* Should throw a BadHeaderException. But these
* files play fine on Windows player. Hence the
* workaround to makes these and potentially others
* like this play.
*/
if ( ((remainingFormatSize > 0) &&
encoding == WavAudioFormat.WAVE_FORMAT_PCM) ||
(remainingFormatSize <= 2) ) {
skip(stream, remainingFormatSize);
remainingFormatSize = 0;
}
}
byte[] codecSpecificHeader = null;
int extraFieldsSize = 0;
if (remainingFormatSize >= 2) {
extraFieldsSize = readShort(stream, /* bigEndian = */ false);
remainingFormatSize -= 2;
if (extraFieldsSize > 0) {
codecSpecificHeader = new byte[extraFieldsSize];
readBytes(stream, codecSpecificHeader, codecSpecificHeader.length);
remainingFormatSize -= extraFieldsSize;
}
}
switch (encoding) {
case WavAudioFormat.WAVE_FORMAT_ADPCM:
case WavAudioFormat.WAVE_FORMAT_DVI_ADPCM:
case WavAudioFormat.WAVE_FORMAT_GSM610:
{
if (extraFieldsSize < 2) {
throw new BadHeaderException("msadpcm: samplesPerBlock field not available");
}
samplesPerBlock = BasicPullParser.parseShortFromArray(codecSpecificHeader,
/* bigEndian = */ false);
locationToMediaTime = (double) samplesPerBlock / (sampleRate * blockAlign);
}
break;
default: // CHECK
locationToMediaTime = 1.0 / (sampleRate * blockAlign);
break;
}
if (remainingFormatSize < 0) {
// TODO: Should throw BadHeaderException
throw new BadHeaderException("WAVE Parser: incorrect chunkSize in the fmt chunk");
}
if (remainingFormatSize > 0) {
skip(stream, remainingFormatSize);
}
// Skip all chunks until you reach the 'data' chunk
while ( ! (readString(stream)).equals("data") ) {
int size = readInt(stream, /* bigEndian = */ false);
skip(stream, size);
}
// Handle Format chunk 'data'
dataSize = readInt(stream, /* bigEndian = */ false);
// Compute the maximum number of integral
// blocks that can stored within the averageBytesPerSecond.
if ( blockAlign != 0 )
if ( bytesPerSecond < dataSize )
bufferSize = bytesPerSecond -
(bytesPerSecond % blockAlign);
else
bufferSize = dataSize - (dataSize % blockAlign);
else
if (bytesPerSecond < dataSize)
bufferSize = bytesPerSecond;
else
bufferSize = dataSize;
// There is only 1 track.
double durationSeconds = -1;
// if (samplesPerBlock > 0) {
// // ADPCM or DVI_ADPCM or GSM610
// durationSeconds = (float)dataSize/blockAlign*samplesPerBlock/sampleRate;
// timePerBlockNano = (samplesPerBlock * 1.0E9) / sampleRate;
// // System.out.println("timePerBlockNano is " + timePerBlockNano);
// } else if ( encoding == WavAudioFormat.WAVE_FORMAT_DSPGROUP_TRUESPEECH ) {
// timePerBlockNano = (blockAlign * 1.0E9 / bytesPerSecond);
// durationSeconds = (float)dataSize/bytesPerSecond;
// } else {
// durationSeconds = (float)dataSize/bytesPerSecond;
// }
if ( (channels * sampleSizeInBits / 8) == blockAlign ) {
/**
* Will Handle formats like
* WAVE_FORMAT_PCM, WAVE_FORMAT_MULAW, WAVE_FORMAT_ALAW
* WAVE_IBM_FORMAT_MULAW, WAVE_IBM_FORMAT_ALAW etc.
*/
durationSeconds = (float)dataSize/bytesPerSecond;
} else if (samplesPerBlock > 0) {
// ADPCM or DVI_ADPCM or GSM610
durationSeconds = (float)dataSize/blockAlign*samplesPerBlock/sampleRate;
timePerBlockNano = (samplesPerBlock * 1.0E9) / sampleRate;
} else {
// WAVE_FORMAT_DSPGROUP_TRUESPEECH and others
timePerBlockNano = (blockAlign * 1.0E9 / bytesPerSecond);
durationSeconds = (float)dataSize/bytesPerSecond;
}
duration = new Time(durationSeconds);
// System.out.println("Encoding: " + encoding );
// System.out.println("Encoding String: " + encodingString );
// System.out.println("Channels: " + channels );
// System.out.println("Sample Rate: "+ sampleRate);
// System.out.println("bytesPerSec: " + bytesPerSecond);
// System.out.println("Block Align: " + blockAlign);
// System.out.println("sample size in bits : " + sampleSizeInBits );
// System.out.println("samplesPerBlock (not applicable if -ve) is " + samplesPerBlock);
// System.out.println("durationSeconds is " + durationSeconds);
// TODO: CHECK
boolean signed;
if (sampleSizeInBits > 8)
signed = true;
else
signed = false;
format = new WavAudioFormat(encodingString,
sampleRate,
sampleSizeInBits,
channels,
/*frameSizeInBits=*/blockAlign * 8,
bytesPerSecond,
AudioFormat.LITTLE_ENDIAN,
signed ? AudioFormat.SIGNED : AudioFormat.UNSIGNED,
Format.NOT_SPECIFIED, // No FRAME_RATE specified
Format.byteArray,
codecSpecificHeader);
// System.out.println("Audio format is " + format);
| public javax.media.Time | setPosition(javax.media.Time where, int rounding)
if (! seekable ) {
return getMediaTime();
}
long time = where.getNanoseconds();
long newPos;
if (time < 0)
time = 0;
// if ( (channels * sampleSizeInBits / 8) == blockAlign ) {
if ( timePerBlockNano <= 0 ) {
/**
* Will Handle formats like
* WAVE_FORMAT_PCM, WAVE_FORMAT_MULAW, WAVE_FORMAT_ALAW
* WAVE_IBM_FORMAT_MULAW, WAVE_IBM_FORMAT_ALAW etc.
*/
// TODO: Precalculate constant expressions
int bytesPerSecond = sampleRate * blockAlign;
double newPosd = time * sampleRate * blockAlign / 1000000000.0;
double remainder = (newPosd % blockAlign);
newPos = (long) (newPosd - remainder);
if (remainder > 0) {
switch (rounding) {
case Positionable.RoundUp:
newPos += blockAlign;
break;
case Positionable.RoundNearest:
if (remainder > (blockAlign / 2.0))
newPos += blockAlign;
break;
}
}
} else {
// ADPCM, DVI_ADPCM, GSM610 and others where
// (channels * sampleSizeInBytes) != blockAlign
double blockNum = time / timePerBlockNano;
int blockNumInt = (int) blockNum;
double remainder = blockNum - blockNumInt;
if (remainder > 0) {
switch (rounding) {
case Positionable.RoundUp:
blockNumInt++;
break;
case Positionable.RoundNearest:
if (remainder > 0.5)
blockNumInt++;
break;
}
}
newPos = blockNumInt * blockAlign;
}
// 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
|
|