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

TonePlayer

public class TonePlayer extends BasicPlayer implements ToneControl
The tone player to play tone sequences.

Fields Summary
private int[]
toneSeq
the tone seq of this tone player
private int
ad
the pointer to the native tone seq data structure.
private int
curTime
the duration of this tone player in milliseconds
private boolean
seqChanged
a flag indicating whether the tone seq has changed
Constructors Summary
Methods Summary
protected voiddoClose()
The worker method to close the player

    
protected voiddoDeallocate()
The worker method to deallocate the player

	toneCommon(ad, 1, 0); // PAUSE
	toneCommon(ad, 5, 0); // CLOSE 
	ad = 0;
    
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.ToneControl") || 
             type.equals("javax.microedition.media.control.VolumeControl")))
	    return this;
	return null;
    
public longdoGetDuration()
Obtain the duration of this player.

return
the duration

	if (curTime >= 0)
	    return (long)(curTime * 1000L);
	else 
	    return TIME_UNKNOWN;
    
public longdoGetMediaTime()
Gets this player's current media time in microseconds.

return
The current media time in microseconds.

	return (toneCommon(ad, 15, 0)*1000L); // GET_CUR_TIME
    
protected voiddoPrefetch()
the worker method to prefetch the player.

	if (ad <= 0) {
	    ad = toneInit(pID);
	    if (ad <= 0)
		throw new MediaException("prefetch failed");
	    
	    if (getLevel() == -1) {
		setLevel(100);
	    } else { 
		toneCommon(ad, 7, getLevel());
	    }

	    if (isMuted()) {
		toneCommon(ad, 7, 0);  // SET_VOLUME
	    }
	}
    
protected voiddoRealize()
the worker method to realize the player

	curTime = 0;
	// if no source stream, player is created from TON_DEVICE_LOCATOR
	// simply return it.
	if (stream == null)
	    return;
	
	// read the whole sequence from the source stream
	int chunksize = 128;
	byte[] tmpseqs = new byte[chunksize];
	byte[] seqs = null;
	// make use of BAOS, since it takes care of growing buffer 
	ByteArrayOutputStream baos = new ByteArrayOutputStream(chunksize);
	
	try {
	    int read;
	    
	    while ((read = stream.read(tmpseqs, 0, chunksize)) != -1) {
		baos.write(tmpseqs, 0, read);
	    }
	    
	    seqs = baos.toByteArray();
	    baos.close();
	    tmpseqs = null;
	    System.gc();
	    
	} catch (IOException ex) {
	    throw new MediaException("fail to read from source");
	}
	this.setSequence(seqs);
    
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.

	// native code takes care of the conversion from 100 to 127.
	toneCommon(ad, 7, vol); // SET_VOL
	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.
throws
MediaException if an error occurs while setting the media time.

	int milli_now = (int)(now /1000);

	if (getState() == STARTED)
	    doStop();
	milli_now = toneCommon(ad, 16, milli_now); // SET_CUR_TIME
	if (getState() == STARTED)
	    doStart();

	return (milli_now * 1000L);
    
protected booleandoStart()
The worker method to start the player.

return
a flag whether the player has been successfully started

	if (seqChanged) {
	    toneSetSeq(ad, toneSeq);
	    seqChanged = false;
	}

	toneCommon(ad, 2, 0); // START
	return true;
    
protected voiddoStop()
The worker method to stop the player

	toneCommon(ad, 1, 0); // PAUSE
    
public java.lang.StringgetContentType()
Return the content type.

return
the wav content type.


                  
       
	chkClosed(true);
	return "audio/x-tone-seq";
    
private voidreportErr(int code)
internal utility method to throw IAE

param
code the error code.

	throw new IllegalArgumentException("bad tone param: err code " + code);
    
public voidsetSequence(byte[] sequence)
Sets the tone sequence.

