package player;
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbisPlayer -- pure Java Ogg Vorbis player
*
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec and
* JOrbisPlayer depends on JOrbis.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import com.jcraft.jogg.Packet;
import com.jcraft.jogg.Page;
import com.jcraft.jogg.StreamState;
import com.jcraft.jogg.SyncState;
import com.jcraft.jorbis.Block;
import com.jcraft.jorbis.Comment;
import com.jcraft.jorbis.DspState;
import com.jcraft.jorbis.Info;
import java.applet.AppletContext;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
import java.util.Vector;
import javax.sound.sampled.*;
import javax.swing.*;
public class JOrbisPlayer extends JApplet implements ActionListener, Runnable {
private static final long serialVersionUID = 1L;
boolean running_as_applet = true;
Thread player = null;
InputStream bitStream = null;
int udp_port = -1;
String udp_baddress = null;
static AppletContext acontext = null;
static final int BUFSIZE = 4096 * 2;
static int convsize = BUFSIZE * 2;
static byte[] convbuffer = new byte[convsize];
private int RETRY = 3;
int retry = RETRY;
String playlistfile = "playlist";
boolean icestats = false;
SyncState oy;
StreamState os;
Page og;
Packet op;
Info vi;
Comment vc;
DspState vd;
Block vb;
byte[] buffer = null;
int bytes = 0;
int format;
int rate = 0;
int channels = 0;
int left_vol_scale = 100;
int right_vol_scale = 100;
SourceDataLine outputLine = null;
String current_source = null;
int frameSizeInBytes;
int bufferLengthInBytes;
boolean playonstartup = false;
public void init() {
running_as_applet = true;
acontext = getAppletContext();
String s = getParameter("jorbis.player.playlist");
playlistfile = s;
s = getParameter("jorbis.player.icestats");
if (s != null && s.equals("yes")) {
icestats = true;
}
loadPlaylist();
initUI();
if (playlist.size() > 0) {
s = getParameter("jorbis.player.playonstartup");
if (s != null && s.equals("yes")) {
playonstartup = true;
}
}
setBackground(Color.lightGray);
// setBackground(Color.white);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(panel);
}
public void start() {
super.start();
if (playonstartup) {
play_sound();
}
}
void init_jorbis() {
oy = new SyncState();
os = new StreamState();
og = new Page();
op = new Packet();
vi = new Info();
vc = new Comment();
vd = new DspState();
vb = new Block(vd);
buffer = null;
bytes = 0;
oy.init();
}
SourceDataLine getOutputLine(int channels, int rate) {
if (outputLine == null || this.rate != rate
|| this.channels != channels) {
if (outputLine != null) {
outputLine.drain();
outputLine.stop();
outputLine.close();
}
init_audio(channels, rate);
outputLine.start();
}
return outputLine;
}
void init_audio(int channels, int rate) {
try {
// ClassLoader originalClassLoader=null;
// try{
// originalClassLoader=Thread.currentThread().getContextClassLoader();
// Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
// }
// catch(Exception ee){
// System.out.println(ee);
// }
AudioFormat audioFormat = new AudioFormat((float) rate, 16,
channels, true, // PCM_Signed
false // littleEndian
);
DataLine.Info info = new DataLine.Info(SourceDataLine.class,
audioFormat, AudioSystem.NOT_SPECIFIED);
if (!AudioSystem.isLineSupported(info)) {
// System.out.println("Line " + info + " not supported.");
return;
}
try {
outputLine = (SourceDataLine) AudioSystem.getLine(info);
// outputLine.addLineListener(this);
outputLine.open(audioFormat);
} catch (LineUnavailableException ex) {
System.out.println("Unable to open the sourceDataLine: " + ex);
return;
} catch (IllegalArgumentException ex) {
System.out.println("Illegal Argument: " + ex);
return;
}
frameSizeInBytes = audioFormat.getFrameSize();
int bufferLengthInFrames = outputLine.getBufferSize()
/ frameSizeInBytes / 2;
bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
// if(originalClassLoader!=null)
// Thread.currentThread().setContextClassLoader(originalClassLoader);
this.rate = rate;
this.channels = channels;
} catch (Exception ee) {
System.out.println(ee);
}
}
private int item2index(String item) {
for (int i = 0; i < cb.getItemCount(); i++) {
String foo = (String) (cb.getItemAt(i));
if (item.equals(foo))
return i;
}
cb.addItem(item);
return cb.getItemCount() - 1;
}
public void run() {
Thread me = Thread.currentThread();
String item = (String) (cb.getSelectedItem());
int current_index = item2index(item);
while (true) {
item = (String) (cb.getItemAt(current_index));
cb.setSelectedIndex(current_index);
bitStream = selectSource(item);
if (bitStream != null) {
if (udp_port != -1) {
play_udp_stream(me);
} else {
play_stream(me);
}
} else if (cb.getItemCount() == 1) {
break;
}
if (player != me) {
break;
}
bitStream = null;
current_index++;
if (current_index >= cb.getItemCount()) {
current_index = 0;
}
if (cb.getItemCount() <= 0)
break;
}
player = null;
start_button.setText("start");
}
private void play_stream(Thread me) {
boolean chained = false;
init_jorbis();
retry = RETRY;
// System.out.println("play_stream>");
loop: while (true) {
int eos = 0;
int index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = bitStream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
oy.wrote(bytes);
if (chained) { //
chained = false; //
} //
else { //
if (oy.pageout(og) != 1) {
if (bytes < BUFSIZE)
break;
System.err
.println("Input does not appear to be an Ogg bitstream.");
return;
}
} //
os.init(og.serialno());
os.reset();
vi.init();
vc.init();
if (os.pagein(og) < 0) {
// error; stream version mismatch perhaps
System.err
.println("Error reading first page of Ogg bitstream data.");
return;
}
retry = RETRY;
if (os.packetout(op) != 1) {
// no page? must not be vorbis
System.err.println("Error reading initial header packet.");
break;
// return;
}
if (vi.synthesis_headerin(vc, op) < 0) {
// error case; not a vorbis header
System.err
.println("This Ogg bitstream does not contain Vorbis audio data.");
return;
}
int i = 0;
while (i < 2) {
while (i < 2) {
int result = oy.pageout(og);
if (result == 0)
break; // Need more data
if (result == 1) {
os.pagein(og);
while (i < 2) {
result = os.packetout(op);
if (result == 0)
break;
if (result == -1) {
System.err
.println("Corrupt secondary header. Exiting.");
// return;
break loop;
}
vi.synthesis_headerin(vc, op);
i++;
}
}
}
index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = bitStream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
if (bytes == 0 && i < 2) {
System.err
.println("End of file before finding all Vorbis headers!");
return;
}
oy.wrote(bytes);
}
{
byte[][] ptr = vc.user_comments;
StringBuffer sb = null;
if (acontext != null)
sb = new StringBuffer();
for (int j = 0; j < ptr.length; j++) {
if (ptr[j] == null)
break;
System.err.println("Comment: "
+ new String(ptr[j], 0, ptr[j].length - 1));
if (sb != null)
sb.append(" "
+ new String(ptr[j], 0, ptr[j].length - 1));
}
System.err.println("Bitstream is " + vi.channels + " channel, "
+ vi.rate + "Hz");
System.err
.println("Encoded by: "
+ new String(vc.vendor, 0, vc.vendor.length - 1)
+ "\n");
if (sb != null)
acontext.showStatus(sb.toString());
}
convsize = BUFSIZE / vi.channels;
vd.synthesis_init(vi);
vb.init(vd);
float[][][] _pcmf = new float[1][][];
int[] _index = new int[vi.channels];
getOutputLine(vi.channels, vi.rate);
while (eos == 0) {
while (eos == 0) {
if (player != me) {
try {
bitStream.close();
outputLine.drain();
outputLine.stop();
outputLine.close();
outputLine = null;
} catch (Exception ee) {
}
return;
}
int result = oy.pageout(og);
if (result == 0)
break; // need more data
if (result == -1) { // missing or corrupt data at this page
// position
// System.err.println("Corrupt or missing data in bitstream; continuing...");
} else {
os.pagein(og);
if (og.granulepos() == 0) { //
chained = true; //
eos = 1; //
break; //
} //
while (true) {
result = os.packetout(op);
if (result == 0)
break; // need more data
if (result == -1) { // missing or corrupt data at
// this page position
// no reason to complain; already complained
// above
// System.err.println("no reason to complain; already complained above");
} else {
// we have a packet. Decode it
int samples;
if (vb.synthesis(op) == 0) { // test for
// success!
vd.synthesis_blockin(vb);
}
while ((samples = vd.synthesis_pcmout(_pcmf,
_index)) > 0) {
float[][] pcmf = _pcmf[0];
int bout = (samples < convsize ? samples
: convsize);
// convert doubles to 16 bit signed ints
// (host order) and
// interleave
for (i = 0; i < vi.channels; i++) {
int ptr = i * 2;
// int ptr=i;
int mono = _index[i];
for (int j = 0; j < bout; j++) {
int val = (int) (pcmf[i][mono + j] * 32767.);
if (val > 32767) {
val = 32767;
}
if (val < -32768) {
val = -32768;
}
if (val < 0)
val = val | 0x8000;
convbuffer[ptr] = (byte) (val);
convbuffer[ptr + 1] = (byte) (val >>> 8);
ptr += 2 * (vi.channels);
}
}
outputLine.write(convbuffer, 0, 2
* vi.channels * bout);
vd.synthesis_read(bout);
}
}
}
if (og.eos() != 0)
eos = 1;
}
}
if (eos == 0) {
index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = bitStream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
if (bytes == -1) {
break;
}
oy.wrote(bytes);
if (bytes == 0)
eos = 1;
}
}
os.clear();
vb.clear();
vd.clear();
vi.clear();
}
oy.clear();
try {
if (bitStream != null)
bitStream.close();
} catch (Exception e) {
}
}
private void play_udp_stream(Thread me) {
init_jorbis();
try {
loop: while (true) {
int index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = bitStream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
oy.wrote(bytes);
if (oy.pageout(og) != 1) {
// if(bytes<BUFSIZE)break;
System.err
.println("Input does not appear to be an Ogg bitstream.");
return;
}
os.init(og.serialno());
os.reset();
vi.init();
vc.init();
if (os.pagein(og) < 0) {
// error; stream version mismatch perhaps
System.err
.println("Error reading first page of Ogg bitstream data.");
return;
}
if (os.packetout(op) != 1) {
// no page? must not be vorbis
System.err.println("Error reading initial header packet.");
// break;
return;
}
if (vi.synthesis_headerin(vc, op) < 0) {
// error case; not a vorbis header
System.err
.println("This Ogg bitstream does not contain Vorbis audio data.");
return;
}
int i = 0;
while (i < 2) {
while (i < 2) {
int result = oy.pageout(og);
if (result == 0)
break; // Need more data
if (result == 1) {
os.pagein(og);
while (i < 2) {
result = os.packetout(op);
if (result == 0)
break;
if (result == -1) {
System.err
.println("Corrupt secondary header. Exiting.");
// return;
break loop;
}
vi.synthesis_headerin(vc, op);
i++;
}
}
}
if (i == 2)
break;
index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = bitStream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
if (bytes == 0 && i < 2) {
System.err
.println("End of file before finding all Vorbis headers!");
return;
}
oy.wrote(bytes);
}
break;
}
} catch (Exception e) {
}
try {
bitStream.close();
} catch (Exception e) {
}
UDPIO io = null;
try {
io = new UDPIO(udp_port);
} catch (Exception e) {
return;
}
bitStream = io;
play_stream(me);
}
public void stop() {
if (player == null) {
try {
outputLine.drain();
outputLine.stop();
outputLine.close();
outputLine = null;
if (bitStream != null)
bitStream.close();
} catch (Exception e) {
}
}
player = null;
}
Vector playlist = new Vector();
public void actionPerformed(ActionEvent e) {
if (e.getSource() == stats_button) {
String item = (String) (cb.getSelectedItem());
if (!item.startsWith("http://"))
return;
if (item.endsWith(".pls")) {
item = fetch_pls(item);
if (item == null)
return;
} else if (item.endsWith(".m3u")) {
item = fetch_m3u(item);
if (item == null)
return;
}
byte[] foo = item.getBytes();
for (int i = foo.length - 1; i >= 0; i--) {
if (foo[i] == '/') {
item = item.substring(0, i + 1) + "stats.xml";
break;
}
}
System.out.println(item);
try {
URL url = null;
if (running_as_applet)
url = new URL(getCodeBase(), item);
else
url = new URL(item);
BufferedReader stats = new BufferedReader(
new InputStreamReader(url.openConnection()
.getInputStream()));
while (true) {
String bar = stats.readLine();
if (bar == null)
break;
System.out.println(bar);
}
} catch (Exception ee) {
// System.err.println(ee);
}
return;
}
String command = ((JButton) (e.getSource())).getText();
if (command.equals("start") && player == null) {
play_sound();
} else if (player != null) {
stop_sound();
}
}
public String getTitle() {
return (String) (cb.getSelectedItem());
}
public void play_sound() {
if (player != null)
return;
player = new Thread(this);
start_button.setText("stop");
player.start();
}
public void stop_sound() {
if (player == null)
return;
player = null;
start_button.setText("start");
}
InputStream selectSource(String item) {
if (item.endsWith(".pls")) {
item = fetch_pls(item);
if (item == null)
return null;
// System.out.println("fetch: "+item);
} else if (item.endsWith(".m3u")) {
item = fetch_m3u(item);
if (item == null)
return null;
// System.out.println("fetch: "+item);
}
if (!item.endsWith(".ogg")) {
return null;
}
InputStream is = null;
URLConnection urlc = null;
try {
URL url = null;
if (running_as_applet)
url = new URL(getCodeBase(), item);
else
url = new URL(item);
urlc = url.openConnection();
is = urlc.getInputStream();
current_source = url.getProtocol() + "://" + url.getHost() + ":"
+ url.getPort() + url.getFile();
} catch (Exception ee) {
System.err.println(ee);
}
if (is == null && !running_as_applet) {
try {
is = new FileInputStream(System.getProperty("user.dir")
+ System.getProperty("file.separator") + item);
current_source = null;
} catch (Exception ee) {
System.err.println(ee);
}
}
if (is == null)
return null;
System.out.println("Select: " + item);
{
boolean find = false;
for (int i = 0; i < cb.getItemCount(); i++) {
String foo = (String) (cb.getItemAt(i));
if (item.equals(foo)) {
find = true;
break;
}
}
if (!find) {
cb.addItem(item);
}
}
int i = 0;
String s = null;
String t = null;
udp_port = -1;
udp_baddress = null;
while (urlc != null && true) {
s = urlc.getHeaderField(i);
t = urlc.getHeaderFieldKey(i);
if (s == null)
break;
i++;
if (t != null && t.equals("udp-port")) {
try {
udp_port = Integer.parseInt(s);
} catch (Exception ee) {
System.err.println(ee);
}
} else if (t != null && t.equals("udp-broadcast-address")) {
udp_baddress = s;
}
}
return is;
}
String fetch_pls(String pls) {
InputStream pstream = null;
if (pls.startsWith("http://")) {
try {
URL url = null;
if (running_as_applet)
url = new URL(getCodeBase(), pls);
else
url = new URL(pls);
URLConnection urlc = url.openConnection();
pstream = urlc.getInputStream();
} catch (Exception ee) {
System.err.println(ee);
return null;
}
}
if (pstream == null && !running_as_applet) {
try {
pstream = new FileInputStream(System.getProperty("user.dir")
+ System.getProperty("file.separator") + pls);
} catch (Exception ee) {
System.err.println(ee);
return null;
}
}
String line = null;
while (true) {
try {
line = readline(pstream);
} catch (Exception e) {
}
if (line == null)
break;
if (line.startsWith("File1=")) {
byte[] foo = line.getBytes();
int i = 6;
for (; i < foo.length; i++) {
if (foo[i] == 0x0d)
break;
}
return line.substring(6, i);
}
}
return null;
}
String fetch_m3u(String m3u) {
InputStream pstream = null;
if (m3u.startsWith("http://")) {
try {
URL url = null;
if (running_as_applet)
url = new URL(getCodeBase(), m3u);
else
url = new URL(m3u);
URLConnection urlc = url.openConnection();
pstream = urlc.getInputStream();
} catch (Exception ee) {
System.err.println(ee);
return null;
}
}
if (pstream == null && !running_as_applet) {
try {
pstream = new FileInputStream(System.getProperty("user.dir")
+ System.getProperty("file.separator") + m3u);
} catch (Exception ee) {
System.err.println(ee);
return null;
}
}
String line = null;
while (true) {
try {
line = readline(pstream);
} catch (Exception e) {
}
if (line == null)
break;
return line;
}
return null;
}
void loadPlaylist() {
if (running_as_applet) {
String s = null;
for (int i = 0; i < 10; i++) {
s = getParameter("jorbis.player.play." + i);
if (s == null)
break;
playlist.addElement(s);
}
}
if (playlistfile == null) {
return;
}
try {
InputStream is = null;
try {
URL url = null;
if (running_as_applet)
url = new URL(getCodeBase(), playlistfile);
else
url = new URL(playlistfile);
URLConnection urlc = url.openConnection();
is = urlc.getInputStream();
} catch (Exception ee) {
}
if (is == null && !running_as_applet) {
try {
is = new FileInputStream(System.getProperty("user.dir")
+ System.getProperty("file.separator")
+ playlistfile);
} catch (Exception ee) {
}
}
if (is == null)
return;
while (true) {
String line = readline(is);
if (line == null)
break;
byte[] foo = line.getBytes();
for (int i = 0; i < foo.length; i++) {
if (foo[i] == 0x0d) {
line = new String(foo, 0, i);
break;
}
}
playlist.addElement(line);
}
} catch (Exception e) {
System.out.println(e);
}
}
private String readline(InputStream is) {
StringBuffer rtn = new StringBuffer();
int temp;
do {
try {
temp = is.read();
} catch (Exception e) {
return (null);
}
if (temp == -1) {
String str = rtn.toString();
if (str.length() == 0)
return (null);
return str;
}
if (temp != 0 && temp != '\n' && temp != '\r')
rtn.append((char) temp);
} while (temp != '\n' && temp != '\r');
return (rtn.toString());
}
public JOrbisPlayer() {
}
JPanel panel;
JComboBox cb;
JButton start_button;
JButton stats_button;
void initUI() {
panel = new JPanel();
cb = new JComboBox(playlist);
cb.setEditable(true);
panel.add(cb);
start_button = new JButton("start");
start_button.addActionListener(this);
panel.add(start_button);
if (icestats) {
stats_button = new JButton("IceStats");
stats_button.addActionListener(this);
panel.add(stats_button);
}
}
class UDPIO extends InputStream {
InetAddress address;
DatagramSocket socket = null;
DatagramPacket sndpacket;
DatagramPacket recpacket;
byte[] buf = new byte[1024];
// String host;
int port;
byte[] inbuffer = new byte[2048];
byte[] outbuffer = new byte[1024];
int instart = 0, inend = 0, outindex = 0;
UDPIO(int port) {
this.port = port;
try {
socket = new DatagramSocket(port);
} catch (Exception e) {
System.err.println(e);
}
recpacket = new DatagramPacket(buf, 1024);
}
void setTimeout(int i) {
try {
socket.setSoTimeout(i);
} catch (Exception e) {
System.out.println(e);
}
}
int getByte() throws java.io.IOException {
if ((inend - instart) < 1) {
read(1);
}
return inbuffer[instart++] & 0xff;
}
int getByte(byte[] array) throws java.io.IOException {
return getByte(array, 0, array.length);
}
int getByte(byte[] array, int begin, int length)
throws java.io.IOException {
int i = 0;
int foo = begin;
while (true) {
if ((i = (inend - instart)) < length) {
if (i != 0) {
System.arraycopy(inbuffer, instart, array, begin, i);
begin += i;
length -= i;
instart += i;
}
read(length);
continue;
}
System.arraycopy(inbuffer, instart, array, begin, length);
instart += length;
break;
}
return begin + length - foo;
}
int getShort() throws java.io.IOException {
if ((inend - instart) < 2) {
read(2);
}
int s = 0;
s = inbuffer[instart++] & 0xff;
s = ((s << 8) & 0xffff) | (inbuffer[instart++] & 0xff);
return s;
}
int getInt() throws java.io.IOException {
if ((inend - instart) < 4) {
read(4);
}
int i = 0;
i = inbuffer[instart++] & 0xff;
i = ((i << 8) & 0xffff) | (inbuffer[instart++] & 0xff);
i = ((i << 8) & 0xffffff) | (inbuffer[instart++] & 0xff);
i = (i << 8) | (inbuffer[instart++] & 0xff);
return i;
}
void getPad(int n) throws java.io.IOException {
int i;
while (n > 0) {
if ((i = inend - instart) < n) {
n -= i;
instart += i;
read(n);
continue;
}
instart += n;
break;
}
}
void read(int n) throws java.io.IOException {
if (n > inbuffer.length) {
n = inbuffer.length;
}
instart = inend = 0;
int i;
while (true) {
recpacket.setData(buf, 0, 1024);
socket.receive(recpacket);
i = recpacket.getLength();
System.arraycopy(recpacket.getData(), 0, inbuffer, inend, i);
if (i == -1) {
throw new java.io.IOException();
}
inend += i;
break;
}
}
public void close() throws java.io.IOException {
socket.close();
}
public int read() throws java.io.IOException {
return 0;
}
public int read(byte[] array, int begin, int length)
throws java.io.IOException {
return getByte(array, begin, length);
}
}
public static void main(String[] arg) {
JFrame frame = new JFrame("JOrbisPlayer");
frame.setBackground(Color.lightGray);
frame.setBackground(Color.white);
frame.getContentPane().setLayout(new BorderLayout());
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
JOrbisPlayer player = new JOrbisPlayer();
player.running_as_applet = false;
if (arg.length > 0) {
for (int i = 0; i < arg.length; i++) {
player.playlist.addElement(arg[i]);
}
}
player.loadPlaylist();
player.initUI();
frame.getContentPane().add(player.panel);
frame.pack();
frame.setVisible(true);
}
}