FileDocCategorySizeDatePackage
WavPlayer.javaAPI DocJ2ME MIDP 2.014434Thu Nov 07 12:02:26 GMT 2002com.sun.mmedia

WavPlayer

public class WavPlayer extends com.sun.mmedia.BasicPlayer implements Runnable
This class implements the wav (audio/x-wav) audio player.

Fields Summary
private long
duration
the duration of this player
private int
sampleRate
Audio format parameters: sampleRate, channel number, sample size
private int
channels
private int
sampleSizeInBits
private int
bytesPerSecond
byte rate and alignment
private int
blockAlign
private long
lastPos
Last time when the player is stopped, how many samples have been played and the media time at that point
private long
origin
private Object
waveLock
waveout lock obj
private Thread
playThread
play thread obj
private boolean
started
a status flag indicating whether this player is started
private boolean
interrupted
a status flag indicating whether this player has been deallocated
private Object
playLock
a play lock obj
private long
startpt
In source stream, the start position of pcm data
private long
endpt
where the pcm data ends in source stream
private int
peer
the pointer to native wave out data structure.
private int
bufLen
The buffer length to read from source stream every time.
private byte[]
buffer
the data buffer read from source stream
private Object
pauseLock
a pause lock obj
private boolean
canPause
a flag indicating if this player could be paused
private String
errMsg
error string
private byte[]
intArray
temporary buffer
Constructors Summary
Methods Summary
protected voiddoClose()
Close the player.

	// Deallocate would have been called before this.
	// So all the resources should have been released.
    
protected voiddoDeallocate()
Deallocate the exclusing resource.

	// Interrupt the playback loop.

	// If the playThread had not been started, we'll need
	// to explicitly close the device.
	if (state == PREFETCHED && playThread == null) {
	    nCommon(peer, 5, 0); // CLOSE
	    return;
	}

	synchronized (playLock) {
	    interrupted = true;

	    // Wake up the run loop if it was stopped.
	    playLock.notifyAll();

	    // Wait for the playback loop to completely stop before 
	    // returning.  There's a maximum wait limit set here in 
	    // case anything goes wrong.
	    if (playThread != null) {
		try {
		    playLock.wait(5000);
		} catch (Exception e) {}
	    }
	}
    
protected ControldoGetControl(java.lang.String type)
The worker method to actually obtain the control.

param
type the class name of the Control.
return
Control for the class or interface name.

	if ((getState() >= REALIZED) && 
	    type.equals("javax.microedition.media.control.VolumeControl")) {
	    return this;
	}
	return null;
    
public longdoGetDuration()
Get the duration of the media represented by this object. The value returned is the media's duration when played at the default rate. If the duration can't be determined (for example, the media object is presenting live * video) getDuration returns TIME_UNKNWON.

return
A long object representing the duration or TIME_UNKNWON.

	return duration;
    
public longdoGetMediaTime()
Gets this player's current media time in microseconds.

return
The current media time in microseconds.

	long pos = 0;
	long mtime;
	synchronized (waveLock) {
	    if (sampleRate == 0)
		return 0;
	    pos = nCommon(peer, 6, 0); /* SAMPLES_PLAYED */
	}

	// Media time is in micro-seconds
	mtime = ((pos - lastPos) * 1000000L) / sampleRate + origin;

	return (mtime < 0 ? 0 : mtime);

    
protected voiddoPrefetch()
Get the resources ready.

	// Open the audio device.
	synchronized (waveLock) {
	    if (peer == 0) {
		peer = nOpen(sampleRate, sampleSizeInBits, channels);
		if (peer <= 0) {
		    throw new MediaException("can't open audio device");
		}

		bufLen = nCommon(peer, 9, 0);
		buffer = new byte[bufLen];

		if (getLevel() == -1)
		    setLevel(nCommon(peer, 8, 0));
	    }
	}

	if (isMuted()) {
	    nCommon(peer, 7, 0);
	} else {
	    nCommon(peer, 7, getLevel());
	}
    
private booleandoProcess()
Read the data from the source and write them to the waveout in native.