param
sequence The sequence to set.
exception
IllegalArgumentException Thrown if the sequence is null or invalid.
exception
IllegalStateException Thrown if the Player that this control belongs to is in the PREFETCHED or STARTED state. bad tone seq error code: 1: Mismatched BLOCK_START and BLOCK_END 2: Try to play a block before it is defined 3: Nested block definition 4: Bad parameters, either note is out of range, or note's duration is non-positive. 5: Bad tempo setting 6: bad version number 7: negative block number 8: bad resolution setting 9: bad multiplier setting 10: bad volume setting 11: REPEAT is not followed by tone event 12: can't define VERSION, TEMPO and RESOLUTIOn in the middle of the sequence

	if (this.getState() >= Player.PREFETCHED)
	    throw new 
		IllegalStateException("cannot set seq after prefetched");
	int tempo = 120;
	int resolution = 64;
	int frac = 1;
	int p = 0; 
	try {
	    Stack sp = new Stack();
	    Hashtable blens = new Hashtable();
	    Hashtable pblks = new Hashtable();
	    boolean inblk = false;
	    int found = 0, thisblen = 0, len;
	    byte note;
	    int i;
	    int startseq = 2;
	    len = 0;
	    int tmp = 0;

	    if (sequence[0] != VERSION || sequence[1] != 1) {
		reportErr(6);
	    }
	    if (sequence[startseq] == TEMPO) {
		if (sequence[startseq+1] < 5) {
		    reportErr(5);
		}
		tempo = (sequence[startseq+1] & 0x7f) << 2;
		startseq += 2;
	    }

	    if (sequence[startseq] == RESOLUTION) {
		if (sequence[startseq+1] <= 0)
		    reportErr(8);
		resolution = sequence[startseq+1];
		startseq += 2;
	    }

	    frac = tempo * resolution;
	    for (i = startseq; i < sequence.length; i += 2) {
		note = sequence[i];
		if (note < REPEAT || 
		    ((note >= 0 || note == SILENCE) && sequence[i+1] <= 0)) {
		    reportErr(4);
		}

		switch (note) {
		case BLOCK_START:
		    if (!inblk) {
			if (sequence[i+1] < 0)
			    reportErr(7);
			found = sequence[i+1];
			inblk = true;
			pblks.put(new Integer(found), new Integer(i));
			thisblen = 0;
			continue;
		    } else {
			reportErr(3);
		    }
		    break;

		case BLOCK_END:
		    if (inblk) {// blk end
			if (sequence[i+1] == found) {
			    inblk = false;
			    blens.put(new Integer(found), 
				      new Integer(thisblen));
			} else {
			    reportErr(1);
			}
			continue;
		    } else {
			reportErr(1);
		    }
		    break;
		    
		case REPEAT:
		    if (sequence[i+1] < 2)
			reportErr(9);
		    note = sequence[i+2];
		    if (!(note == SILENCE || note >= 0))
			reportErr(11);
		    break;
		   
		case SET_VOLUME:
		    if (sequence[i+1] < 0 || sequence[i+1] > 100)
			reportErr(10);
		    len += 2;
		    break;
		    
		case PLAY_BLOCK:
		    if (blens.get(new Integer(sequence[i+1])) == null) 
			reportErr(2);
		    
		    tmp = ((Integer)(blens.get(new Integer(sequence[i+1])))).
			intValue();	
		    if (inblk) {
			thisblen += tmp;	
		    } else {
			len += tmp;
		    }
		    break;

		case VERSION:
		case TEMPO:
		case RESOLUTION:
		    reportErr(12);
		    break;
		default: 
		    // SILENCE or normal tone
		    if (inblk) {
			thisblen += 2;
		    } else {
			len += 2;
		    }
		} // switch
	    } // end of for(i)
	    
	    if (inblk) {
		reportErr(1);
	    }
	    
	    // valid tone seq
	    toneSeq = new int[len];

	    curTime = 0;
	    p = 0;

	    i = startseq;
	    int mul = 1;

	    while (i < sequence.length) {
		note =  sequence[i];
		switch (note) {
		case BLOCK_START: // blk definition, start
		    do {
			i += 2;
		    } while (sequence[i] != BLOCK_END);
		    break;
		    
		case PLAY_BLOCK:  // play_blk
		    sp.push(new Integer(i+2));
		    i = ((Integer)pblks.get(new Integer(sequence[i+1]))).
			intValue() + 2;
		    continue;

		case BLOCK_END: // end playing blk
		    i = ((Integer)(sp.pop())).intValue();
		    continue;

		case SET_VOLUME:
		    // 0 <= sequence[i+1] <= 100
		    toneSeq[p++] = SET_VOLUME;
		    toneSeq[p++] = (sequence[i+1] & 0x7f);
		    
		    break;
		    
		case REPEAT:
		    // 2 <= sequence[i+1] <= 127
		    mul = sequence[i+1];
		    break;

		default: // regular tone or SILENCE
		    toneSeq[p++] = sequence[i];
		    // dur as milliseconds
		    toneSeq[p++] += (sequence[i+1]&0x7f) * mul * 240000 / frac;
		    curTime += toneSeq[p-1];
		    mul = 1;
		} // switch
		
		i += 2;
	    } // while
	} catch (IllegalArgumentException ex) {
	    throw ex;
	} catch (Exception ex) {
	    throw new IllegalArgumentException(ex.getMessage());
	}

	seqChanged = true;
    
private native inttoneCommon(int ad, int code, int param)
Utility native functions.

param
ad the pointer to the tone seq data structure
param
code function code.
param
param the parameter for a particular function
return
succeed or not.

private native inttoneInit(int pID)
Initialize the audio device for this tone seq

param
pID the global player ID of this tone player
return
the pointer to the tone seq data structure.

private native voidtoneSetSeq(int ad, int[] toneseq)
Pass the tone seq to the native code.

param
ad the pointer to the tone seq data structure
param
toneseq the tone seq to be set.