From 9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e Mon Sep 17 00:00:00 2001
From: Michiel Van Der Kolk
Date: Mon, 11 Jul 2005 15:42:37 +0000
Subject: Songdb java version, source. only 1.5 compatible
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
---
songdbj/de/jarnbjo/ogg/BasicStream.java | 121 ++++++
songdbj/de/jarnbjo/ogg/CachedUrlStream.java | 252 ++++++++++++
.../de/jarnbjo/ogg/EndOfOggStreamException.java | 45 +++
songdbj/de/jarnbjo/ogg/FileStream.java | 154 ++++++++
songdbj/de/jarnbjo/ogg/LogicalOggStream.java | 151 ++++++++
songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java | 213 ++++++++++
songdbj/de/jarnbjo/ogg/OggFormatException.java | 50 +++
songdbj/de/jarnbjo/ogg/OggPage.java | 431 +++++++++++++++++++++
songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java | 127 ++++++
songdbj/de/jarnbjo/ogg/PhysicalOggStream.java | 124 ++++++
songdbj/de/jarnbjo/ogg/UncachedUrlStream.java | 207 ++++++++++
.../util/audio/FadeableAudioInputStream.java | 62 +++
songdbj/de/jarnbjo/util/io/BitInputStream.java | 185 +++++++++
.../jarnbjo/util/io/ByteArrayBitInputStream.java | 352 +++++++++++++++++
songdbj/de/jarnbjo/util/io/HuffmanNode.java | 144 +++++++
songdbj/de/jarnbjo/vorbis/AudioPacket.java | 328 ++++++++++++++++
songdbj/de/jarnbjo/vorbis/CodeBook.java | 275 +++++++++++++
songdbj/de/jarnbjo/vorbis/CommentHeader.java | 244 ++++++++++++
songdbj/de/jarnbjo/vorbis/Floor.java | 124 ++++++
songdbj/de/jarnbjo/vorbis/Floor0.java | 74 ++++
songdbj/de/jarnbjo/vorbis/Floor1.java | 324 ++++++++++++++++
.../de/jarnbjo/vorbis/IdentificationHeader.java | 120 ++++++
songdbj/de/jarnbjo/vorbis/Mapping.java | 59 +++
songdbj/de/jarnbjo/vorbis/Mapping0.java | 146 +++++++
songdbj/de/jarnbjo/vorbis/MdctFloat.java | 321 +++++++++++++++
songdbj/de/jarnbjo/vorbis/Mode.java | 75 ++++
songdbj/de/jarnbjo/vorbis/Residue.java | 260 +++++++++++++
songdbj/de/jarnbjo/vorbis/Residue0.java | 53 +++
songdbj/de/jarnbjo/vorbis/Residue1.java | 55 +++
songdbj/de/jarnbjo/vorbis/Residue2.java | 123 ++++++
songdbj/de/jarnbjo/vorbis/SetupHeader.java | 131 +++++++
songdbj/de/jarnbjo/vorbis/Util.java | 127 ++++++
.../de/jarnbjo/vorbis/VorbisAudioFileReader.java | 217 +++++++++++
.../de/jarnbjo/vorbis/VorbisFormatException.java | 51 +++
songdbj/de/jarnbjo/vorbis/VorbisStream.java | 247 ++++++++++++
35 files changed, 5972 insertions(+)
create mode 100644 songdbj/de/jarnbjo/ogg/BasicStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/CachedUrlStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java
create mode 100644 songdbj/de/jarnbjo/ogg/FileStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/LogicalOggStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java
create mode 100644 songdbj/de/jarnbjo/ogg/OggFormatException.java
create mode 100644 songdbj/de/jarnbjo/ogg/OggPage.java
create mode 100644 songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/PhysicalOggStream.java
create mode 100644 songdbj/de/jarnbjo/ogg/UncachedUrlStream.java
create mode 100644 songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java
create mode 100644 songdbj/de/jarnbjo/util/io/BitInputStream.java
create mode 100644 songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java
create mode 100644 songdbj/de/jarnbjo/util/io/HuffmanNode.java
create mode 100644 songdbj/de/jarnbjo/vorbis/AudioPacket.java
create mode 100644 songdbj/de/jarnbjo/vorbis/CodeBook.java
create mode 100644 songdbj/de/jarnbjo/vorbis/CommentHeader.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Floor.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Floor0.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Floor1.java
create mode 100644 songdbj/de/jarnbjo/vorbis/IdentificationHeader.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Mapping.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Mapping0.java
create mode 100644 songdbj/de/jarnbjo/vorbis/MdctFloat.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Mode.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Residue.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Residue0.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Residue1.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Residue2.java
create mode 100644 songdbj/de/jarnbjo/vorbis/SetupHeader.java
create mode 100644 songdbj/de/jarnbjo/vorbis/Util.java
create mode 100644 songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java
create mode 100644 songdbj/de/jarnbjo/vorbis/VorbisFormatException.java
create mode 100644 songdbj/de/jarnbjo/vorbis/VorbisStream.java
(limited to 'songdbj/de')
diff --git a/songdbj/de/jarnbjo/ogg/BasicStream.java b/songdbj/de/jarnbjo/ogg/BasicStream.java
new file mode 100644
index 0000000000..9939524d6c
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/BasicStream.java
@@ -0,0 +1,121 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.3 2004/09/21 12:09:45 shred
+ * *** empty log message ***
+ *
+ * Revision 1.2 2004/09/21 06:38:45 shred
+ * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+/**
+ * Implementation of the PhysicalOggStream
interface for reading
+ * an Ogg stream from a URL. This class performs
+ * no internal caching, and will not read data from the network before
+ * requested to do so. It is intended to be used in non-realtime applications
+ * like file download managers or similar.
+ */
+
+public class BasicStream implements PhysicalOggStream {
+
+ private boolean closed=false;
+ private InputStream sourceStream;
+ private Object drainLock=new Object();
+ private LinkedList pageCache=new LinkedList();
+ private long numberOfSamples=-1;
+ private int position=0;
+
+ private HashMap logicalStreams=new HashMap();
+ private OggPage firstPage;
+
+ public BasicStream(InputStream sourceStream) throws OggFormatException, IOException {
+ firstPage=OggPage.create(sourceStream);
+ position+=firstPage.getTotalLength();
+ LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber());
+ logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los);
+ los.checkFormat(firstPage);
+ }
+
+ public Collection getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed=true;
+ sourceStream.close();
+ }
+
+ public int getContentLength() {
+ return -1;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ int pageNumber=2;
+
+ public OggPage getOggPage(int index) throws IOException {
+ if(firstPage!=null) {
+ OggPage tmp=firstPage;
+ firstPage=null;
+ return tmp;
+ }
+ else {
+ OggPage page=OggPage.create(sourceStream);
+ position+=page.getTotalLength();
+ return page;
+ }
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber));
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ throw new UnsupportedOperationException("Method not supported by this class");
+ }
+
+ /**
+ * @return always false
+ */
+
+ public boolean isSeekable() {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/CachedUrlStream.java b/songdbj/de/jarnbjo/ogg/CachedUrlStream.java
new file mode 100644
index 0000000000..86f792e272
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/CachedUrlStream.java
@@ -0,0 +1,252 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Implementation of the PhysicalOggStream
interface for reading
+ * and caching an Ogg stream from a URL. This class reads the data as fast as
+ * possible from the URL, caches it locally either in memory or on disk, and
+ * supports seeking within the available data.
+ */
+
+public class CachedUrlStream implements PhysicalOggStream {
+
+ private boolean closed=false;
+ private URLConnection source;
+ private InputStream sourceStream;
+ private Object drainLock=new Object();
+ private RandomAccessFile drain;
+ private byte[] memoryCache;
+ private ArrayList pageOffsets=new ArrayList();
+ private ArrayList pageLengths=new ArrayList();
+ private long numberOfSamples=-1;
+ private long cacheLength;
+
+ private HashMap logicalStreams=new HashMap();
+
+ private LoaderThread loaderThread;
+
+ /**
+ * Creates an instance of this class, using a memory cache.
+ */
+
+ public CachedUrlStream(URL source) throws OggFormatException, IOException {
+ this(source, null);
+ }
+
+ /**
+ * Creates an instance of this class, using the specified file as cache. The
+ * file is not automatically deleted when this class is disposed.
+ */
+
+ public CachedUrlStream(URL source, RandomAccessFile drain) throws OggFormatException, IOException {
+
+ this.source=source.openConnection();
+
+ if(drain==null) {
+ int contentLength=this.source.getContentLength();
+ if(contentLength==-1) {
+ throw new IOException("The URLConncetion's content length must be set when operating with a in-memory cache.");
+ }
+ memoryCache=new byte[contentLength];
+ }
+
+ this.drain=drain;
+ this.sourceStream=this.source.getInputStream();
+
+ loaderThread=new LoaderThread(sourceStream, drain, memoryCache);
+ new Thread(loaderThread).start();
+
+ while(!loaderThread.isBosDone() || pageOffsets.size()<20) {
+ System.out.print("pageOffsets.size(): "+pageOffsets.size()+"\r");
+ try {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException ex) {
+ }
+ }
+ System.out.println();
+ System.out.println("caching "+pageOffsets.size()+"/20 pages\r");
+ }
+
+ public Collection getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed=true;
+ sourceStream.close();
+ }
+
+ public long getCacheLength() {
+ return cacheLength;
+ }
+
+ /*
+ private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException {
+ return getNextPage(false);
+ }
+
+ private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException {
+ return OggPage.create(sourceStream, skipData);
+ }
+ */
+
+ public OggPage getOggPage(int index) throws IOException {
+ synchronized(drainLock) {
+ Long offset=(Long)pageOffsets.get(index);
+ Long length=(Long)pageLengths.get(index);
+ if(offset!=null) {
+ if(drain!=null) {
+ drain.seek(offset.longValue());
+ return OggPage.create(drain);
+ }
+ else {
+ byte[] tmpArray=new byte[length.intValue()];
+ System.arraycopy(memoryCache, offset.intValue(), tmpArray, 0, length.intValue());
+ return OggPage.create(tmpArray);
+ }
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber));
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) {
+ LogicalOggStream los=(LogicalOggStream)iter.next();
+ los.setTime(granulePosition);
+ }
+ }
+
+ public class LoaderThread implements Runnable {
+
+ private InputStream source;
+ private RandomAccessFile drain;
+ private byte[] memoryCache;
+
+ private boolean bosDone=false;
+
+ private int pageNumber;
+
+ public LoaderThread(InputStream source, RandomAccessFile drain, byte[] memoryCache) {
+ this.source=source;
+ this.drain=drain;
+ this.memoryCache=memoryCache;
+ }
+
+ public void run() {
+ try {
+ boolean eos=false;
+ byte[] buffer=new byte[8192];
+ while(!eos) {
+ OggPage op=OggPage.create(source);
+ synchronized (drainLock) {
+ int listSize=pageOffsets.size();
+
+ long pos=
+ listSize>0?
+ ((Long)pageOffsets.get(listSize-1)).longValue()+
+ ((Long)pageLengths.get(listSize-1)).longValue():
+ 0;
+
+ byte[] arr1=op.getHeader();
+ byte[] arr2=op.getSegmentTable();
+ byte[] arr3=op.getData();
+
+ if(drain!=null) {
+ drain.seek(pos);
+ drain.write(arr1);
+ drain.write(arr2);
+ drain.write(arr3);
+ }
+ else {
+ System.arraycopy(arr1, 0, memoryCache, (int)pos, arr1.length);
+ System.arraycopy(arr2, 0, memoryCache, (int)pos+arr1.length, arr2.length);
+ System.arraycopy(arr3, 0, memoryCache, (int)pos+arr1.length+arr2.length, arr3.length);
+ }
+
+ pageOffsets.add(new Long(pos));
+ pageLengths.add(new Long(arr1.length+arr2.length+arr3.length));
+ }
+
+ if(!op.isBos()) {
+ bosDone=true;
+ //System.out.println("bosDone=true;");
+ }
+ if(op.isEos()) {
+ eos=true;
+ }
+
+ LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber());
+ if(los==null) {
+ los=new LogicalOggStreamImpl(CachedUrlStream.this, op.getStreamSerialNumber());
+ logicalStreams.put(new Integer(op.getStreamSerialNumber()), los);
+ los.checkFormat(op);
+ }
+
+ los.addPageNumberMapping(pageNumber);
+ los.addGranulePosition(op.getAbsoluteGranulePosition());
+
+ pageNumber++;
+ cacheLength=op.getAbsoluteGranulePosition();
+ //System.out.println("read page: "+pageNumber);
+ }
+ }
+ catch(EndOfOggStreamException e) {
+ // ok
+ }
+ catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isBosDone() {
+ return bosDone;
+ }
+ }
+
+ public boolean isSeekable() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java b/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java
new file mode 100644
index 0000000000..4a0c3200f4
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java
@@ -0,0 +1,45 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2005/02/09 23:10:47 shred
+ * Serial UID für jarnbjo
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+ package de.jarnbjo.ogg;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when reaching the end of an Ogg stream
+ */
+
+public class EndOfOggStreamException extends IOException {
+ private static final long serialVersionUID = 3907210438109444408L;
+
+ public EndOfOggStreamException() {
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/FileStream.java b/songdbj/de/jarnbjo/ogg/FileStream.java
new file mode 100644
index 0000000000..5a526300bf
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/FileStream.java
@@ -0,0 +1,154 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Implementation of the PhysicalOggStream
interface for accessing
+ * normal disk files.
+ */
+
+public class FileStream implements PhysicalOggStream {
+
+ private boolean closed=false;
+ private RandomAccessFile source;
+ private long[] pageOffsets;
+ private long numberOfSamples=-1;
+
+ private HashMap logicalStreams=new HashMap();
+
+ /**
+ * Creates access to the specified file through the PhysicalOggStream
interface.
+ * The specified source file must have been opened for reading.
+ *
+ * @param source the file to read from
+ *
+ * @throws OggFormatException if the stream format is incorrect
+ * @throws IOException if some other IO error occurs when reading the file
+ */
+
+ public FileStream(RandomAccessFile source) throws OggFormatException, IOException {
+ this.source=source;
+
+ ArrayList po=new ArrayList();
+ int pageNumber=0;
+ try {
+ while(true) {
+ po.add(new Long(this.source.getFilePointer()));
+
+ // skip data if pageNumber>0
+ OggPage op=getNextPage(pageNumber>0);
+ if(op==null) {
+ break;
+ }
+
+ LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber());
+ if(los==null) {
+ los=new LogicalOggStreamImpl(this, op.getStreamSerialNumber());
+ logicalStreams.put(new Integer(op.getStreamSerialNumber()), los);
+ }
+
+ if(pageNumber==0) {
+ los.checkFormat(op);
+ }
+
+ los.addPageNumberMapping(pageNumber);
+ los.addGranulePosition(op.getAbsoluteGranulePosition());
+
+ if(pageNumber>0) {
+ this.source.seek(this.source.getFilePointer()+op.getTotalLength());
+ }
+
+ pageNumber++;
+ }
+ }
+ catch(EndOfOggStreamException e) {
+ // ok
+ }
+ catch(IOException e) {
+ throw e;
+ }
+ //System.out.println("pageNumber: "+pageNumber);
+ this.source.seek(0L);
+ pageOffsets=new long[po.size()];
+ int i=0;
+ Iterator iter=po.iterator();
+ while(iter.hasNext()) {
+ pageOffsets[i++]=((Long)iter.next()).longValue();
+ }
+ }
+
+ public Collection getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed=true;
+ source.close();
+ }
+
+ private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException {
+ return getNextPage(false);
+ }
+
+ private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException {
+ return OggPage.create(source, skipData);
+ }
+
+ public OggPage getOggPage(int index) throws IOException {
+ source.seek(pageOffsets[index]);
+ return OggPage.create(source);
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber));
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) {
+ LogicalOggStream los=(LogicalOggStream)iter.next();
+ los.setTime(granulePosition);
+ }
+ }
+
+ /**
+ * @return always true
+ */
+
+ public boolean isSeekable() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/LogicalOggStream.java b/songdbj/de/jarnbjo/ogg/LogicalOggStream.java
new file mode 100644
index 0000000000..2f97b2a728
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/LogicalOggStream.java
@@ -0,0 +1,151 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.IOException;
+
+/**
+ * Interface providing access to a logical Ogg stream as part of a
+ * physical Ogg stream.
+ */
+
+
+public interface LogicalOggStream {
+
+ public static final String FORMAT_UNKNOWN = "application/octet-stream";
+
+ public static final String FORMAT_VORBIS = "audio/x-vorbis";
+ public static final String FORMAT_FLAC = "audio/x-flac";
+ public static final String FORMAT_THEORA = "video/x-theora";
+
+ /**
+ * Note: To read from the stream, you must use either
+ * this method or the method getNextOggPacket
.
+ * Mixing calls to the two methods will cause data corruption.
+ *
+ * @return the next Ogg page
+ *
+ * @see #getNextOggPacket()
+ *
+ * @throws OggFormatException if the ogg stream is corrupted
+ * @throws IOException if some other IO error occurs
+ */
+
+ public OggPage getNextOggPage() throws OggFormatException, IOException;
+
+ /**
+ * Note: To read from the stream, you must use either
+ * this method or the method getNextOggPage
.
+ * Mixing calls to the two methods will cause data corruption.
+ *
+ * @return the next packet as a byte array
+ *
+ * @see #getNextOggPage()
+ *
+ * @throws OggFormatException if the ogg stream is corrupted
+ * @throws IOException if some other IO error occurs
+ */
+
+ public byte[] getNextOggPacket() throws OggFormatException, IOException;
+
+ /**
+ * Checks if this stream is open for reading.
+ *
+ * @return true
if this stream is open for reading,
+ * false
otherwise
+ */
+
+ public boolean isOpen();
+
+ /**
+ * Closes this stream. After invoking this method, no further access
+ * to the streams data is possible.
+ *
+ * @throws IOException if an IO error occurs
+ */
+
+ public void close() throws IOException;
+
+ /**
+ * Sets the stream's position to the beginning of the stream.
+ * This method does not work if the physical Ogg stream is not
+ * seekable.
+ *
+ * @throws OggFormatException if the ogg stream is corrupted
+ * @throws IOException if some other IO error occurs
+ */
+
+ public void reset() throws OggFormatException, IOException;
+
+ /**
+ * This method does not work if the physical Ogg stream is not
+ * seekable.
+ *
+ * @return the granule position of the last page within
+ * this stream
+ */
+
+ public long getMaximumGranulePosition();
+
+ /**
+ * This method is invoked on all logical streams when
+ * calling the same method on the physical stream. The
+ * same restrictions as mentioned there apply.
+ * This method does not work if the physical Ogg stream is not
+ * seekable.
+ *
+ * @param granulePosition
+ *
+ * @see PhysicalOggStream#setTime(long)
+ *
+ * @throws IOException if an IO error occurs
+ */
+
+ public void setTime(long granulePosition) throws IOException;
+
+ /**
+ * @return the last parsed granule position of this stream
+ */
+
+ public long getTime();
+
+ /**
+ * @return the content type of this stream
+ *
+ * @see #FORMAT_UNKNOWN
+ * @see #FORMAT_VORBIS
+ * @see #FORMAT_FLAC
+ * @see #FORMAT_THEORA
+ */
+
+ public String getFormat();
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java b/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java
new file mode 100644
index 0000000000..1a503e91ca
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java
@@ -0,0 +1,213 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/03/31 00:23:04 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:26 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+import java.util.*;
+
+public class LogicalOggStreamImpl implements LogicalOggStream {
+
+ private PhysicalOggStream source;
+ private int serialNumber;
+
+ private ArrayList pageNumberMapping=new ArrayList();
+ private ArrayList granulePositions=new ArrayList();
+
+ private int pageIndex=0;
+ private OggPage currentPage;
+ private int currentSegmentIndex;
+
+ private boolean open=true;
+
+ private String format=FORMAT_UNKNOWN;
+
+ public LogicalOggStreamImpl(PhysicalOggStream source, int serialNumber) {
+ this.source=source;
+ this.serialNumber=serialNumber;
+ }
+
+ public void addPageNumberMapping(int physicalPageNumber) {
+ pageNumberMapping.add(new Integer(physicalPageNumber));
+ }
+
+ public void addGranulePosition(long granulePosition) {
+ granulePositions.add(new Long(granulePosition));
+ }
+
+ public synchronized void reset() throws OggFormatException, IOException {
+ currentPage=null;
+ currentSegmentIndex=0;
+ pageIndex=0;
+ }
+
+ public synchronized OggPage getNextOggPage() throws EndOfOggStreamException, OggFormatException, IOException {
+ if(source.isSeekable()) {
+ currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue());
+ }
+ else {
+ currentPage=source.getOggPage(-1);
+ }
+ return currentPage;
+ }
+
+ public synchronized byte[] getNextOggPacket() throws EndOfOggStreamException, OggFormatException, IOException {
+ ByteArrayOutputStream res=new ByteArrayOutputStream();
+ int segmentLength=0;
+
+ if(currentPage==null) {
+ currentPage=getNextOggPage();
+ }
+
+ do {
+ if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) {
+ currentSegmentIndex=0;
+
+ if(!currentPage.isEos()) {
+ if(source.isSeekable() && pageNumberMapping.size()<=pageIndex) {
+ while(pageNumberMapping.size()<=pageIndex+10) {
+ try {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException ex) {
+ }
+ }
+ }
+ currentPage=getNextOggPage();
+
+ if(res.size()==0 && currentPage.isContinued()) {
+ boolean done=false;
+ while(!done) {
+ if(currentPage.getSegmentLengths()[currentSegmentIndex++]!=255) {
+ done=true;
+ }
+ if(currentSegmentIndex>currentPage.getSegmentTable().length) {
+ currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue());
+ }
+ }
+ }
+ }
+ else {
+ throw new EndOfOggStreamException();
+ }
+ }
+ segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex];
+ res.write(currentPage.getData(), currentPage.getSegmentOffsets()[currentSegmentIndex], segmentLength);
+ currentSegmentIndex++;
+ } while(segmentLength==255);
+
+ return res.toByteArray();
+ }
+
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void close() throws IOException {
+ open=false;
+ }
+
+ public long getMaximumGranulePosition() {
+ Long mgp=(Long)granulePositions.get(granulePositions.size()-1);
+ return mgp.longValue();
+ }
+
+ public synchronized long getTime() {
+ return currentPage!=null?currentPage.getAbsoluteGranulePosition():-1;
+ }
+
+ public synchronized void setTime(long granulePosition) throws IOException {
+
+ int page=0;
+ for(page=0; pagegranulePosition) {
+ break;
+ }
+ }
+
+ pageIndex=page;
+ currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue());
+ currentSegmentIndex=0;
+ int segmentLength=0;
+ do {
+ if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) {
+ currentSegmentIndex=0;
+ if(pageIndex>=pageNumberMapping.size()) {
+ throw new EndOfOggStreamException();
+ }
+ currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue());
+ }
+ segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex];
+ currentSegmentIndex++;
+ } while(segmentLength==255);
+ }
+
+ public void checkFormat(OggPage page) {
+ byte[] data=page.getData();
+
+ if(data.length>=7 &&
+ data[1]==0x76 &&
+ data[2]==0x6f &&
+ data[3]==0x72 &&
+ data[4]==0x62 &&
+ data[5]==0x69 &&
+ data[6]==0x73) {
+
+ format=FORMAT_VORBIS;
+ }
+ else if(data.length>=7 &&
+ data[1]==0x74 &&
+ data[2]==0x68 &&
+ data[3]==0x65 &&
+ data[4]==0x6f &&
+ data[5]==0x72 &&
+ data[6]==0x61) {
+
+ format=FORMAT_THEORA;
+ }
+ else if (data.length==4 &&
+ data[0]==0x66 &&
+ data[1]==0x4c &&
+ data[2]==0x61 &&
+ data[3]==0x43) {
+
+ format=FORMAT_FLAC;
+ }
+ }
+
+ public String getFormat() {
+ return format;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/OggFormatException.java b/songdbj/de/jarnbjo/ogg/OggFormatException.java
new file mode 100644
index 0000000000..a6b2466b92
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/OggFormatException.java
@@ -0,0 +1,50 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2005/02/09 23:10:47 shred
+ * Serial UID für jarnbjo
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when trying to read a corrupted Ogg stream.
+ */
+
+public class OggFormatException extends IOException {
+ private static final long serialVersionUID = 3544953238333175349L;
+
+ public OggFormatException() {
+ super();
+ }
+
+ public OggFormatException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/OggPage.java b/songdbj/de/jarnbjo/ogg/OggPage.java
new file mode 100644
index 0000000000..cc965cc7a9
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/OggPage.java
@@ -0,0 +1,431 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/31 00:23:04 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+
+import de.jarnbjo.util.io.*;
+
+/**
+ * An instance of this class represents an ogg page read from an ogg file
+ * or network stream. It has no public constructor, but instances can be
+ * created by the create
methods, supplying a JMF stream or
+ * a RandomAccessFile
+ * which is positioned at the beginning of an Ogg page.
+ *
+ * Furtheron, the class provides methods for accessing the raw page data,
+ * as well as data attributes like segmenting information, sequence number,
+ * stream serial number, chechsum and wether this page is the beginning or
+ * end of a logical bitstream (BOS, EOS) and if the page data starts with a
+ * continued packet or a fresh data packet.
+ */
+
+public class OggPage {
+
+ private int version;
+ private boolean continued, bos, eos;
+ private long absoluteGranulePosition;
+ private int streamSerialNumber, pageSequenceNumber, pageCheckSum;
+ private int[] segmentOffsets;
+ private int[] segmentLengths;
+ private int totalLength;
+ private byte[] header, segmentTable, data;
+
+ protected OggPage() {
+ }
+
+ private OggPage(
+ int version,
+ boolean continued,
+ boolean bos,
+ boolean eos,
+ long absoluteGranulePosition,
+ int streamSerialNumber,
+ int pageSequenceNumber,
+ int pageCheckSum,
+ int[] segmentOffsets,
+ int[] segmentLengths,
+ int totalLength,
+ byte[] header,
+ byte[] segmentTable,
+ byte[] data) {
+
+ this.version=version;
+ this.continued=continued;
+ this.bos=bos;
+ this.eos=eos;
+ this.absoluteGranulePosition=absoluteGranulePosition;
+ this.streamSerialNumber=streamSerialNumber;
+ this.pageSequenceNumber=pageSequenceNumber;
+ this.pageCheckSum=pageCheckSum;
+ this.segmentOffsets=segmentOffsets;
+ this.segmentLengths=segmentLengths;
+ this.totalLength=totalLength;
+ this.header=header;
+ this.segmentTable=segmentTable;
+ this.data=data;
+ }
+
+ /**
+ * this method equals to create(RandomAccessFile source, false)
+ *
+ * @see #create(RandomAccessFile, boolean)
+ */
+
+ public static OggPage create(RandomAccessFile source) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create(source, false);
+ }
+
+ /**
+ * This method is called to read data from the current position in the
+ * specified RandomAccessFile and create a new OggPage instance based on the data
+ * read. If the parameter skipData
is set to true
,
+ * the actual page segments (page data) is skipped and not read into
+ * memory. This mode is useful when scanning through an ogg file to build
+ * a seek table.
+ *
+ * @param source the source from which the ogg page is generated
+ * @param skipData if set to true
, the actual page data is not read into memory
+ * @return an ogg page created by reading data from the specified source, starting at the current position
+ * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page
+ * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source
+ * @throws IOException if some other I/O error is detected when reading from the source
+ *
+ * @see #create(RandomAccessFile)
+ */
+
+ public static OggPage create(RandomAccessFile source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create((Object)source, skipData);
+ }
+
+ /**
+ * this method equals to create(InputStream source, false)
+ *
+ * @see #create(InputStream, boolean)
+ */
+
+ public static OggPage create(InputStream source) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create(source, false);
+ }
+
+ /**
+ * This method is called to read data from the current position in the
+ * specified InpuStream and create a new OggPage instance based on the data
+ * read. If the parameter skipData
is set to true
,
+ * the actual page segments (page data) is skipped and not read into
+ * memory. This mode is useful when scanning through an ogg file to build
+ * a seek table.
+ *
+ * @param source the source from which the ogg page is generated
+ * @param skipData if set to true
, the actual page data is not read into memory
+ * @return an ogg page created by reading data from the specified source, starting at the current position
+ * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page
+ * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source
+ * @throws IOException if some other I/O error is detected when reading from the source
+ *
+ * @see #create(InputStream)
+ */
+
+ public static OggPage create(InputStream source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create((Object)source, skipData);
+ }
+
+ /**
+ * this method equals to create(byte[] source, false)
+ *
+ * @see #create(byte[], boolean)
+ */
+
+ public static OggPage create(byte[] source) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create(source, false);
+ }
+
+ /**
+ * This method is called to
+ * create a new OggPage instance based on the specified byte array.
+ *
+ * @param source the source from which the ogg page is generated
+ * @param skipData if set to true
, the actual page data is not read into memory
+ * @return an ogg page created by reading data from the specified source, starting at the current position
+ * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page
+ * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source
+ * @throws IOException if some other I/O error is detected when reading from the source
+ *
+ * @see #create(byte[])
+ */
+
+ public static OggPage create(byte[] source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException {
+ return create((Object)source, skipData);
+ }
+
+ private static OggPage create(Object source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException {
+
+ try {
+ int sourceOffset=27;
+
+ byte[] header=new byte[27];
+ if(source instanceof RandomAccessFile) {
+ RandomAccessFile raf=(RandomAccessFile)source;
+ if(raf.getFilePointer()==raf.length()) {
+ return null;
+ }
+ raf.readFully(header);
+ }
+ else if(source instanceof InputStream) {
+ readFully((InputStream)source, header);
+ }
+ else if(source instanceof byte[]) {
+ System.arraycopy((byte[])source, 0, header, 0, 27);
+ }
+
+ BitInputStream bdSource=new ByteArrayBitInputStream(header);
+
+ int capture=bdSource.getInt(32);
+
+ if(capture!=0x5367674f) {
+ //throw new FormatException("Ogg page does not start with 'OggS' (0x4f676753)");
+
+ /*
+ ** This condition is IMHO an error, but older Ogg files often contain
+ ** pages with a different capture than OggS. I am not sure how to
+ ** manage these pages, but the decoder seems to work properly, if
+ ** the incorrect capture is simply ignored.
+ */
+
+ String cs=Integer.toHexString(capture);
+ while(cs.length()<8) {
+ cs="0"+cs;
+ }
+ cs=cs.substring(6, 8)+cs.substring(4, 6)+cs.substring(2, 4)+cs.substring(0, 2);
+ char c1=(char)(Integer.valueOf(cs.substring(0, 2), 16).intValue());
+ char c2=(char)(Integer.valueOf(cs.substring(2, 4), 16).intValue());
+ char c3=(char)(Integer.valueOf(cs.substring(4, 6), 16).intValue());
+ char c4=(char)(Integer.valueOf(cs.substring(6, 8), 16).intValue());
+ System.out.println("Ogg packet header is 0x"+cs+" ("+c1+c2+c3+c4+"), should be 0x4f676753 (OggS)");
+ }
+
+ int version=bdSource.getInt(8);
+ byte tmp=(byte)bdSource.getInt(8);
+ boolean bf1=(tmp&1)!=0;
+ boolean bos=(tmp&2)!=0;
+ boolean eos=(tmp&4)!=0;
+ long absoluteGranulePosition=bdSource.getLong(64);
+ int streamSerialNumber=bdSource.getInt(32);
+ int pageSequenceNumber=bdSource.getInt(32);
+ int pageCheckSum=bdSource.getInt(32);
+ int pageSegments=bdSource.getInt(8);
+
+ //System.out.println("OggPage: "+streamSerialNumber+" / "+absoluteGranulePosition+" / "+pageSequenceNumber);
+
+ int[] segmentOffsets=new int[pageSegments];
+ int[] segmentLengths=new int[pageSegments];
+ int totalLength=0;
+
+ byte[] segmentTable=new byte[pageSegments];
+ byte[] tmpBuf=new byte[1];
+
+ for(int i=0; itrue if this page begins with a continued packet
+ */
+
+ public boolean isContinued() {
+ return continued;
+ }
+
+ /**
+ * @return true
if this page begins with a fresh packet
+ */
+
+ public boolean isFresh() {
+ return !continued;
+ }
+
+ /**
+ * @return true
if this page is the beginning of a logical stream
+ */
+
+ public boolean isBos() {
+ return bos;
+ }
+
+ /**
+ * @return true
if this page is the end of a logical stream
+ */
+
+ public boolean isEos() {
+ return eos;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java b/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java
new file mode 100644
index 0000000000..98159c4e7c
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java
@@ -0,0 +1,127 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/31 00:23:04 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Implementation of the PhysicalOggStream
interface for reading
+ * an Ogg stream from a URL. This class performs
+ * no internal caching, and will not read data from the network before
+ * requested to do so. It is intended to be used in non-realtime applications
+ * like file download managers or similar.
+ */
+
+public class OnDemandUrlStream implements PhysicalOggStream {
+
+ private boolean closed=false;
+ private URLConnection source;
+ private InputStream sourceStream;
+ private Object drainLock=new Object();
+ private LinkedList pageCache=new LinkedList();
+ private long numberOfSamples=-1;
+ private int contentLength=0;
+ private int position=0;
+
+ private HashMap logicalStreams=new HashMap();
+ private OggPage firstPage;
+
+ private static final int PAGECACHE_SIZE = 20;
+
+ public OnDemandUrlStream(URL source) throws OggFormatException, IOException {
+ this.source=source.openConnection();
+ this.sourceStream=this.source.getInputStream();
+
+ contentLength=this.source.getContentLength();
+
+ firstPage=OggPage.create(sourceStream);
+ position+=firstPage.getTotalLength();
+ LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber());
+ logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los);
+ los.checkFormat(firstPage);
+ }
+
+ public Collection getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public void close() throws IOException {
+ closed=true;
+ sourceStream.close();
+ }
+
+ public int getContentLength() {
+ return contentLength;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ int pageNumber=2;
+
+ public OggPage getOggPage(int index) throws IOException {
+ if(firstPage!=null) {
+ OggPage tmp=firstPage;
+ firstPage=null;
+ return tmp;
+ }
+ else {
+ OggPage page=OggPage.create(sourceStream);
+ position+=page.getTotalLength();
+ return page;
+ }
+ }
+
+ private LogicalOggStream getLogicalStream(int serialNumber) {
+ return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber));
+ }
+
+ public void setTime(long granulePosition) throws IOException {
+ throw new UnsupportedOperationException("Method not supported by this class");
+ }
+
+ /**
+ * @return always false
+ */
+
+ public boolean isSeekable() {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java b/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java
new file mode 100644
index 0000000000..5f342a38b7
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java
@@ -0,0 +1,124 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/31 00:23:04 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Interface providing access to a physical Ogg stream. Typically this is
+ * a file.
+ */
+
+public interface PhysicalOggStream {
+
+ /**
+ * Returns a collection of objects implementing LogicalOggStream
+ * for accessing the separate logical streams within this physical Ogg stream.
+ *
+ * @return a collection of objects implementing LogicalOggStream
+ * which are representing the logical streams contained within this
+ * physical stream
+ *
+ * @see LogicalOggStream
+ */
+
+ public Collection getLogicalStreams();
+
+ /**
+ * Return the Ogg page with the absolute index index
,
+ * independent from the logical structure of this stream or if the
+ * index parameter is -1, the next Ogg page is returned.
+ * This method should only be used by implementations of LogicalOggStream
+ * to access the raw pages.
+ *
+ * @param index the absolute index starting from 0 at the beginning of
+ * the file or stream or -1 to get the next page in a non-seekable
+ * stream
+ *
+ * @return the Ogg page with the physical absolute index index
+ *
+ * @throws OggFormatException if the ogg stream is corrupted
+ * @throws IOException if some other IO error occurs
+ */
+
+ public OggPage getOggPage(int index) throws OggFormatException, IOException;
+
+ /**
+ * Checks if this stream is open for reading.
+ *
+ * @return true
if this stream is open for reading,
+ * false
otherwise
+ */
+
+ public boolean isOpen();
+
+ /**
+ * Closes this stream. After invoking this method, no further access
+ * to the streams data is possible.
+ *
+ * @throws IOException
+ */
+
+ public void close() throws IOException;
+
+ /**
+ * Sets this stream's (and its logical stream's) position to the granule
+ * position. The next packet read from any logical stream will be the
+ * first packet beginning on the first page with a granule position higher
+ * than the argument.
+ *
+ * At the moment, this method only works correctly for Ogg files with
+ * a single logical Vorbis stream, and due to the different interpretations
+ * of the granule position, depending on mixed content, this method will
+ * never be able to work for mixed streams. Chained and interleaved streams are
+ * also not yet supported. Actually, this method is only a hack to support
+ * seeking from JMF, but may of course be abused otherwise too :)
+ *
+ * @param granulePosition
+ *
+ * @throws OggFormatException if the ogg stream is corrupted
+ * @throws IOException if some other IO error occurs
+ */
+
+ public void setTime(long granulePosition) throws OggFormatException, IOException;
+
+ /**
+ * @return true
if the stream is seekable, false
+ * otherwise
+ */
+
+ public boolean isSeekable();
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java b/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java
new file mode 100644
index 0000000000..a07f0ac00e
--- /dev/null
+++ b/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java
@@ -0,0 +1,207 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.1 2003/04/10 19:48:22 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.ogg;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Implementation of the PhysicalOggStream
interface for reading
+ * an Ogg stream from a URL. This class performs only the necessary caching
+ * to provide continous playback. Seeking within the stream is not supported.
+ */
+
+public class UncachedUrlStream implements PhysicalOggStream {
+
+ private boolean closed=false;
+ private URLConnection source;
+ private InputStream sourceStream;
+ private Object drainLock=new Object();
+ private LinkedList pageCache=new LinkedList();
+ private long numberOfSamples=-1;
+
+ private HashMap logicalStreams=new HashMap();
+
+ private LoaderThread loaderThread;
+
+ private static final int PAGECACHE_SIZE = 10;
+
+ /** Creates an instance of the PhysicalOggStream
interface
+ * suitable for reading an Ogg stream from a URL.
+ */
+
+ public UncachedUrlStream(URL source) throws OggFormatException, IOException {
+
+ this.source=source.openConnection();
+ this.sourceStream=this.source.getInputStream();
+
+ loaderThread=new LoaderThread(sourceStream, pageCache);
+ new Thread(loaderThread).start();
+
+ while(!loaderThread.isBosDone() || pageCache.size()PAGECACHE_SIZE) {
+ try {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+ catch(EndOfOggStreamException e) {
+ // ok
+ }
+ catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isBosDone() {
+ return bosDone;
+ }
+ }
+
+ /**
+ * @return always false
+ */
+
+ public boolean isSeekable() {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java b/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java
new file mode 100644
index 0000000000..4916102d4b
--- /dev/null
+++ b/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java
@@ -0,0 +1,62 @@
+package de.jarnbjo.util.audio;
+
+import java.io.*;
+import javax.sound.sampled.*;
+
+public class FadeableAudioInputStream extends AudioInputStream {
+
+ private AudioInputStream stream;
+ private boolean fading=false;
+ private double phi=0.0;
+
+ public FadeableAudioInputStream(AudioInputStream stream) throws IOException {
+ super(stream, stream.getFormat(), -1L);
+ }
+
+ public void fadeOut() {
+ fading=true;
+ phi=0.0;
+ }
+
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int offset, int length) throws IOException {
+ int read=super.read(b, offset, length);
+
+ //System.out.println("read "+read);
+
+ if(fading) {
+ int j=0, l=0, r=0;
+ double gain=0.0;
+
+ for(int i=offset; i>8)&0xff);
+ b[j++]=(byte)(r&0xff);
+ b[j++]=(byte)((r>>8)&0xff);
+ }
+ }
+
+ return read;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/util/io/BitInputStream.java b/songdbj/de/jarnbjo/util/io/BitInputStream.java
new file mode 100644
index 0000000000..89cadb8380
--- /dev/null
+++ b/songdbj/de/jarnbjo/util/io/BitInputStream.java
@@ -0,0 +1,185 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.5 2003/04/10 19:48:31 jarnbjo
+ * no message
+ *
+ * Revision 1.4 2003/03/16 20:57:06 jarnbjo
+ * no message
+ *
+ * Revision 1.3 2003/03/16 20:56:56 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:39 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.util.io;
+
+import java.io.IOException;
+
+/**
+ * An interface with methods allowing bit-wise reading from
+ * an input stream. All methods in this interface are optional
+ * and an implementation not support a method or a specific state
+ * (e.g. endian) will throw an UnspportedOperationException if
+ * such a method is being called. This should be speicified in
+ * the implementation documentation.
+ */
+
+public interface BitInputStream {
+
+ /**
+ * constant for setting this stream's mode to little endian
+ *
+ * @see #setEndian(int)
+ */
+
+ public static final int LITTLE_ENDIAN = 0;
+
+ /**
+ * constant for setting this stream's mode to big endian
+ *
+ * @see #setEndian(int)
+ */
+
+ public static final int BIG_ENDIAN = 1;
+
+ /**
+ * reads one bit (as a boolean) from the input stream
+ *
+ * @return true
if the next bit is 1,
+ * false
otherwise
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public boolean getBit() throws IOException;
+
+ /**
+ * reads bits
number of bits from the input
+ * stream
+ *
+ * @return the unsigned integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public int getInt(int bits) throws IOException;
+
+ /**
+ * reads bits
number of bits from the input
+ * stream
+ *
+ * @return the signed integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public int getSignedInt(int bits) throws IOException;
+
+ /**
+ * reads a huffman codeword based on the root
+ * parameter and returns the decoded value
+ *
+ * @param root the root of the Huffman tree used to decode the codeword
+ * @return the decoded unsigned integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public int getInt(HuffmanNode root) throws IOException;
+
+ /**
+ * reads an integer encoded as "signed rice" as described in
+ * the FLAC audio format specification
+ *
+ * @param order
+ * @return the decoded integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public int readSignedRice(int order) throws IOException;
+
+ /**
+ * fills the array from offset
with len
+ * integers encoded as "signed rice" as described in
+ * the FLAC audio format specification
+ *
+ * @param order
+ * @param buffer
+ * @param offset
+ * @param len
+ * @return the decoded integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public void readSignedRice(int order, int[] buffer, int offset, int len) throws IOException;
+
+ /**
+ * reads bits
number of bits from the input
+ * stream
+ *
+ * @return the unsigned long value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public long getLong(int bits) throws IOException;
+
+ /**
+ * causes the read pointer to be moved to the beginning
+ * of the next byte, remaining bits in the current byte
+ * are discarded
+ *
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public void align();
+
+ /**
+ * changes the endian mode used when reading bit-wise from
+ * the stream, changing the mode mid-stream will cause the
+ * read cursor to move to the beginning of the next byte
+ * (as if calling the allign
method
+ *
+ * @see #align()
+ *
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public void setEndian(int endian);
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java b/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java
new file mode 100644
index 0000000000..9c84c7daca
--- /dev/null
+++ b/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java
@@ -0,0 +1,352 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/10 19:48:31 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:39 jarnbjo
+ * no message
+ *
+ * Revision 1.1 2003/03/03 21:02:20 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.util.io;
+
+import java.io.IOException;
+
+/**
+ * Implementation of the BitInputStream
interface,
+ * using a byte array as data source.
+*/
+
+public class ByteArrayBitInputStream implements BitInputStream {
+
+ private byte[] source;
+ private byte currentByte;
+
+ private int endian;
+
+ private int byteIndex=0;
+ private int bitIndex=0;
+
+ public ByteArrayBitInputStream(byte[] source) {
+ this(source, LITTLE_ENDIAN);
+ }
+
+ public ByteArrayBitInputStream(byte[] source, int endian) {
+ this.endian=endian;
+ this.source=source;
+ currentByte=source[0];
+ bitIndex=(endian==LITTLE_ENDIAN)?0:7;
+ }
+
+ public boolean getBit() throws IOException {
+ if(endian==LITTLE_ENDIAN) {
+ if(bitIndex>7) {
+ bitIndex=0;
+ currentByte=source[++byteIndex];
+ }
+ return (currentByte&(1<<(bitIndex++)))!=0;
+ }
+ else {
+ if(bitIndex<0) {
+ bitIndex=7;
+ currentByte=source[++byteIndex];
+ }
+ return (currentByte&(1<<(bitIndex--)))!=0;
+ }
+ }
+
+ public int getInt(int bits) throws IOException {
+ if(bits>32) {
+ throw new IllegalArgumentException("Argument \"bits\" must be <= 32");
+ }
+ int res=0;
+ if(endian==LITTLE_ENDIAN) {
+ for(int i=0; i>offset;
+ bitIndex-=bits;
+ }
+ else {
+ res=(((int)currentByte)&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1);
+ bits-=bitIndex+1;
+ currentByte=source[++byteIndex];
+ while(bits>=8) {
+ bits-=8;
+ res|=(((int)source[byteIndex])&0xff)<0) {
+ int ci=((int)source[byteIndex])&0xff;
+ res|=(ci>>(8-bits))&((1<=1<<(bits-1)) {
+ raw-=1<7) {
+ bitIndex=0;
+ currentByte=source[++byteIndex];
+ }
+ root=(currentByte&(1<<(bitIndex++)))!=0?root.o1:root.o0;
+ }
+ return root.value.intValue();
+ }
+
+ public long getLong(int bits) throws IOException {
+ if(bits>64) {
+ throw new IllegalArgumentException("Argument \"bits\" must be <= 64");
+ }
+ long res=0;
+ if(endian==LITTLE_ENDIAN) {
+ for(int i=0; i=0; i--) {
+ if(getBit()) {
+ res|=(1L<reads an integer encoded as "signed rice" as described in
+ * the FLAC audio format specification
+ *
+ * not supported for little endian
+ *
+ * @param order
+ * @return the decoded integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public int readSignedRice(int order) throws IOException {
+
+ int msbs=-1, lsbs=0, res=0;
+
+ if(endian==LITTLE_ENDIAN) {
+ // little endian
+ throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode");
+ }
+ else {
+ // big endian
+
+ byte cb=source[byteIndex];
+ do {
+ msbs++;
+ if(bitIndex<0) {
+ bitIndex=7;
+ byteIndex++;
+ cb=source[byteIndex];
+ }
+ } while((cb&(1<>offset;
+ bitIndex-=bits;
+ }
+ else {
+ lsbs=(((int)source[byteIndex])&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1);
+ bits-=bitIndex+1;
+ byteIndex++;
+ while(bits>=8) {
+ bits-=8;
+ lsbs|=(((int)source[byteIndex])&0xff)<0) {
+ int ci=((int)source[byteIndex])&0xff;
+ lsbs|=(ci>>(8-bits))&((1<>1)-1:(res>>1);
+ }
+
+ /**
+ * fills the array from offset
with len
+ * integers encoded as "signed rice" as described in
+ * the FLAC audio format specification
+ *
+ * not supported for little endian
+ *
+ * @param order
+ * @param buffer
+ * @param offset
+ * @param len
+ * @return the decoded integer value read from the stream
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws UnsupportedOperationException if the method is not supported by the implementation
+ */
+
+ public void readSignedRice(int order, int[] buffer, int off, int len) throws IOException {
+
+ if(endian==LITTLE_ENDIAN) {
+ // little endian
+ throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode");
+ }
+ else {
+ // big endian
+ for(int i=off; i>offset;
+ bitIndex-=bits;
+ }
+ else {
+ lsbs=(((int)source[byteIndex])&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1);
+ bits-=bitIndex+1;
+ byteIndex++;
+ while(bits>=8) {
+ bits-=8;
+ lsbs|=(((int)source[byteIndex])&0xff)<0) {
+ int ci=((int)source[byteIndex])&0xff;
+ lsbs|=(ci>>(8-bits))&((1<>1)-1:(res>>1);
+ }
+ }
+ }
+
+ public void align() {
+ if(endian==BIG_ENDIAN && bitIndex>=0) {
+ bitIndex=7;
+ byteIndex++;
+ }
+ else if(endian==LITTLE_ENDIAN && bitIndex<=7) {
+ bitIndex=0;
+ byteIndex++;
+ }
+ }
+
+ public void setEndian(int endian) {
+ if(this.endian==BIG_ENDIAN && endian==LITTLE_ENDIAN) {
+ bitIndex=0;
+ byteIndex++;
+ }
+ else if(this.endian==LITTLE_ENDIAN && endian==BIG_ENDIAN) {
+ bitIndex=7;
+ byteIndex++;
+ }
+ this.endian=endian;
+ }
+
+ /**
+ * @return the byte array used as a source for this instance
+ */
+
+ public byte[] getSource() {
+ return source;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/util/io/HuffmanNode.java b/songdbj/de/jarnbjo/util/io/HuffmanNode.java
new file mode 100644
index 0000000000..88600a4ddd
--- /dev/null
+++ b/songdbj/de/jarnbjo/util/io/HuffmanNode.java
@@ -0,0 +1,144 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/04/10 19:48:31 jarnbjo
+ * no message
+ *
+ */
+
+package de.jarnbjo.util.io;
+
+import java.io.IOException;
+import de.jarnbjo.util.io.BitInputStream;
+
+/**
+ * Representation of a node in a Huffman tree, used to read
+ * Huffman compressed codewords from e.g. a Vorbis stream.
+ */
+
+final public class HuffmanNode {
+
+ private HuffmanNode parent;
+ private int depth=0;
+ protected HuffmanNode o0, o1;
+ protected Integer value;
+ private boolean full=false;
+
+ /**
+ * creates a new Huffman tree root node
+ */
+
+ public HuffmanNode() {
+ this(null);
+ }
+
+ protected HuffmanNode(HuffmanNode parent) {
+ this.parent=parent;
+ if(parent!=null) {
+ depth=parent.getDepth()+1;
+ }
+ }
+
+ protected HuffmanNode(HuffmanNode parent, int value) {
+ this(parent);
+ this.value=new Integer(value);
+ full=true;
+ }
+
+ protected int read(BitInputStream bis) throws IOException {
+ HuffmanNode iter=this;
+ while(iter.value==null) {
+ iter=bis.getBit()?iter.o1:iter.o0;
+ }
+ return iter.value.intValue();
+ }
+
+ protected HuffmanNode get0() {
+ return o0==null?set0(new HuffmanNode(this)):o0;
+ }
+
+ protected HuffmanNode get1() {
+ return o1==null?set1(new HuffmanNode(this)):o1;
+ }
+
+ protected Integer getValue() {
+ return value;
+ }
+
+ private HuffmanNode getParent() {
+ return parent;
+ }
+
+ protected int getDepth() {
+ return depth;
+ }
+
+ private boolean isFull() {
+ return full?true:(full=o0!=null&&o0.isFull()&&o1!=null&&o1.isFull());
+ }
+
+ private HuffmanNode set0(HuffmanNode value) {
+ return o0=value;
+ }
+
+ private HuffmanNode set1(HuffmanNode value) {
+ return o1=value;
+ }
+
+ private void setValue(Integer value) {
+ full=true;
+ this.value=value;
+ }
+
+ /**
+ * creates a new tree node at the first free location at the given
+ * depth, and assigns the value to it
+ *
+ * @param depth the tree depth of the new node (codeword length in bits)
+ * @param value the node's new value
+ */
+
+ public boolean setNewValue(int depth, int value) {
+ if(isFull()) {
+ return false;
+ }
+ if(depth==1) {
+ if(o0==null) {
+ set0(new HuffmanNode(this, value));
+ return true;
+ }
+ else if(o1==null) {
+ set1(new HuffmanNode(this, value));
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return get0().setNewValue(depth-1, value)?
+ true:
+ get1().setNewValue(depth-1, value);
+ }
+ }
+}
diff --git a/songdbj/de/jarnbjo/vorbis/AudioPacket.java b/songdbj/de/jarnbjo/vorbis/AudioPacket.java
new file mode 100644
index 0000000000..90a54073c1
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/AudioPacket.java
@@ -0,0 +1,328 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2004/09/21 06:39:06 shred
+ * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+
+import de.jarnbjo.util.io.BitInputStream;
+
+class AudioPacket {
+
+ private int modeNumber;
+ private Mode mode;
+ private Mapping mapping;
+ private int n; // block size
+ private boolean blockFlag, previousWindowFlag, nextWindowFlag;
+
+ private int windowCenter, leftWindowStart, leftWindowEnd, leftN, rightWindowStart, rightWindowEnd, rightN;
+ private float[] window;
+ private float[][] pcm;
+ private int[][] pcmInt;
+
+ private Floor[] channelFloors;
+ private boolean[] noResidues;
+
+ private final static float[][] windows=new float[8][];
+
+ protected AudioPacket(final VorbisStream vorbis, final BitInputStream source) throws VorbisFormatException, IOException {
+
+ final SetupHeader sHeader=vorbis.getSetupHeader();
+ final IdentificationHeader iHeader=vorbis.getIdentificationHeader();
+ final Mode[] modes=sHeader.getModes();
+ final Mapping[] mappings=sHeader.getMappings();
+ final Residue[] residues=sHeader.getResidues();
+ final int channels=iHeader.getChannels();
+
+ if(source.getInt(1)!=0) {
+ throw new VorbisFormatException("Packet type mismatch when trying to create an audio packet.");
+ }
+
+ modeNumber=source.getInt(Util.ilog(modes.length-1));
+
+ try {
+ mode=modes[modeNumber];
+ }
+ catch(ArrayIndexOutOfBoundsException e) {
+ throw new VorbisFormatException("Reference to invalid mode in audio packet.");
+ }
+
+ mapping=mappings[mode.getMapping()];
+
+ final int[] magnitudes=mapping.getMagnitudes();
+ final int[] angles=mapping.getAngles();
+
+ blockFlag=mode.getBlockFlag();
+
+ final int blockSize0=iHeader.getBlockSize0();
+ final int blockSize1=iHeader.getBlockSize1();
+
+ n=blockFlag?blockSize1:blockSize0;
+
+ if(blockFlag) {
+ previousWindowFlag=source.getBit();
+ nextWindowFlag=source.getBit();
+ }
+
+ windowCenter=n/2;
+
+ if(blockFlag && !previousWindowFlag) {
+ leftWindowStart=n/4-blockSize0/4;
+ leftWindowEnd=n/4+blockSize0/4;
+ leftN=blockSize0/2;
+ }
+ else {
+ leftWindowStart=0;
+ leftWindowEnd=n/2;
+ leftN=windowCenter;
+ }
+
+ if(blockFlag && !nextWindowFlag) {
+ rightWindowStart=n*3/4-blockSize0/4;
+ rightWindowEnd=n*3/4+blockSize0/4;
+ rightN=blockSize0/2;
+ }
+ else {
+ rightWindowStart=windowCenter;
+ rightWindowEnd=n;
+ rightN=n/2;
+ }
+
+ window=getComputedWindow();//new double[n];
+
+ channelFloors=new Floor[channels];
+ noResidues=new boolean[channels];
+
+ pcm=new float[channels][n];
+ pcmInt=new int[channels][n];
+
+ boolean allFloorsEmpty=true;
+
+ for(int i=0; i=0; i--) {
+ double newA=0, newM=0;
+ final float[] magnitudeVector=pcm[magnitudes[i]];
+ final float[] angleVector=pcm[angles[i]];
+ for(int j=0; j0) {
+ //magnitudeVector[j]=m;
+ angleVector[j]=m>0?m-a:m+a;
+ }
+ else {
+ magnitudeVector[j]=m>0?m+a:m-a;
+ angleVector[j]=m;
+ }
+ }
+ }
+
+ for(int i=0; i32767) val=32767;
+ if(val<-32768) val=-32768;
+ target[j1++]=val;
+ }
+ }
+
+ // use System.arraycopy to copy the middle part (if any)
+ // of the window
+ if(leftWindowEnd+132767) val=32767;
+ if(val<-32768) val=-32768;
+ buffer[ix+(i*2)+1]=(byte)(val&0xff);
+ buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
+ ix+=channels*2;
+ }
+
+ ix=(leftWindowEnd-leftWindowStart)*channels*2;
+ for(int j=leftWindowEnd; j32767) val=32767;
+ if(val<-32768) val=-32768;
+ buffer[ix+(i*2)+1]=(byte)(val&0xff);
+ buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
+ ix+=channels*2;
+ }
+ }
+ }
+
+ protected float[] getWindow() {
+ return window;
+ }
+
+ protected int getLeftWindowStart() {
+ return leftWindowStart;
+ }
+
+ protected int getLeftWindowEnd() {
+ return leftWindowEnd;
+ }
+
+ protected int getRightWindowStart() {
+ return rightWindowStart;
+ }
+
+ protected int getRightWindowEnd() {
+ return rightWindowEnd;
+ }
+
+ public int[][] getPcm() {
+ return pcmInt;
+ }
+
+ public float[][] getFreqencyDomain() {
+ return pcm;
+ }
+}
diff --git a/songdbj/de/jarnbjo/vorbis/CodeBook.java b/songdbj/de/jarnbjo/vorbis/CodeBook.java
new file mode 100644
index 0000000000..c865b120ca
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/CodeBook.java
@@ -0,0 +1,275 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2004/09/21 06:39:06 shred
+ * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/10 19:49:04 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import de.jarnbjo.util.io.BitInputStream;
+import de.jarnbjo.util.io.HuffmanNode;
+
+class CodeBook {
+
+ private HuffmanNode huffmanRoot;
+ private int dimensions, entries;
+
+ private int[] entryLengths;
+ private float[][] valueVector;
+
+ protected CodeBook(BitInputStream source) throws VorbisFormatException, IOException {
+
+ // check sync
+ if(source.getInt(24)!=0x564342) {
+ throw new VorbisFormatException("The code book sync pattern is not correct.");
+ }
+
+ dimensions=source.getInt(16);
+ entries=source.getInt(24);
+
+ entryLengths=new int[entries];
+
+ boolean ordered=source.getBit();
+
+ if(ordered) {
+ int cl=source.getInt(5)+1;
+ for(int i=0; ientryLengths.length) {
+ throw new VorbisFormatException("The codebook entry length list is longer than the actual number of entry lengths.");
+ }
+ Arrays.fill(entryLengths, i, i+num, cl);
+ cl++;
+ i+=num;
+ }
+ }
+ else {
+ // !ordered
+ boolean sparse=source.getBit();
+
+ if(sparse) {
+ for(int i=0; i0) {
+ if(!huffmanRoot.setNewValue(el, i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ protected int getDimensions() {
+ return dimensions;
+ }
+
+ protected int getEntries() {
+ return entries;
+ }
+
+ protected HuffmanNode getHuffmanRoot() {
+ return huffmanRoot;
+ }
+
+ //public float[] readVQ(ReadableBitChannel source) throws IOException {
+ // return valueVector[readInt(source)];
+ //}
+
+ protected int readInt(final BitInputStream source) throws IOException {
+ return source.getInt(huffmanRoot);
+ /*
+ HuffmanNode node;
+ for(node=huffmanRoot; node.value==null; node=source.getBit()?node.o1:node.o0);
+ return node.value.intValue();
+ */
+ }
+
+ protected void readVvAdd(float[][] a, BitInputStream source, int offset, int length)
+ throws VorbisFormatException, IOException {
+
+ int i,j;//k;//entry;
+ int chptr=0;
+ int ch=a.length;
+
+ if(ch==0) {
+ return;
+ }
+
+ int lim=(offset+length)/ch;
+
+ for(i=offset/ch;i8){
+ for(i=0;iheader.getCodeBooks().length) {
+ throw new VorbisFormatException("A floor0_book_list entry is higher than the code book count.");
+ }
+ }
+ }
+
+ protected int getType() {
+ return 0;
+ }
+
+ protected Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException {
+ /** @todo implement */
+ throw new UnsupportedOperationException();
+ }
+
+ protected void computeFloor(float[] vector) {
+ /** @todo implement */
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/Floor1.java b/songdbj/de/jarnbjo/vorbis/Floor1.java
new file mode 100644
index 0000000000..69a118b44e
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/Floor1.java
@@ -0,0 +1,324 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$multip
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+import java.util.*;
+
+import de.jarnbjo.util.io.BitInputStream;
+
+
+class Floor1 extends Floor implements Cloneable {
+
+ private int[] partitionClassList;
+ private int maximumClass, multiplier, rangeBits;
+ private int[] classDimensions;
+ private int[] classSubclasses;
+ private int[] classMasterbooks;
+ private int[][] subclassBooks;
+ private int[] xList;
+ private int[] yList;
+ private int[] lowNeighbours, highNeighbours;
+ //private boolean[] step2Flags;
+
+ private static final int[] RANGES = {256, 128, 86, 64};
+
+ private Floor1() {
+ }
+
+ protected Floor1(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
+
+ maximumClass=-1;
+ int partitions=source.getInt(5);
+ partitionClassList=new int[partitions];
+
+ for(int i=0; imaximumClass) {
+ maximumClass=partitionClassList[i];
+ }
+ }
+
+
+ classDimensions=new int[maximumClass+1];
+ classSubclasses=new int[maximumClass+1];
+ classMasterbooks=new int[maximumClass+1];
+ subclassBooks=new int[maximumClass+1][];
+
+ int xListLength=2;
+
+ for(int i=0; i<=maximumClass; i++) {
+ classDimensions[i]=source.getInt(3)+1;
+ xListLength+=classDimensions[i];
+ classSubclasses[i]=source.getInt(2);
+
+ if(classDimensions[i] > header.getCodeBooks().length ||
+ classSubclasses[i] > header.getCodeBooks().length) {
+ throw new VorbisFormatException("There is a class dimension or class subclasses entry higher than the number of codebooks in the setup header.");
+ }
+ if(classSubclasses[i]!=0) {
+ classMasterbooks[i]=source.getInt(8);
+ }
+ subclassBooks[i]=new int[1<0) {
+ cval=source.getInt(vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].getHuffmanRoot());
+ //cval=vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].readInt(source);
+ //System.out.println("cval: "+cval);
+ }
+ //System.out.println("0: "+cls+" "+cdim+" "+cbits+" "+csub+" "+cval);
+ for(int j=0; j>>=cbits;
+ if(book>=0) {
+ clone.yList[j+offset]=source.getInt(vorbis.getSetupHeader().getCodeBooks()[book].getHuffmanRoot());
+ //clone.yList[j+offset]=vorbis.getSetupHeader().getCodeBooks()[book].readInt(source);
+ //System.out.println("b: "+(j+offset)+" "+book+" "+clone.yList[j+offset]);
+ //System.out.println("");
+ }
+ else {
+ clone.yList[j+offset]=0;
+ }
+ }
+ offset+=cdim;
+ }
+
+ //System.out.println("");
+ //for(int i=0; i=room) {
+ yList[i]=highRoom>lowRoom?
+ val-lowRoom+predicted:
+ -val+highRoom+predicted-1;
+ }
+ else {
+ yList[i]=(val&1)==1?
+ predicted-((val+1)>>1):
+ predicted+(val>>1);
+ }
+ }
+ else {
+ step2Flags[i]=false;
+ yList[i]=predicted;
+ }
+ }
+
+ final int[] xList2=new int[values];
+
+ System.arraycopy(xList, 0, xList2, 0, values);
+ sort(xList2, yList, step2Flags);
+
+ int hx=0, hy=0, lx=0, ly=yList[0]*multiplier;
+
+ float[] vector2=new float[vector.length];
+ float[] vector3=new float[vector.length];
+ Arrays.fill(vector2, 1.0f);
+ System.arraycopy(vector, 0, vector3, 0, vector.length);
+
+ for(int i=1; ioff && x[j-1]>x[j]; j--) {
+ itmp=x[j];
+ x[j]=x[j-1];
+ x[j-1]=itmp;
+ itmp=y[j];
+ y[j]=y[j-1];
+ y[j-1]=itmp;
+ btmp=b[j];
+ b[j]=b[j-1];
+ b[j-1]=btmp;
+ //swap(x, j, j-1);
+ //swap(y, j, j-1);
+ //swap(b, j, j-1);
+ }
+ }
+ }
+
+ private final static void swap(int x[], int a, int b) {
+ int t = x[a];
+ x[a] = x[b];
+ x[b] = t;
+ }
+
+ private final static void swap(boolean x[], int a, int b) {
+ boolean t = x[a];
+ x[a] = x[b];
+ x[b] = t;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java b/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java
new file mode 100644
index 0000000000..1e18163385
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java
@@ -0,0 +1,120 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2004/09/21 06:39:06 shred
+ * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/03/31 00:20:16 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+
+import de.jarnbjo.util.io.BitInputStream;
+
+/**
+ */
+
+public class IdentificationHeader {
+
+ private int version, channels, sampleRate, bitrateMaximum, bitrateNominal, bitrateMinimum, blockSize0, blockSize1;
+ private boolean framingFlag;
+ private MdctFloat[] mdct=new MdctFloat[2];
+ //private MdctLong[] mdctInt=new MdctLong[2];
+
+ private static final long HEADER = 0x736962726f76L; // 'vorbis'
+
+ public IdentificationHeader(BitInputStream source) throws VorbisFormatException, IOException {
+ //equalizer=new Equalizer();
+ //equalizer.pack();
+ //equalizer.show();
+
+ long leading=source.getLong(48);
+ if(leading!=HEADER) {
+ throw new VorbisFormatException("The identification header has an illegal leading.");
+ }
+ version=source.getInt(32);
+ channels=source.getInt(8);
+ sampleRate=source.getInt(32);
+ bitrateMaximum=source.getInt(32);
+ bitrateNominal=source.getInt(32);
+ bitrateMinimum=source.getInt(32);
+ int bs=source.getInt(8);
+ blockSize0=1<<(bs&0xf);
+ blockSize1=1<<(bs>>4);
+
+ mdct[0]=new MdctFloat(blockSize0);
+ mdct[1]=new MdctFloat(blockSize1);
+ //mdctInt[0]=new MdctLong(blockSize0);
+ //mdctInt[1]=new MdctLong(blockSize1);
+
+ framingFlag=source.getInt(8)!=0;
+ }
+
+ public int getSampleRate() {
+ return sampleRate;
+ }
+
+ public int getMaximumBitrate() {
+ return bitrateMaximum;
+ }
+
+ public int getNominalBitrate() {
+ return bitrateNominal;
+ }
+
+ public int getMinimumBitrate() {
+ return bitrateMinimum;
+ }
+
+ public int getChannels() {
+ return channels;
+ }
+
+ public int getBlockSize0() {
+ return blockSize0;
+ }
+
+ public int getBlockSize1() {
+ return blockSize1;
+ }
+
+ protected MdctFloat getMdct0() {
+ return mdct[0];
+ }
+
+ protected MdctFloat getMdct1() {
+ return mdct[1];
+ }
+
+ public int getVersion() {
+ return version;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/Mapping.java b/songdbj/de/jarnbjo/vorbis/Mapping.java
new file mode 100644
index 0000000000..24a2eaa19a
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/Mapping.java
@@ -0,0 +1,59 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+
+import de.jarnbjo.util.io.BitInputStream;
+
+abstract class Mapping {
+
+ protected static Mapping createInstance(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
+
+ int type=source.getInt(16);
+ switch(type) {
+ case 0:
+ //System.out.println("mapping type 0");
+ return new Mapping0(vorbis, source, header);
+ default:
+ throw new VorbisFormatException("Mapping type "+type+" is not supported.");
+ }
+ }
+
+ protected abstract int getType();
+ protected abstract int[] getAngles();
+ protected abstract int[] getMagnitudes() ;
+ protected abstract int[] getMux();
+ protected abstract int[] getSubmapFloors();
+ protected abstract int[] getSubmapResidues();
+ protected abstract int getCouplingSteps();
+ protected abstract int getSubmaps();
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/Mapping0.java b/songdbj/de/jarnbjo/vorbis/Mapping0.java
new file mode 100644
index 0000000000..e8fde4686f
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/Mapping0.java
@@ -0,0 +1,146 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+
+import de.jarnbjo.util.io.BitInputStream;
+
+class Mapping0 extends Mapping {
+
+ private int[] magnitudes, angles, mux, submapFloors, submapResidues;
+
+ protected Mapping0(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
+
+ int submaps=1;
+
+ if(source.getBit()) {
+ submaps=source.getInt(4)+1;
+ }
+
+ //System.out.println("submaps: "+submaps);
+
+ int channels=vorbis.getIdentificationHeader().getChannels();
+ int ilogChannels=Util.ilog(channels-1);
+
+ //System.out.println("ilogChannels: "+ilogChannels);
+
+ if(source.getBit()) {
+ int couplingSteps=source.getInt(8)+1;
+ magnitudes=new int[couplingSteps];
+ angles=new int[couplingSteps];
+
+ for(int i=0; i=channels || angles[i]>=channels) {
+ System.err.println(magnitudes[i]);
+ System.err.println(angles[i]);
+ throw new VorbisFormatException("The channel magnitude and/or angle mismatch.");
+ }
+ }
+ }
+ else {
+ magnitudes=new int[0];
+ angles=new int[0];
+ }
+
+ if(source.getInt(2)!=0) {
+ throw new VorbisFormatException("A reserved mapping field has an invalid value.");
+ }
+
+ mux=new int[channels];
+ if(submaps>1) {
+ for(int i=0; isubmaps) {
+ throw new VorbisFormatException("A mapping mux value is higher than the number of submaps");
+ }
+ }
+ }
+ else {
+ for(int i=0; ifloorCount) {
+ throw new VorbisFormatException("A mapping floor value is higher than the number of floors.");
+ }
+
+ if(submapResidues[i]>residueCount) {
+ throw new VorbisFormatException("A mapping residue value is higher than the number of residues.");
+ }
+ }
+ }
+
+ protected int getType() {
+ return 0;
+ }
+
+ protected int[] getAngles() {
+ return angles;
+ }
+
+ protected int[] getMagnitudes() {
+ return magnitudes;
+ }
+
+ protected int[] getMux() {
+ return mux;
+ }
+
+ protected int[] getSubmapFloors() {
+ return submapFloors;
+ }
+
+ protected int[] getSubmapResidues() {
+ return submapResidues;
+ }
+
+ protected int getCouplingSteps() {
+ return angles.length;
+ }
+
+ protected int getSubmaps() {
+ return submapFloors.length;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/MdctFloat.java b/songdbj/de/jarnbjo/vorbis/MdctFloat.java
new file mode 100644
index 0000000000..4f354b259b
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/MdctFloat.java
@@ -0,0 +1,321 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2004/09/21 12:09:45 shred
+ * *** empty log message ***
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/10 19:49:04 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+class MdctFloat {
+ static private final float cPI3_8=0.38268343236508977175f;
+ static private final float cPI2_8=0.70710678118654752441f;
+ static private final float cPI1_8=0.92387953251128675613f;
+
+ private int n;
+ private int log2n;
+
+ private float[] trig;
+ private int[] bitrev;
+
+ private float[] equalizer;
+
+ private float scale;
+
+ private int itmp1, itmp2, itmp3, itmp4, itmp5, itmp6, itmp7, itmp8, itmp9;
+ private float dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, dtmp6, dtmp7, dtmp8, dtmp9;
+
+ protected MdctFloat(int n) {
+ bitrev=new int[n/4];
+ trig=new float[n+n/4];
+
+ int n2=n>>>1;
+ log2n=(int)Math.rint(Math.log(n)/Math.log(2));
+ this.n=n;
+
+ int AE=0;
+ int AO=1;
+ int BE=AE+n/2;
+ int BO=BE+1;
+ int CE=BE+n/2;
+ int CO=CE+1;
+ // trig lookups...
+ for(int i=0;i>>j!=0;j++)
+ if(((msb>>>j)&i)!=0)acc|=1<>1;
+ int n4=n>>2;
+ int n8=n>>3;
+
+ if(equalizer!=null) {
+ for(int i=0; i32767.0f) temp1=32767.0f;
+ //if(temp1<-32768.0f) temp1=-32768.0f;
+ //if(temp2>32767.0f) temp2=32767.0f;
+ //if(temp2<-32768.0f) temp2=-32768.0f;
+
+ pcm[o1]=(int)(-temp1*window[o1]);
+ pcm[o2]=(int)( temp1*window[o2]);
+ pcm[o3]=(int)( temp2*window[o3]);
+ pcm[o4]=(int)( temp2*window[o4]);
+
+ o1++;
+ o2--;
+ o3++;
+ o4--;
+ //xx+=2;
+ //B+=2;
+ }
+ }
+ }
+
+ private float[] kernel(float[] x, float[] w,
+ int n, int n2, int n4, int n8){
+ // step 2
+
+ int xA=n4;
+ int xB=0;
+ int w2=n4;
+ int A=n2;
+
+ for(int i=0;i>>(i+2);
+ int k1=1<<(i+3);
+ int wbase=n2-2;
+
+ A=0;
+ float[] temp;
+
+ for(int r=0;r<(k0>>>2);r++){
+ int w1=wbase;
+ w2=w1-(k0>>1);
+ float AEv= trig[A],wA;
+ float AOv= trig[A+1],wB;
+ wbase-=2;
+
+ k0++;
+ for(int s=0;s<(2<header.getMappings().length) {
+ throw new VorbisFormatException("Mode mapping number is higher than total number of mappings.");
+ }
+ }
+
+ protected boolean getBlockFlag() {
+ return blockFlag;
+ }
+
+ protected int getWindowType() {
+ return windowType;
+ }
+
+ protected int getTransformType() {
+ return transformType;
+ }
+
+ protected int getMapping() {
+ return mapping;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/Residue.java b/songdbj/de/jarnbjo/vorbis/Residue.java
new file mode 100644
index 0000000000..78c28fa5ed
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/Residue.java
@@ -0,0 +1,260 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.3 2003/04/04 08:33:02 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import de.jarnbjo.util.io.*;
+
+
+abstract class Residue {
+
+ protected int begin, end;
+ protected int partitionSize; // grouping
+ protected int classifications; // partitions
+ protected int classBook; // groupbook
+ protected int[] cascade; // secondstages
+ protected int[][] books;
+ protected HashMap looks=new HashMap();
+
+ protected Residue() {
+ }
+
+ protected Residue(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
+ begin=source.getInt(24);
+ end=source.getInt(24);
+ partitionSize=source.getInt(24)+1;
+ classifications=source.getInt(6)+1;
+ classBook=source.getInt(8);
+
+ cascade=new int[classifications];
+
+ int acc=0;
+
+ for(int i=0; iheader.getCodeBooks().length) {
+ throw new VorbisFormatException("Reference to invalid codebook entry in residue header.");
+ }
+ }
+ }
+ }
+ }
+
+
+ protected static Residue createInstance(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
+
+ int type=source.getInt(16);
+ switch(type) {
+ case 0:
+ //System.out.println("residue type 0");
+ return new Residue0(source, header);
+ case 1:
+ //System.out.println("residue type 1");
+ return new Residue2(source, header);
+ case 2:
+ //System.out.println("residue type 2");
+ return new Residue2(source, header);
+ default:
+ throw new VorbisFormatException("Residue type "+type+" is not supported.");
+ }
+ }
+
+ protected abstract int getType();
+ protected abstract void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException;
+ //public abstract double[][] getDecodedVectors();
+
+ protected int getBegin() {
+ return begin;
+ }
+
+ protected int getEnd() {
+ return end;
+ }
+
+ protected int getPartitionSize() {
+ return partitionSize;
+ }
+
+ protected int getClassifications() {
+ return classifications;
+ }
+
+ protected int getClassBook() {
+ return classBook;
+ }
+
+ protected int[] getCascade() {
+ return cascade;
+ }
+
+ protected int[][] getBooks() {
+ return books;
+ }
+
+ protected final void fill(Residue clone) {
+ clone.begin=begin;
+ clone.books=books;
+ clone.cascade=cascade;
+ clone.classBook=classBook;
+ clone.classifications=classifications;
+ clone.end=end;
+ clone.partitionSize=partitionSize;
+ }
+
+ protected Look getLook(VorbisStream source, Mode key) {
+ //return new Look(source, key);
+ Look look=(Look)looks.get(key);
+ if(look==null) {
+ look=new Look(source, key);
+ looks.put(key, look);
+ }
+ return look;
+ }
+
+
+ class Look {
+ int map;
+ int parts;
+ int stages;
+ CodeBook[] fullbooks;
+ CodeBook phrasebook;
+ int[][] partbooks;
+ int partvals;
+ int[][] decodemap;
+ int postbits;
+ int phrasebits;
+ int frames;
+
+ protected Look (VorbisStream source, Mode mode) {
+ int dim=0, acc=0, maxstage=0;
+
+ map=mode.getMapping();
+ parts=Residue.this.getClassifications();
+ fullbooks=source.getSetupHeader().getCodeBooks();
+ phrasebook=fullbooks[Residue.this.getClassBook()];
+ dim=phrasebook.getDimensions();
+
+ partbooks=new int[parts][];
+
+ for(int j=0;jmaxstage) {
+ maxstage=stages;
+ }
+ partbooks[j]=new int[stages];
+ for(int k=0; k0; x>>=1, res++);
+ return res;
+ }
+
+ public static final float float32unpack(int x) {
+ float mantissa=x&0x1fffff;
+ float e=(x&0x7fe00000)>>21;
+ if((x&0x80000000)!=0) {
+ mantissa=-mantissa;
+ }
+ return mantissa*(float)Math.pow(2.0, e-788.0);
+ }
+
+ public static final int lookup1Values(int a, int b) {
+ int res=(int)Math.pow(Math.E, Math.log(a)/b);
+ return intPow(res+1, b)<=a?res+1:res;
+ }
+
+ public static final int intPow(int base, int e) {
+ int res=1;
+ for(; e>0; e--, res*=base);
+ return res;
+ }
+
+ public static final boolean isBitSet(int value, int bit) {
+ return (value&(1<0) {
+ res+=value&1;
+ value>>=1;
+ }
+ return res;
+ }
+
+ public static final int lowNeighbour(int[] v, int x) {
+ int max=-1, n=0;
+ for(int i=0; imax && v[i]v[x]) {
+ min=v[i];
+ n=i;
+ }
+ }
+ return n;
+ }
+
+ public static final int renderPoint(int x0, int x1, int y0, int y1, int x) {
+ int dy=y1-y0;
+ int ady=dy<0?-dy:dy;
+ int off=(ady*(x-x0))/(x1-x0);
+ return dy<0?y0-off:y0+off;
+ }
+
+ public static final void renderLine(final int x0, final int y0, final int x1, final int y1, final float[] v) {
+ final int dy=y1-y0;
+ final int adx=x1-x0;
+ final int base=dy/adx;
+ final int sy=dy<0?base-1:base+1;
+ int x=x0;
+ int y=y0;
+ int err=0;
+ final int ady=(dy<0?-dy:dy)-(base>0?base*adx:-base*adx);
+
+ v[x]*=Floor.DB_STATIC_TABLE[y];
+ for(x=x0+1; x=adx) {
+ err-=adx;
+ v[x]*=Floor.DB_STATIC_TABLE[y+=sy];
+ }
+ else {
+ v[x]*=Floor.DB_STATIC_TABLE[y+=base];
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java b/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java
new file mode 100644
index 0000000000..b1bc999947
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java
@@ -0,0 +1,217 @@
+package de.jarnbjo.vorbis;
+
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2004/09/21 06:39:06 shred
+ * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ *
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.util.Collection;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.sound.sampled.spi.AudioFileReader;
+
+import de.jarnbjo.ogg.BasicStream;
+import de.jarnbjo.ogg.EndOfOggStreamException;
+import de.jarnbjo.ogg.FileStream;
+import de.jarnbjo.ogg.LogicalOggStream;
+import de.jarnbjo.ogg.OggFormatException;
+import de.jarnbjo.ogg.PhysicalOggStream;
+import de.jarnbjo.ogg.UncachedUrlStream;
+
+public class VorbisAudioFileReader extends AudioFileReader {
+
+ public VorbisAudioFileReader() {
+ }
+
+ public AudioFileFormat getAudioFileFormat(File file) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioFileFormat(new FileStream(new RandomAccessFile(file, "r")));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ public AudioFileFormat getAudioFileFormat(InputStream stream) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioFileFormat(new BasicStream(stream));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ public AudioFileFormat getAudioFileFormat(URL url) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioFileFormat(new UncachedUrlStream(url));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ private AudioFileFormat getAudioFileFormat(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException {
+ try {
+ Collection streams=oggStream.getLogicalStreams();
+ if(streams.size()!=1) {
+ throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
+ }
+
+ LogicalOggStream los=(LogicalOggStream)streams.iterator().next();
+ if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) {
+ throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
+ }
+
+ VorbisStream vs=new VorbisStream(los);
+
+ AudioFormat audioFormat=new AudioFormat(
+ (float)vs.getIdentificationHeader().getSampleRate(),
+ 16,
+ vs.getIdentificationHeader().getChannels(),
+ true, true);
+
+ return new AudioFileFormat(VorbisFormatType.getInstance(), audioFormat, AudioSystem.NOT_SPECIFIED);
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ catch(VorbisFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+
+
+ public AudioInputStream getAudioInputStream(File file) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioInputStream(new FileStream(new RandomAccessFile(file, "r")));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ public AudioInputStream getAudioInputStream(InputStream stream) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioInputStream(new BasicStream(stream));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ public AudioInputStream getAudioInputStream(URL url) throws IOException, UnsupportedAudioFileException {
+ try {
+ return getAudioInputStream(new UncachedUrlStream(url));
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+ private AudioInputStream getAudioInputStream(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException {
+ try {
+ Collection streams=oggStream.getLogicalStreams();
+ if(streams.size()!=1) {
+ throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
+ }
+
+ LogicalOggStream los=(LogicalOggStream)streams.iterator().next();
+ if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) {
+ throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
+ }
+
+ VorbisStream vs=new VorbisStream(los);
+
+ AudioFormat audioFormat=new AudioFormat(
+ (float)vs.getIdentificationHeader().getSampleRate(),
+ 16,
+ vs.getIdentificationHeader().getChannels(),
+ true, true);
+
+ return new AudioInputStream(new VorbisInputStream(vs), audioFormat, -1);
+ }
+ catch(OggFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ catch(VorbisFormatException e) {
+ throw new UnsupportedAudioFileException(e.getMessage());
+ }
+ }
+
+
+ public static class VorbisFormatType extends AudioFileFormat.Type {
+
+ private static final VorbisFormatType instance=new VorbisFormatType();
+
+ private VorbisFormatType() {
+ super("VORBIS", "ogg");
+ }
+
+ public static AudioFileFormat.Type getInstance() {
+ return instance;
+ }
+ }
+
+ public static class VorbisInputStream extends InputStream {
+
+ private VorbisStream source;
+ private byte[] buffer=new byte[8192];
+
+ public VorbisInputStream(VorbisStream source) {
+ this.source=source;
+ }
+
+ public int read() throws IOException {
+ return 0;
+ }
+
+ public int read(byte[] buffer) throws IOException {
+ return read(buffer, 0, buffer.length);
+ }
+
+ public int read(byte[] buffer, int offset, int length) throws IOException {
+ try {
+ return source.readPcm(buffer, offset, length);
+ }
+ catch(EndOfOggStreamException e) {
+ return -1;
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java b/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java
new file mode 100644
index 0000000000..5214298378
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java
@@ -0,0 +1,51 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.2 2005/02/09 23:10:47 shred
+ * Serial UID für jarnbjo
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when trying to read a corrupted Vorbis stream.
+ */
+
+public class VorbisFormatException extends IOException {
+ private static final long serialVersionUID = 3616453405694834743L;
+
+ public VorbisFormatException() {
+ super();
+ }
+
+ public VorbisFormatException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisStream.java b/songdbj/de/jarnbjo/vorbis/VorbisStream.java
new file mode 100644
index 0000000000..36659c7106
--- /dev/null
+++ b/songdbj/de/jarnbjo/vorbis/VorbisStream.java
@@ -0,0 +1,247 @@
+/*
+ * $ProjectName$
+ * $ProjectRevision$
+ * -----------------------------------------------------------
+ * $Id$
+ * -----------------------------------------------------------
+ *
+ * $Author$
+ *
+ * Description:
+ *
+ * Copyright 2002-2003 Tor-Einar Jarnbjo
+ * -----------------------------------------------------------
+ *
+ * Change History
+ * -----------------------------------------------------------
+ * $Log$
+ * Revision 1.1 2005/07/11 15:42:36 hcl
+ * Songdb java version, source. only 1.5 compatible
+ *
+ * Revision 1.1.1.1 2004/04/04 22:09:12 shred
+ * First Import
+ *
+ * Revision 1.4 2003/04/10 19:49:04 jarnbjo
+ * no message
+ *
+ * Revision 1.3 2003/03/31 00:20:16 jarnbjo
+ * no message
+ *
+ * Revision 1.2 2003/03/16 01:11:12 jarnbjo
+ * no message
+ *
+ *
+ */
+
+package de.jarnbjo.vorbis;
+
+import java.io.*;
+import java.util.*;
+
+import de.jarnbjo.ogg.*;
+import de.jarnbjo.util.io.*;
+
+/**
+ */
+
+public class VorbisStream {
+
+ private LogicalOggStream oggStream;
+ private IdentificationHeader identificationHeader;
+ private CommentHeader commentHeader;
+ private SetupHeader setupHeader;
+
+ private AudioPacket lastAudioPacket, nextAudioPacket;
+ private LinkedList audioPackets=new LinkedList();
+ private byte[] currentPcm;
+ private int currentPcmIndex;
+ private int currentPcmLimit;
+
+ private static final int IDENTIFICATION_HEADER = 1;
+ private static final int COMMENT_HEADER = 3;
+ private static final int SETUP_HEADER = 5;
+
+ private int bitIndex=0;
+ private byte lastByte=(byte)0;
+ private boolean initialized=false;
+
+ private Object streamLock=new Object();
+ private int pageCounter=0;
+
+ private int currentBitRate=0;
+
+ private long currentGranulePosition;
+
+ public static final int BIG_ENDIAN = 0;
+ public static final int LITTLE_ENDIAN = 1;
+
+ public VorbisStream() {
+ }
+
+ public VorbisStream(LogicalOggStream oggStream) throws VorbisFormatException, IOException {
+ this.oggStream=oggStream;
+
+ for(int i=0; i<3; i++) {
+ BitInputStream source=new ByteArrayBitInputStream(oggStream.getNextOggPacket());
+ int headerType=source.getInt(8);
+ switch(headerType) {
+ case IDENTIFICATION_HEADER:
+ identificationHeader=new IdentificationHeader(source);
+ break;
+ case COMMENT_HEADER:
+ commentHeader=new CommentHeader(source);
+ break;
+ case SETUP_HEADER:
+ setupHeader=new SetupHeader(this, source);
+ break;
+ }
+ }
+
+ if(identificationHeader==null) {
+ throw new VorbisFormatException("The file has no identification header.");
+ }
+
+ if(commentHeader==null) {
+ throw new VorbisFormatException("The file has no commentHeader.");
+ }
+
+ if(setupHeader==null) {
+ throw new VorbisFormatException("The file has no setup header.");
+ }
+
+ //currentPcm=new int[identificationHeader.getChannels()][16384];
+ currentPcm=new byte[identificationHeader.getChannels()*identificationHeader.getBlockSize1()*2];
+ //new BufferThread().start();
+ }
+
+ public IdentificationHeader getIdentificationHeader() {
+ return identificationHeader;
+ }
+
+ public CommentHeader getCommentHeader() {
+ return commentHeader;
+ }
+
+ protected SetupHeader getSetupHeader() {
+ return setupHeader;
+ }
+
+ public boolean isOpen() {
+ return oggStream.isOpen();
+ }
+
+ public void close() throws IOException {
+ oggStream.close();
+ }
+
+
+ public int readPcm(byte[] buffer, int offset, int length) throws IOException {
+ synchronized (streamLock) {
+ final int channels=identificationHeader.getChannels();
+
+ if(lastAudioPacket==null) {
+ lastAudioPacket=getNextAudioPacket();
+ }
+ if(currentPcm==null || currentPcmIndex>=currentPcmLimit) {
+ AudioPacket ap=getNextAudioPacket();
+ try {
+ ap.getPcm(lastAudioPacket, currentPcm);
+ currentPcmLimit=ap.getNumberOfSamples()*identificationHeader.getChannels()*2;
+ }
+ catch(ArrayIndexOutOfBoundsException e) {
+ return 0;
+ }
+ currentPcmIndex=0;
+ lastAudioPacket=ap;
+ }
+ int written=0;
+ int i=0;
+ int arrIx=0;
+ for(i=currentPcmIndex; i