001/*
002 *  $URL: svn://fred.webarts.bc.ca/open/trunk/projects/jOggPlayer/ca/bc/webarts/JOggPlayerListener.java $
003 *  $Revision: 1358 $
004 *  $Date: 2020-06-20 14:25:16 -0700 (Sat, 20 Jun 2020) $
005 *  $Author: tgutwin $
006 */
007/*
008 *  TOM Gutwin's fork of...
009 *  JOggPlayer -- pure Java Ogg Vorbis player
010 *    into a nonGUI singleton class that functions like a Listener to
011 *      take commands and play tunes from the playlist
012 *  Written by: Tom Gutwin WebARTS Design
013 *  Copyright (C) 2020 WebARTS Design, Burnaby Canada
014 *
015 *  It is based on Java Vorbis Ogg Audio/Decoder/Player code from JCraft,Inc.
016 *  Copyright (C) 2000 ymnk, JCraft,Inc.
017 *
018 *  This program is free software; you can redistribute it and/or modify
019 *  it under the terms of the GNU General Public License as published by
020 *  the Free Software Foundation; either version 2 of the License, or
021 *  (at your option) any later version.
022 *
023 *  This program is distributed in the hope that it will be useful,
024 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
025 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
026 *  GNU General Public License for more details.
027 *
028 *  You should have received a copy of the GNU General Public License
029 *  along with this program; if not, write to the Free Software
030 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
031 */
032/*
033 *  Original Java Vorbis Ogg Audio/Decoder/Player code Written by:
034 *  2000 ymnk<ymnk@jcraft.com>
035 *  Copyright (C) 2000 ymnk, JCraft,Inc.
036 *  http://www.jcraft.com
037 *
038 *  Many thanks to
039 *  Monty <monty@xiph.org> and
040 *  The XIPHOPHORUS Company http://www.xiph.org/ .
041 *  JOrbis has been based on their awesome works, Vorbis codec and
042 *  JOrbisPlayer depends on JOrbis.
043 *
044 */
045
046package ca.bc.webarts;
047
048import com.jcraft.jogg.*;
049
050import com.jcraft.jorbis.*;
051import java.applet.*;
052import java.awt.*;
053import java.awt.event.*;
054import java.io.*;
055import java.net.*;
056
057import java.util.*;
058
059import javax.sound.midi.*;
060import javax.sound.sampled.*;
061import javax.swing.*;
062
063import ca.bc.webarts.widgets.Util;
064
065
066/**
067 *  A Singleton JOggPlayer that does NOT function from the commandline or gui;
068 *  only as an intantiated class from another app such as a TCP PortListener.
069 * <br><br><b><u>Example Method in another class:</u></b><br>
070 * <pre>
071 *   protected int runWithJOggPlayer(String[] cmdArgs)
072 *   {
073 *      int retVal = 1;
074 *      JOggPlayerListener player_ = JOggPlayerListener.getInstance();
075 *      String piTunePath = "";
076 *      if(!"stop".equalsIgnoreCase(cmdArgs[0]))
077 *      {
078 *        piTunePath = "/mnt/nas/snd/ogg"+"/"+cmdArgs[1];
079 *        if (cmdArgs.length &gt;1)
080 *        {
081 *          for (int i = 1; i &lt; cmdArgs.length; i++)
082 *          {
083 *            piTunePath = piSndOggRootDir_+"/"+cmdArgs[i];
084 *            player_.addToPlaylist(piTunePath);
085 *          }
086 *        }
087 *        player_.play_sound(); // this send the message to start the player thread and return here
088 *        retVal = 0;
089 *      }
090 *      else  // stop was called
091 *      {
092 *        // call stop
093 *        player_.signalStopPlay();
094 *      }
095 *      // because all the above calls send signals to JOggPlayerListener to execute in a separate Thread
096 *      // they quickly return back here
097 *      // so, we can do extra stuff here while we wait
098 *          if(retVal==0)
099 *          {
100 *            System.out.println("\n   waiting until song"+("tunes".equalsIgnoreCase(cmdArgs[0])?"s":"")+
101 *                               " are done");
102 *
103 *            while(player_ &#33;&#61; null &amp;&amp; player_.isPlaying())
104 *            {
105 *              ca.bc.webarts.widgets.Util.sleep(5*1000);
106 *              System.out.print(player_.retrieveCurrentTunePlayTimeInMiliSeconds()/1000+ "("+ player_.retrieveTuneProgressRatio()+")   ");
107 *            }
108 *            System.out.println("\nJOggPlayer is done.");
109 *          }
110 *      return retVal;
111 *   }</pre>
112 * <br>
113 * <b>See a pre-built sample commandline app called {@link JOggPlayerCommandline JOggPlayerCommandline} that uses this class</b>
114 * <br>
115 * See https://dzone.com/articles/singleton-in-java for Singleton implementation style.
116 *
117 * @author    tgutwin
118 */
119public class JOggPlayerListener implements Runnable
120{
121
122  /** Debug level.  0 is silent, 1 is error only; 2 is basic info, 3 is verbose, 4 is all **/
123  int debug_ = 2;
124
125  /**
126   * The File Load Buffer multiplier - the num kbites to take .
127   */
128  final static int BUFFER_MULTIPLE = 4;
129
130  /**  The Play buffer size. */
131  final static int BUFSIZE = BUFFER_MULTIPLE * 1024 * 3;
132
133  /**  the size (bytes) to allocate for the pcm conversion buffer. */
134  static int convsize_ = BUFSIZE * 2;
135
136  /**  The pcm conversion buffer. */
137  static byte[] convbuffer_ = new byte[convsize_];
138
139  /**  Description of the Field */
140  private int RETRY = 3;
141
142  /**  Description of the Field */
143  int retry = RETRY;
144
145  /**  The Ogg Vorbis stream of data. */
146  InputStream oggVorbisBitStream_ = null;
147
148  /**  The (optional) UDP port to retrieve the Vorvis data FROM. */
149  int udp_port = -1;
150  /**  Description of the Field */
151  String udp_baddress = null;
152
153  /**  Description of the Field */
154  String playlistfile = "playlist";
155
156  /**  The JCraft JOgg SyncState. */
157  SyncState oy;
158  /**   The JCraft JOgg  StreamState. */
159  StreamState os;
160  /**  Vorbis Ogg Data. */
161  Page og;
162  /**  Vorbis Ogg Data. */
163  Packet op;
164  /**  Vorbis Ogg Data. */
165  Info vi;
166  /**  The currently being read Vorbis Comment. */
167  Comment vc;
168  /**  Vorbis Ogg Data. */
169  DspState vd;
170  /**  Vorbis Ogg Data. */
171  Block vb;
172
173  /**
174   * Holds the current tune Vorbis Comments.
175   */
176  Vector<String> songComments_ = new <String>Vector();
177
178
179  /**  Description of the Field */
180  byte[] buffer = null;
181  /**  current bytes being worked on.*/
182  int bytes = 0;
183
184  /**  Description of the Field */
185  int rate = 0;
186  /**  Description of the Field */
187  int channels = 0;
188  /**  Description of the Field */
189  int left_vol_scale = 100;
190  /**  Description of the Field */
191  int right_vol_scale = 100;
192  /**  Description of the Field */
193  SourceDataLine outputLine = null;
194  /**  Description of the Field */
195  String current_source = null;
196
197  int current_songDuration_ = 0;
198
199  /**  Description of the Field */
200  int frameSizeInBytes;
201  /**  Description of the Field */
202  int bufferLengthInBytes;
203
204  /**  Description of the Field */
205  boolean playonstartup = true;
206
207  boolean playing_ = false;
208
209  /**
210   * Signals the playing loop to jump to next tune in playlist.
211   */
212  private boolean signalNextInPlaylist_ = false;
213  /**
214   * Signals the playing loop to jump to previous tune in playlist.
215   */
216  private boolean signalPreviousInPlaylist_ = false;
217  /**
218   * Signals the playing loop to pause in a sleep loop until !loopPaused_.
219   */
220  private boolean loopPaused_ = false;
221  /**
222   * Signals the playing loop to stop.
223   */
224  private boolean loopStopped_ = false;
225
226  /**
227     * These variables are used to distinguish stopped, paused, playing states.
228     * We need them to control Thread.
229     */
230    public static final int UNKNOWN = -1;
231    public static final int PLAYING = 0;
232    public static final int PAUSED = 1;
233    public static final int STOPPED = 2;
234    public static final int OPENED = 3;
235    public static final int SEEKING = 4;
236    private int m_status = UNKNOWN;
237
238  /**
239   * Description of the Field
240   */
241  boolean timerRunning_ = false;
242
243  Date date = new Date();
244
245  /** The bytes retrieved from current playing tune. It is used to calculate progress through the tune file. */
246  int progressCount_ = 0;
247
248  public static String albumCoverURLBase_ = "https://fred.webarts.bc.ca:9443/tunes/tunes";
249
250    /**
251     * The thread that watches the time the song has been playing.
252     * Handles the Timer display in its own Thread.<P>
253     *
254     * Why not use the Timer class in the JDK??? Because it is since JDK 1.3.
255     */
256    class TimerThread extends Thread
257    {
258      int min = 0;
259      int sec = 0;
260      long pausedTime = 0l;
261      int pausedMin = 0;
262      int pausedSec = 0;
263      Date runningDate;
264      long runningTime = 0l;
265      int playTime = 0;
266      /** the current tune's length /duration in Seconds. **/
267      int songDuration = 0;
268      long startPauseTime = 0l;
269      long lastPauseTime = 0l;
270      long newPauseTime = 0l;
271      long historyPauseTime = 0l;
272      int diffDate = 0;
273      String timeString_ = "00:00";
274
275
276      /** the playtime in mSec.
277        *
278        * @return int the playing time in milli seconds
279        **/
280      public int getPlayTime()
281      {
282        return playTime;
283      }
284
285
286      /** the length of the current tune in mSec. NOT Truly Implemented YET.
287        *
288        * @return int the length of the current tune in milli seconds
289        **/
290      public int getSongDurationInMiliSeconds()
291      {
292        songDuration = ((int) (playTime / 1000));  //update it first
293        return songDuration;
294      }
295
296
297      /** The progress through the song ratio between 0 to 1000.
298        *
299        * @return int between 0 to 1000
300        **/
301      public int getSongProgressRatio()
302      {
303        String item = (String) playlist_.get(currentPlaylistIndex_);
304        int currTuneFileLength = (int) (new File(item)).length();
305
306        return progressCount_ / currTuneFileLength ; // * ((int) (playTime / 1000))/;
307      }
308
309
310      public void run()
311      {
312        date = new Date();
313        timerRunning_ = true;
314        while (playing_ && timerRunning_)
315        {
316          ca.bc.webarts.widgets.Util.sleep(490);
317          runningDate = new Date();
318          runningTime = runningDate.getTime();
319          diffDate = (int) ((runningTime - date.getTime()));
320          if (getLoopPaused())
321          {
322            pausedTime = runningTime - startPauseTime;
323            newPauseTime = runningTime - lastPauseTime;
324            historyPauseTime += newPauseTime;
325            pausedMin = (int) historyPauseTime / 60000;
326            pausedSec = (int) (historyPauseTime / 1000 - pausedMin * 60);
327
328            // Flash Tne Paused Message
329            if (((int) (historyPauseTime / 1000)) % 2 == 0)
330            {
331              //timeLabelValue.setText(timeString_ + " (PAUSED)");
332            }
333            else
334            {
335              //timeLabelValue.setText(timeString_);
336            }
337            lastPauseTime = runningTime;
338
339          }
340          else
341          {
342            playTime = diffDate - (int)historyPauseTime;
343            min = (int) (playTime / 60000);
344            sec = ((int) (playTime / 1000)) - min * 60;
345            //songDuration = songProgress.getMaximum()*((int) (playTime / 1000))/progressCount_;
346            songDuration = ((int) (playTime / 1000));
347            timeString_ = (min < 9 ? "0" : "") + min + ":" + (sec < 10 ? "0" : "") + sec;
348            //timeLabelValue.setText(timeString_ + " &nbsp; (" + progressCount_ / 1000 + "kB)");
349            startPauseTime = runningTime;
350            lastPauseTime = runningTime;
351          }
352        }
353      }
354    };
355    /** Timer Thread. Use {@link #startTimer() startTimer} method to init and start the timer. **/
356    TimerThread timeWatcherThread_ = null;
357
358  /**  Description of the Field */
359  public Vector <String> playlist_ = new Vector<String>();
360  int currentPlaylistIndex_ = -1;
361
362  private boolean shuffling_ = false;
363
364  /**  Description of the Field */
365  Thread playerThread_ = null;
366
367  /** Singleton instance. **/
368  private static JOggPlayerListener INSTANCE = null;
369
370
371  /** Private constructor for Singleton instance. **/
372  private JOggPlayerListener() {}
373
374
375  /** Singleton instance getter.
376    * @return the singelton JOggPlayerListener object
377    **/
378  public static JOggPlayerListener getInstance()
379  {
380    if (INSTANCE == null)
381    {
382      synchronized (JOggPlayerListener.class)
383      {
384        if (INSTANCE == null) { System.out.println("** JOggPlayerListener**  NEW Instance Created"); INSTANCE = new JOggPlayerListener();}
385      }
386    }
387    return INSTANCE;
388  }
389
390
391  /**  Description of the Method */
392  public void start()
393  {
394    play_sound();
395  }
396
397
398  /**
399   * Reads from the oggVorbisBitStream_ a specified number of Bytes(BUFSIZE) worth
400   * sarting at index and puts them in the specified buffer[].
401   *
402   * @param buffer the byteBuffer holding the read in data
403   * @param index where to start reading from the string
404   * @param BUFSIZE the max number of bytes to read
405   * @return             the number of bytes read or -1 if error.
406   */
407  private int readFromStream(byte[] buffer, int index, int BUFSIZE)
408  {
409    int bytes = 0;
410    try
411    {
412      bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
413      progressCount_ += bytes;
414    }
415    catch (Exception e)
416    {
417       System.err.println(e.getMessage());
418       System.err.println("Cannot Read Selected Song - index=" + index);
419      bytes = -1;
420    }
421    return bytes;
422  }
423
424
425  /**
426   * Helper method to encapsulate the starting of the timeWatcherRunnable_.*
427   */
428  private void startTimer()
429  {
430    if (timeWatcherThread_ == null)
431    {
432      timeWatcherThread_ = new TimerThread();
433      timeWatcherThread_.start();
434    }
435    else if (!timeWatcherThread_.isAlive())
436    {
437      timerRunning_ = false;
438      timeWatcherThread_ = new TimerThread();
439      timeWatcherThread_.start();
440    }
441    else if (!timeWatcherThread_.isAlive())
442    {
443      timeWatcherThread_ = new TimerThread();
444      timeWatcherThread_.start();
445    }
446  }
447
448
449  private void reStartTimer()
450  {
451    timeWatcherThread_ = null;
452    startTimer();
453  }
454
455
456  public int retrieveCurrentTunePlayTimeInMiliSeconds()
457  {
458    int retVal = -1;
459    if(timerRunning_) retVal = timeWatcherThread_.getPlayTime();
460    return retVal;
461  }
462
463
464  public int retrieveTuneProgressRatio()
465  {
466    int retVal = -1;
467    if(timerRunning_) retVal = timeWatcherThread_.getSongProgressRatio();
468    return retVal;
469  }
470
471
472  public int retrieveSongDurationInMiliSeconds()
473  {
474    int retVal = current_songDuration_;
475
476    //if(timerRunning_) retVal = timeWatcherThread_.getSongDurationInMiliSeconds();  // the timer should not be the source for this
477    return retVal;
478  }
479
480
481  public synchronized int getStatus() { return m_status; }
482  public synchronized boolean isPaused() { return (m_status==PAUSED); }
483  public synchronized boolean isPlaying() { return (m_status==PLAYING); }
484  public synchronized boolean isStopped() { return (m_status==STOPPED); }
485
486
487  /**
488    * Set Method for class field 'songComments_'.
489    *
490    * @param songComments is the value to set this class field to.
491    *
492    **/
493  public  void setSongComments(Vector<String> songComments)
494  {
495    this.songComments_ = songComments;
496  }  // setSongComments Method
497
498
499  /**
500    * Get Method for class field 'songComments_'.
501    *
502    * @return Vector<String> - The value the class field 'songComments_'.
503    *
504    **/
505  public Vector<String> getSongComments()
506  {
507    return songComments_;
508  }  // getSongComments Method
509
510
511  /**
512   * The Runnable to do the actual playing of the song. All the rest of this class
513   * is fluff to get to this point [:)] . This code was developed by the jCraft
514   * group.
515   *
516   */
517  public void run()
518  {
519    SyncState oggSyncState_ = null;
520    Page oggPage_ = og;
521    Packet oggPacket_ = op;
522    StreamState oggStreamState_ = os;
523    Info vorbisInfo = vi;
524    Comment vorbisComment = vc;
525    DspState vorbisDspState = vd;
526    Block vorbisBlock = vb;
527
528    retry = RETRY;
529    boolean stopSignal = false;
530    boolean loopTrace = false;
531
532    System.out.println("       >> >>>  run()>");
533
534    Thread me = Thread.currentThread();
535    me.setPriority(Thread.MAX_PRIORITY);
536
537    //currentPlaylistIndex_ = 0;
538    if(playlist_.size()>0)
539    {
540      String item = (String) playlist_.get(currentPlaylistIndex_);
541      oggVorbisBitStream_ = selectSource(item); // turns a named item into its InputStream
542
543      progressCount_ = 0;
544      int index = 0;
545      boolean streamStillHasData = (oggVorbisBitStream_ != null);  //true;
546      if (streamStillHasData)
547      try
548      {
549        while (!stopSignal ||
550               ( playing_ && (streamStillHasData || (currentPlaylistIndex_ < playlist_.size())) )
551              )
552        {
553          clearPlaylistSignals();
554          init_jorbis();  // gets all the oy, og ...streams seup
555
556          oggSyncState_ = oy;
557          oggPage_ = og;
558          oggPacket_ = op;
559          oggStreamState_ = os;
560          vorbisInfo = vi;
561          vorbisComment = vc;
562          vorbisDspState = vd;
563          vorbisBlock = vb;
564
565          /*  Get the next Song setup */
566          item = (String) playlist_.get(currentPlaylistIndex_);
567          /* ************************************************ */
568
569          System.out.print("\n       >> JOggPlayerListener Thread starting to play item");
570          System.out.println("["+currentPlaylistIndex_+"/"+(playlist_.size()-1)+"] "+item);
571
572          oggVorbisBitStream_ = selectSource(item); // turns a named item into its InputStream
573          int eos = 0;
574          //System.out.println(" run() Init file...");
575
576          index = oggSyncState_.buffer(BUFSIZE);
577          buffer = oggSyncState_.data;
578
579          bytes = readFromStream(buffer, index, BUFSIZE);
580          if (bytes == -1)
581          {
582            streamStillHasData = false;
583            System.out.println("       >> ERROR Cannot get any data from selected Ogg bitstream.");
584            break;
585          }
586          oggSyncState_.wrote(bytes);
587          if (oggSyncState_.pageout(oggPage_) != 1)
588          {
589            streamStillHasData = false;
590            if (bytes < BUFSIZE)
591            {
592              System.out.println("       >> ERROR No more data");
593              break;
594            }
595            System.out.println("       >> ERROR Input does not appear to be an Ogg bitstream.");
596            break;
597          }
598          oggStreamState_.init(oggPage_.serialno());
599          vorbisInfo.init();
600          vorbisComment.init();
601          if (oggStreamState_.pagein(oggPage_) < 0)
602          {
603            // error; stream version mismatch perhaps
604            System.out.println("       >> ERROR reading first page of Ogg bitstream data.");
605            break;
606          }
607          int tmpInt = oggStreamState_.packetout(oggPacket_);
608          if (tmpInt != 1)
609          {
610            // no page? must not be vorbis
611            System.out.println("       >> ERROR reading initial header packet. ("+tmpInt + ")");
612            break;
613          }
614          tmpInt = vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_);
615          if (tmpInt < 0)
616          {
617            // error case; not a vorbis header
618            System.out.println("       >> ERROR This Ogg bitstream does not contain Vorbis audio data. ("+tmpInt + ")");
619            break;
620          }
621          System.out.println("       >> Valid Ogg bitstream.");
622          int i = 0;
623          while (i < 2)
624          {
625            while (i < 2)
626            {
627              int result = oggSyncState_.pageout(oggPage_);
628              if (result == 0)
629              {
630                break;
631              } // Need more data
632              if (result == 1)
633              {
634                oggStreamState_.pagein(oggPage_);
635                while (i < 2)
636                {
637                  result = oggStreamState_.packetout(oggPacket_);
638                  if (result == 0)
639                  {
640                    break;
641                  }
642                  if (result == -1)
643                  {
644                    System.out.println("       >> ERROR Corrupt secondary header.  Exiting.");
645                    return;
646                  }
647                  vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_);
648                  i++;
649                }
650              }
651            }
652            index = oggSyncState_.buffer(BUFSIZE);
653            buffer = oggSyncState_.data;
654            bytes = readFromStream(buffer, index, BUFSIZE);
655            if (bytes == -1)
656            {
657              break;
658            }
659            if (bytes == 0 && i < 2)
660            {
661              System.out.println("       >> ERROR  End of file before finding all Vorbis  headers!");
662              return;
663            }
664            oggSyncState_.wrote(bytes);
665          }
666
667          /* So Far So good on the stream, continue with */
668          /* Vorbis user_comments */
669          byte[][] ptr = vorbisComment.user_comments;
670          String currComment = "";
671          songComments_.clear();
672          for (int j = 0; j < ptr.length; j++)
673          {
674            if (ptr[j] == null)
675            {
676              break;
677            }
678            currComment = (new String(ptr[j], 0, ptr[j].length - 1)).trim();
679            songComments_.add(currComment);
680
681            //System.out.println("Comment: " + currComment);
682          }
683          currComment = "Bitstream: " + vorbisInfo.channels + " channel," + vorbisInfo.rate + "Hz";
684          //currComment = "Bitstream: " + playList.getSongChannels(sel) + " channel," + playList.getSongBitrate(sel) + "Hz";
685          songComments_.add(currComment);
686          //System.out.println(currComment);
687          currComment = "Encoded by: " + new String(vorbisComment.vendor, 0,
688            vorbisComment.vendor.length - 1);
689          songComments_.add(currComment);
690
691          convsize_ = BUFSIZE / vorbisInfo.channels;
692          vorbisDspState.synthesis_init(vorbisInfo);
693          vorbisBlock.init(vorbisDspState);
694          double[][][] _pcm = new double[1][][];
695          float[][][] _pcmf = new float[1][][];
696          int[] _index = new int[vorbisInfo.channels];
697          getOutputLine(vorbisInfo.channels, vorbisInfo.rate);
698          reStartTimer();
699
700          System.out.print("    run() Decoding ");
701
702          while (eos == 0)
703          {
704            if(loopTrace) System.out.print(" *");
705            while (eos == 0)
706            {
707              if(loopTrace) System.out.print(" **");
708              if (!playing_ || playerThread_ == null)
709              {
710                System.err.println("bye.");
711                timerRunning_ = false;
712                try
713                {
714                  //outputLine.drain();
715                  //outputLine.stop();
716                  //outputLine.close();
717                  oggVorbisBitStream_.close();
718                }
719                catch (Exception ee)
720                {
721                }
722                return;
723              }
724              int result = oggSyncState_.pageout(oggPage_);
725              if (result == 0)
726              {
727                //System.err.println("Puked on oggSyncState_.pageout(oggPage_)");
728                break;
729              } // need more data
730              if (result == -1)
731              { // missing or corrupt data at this page position
732                System.err.println("       >> Corrupt or missing data in bitstream; " +
733                                   "continuing...");
734              }
735              else
736              {
737                oggStreamState_.pagein(oggPage_);
738
739                /* This is the endless Play song loop */
740                /* ---------------------------------- */
741                result = 1;
742                while (playing_ && result!=0)
743                {
744                  result = oggStreamState_.packetout(oggPacket_);
745                  if(loopTrace) System.out.print(" ***"+result);
746                  //if (result == 0)
747                  //{
748                  //  break;
749                  //} // need more data
750                  if (result <1 ) //== -1)
751                  { // missing or corrupt data at this page position
752                    // no reason to complain; already complained above
753                  }
754                  else // only if there is data
755                  {
756                    if(loopTrace) System.out.print("!!");
757                    // we have a packet.  Decode it
758                    int samples;
759                    if (vorbisBlock.synthesis(oggPacket_) == 0)
760                    { // test for success!
761                      if(loopTrace) System.out.print("!");
762                      vorbisDspState.synthesis_blockin(vorbisBlock);
763                    }
764
765                    /* ******************************************   */
766                    /* This is the Data Read and convert Block      */
767                    /* ******************************************   */
768                    // start sampling the data into _pcmf
769                    samples = vorbisDspState.synthesis_pcmout(_pcmf, _index);
770                    if(loopTrace) System.out.print("["+samples+"]");
771                    while (samples  > 0)
772                    {   //samples holds the size of data in recent read
773
774                      if(loopTrace) System.out.print(".");
775
776                      //double[][] pcm = _pcm[0];  // not used  DELETE ME
777                      float[][] pcmf = _pcmf[0];
778                      boolean clipflag = false;
779                      int bout = (samples < convsize_ ? samples : convsize_);
780                      double fVal = 0.0;
781                      // convert doubles to 16 bit signed ints (host order) and interleave
782                      for (i = 0; i < vorbisInfo.channels; i++)
783                      {
784                        int pointer = i * 2;
785                        //int ptr=i;
786                        int mono = _index[i];
787                        for (int j = 0; j < bout; j++)
788                        {
789                          fVal = (float) pcmf[i][mono + j] * 32767.;
790                          /*
791                           *  volume Adjust
792                           */
793                          //fVal = fVal * currentVolumeMultiplier_;
794                          int val = (int) (fVal);
795                          if (val > 32767)
796                          {
797                            val = 32767;
798                            clipflag = true;
799                          }
800                          if (val < -32768)
801                          {
802                            val = -32768;
803                            clipflag = true;
804                          }
805                          if (val < 0)
806                          {
807                            val = val | 0x8000;
808                          }
809                          convbuffer_[pointer] = (byte) (val);
810                          convbuffer_[pointer + 1] = (byte) (val >>> 8);
811                          pointer += 2 * (vorbisInfo.channels);
812                        }
813                      }
814
815                      // transfer the converted bytes to the output line
816                      outputLine.write(convbuffer_, 0, 2 * vorbisInfo.channels * bout);
817                      vorbisDspState.synthesis_read(bout);
818
819                      // read next sample and go back
820                      samples = vorbisDspState.synthesis_pcmout(_pcmf, _index);
821                    } // inner decoding loop
822
823                    /* loop control lives here */
824                    /* *********************** */
825                    if(getLoopStopped())
826                    {
827                      System.out.println("\n       >>  ** STOP Signal ");
828                      stopSignal = true;
829                      playing_ = false;
830                      m_status = STOPPED;
831                      eos = 1;
832                      result=0;
833                      break;
834                    }
835                    if(getSignalPreviousInPlaylist())
836                    {
837                     //do something
838                     System.out.print("\n       >>  ** Previous Signal from: "+currentPlaylistIndex_);
839                     currentPlaylistIndex_--;
840                     if(currentPlaylistIndex_ < 0) currentPlaylistIndex_=playlist_.size()-1;
841                     System.out.println(" to: "+currentPlaylistIndex_);
842                     eos = 1;
843                     result=0;
844                     currentPlaylistIndex_--;
845                    }
846                    if(getSignalNextInPlaylist())
847                    {
848                      //do something
849                      System.out.print("\n       >>  ** NEXT Signal from: "+currentPlaylistIndex_);
850                      if(!getShuffling())
851                      {
852                       currentPlaylistIndex_++;
853                       if(currentPlaylistIndex_ >= playlist_.size()) currentPlaylistIndex_=0;
854                       System.out.println(" to: "+currentPlaylistIndex_);
855                      }
856                      else
857                      {
858                       currentPlaylistIndex_ = (int) (Math.random() * playlist_.size());
859                       System.out.println(" to: "+currentPlaylistIndex_);
860                      }
861                      currentPlaylistIndex_--;
862                      eos = 1;
863                      result=0;
864                    }
865                    // Pause the stream
866                    boolean wasPaused = false;
867                    while (getLoopPaused())
868                    {
869                      pausePlayback();
870                      ca.bc.webarts.widgets.Util.sleep(250);
871                      wasPaused = true;
872                    }
873                    if(wasPaused) resumePlayback();
874
875                  } //valid packet processed
876
877                } //// Still more data until playing_ stops ORresult =0
878                //System.out.print(".");
879
880                if (stopSignal || oggPage_.eos() != 0)
881                {
882                  eos = 1;
883                }
884              }// Endless Play Loop until playing_ stops us OR the ogg stream was empty
885            }
886
887            //System.out.println(" * IN loop1 getting more buffer data");
888            if (!stopSignal && eos == 0)
889            {
890              index = oggSyncState_.buffer(BUFSIZE);
891              buffer = oggSyncState_.data;
892              bytes = readFromStream(buffer, index, BUFSIZE);
893              if (bytes == -1)
894              {
895                System.out.println("\n Current tune Ogg Stream empty.");
896                streamStillHasData = false;
897                eos = 1;
898                //break;
899              }
900              else
901              {
902                oggSyncState_.wrote(bytes);
903                if (bytes == 0)
904                {
905                  System.out.println("No data left in Ogg Stream.");
906                  eos = 1;
907                }
908              }
909            }
910          }
911          System.out.print("       <<     run() DONE Decoding, cleaning up ");
912          if (oggStreamState_!=null) oggStreamState_.clear();
913          if (vorbisBlock!=null)vorbisBlock.clear();
914          if (vorbisDspState!=null)vorbisDspState.clear();
915          if (vorbisInfo!=null)vorbisInfo.clear();
916          System.out.println("\n       >> <<< Done Song >>>");
917
918          // loop back
919          if(!getLoopStopped() )
920          {
921            // Shuffle ??? NEXT song
922            if(!getShuffling())
923            {
924              if(++currentPlaylistIndex_ >= playlist_.size()) currentPlaylistIndex_=0;
925              //System.out.println(" to: "+currentPlaylistIndex_);
926            }
927            else
928            {
929              currentPlaylistIndex_ = (int) (Math.random() * playlist_.size());
930              //System.out.println(" to: "+currentPlaylistIndex_);
931            }
932          }
933          else if(getSignalPreviousInPlaylist() || getSignalNextInPlaylist()) eos = 0;
934
935        System.out.println("       >>      Do we go back for another tune???\n"+
936                           "               playing_="+playing_+
937                           "\n               currentPlaylistIndex_="+currentPlaylistIndex_+" / "+(playlist_.size()-1)+
938                           "\n               shuffling="+getShuffling()+
939                           "\n               stopSignal="+stopSignal+
940                           "\n               streamStillHasData="+streamStillHasData+
941                           "  (currentPlaylistIndex_<playlist_.size())="+(currentPlaylistIndex_<playlist_.size()));
942        } // main song loop
943        System.out.println("       <<      NOPE - DONE Looping\n");
944
945        if (oggSyncState_!=null) oggSyncState_.clear();
946        System.out.println("       << Done Playlist.");
947        timerRunning_ = false;
948        stop_sound();
949        try
950        {
951          if (oggVorbisBitStream_ != null) oggVorbisBitStream_.close();
952          playing_ = false; //playerThread_ = null;
953        }
954        catch (Exception e)
955        {
956        }
957      }
958      catch (Exception ex)
959      {
960        //System.out.println("Ogg Stream Exception. "+ex.getMessage());
961        ex.printStackTrace();
962        System.out.println("Ogg Stream Exception. "+ex.getMessage());
963      }
964
965    }
966    else
967      System.out.println("  No songs in playlist.");
968
969    stop();
970    stopSignal = false;
971    clearStopSignal();
972    System.out.println("       << EXITING JOggPlayerListener Thread: "+ me.getId());
973  }
974
975
976  /**
977    * Threadsafe signal the current play to STOP.
978    *
979    **/
980  public synchronized void signalStopPlay()
981  {
982    this.loopStopped_ = true;
983  }  // signalStopPlay Method
984
985
986  /**
987    * Threadsafe Sets loopStopped_backto false after successfull stop.
988    *
989    **/
990  public synchronized void clearStopSignal()
991  {
992    this.loopStopped_ = false;
993  }  // signalStopPlay Method
994
995
996  public synchronized Vector<String> getPlaylistArtistAlbumSongnames(){return getIndexedPlaylistArtistAlbumSongnames(false);}
997  public synchronized Vector<String> getIndexedPlaylistArtistAlbumSongnames(boolean addIndex)
998  {
999    if (debug_>1) System.out.println(" JOggPlayerListener: getIndexedPlaylistArtistAlbumSongnames(addIndex="+addIndex+")");
1000    Vector<String> retVal = new Vector<String>();
1001    for (int i=0; i< playlist_.size(); i++)
1002    {
1003      retVal.add((addIndex?""+i+") ":"")+getArtistName(i)+" / "+getAlbumName(i)+" / " +getTitle(i));
1004    }
1005    if (debug_>1) System.out.println("                   : IndexedPlaylist=");
1006    int t = 0;
1007    if (debug_>1) for(String sss : retVal) System.out.println("                   "+(t++) +") "+sss);
1008    return retVal ;
1009  }  // getSignalNextInPlaylist Method
1010
1011
1012  /** Threadsafe Get Method for class var currentPlaylistIndex_.
1013    *
1014    * @return int currentPlaylistIndex_
1015    **/
1016  public synchronized int getCurrentPlaylistIndex()
1017  {
1018    return this.currentPlaylistIndex_ ;
1019  }  // getSignalNextInPlaylist Method
1020
1021
1022  /** Threadsafe Get Method for class var currentPlaylistIndex_.
1023    *
1024    * @return int currentPlaylistIndex_
1025    **/
1026  public synchronized int getPlaylistSize()
1027  {
1028    return this.playlist_.size() ;
1029  }  // getSignalNextInPlaylist Method
1030
1031
1032  /**
1033    * signal the current play to jump to next in playlist.
1034    *
1035    **/
1036  public synchronized void signalNextInPlaylist()
1037  {
1038    this.signalNextInPlaylist_ = true;
1039  }  // signalNextInPlaylist Method
1040
1041
1042  /** Threadsafe Get Method for class var signalNextInPlaylist_.
1043    *
1044    * @return boolean signalNextInPlaylist_
1045    **/
1046  public synchronized boolean getSignalNextInPlaylist()
1047  {
1048    return this.signalNextInPlaylist_ ;
1049  }  // getSignalNextInPlaylist Method
1050
1051
1052  /**
1053    * Threadsafe signal the current play to jump to previous in playlist.
1054    *
1055    **/
1056  public synchronized void signalPreviousInPlaylist()
1057  {
1058    this.signalPreviousInPlaylist_ = true;
1059  }  // signalPreviousInPlaylist Method
1060
1061
1062  /** Threadsafe Get Method for class var signalPreviousInPlaylist_.
1063    *
1064    * @return boolean signalPreviousInPlaylist_
1065    **/
1066  public synchronized boolean getSignalPreviousInPlaylist()
1067  {
1068    return this.signalPreviousInPlaylist_ ;
1069  }  // getSignalPreviousInPlaylist Method
1070
1071
1072  /**
1073    * Set Method for class field 'shuffling_'.
1074    *
1075    * @param shuffling is the value to set this class field to.
1076    *
1077    **/
1078  public synchronized void setShuffling(boolean shuffling)
1079  {
1080    this.shuffling_ = shuffling;
1081  }  // setShuffling Method
1082
1083
1084  /**
1085    * Get Method for class field 'shuffling_'.
1086    *
1087    * @return boolean - The value the class field 'shuffling_'.
1088    *
1089    **/
1090  public synchronized boolean getShuffling()
1091  {
1092    return this.shuffling_;
1093  }  // getShuffling Method
1094
1095
1096  /**
1097    * Threadsafe Clears ALL thread signals for the playlist playing.
1098    *
1099    **/
1100  public synchronized void clearPlaylistSignals()
1101  {
1102    this.signalNextInPlaylist_ = false;
1103    this.signalPreviousInPlaylist_ = false;
1104  }  protected void togglePause()
1105  {
1106    if (m_status == PLAYING)      pausePlayback();
1107    else if (m_status == PAUSED) resumePlayback();
1108  }
1109
1110
1111  /**
1112   * Pauses the playback.<br>
1113   *
1114   * Player Status = PAUSED.
1115   */
1116  protected void pausePlayback()
1117  {
1118      if (outputLine != null)
1119      {
1120          if (m_status == PLAYING)
1121          {
1122              outputLine.drain();    //flush();
1123              outputLine.stop();
1124              m_status = PAUSED;
1125              //log.info("pausePlayback() completed");
1126              //notifyEvent(BasicPlayerEvent.PAUSED, getEncodedStreamPosition(), -1, null);
1127          }
1128      }
1129  }
1130
1131  /**
1132   * Resumes the playback.<br>
1133   *
1134   * Player Status = PLAYING.
1135   */
1136  protected void resumePlayback()
1137  {
1138      if (outputLine != null)
1139      {
1140          if (m_status == PAUSED)
1141          {
1142              outputLine.start();
1143              m_status = PLAYING;
1144              //log.info("resumePlayback() completed");
1145              //notifyEvent(BasicPlayerEvent.RESUMED, getEncodedStreamPosition(), -1, null);
1146          }
1147      }
1148  }
1149
1150
1151  /** Threadsafe Send a signal to toggle the paused state of the loop playing. **/
1152  public synchronized void signalLoopPaused()
1153  {
1154    setLoopPaused(!getLoopPaused());
1155  }
1156
1157  /**
1158    * Threadsafe Set Method for class field 'loopPaused_'.
1159    *
1160    * @param loopPaused is the value to set this class field to.
1161    *
1162    **/
1163  public synchronized void setLoopPaused(boolean loopPaused)
1164  {
1165    this.loopPaused_ = loopPaused;
1166  }  // setLoopPaused Method
1167
1168
1169  /**
1170    * Threadsafe Get Method for class field 'loopPaused_'.
1171    *
1172    * @return boolean - The value the class field 'loopPaused_'.
1173    *
1174    **/
1175  public synchronized boolean getLoopPaused()
1176  {
1177    return loopPaused_;
1178  }  // getLoopPaused Method
1179
1180
1181  /** Threadsafe Get method for class var loopStopped_.
1182    * @return boolean loopStopped_
1183    **/
1184  public synchronized boolean getLoopStopped()
1185  {
1186    return loopStopped_;
1187  }  // getLoopPaused Method
1188
1189
1190  /**
1191   *  Gets the Filename of the current item in the playlist.
1192   *
1193   * @return    The current tune title value
1194   */
1195  public String getFilename()
1196  {
1197    return getFilename(currentPlaylistIndex_);
1198  }
1199
1200
1201  /**
1202   *  Gets the Filename of the specified item in the playlist.
1203   *
1204   * @param playlistItem the item to get name for
1205   * @return    The current tune title value
1206   */
1207  public String getFilename(int playListIndex)
1208  {
1209    String retVal = "";
1210    if(playListIndex>-1)  retVal = (String) playlist_.get(playListIndex);
1211    return retVal;
1212  }
1213
1214
1215  /**
1216   *  Gets the title of the current item in the playlist. It is everything after the last '/' in the filename.
1217   *
1218   * @return String The current tune title value
1219   */
1220  public String getTitle()
1221  {
1222    return getTitle(currentPlaylistIndex_);
1223  }
1224
1225
1226  /**
1227   *  Gets the title of the specified item in the playlist. It is everything after the last '/' in the filename.
1228   *
1229   * @param playlistItem the item to get name for
1230   * @return String The current tune title value
1231   */
1232  public String getTitle(int playListIndex)
1233  {
1234    String retVal = "";
1235    if(playListIndex>-1)
1236      try {
1237        String f = getFilename(playListIndex);
1238        retVal = f.substring(f.lastIndexOf(ca.bc.webarts.widgets.Util.SYSTEM_FILE_SEPERATOR)+1).trim().replace("&#38;","&");
1239      } catch(Exception ex) { /* return empty retVal */ ex.printStackTrace();}
1240    return retVal;
1241  }
1242
1243
1244  public String getAlbumCoverURL()
1245  {
1246    return getAlbumCoverURL(currentPlaylistIndex_);
1247  }
1248  /**
1249   *  Gets the Album Cover URL of the current item in the playlist.
1250   *
1251   * @return  String The  URL
1252   */
1253  public String getAlbumCoverURL(int playListIndex)
1254  {
1255    String retVal = "";
1256    String t = "";
1257    String aName = "";
1258    String alName = "";
1259    if(playListIndex>-1)
1260    {
1261       t = getTitle(playListIndex);
1262      try
1263      {
1264        int endIndex = t.indexOf("_");
1265        if(endIndex<0) endIndex = t.indexOf("-")-2;
1266        aName = t.substring(0,endIndex);
1267      }
1268      catch(Exception ex) { /* return empty retVal */ ex.printStackTrace();System.out.println("        playlist["+playListIndex+"] title="+t); }
1269    }
1270    if(playListIndex>-1)
1271    {
1272      t = getFilename(playListIndex);
1273      try
1274      {
1275        String tt = t.substring(0,t.lastIndexOf(ca.bc.webarts.widgets.Util.SYSTEM_FILE_SEPERATOR));
1276        alName = tt.substring(tt.lastIndexOf(ca.bc.webarts.widgets.Util.SYSTEM_FILE_SEPERATOR)+1);
1277        retVal = albumCoverURLBase_
1278           +"/"+aName.replace("&#38;","&")
1279           +"/"+alName.replace("&#38;","&")
1280           +"/cover.jpg";
1281
1282      }
1283      catch(Exception ex) { /* return empty retVal */ ex.printStackTrace(); System.out.println("        playlist["+playListIndex+"] filename="+t); }
1284    }
1285
1286    return retVal;
1287  }
1288
1289
1290  /**
1291   *  Gets the Artist name of the current item in the playlist.
1292   *
1293   * @return  String The  Artist name
1294   */
1295  public String getArtistName()
1296  {
1297    return getArtistName(currentPlaylistIndex_);
1298  }
1299
1300
1301  /**
1302   *  Gets the Artist name of the passed in playListIndex in the playlist.
1303   *
1304   * @return  String  The  title value
1305   */
1306  public String getArtistName(int playListIndex)
1307  {
1308    return getArtistName(playListIndex, true);
1309  }
1310
1311
1312  /**
1313   *  Gets the Artist name of the passed in playListIndex in the playlist.
1314   *
1315   * @return  String  The  title value
1316   */
1317  public String getArtistName(int playListIndex, boolean capsToSpaces)
1318  {
1319    String retVal = "";
1320    String t = "";
1321    if(playListIndex>-1)
1322    {
1323       t = getTitle(playListIndex);
1324      try
1325      {
1326        int endIndex = t.indexOf("_");
1327        if(endIndex<0) endIndex = t.indexOf("-")-2;
1328        retVal = Util.tokenReplace(t.substring(0,endIndex),"&#38;","&");
1329        if(capsToSpaces)
1330          retVal = ca.bc.webarts.widgets.Util.capsToSpacesInString( retVal);
1331
1332        /* handle some special cases */
1333        if(retVal.equals("I N X S")) retVal = "INXS";
1334        else if(retVal.equals("Inxs")) retVal = "INXS";
1335        else if(retVal.equals("A C D C")) retVal = "ACDC";
1336        else if(retVal.equals("Acdc")) retVal = "ACDC";
1337      }
1338      catch(Exception ex) { /* return empty retVal */ ex.printStackTrace();System.out.println("        playlist["+playListIndex+"] title="+t); }
1339    }
1340
1341    return retVal;
1342  }
1343
1344
1345  /**
1346   *  Gets the Song name of the current item in the playlist.
1347   *
1348   * @return String The current tune Song name
1349   */
1350  public String getSongName()
1351  {
1352    return getSongName(currentPlaylistIndex_);
1353  }
1354
1355
1356  /**
1357   *  Gets the Song name of the passed in  playListIndex in the playlist.
1358   *
1359   * @return String The current tune Song name
1360   */
1361  public String getSongName(int playListIndex)
1362  {
1363    String retVal = "";
1364    String t = "";
1365    if(playListIndex>-1)
1366    {
1367      t = getTitle(playListIndex);
1368      try
1369      {
1370        String number = "00";
1371        String cdNumber = "01";
1372        boolean multiCD = false;
1373        int numSize = 2;
1374        if(t.indexOf("_")>0)
1375            number = t.substring(t.indexOf("_")+1, t.indexOf("-", t.indexOf("_")+1));
1376        else if(t.indexOf("-")>0)
1377          number = t.substring(t.indexOf("-")-2, t.indexOf("-"));
1378        if(number.length()>2)
1379        {
1380          // it includes a disc number
1381          multiCD = true;
1382          numSize = 4;
1383          cdNumber = Util.left(number,2);
1384          number = Util.right(number,2);
1385        }
1386
1387
1388          String art = "";;
1389          if(t.indexOf("-")>0)
1390            art = t.substring(0,t.indexOf("-"));
1391          if(art.indexOf("_")>0)
1392          {
1393            art = art.substring(0,t.indexOf("_"));
1394          }
1395          else
1396          {
1397            art = art.substring(0,art.length()-numSize-2);
1398          }
1399          //artistName_ = art;
1400
1401        int endIndex = t.indexOf("_");
1402        if(endIndex<0) endIndex = t.indexOf("-")-numSize;
1403        retVal = ca.bc.webarts.widgets.Util.capsToSpacesInString(t.substring(endIndex+numSize+2, t.lastIndexOf(".")));
1404      }
1405      catch(Exception ex) { /* return empty retVal */ ex.printStackTrace();System.out.println("        playlist["+playListIndex+"] title="+t); }
1406    }
1407
1408    return retVal;
1409  }
1410
1411
1412   /**
1413   *  Gets the Album Name of the passed in playlistItem in the playlist.
1414   *
1415   * @return String  The Album Name of current tune
1416   */
1417  public String getAlbumName()
1418  {
1419    return getAlbumName(currentPlaylistIndex_);
1420  }
1421
1422
1423   /**
1424   *  Gets the Album Name of the passed in playListIndex in the playlist.
1425   *
1426   * @return String   The Album Name of current tune
1427   */
1428  public String getAlbumName(int playListIndex)
1429  {
1430    return getAlbumName(playListIndex, true);
1431  }
1432
1433
1434   /**
1435   *  Gets the Album Name of the passed in playListIndex in the playlist.
1436   *
1437   * @return String   The Album Name of current tune
1438   */
1439  public String getAlbumName(int playListIndex, boolean capsToSpaces)
1440  {
1441    String retVal = "";
1442    String t = "";
1443    if(playListIndex>-1)
1444    {
1445      t = getFilename(playListIndex);
1446      try
1447      {
1448        String tt = t.substring(0,t.lastIndexOf(ca.bc.webarts.widgets.Util.SYSTEM_FILE_SEPERATOR));
1449        retVal = tt.substring(tt.lastIndexOf(ca.bc.webarts.widgets.Util.SYSTEM_FILE_SEPERATOR)+1);
1450        if(capsToSpaces)
1451          retVal = ca.bc.webarts.widgets.Util.capsToSpacesInString(retVal);
1452      }
1453      catch(Exception ex) { /* return empty retVal */ ex.printStackTrace(); System.out.println("        playlist["+playListIndex+"] filename="+t); }
1454    }
1455
1456    return retVal;
1457  }
1458
1459
1460  /**
1461   *  Gets the album TrackNum of the current item in the playlist.
1462   *
1463   * @return int   The TrackNum
1464   */
1465  public int getAlbumTrackNum()
1466  {
1467    return getAlbumTrackNum(currentPlaylistIndex_);
1468  }
1469
1470
1471  /**
1472   *  Gets the album TrackNum of the passed in playListIndex in the playlist.
1473   *
1474   * @return  int  The TrackNum
1475   */
1476  public int getAlbumTrackNum(int playListIndex)
1477  {
1478    int retVal = 0;
1479    String t = getTitle(playListIndex);
1480    try
1481    {
1482        String number = "00";
1483        String cdNumber = "01";
1484        boolean multiCD = false;
1485        int numSize = 2;
1486        if(t.indexOf("_")>0)
1487            number = t.substring(t.indexOf("_")+1, t.indexOf("-", t.indexOf("_")+1));
1488        else if(t.indexOf("-")>0)
1489          number = t.substring(t.indexOf("-")-2, t.indexOf("-"));
1490        if(number.length()>2)
1491        {
1492          // it includes a disc number
1493          multiCD = true;
1494          numSize = 4;
1495          cdNumber = Util.left(number,2);
1496          number = Util.right(number,2);
1497        }
1498        retVal = Integer.parseInt(cdNumber);
1499
1500        //retVal = Integer.parseInt(t.substring(t.indexOf("_")+1 , t.indexOf("_")+3));
1501
1502    }
1503    catch(Exception ex) { /* return empty retVal */ }
1504
1505    return retVal;
1506  }
1507
1508
1509  /**  Starts the playing from index 0 in the playlist. */
1510  public void play_sound()
1511  {
1512    play_sound(0);
1513  }
1514
1515
1516  /**  Starts the playing from current index in the playlist. */
1517  public void play_current_sound()
1518  {
1519    play_sound(currentPlaylistIndex_);
1520  }
1521
1522
1523  /**  Start playing a tune from the playlist starting from the index passed in as a parameter playListIndex.
1524    *
1525    *  @param playListIndex is the tune to start playing
1526    */
1527  public void play_sound(int playListIndex)
1528  {
1529
1530    if(playListIndex>=0 && playListIndex<playlist_.size())
1531    {
1532      if(isPlaying())
1533      {
1534        signalStopPlay();
1535        ca.bc.webarts.widgets.Util.sleep(300);
1536      }
1537      currentPlaylistIndex_ = playListIndex;
1538      playerThread_ = new Thread(JOggPlayerListener.getInstance());
1539      clearStopSignal();
1540      playing_ = true;
1541      m_status = PLAYING;
1542      playerThread_.start();
1543      System.out.println("       >> JOggPlayerListener thread now started playing playlist item #"+playListIndex+"/"+(playlist_.size()-1));
1544    }
1545    else
1546      System.out.println("       >> JOggPlayerListener ERROR: there are not that many ["+playListIndex+"] tunes in the playlist = "+(playlist_.size()-1));
1547  }
1548
1549
1550  /**
1551    *  Stop the playback
1552    *  Player Status = STOPPED.
1553    **/
1554  public void stop()
1555  {
1556    if (playerThread_ != null)
1557    {
1558      try
1559      {
1560        System.out.println("       >> Closing JOggPlayerListener.outputLines"+ Thread.currentThread().getId());
1561        outputLine.drain();
1562        outputLine.stop();
1563        outputLine.close();
1564        if (oggVorbisBitStream_ != null)
1565        {
1566          oggVorbisBitStream_.close();
1567        }
1568      }
1569      catch (Exception e)
1570      {
1571        System.out.println("       >>  error closing JOggPlayerListener output streams: "+e.getMessage());
1572      }
1573    }
1574    playerThread_ = null;
1575    m_status = STOPPED;
1576  }
1577
1578
1579  /**  Stops play */
1580  public void stop_sound()
1581  {
1582    playerThread_ = null;
1583    m_status = STOPPED;
1584  }
1585
1586
1587  /**  instantaites all the JOgg streams and states. */
1588  void init_jorbis()
1589  {
1590    oy = new SyncState();
1591    os = new StreamState();
1592    og = new Page();
1593    op = new Packet();
1594
1595    vi = new Info();
1596    vc = new Comment();
1597    vd = new DspState();
1598    vb = new Block(vd);
1599
1600    buffer = null;
1601    bytes = 0;
1602
1603    oy.init();
1604  }
1605
1606
1607  /**
1608   *  Gets the outputLine attribute of the JOggPlayerListener object
1609   *
1610   * @param  channels  Description of the Parameter
1611   * @param  rate      Description of the Parameter
1612   * @return           The outputLine value
1613   */
1614  SourceDataLine getOutputLine(int channels, int rate)
1615  {
1616    if (outputLine == null || this.rate != rate || this.channels != channels)
1617    {
1618      if (outputLine != null)
1619      {
1620        outputLine.drain();
1621        outputLine.stop();
1622        outputLine.close();
1623      }
1624      init_audio(channels, rate);
1625      outputLine.start();
1626    }
1627    return outputLine;
1628  }
1629
1630
1631  /**
1632   *  Description of the Method
1633   *
1634   * @param  channels  Description of the Parameter
1635   * @param  rate      Description of the Parameter
1636   */
1637  void init_audio(int channels, int rate)
1638  {
1639    try
1640    {
1641      //ClassLoader originalClassLoader=null;
1642      //try{
1643      //  originalClassLoader=Thread.currentThread().getContextClassLoader();
1644      //  Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
1645      //}
1646      //catch(Exception ee){
1647      //  System.out.println(ee);
1648      //}
1649      AudioFormat audioFormat =
1650                                new AudioFormat((float) rate,
1651                                16,
1652                                channels,
1653                                true, // PCM_Signed
1654                                false// littleEndian
1655                            );
1656      DataLine.Info info =
1657                                new DataLine.Info(SourceDataLine.class,
1658                                audioFormat,
1659                                AudioSystem.NOT_SPECIFIED
1660                            );
1661
1662      if (!AudioSystem.isLineSupported(info))
1663      {
1664        //System.out.println("Line " + info + " not supported.");
1665        return;
1666      }
1667
1668      try
1669      {
1670        outputLine = (SourceDataLine) AudioSystem.getLine(info);
1671        //outputLine.addLineListener(this);
1672        outputLine.open(audioFormat);
1673      }
1674      catch (LineUnavailableException ex)
1675      {
1676        System.out.println("Unable to open the sourceDataLine: " + ex);
1677        return;
1678      }
1679      catch (IllegalArgumentException ex)
1680      {
1681        System.out.println("Illegal Argument: " + ex);
1682        return;
1683      }
1684
1685      frameSizeInBytes = audioFormat.getFrameSize();
1686      int bufferLengthInFrames = outputLine.getBufferSize() / frameSizeInBytes / 2;
1687      bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
1688
1689      //buffer = new byte[bufferLengthInBytes];
1690      //if(originalClassLoader!=null)
1691      //  Thread.currentThread().setContextClassLoader(originalClassLoader);
1692
1693      this.rate = rate;
1694      this.channels = channels;
1695    }
1696    catch (Exception ee)
1697    {
1698      System.out.println(ee);
1699    }
1700  }
1701
1702
1703  /**
1704   *  turns a named item into its InputStream.
1705   *
1706   * @param  item  Description of the Parameter
1707   * @return       Description of the Return Value
1708   */
1709  InputStream selectSource(String item)
1710  {
1711    //System.getProperty(" Selecting source InputStream for item: "+ item);
1712    if (item.endsWith(".pls"))
1713    {
1714      item = fetch_pls(item);
1715      if (item == null)
1716      {
1717        return null;
1718      }
1719      //System.out.println("fetch: "+item);
1720    }
1721    else if (item.endsWith(".m3u"))
1722    {
1723      item = fetch_m3u(item);
1724      if (item == null)
1725      {
1726        return null;
1727      }
1728      //System.out.println("fetch: "+item);
1729    }
1730
1731    if (!item.endsWith(".ogg"))
1732    {
1733      return null;
1734    }
1735
1736    InputStream is = null;
1737    URLConnection urlc = null;
1738    try
1739    {
1740      URL url = null;
1741      url = new URL(item);
1742      urlc = url.openConnection();
1743      is = urlc.getInputStream();
1744      current_source = url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + url.getFile();
1745      current_songDuration_ = (urlc.getContentLength() > 0 ? urlc.getContentLength() : 0);
1746    }
1747    catch (Exception ee)
1748    {
1749      //System.err.println(ee);
1750      //ignore and try the next
1751    }
1752
1753    if (is == null)
1754    {
1755      //System.getProperty("  Trying item as a file...");
1756      try
1757      {
1758        is = new FileInputStream( //System.getProperty("user.dir") +
1759                                  //System.getProperty("file.separator") +
1760                                  item);
1761        current_source = null;
1762        //ifweget here its a valid file
1763        current_songDuration_ = (int) (new File(item)).length();
1764
1765      }
1766      catch (Exception ee)
1767      {
1768      //System.err.println(ee);
1769      //ignore and try the next
1770      }
1771    }
1772
1773    if (is == null)
1774    {
1775      return null;
1776    }
1777
1778    //System.out.println(" Valid InputStream for Selected Item: " + item);
1779    {
1780      boolean find = false;
1781      for (int i = 0; i < playlist_.size(); i++)
1782      {
1783        String foo = (String) (playlist_.get(i));
1784        if (item.equals(foo))
1785        {
1786          find = true;
1787          //System.out.println("   AND found it inthe playlist vector");
1788          break;
1789        }
1790      }
1791    }
1792
1793    int i = 0;
1794    String s = null;
1795    String t = null;
1796    udp_port = -1;
1797    udp_baddress = null;
1798    while (urlc != null && true)
1799    {
1800      s = urlc.getHeaderField(i);
1801      t = urlc.getHeaderFieldKey(i);
1802      if (s == null)
1803      {
1804        break;
1805      }
1806      i++;
1807      if (t != null && t.equals("udp-port"))
1808      {
1809        try
1810        {
1811          udp_port = Integer.parseInt(s);
1812        }
1813        catch (Exception ee)
1814        {
1815      //System.err.println(ee);
1816      //ignore and try the next
1817        }
1818      }
1819      else if (t != null && t.equals("udp-broadcast-address"))
1820      {
1821        udp_baddress = s;
1822      }
1823    }
1824    return is;
1825  }
1826
1827
1828  /**
1829   *  Description of the Method
1830   *
1831   * @param  pls  Description of the Parameter
1832   * @return      Description of the Return Value
1833   */
1834  String fetch_pls(String pls)
1835  {
1836    InputStream pstream = null;
1837    if (pls.startsWith("http://"))
1838    {
1839      try
1840      {
1841        URL url = null;
1842        url = new URL(pls);
1843        URLConnection urlc = url.openConnection();
1844        pstream = urlc.getInputStream();
1845      }
1846      catch (Exception ee)
1847      {
1848        System.err.println(ee);
1849        return null;
1850      }
1851    }
1852    if (pstream == null )
1853    {
1854      try
1855      {
1856        pstream = new FileInputStream( //System.getProperty("user.dir") +
1857                                       //System.getProperty("file.separator") +
1858                                       pls);
1859      }
1860      catch (Exception ee)
1861      {
1862        System.err.println(ee);
1863        return null;
1864      }
1865    }
1866
1867    String line = null;
1868    while (true)
1869    {
1870      try
1871      {
1872        line = readline(pstream);
1873      }
1874      catch (Exception e)
1875      {}
1876      if (line == null)
1877      {
1878        break;
1879      }
1880      if (line.startsWith("File1="))
1881      {
1882        byte[] foo = line.getBytes();
1883        int i = 6;
1884        for (; i < foo.length; i++)
1885        {
1886          if (foo[i] == 0x0d)
1887          {
1888            break;
1889          }
1890        }
1891        return line.substring(6, i);
1892      }
1893    }
1894    return null;
1895  }
1896
1897
1898  /**
1899   *  Description of the Method
1900   *
1901   * @param  m3u  Description of the Parameter
1902   * @return      Description of the Return Value
1903   */
1904  String fetch_m3u(String m3u)
1905  {
1906    InputStream pstream = null;
1907    if (m3u.startsWith("http://"))
1908    {
1909      try
1910      {
1911        URL url = null;
1912        url = new URL(m3u);
1913        URLConnection urlc = url.openConnection();
1914        pstream = urlc.getInputStream();
1915      }
1916      catch (Exception ee)
1917      {
1918        System.err.println(ee);
1919        return null;
1920      }
1921    }
1922    if (pstream == null)
1923    {
1924      try
1925      {
1926        pstream = new FileInputStream( //System.getProperty("user.dir") +
1927                                       //System.getProperty("file.separator") +
1928                                       m3u);
1929      }
1930      catch (Exception ee)
1931      {
1932        System.err.println(ee);
1933        return null;
1934      }
1935    }
1936
1937    String line = null;
1938    while (true)
1939    {
1940      try
1941      {
1942        line = readline(pstream);
1943      }
1944      catch (Exception e)
1945      {}
1946      if (line == null)
1947      {
1948        break;
1949      }
1950      return line;
1951    }
1952    return null;
1953  }
1954
1955
1956  /**  Add a single tune paths to the playlist.
1957   *
1958   * @param tunePath is the path the the tune to add to the playlist
1959   **/
1960  public void addToPlaylist(String tunePath)
1961  {
1962    System.out.println(" JOggPlayerListener: addToPlaylist("+tunePath+")");
1963    playlist_.addElement(tunePath);
1964  }
1965
1966
1967  /**  Add multiple tune paths to the playlist.
1968   *
1969   * @param tunePaths is the array of paths for the tunes to add to the playlist
1970   **/
1971  public void addToPlaylist(String [] tunePaths)
1972  {
1973    System.out.println(" JOggPlayerListener: addToPlaylist( ["+tunePaths.length+"] )");
1974    if(tunePaths.length>0)
1975      for(int i=0; i<tunePaths.length; i++)
1976      {
1977        System.out.println("                    "+tunePaths[i]);
1978        playlist_.addElement(tunePaths[i]);
1979      }
1980  }
1981
1982
1983  /**  Recursively Adds multiple directories of *.ogg files to the playlist. It scans all the *.ogg files in those dirs and adds them.
1984   *
1985   * @param dirPath is the directory path for the tunes to add to the playlist
1986   **/
1987  public void addDirFilesToPlaylist(String dirPath)
1988  {
1989    addDirFilesToPlaylist(dirPath, true);
1990  }
1991
1992
1993  /**  Recursively Adds multiple directories of *.ogg files to the playlist. It scans all the *.ogg files in the dirPath and adds them.
1994    * <br>It adds subdir filenames sorted alphabetically.
1995   *
1996   * @param dirPath is the directory path for the tunes to add to the playlist
1997   * @param recurseSubDirs recurseall sub-dirs while processing
1998   **/
1999  public void addDirFilesToPlaylist(String dirPath, boolean recurseSubDirs)
2000  {
2001    String [] dirPaths = {dirPath};
2002    addDirFilesToPlaylist(dirPaths, recurseSubDirs);
2003  }
2004
2005
2006  /**  Recursively Adds multiple directories of *.ogg files to the playlist. It scans all the *.ogg files in those dirs and adds them.
2007    * <br>It adds them sorted alphabetically.
2008   *
2009   * @param dirPaths is the array of directory paths for the tunes to add to the playlist
2010   **/
2011  public void addDirFilesToPlaylist(String [] dirPaths)
2012  {
2013    addDirFilesToPlaylist(dirPaths, true, true);
2014  }
2015  /**  Adds multiple directories of *.ogg files to the playlist. It scans all the *.ogg files in those dirs and adds them.
2016    * <br>It adds them sorted alphabetically.
2017   *
2018   * @param dirPaths is the array of directory paths for the tunes to add to the playlist
2019   * @param recurseSubDirs choose to recurse subDirs for *.ogg files
2020   **/
2021  public void addDirFilesToPlaylist(String [] dirPaths, boolean recurseSubDirs)
2022  {
2023    addDirFilesToPlaylist(dirPaths, recurseSubDirs, true);
2024  }
2025
2026
2027  /**  Add multiple directories of *.ogg files to the playlist. It scans all the *.ogg files in those dirs and adds them.
2028   *
2029   * @param dirPaths is the array of directory paths for the tunes to add to the playlist
2030   * @param recurseSubDirs choose to recurse subDirs for *.ogg files
2031   * @param sortFilenames choose to sort the *.ogg files as they go into playlist_
2032   **/
2033  public void addDirFilesToPlaylist(String [] dirPaths, boolean recurseSubDirs, boolean sortFilenames)
2034  {
2035    if (debug_>1) System.out.print("\n JOggPlayerListener: addDirFilesToPlaylist( ["+dirPaths.length+"] )");
2036    if (debug_>1) System.out.println("   "+Arrays.toString(dirPaths));
2037    //boolean sortFilenames = true;
2038    Vector<String> filePaths = new Vector<String>();
2039    if(dirPaths.length>0)
2040    {
2041      for(int i=0; i<dirPaths.length; i++)
2042      {
2043        File[] dirFiles = (new File(dirPaths[i])).listFiles();
2044        if (debug_>1) System.out.print(" 0)   dirFiles ");
2045        if (debug_>1) System.out.println(dirFiles);
2046        if(dirFiles!=null)
2047        {
2048          for (File currFile: dirFiles)
2049          {
2050            if (debug_>1) System.out.println(" 1)   Checking "+currFile.getName());
2051            if(currFile!=null && !"@eaDir".equals(currFile.getName()) ) //&& currFile.canRead())
2052            {
2053              if (debug_>1) System.out.print(" 2)   Checking "+currFile.getAbsolutePath());
2054              if(!currFile.isDirectory() && currFile.getAbsolutePath().endsWith(".ogg")) // && currFile.canRead())
2055              {
2056                if(sortFilenames)
2057                  filePaths.addElement(currFile.getAbsolutePath());
2058                else
2059                  playlist_.addElement(currFile.getAbsolutePath());
2060                if (debug_>1) System.out.println(" !!!ADDED!!! ");
2061              }
2062              else if (recurseSubDirs && currFile.isDirectory())
2063              {
2064                addDirFilesToPlaylist(currFile.getAbsolutePath(), recurseSubDirs);
2065              }
2066              //else System.out.println("\n  NOT adding "+currFile.getAbsolutePath());
2067            }
2068          }
2069          if(sortFilenames)
2070          {
2071            String [] sortedList = new String[filePaths.size()];
2072            for(int j=0;j<sortedList.length;j++)sortedList[j]=filePaths.get(j);
2073            ca.bc.webarts.widgets.Quick.sort(sortedList);
2074            for(int j=0;j<sortedList.length;j++)playlist_.add(sortedList[j]);
2075          }
2076
2077        }
2078      }
2079    }
2080  }
2081
2082
2083  /**  Description of the Method */
2084  public void clearPlaylist()
2085  {
2086    playlist_ = new Vector<String>();
2087    currentPlaylistIndex_=-1;
2088  }
2089
2090
2091  /**
2092    *  Converts the raw Vorbis data int music! This method does all the real work.
2093    *  It uses the class oggVorbisBitStream and does its JCraft magic to convert that data to audio stream.
2094    *
2095    */
2096  private void play_stream()
2097  {
2098    int last_channels = -1;
2099    int last_rate = -1;
2100    boolean chained = false;
2101
2102    init_jorbis();
2103
2104    retry = RETRY;
2105    boolean stopSignal = false;
2106
2107    System.out.println("play_stream>");
2108
2109    loop :
2110    while (!stopSignal && true)// loop on the song forever
2111    {                                //rely on the breaks to exit this loop
2112      clearPlaylistSignals(); // 1st time thru - start fresh so any new signals are for this tune.
2113
2114      int eos = 0;
2115      int index = oy.buffer(BUFSIZE);
2116      buffer = oy.data;
2117      try
2118      {
2119        bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
2120      }
2121      catch (Exception e)
2122      {
2123        System.err.println(e);
2124        return;
2125      }
2126      oy.wrote(bytes);
2127
2128      if (chained)
2129      {//
2130        chained = false;//
2131      }//
2132      else
2133      {//
2134        if (oy.pageout(og) != 1)
2135        {
2136          if (bytes < BUFSIZE)
2137          {
2138            break; // done, so break out of while loop  and close things up
2139          }
2140          System.err.println("Input does not appear to be an Ogg bitstream.");
2141          return;
2142        }
2143      }//
2144      os.init(og.serialno());
2145      os.reset();
2146
2147      vi.init();
2148      vc.init();
2149
2150      if (os.pagein(og) < 0)
2151      {
2152        // error; stream version mismatch perhaps
2153        System.err.println("Error reading first page of Ogg bitstream data.");
2154        return;
2155      }
2156
2157      retry = RETRY;
2158
2159      if (os.packetout(op) != 1)
2160      {
2161        // no page? must not be vorbis
2162        System.err.println("Error reading initial header packet.");
2163        break;
2164        //      return;
2165      }
2166
2167      if (vi.synthesis_headerin(vc, op) < 0)
2168      {
2169        // error case; not a vorbis header
2170        System.err.println("This Ogg bitstream does not contain Vorbis audio data.");
2171        return;
2172      }
2173
2174      /* So Far So good on the stream, continue with */
2175      /* Vorbis HEADERS */
2176      int i = 0;
2177      while (i < 2)
2178      {
2179        while (i < 2)
2180        {
2181          int result = oy.pageout(og);
2182          if (result == 0)
2183          {
2184            break;
2185          }// Need more data
2186          if (result == 1)
2187          {
2188            os.pagein(og);
2189            while (i < 2)
2190            {
2191              result = os.packetout(op);
2192              if (result == 0)
2193              {
2194                break;
2195              }
2196              if (result == -1)
2197              {
2198                System.err.println("Corrupt secondary header.  Exiting.");
2199                //return;
2200                break loop;
2201              }
2202              vi.synthesis_headerin(vc, op);
2203              i++;
2204            }
2205          }
2206        }
2207
2208        index = oy.buffer(BUFSIZE);
2209        buffer = oy.data;
2210        try
2211        {
2212          bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
2213        }
2214        catch (Exception e)
2215        {
2216          System.err.println(e);
2217          return;
2218        }
2219        if (bytes == 0 && i < 2)
2220        {
2221          System.err.println("End of file before finding all Vorbis headers!");
2222          return;
2223        }
2224        oy.wrote(bytes);
2225      }
2226
2227      /* So Far So good on the stream, continue with */
2228      /* Vorbis user_comments */
2229      {
2230        byte[][] ptr = vc.user_comments;
2231        String currComment = "";
2232        String coverImage = "";
2233        for (int j = 0; j < ptr.length; j++)
2234        {
2235          if (ptr[j] == null)
2236          {
2237            break;
2238          }
2239          currComment = new String(ptr[j], 0, ptr[j].length - 1);
2240          if(currComment.length()<"METADATA_BLOCK_PICTURE".length() ||
2241             !"METADATA_BLOCK_PICTURE".equalsIgnoreCase(currComment.substring(0,"METADATA_BLOCK_PICTURE".length()) ))
2242            System.out.println("Comment: " +currComment );
2243          else
2244          {
2245            coverImage = currComment;
2246            System.out.println("Comment: " +"METADATA_BLOCK_PICTURE="+"imageData..." );
2247          }
2248        }
2249        System.out.println("Bitstream is " + vi.channels + " channel, " + vi.rate + "Hz");
2250        if(vc!=null&& vc.vendor!=null)
2251          System.out.println("Encoded by: " + new String(vc.vendor, 0, vc.vendor.length - 1) + "\n");
2252      }
2253
2254      /* So Far So good on the stream, continue with */
2255      /* Vorbis Music Decoding */
2256      convsize_ = BUFSIZE / vi.channels;
2257
2258      vd.synthesis_init(vi);
2259      vb.init(vd);
2260
2261      double[][][] _pcm = new double[1][][];
2262      float[][][] _pcmf = new float[1][][];
2263      int[] _index = new int[vi.channels];
2264
2265      getOutputLine(vi.channels, vi.rate);
2266
2267      System.out.print("    Decoding ");
2268
2269      while (!stopSignal && eos == 0)
2270      {
2271        //clearPlaylistSignals(); // Each loop start fresh so any new signals are for this tune.
2272        while (!stopSignal && eos == 0)
2273        {
2274          int result = oy.pageout(og);
2275          if (result == 0)
2276          {
2277            break;
2278          }// need more data
2279          if (result == -1)
2280          {
2281            // missing or corrupt data at this page position
2282                  System.err.println("Corrupt or missing data in bitstream; continuing...");
2283          }
2284          else
2285          {
2286            // read a page in to process to pcm
2287            os.pagein(og);  // og is a Page of data
2288
2289            if (og.granulepos() == 0)
2290            {//
2291              chained = true;//
2292              eos = 1;//
2293              break;//
2294            }//
2295
2296            while (true)
2297            {
2298              //  process to pcm
2299              result = os.packetout(op);
2300              if (result == 0)
2301              {
2302                break;
2303              }// need more data
2304              if (result == -1)
2305              {// missing or corrupt data at this page position
2306                // no reason to complain; already complained above
2307
2308                System.err.println("no reason to complain; already complained above");
2309              }
2310              else
2311              {
2312                // we have a packet.  Decode it
2313                int samples;
2314                if (vb.synthesis(op) == 0)
2315                {// test for success!
2316                  vd.synthesis_blockin(vb);
2317                }
2318                while ((samples = vd.synthesis_pcmout(_pcmf, _index)) > 0)
2319                {
2320                  //System.out.print(".");
2321                  double[][] pcm = _pcm[0];
2322                  float[][] pcmf = _pcmf[0];
2323                  boolean clipflag = false;
2324                  int bout = (samples < convsize_ ? samples : convsize_);
2325
2326                  // convert doubles to 16 bit signed ints (host order) and
2327                  // interleave
2328                  for (i = 0; i < vi.channels; i++)
2329                  {
2330                    int ptr = i * 2;
2331                    //int ptr=i;
2332                    int mono = _index[i];
2333                    for (int j = 0; j < bout; j++)
2334                    {
2335                      int val = (int) (pcmf[i][mono + j] * 32767.);
2336                      if (val > 32767)
2337                      {
2338                        val = 32767;
2339                        clipflag = true;
2340                      }
2341                      if (val < -32768)
2342                      {
2343                        val = -32768;
2344                        clipflag = true;
2345                      }
2346                      if (val < 0)
2347                      {
2348                        val = val | 0x8000;
2349                      }
2350                      convbuffer_[ptr] = (byte) (val);
2351                      convbuffer_[ptr + 1] = (byte) (val >>> 8);
2352                      ptr += 2 * (vi.channels);
2353                    }
2354                  }
2355                  outputLine.write(convbuffer_, 0, 2 * vi.channels * bout);
2356                  vd.synthesis_read(bout);
2357                }
2358
2359                /* loop control lives here */
2360                /* *********************** */
2361                if(getLoopStopped())
2362                {
2363                  stopSignal = true;
2364
2365                  break;
2366                }
2367                if(getSignalPreviousInPlaylist())
2368                {
2369                 //do something
2370                }
2371                if(getSignalNextInPlaylist())
2372                {
2373                 //do something
2374                }
2375                // Pause the stream
2376                while (getLoopPaused())
2377                {
2378                  ca.bc.webarts.widgets.Util.sleep(250);
2379                }
2380              }
2381            }
2382            if (og.eos() != 0)
2383            {
2384              eos = 1;
2385            }
2386          }
2387          //System.out.print(">>>");
2388
2389        } // inner Decoding while loop
2390        //System.out.println("!");
2391
2392        // Process last bit of data
2393        if (eos == 0)
2394        {
2395          index = oy.buffer(BUFSIZE);
2396          buffer = oy.data;
2397          try
2398          {
2399            bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
2400          }
2401          catch (Exception e)
2402          {
2403            System.err.println(e);
2404            return;
2405          }
2406          if (bytes == -1)
2407          {
2408            //System.err.println("bytes==-1");
2409            break;
2410          }
2411          oy.wrote(bytes);
2412          if (bytes == 0)
2413          {
2414            eos = 1;
2415          }
2416        }
2417      } // Decoding while loop
2418      System.out.println("DONE Decoding ");
2419
2420      // finish... Close up streams
2421      os.clear();
2422      vb.clear();
2423      vd.clear();
2424      vi.clear();
2425    } // main play loop
2426
2427    oy.clear();
2428
2429    //System.out.println(" play_stream <");
2430    //System.err.println("    ... Done.");
2431
2432    try
2433    {
2434      if (oggVorbisBitStream_ != null)
2435      {
2436        oggVorbisBitStream_.close();
2437      }
2438    }
2439    catch (Exception e)
2440    {}
2441  }
2442
2443
2444  /**
2445   *  Description of the Method
2446   *
2447   * @param  me  Description of the Parameter
2448   */
2449  private void play_udp_stream(Thread me)
2450  {
2451    init_jorbis();
2452
2453    boolean firstloop = true;
2454
2455    try
2456    {
2457      loop :
2458      while (true)
2459      {
2460        int eos = 0;
2461        int index = oy.buffer(BUFSIZE);
2462        buffer = oy.data;
2463        try
2464        {
2465          bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
2466        }
2467        catch (Exception e)
2468        {
2469          System.err.println(e);
2470          return;
2471        }
2472
2473        oy.wrote(bytes);
2474        if (oy.pageout(og) != 1)
2475        {
2476//        if(bytes<BUFSIZE)break;
2477          System.err.println("Input does not appear to be an Ogg bitstream.");
2478          return;
2479        }
2480
2481        os.init(og.serialno());
2482        os.reset();
2483
2484        vi.init();
2485        vc.init();
2486        if (os.pagein(og) < 0)
2487        {
2488          // error; stream version mismatch perhaps
2489          System.err.println("Error reading first page of Ogg bitstream data.");
2490          return;
2491        }
2492
2493        if (os.packetout(op) != 1)
2494        {
2495          // no page? must not be vorbis
2496          System.err.println("Error reading initial header packet.");
2497          //        break;
2498          return;
2499        }
2500
2501        if (vi.synthesis_headerin(vc, op) < 0)
2502        {
2503          // error case; not a vorbis header
2504          System.err.println("This Ogg bitstream does not contain Vorbis audio data.");
2505          return;
2506        }
2507
2508        int i = 0;
2509        while (i < 2)
2510        {
2511          while (i < 2)
2512          {
2513            int result = oy.pageout(og);
2514            if (result == 0)
2515            {
2516              break;
2517            }// Need more data
2518            if (result == 1)
2519            {
2520              os.pagein(og);
2521              while (i < 2)
2522              {
2523                result = os.packetout(op);
2524                if (result == 0)
2525                {
2526                  break;
2527                }
2528                if (result == -1)
2529                {
2530                  System.err.println("Corrupt secondary header.  Exiting.");
2531                  //return;
2532                  break loop;
2533                }
2534                vi.synthesis_headerin(vc, op);
2535                i++;
2536              }
2537            }
2538          }
2539
2540          if (i == 2)
2541          {
2542            break;
2543          }
2544
2545          index = oy.buffer(BUFSIZE);
2546          buffer = oy.data;
2547          try
2548          {
2549            bytes = oggVorbisBitStream_.read(buffer, index, BUFSIZE);
2550          }
2551          catch (Exception e)
2552          {
2553            System.err.println(e);
2554            return;
2555          }
2556          if (bytes == 0 && i < 2)
2557          {
2558            System.err.println("End of file before finding all Vorbis headers!");
2559            return;
2560          }
2561          oy.wrote(bytes);
2562        }
2563        break;
2564      }
2565    }
2566    catch (Exception e)
2567    {
2568    }
2569
2570    try
2571    {
2572      oggVorbisBitStream_.close();
2573    }
2574    catch (Exception e)
2575    {}
2576
2577    UDPIO io = null;
2578    try
2579    {
2580      io = new UDPIO(udp_port);
2581    }
2582    catch (Exception e)
2583    {
2584      return;
2585    }
2586
2587    oggVorbisBitStream_ = io;
2588    play_stream();
2589  }
2590
2591
2592  /**
2593   *  Description of the Method
2594   *
2595   * @param  is  Description of the Parameter
2596   * @return     Description of the Return Value
2597   */
2598  private String readline(InputStream is)
2599  {
2600    StringBuffer rtn = new StringBuffer();
2601    int temp;
2602    do
2603    {
2604      try
2605      {
2606        temp = is.read();
2607      }
2608      catch (Exception e)
2609      {
2610        return (null);
2611      }
2612      if (temp == -1)
2613      {
2614        return (null);
2615      }
2616      if (temp != 0 && temp != '\n')
2617      {
2618        rtn.append((char) temp);
2619      }
2620    } while (temp != '\n');
2621    return (rtn.toString());
2622  }
2623
2624
2625  /**
2626   *  Description of the Class
2627   *
2628   * @author    tgutwin
2629   */
2630  class UDPIO extends InputStream
2631  {
2632    /**  Description of the Field */
2633    InetAddress address;
2634    /**  Description of the Field */
2635    DatagramSocket socket = null;
2636    /**  Description of the Field */
2637    DatagramPacket sndpacket;
2638    /**  Description of the Field */
2639    DatagramPacket recpacket;
2640    /**  Description of the Field */
2641    byte[] buf = new byte[1024];
2642    //String host;
2643    /**  Description of the Field */
2644    int port;
2645    /**  Description of the Field */
2646    byte[] inbuffer = new byte[2048];
2647    /**  Description of the Field */
2648    byte[] outbuffer = new byte[1024];
2649    /**  Description of the Field */
2650    int instart = 0, inend = 0, outindex = 0;
2651
2652
2653    /**
2654     *  Constructor for the UDPIO object
2655     *
2656     * @param  port  Description of the Parameter
2657     */
2658    UDPIO(int port)
2659    {
2660      //this.host="192.168.1.2";
2661      this.port = port;
2662      try
2663      {
2664        socket = new DatagramSocket(port);
2665      }
2666      catch (Exception e)
2667      {
2668        System.err.println(e);
2669      }
2670      recpacket = new DatagramPacket(buf, 1024);
2671//    sndpacket=new DatagramPacket(outbuffer, 0, address, port);
2672    }
2673
2674
2675    /**
2676     *  Description of the Method
2677     *
2678     * @exception  java.io.IOException  Description of the Exception
2679     */
2680    public void close()
2681      throws java.io.IOException
2682    {
2683      socket.close();
2684    }
2685
2686
2687    /**
2688     *  Description of the Method
2689     *
2690     * @return                          Description of the Return Value
2691     * @exception  java.io.IOException  Description of the Exception
2692     */
2693    public int read()
2694      throws java.io.IOException
2695    {
2696      return 0;
2697    }
2698
2699
2700    /**
2701     *  Description of the Method
2702     *
2703     * @param  array                    Description of the Parameter
2704     * @param  begin                    Description of the Parameter
2705     * @param  length                   Description of the Parameter
2706     * @return                          Description of the Return Value
2707     * @exception  java.io.IOException  Description of the Exception
2708     */
2709    public int read(byte[] array, int begin, int length)
2710      throws java.io.IOException
2711    {
2712      return getByte(array, begin, length);
2713    }
2714
2715
2716    /**
2717     *  Sets the timeout attribute of the UDPIO object
2718     *
2719     * @param  i  The new timeout value
2720     */
2721    void setTimeout(int i)
2722    {
2723      try
2724      {
2725        socket.setSoTimeout(i);
2726      }
2727      catch (Exception e)
2728      {
2729        System.out.println(e);
2730      }
2731    }
2732
2733
2734    /**
2735     *  Gets the byte attribute of the UDPIO object
2736     *
2737     * @return                          The byte value
2738     * @exception  java.io.IOException  Description of the Exception
2739     */
2740    int getByte()
2741      throws java.io.IOException
2742    {
2743      if ((inend - instart) < 1)
2744      {
2745        read(1);
2746      }
2747      return inbuffer[instart++] & 0xff;
2748    }
2749
2750
2751    /**
2752     *  Gets the byte attribute of the UDPIO object
2753     *
2754     * @param  array                    Description of the Parameter
2755     * @return                          The byte value
2756     * @exception  java.io.IOException  Description of the Exception
2757     */
2758    int getByte(byte[] array)
2759      throws java.io.IOException
2760    {
2761      return getByte(array, 0, array.length);
2762    }
2763
2764
2765    /**
2766     *  Gets the byte attribute of the UDPIO object
2767     *
2768     * @param  array                    Description of the Parameter
2769     * @param  begin                    Description of the Parameter
2770     * @param  length                   Description of the Parameter
2771     * @return                          The byte value
2772     * @exception  java.io.IOException  Description of the Exception
2773     */
2774    int getByte(byte[] array, int begin, int length)
2775      throws java.io.IOException
2776    {
2777      int i = 0;
2778      int foo = begin;
2779      while (true)
2780      {
2781        if ((i = (inend - instart)) < length)
2782        {
2783          if (i != 0)
2784          {
2785            System.arraycopy(inbuffer, instart, array, begin, i);
2786            begin += i;
2787            length -= i;
2788            instart += i;
2789          }
2790          read(length);
2791          continue;
2792        }
2793        System.arraycopy(inbuffer, instart, array, begin, length);
2794        instart += length;
2795        break;
2796      }
2797      return begin + length - foo;
2798    }
2799
2800
2801    /**
2802     *  Gets the short attribute of the UDPIO object
2803     *
2804     * @return                          The short value
2805     * @exception  java.io.IOException  Description of the Exception
2806     */
2807    int getShort()
2808      throws java.io.IOException
2809    {
2810      if ((inend - instart) < 2)
2811      {
2812        read(2);
2813      }
2814      int s = 0;
2815      s = inbuffer[instart++] & 0xff;
2816      s = ((s << 8) & 0xffff) | (inbuffer[instart++] & 0xff);
2817      return s;
2818    }
2819
2820
2821    /**
2822     *  Gets the int attribute of the UDPIO object
2823     *
2824     * @return                          The int value
2825     * @exception  java.io.IOException  Description of the Exception
2826     */
2827    int getInt()
2828      throws java.io.IOException
2829    {
2830      if ((inend - instart) < 4)
2831      {
2832        read(4);
2833      }
2834      int i = 0;
2835      i = inbuffer[instart++] & 0xff;
2836      i = ((i << 8) & 0xffff) | (inbuffer[instart++] & 0xff);
2837      i = ((i << 8) & 0xffffff) | (inbuffer[instart++] & 0xff);
2838      i = (i << 8) | (inbuffer[instart++] & 0xff);
2839      return i;
2840    }
2841
2842
2843    /**
2844     *  Gets the pad attribute of the UDPIO object
2845     *
2846     * @param  n                        Description of the Parameter
2847     * @exception  java.io.IOException  Description of the Exception
2848     */
2849    void getPad(int n)
2850      throws java.io.IOException
2851    {
2852      int i;
2853      while (n > 0)
2854      {
2855        if ((i = inend - instart) < n)
2856        {
2857          n -= i;
2858          instart += i;
2859          read(n);
2860          continue;
2861        }
2862        instart += n;
2863        break;
2864      }
2865    }
2866
2867
2868    /**
2869     *  Description of the Method
2870     *
2871     * @param  n                        Description of the Parameter
2872     * @exception  java.io.IOException  Description of the Exception
2873     */
2874    void read(int n)
2875      throws java.io.IOException
2876    {
2877      if (n > inbuffer.length)
2878      {
2879        n = inbuffer.length;
2880      }
2881      instart = inend = 0;
2882      int i;
2883      while (true)
2884      {
2885        recpacket.setData(buf, 0, 1024);
2886        socket.receive(recpacket);
2887
2888        i = recpacket.getLength();
2889        System.arraycopy(recpacket.getData(), 0, inbuffer, inend, i);
2890        if (i == -1)
2891        {
2892          throw new java.io.IOException();
2893        }
2894        inend += i;
2895        break;
2896      }
2897    }
2898  } // innerclass UDPIO
2899
2900}// JOggPlayerListener
2901