/*
* OggTag.java
*
* Created on Jun 9, 2007, 1:28:56 AM
*
* NB: This code was originally taken from the example code provided
* with the jorbis library: http://www.jcraft.com/jorbis/
*
*/
package com.pugh.sockso.music.tag;
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import com.jcraft.jogg.StreamState;
import com.jcraft.jogg.SyncState;
import com.jcraft.jogg.Page;
import com.jcraft.jogg.Packet;
import com.jcraft.jorbis.Comment;
import com.jcraft.jorbis.Info;
import com.pugh.sockso.Utils;
import org.apache.log4j.Logger;
public class OggTag extends AudioTag {
private static final Logger log = Logger.getLogger( OggTag.class );
private State state = null;
private static final int CHUNKSIZE = 4096;
public void parse( final File file ) throws IOException {
InputStream in = null;
try {
in = new FileInputStream( file );
state = new State();
read( in );
for ( int i=0; i<state.vc.comments; i++ ) {
final String c = (String) state.vc.getComment( i );
final String[] parts = c.split( "=" );
final String name = parts.length > 0 ? parts[0].toLowerCase() : "";
final String value = parts.length > 1 ? parts[1] : "";
if ( name.equals("tracknumber") )
setTrackNumber( value );
else if ( name.equals("artist") )
artistTitle = value;
else if ( name.equals("album") )
albumTitle = value;
else if ( name.equals("albumartist") )
albumArtist = value;
else if ( name.equals("title") )
trackTitle = value;
else if ( name.equalsIgnoreCase( "date" ) )
albumYear = value;
else if ( name.equalsIgnoreCase("genre") )
this.genre = value;
}
}
finally {
Utils.close( in );
}
}
private void read( final InputStream in ) {
state.in = in;
final Page og = new Page();
int index;
byte[] buffer;
int bytes = 0;
state.oy = new SyncState();
state.oy.init();
index = state.oy.buffer( CHUNKSIZE );
buffer = state.oy.data;
try {
bytes = state.in.read( buffer, index, CHUNKSIZE );
}
catch ( final Exception e ) {
log.error( e );
return;
}
state.oy.wrote( bytes );
if ( state.oy.pageout(og) != 1 ) {
if( bytes < CHUNKSIZE )
log.error( "Input truncated or empty." );
else
log.error( "Input is not an Ogg bitstream." );
return;
}
state.serial = og.serialno();
state.os = new StreamState();
state.os.init( state.serial );
state.vi = new Info();
state.vi.init();
state.vc = new Comment();
state.vc.init();
if ( state.os.pagein(og) < 0 ) {
log.error( "Error reading first page of Ogg bitstream data." );
return;
}
final Packet header_main = new Packet();
if ( state.os.packetout(header_main) != 1) {
log.error( "Error reading initial header packet." );
return;
}
if ( state.vi.synthesis_headerin(state.vc, header_main) < 0 ) {
log.error( "This Ogg bitstream does not contain Vorbis data." );
return;
}
state.mainlen = header_main.bytes;
state.mainbuf = new byte[ state.mainlen ];
System.arraycopy(
header_main.packet_base,
header_main.packet,
state.mainbuf,
0,
state.mainlen
);
int i = 0;
Packet header;
final Packet header_comments = new Packet();
final Packet header_codebooks = new Packet();
header = header_comments;
while ( i < 2 ) {
while( i < 2 ) {
int result = state.oy.pageout( og );
if ( result == 0 ) break; /* Too little data so far */
else if( result == 1 ) {
state.os.pagein(og);
while( i < 2 ) {
result = state.os.packetout(header);
if ( result == 0 ) break;
if ( result == -1 ) {
log.debug( "Corrupt secondary header." );
return;
}
state.vi.synthesis_headerin( state.vc, header );
if ( i == 1 ) {
state.booklen = header.bytes;
state.bookbuf = new byte[ state.booklen ];
System.arraycopy(
header.packet_base,
header.packet,
state.bookbuf,
0,
header.bytes
);
}
i++;
header = header_codebooks;
}
}
}
index = state.oy.buffer( CHUNKSIZE );
buffer = state.oy.data;
try {
bytes = state.in.read( buffer, index, CHUNKSIZE );
}
catch ( final Exception e ) {
log.error( e );
return;
}
if ( bytes == 0 && i < 2 ) {
log.debug("EOF before end of vorbis headers.");
return;
}
state.oy.wrote(bytes);
}
log.debug(state.vi);
}
}
class State {
private static final Logger log = Logger.getLogger( State.class );
private static final int CHUNKSIZE = 4096;
SyncState oy;
StreamState os;
Comment vc;
Info vi;
InputStream in;
int serial, mainlen, booklen, prevW;
byte[] mainbuf, bookbuf;
String lasterror;
final Page og = new Page();
public int blocksize( Packet p ) {
int _this = vi.blocksize( p );
int ret = ( _this + prevW ) / 4;
if ( prevW == 0 ) {
prevW = _this;
return 0;
}
prevW = _this;
return ret;
}
public int fetch_next_packet( Packet p ) {
final int result;
int index, bytes;
byte[] buffer;
result = os.packetout( p );
if ( result > 0 ) {
return 1;
}
while ( oy.pageout(og) <= 0 ) {
index = oy.buffer( CHUNKSIZE );
buffer = oy.data;
try {
bytes = in.read( buffer, index, CHUNKSIZE );
}
catch ( final IOException e ) {
log.error( e );
return 0;
}
if ( bytes > 0 ) {
oy.wrote( bytes );
if ( bytes == 0 || bytes == -1 ) {
return 0;
}
}
os.pagein( og );
}
return fetch_next_packet(p);
}
}