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();