1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
 
   2 /* JOrbisPlayer -- pure Java Ogg Vorbis player
 
   4  * Copyright (C) 2000 ymnk, JCraft,Inc.
 
   6  * Written by: 2000 ymnk<ymnk@jcraft.com>
 
   9  *   Monty <monty@xiph.org> and 
 
  10  *   The XIPHOPHORUS Company http://www.xiph.org/ .
 
  11  * JOrbis has been based on their awesome works, Vorbis codec and
 
  12  * JOrbisPlayer depends on JOrbis.
 
  14  * This program is free software; you can redistribute it and/or modify
 
  15  * it under the terms of the GNU General Public License as published by
 
  16  * the Free Software Foundation; either version 2 of the License, or
 
  17  * (at your option) any later version.
 
  19  * This program is distributed in the hope that it will be useful,
 
  20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  22  * GNU General Public License for more details.
 
  24  * You should have received a copy of the GNU General Public License
 
  25  * along with this program; if not, write to the Free Software
 
  26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  30  * Modified 2007 by Roger Bystrøm for pitchfork <pitchfork@remiss.org>
 
  34 import java.io.BufferedReader;
 
  35 import java.io.InputStream;
 
  36 import java.io.InputStreamReader;
 
  40 import java.awt.event.*;
 
  44 import com.jcraft.jorbis.*;
 
  45 import com.jcraft.jogg.*;
 
  47 import javax.sound.sampled.*;
 
  49 public class JOrbisPlayer extends JApplet implements ActionListener {
 
  52         InputStream bitStream=null;
 
  53         URLConnection urlc = null;
 
  55         public static final String PLAY = "Play", STOP = "Stop";
 
  57         static AppletContext acontext=null;
 
  59         static final int BUFSIZE=4096*2;
 
  60         static int convsize=BUFSIZE*2;
 
  61         static byte[] convbuffer=new byte[convsize]; 
 
  83         int left_vol_scale=100;
 
  84         int right_vol_scale=100;
 
  85         SourceDataLine outputLine=null;
 
  86         String current_source=null;
 
  89         int bufferLengthInBytes;
 
  91         boolean playonstartup=false;
 
  93         public Color bgColor = Color.lightGray;
 
  97                 playThread = new PlayWatch();
 
 100                 acontext=getAppletContext();
 
 104                 if(playlist.size()>0){
 
 105                         String s=getParameter("jorbis.player.playonstartup");
 
 106                         if(s!=null && s.equals("yes"))
 
 110                 String c = getParameter("jorbis.player.bgcolor");
 
 113                         bgColor = new Color(Integer.parseInt(c));
 
 114                 } catch (Exception e) {}
 
 116                 System.out.println("Bg-color: specified: " +c + ", using: " +  bgColor.toString());
 
 120                 getContentPane().setLayout(new BorderLayout());
 
 121                 getContentPane().add(panel);
 
 122                 setBackground(bgColor);
 
 136                 os=new StreamState();
 
 151         public void closeOutputLine() {
 
 152                 if(outputLine!=null){
 
 153                         //outputLine.drain();
 
 161         public void closeBitStream() {
 
 162                 if(bitStream!=null) {
 
 165                         } catch(Exception ee) {}
 
 169         SourceDataLine getOutputLine(int channels, int rate) throws Exception {
 
 170                 if(outputLine==null || this.rate!=rate || this.channels!=channels){
 
 172                         init_audio(channels, rate);
 
 178         void init_audio(int channels, int rate) throws Exception {
 
 181                         AudioFormat audioFormat = 
 
 182                                 new AudioFormat((float)rate, 
 
 186                                                 false  // littleEndian
 
 189                                 new DataLine.Info(SourceDataLine.class,
 
 191                                                 AudioSystem.NOT_SPECIFIED);
 
 192                         if (!AudioSystem.isLineSupported(info)) {
 
 193                                 //System.out.println("Line " + info + " not supported.");
 
 198                                 outputLine = (SourceDataLine) AudioSystem.getLine(info);
 
 199                                 //outputLine.addLineListener(this);
 
 200                                 outputLine.open(audioFormat);
 
 201                         } catch (LineUnavailableException ex) { 
 
 202                                 System.out.println("Unable to open the sourceDataLine: " + ex);
 
 204                                         acontext.showStatus("Streaming applet: unable to open output device");
 
 208                         frameSizeInBytes = audioFormat.getFrameSize();
 
 209                         int bufferLengthInFrames = outputLine.getBufferSize()/frameSizeInBytes/2;
 
 210                         bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
 
 213                         this.channels=channels;
 
 216                         System.out.println(ee);
 
 222         private void play_stream(Thread me) {
 
 224                 boolean chained=false;
 
 234                                 int index=oy.buffer(BUFSIZE);
 
 237                                         bytes=bitStream.read(buffer, index, BUFSIZE); 
 
 240                                         System.err.println(e);
 
 249                                         if(oy.pageout(og)!=1){
 
 250                                                 if(bytes<BUFSIZE)break;
 
 251                                                 System.err.println("Input does not appear to be an Ogg bitstream.");
 
 255                                 os.init(og.serialno());
 
 262                                         // error; stream version mismatch perhaps
 
 263                                         System.err.println("Error reading first page of Ogg bitstream data.");
 
 269                                 if(os.packetout(op)!=1){ 
 
 270                                         // no page? must not be vorbis
 
 271                                         System.err.println("Error reading initial header packet.");
 
 275                                 if(vi.synthesis_headerin(vc, op)<0){ 
 
 276                                         // error case; not a vorbis header
 
 277                                         System.err.println("This Ogg bitstream does not contain Vorbis audio data.");
 
 285                                                 int result=oy.pageout(og);
 
 286                                                 if(result==0) break; // Need more data
 
 290                                                                 result=os.packetout(op);
 
 293                                                                         System.err.println("Corrupt secondary header.  Exiting.");
 
 297                                                                 vi.synthesis_headerin(vc, op);
 
 303                                         index=oy.buffer(BUFSIZE);
 
 305                                         try{ bytes=bitStream.read(buffer, index, BUFSIZE); }
 
 307                                                 System.err.println(e);
 
 311                                                 System.err.println("End of file before finding all Vorbis headers!");
 
 318                                         byte[][] ptr=vc.user_comments;
 
 319                                         StringBuffer sb=null;
 
 320                                         if(acontext!=null) sb=new StringBuffer();
 
 321                                         String artist = null, title = null, album = null, tmp;
 
 322                                         for(int j=0; j<ptr.length;j++){
 
 323                                                 if(ptr[j]==null) break;
 
 324                                                 tmp = new String(ptr[j], 0, ptr[j].length-1);
 
 325                                                 System.err.println("Comment: "+tmp);
 
 327                                                         if(tmp.startsWith("ARTIST")) 
 
 328                                                                 artist = new String(ptr[j], 7, ptr[j].length-8);
 
 329                                                         else if(tmp.startsWith("TITLE"))
 
 330                                                                 title = new String(ptr[j], 6, ptr[j].length-7);
 
 331                                                         else if(tmp.startsWith("ALBUM"))
 
 332                                                                 album = new String(ptr[j], 6, ptr[j].length-7);
 
 337                                         System.err.println("Bitstream is "+vi.channels+" channel, "+vi.rate+"Hz");
 
 338                                         System.err.println("Encoded by: "+new String(vc.vendor, 0, vc.vendor.length-1)+"\n");
 
 340                                                 StringBuffer stat = new StringBuffer();
 
 349                                                 else if(album!=null){ // hmm
 
 350                                                         stat.append("Album ");
 
 360                                                         stat.append(" " +sb);
 
 361                                                 acontext.showStatus(stat.toString());
 
 365                                 convsize=BUFSIZE/vi.channels;
 
 367                                 vd.synthesis_init(vi);
 
 370                                 float[][][] _pcmf=new float[1][][];
 
 371                                 int[] _index=new int[vi.channels];
 
 374                                         getOutputLine(vi.channels, vi.rate);
 
 375                                 } catch(Exception e) {
 
 384                                                         System.err.println("player!=me bye.");
 
 388                                                                 acontext.showStatus("");
 
 392                                                 int result=oy.pageout(og);
 
 393                                                 if(result==0)break; // need more data
 
 394                                                 if(result==-1){ // missing or corrupt data at this page position
 
 395                                                         //System.err.println("Corrupt or missing data in bitstream; continuing...");
 
 400                                                         if(og.granulepos()==0){  //
 
 407                                                                 result=os.packetout(op);
 
 408                                                                 if(result==0)break; // need more data
 
 410                                                                         // missing or corrupt data at this page position
 
 413                                                                         // we have a packet.  Decode it
 
 415                                                                         if(vb.synthesis(op)==0){ // test for success!
 
 416                                                                                 vd.synthesis_blockin(vb);
 
 418                                                                         while((samples=vd.synthesis_pcmout(_pcmf, _index))>0){
 
 419                                                                                 float[][] pcmf=_pcmf[0];
 
 420                                                                                 int bout=(samples<convsize?samples:convsize);
 
 422                                                                                 // convert doubles to 16 bit signed ints (host order) and
 
 424                                                                                 for(i=0;i<vi.channels;i++){
 
 428                                                                                         for(int j=0;j<bout;j++){
 
 429                                                                                                 int val=(int)(pcmf[i][mono+j]*32767.);
 
 436                                                                                                 if(val<0) val=val|0x8000;
 
 437                                                                                                 convbuffer[ptr]=(byte)(val);
 
 438                                                                                                 convbuffer[ptr+1]=(byte)(val>>>8);
 
 439                                                                                                 ptr+=2*(vi.channels);
 
 442                                                                                 outputLine.write(convbuffer, 0, 2*vi.channels*bout);
 
 443                                                                                 vd.synthesis_read(bout);
 
 447                                                         if(og.eos()!=0)eos=1;
 
 452                                                 index=oy.buffer(BUFSIZE);
 
 454                                                 try{ bytes=bitStream.read(buffer,index,BUFSIZE); }
 
 456                                                         System.err.println(e);
 
 477                         acontext.showStatus("");
 
 490         Vector playlist=new Vector();
 
 492         public void actionPerformed(ActionEvent e){
 
 493                 String command=((JButton)(e.getSource())).getText();
 
 494                 if(command.equals(PLAY) && player==null){ 
 
 497                 else if(player!=null){
 
 502         private void playFromOwnThread() {
 
 503                 synchronized(playThread) {
 
 508         private void playFromThisThread() {
 
 511                 /*player=new Thread(this);
 
 514                 start_button.setText(STOP); 
 
 515                 String item=getShoutSource();
 
 520                 bitStream=selectSource(item);
 
 521                 player = Thread.currentThread();
 
 525                 else System.out.println("Bitstream is null");
 
 534         public boolean isPlaying() {
 
 535                 return player != null;
 
 538         public void play_sound(){
 
 542         public void stop_sound(){
 
 544                 start_button.setText(PLAY);
 
 548         InputStream selectSource(String item){
 
 549                 if(item.endsWith(".pls")){
 
 550                         item=fetch_pls(item);
 
 551                         if(item==null) return null;
 
 552                         //System.out.println("fetch: "+item);
 
 554                 else if(item.endsWith(".m3u")){
 
 555                         item=fetch_m3u(item);
 
 556                         if(item==null)return null;
 
 557                         //System.out.println("fetch: "+item);
 
 560                 if(!item.endsWith(".ogg")){
 
 565                 URLConnection urlc=null;
 
 568                         url=new URL(getCodeBase(), item);
 
 569                         urlc=url.openConnection();
 
 570                         urlc.setRequestProperty("Pragma", "no-cache");
 
 571                         urlc.setUseCaches(false);
 
 572                         is=urlc.getInputStream();
 
 573                         current_source=url.getProtocol()+"://"+url.getHost()+":"+url.getPort()+url.getFile();
 
 576                         System.err.println(ee);             
 
 580                         System.out.println("Selected input stream is null");
 
 584                 System.out.println("Select: "+item);
 
 588         String fetch_pls(String pls){
 
 589                 InputStream pstream=null;
 
 590                 if(pls.startsWith("http://")){
 
 593                                 url=new URL(getCodeBase(), pls);
 
 594                                  urlc=url.openConnection();
 
 595                                 pstream=urlc.getInputStream();
 
 598                                 System.err.println(ee);             
 
 605                         try{line=readline(pstream);}catch(Exception e){}
 
 607                         if(line.startsWith("File1=")){
 
 608                                 byte[] foo=line.getBytes();
 
 610                                 for(;i<foo.length; i++){
 
 611                                         if(foo[i]==0x0d)break;
 
 613                                 return line.substring(6, i);
 
 619         String fetch_m3u(String m3u){
 
 620                 InputStream pstream=null;
 
 621                 if(m3u.startsWith("http://")){
 
 624                                 url=new URL(getCodeBase(), m3u);
 
 626                                 URLConnection urlc=url.openConnection();
 
 627                                 pstream=urlc.getInputStream();
 
 630                                 System.err.println(ee);             
 
 637                         try{line=readline(pstream);}catch(Exception e){}
 
 647                 for(int i=0; i<10; i++){
 
 648                         s=getParameter("jorbis.player.play."+i);
 
 649                         System.out.println("Play" + i + ": " + s);
 
 652                         playlist.addElement(s);
 
 656         private String readline(InputStream is) {
 
 657                 StringBuffer rtn=new StringBuffer();
 
 668                         if(temp!=0 && temp!='\n')
 
 669                                 rtn.append((char)temp);
 
 671                 return(rtn.toString());
 
 674         public JOrbisPlayer(){
 
 678         JButton start_button;
 
 683                 start_button=new JButton(PLAY);
 
 684                 start_button.addActionListener(this);
 
 685                 panel.add(start_button);
 
 686                 panel.setBackground(bgColor);
 
 689         public String getShoutSource() {
 
 691                         return (String)playlist.firstElement();
 
 693                 catch(NoSuchElementException e) { 
 
 698         /* since js don't have proper access right's we'll need a seperate watcher thread */
 
 699         public class PlayWatch extends Thread {
 
 701                 public PlayWatch() { }
 
 708                                         }catch (InterruptedException e) {}
 
 710                                 playFromThisThread();