return
the status.

    
                          
       
	int len = 0, wlen = 0;
	
	try {
	    len = readBytes(buffer, 0, bufLen);
	} catch (IOException ioe) {
	    errMsg = ioe.getMessage();
	    return false;
	}

	if (len < 1) {
	    synchronized (pauseLock) {
		canPause = false;
		try {
		    while (nCommon(peer, 4, 0) != 1) // DRAIN
			pauseLock.wait(20);
		} catch (Exception ex) {}
		canPause = true;
		pauseLock.notifyAll();
	    }

	    Thread.yield();
	    started = false;
	    sendEvent(PlayerListener.END_OF_MEDIA, new Long(getMediaTime()));
	    return true;
	}

	synchronized (pauseLock) {
	    canPause = false;
	    while ((wlen = nWrite(peer, buffer, 0, len)) == 0) {
		try {
		    pauseLock.wait(16);
		} catch (Exception ex) {}
	    }
	    canPause = true;
	    pauseLock.notifyAll();
	}

	if (wlen == -1) 
	    return false;
	
	return true;
    
protected voiddoRealize()
Parse the input, realize the player

	try {
	    readHeader();
	    startpt = getStrmLoc();
	} catch (IOException e) {
	    throw new MediaException(e.getMessage());
	}
    
protected intdoSetLevel(int vol)
The worker method to actually obtain the control.

param
vol the volume level to be set.
return
the actual level has been set.

	// 0 <= vol <= 100
	synchronized (waveLock) {
	    if (peer > 0) {
		nCommon(peer, 7, vol); // SETVOL
	    }
	}
	return vol;
    
protected longdoSetMediaTime(long now)
The worker method to actually set player's media time.

param
now The new media time in microseconds.
return
The actual media time set in microseconds.
exception
MediaException Thrown if an error occurs while setting the media time.

	long ret = now;
	try {
	    long pp = (bytesPerSecond * now / 1000000L / blockAlign) *
		blockAlign + startpt;
	    if (getState() == STARTED)
		doStop();
	    nCommon(peer, 3, 0); // FLUSH
	    lastPos = nCommon(peer, 6, 0); // SAMPLES_PLAYED
	    ret = seekStrm(pp);
	    ret = (ret-startpt) * 1000000L / bytesPerSecond;
	    origin = ret;
	    if (getState() == STARTED)
		doStart();
	} catch (Exception e) {
	    throw new MediaException(e.getMessage());
	}

	return ret;
    
protected booleandoStart()
Start the playback.

return
the status if the player has been successfully started.

	if (started)
	    return true;

	started = true;

	// Start the playback loop.
	synchronized (playLock) {
	    if (playThread == null) {
		playThread = new Thread(this);
		playThread.start();
	    } else
		playLock.notifyAll();
	}
	    
	nCommon(peer, 2, 0); // RESUME
	return true;
    
protected voiddoStop()
Stop the playback loop.

	if (!started)
	    return;
	
	started = false;
	synchronized (pauseLock) {
	    while (!canPause)
		try {
		    pauseLock.wait();
		} catch (Exception ex) {
		}
	    nCommon(peer, 1, 0); // PAUSE
	    pauseLock.notifyAll();
	}
    
public java.lang.StringgetContentType()
Return the content type.

return
the wav content type.


                                                                  
             
    
                                                                 
              

                                      
            
    
                  
       
	chkClosed(true);
	return "audio/x-wav";
    
private native intnCommon(int peer, int code, int param)
Utility functions for wave out

param
peer the pointer to the native wave out data structure.
param
code functionality code
param
param parameters for a particular functionality.
return
status.

private native intnOpen(int sampleRate, int bits, int channels)
Open the audio device in a particular format.

param
sampleRate the sampleRate of the intended format
param
bits how many bits per sample of the intended format
param
channels mono or stereo
return
the pointer to the wave out data structure, if succeeded. non-positive number if failed.

private native intnWrite(int peer, byte[] data, int offset, int len)
Pass a data buffer to native code.

param
peer the pointer to the native wave out data structure.
param
data the data buffer to be written to native wave out
param
offset the offset in data buffer.
param
len how many bytes of data to be written.
return
the acutal number of bytes has been written.

private intreadBytes(byte[] array, int offset, int num)
Read bytes from source stream.

