Fields Summary |
---|
private long | durationthe duration of this player |
private int | sampleRateAudio format parameters: sampleRate, channel number, sample size |
private int | channels |
private int | sampleSizeInBits |
private int | bytesPerSecondbyte rate and alignment |
private int | blockAlign |
private long | lastPosLast time when the player is stopped, how many samples have been
played and the media time at that point |
private long | origin |
private Object | waveLockwaveout lock obj |
private Thread | playThreadplay thread obj |
private boolean | starteda status flag indicating whether this player is started |
private boolean | interrupteda status flag indicating whether this player has been deallocated |
private Object | playLocka play lock obj |
private long | startptIn source stream, the start position of pcm data |
private long | endptwhere the pcm data ends in source stream |
private int | peerthe pointer to native wave out data structure. |
private int | bufLenThe buffer length to read from source stream every time. |
private byte[] | bufferthe data buffer read from source stream |
private Object | pauseLocka pause lock obj |
private boolean | canPausea flag indicating if this player could be paused |
private String | errMsgerror string |
private byte[] | intArraytemporary buffer |
Methods Summary |
---|
protected void | doClose()Close the player.
// Deallocate would have been called before this.
// So all the resources should have been released.
|
protected void | doDeallocate()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 Control | doGetControl(java.lang.String type)The worker method to actually obtain the control.
if ((getState() >= REALIZED) &&
type.equals("javax.microedition.media.control.VolumeControl")) {
return this;
}
return null;
|
public long | doGetDuration()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 duration;
|
public long | doGetMediaTime()Gets this player's 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 void | doPrefetch()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 boolean | doProcess()Read the data from the source and write them to the waveout in native.
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 void | doRealize()Parse the input, realize the player
try {
readHeader();
startpt = getStrmLoc();
} catch (IOException e) {
throw new MediaException(e.getMessage());
}
|
protected int | doSetLevel(int vol)The worker method to actually obtain the control.
// 0 <= vol <= 100
synchronized (waveLock) {
if (peer > 0) {
nCommon(peer, 7, vol); // SETVOL
}
}
return vol;
|
protected long | doSetMediaTime(long now)The worker method to actually set player's 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 boolean | doStart()Start the playback.
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 void | doStop()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.String | getContentType()Return the content type.
chkClosed(true);
return "audio/x-wav";
|
private native int | nCommon(int peer, int code, int param)Utility functions for wave out
|
private native int | nOpen(int sampleRate, int bits, int channels)Open the audio device in a particular format.
|
private native int | nWrite(int peer, byte[] data, int offset, int len)Pass a data buffer to native code.
|
private int | readBytes(byte[] array, int offset, int num)Read bytes from source stream.
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 void | readHeader()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 int | readInt()Read an integer from source stream
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 short | readShort()Read a short from source stream
if (readBytes(intArray, 0, 2) < 2)
throw new IOException("malformed wave data");
return (short) (((intArray[1] & 0xFF) << 8) |
(intArray[0] & 0xFF));
|
public void | run()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);
}
|