Initial revision of ripping support. (Reads track info, doesn't rip tracks
yet.) git-svn-id: https://samskivert.googlecode.com/svn/trunk@7 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// $Id: CDParanoiaRipper.java,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
package robodj.rip;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import gnu.regexp.*;
|
||||
|
||||
/**
|
||||
* A ripper implementation that uses cdparanoia to do it's job.
|
||||
*/
|
||||
public class CDParanoiaRipper implements Ripper
|
||||
{
|
||||
public TrackInfo[] getTrackInfo () throws RipException
|
||||
{
|
||||
try {
|
||||
// fork off a cdparanoia process to read the TOC
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Process tocproc = rt.exec("cdparanoia -Q");
|
||||
|
||||
InputStream in = tocproc.getErrorStream();
|
||||
BufferedInputStream bin = new BufferedInputStream(in);
|
||||
DataInputStream din = new DataInputStream(bin);
|
||||
|
||||
String inline;
|
||||
StringBuffer input = new StringBuffer();
|
||||
ArrayList flist = new ArrayList();
|
||||
|
||||
// an input line that we're interested in looks something like
|
||||
// this:
|
||||
//
|
||||
// 1. 17980 [03:59.55] 0 [00:00.00] no no 2
|
||||
RE regex =
|
||||
new RE("^\\s*\\d+\\.\\s+(\\d+)\\s\\[\\S*\\]\\s+(\\d+)");
|
||||
|
||||
while ((inline = din.readLine()) != null) {
|
||||
// skip blank lines and lines that are in the header
|
||||
if (inline.trim().length() == 0 ||
|
||||
inline.indexOf("mit.edu") != -1 ||
|
||||
inline.indexOf("cdparanoia") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// System.out.println("Matching: " + inline);
|
||||
|
||||
// keep track of all of the input in case we need to
|
||||
// report an error later
|
||||
input.append(inline).append("\n");
|
||||
|
||||
// see if we match our regular expression
|
||||
REMatch match = regex.getMatch(inline);
|
||||
if (match != null) {
|
||||
flist.add(match.toString(1));
|
||||
flist.add(match.toString(2));
|
||||
}
|
||||
}
|
||||
|
||||
// check the return value of the process
|
||||
try {
|
||||
int retval = tocproc.waitFor();
|
||||
if (retval != 0) {
|
||||
// ship off the error output from cdparanoia
|
||||
throw new RipException(input.toString());
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
// why we were interrupted I can only speculate, but we'll
|
||||
// go ahead and freak out anyway
|
||||
throw new RipException("Interrupted while waiting for " +
|
||||
"cdparanoia process to exit.");
|
||||
}
|
||||
|
||||
// parse the frame offsets and stick them in an array
|
||||
TrackInfo[] frames = new TrackInfo[flist.size()/2];
|
||||
for (int i = 0; i < frames.length; i++) {
|
||||
try {
|
||||
frames[i] = new TrackInfo();
|
||||
frames[i].length =
|
||||
Integer.parseInt((String)flist.get(2*i));
|
||||
frames[i].offset =
|
||||
Integer.parseInt((String)flist.get(2*i+1));
|
||||
|
||||
// for some reason, cdparanoia reports track offsets
|
||||
// starting from zero but CDDB assumes they start at 2
|
||||
// seconds, so we have to adjust... sigh.
|
||||
frames[i].offset += 2 * RipUtil.FRAMES_PER_SECOND;
|
||||
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new RipException("Bogus frame value for track " +
|
||||
(i+1) + ": " + nfe.getMessage() +
|
||||
"\n\n" + input);
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
|
||||
} catch (REException ree) {
|
||||
throw new RipException("Can't compile regular expression?! " +
|
||||
ree);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new RipException("Error talking to rip process: " + ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# $Id: Makefile,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
ROOT = ../..
|
||||
|
||||
SRCS = \
|
||||
CDParanoiaRipper.java \
|
||||
RipException.java \
|
||||
RipTest.java \
|
||||
RipUtil.java \
|
||||
Ripper.java \
|
||||
|
||||
include $(ROOT)/build/Makefile.java
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// $Id: RipException.java,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
package robodj.rip;
|
||||
|
||||
/**
|
||||
* A rip exception can be thrown to indicate that some problem was
|
||||
* encountered during some part of the ripping process.
|
||||
*/
|
||||
public class RipException extends Exception
|
||||
{
|
||||
public RipException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// $Id: RipTest.java,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
package robodj.rip;
|
||||
|
||||
import com.samskivert.net.cddb.*;
|
||||
|
||||
public class RipTest
|
||||
{
|
||||
public static void lookupCDDB (String hostname, Ripper.TrackInfo[] info)
|
||||
throws Exception
|
||||
{
|
||||
CDDB cddb = new CDDB();
|
||||
|
||||
long discid = RipUtil.computeDiscId(info);
|
||||
String cdid = Long.toString(discid, 0x10);
|
||||
|
||||
// create an array with the track offsets
|
||||
int[] offsets = new int[info.length];
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
offsets[i] = info[i].offset;
|
||||
}
|
||||
|
||||
int length = RipUtil.computeDiscLength(info);
|
||||
|
||||
try {
|
||||
String rsp = cddb.connect(hostname);
|
||||
|
||||
// set the timeout to 30 seconds
|
||||
cddb.setTimeout(30*1000);
|
||||
|
||||
// try a test query
|
||||
CDDB.Entry[] entries = cddb.query(cdid, offsets, length);
|
||||
|
||||
if (entries == null || entries.length == 0) {
|
||||
System.out.println("No match for " + cdid + ".");
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
System.out.println("Match " + entries[i].category + "/" +
|
||||
entries[i].cdid + "/" +
|
||||
entries[i].title);
|
||||
}
|
||||
|
||||
CDDB.Detail detail = cddb.read(entries[0].category,
|
||||
entries[0].cdid);
|
||||
System.out.println("Title: " + detail.title);
|
||||
for (int i = 0; i < detail.trackNames.length; i++) {
|
||||
System.out.println(pad(i) + ": " + detail.trackNames[i]);
|
||||
}
|
||||
System.out.println("Extended data: " + detail.extendedData);
|
||||
for (int i = 0; i < detail.extendedTrackData.length; i++) {
|
||||
System.out.println(pad(i) + ": " +
|
||||
detail.extendedTrackData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
cddb.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected static String pad (int value)
|
||||
{
|
||||
return ((value > 9) ? "" : " ") + value;
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
try {
|
||||
Ripper ripper = new CDParanoiaRipper();
|
||||
Ripper.TrackInfo[] info = ripper.getTrackInfo();
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
System.out.println((i+1) + ": " + info[i].offset + ", " +
|
||||
info[i].length);
|
||||
}
|
||||
|
||||
if (info.length == 0) {
|
||||
System.out.println("Matched no tracks?!");
|
||||
|
||||
} else {
|
||||
// finally try the CDDB lookup
|
||||
lookupCDDB("us.cddb.com", info);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// $Id: RipUtil.java,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
package robodj.rip;
|
||||
|
||||
/**
|
||||
* This class contains utility functions related to ripping.
|
||||
*/
|
||||
public class RipUtil
|
||||
{
|
||||
/**
|
||||
* Compact discs contain this many frames per second.
|
||||
*/
|
||||
public static int FRAMES_PER_SECOND = 75;
|
||||
|
||||
/**
|
||||
* Computes the CDDB disc id for the specified CD using the supplied
|
||||
* track info.
|
||||
*/
|
||||
public static long computeDiscId (Ripper.TrackInfo[] info)
|
||||
{
|
||||
// first we sum all the digits in the numbers that represent the
|
||||
// number of seconds for each track
|
||||
long digits = 0;
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
int secs = info[i].offset/FRAMES_PER_SECOND;
|
||||
digits += addDigits(secs);
|
||||
}
|
||||
|
||||
// determine the total number of seconds of all the tracks on the
|
||||
// CD (doing so by subtracting the offset of the end of the disc
|
||||
// from the offset of the beginning which is how the CDDB
|
||||
// algorithm does it)
|
||||
long totsecs = (long)computeDiscLength(info);
|
||||
|
||||
// finally combine these two values with the number of tracks on
|
||||
// the disc into the actual disc id
|
||||
return ((digits & 0xFF) << 24) | (totsecs << 8) | info.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of the CD (in seconds) as computed from the
|
||||
* supplied frame information.
|
||||
*/
|
||||
public static int computeDiscLength (Ripper.TrackInfo[] info)
|
||||
{
|
||||
return (info[info.length-1].offset +
|
||||
info[info.length-1].length -
|
||||
info[0].offset)/FRAMES_PER_SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sum of the digits in the supplied number (eg. 6 for 15,
|
||||
* 8 for 251, etc.).
|
||||
*/
|
||||
protected static int addDigits (int value)
|
||||
{
|
||||
int rv = 0;
|
||||
while (value > 0) {
|
||||
rv += value%10;
|
||||
value /= 10;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id: Ripper.java,v 1.1 2000/10/30 21:08:53 mdb Exp $
|
||||
|
||||
package robodj.rip;
|
||||
|
||||
/**
|
||||
* The ripper interface is used to manipulate a particular piece of CD
|
||||
* ripping software in the ways needed by the RoboDJ system.
|
||||
*/
|
||||
public interface Ripper
|
||||
{
|
||||
/**
|
||||
* Instances of this object are used to communicate the track
|
||||
* information of a CD back to the caller.
|
||||
*/
|
||||
public class TrackInfo
|
||||
{
|
||||
/**
|
||||
* The offset of the start of this track (in frames).
|
||||
*/
|
||||
public int offset;
|
||||
|
||||
/**
|
||||
* The length of this track (in frames).
|
||||
*/
|
||||
public int length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should return an array with an entry for each track
|
||||
* on the CD (in order) containing the frame offset and length of that
|
||||
* track. This function will be called in another thread and has 30
|
||||
* seconds to do its job or it will be declared delinquent and
|
||||
* ignored.
|
||||
*
|
||||
* @exception RipException can be thrown if some problem occurs trying
|
||||
* to read the CD table of contents (like lack of access to the CDROM
|
||||
* device or any other errors).
|
||||
*/
|
||||
public TrackInfo[] getTrackInfo () throws RipException;
|
||||
}
|
||||
Reference in New Issue
Block a user