param
array the byte array to hold the data
param
offset the offset in the byte array
param
num the number of bytes to be read
return
the actual number of bytes has been read

	if (num == 0) {
	    return 0;
	}

	long cpos = getStrmLoc();
	long available = endpt - cpos;

	if (available <= 0) {
	    return -1;
	}
	
	if (num > available) {
	    num = (int) available;
	}

	int rem = num;
	int read = 0;

	while (rem > 0) {
	    try {
		read = readStrm(array, offset, rem);
	    } catch (IOException e) {
		return -1;
	    }
	    if (read == -1) {	// End of stream
		if (rem == num) {
		    return -1;
		} else {
		    return num - rem;
		}
	    }
	    rem -= read;
	    offset += read;
	}
	return num;
    
protected voidreadHeader()
Parse the Wave audio file header.

	if (readInt() != 0x46464952) // RIFF
	    throw new IOException("malformed wave data");
	
	readInt();
	
	if (readInt() != 0x45564157) // WAVE
	    throw new IOException("malformed wave data");
	
	// 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 (readInt() != 0x20746D66) { // FMT
	    int size = readInt();
	    skipStrm(size);
	}
	
	// Handle Format chunk 'fmt ', formatSize
	int fmtsize = readInt();
	if (fmtsize < 16)
	    throw new IOException("bad fmt chunk");
	fmtsize -= 16;
	
	int encoding = readShort();
	if (encoding != 0x0001) // WF_PCM
	    throw new IOException("only supports PCM");

	channels = readShort();
	sampleRate = readInt();
	bytesPerSecond = readInt();
	blockAlign = readShort();
	sampleSizeInBits = readShort();

	// bytesPerSecond and blockAlign might not accurate in the file
	// need to calculate 
	bytesPerSecond = sampleRate * channels * sampleSizeInBits / 8;
	blockAlign = channels * sampleSizeInBits / 8;

	// skip the rest of the format chunk
	if (fmtsize > 0)
	    skipStrm(fmtsize);

	// Skip all chunks until you reach the 'data' chunk
	while (readInt() != 0x61746164) { // DATA
	    int size = readInt();
	    skipStrm(size);
	}
	
	// Handle Format chunk 'data'
	long dataSize = readInt();
	endpt = getStrmLoc() + dataSize;

	if ((channels * sampleSizeInBits / 8) == blockAlign) {
	    duration = (dataSize*1000000L)/bytesPerSecond;
	} else {
	    duration = TIME_UNKNOWN;
	}

	return;
    
private intreadInt()
Read an integer from source stream

return
the integer read.
throws
IOExeption if there is an error


                          
         
	if (readBytes(intArray, 0, 4) < 4)
	    throw new IOException("malformed wave data");
	return ((intArray[3] & 0xFF) << 24) |
	    ((intArray[2] & 0xFF) << 16) |
	    ((intArray[1] & 0xFF) << 8) |
	    (intArray[0] & 0xFF);
    
private shortreadShort()
Read a short from source stream

return
the short read
throws
IOException if there is an error

	if (readBytes(intArray, 0, 2) < 2)
	    throw new IOException("malformed wave data");
	return (short) (((intArray[1] & 0xFF) << 8) |
		(intArray[0] & 0xFF));
    
public voidrun()
Main process loop driving the media flow.

	boolean statusOK = true;

	while (true) {

	    while (!interrupted && started && statusOK) {
		statusOK = doProcess();
		Thread.yield();
	    }

	    synchronized (playLock) {
		if (interrupted || !statusOK)
		    break;

		try {
		    playLock.wait();
		} catch (Exception ex) {}
	    }

	} // end of while (true)

	// Close the audio device, exit safely out of the process loop.

	nCommon(peer, 3, 0); // FLUSH
	synchronized (waveLock) {
	    nCommon(peer, 5, 0); // CLOSE
	    peer = 0;
	    interrupted = started = false;
	}
	lastPos = 0;

	synchronized (playLock) {
	    playThread = null;
	    // Notify the blocking deallocate that we are done with
	    // the process loop.
	    playLock.notifyAll();
	}

	if (!statusOK) {
	    if (stream != null) {
		try {
		    stream.close();
		} catch (IOException ex) {}
	    }
	    sendEvent(PlayerListener.ERROR, errMsg);
	}