FileDocCategorySizeDatePackage
WavParser.javaAPI DocJMF 2.1.1e15245Mon May 12 12:20:52 BST 2003com.sun.media.parser.audio

WavParser

public 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
Constructors Summary
Methods Summary
public javax.media.TimegetDuration()

	return duration;
    
public javax.media.TimegetMediaTime()

	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.StringgetName()
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 voidreadHeader()

	
	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.TimesetPosition(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