// player.java // // Description // =========== // // Plays a loop of a an 8 bit, 8K Mono AU file over the web page. Allows // the viewer to start & stop the player using a play/pause button displayed // on the page // // Revision History // ================ // // 15-Feb-1996/SBH Very first rough hack. No error checks. // (0.0 alpha) // 16-Feb-1996/SBH Added start/stop control. // (0.1 alpha) // 21-Feb-1996/SBH Added loop=YES/NO (default is YES). Note, if in // (0.2alpha) NO mode, button does not reset when finished playing. // 23-Feb-1996/SBH Added Timer.class, which now means that the button // (1.0) will revert to a 'play' button a specified time after // the sound starts playing. Ideally, this should be a few // milliseconds longer than the file plays for. // // // // // ------------------------------------------------------------ import java.applet.Applet; import java.applet.AudioClip; import java.awt.*; import java.util.Date; import java.lang.Thread; // ------------------------------------------------------------ // This class is used to start another thread, which will run for // a specified number of milliseconds, before calling the parent's // expireTimer() function. // // A seperate thread is used to allow the parent to continue receiving // mouse messages whilst we implement a delay until the current play // has completed. Ideally, this class should be more generic and just send // an event to it's parent...but that comes later. class timer extends Thread { private player myParent = null; // Parent to call back when expired private int iMilliSecs; // Time to wait private int iReference; // Our reference - used to determine // which we are if multiple timers runnig public timer (player _myParent, int _iMilliSecs, int _iReference ) { myParent = _myParent; iMilliSecs = _iMilliSecs; iReference = _iReference; }//timer() // ------------------------------------------------------------ public void run() { // we come here when our .start() method is called. try { sleep(iMilliSecs); } catch (InterruptedException e) { } myParent.expireTimer(iReference); // The object will be discarded when we reach here } }//timer.class //----------------------------------------------------------------- public class player extends java.applet.Applet implements Runnable { String szVersion = "player.class v1.0 (c)1996 Steve Hill 100144.3120@compuserve.com"; Thread Thread2; // Our thread AudioClip acMySound; // The Audio clip. Should be MONO, 8000 Samples/sec Graphics gcOn; // Graphics context (prevents flicker on redraw) boolean bPlaying = true; // Are we playing or silent? boolean bMouseDown = false; // Is the button up or down? boolean bLoopMode = true; // Loop sound, or single shot? boolean bPlayImmediate = true; // if true, play immediately else wait // until play pressed Rectangle buttonPlay = new Rectangle(0,0,20,20); //size of button int iTimeToPlay; // Length of .AU in Milliseconds int iNextPlayReference = 0; // Next timer reference int iPlayingRef =0; // Reference currently being played public void init() { // get our graphics context...we do this to avoid flicker on paint gcOn = getGraphics(); }//init() // ------------------------------------------------------------ public String getAppletInfo() //Description //=========== // // This function is called in appletviewer if the user asks for applet info // it should provide details of applets origin. { return(szVersion); }//getAppletInfo() // ------------------------------------------------------------ public String[][] getParameterInfo() //Description //=========== // // This function is called in appletviewer if the user asks for applet info // it should provide details of applets usage (ie, paramters) { String pinfo[][] = { {"copyright", "string", szVersion}, {"au_file", "URL", "URL of .AU file to play"}, {"loop", "YES|NO (default=YES)", "Played in continous loop?"}, {"init", "PLAY|PAUSE(default=PLAY)","Start condition"}, {"playtime", "integer","Approx Length of AudioClip in MilliSecs. Required if loop=NO"} }; return (pinfo); } // ------------------------------------------------------------ void InitAudio() { // do we start playing immediately? String szInit = getParameter("init"); if (szInit == null) { szInit = "PLAY"; } if (szInit.toUpperCase().equals("PAUSE")) { bPlaying = false; } else { bPlaying = true; } // This fetches the file, and won't return until loaded (unlike image) acMySound = getAudioClip(getDocumentBase(), getParameter("au_file")); // Do we play a loop or not? String szLoop = getParameter("loop"); if (szLoop == null) szLoop = "YES"; if (!szLoop.toUpperCase().equals("NO")) { if (bPlaying) { acMySound.loop(); } bLoopMode=true; } else { //if we're in loop mode, read the file playtime (in millisecs) String szLength = getParameter("playtime"); //convert to an integer, default to 1 second if not specified. if (szLength != null) { try { iTimeToPlay = Integer.parseInt(szLength); } catch (Exception e) { iTimeToPlay = 1000; System.out.println("Invalid playtime specified. Defaulted to " + iTimeToPlay +" MilliSeconds."); } } else { System.out.println("No playtime specified. Defaulted to " + iTimeToPlay +" MilliSeconds."); } bLoopMode=false; if (bPlaying) { playSound(); } } }//InitAudio() // ------------------------------------------------------------ void drawButtonEdge(boolean bUp, Rectangle myRect) { //Draws a blank button, in an up or down state. int iMidX,iMidY,x,y,w,h; x = myRect.x; y = myRect.y; w = myRect.width; h = myRect.height; //blank the middle gcOn.setColor(Color.lightGray); gcOn.fillRect(x+1,y+1,w-1,h-1); // Draw the black bit.. gcOn.setColor(Color.black); gcOn.drawRect(x,y,w,h); // and the white bit if (!bUp) { iMidX = x; iMidY = y; } else { iMidX = x+w; iMidY = y+h; } gcOn.setColor(Color.white); gcOn.drawLine(x,y+h,iMidX,iMidY); gcOn.drawLine(x+w,y,iMidX,iMidY); }//drawButtonEdge() // ------------------------------------------------------------ void drawButtons() { // Draws the button. The symbol (play/pause) and up/down appearance // change dependendant on bPlaying & bMouseDown int x,y,off; drawButtonEdge(bMouseDown,buttonPlay); // We offset the symbol to the right and down by one pixel if the button // is depressed. If it's happy - no offset. if (bMouseDown) { off=1; } else { off=0; } // Draw different symbol pause/play as necessary.. if (bPlaying) { drawStop(8+off,5+off); } else { drawPlay(8+off,5+off); } }//drawButtons() // ------------------------------------------------------------ public void expireTimer(int iReference) { // if this Timer was the one that was started when we last pressed // the play button, and we are still playing, then clear the bplaying // flag and redraw the button. if ((iReference == iPlayingRef) && bPlaying) { bPlaying = false; repaint(); } }//expireTimer() // ------------------------------------------------------------ public void paint(Graphics g) { // pretty obvious really... drawButtons(); }//paint() // ------------------------------------------------------------ public void start() { // This is our main thread Thread2 = new Thread(this); Thread2.start(); }//start() // ------------------------------------------------------------ public void stop() { Thread2.stop(); }//stop() // ------------------------------------------------------------ void playSound() { acMySound.play(); // start a separate thread to call back when we have finished playing timer timerPlay = new timer(this,iTimeToPlay,++iNextPlayReference); // keep a record of which play this is - it may be started/stopped several // times before the timer expires. iPlayingRef = iNextPlayReference; // and start the timer running.. timerPlay.start(); }//playSound() // ------------------------------------------------------------ void drawStop(int x,int y) { // draws two vertical back bars gcOn.setColor(Color.black); gcOn.fillRect(x,y,2,10); gcOn.fillRect(x+4,y,2,10); }//drawStop() // ------------------------------------------------------------ void drawPlay(int x,int y) { // draws a '>' play symbol gcOn.setColor(Color.black); gcOn.drawLine(x,y,x+3,y+5); gcOn.drawLine(x+1,y,x+4,y+5); gcOn.drawLine(x,y+10,x+3,y+5); gcOn.drawLine(x+1,y+10,x+4,y+5); }//drawPlay() // ------------------------------------------------------------ public boolean mouseDown(Event evt,int x, int y) { //When the mouse goes, draw the button in a 'down' position. if (buttonPlay.inside(x,y)) { bMouseDown = true; } drawButtons(); return (true); }//mouseDown() // ------------------------------------------------------------ public boolean mouseUp(Event evt,int x, int y) { //if we are still over the button, and it was down, then we toggle //play state. if (buttonPlay.inside(x,y) && bMouseDown) bPlaying = !bPlaying; bMouseDown = false; // redraw button. Symbol & up/down state have changed drawButtons(); // now determine action to take if (bPlaying) if (bLoopMode) acMySound.loop(); else playSound(); else { acMySound.stop(); // we rest this so that callback from timer has no effect iPlayingRef = 0; } return (true); }//mouseUp() // ------------------------------------------------------------ public void run() { System.out.println(szVersion); InitAudio(); drawButtons(); }//run() }//player.class //EOF