From 9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e Mon Sep 17 00:00:00 2001
From: Michiel Van Der Kolk 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 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.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; pagecreate
methods, supplying a JMF stream or
+ * a RandomAccessFile
+ * which is positioned at the beginning of an Ogg page.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 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()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; itrue
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
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<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; iThe prefered way to obtain an AudioFile
is to use the AudioFileIO.read(File)
method.
The AudioFile
contains every properties associated with the file itself (no meta-data), like the bitrate, the sampling rate, the encoding infos, etc.
To get the meta-data contained in this file you have to get the Tag
of this AudioFile
These constructors are used by the different readers, users should not use them, but use the AudioFileIO.read(File)
method instead !.
Create the AudioFile representing file denoted by pathname s, the encodinginfos and containing the tag
+ * + *@param s The pathname of the audiofile + *@param info the encoding infos over this file + *@param tag the tag contained in this file + */ + public AudioFile(String s, EncodingInfo info, Tag tag) { + super(s); + this.info = info; + this.tag = tag; + } + + /** + *These constructors are used by the different readers, users should not use them, but use the AudioFileIO.read(File)
method instead !.
Create the AudioFile representing file denoted by pathname s, the encodinginfos and containing an empty tag
+ * + *@param s The pathname of the audiofile + *@param info the encoding infos over this file + */ + public AudioFile(String s, EncodingInfo info) { + super(s); + this.info = info; + this.tag = new GenericTag(); + } + + /** + *These constructors are used by the different readers, users should not use them, but use the AudioFileIO.read(File)
method instead !.
Create the AudioFile representing file f, the encodinginfos and containing the tag
+ * + *@param f The file of the audiofile + *@param info the encoding infos over this file + *@param tag the tag contained in this file + */ + public AudioFile(File f, EncodingInfo info, Tag tag) { + super(f.getAbsolutePath()); + this.info = info; + this.tag = tag; + } + + /** + *These constructors are used by the different readers, users should not use them, but use the AudioFileIO.read(File)
method instead !.
Create the AudioFile representing file f, the encodinginfos and containing an empty tag
+ * + *@param f The file of the audiofile + *@param info the encoding infos over this file + */ + public AudioFile(File f, EncodingInfo info) { + super(f.getAbsolutePath()); + this.info = info; + this.tag = new GenericTag(); + } + + /** + *Returns the bitrate of this AufioFile in kilobytes per second (KB/s). Example: 192 KB/s
+ * + *@return Returns the bitrate of this AufioFile + */ + public int getBitrate() { + return info.getBitrate(); + } + + /** + *Returns the number of audio channels contained in this AudioFile, 2 for example means stereo
+ * + *@return Returns the number of audio channels contained in this AudioFile + */ + public int getChannelNumber() { + return info.getChannelNumber(); + } + + /** + *Returns the encoding type of this AudioFile, this needs to be precisely specified in the future
+ * + *@return Returns the encoding type of this AudioFile + *@todo This method needs to be fully specified + */ + public String getEncodingType() { + return info.getEncodingType(); + } + + /** + *Returns the extra encoding infos of this AudioFile, this needs to be precisely specified in the future
+ * + *@return Returns the extra encoding infos of this AudioFile + *@todo This method needs to be fully specified + */ + public String getExtraEncodingInfos() { + return info.getExtraEncodingInfos(); + } + + /** + *Returns the sampling rate of this AudioFile in Hertz (Hz). Example: 44100 Hz for most of the audio files
+ * + *@return Returns the sampling rate of this AudioFile + */ + public int getSamplingRate() { + return info.getSamplingRate(); + } + + /** + *Returns the length (duration) in seconds (s) of this AudioFile.Example: 241 seconds
+ * + *@return Returns the length (duration) of this AudioFile + */ + public int getLength() { + return info.getLength(); + } + + /** + *Returns the tag contained in this AudioFile, the Tag
contains any useful meta-data, like artist, album, title, etc.
If the file does not contain any tag, a new empty tag is returned
+ * + *@return Returns the tag contained in this AudioFile, or a new one if file hasn't any tag. + */ + public Tag getTag() { + return (tag == null) ? new GenericTag() : tag; + } + + /* + *Checks if this file is a VBR (variable bitrate) or a Constant Bitrate one
+ *True means VBR, false means CBR
+ *This has only meaning with MP3 and MPC files, other formats are always VBR + * since it offers a better compression ratio (and lossless compression is by nature VBR
+ */ + public boolean isVbr() { + return info.isVbr(); + } + + /** + *Returns a multi-line string with the file path, the encoding informations, and the tag contents.
+ * + *@return A multi-line string with the file path, the encoding informations, and the tag contents. + *@todo Maybe this can be changed ? + */ + public String toString() { + return "AudioFile "+getAbsolutePath()+" --------\n"+info.toString()+"\n"+ ( (tag == null) ? "" : tag.toString())+"\n-------------------"; + } +} diff --git a/songdbj/entagged/audioformats/EncodingInfo.java b/songdbj/entagged/audioformats/EncodingInfo.java new file mode 100644 index 0000000000..6f1ff0ff91 --- /dev/null +++ b/songdbj/entagged/audioformats/EncodingInfo.java @@ -0,0 +1,116 @@ +/* + * ******************************************************************** ** + * Copyright notice ** + * ** ** + * (c) 2003 Entagged Developpement Team ** + * http://www.sourceforge.net/projects/entagged ** + * ** ** + * All rights reserved ** + * ** ** + * This script is part of the Entagged project. The Entagged ** + * project is free software; you can redistribute it and/or modify ** + * it under the terms of the GNU General Public License as published by ** + * the Free Software Foundation; either version 2 of the License, or ** + * (at your option) any later version. ** + * ** ** + * The GNU General Public License can be found at ** + * http://www.gnu.org/copyleft/gpl.html. ** + * ** ** + * This copyright notice MUST APPEAR in all copies of the file! ** + * ******************************************************************** + */ +package entagged.audioformats; + +import java.util.*; + +public class EncodingInfo { + + private Hashtable content; + + public EncodingInfo() { + content = new Hashtable(6); + content.put("BITRATE", new Integer(-1) ); + content.put("CHANNB", new Integer(-1) ); + content.put("TYPE", ""); + content.put("INFOS", ""); + content.put("SAMPLING", new Integer(-1) ); + content.put("LENGTH", new Integer(-1) ); + content.put("VBR", new Boolean(true) ); + } + + //Sets the bitrate in KByte/s + public void setBitrate( int bitrate ) { + content.put("BITRATE", new Integer(bitrate) ); + } + //Sets the number of channels + public void setChannelNumber( int chanNb ) { + content.put("CHANNB", new Integer(chanNb) ); + } + //Sets the type of the encoding, this is a bit format specific. eg:Layer I/II/II + public void setEncodingType( String encodingType ) { + content.put("TYPE", encodingType ); + } + //A string contianing anything else that might be interesting + public void setExtraEncodingInfos( String infos ) { + content.put("INFOS", infos ); + } + //Sets the Sampling rate in Hz + public void setSamplingRate( int samplingRate ) { + content.put("SAMPLING", new Integer(samplingRate) ); + } + //Sets the length of the song in seconds + public void setLength( int length ) { + content.put("LENGTH", new Integer(length) ); + } + //Is the song vbr or not ? + public void setVbr( boolean b ) { + content.put("VBR", new Boolean(b) ); + } + + + //returns the bitrate in KByte/s + public int getBitrate() { + return ((Integer) content.get("BITRATE")).intValue(); + } + //Returns the number of channels + public int getChannelNumber() { + return ((Integer) content.get("CHANNB")).intValue(); + } + //returns the encoding type. + public String getEncodingType() { + return (String) content.get("TYPE"); + } + //returns a string with misc. information about the encoding + public String getExtraEncodingInfos() { + return (String) content.get("INFOS"); + } + //returns the sample rate in Hz + public int getSamplingRate() { + return ((Integer) content.get("SAMPLING")).intValue(); + } + //Returns the length of the song in seconds + public int getLength() { + return ((Integer) content.get("LENGTH")).intValue(); + } + //Is the song vbr ? + public boolean isVbr() { + return ((Boolean) content.get("VBR")).booleanValue(); + } + + //Pretty prints this encoding info + public String toString() { + StringBuffer out = new StringBuffer(50); + out.append("Encoding infos content:\n"); + Enumeration en = content.keys(); + while(en.hasMoreElements()) { + Object key = en.nextElement(); + Object val = content.get(key); + out.append("\t"); + out.append(key); + out.append(" : "); + out.append(val); + out.append("\n"); + } + return out.toString().substring(0,out.length()-1); + } +} diff --git a/songdbj/entagged/audioformats/Tag.java b/songdbj/entagged/audioformats/Tag.java new file mode 100644 index 0000000000..c1189ec2e3 --- /dev/null +++ b/songdbj/entagged/audioformats/Tag.java @@ -0,0 +1,116 @@ +package entagged.audioformats; + +import java.util.Iterator; +import java.util.List; + +import entagged.audioformats.generic.TagField; + +public interface Tag { + /** + * This final field contains all the tags that id3v1 supports. The list has + * the same order as the id3v1 genres. To be perfectly compatible (with + * id3v1) the genre field should match one of these genre (case ignored). + * You can also use this list to present a list of basic (modifiable) + * possible choices for the genre field. + */ + public static final String[] DEFAULT_GENRES = { "Blues", "Classic Rock", + "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", + "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", + "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", + "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", + "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", + "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", + "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", + "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", + "Gothic", "Darkwave", "Techno-Industrial", "Electronic", + "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", + "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", + "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", + "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", + "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", + "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", + "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", + "Avantgarde", "Gothic Rock", "Progressive Rock", + "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", + "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", + "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", + "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", + "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", + "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", + "A capella", "Euro-House", "Dance Hall" }; + + public void add(TagField field); + + public void addAlbum(String s); + + public void addArtist(String s); + + public void addComment(String s); + + public void addGenre(String s); + + public void addTitle(String s); + + public void addTrack(String s); + + public void addYear(String s); + + public List get(String id); + + public Iterator getFields(); + + public List getGenre(); + + public List getTitle(); + + public List getTrack(); + + public List getYear(); + public List getAlbum(); + + public List getArtist(); + + public List getComment(); + + public String getFirstGenre(); + + public String getFirstTitle(); + + public String getFirstTrack(); + + public String getFirstYear(); + public String getFirstAlbum(); + + public String getFirstArtist(); + + public String getFirstComment(); + + public boolean hasCommonFields(); + + public boolean hasField(String id); + + public boolean isEmpty(); + + //public Iterator getCommonFields(); + //public Iterator getSpecificFields(); + + public void merge(Tag tag); + + public void set(TagField field); + + public void setAlbum(String s); + + public void setArtist(String s); + + public void setComment(String s); + + public void setGenre(String s); + + public void setTitle(String s); + + public void setTrack(String s); + + public void setYear(String s); + + public String toString(); +} \ No newline at end of file diff --git a/songdbj/entagged/audioformats/asf/AsfFileReader.java b/songdbj/entagged/audioformats/asf/AsfFileReader.java new file mode 100644 index 0000000000..d408a9fa56 --- /dev/null +++ b/songdbj/entagged/audioformats/asf/AsfFileReader.java @@ -0,0 +1,112 @@ +/* + * ******************************************************************** ** + * Copyright notice ** + * ** ** + * (c) 2003 Entagged Developpement Team ** + * http://www.sourceforge.net/projects/entagged ** + * ** ** + * All rights reserved ** + * ** ** + * This script is part of the Entagged project. The Entagged ** + * project is free software; you can redistribute it and/or modify ** + * it under the terms of the GNU General Public License as published by ** + * the Free Software Foundation; either version 2 of the License, or ** + * (at your option) any later version. ** + * ** ** + * The GNU General Public License can be found at ** + * http://www.gnu.org/copyleft/gpl.html. ** + * ** ** + * This copyright notice MUST APPEAR in all copies of the file! ** + * ******************************************************************** + */ +package entagged.audioformats.asf; + +import java.io.IOException; +import java.io.RandomAccessFile; + +import entagged.audioformats.EncodingInfo; +import entagged.audioformats.Tag; +import entagged.audioformats.asf.data.AsfHeader; +import entagged.audioformats.asf.io.AsfHeaderReader; +import entagged.audioformats.asf.util.TagConverter; +import entagged.audioformats.exceptions.CannotReadException; +import entagged.audioformats.generic.AudioFileReader; + +/** + * This reader can read asf files containing any content (stream type).null
deletes the
+ * chunk.
+ */
+ public void setContentDescription(ContentDescription contentDesc) {
+ this.contentDescription = contentDesc;
+ }
+
+ /**
+ * @param encChunk
+ * The encodingChunk to set.
+ */
+ public void setEncodingChunk(EncodingChunk encChunk) {
+ if (encChunk == null)
+ throw new IllegalArgumentException("Argument must not be null.");
+ this.encodingChunk = encChunk;
+ }
+
+ /**
+ * @param th
+ * sets the extendedContentDescription. null
+ * delete the chunk.
+ */
+ public void setExtendedContentDescription(ExtendedContentDescription th) {
+ this.extendedContentDescription = th;
+ }
+
+ /**
+ * @param fh
+ */
+ public void setFileHeader(FileHeader fh) {
+ if (fh == null)
+ throw new IllegalArgumentException("Argument must not be null.");
+ this.fileHeader = fh;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/data/AudioStreamChunk.java b/songdbj/entagged/audioformats/asf/data/AudioStreamChunk.java
new file mode 100644
index 0000000000..619f0c2a42
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/data/AudioStreamChunk.java
@@ -0,0 +1,292 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.data;
+
+import java.math.BigInteger;
+
+import entagged.audioformats.asf.util.Utils;
+
+/**
+ * This class represents the streamchunk describing an audio stream. true
if error concealment is used.
+ */
+ public boolean isErrorConcealed() {
+ return getErrorConcealment().equals(
+ GUID.GUID_AUDIO_ERROR_CONCEALEMENT_INTERLEAVED);
+ }
+
+ /**
+ * (overridden)
+ *
+ * @see entagged.audioformats.asf.data.StreamChunk#prettyPrint()
+ */
+ public String prettyPrint() {
+ StringBuffer result = new StringBuffer(super.prettyPrint().replaceAll(
+ Utils.LINE_SEPARATOR, Utils.LINE_SEPARATOR + " "));
+ result.insert(0, Utils.LINE_SEPARATOR + "AudioStream");
+ result.append("Audio info:" + Utils.LINE_SEPARATOR);
+ result.append(" Bitrate : " + getKbps() + Utils.LINE_SEPARATOR);
+ result.append(" Channels : " + getChannelCount() + " at "
+ + getSamplingRate() + " Hz" + Utils.LINE_SEPARATOR);
+ result.append(" Bits per Sample: " + getBitsPerSample()
+ + Utils.LINE_SEPARATOR);
+ result.append(" Formatcode: " + getCodecDescription()
+ + Utils.LINE_SEPARATOR);
+ return result.toString();
+ }
+
+ /**
+ * @param avgeBytesPerSec
+ * The averageBytesPerSec to set.
+ */
+ public void setAverageBytesPerSec(long avgeBytesPerSec) {
+ this.averageBytesPerSec = avgeBytesPerSec;
+ }
+
+ /**
+ * Sets the bitsPerSample
+ *
+ * @param bps
+ */
+ public void setBitsPerSample(int bps) {
+ this.bitsPerSample = bps;
+ }
+
+ /**
+ * Sets the blockAlignment.
+ *
+ * @param align
+ */
+ public void setBlockAlignment(long align) {
+ this.blockAlignment = align;
+ }
+
+ /**
+ * @param channels
+ * The channelCount to set.
+ */
+ public void setChannelCount(long channels) {
+ this.channelCount = channels;
+ }
+
+ /**
+ * Sets the codecData
+ *
+ * @param codecSpecificData
+ */
+ public void setCodecData(byte[] codecSpecificData) {
+ this.codecData = codecSpecificData;
+ }
+
+ /**
+ * @param cFormatCode
+ * The compressionFormat to set.
+ */
+ public void setCompressionFormat(long cFormatCode) {
+ this.compressionFormat = cFormatCode;
+ }
+
+ /**
+ * This method sets the error concealment type which is given by two GUIDs.
+ * true
if first byte of {@link #content}is not zero.
+ *
+ * @return boolean representation of the current value.
+ */
+ public boolean getBoolean() {
+ return content.length > 0 && content[0] != 0;
+ }
+
+ /**
+ * This method will return a byte array, which can directly be written into
+ * an "Extended Content Description"-chunk. true
if a common field.
+ */
+ public boolean isCommon() {
+ return COMMON_FIELD_IDS.contains(this.getName());
+ }
+
+ /**
+ * This method checks if the binary data is empty. true
if no value is set.
+ */
+ public boolean isEmpty() {
+ return this.content.length == 0;
+ }
+
+ /**
+ * Sets the Value of the current content descriptor. null
. Any modification of the contents of this object
+ * will set this field to null
.
+ */
+ private HashMap indexMap = null;
+
+ /**
+ * Creates an instance.
+ *
+ */
+ public ExtendedContentDescription() {
+ this(0, BigInteger.valueOf(0));
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * @param pos
+ * Position of header object within file or stream.
+ * @param chunkLen
+ * Length of the represented chunck.
+ */
+ public ExtendedContentDescription(long pos, BigInteger chunkLen) {
+ super(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, pos, chunkLen);
+ this.descriptors = new ArrayList();
+ }
+
+ /**
+ * This method inserts the given ContentDescriptor.
+ *
+ * @param toAdd
+ * ContentDescriptor to insert.
+ */
+ public void addDescriptor(ContentDescriptor toAdd) {
+ assert toAdd != null : "Argument must not be null.";
+ if (getDescriptor(toAdd.getName()) != null) {
+ throw new RuntimeException(toAdd.getName() + " is already present");
+ }
+ this.descriptors.add(toAdd);
+ this.indexMap.put(toAdd.getName(), new Integer(descriptors.size() - 1));
+ }
+
+ /**
+ * This method adds or replaces an existing content descriptor.
+ *
+ * @param descriptor
+ * Descriptor to be added or replaced.
+ */
+ public void addOrReplace(ContentDescriptor descriptor) {
+ assert descriptor != null : "Argument must not be null";
+ if (getDescriptor(descriptor.getName()) != null) {
+ /*
+ * Just remove if exists. Will prevent the indexmap being rebuild.
+ */
+ remove(descriptor.getName());
+ }
+ addDescriptor(descriptor);
+ }
+
+ /**
+ * Returns the album entered in the content descriptor chunk.
+ *
+ * @return Album, ""
if not defined.
+ */
+ public String getAlbum() {
+ ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ALBUM);
+ if (result == null)
+ return "";
+
+ return result.getString();
+ }
+
+ /**
+ * Returns the "WM/AlbumArtist" entered in the extended content description.
+ *
+ * @return Title, ""
if not defined.
+ */
+ public String getArtist() {
+ ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ARTIST);
+ if (result == null)
+ return "";
+ return result.getString();
+ }
+
+ /**
+ * This method creates a byte array which can be written to asf files.
+ *
+ * @return asf file representation of the current object.
+ */
+ public byte[] getBytes() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ ByteArrayOutputStream content = new ByteArrayOutputStream();
+ // Write the number of descriptors.
+ content.write(Utils.getBytes(this.descriptors.size(), 2));
+ Iterator it = this.descriptors.iterator();
+ while (it.hasNext()) {
+ ContentDescriptor current = (ContentDescriptor) it.next();
+ content.write(current.getBytes());
+ }
+ byte[] contentBytes = content.toByteArray();
+ // Write the guid
+ result.write(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION.getBytes());
+ // Write the length + 24.
+ result.write(Utils.getBytes(contentBytes.length + 24, 8));
+ // Write the content
+ result.write(contentBytes);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Returns a previously inserted content descriptor.
+ *
+ * @param name
+ * name of the content descriptor.
+ * @return null
if not present.
+ */
+ public ContentDescriptor getDescriptor(String name) {
+ if (this.indexMap == null) {
+ this.indexMap = new HashMap();
+ for (int i = 0; i < descriptors.size(); i++) {
+ ContentDescriptor current = (ContentDescriptor) descriptors
+ .get(i);
+ indexMap.put(current.getName(), new Integer(i));
+ }
+ }
+ Integer pos = (Integer) indexMap.get(name);
+ if (pos != null) {
+ return (ContentDescriptor) descriptors.get(pos.intValue());
+ }
+ return null;
+ }
+
+ /**
+ * @return Returns the descriptorCount.
+ */
+ public long getDescriptorCount() {
+ return descriptors.size();
+ }
+
+ /**
+ * Returns a collection of all {@link ContentDescriptor}objects stored in
+ * this extended content description.
+ *
+ * @return An enumeration of {@link ContentDescriptor}objects.
+ */
+ public Collection getDescriptors() {
+ return new ArrayList(this.descriptors);
+ }
+
+ /**
+ * Returns the Genre entered in the content descriptor chunk.
+ *
+ * @return Genre, ""
if not defined.
+ */
+ public String getGenre() {
+ String result = null;
+ ContentDescriptor prop = getDescriptor(ContentDescriptor.ID_GENRE);
+ if (prop == null) {
+ prop = getDescriptor(ContentDescriptor.ID_GENREID);
+ if (prop == null)
+ result = "";
+ else {
+ result = prop.getString();
+ if (result.startsWith("(") && result.endsWith(")")) {
+ result = result.substring(1, result.length() - 1);
+ try {
+ int genreNum = Integer.parseInt(result);
+ if (genreNum >= 0
+ && genreNum < Tag.DEFAULT_GENRES.length) {
+ result = Tag.DEFAULT_GENRES[genreNum];
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing
+ }
+ }
+ }
+ } else {
+ result = prop.getString();
+ }
+ return result;
+ }
+
+ /**
+ * Returns the Track entered in the content descriptor chunk.
+ *
+ * @return Track, ""
if not defined.
+ */
+ public String getTrack() {
+ ContentDescriptor result = getDescriptor(ContentDescriptor.ID_TRACKNUMBER);
+ if (result == null)
+ return "";
+
+ return result.getString();
+ }
+
+ /**
+ * Returns the Year entered in the extended content descripion.
+ *
+ * @return Year, ""
if not defined.
+ */
+ public String getYear() {
+ ContentDescriptor result = getDescriptor(ContentDescriptor.ID_YEAR);
+ if (result == null)
+ return "";
+
+ return result.getString();
+ }
+
+ /**
+ * This method creates a String containing the tag elements an their values
+ * for printing. null
.
+ */
+ public ContentDescriptor remove(String id) {
+ ContentDescriptor result = getDescriptor(id);
+ if (result != null) {
+ descriptors.remove(result);
+ }
+ this.indexMap = null;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/data/FileHeader.java b/songdbj/entagged/audioformats/asf/data/FileHeader.java
new file mode 100644
index 0000000000..cdafbeb6be
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/data/FileHeader.java
@@ -0,0 +1,235 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.data;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+import entagged.audioformats.asf.util.Utils;
+
+/**
+ * This class stores the information about the file, which is contained within a
+ * special chunk of asf files.value
is matching the GUID
+ * specification of ASF streams. true
if value
matches the
+ * specification of a GUID.
+ */
+ public static boolean assertGUID(int[] value) {
+ boolean result = false;
+ if (value != null) {
+ if (value.length == GUID.GUID_LENGTH) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method searches a GUID in {@link #KNOWN_GUIDS}which is equal to the
+ * given guid
and returns its description. null
+ */
+ public static String getGuidDescription(GUID guid) {
+ String result = null;
+ if (guid == null) {
+ throw new IllegalArgumentException("Argument must not be null.");
+ }
+ for (int i = 0; i < KNOWN_GUIDS.length; i++) {
+ if (KNOWN_GUIDS[i].equals(guid)) {
+ result = KNOWN_GUIDS[i].getDescription();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Stores an optionally description of the GUID.
+ */
+ private String description = "";
+
+ /**
+ * An isntance of this class stores the value of the wrapped GUID in this
+ * field. guid
.true
if it is.
+ */
+ public boolean isValid() {
+ return assertGUID(getGUID());
+ }
+
+ /**
+ * This method saves a copy of the given value
as the
+ * represented value of this object. true
, the stream data is encrypted.
+ */
+ private boolean contentEncrypted;
+
+ /**
+ * This field stores the number of the current stream. null
is returned. null
if no supported Asf
+ * header was found.
+ * @throws IOException
+ * Read errors
+ */
+ public static AsfHeader readHeader(RandomAccessFile in) throws IOException {
+ AsfHeaderReader reader = new AsfHeaderReader();
+ return reader.parseData(in);
+ }
+
+ /**
+ * Protected default constructor. null
if no valid data found, else a Wrapper
+ * containing all supported data.
+ * @throws IOException
+ * Read errors.
+ */
+ private AsfHeader parseData(RandomAccessFile in) throws IOException {
+ AsfHeader result = null;
+ long chunkStart = in.getFilePointer();
+ GUID possibleGuid = Utils.readGUID(in);
+
+ if (GUID.GUID_HEADER.equals(possibleGuid)) {
+ // For Know the filepointer pointed to an ASF header chunk.
+ BigInteger chunkLen = Utils.readBig64(in);
+
+ long chunkCount = Utils.readUINT32(in);
+ // They are of unknown use.
+ in.skipBytes(2);
+
+ /*
+ * Now reading header of chuncks.
+ */
+ ArrayList chunks = new ArrayList();
+ while (chunkLen.compareTo(BigInteger.valueOf(in.getFilePointer())) > 0) {
+ Chunk chunk = ChunkHeaderReader.readChunckHeader(in);
+ chunks.add(chunk);
+ in.seek(chunk.getChunckEnd());
+ }
+
+ /*
+ * Creating the resulting object because streamchunks will be added.
+ */
+ result = new AsfHeader(chunkStart, chunkLen, chunkCount);
+ /*
+ * Now we know all positions and guids of chunks which are contained
+ * whithin asf header. Further we need to identify the type of those
+ * chunks and parse the interesting ones.
+ */
+ FileHeader fileHeader = null;
+ ExtendedContentDescription extendedDescription = null;
+ EncodingChunk encodingChunk = null;
+ StreamChunk streamChunk = null;
+ ContentDescription contentDescription = null;
+
+ Iterator iterator = chunks.iterator();
+ while (iterator.hasNext()) {
+ Chunk currentChunk = (Chunk) iterator.next();
+ if (fileHeader == null
+ && (fileHeader = FileHeaderReader
+ .read(in, currentChunk)) != null) {
+ continue;
+ }
+ if (extendedDescription == null
+ && (extendedDescription = ExtContentDescReader.read(in,
+ currentChunk)) != null) {
+ continue;
+ }
+ if (encodingChunk == null
+ && (encodingChunk = EncodingChunkReader.read(in,
+ currentChunk)) != null) {
+ continue;
+ }
+ if (streamChunk == null
+ && (streamChunk = StreamChunkReader.read(in,
+ currentChunk)) != null) {
+ result.addStreamChunk(streamChunk);
+ streamChunk = null;
+ continue;
+ }
+ if (contentDescription == null
+ && (contentDescription = ContentDescriptionReader.read(
+ in, currentChunk)) != null) {
+ continue;
+ }
+ /*
+ * If none of the above statements executed the "continue", this
+ * chunk couldn't be interpreted. Despite this the chunk is
+ * remembered
+ */
+ result.addUnspecifiedChunk(currentChunk);
+ }
+ /*
+ * Finally store the parsed chunks in the resulting ASFHeader
+ * object.
+ */
+ result.setFileHeader(fileHeader);
+ result.setEncodingChunk(encodingChunk);
+ /*
+ * Warning, extendedDescription and contentDescription maybe null
+ * since they are optional fields.
+ */
+ result.setExtendedContentDescription(extendedDescription);
+ result.setContentDescription(contentDescription);
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/io/ChunkHeaderReader.java b/songdbj/entagged/audioformats/asf/io/ChunkHeaderReader.java
new file mode 100644
index 0000000000..7d7c8d98a1
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/io/ChunkHeaderReader.java
@@ -0,0 +1,57 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.io;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+
+import entagged.audioformats.asf.data.Chunk;
+import entagged.audioformats.asf.data.GUID;
+import entagged.audioformats.asf.util.Utils;
+
+/**
+ * Default reader, Reads GUID and size out of an inputsream and creates a
+ * {@link entagged.audioformats.asf.data.Chunk}object.
+ *
+ * @author Christian Laireiter
+ */
+class ChunkHeaderReader {
+
+ /**
+ * Interprets current data as a header of a chunk.
+ *
+ * @param input
+ * inputdata
+ * @return Chunk.
+ * @throws IOException
+ * Access errors.
+ */
+ public static Chunk readChunckHeader(RandomAccessFile input)
+ throws IOException {
+ long pos = input.getFilePointer();
+ GUID guid = Utils.readGUID(input);
+ BigInteger chunkLength = Utils.readBig64(input);
+ return new Chunk(guid, pos, chunkLength);
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/io/ContentDescriptionReader.java b/songdbj/entagged/audioformats/asf/io/ContentDescriptionReader.java
new file mode 100644
index 0000000000..a2b120f7d1
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/io/ContentDescriptionReader.java
@@ -0,0 +1,175 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.io;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+
+import entagged.audioformats.asf.data.ContentDescription;
+import entagged.audioformats.asf.data.Chunk;
+import entagged.audioformats.asf.data.GUID;
+import entagged.audioformats.asf.util.Utils;
+
+/**
+ * Reads and interprets the data of a asf chunk containing title, author... raf
is at valid
+ * fileheader.
+ * @throws IOException
+ * Read errors.
+ */
+ public static ContentDescription read(RandomAccessFile raf, Chunk candidate)
+ throws IOException {
+ if (raf == null || candidate == null) {
+ throw new IllegalArgumentException("Arguments must not be null.");
+ }
+ if (GUID.GUID_CONTENTDESCRIPTION.equals(candidate.getGuid())) {
+ raf.seek(candidate.getPosition());
+ return new ContentDescriptionReader().parseData(raf);
+ }
+ return null;
+ }
+
+ /**
+ * This method reads a UTF-16 encoded String. null
if its not a valid encoding
+ * chunk. null
if its not a valid encoding
+ * chunk. false
) and 1 (for true
). The third one is
+ * zero, maybe indication the end of the value. raf
is at valid
+ * fileheader.
+ * @throws IOException
+ * Read errors.
+ */
+ public static FileHeader read(RandomAccessFile raf, Chunk candidate)
+ throws IOException {
+ if (raf == null || candidate == null) {
+ throw new IllegalArgumentException("Arguments must not be null.");
+ }
+ if (GUID.GUID_FILE.equals(candidate.getGuid())) {
+ raf.seek(candidate.getPosition());
+ return new FileHeaderReader().parseData(raf);
+ }
+ return null;
+ }
+
+ /**
+ * Should not be used for now.
+ *
+ */
+ protected FileHeaderReader() {
+ // NOTHING toDo
+ }
+
+ /**
+ * Tries to extract an ASF file header object out of the given input.
+ *
+ * @param raf
+ * @return null
if no valid file header object.
+ * @throws IOException
+ */
+ private FileHeader parseData(RandomAccessFile raf) throws IOException {
+ FileHeader result = null;
+ long fileHeaderStart = raf.getFilePointer();
+ GUID guid = Utils.readGUID(raf);
+ if (GUID.GUID_FILE.equals(guid)) {
+ BigInteger chunckLen = Utils.readBig64(raf);
+ // Skip client GUID.
+ raf.skipBytes(16);
+
+ BigInteger fileSize = Utils.readBig64(raf);
+ if (fileSize.intValue() != raf.length()) {
+ System.err
+ .println("Filesize of file doesn't match len of Fileheader. ("
+ + fileSize.toString() + ", file: "+raf.length()+")");
+ }
+ // fileTime in 100 ns since midnight of 1st january 1601 GMT
+ BigInteger fileTime = Utils.readBig64(raf);
+
+ BigInteger packageCount = Utils.readBig64(raf);
+
+ BigInteger timeEndPos = Utils.readBig64(raf);
+ BigInteger duration = Utils.readBig64(raf);
+ BigInteger timeStartPos = Utils.readBig64(raf);
+
+ long flags = Utils.readUINT32(raf);
+
+ long minPkgSize = Utils.readUINT32(raf);
+ long maxPkgSize = Utils.readUINT32(raf);
+ long uncompressedFrameSize = Utils.readUINT32(raf);
+
+ result = new FileHeader(fileHeaderStart, chunckLen, fileSize,
+ fileTime, packageCount, duration, timeStartPos, timeEndPos,
+ flags, minPkgSize, maxPkgSize, uncompressedFrameSize);
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/io/StreamChunkReader.java b/songdbj/entagged/audioformats/asf/io/StreamChunkReader.java
new file mode 100644
index 0000000000..97563ca320
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/io/StreamChunkReader.java
@@ -0,0 +1,187 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.io;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+
+import entagged.audioformats.asf.data.AudioStreamChunk;
+import entagged.audioformats.asf.data.Chunk;
+import entagged.audioformats.asf.data.GUID;
+import entagged.audioformats.asf.data.StreamChunk;
+import entagged.audioformats.asf.data.VideoStreamChunk;
+import entagged.audioformats.asf.util.Utils;
+
+/**
+ * Reads and interprets the data of the audio or video stream information chunk.
+ * null
if its not an audio
+ * stream object.
+ * @throws IOException
+ * read errors
+ */
+ public static StreamChunk read(RandomAccessFile raf, Chunk candidate)
+ throws IOException {
+ if (raf == null || candidate == null) {
+ throw new IllegalArgumentException("Arguments must not be null.");
+ }
+ if (GUID.GUID_STREAM.equals(candidate.getGuid())) {
+ raf.seek(candidate.getPosition());
+ return new StreamChunkReader().parseData(raf);
+ }
+ return null;
+ }
+
+ /**
+ * Reads audio and video stream information.
+ *
+ * @param raf
+ * input source.
+ * @return Audio Stream Information. null
if its not an audio
+ * stream object.
+ * @throws IOException
+ * read errors
+ */
+ private StreamChunk parseData(RandomAccessFile raf) throws IOException {
+ StreamChunk result = null;
+ long chunkStart = raf.getFilePointer();
+ GUID guid = Utils.readGUID(raf);
+ if (GUID.GUID_STREAM.equals(guid)) {
+ BigInteger chunkLength = Utils.readBig64(raf);
+ // Now comes GUID indicating whether stream content type is audio or
+ // video
+ GUID streamTypeGUID = Utils.readGUID(raf);
+ if (GUID.GUID_AUDIOSTREAM.equals(streamTypeGUID)
+ || GUID.GUID_VIDEOSTREAM.equals(streamTypeGUID)) {
+
+ // A guid is indicating whether the stream is error
+ // concealed
+ GUID errorConcealment = Utils.readGUID(raf);
+ /*
+ * Read the Time Offset
+ */
+ long timeOffset = Utils.readUINT64(raf);
+
+ long typeSpecificDataSize = Utils.readUINT32(raf);
+ long streamSpecificDataSize = Utils.readUINT32(raf);
+
+ /*
+ * Read a bitfield. (Contains streamnumber, and whether
+ * the stream content is encrypted.)
+ */
+ int mask = Utils.readUINT16(raf);
+ int streamNumber = mask & 127;
+ boolean contentEncrypted = (mask & (1 << 15)) != 0;
+
+ /*
+ * Skip a reserved field
+ */
+ raf.skipBytes(4);
+
+ if (GUID.GUID_AUDIOSTREAM.equals(streamTypeGUID)) {
+ /*
+ * Reading audio specific information
+ */
+ AudioStreamChunk audioStreamChunk = new AudioStreamChunk(
+ chunkStart, chunkLength);
+ result = audioStreamChunk;
+
+ /*
+ * read WAVEFORMATEX and format extension.
+ */
+ long compressionFormat = Utils.readUINT16(raf);
+ long channelCount = Utils.readUINT16(raf);
+ long samplingRate = Utils.readUINT32(raf);
+ long avgBytesPerSec = Utils.readUINT32(raf);
+ long blockAlignment = Utils.readUINT16(raf);
+ int bitsPerSample = Utils.readUINT16(raf);
+ int codecSpecificDataSize = Utils.readUINT16(raf);
+ byte[] codecSpecificData = new byte[codecSpecificDataSize];
+ raf.readFully(codecSpecificData);
+
+ audioStreamChunk.setCompressionFormat(compressionFormat);
+ audioStreamChunk.setChannelCount(channelCount);
+ audioStreamChunk.setSamplingRate(samplingRate);
+ audioStreamChunk.setAverageBytesPerSec(avgBytesPerSec);
+ audioStreamChunk.setErrorConcealment(errorConcealment);
+ audioStreamChunk.setBlockAlignment(blockAlignment);
+ audioStreamChunk.setBitsPerSample (bitsPerSample);
+ audioStreamChunk.setCodecData (codecSpecificData);
+ } else if (GUID.GUID_VIDEOSTREAM.equals(streamTypeGUID)) {
+ /*
+ * Reading video specific information
+ */
+ VideoStreamChunk videoStreamChunk = new VideoStreamChunk(
+ chunkStart, chunkLength);
+ result = videoStreamChunk;
+
+ long pictureWidth = Utils.readUINT32(raf);
+ long pictureHeight = Utils.readUINT32(raf);
+
+ // Skipt unknown field
+ raf.skipBytes(1);
+
+ // long bitMapInfoHeaderSize = Utils.readUINT32(raf);
+ // bitMapInfoHeaderEnd stores now the end of the
+ // bitMapInfoHeader
+ // long bitMapInfoHeaderEnd = raf.getFilePointer()
+ // + bitMapInfoHeaderSize;
+
+ videoStreamChunk.setPictureWidth(pictureWidth);
+ videoStreamChunk.setPictureHeight(pictureHeight);
+ }
+
+ /*
+ * Setting common values for audio and video
+ */
+ result.setStreamNumber(streamNumber);
+ result.setStreamSpecificDataSize(streamSpecificDataSize);
+ result.setTypeSpecificDataSize(typeSpecificDataSize);
+ result.setTimeOffset(timeOffset);
+ result.setContentEncrypted(contentEncrypted);
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/songdbj/entagged/audioformats/asf/util/ChunkPositionComparator.java b/songdbj/entagged/audioformats/asf/util/ChunkPositionComparator.java
new file mode 100644
index 0000000000..864b6cb432
--- /dev/null
+++ b/songdbj/entagged/audioformats/asf/util/ChunkPositionComparator.java
@@ -0,0 +1,52 @@
+/*
+ * ******************************************************************** **
+ * Copyright notice **
+ * ** **
+ * (c) 2003 Entagged Developpement Team **
+ * http://www.sourceforge.net/projects/entagged **
+ * ** **
+ * All rights reserved **
+ * ** **
+ * This script is part of the Entagged project. The Entagged **
+ * project is free software; you can redistribute it and/or modify **
+ * it under the terms of the GNU General Public License as published by **
+ * the Free Software Foundation; either version 2 of the License, or **
+ * (at your option) any later version. **
+ * ** **
+ * The GNU General Public License can be found at **
+ * http://www.gnu.org/copyleft/gpl.html. **
+ * ** **
+ * This copyright notice MUST APPEAR in all copies of the file! **
+ * ********************************************************************
+ */
+package entagged.audioformats.asf.util;
+
+import java.util.Comparator;
+
+import entagged.audioformats.asf.data.Chunk;
+
+/**
+ * This class is needed for ordering all types of
+ * {@link entagged.audioformats.asf.data.Chunk}s ascending by their Position.
+ * tag
which are defined to
+ * be common by entagged. tag
.tag
.
+ */
+ public static ContentDescription createContentDescription(Tag tag) {
+ ContentDescription result = new ContentDescription();
+ result.setAuthor(tag.getFirstArtist());
+ result.setTitle(tag.getFirstTitle());
+ result.setComment(tag.getFirstComment());
+ return result;
+ }
+
+ /**
+ * This method creates a new {@link ExtendedContentDescription}object
+ * filled with the values of the given tag
.raf
until chars
is
+ * filled.
+ *
+ * @param chars
+ * to be filled
+ * @param raf
+ * to be read
+ * @throws IOException
+ * read error, or file at end before chars
is
+ * filled.
+ */
+ public static void fillChars(char[] chars, RandomAccessFile raf)
+ throws IOException {
+ if (chars == null) {
+ throw new IllegalArgumentException("Argument must not be null.");
+ }
+ for (int i = 0; i < chars.length; i++) {
+ chars[i] = raf.readChar();
+ }
+ }
+
+ /**
+ * This method will create a byte[] at the size of byteCount
+ * and insert the bytes of value
(starting from lowset byte)
+ * into it. byteCount
containing the
+ * lower byte values of value
.
+ */
+ public static byte[] getBytes(long value, int byteCount) {
+ byte[] result = new byte[byteCount];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (byte) (value & 0xFF);
+ value >>>= 8;
+ }
+ return result;
+ }
+
+ /**
+ * Since date values in asf files are given in 100 ns steps since first
+ * january of 1601 a little conversion must be done. raf
and creates an
+ * unsigned value of it. raf
must be at the number of
+ * characters. This number contains the terminating zero character (UINT16).
+ *
+ * @param raf
+ * Input source
+ * @return String
+ * @throws IOException
+ * read errors
+ */
+ public static String readCharacterSizedString(RandomAccessFile raf)
+ throws IOException {
+ StringBuffer result = new StringBuffer();
+ int strLen = readUINT16(raf);
+ int character = raf.read();
+ character |= raf.read() << 8;
+ do {
+ if (character != 0) {
+ result.append((char) character);
+ character = raf.read();
+ character |= raf.read() << 8;
+ }
+ } while (character != 0 || (result.length() + 1) > strLen);
+ if (strLen != (result.length() + 1)) {
+ throw new IllegalStateException(
+ "Invalid Data for current interpretation");
+ }
+ return result.toString();
+ }
+
+ /**
+ * This Method reads a GUID (which is a 16 byte long sequence) from the
+ * given raf
and creates a wrapper. This class stores all the meta-data (artist, album, ..) contained in an AudioFile.
+ *All the set
operation are not "real-time", they only apply to this object. To commit the modification, either the AudioFile
's commit()
or the AudioFileIO.write(AudioFile)
have to be called
There are two types of fields contained in a Tag
. The most commonly used (the common fields) are accessible through get/set methods, like the artist, tha album, the track number, etc.
Each tag format has then specific fields (eg. graphics, urls, etc) that do not exist in the other formats, or that cannot be mapped to common fields
+ *To retreive the content of these specific fields, the getSpecificields()
can be called giving the names of the specific fields stored in this tag
This ensures that all the old meta-data contained in a tag read from AudioFileIO
is kept and rewritten when the tag is updated
Creates a new empty tag, all the common fields are initialized to the empty string, and no specific fields exists.
+ */ + public OldTag() { + fields = new Hashtable(); + fields.put("TITLE" ,""); + fields.put("ALBUM" ,""); + fields.put("ARTIST" ,""); + fields.put("GENRE" ,""); + fields.put("TRACK" ,""); + fields.put("YEAR" ,""); + fields.put("COMMENT",""); + } + + /** + *Returns the title of this song, eg: "Stairway to Heaven".
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the title of this song + */ + public String getTitle() { + return (String) fields.get("TITLE"); + } + + /** + *Returns the album of this song, eg: "Led Zeppelin IV".
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the album of this song + */ + public String getAlbum() { + return (String) fields.get("ALBUM"); + } + + /** + *Returns the artist of this song, eg: "Led Zeppelin".
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the artist of this song + */ + public String getArtist() { + return (String) fields.get("ARTIST"); + } + + /** + *Returns the genre of this song, eg: "Classic Rock".
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the genre of this song + */ + public String getGenre() { + return (String) fields.get("GENRE"); + } + + /** + *Returns the track number of this song.
+ *The String can be anything, so don't expect a simple number, it could be "10" but also "10 / 15"
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the track number of this song + */ + public String getTrack() { + return (String) fields.get("TRACK"); + } + + /** + *Returns the date of this song.
+ *Most of the time the year of release of the album eg: "1970", but this can also be an arbitrary string like "19 december 1934"
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the date of this song + */ + public String getYear() { + return (String) fields.get("YEAR"); + } + + /** + *Returns the comment associated with this song, eg: "Recorded live at the Royal Albert Hall".
+ *Some tag format allow multi-line comments, line are separated with "\n" strings ("\\n" in java not "\n"). That means the String returned may have to be parsed to transform those strings in newline characters ("\n" in java)
+ *If there wasn't such a field in the AudioFile, an empty String is returned
+ * + *@return Returns the comment associated with this song + *@todo Parse the returned string to replace \\n by \n for multiline comments + */ + public String getComment() { + return (String) fields.get("COMMENT"); + } + + /** + *Sets the title for this song, replacing the previous one.
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new title of this song + */ + public void setTitle(String s) { + if(s == null) + fields.put("TITLE",""); + else + fields.put("TITLE",s); + } + + /** + *Sets the new album for this song, replacing the previous one.
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new album for this song + */ + public void setAlbum(String s) { + if(s == null) + fields.put("ALBUM",""); + else + fields.put("ALBUM",s); + } + + /** + *Sets the new artist for this song, replacing the previous one.
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new artist for this song + */ + public void setArtist(String s) { + if(s == null) + fields.put("ARTIST",""); + else + fields.put("ARTIST",s); + } + + /** + *Sets the new genre for this song, replacing the previous one.
+ *Some formats does not support an arbitrary string as genre and use a predefined list of genres, with an index. If this is the case, the given genre will be matched against a predefined list, if a correspondance is found, that index is used, if not, a "no genre" or equivalent will be used instead
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new genre for this song + */ + public void setGenre(String s) { + if(s == null) + fields.put("GENRE",""); + else + fields.put("GENRE",s); + } + + /** + *Sets the new track number for this song, replacing the previous one.
+ *Some formats use a single byte track encoding, allowing tracks to be only a number from 0 to 255. If this is the case, and the track number is not a simple number (like "19/30"), the track number is set to 0
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new track number for this song + */ + public void setTrack(String s) { + if(s == null) + fields.put("TRACK",""); + else + fields.put("TRACK",s); + } + + /** + *Sets the new date for this song, replacing the previous one.
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new date for this song + */ + public void setYear(String s) { + if(s == null) + fields.put("YEAR",""); + else + fields.put("YEAR",s); + } + + /** + *Sets the new comment of this song, replacing the previous one.
+ *Some tag allow multi-line formats, using the "\n" string ("\\n" in java) but not the newline character ("\n" in java)
+ *s
can be an arbitrary string, but keep in mind that some tag formats have limited space to store informations, if s
is too long, it will be cut at the maximum length allowed by the tag format
If a null string is passed, it is converted to an empty String
+ * + *@param s the new comment for this song + *@todo Parse the given string to replace \n by \\n for multiline comments + */ + public void setComment(String s) { + if(s == null) + fields.put("COMMENT",""); + else + fields.put("COMMENT",s); + } + + + /** + *This creates an iterator over the specific fields for this tag. Specific fields are tag-format specific fields (other than artist, album, etc).
+ *If this tag was created by new Tag()
, the iterator will be empty
To retreive the value associated to a specific field use the getSpecificField(String)
method in this class
Example: an ogg file can contain an arbitrary number of arbitrary fields, so a field like MYCUSTOMFIELD cannot be mapped to artist, album, or other common field, it is instead stored as a specific field that can be retreived using this iterator
+ *@return an iterator over the specific fields of this tag + */ + public Iterator getSpecificFields() { + //Those fileds are created by subclasses of this class + //And begin with a "-", the iterator contains the fields without the leading "-" + List l = new LinkedList(); + Enumeration en = fields.keys(); + while(en.hasMoreElements()) { + String key = (String) en.nextElement(); + if( key.startsWith("-") ) + l.add(key.substring(1)); + } + + return l.iterator(); + } + + /** + *Returns the specific field's content.
+ *if s
is not a valid specific field, the empty string is returned
Valid specific fields are returned by the iterator created by getSpecificFields()
method
Note that some specific fields can have binary value (not a human readable string), the binary data can be obtained back using the getBytes()
method on the String
Checks wether all the common fields (artist, genre, ..) are empty, that is: contains the empty string or only spaces.
+ *Specific fields can exist even if this returns true, use the isSpecificEmpty()
to check if the specific fields are also empty
Checks wether all the specific fields (other than artist, genre, ..) are empty, that is: contains the empty string or only spaces.
+ *Common fields can exist even if this returns true, use the isEmpty()
to check if the common fields are also empty
Returns a multi-line string showing all the meta-data of this tag (the common fields, and any specific field).
+ *Specific fields are denoted with a "-" appended to the field name
+ * + *@return the contents of this tag + */ + public String toString() { + StringBuffer out = new StringBuffer(50); + out.append("Tag content:\n"); + Enumeration en = fields.keys(); + while(en.hasMoreElements()) { + Object field = en.nextElement(); + Object content = fields.get(field); + out.append("\t"); + out.append(field); + out.append(" : "); + out.append(content); + out.append("\n"); + } + return out.toString().substring(0,out.length()-1); + } +} diff --git a/songdbj/entagged/audioformats/generic/TagField.java b/songdbj/entagged/audioformats/generic/TagField.java new file mode 100644 index 0000000000..435bec8b19 --- /dev/null +++ b/songdbj/entagged/audioformats/generic/TagField.java @@ -0,0 +1,43 @@ +/* + * ******************************************************************** ** + * Copyright notice ** + * ** ** + * (c) 2003 Entagged Developpement Team ** + * http://www.sourceforge.net/projects/entagged ** + * ** ** + * All rights reserved ** + * ** ** + * This script is part of the Entagged project. The Entagged ** + * project is free software; you can redistribute it and/or modify ** + * it under the terms of the GNU General Public License as published by ** + * the Free Software Foundation; either version 2 of the License, or ** + * (at your option) any later version. ** + * ** ** + * The GNU General Public License can be found at ** + * http://www.gnu.org/copyleft/gpl.html. ** + * ** ** + * This copyright notice MUST APPEAR in all copies of the file! ** + * ******************************************************************** + */ +package entagged.audioformats.generic; + +import java.io.UnsupportedEncodingException; + +public interface TagField { + + public String getId(); + + public byte[] getRawContent() throws UnsupportedEncodingException; + + public boolean isBinary(); + + public void isBinary(boolean b); + + public boolean isCommon(); + + public boolean isEmpty(); + + public String toString(); + + public void copyContent(TagField field); +} \ No newline at end of file diff --git a/songdbj/entagged/audioformats/generic/TagTextField.java b/songdbj/entagged/audioformats/generic/TagTextField.java new file mode 100644 index 0000000000..b54f8c96ca --- /dev/null +++ b/songdbj/entagged/audioformats/generic/TagTextField.java @@ -0,0 +1,33 @@ +/* + * ******************************************************************** ** + * Copyright notice ** + * ** ** + * (c) 2003 Entagged Developpement Team ** + * http://www.sourceforge.net/projects/entagged ** + * ** ** + * All rights reserved ** + * ** ** + * This script is part of the Entagged project. The Entagged ** + * project is free software; you can redistribute it and/or modify ** + * it under the terms of the GNU General Public License as published by ** + * the Free Software Foundation; either version 2 of the License, or ** + * (at your option) any later version. ** + * ** ** + * The GNU General Public License can be found at ** + * http://www.gnu.org/copyleft/gpl.html. ** + * ** ** + * This copyright notice MUST APPEAR in all copies of the file! ** + * ******************************************************************** + */ +package entagged.audioformats.generic; + +public interface TagTextField extends TagField { + + public String getContent(); + + public String getEncoding(); + + public void setContent(String s); + + public void setEncoding(String s); +} \ No newline at end of file diff --git a/songdbj/entagged/audioformats/generic/Utils.java b/songdbj/entagged/audioformats/generic/Utils.java new file mode 100644 index 0000000000..dd2ccd06db --- /dev/null +++ b/songdbj/entagged/audioformats/generic/Utils.java @@ -0,0 +1,95 @@ +/* + * ******************************************************************** ** + * Copyright notice ** + * ** ** + * (c) 2003 Entagged Developpement Team ** + * http://www.sourceforge.net/projects/entagged ** + * ** ** + * All rights reserved ** + * ** ** + * This script is part of the Entagged project. The Entagged ** + * project is free software; you can redistribute it and/or modify ** + * it under the terms of the GNU General Public License as published by ** + * the Free Software Foundation; either version 2 of the License, or ** + * (at your option) any later version. ** + * ** ** + * The GNU General Public License can be found at ** + * http://www.gnu.org/copyleft/gpl.html. ** + * ** ** + * This copyright notice MUST APPEAR in all copies of the file! ** + * ******************************************************************** + */ +package entagged.audioformats.generic; + +import java.io.*; + +/* + * Contains various frequently used static functions in the different tag formats + * + *@author Raphael Slinckx + *@version $Id$ + *@since v0.02 + */ +public class Utils { + /* + * Returns the extension of the given file. + * The extension is empty if there is no extension + * The extension is the string after the last "." + * + * @param f The file whose extension is requested + * @return The extension of the given file + */ + public static String getExtension(File f) { + String name = f.getName().toLowerCase(); + int i = name.lastIndexOf( "." ); + if(i == -1) + return ""; + + return name.substring( i + 1 ); + } + + /* + * Tries to convert a string into an UTF8 array of bytes + * If the conversion fails, return the string converted with the default + * encoding. + * + * @param s The string to convert + * @return The byte array representation of this string in UTF8 encoding + */ + public static byte[] getUTF8Bytes(String s) throws UnsupportedEncodingException { + return s.getBytes("UTF-8"); + } + + /* + * Computes a number composed of (end-start) bytes in the b array. + * + * @param b The byte array + * @param start The starting offset in b (b[offset]). The less significant byte + * @param end The end index (included) in b (b[end]). The most significant byte + * @return a long number represented by the byte sequence. + */ + public static long getLongNumber(byte[] b, int start, int end) { + long number = 0; + for(int i = 0; i<(end-start+1); i++) { + number += ((b[start+i]&0xFF) << i*8); + } + + return number; + } + + /* + * same as above, but returns an int instead of a long + * @param b The byte array + * @param start The starting offset in b (b[offset]). The less significant byte + * @param end The end index (included) in b (b[end]). The most significant byte + * @return a int number represented by the byte sequence. + */ + public static int getNumber( byte[] b, int start, int end) { + int number = 0; + for(int i = 0; i<(end-start+1); i++) { + number += ((b[start+i]&0xFF) << i*8); + } + + return number; + } +} diff --git a/songdbj/javazoom/jl/converter/Converter.java b/songdbj/javazoom/jl/converter/Converter.java new file mode 100644 index 0000000000..845082e626 --- /dev/null +++ b/songdbj/javazoom/jl/converter/Converter.java @@ -0,0 +1,411 @@ +/* + * 11/19/04 1.0 moved to LGPL. + * 12/12/99 Original verion. mdm@techie.com. + *----------------------------------------------------------------------- + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + *---------------------------------------------------------------------- + */ + +package javazoom.jl.converter; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import javazoom.jl.decoder.Bitstream; +import javazoom.jl.decoder.Decoder; +import javazoom.jl.decoder.Header; +import javazoom.jl.decoder.JavaLayerException; +import javazoom.jl.decoder.Obuffer; + +/** + * TheConverter
class implements the conversion of
+ * an MPEG audio file to a .WAV file. To convert an MPEG audio stream,
+ * just create an instance of this class and call the convert()
+ * method, passing in the names of the input and output files. You can
+ * pass in optional ProgressListener
and
+ * Decoder.Params
objects also to customize the conversion.
+ *
+ * @author MDM 12/12/99
+ * @since 0.0.7
+ */
+public class Converter
+{
+ /**
+ * Creates a new converter instance.
+ */
+ public Converter()
+ {
+ }
+
+ public synchronized void convert(String sourceName, String destName)
+ throws JavaLayerException
+ {
+ convert(sourceName, destName, null, null);
+ }
+
+ public synchronized void convert(String sourceName, String destName,
+ ProgressListener progressListener)
+ throws JavaLayerException
+ {
+ convert(sourceName, destName, progressListener, null);
+ }
+
+
+ public void convert(String sourceName, String destName,
+ ProgressListener progressListener, Decoder.Params decoderParams)
+ throws JavaLayerException
+ {
+ if (destName.length()==0)
+ destName = null;
+ try {
+ InputStream in = openInput(sourceName);
+ convert(in, destName, progressListener, decoderParams);
+ in.close();
+ } catch(IOException ioe) {
+ throw new JavaLayerException(ioe.getLocalizedMessage(), ioe);
+ }
+ }
+
+ public synchronized void convert(InputStream sourceStream, String destName,
+ ProgressListener progressListener, Decoder.Params decoderParams)
+ throws JavaLayerException
+ {
+ if (progressListener==null)
+ progressListener = PrintWriterProgressListener.newStdOut(
+ PrintWriterProgressListener.NO_DETAIL);
+ try {
+ if (!(sourceStream instanceof BufferedInputStream))
+ sourceStream = new BufferedInputStream(sourceStream);
+ int frameCount = -1;
+ if (sourceStream.markSupported()) {
+ sourceStream.mark(-1);
+ frameCount = countFrames(sourceStream);
+ sourceStream.reset();
+ }
+ progressListener.converterUpdate(ProgressListener.UPDATE_FRAME_COUNT, frameCount, 0);
+
+
+ Obuffer output = null;
+ Decoder decoder = new Decoder(decoderParams);
+ Bitstream stream = new Bitstream(sourceStream);
+
+ if (frameCount==-1)
+ frameCount = Integer.MAX_VALUE;
+
+ int frame = 0;
+ long startTime = System.currentTimeMillis();
+
+ try
+ {
+ for (; frameThrowable
instance that
+ * was thrown.
+ *
+ * @return true
to continue processing, or false
+ * to abort conversion.
+ *
+ * If this method returns false
, the exception
+ * is propagated to the caller of the convert() method. If
+ * true
is returned, the exception is silently
+ * ignored and the converter moves onto the next frame.
+ */
+ public boolean converterException(Throwable t);
+
+ }
+
+
+ /**
+ * Implementation of ProgressListener
that writes
+ * notification text to a PrintWriter
.
+ */
+ // REVIEW: i18n of text and order required.
+ static public class PrintWriterProgressListener implements ProgressListener
+ {
+ static public final int NO_DETAIL = 0;
+
+ /**
+ * Level of detail typically expected of expert
+ * users.
+ */
+ static public final int EXPERT_DETAIL = 1;
+
+ /**
+ * Verbose detail.
+ */
+ static public final int VERBOSE_DETAIL = 2;
+
+ /**
+ * Debug detail. All frame read notifications are shown.
+ */
+ static public final int DEBUG_DETAIL = 7;
+
+ static public final int MAX_DETAIL = 10;
+
+ private PrintWriter pw;
+
+ private int detailLevel;
+
+ static public PrintWriterProgressListener newStdOut(int detail)
+ {
+ return new PrintWriterProgressListener(
+ new PrintWriter(System.out, true), detail);
+ }
+
+ public PrintWriterProgressListener(PrintWriter writer, int detailLevel)
+ {
+ this.pw = writer;
+ this.detailLevel = detailLevel;
+ }
+
+
+ public boolean isDetail(int detail)
+ {
+ return (this.detailLevel >= detail);
+ }
+
+ public void converterUpdate(int updateID, int param1, int param2)
+ {
+ if (isDetail(VERBOSE_DETAIL))
+ {
+ switch (updateID)
+ {
+ case UPDATE_CONVERT_COMPLETE:
+ // catch divide by zero errors.
+ if (param2==0)
+ param2 = 1;
+
+ pw.println();
+ pw.println("Converted "+param2+" frames in "+param1+" ms ("+
+ (param1/param2)+" ms per frame.)");
+ }
+ }
+ }
+
+ public void parsedFrame(int frameNo, Header header)
+ {
+ if ((frameNo==0) && isDetail(VERBOSE_DETAIL))
+ {
+ String headerString = header.toString();
+ pw.println("File is a "+headerString);
+ }
+ else if (isDetail(MAX_DETAIL))
+ {
+ String headerString = header.toString();
+ pw.println("Prased frame "+frameNo+": "+headerString);
+ }
+ }
+
+ public void readFrame(int frameNo, Header header)
+ {
+ if ((frameNo==0) && isDetail(VERBOSE_DETAIL))
+ {
+ String headerString = header.toString();
+ pw.println("File is a "+headerString);
+ }
+ else if (isDetail(MAX_DETAIL))
+ {
+ String headerString = header.toString();
+ pw.println("Read frame "+frameNo+": "+headerString);
+ }
+ }
+
+ public void decodedFrame(int frameNo, Header header, Obuffer o)
+ {
+ if (isDetail(MAX_DETAIL))
+ {
+ String headerString = header.toString();
+ pw.println("Decoded frame "+frameNo+": "+headerString);
+ pw.println("Output: "+o);
+ }
+ else if (isDetail(VERBOSE_DETAIL))
+ {
+ if (frameNo==0)
+ {
+ pw.print("Converting.");
+ pw.flush();
+ }
+
+ if ((frameNo % 10)==0)
+ {
+ pw.print('.');
+ pw.flush();
+ }
+ }
+ }
+
+ public boolean converterException(Throwable t)
+ {
+ if (this.detailLevel>NO_DETAIL)
+ {
+ t.printStackTrace(pw);
+ pw.flush();
+ }
+ return false;
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/songdbj/javazoom/jl/converter/RiffFile.java b/songdbj/javazoom/jl/converter/RiffFile.java
new file mode 100644
index 0000000000..fb5d9e53c6
--- /dev/null
+++ b/songdbj/javazoom/jl/converter/RiffFile.java
@@ -0,0 +1,495 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 02/23/99 JavaConversion by E.B
+ * Don Cross, April 1993.
+ * RIFF file format classes.
+ * See Chapter 8 of "Multimedia Programmer's Reference" in
+ * the Microsoft Windows SDK.
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.converter;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+/**
+ * Class to manage RIFF files
+ */
+public class RiffFile
+{
+ class RiffChunkHeader
+ {
+ public int ckID = 0; // Four-character chunk ID
+ public int ckSize = 0; // Length of data in chunk
+ public RiffChunkHeader()
+ {}
+ }
+
+
+ // DDCRET
+ public static final int DDC_SUCCESS = 0; // The operation succeded
+ public static final int DDC_FAILURE = 1; // The operation failed for unspecified reasons
+ public static final int DDC_OUT_OF_MEMORY = 2; // Operation failed due to running out of memory
+ public static final int DDC_FILE_ERROR = 3; // Operation encountered file I/O error
+ public static final int DDC_INVALID_CALL = 4; // Operation was called with invalid parameters
+ public static final int DDC_USER_ABORT = 5; // Operation was aborted by the user
+ public static final int DDC_INVALID_FILE = 6; // File format does not match
+
+ // RiffFileMode
+ public static final int RFM_UNKNOWN = 0; // undefined type (can use to mean "N/A" or "not open")
+ public static final int RFM_WRITE = 1; // open for write
+ public static final int RFM_READ = 2; // open for read
+
+ private RiffChunkHeader riff_header; // header for whole file
+ protected int fmode; // current file I/O mode
+ protected RandomAccessFile file; // I/O stream to use
+
+ /**
+ * Dummy Constructor
+ */
+ public RiffFile()
+ {
+ file = null;
+ fmode = RFM_UNKNOWN;
+ riff_header = new RiffChunkHeader();
+
+ riff_header.ckID = FourCC("RIFF");
+ riff_header.ckSize = 0;
+ }
+
+ /**
+ * Return File Mode.
+ */
+ public int CurrentFileMode()
+ {return fmode;}
+
+ /**
+ * Open a RIFF file.
+ */
+ public int Open(String Filename, int NewMode)
+ {
+ int retcode = DDC_SUCCESS;
+
+ if ( fmode != RFM_UNKNOWN )
+ {
+ retcode = Close();
+ }
+
+ if ( retcode == DDC_SUCCESS )
+ {
+ switch ( NewMode )
+ {
+ case RFM_WRITE:
+ try
+ {
+ file = new RandomAccessFile(Filename,"rw");
+
+ try
+ {
+ // Write the RIFF header...
+ // We will have to come back later and patch it!
+ byte[] br = new byte[8];
+ br[0] = (byte) ((riff_header.ckID >>> 24) & 0x000000FF);
+ br[1] = (byte) ((riff_header.ckID >>> 16) & 0x000000FF);
+ br[2] = (byte) ((riff_header.ckID >>> 8) & 0x000000FF);
+ br[3] = (byte) (riff_header.ckID & 0x000000FF);
+
+ byte br4 = (byte) ((riff_header.ckSize >>> 24)& 0x000000FF);
+ byte br5 = (byte) ((riff_header.ckSize >>> 16)& 0x000000FF);
+ byte br6 = (byte) ((riff_header.ckSize >>> 8)& 0x000000FF);
+ byte br7 = (byte) (riff_header.ckSize & 0x000000FF);
+
+ br[4] = br7;
+ br[5] = br6;
+ br[6] = br5;
+ br[7] = br4;
+
+ file.write(br,0,8);
+ fmode = RFM_WRITE;
+ } catch (IOException ioe)
+ {
+ file.close();
+ fmode = RFM_UNKNOWN;
+ }
+ } catch (IOException ioe)
+ {
+ fmode = RFM_UNKNOWN;
+ retcode = DDC_FILE_ERROR;
+ }
+ break;
+
+ case RFM_READ:
+ try
+ {
+ file = new RandomAccessFile(Filename,"r");
+ try
+ {
+ // Try to read the RIFF header...
+ byte[] br = new byte[8];
+ file.read(br,0,8);
+ fmode = RFM_READ;
+ riff_header.ckID = ((br[0]<<24)& 0xFF000000) | ((br[1]<<16)&0x00FF0000) | ((br[2]<<8)&0x0000FF00) | (br[3]&0x000000FF);
+ riff_header.ckSize = ((br[4]<<24)& 0xFF000000) | ((br[5]<<16)&0x00FF0000) | ((br[6]<<8)&0x0000FF00) | (br[7]&0x000000FF);
+ } catch (IOException ioe)
+ {
+ file.close();
+ fmode = RFM_UNKNOWN;
+ }
+ } catch (IOException ioe)
+ {
+ fmode = RFM_UNKNOWN;
+ retcode = DDC_FILE_ERROR;
+ }
+ break;
+ default:
+ retcode = DDC_INVALID_CALL;
+ }
+ }
+ return retcode;
+ }
+
+ /**
+ * Write NumBytes data.
+ */
+ public int Write(byte[] Data, int NumBytes )
+ {
+ if ( fmode != RFM_WRITE )
+ {
+ return DDC_INVALID_CALL;
+ }
+ try
+ {
+ file.write(Data,0,NumBytes);
+ fmode = RFM_WRITE;
+ }
+ catch (IOException ioe)
+ {
+ return DDC_FILE_ERROR;
+ }
+ riff_header.ckSize += NumBytes;
+ return DDC_SUCCESS;
+ }
+
+
+
+ /**
+ * Write NumBytes data.
+ */
+ public int Write(short[] Data, int NumBytes )
+ {
+ byte[] theData = new byte[NumBytes];
+ int yc = 0;
+ for (int y = 0;yjlc
class presents the JavaLayer
+ * Conversion functionality as a command-line program.
+ *
+ * @since 0.0.7
+ */
+public class jlc
+{
+
+ static public void main(String args[])
+ {
+ String[] argv;
+ long start = System.currentTimeMillis();
+ int argc = args.length + 1;
+ argv = new String[argc];
+ argv[0] = "jlc";
+ for(int i=0;i+ * The implementation stores single bits as a word in the buffer. If + * a bit is set, the corresponding word in the buffer will be non-zero. + * If a bit is clear, the corresponding word is zero. Although this + * may seem waseful, this can be a factor of two quicker than + * packing 8 bits to a byte and extracting. + *
+ */
+
+// REVIEW: there is no range checking, so buffer underflow or overflow
+// can silently occur.
+final class BitReserve
+{
+ /**
+ * Size of the internal buffer to store the reserved bits.
+ * Must be a power of 2. And x8, as each bit is stored as a single
+ * entry.
+ */
+ private static final int BUFSIZE = 4096*8;
+
+ /**
+ * Mask that can be used to quickly implement the
+ * modulus operation on BUFSIZE.
+ */
+ private static final int BUFSIZE_MASK = BUFSIZE-1;
+
+ private int offset, totbit, buf_byte_idx;
+ private final int[] buf = new int[BUFSIZE];
+ private int buf_bit_idx;
+
+ BitReserve()
+ {
+
+ offset = 0;
+ totbit = 0;
+ buf_byte_idx = 0;
+ }
+
+
+ /**
+ * Return totbit Field.
+ */
+ public int hsstell()
+ {
+ return(totbit);
+ }
+
+ /**
+ * Read a number bits from the bit stream.
+ * @param N the number of
+ */
+ public int hgetbits(int N)
+ {
+ totbit += N;
+
+ int val = 0;
+
+ int pos = buf_byte_idx;
+ if (pos+N < BUFSIZE)
+ {
+ while (N-- > 0)
+ {
+ val <<= 1;
+ val |= ((buf[pos++]!=0) ? 1 : 0);
+ }
+ }
+ else
+ {
+ while (N-- > 0)
+ {
+ val <<= 1;
+ val |= ((buf[pos]!=0) ? 1 : 0);
+ pos = (pos+1) & BUFSIZE_MASK;
+ }
+ }
+ buf_byte_idx = pos;
+ return val;
+ }
+
+
+
+ /**
+ * Read 1 bit from the bit stream.
+ */
+/*
+ public int hget1bit_old()
+ {
+ int val;
+ totbit++;
+ if (buf_bit_idx == 0)
+ {
+ buf_bit_idx = 8;
+ buf_byte_idx++;
+ }
+ // BUFSIZE = 4096 = 2^12, so
+ // buf_byte_idx%BUFSIZE == buf_byte_idx & 0xfff
+ val = buf[buf_byte_idx & BUFSIZE_MASK] & putmask[buf_bit_idx];
+ buf_bit_idx--;
+ val = val >>> buf_bit_idx;
+ return val;
+ }
+ */
+ /**
+ * Returns next bit from reserve.
+ * @returns 0 if next bit is reset, or 1 if next bit is set.
+ */
+ public int hget1bit()
+ {
+ totbit++;
+ int val = buf[buf_byte_idx];
+ buf_byte_idx = (buf_byte_idx+1) & BUFSIZE_MASK;
+ return val;
+ }
+
+ /**
+ * Retrieves bits from the reserve.
+ */
+/*
+ public int readBits(int[] out, int len)
+ {
+ if (buf_bit_idx == 0)
+ {
+ buf_bit_idx = 8;
+ buf_byte_idx++;
+ current = buf[buf_byte_idx & BUFSIZE_MASK];
+ }
+
+
+
+ // save total number of bits returned
+ len = buf_bit_idx;
+ buf_bit_idx = 0;
+
+ int b = current;
+ int count = len-1;
+
+ while (count >= 0)
+ {
+ out[count--] = (b & 0x1);
+ b >>>= 1;
+ }
+
+ totbit += len;
+ return len;
+ }
+ */
+
+ /**
+ * Write 8 bits into the bit stream.
+ */
+ public void hputbuf(int val)
+ {
+ int ofs = offset;
+ buf[ofs++] = val & 0x80;
+ buf[ofs++] = val & 0x40;
+ buf[ofs++] = val & 0x20;
+ buf[ofs++] = val & 0x10;
+ buf[ofs++] = val & 0x08;
+ buf[ofs++] = val & 0x04;
+ buf[ofs++] = val & 0x02;
+ buf[ofs++] = val & 0x01;
+
+ if (ofs==BUFSIZE)
+ offset = 0;
+ else
+ offset = ofs;
+
+ }
+
+ /**
+ * Rewind N bits in Stream.
+ */
+ public void rewindNbits(int N)
+ {
+ totbit -= N;
+ buf_byte_idx -= N;
+ if (buf_byte_idx<0)
+ buf_byte_idx += BUFSIZE;
+ }
+
+ /**
+ * Rewind N bytes in Stream.
+ */
+ public void rewindNbytes(int N)
+ {
+ int bits = (N << 3);
+ totbit -= bits;
+ buf_byte_idx -= bits;
+ if (buf_byte_idx<0)
+ buf_byte_idx += BUFSIZE;
+ }
+}
diff --git a/songdbj/javazoom/jl/decoder/Bitstream.java b/songdbj/javazoom/jl/decoder/Bitstream.java
new file mode 100644
index 0000000000..cebbd5b03b
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/Bitstream.java
@@ -0,0 +1,655 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net
+ *
+ * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net
+ *
+ * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,
+ * Temporary removed seek functionality. mdm@techie.com
+ *
+ * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
+ *
+ * 04/14/97 : Added function prototypes for new syncing and seeking
+ * mechanisms. Also made this file portable. Changes made by Jeff Tsay
+ *
+ * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
+ * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
+ * @(#) Berlin University of Technology
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+
+/**
+ * The
+ * The exception provides details of the exception condition
+ * in two ways:
+ * Bistream
class is responsible for parsing
+ * an MPEG audio bitstream.
+ *
+ * REVIEW: much of the parsing currently occurs in the
+ * various decoders. This should be moved into this class and associated
+ * inner classes.
+ */
+public final class Bitstream implements BitstreamErrors
+{
+ /**
+ * Synchronization control constant for the initial
+ * synchronization to the start of a frame.
+ */
+ static byte INITIAL_SYNC = 0;
+
+ /**
+ * Synchronization control constant for non-initial frame
+ * synchronizations.
+ */
+ static byte STRICT_SYNC = 1;
+
+ // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
+ /**
+ * Maximum size of the frame buffer.
+ */
+ private static final int BUFFER_INT_SIZE = 433;
+
+ /**
+ * The frame buffer that holds the data for the current frame.
+ */
+ private final int[] framebuffer = new int[BUFFER_INT_SIZE];
+
+ /**
+ * Number of valid bytes in the frame buffer.
+ */
+ private int framesize;
+
+ /**
+ * The bytes read from the stream.
+ */
+ private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];
+
+ /**
+ * Index into framebuffer
where the next bits are
+ * retrieved.
+ */
+ private int wordpointer;
+
+ /**
+ * Number (0-31, from MSB to LSB) of next bit for get_bits()
+ */
+ private int bitindex;
+
+ /**
+ * The current specified syncword
+ */
+ private int syncword;
+
+ /**
+ * Audio header position in stream.
+ */
+ private int header_pos = 0;
+
+ /**
+ *
+ */
+ private boolean single_ch_mode;
+ //private int current_frame_number;
+ //private int last_frame_number;
+
+ private final int bitmask[] = {0, // dummy
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000F,
+ 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
+ 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
+ 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
+ 0x0001FFFF };
+
+ private final PushbackInputStream source;
+
+ private final Header header = new Header();
+
+ private final byte syncbuf[] = new byte[4];
+
+ private Crc16[] crc = new Crc16[1];
+
+ private byte[] rawid3v2 = null;
+
+ private boolean firstframe = true;
+
+
+ /**
+ * Construct a IBitstream that reads data from a
+ * given InputStream.
+ *
+ * @param in The InputStream to read from.
+ */
+ public Bitstream(InputStream in)
+ {
+ if (in==null) throw new NullPointerException("in");
+ in = new BufferedInputStream(in);
+ loadID3v2(in);
+ firstframe = true;
+ //source = new PushbackInputStream(in, 1024);
+ source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);
+
+ closeFrame();
+ //current_frame_number = -1;
+ //last_frame_number = -1;
+ }
+
+ /**
+ * Return position of the first audio header.
+ * @return size of ID3v2 tag frames.
+ */
+ public int header_pos()
+ {
+ return header_pos;
+ }
+
+ /**
+ * Load ID3v2 frames.
+ * @param in MP3 InputStream.
+ * @author JavaZOOM
+ */
+ private void loadID3v2(InputStream in)
+ {
+ int size = -1;
+ try
+ {
+ // Read ID3v2 header (10 bytes).
+ in.mark(10);
+ size = readID3v2Header(in);
+ header_pos = size;
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ try
+ {
+ // Unread ID3v2 header (10 bytes).
+ in.reset();
+ }
+ catch (IOException e)
+ {}
+ }
+ // Load ID3v2 tags.
+ try
+ {
+ if (size > 0)
+ {
+ rawid3v2 = new byte[size];
+ in.read(rawid3v2,0,rawid3v2.length);
+ }
+ }
+ catch (IOException e)
+ {}
+ }
+
+ /**
+ * Parse ID3v2 tag header to find out size of ID3v2 frames.
+ * @param in MP3 InputStream
+ * @return size of ID3v2 frames + header
+ * @throws IOException
+ * @author JavaZOOM
+ */
+ private int readID3v2Header(InputStream in) throws IOException
+ {
+ byte[] id3header = new byte[4];
+ int size = -10;
+ in.read(id3header,0,3);
+ // Look for ID3v2
+ if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))
+ {
+ in.read(id3header,0,3);
+ int majorVersion = id3header[0];
+ int revision = id3header[1];
+ in.read(id3header,0,4);
+ size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
+ }
+ return (size+10);
+ }
+
+ /**
+ * Return raw ID3v2 frames + header.
+ * @return ID3v2 InputStream or null if ID3v2 frames are not available.
+ */
+ public InputStream getRawID3v2()
+ {
+ if (rawid3v2 == null) return null;
+ else
+ {
+ ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
+ return bain;
+ }
+ }
+
+ /**
+ * Close the Bitstream.
+ * @throws BitstreamException
+ */
+ public void close() throws BitstreamException
+ {
+ try
+ {
+ source.close();
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR, ex);
+ }
+ }
+
+ /**
+ * Reads and parses the next frame from the input source.
+ * @return the Header describing details of the frame read,
+ * or null if the end of the stream has been reached.
+ */
+ public Header readFrame() throws BitstreamException
+ {
+ Header result = null;
+ try
+ {
+ result = readNextFrame();
+ // E.B, Parse VBR (if any) first frame.
+ if (firstframe == true)
+ {
+ result.parseVBR(frame_bytes);
+ firstframe = false;
+ }
+ }
+ catch (BitstreamException ex)
+ {
+ if ((ex.getErrorCode()==INVALIDFRAME))
+ {
+ // Try to skip this frame.
+ //System.out.println("INVALIDFRAME");
+ try
+ {
+ closeFrame();
+ result = readNextFrame();
+ }
+ catch (BitstreamException e)
+ {
+ if ((e.getErrorCode()!=STREAM_EOF))
+ {
+ // wrap original exception so stack trace is maintained.
+ throw newBitstreamException(e.getErrorCode(), e);
+ }
+ }
+ }
+ else if ((ex.getErrorCode()!=STREAM_EOF))
+ {
+ // wrap original exception so stack trace is maintained.
+ throw newBitstreamException(ex.getErrorCode(), ex);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Read next MP3 frame.
+ * @return MP3 frame header.
+ * @throws BitstreamException
+ */
+ private Header readNextFrame() throws BitstreamException
+ {
+ if (framesize == -1)
+ {
+ nextFrame();
+ }
+ return header;
+ }
+
+
+ /**
+ * Read next MP3 frame.
+ * @throws BitstreamException
+ */
+ private void nextFrame() throws BitstreamException
+ {
+ // entire frame is read by the header class.
+ header.read_header(this, crc);
+ }
+
+ /**
+ * Unreads the bytes read from the frame.
+ * @throws BitstreamException
+ */
+ // REVIEW: add new error codes for this.
+ public void unreadFrame() throws BitstreamException
+ {
+ if (wordpointer==-1 && bitindex==-1 && (framesize>0))
+ {
+ try
+ {
+ source.unread(frame_bytes, 0, framesize);
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Close MP3 frame.
+ */
+ public void closeFrame()
+ {
+ framesize = -1;
+ wordpointer = -1;
+ bitindex = -1;
+ }
+
+ /**
+ * Determines if the next 4 bytes of the stream represent a
+ * frame header.
+ */
+ public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException
+ {
+ int read = readBytes(syncbuf, 0, 4);
+ int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
+
+ try
+ {
+ source.unread(syncbuf, 0, read);
+ }
+ catch (IOException ex)
+ {
+ }
+
+ boolean sync = false;
+ switch (read)
+ {
+ case 0:
+ sync = true;
+ break;
+ case 4:
+ sync = isSyncMark(headerstring, syncmode, syncword);
+ break;
+ }
+
+ return sync;
+ }
+
+
+ // REVIEW: this class should provide inner classes to
+ // parse the frame contents. Eventually, readBits will
+ // be removed.
+ public int readBits(int n)
+ {
+ return get_bits(n);
+ }
+
+ public int readCheckedBits(int n)
+ {
+ // REVIEW: implement CRC check.
+ return get_bits(n);
+ }
+
+ protected BitstreamException newBitstreamException(int errorcode)
+ {
+ return new BitstreamException(errorcode, null);
+ }
+ protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)
+ {
+ return new BitstreamException(errorcode, throwable);
+ }
+
+ /**
+ * Get next 32 bits from bitstream.
+ * They are stored in the headerstring.
+ * syncmod allows Synchro flag ID
+ * The returned value is False at the end of stream.
+ */
+
+ int syncHeader(byte syncmode) throws BitstreamException
+ {
+ boolean sync;
+ int headerstring;
+ // read additional 2 bytes
+ int bytesRead = readBytes(syncbuf, 0, 3);
+
+ if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);
+
+ headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);
+
+ do
+ {
+ headerstring <<= 8;
+
+ if (readBytes(syncbuf, 3, 1)!=1)
+ throw newBitstreamException(STREAM_EOF, null);
+
+ headerstring |= (syncbuf[3] & 0x000000FF);
+
+ sync = isSyncMark(headerstring, syncmode, syncword);
+ }
+ while (!sync);
+
+ //current_frame_number++;
+ //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;
+
+ return headerstring;
+ }
+
+ public boolean isSyncMark(int headerstring, int syncmode, int word)
+ {
+ boolean sync = false;
+
+ if (syncmode == INITIAL_SYNC)
+ {
+ //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
+ sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
+ }
+ else
+ {
+ sync = ((headerstring & 0xFFF80C00) == word) &&
+ (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
+ }
+
+ // filter out invalid sample rate
+ if (sync)
+ sync = (((headerstring >>> 10) & 3)!=3);
+ // filter out invalid layer
+ if (sync)
+ sync = (((headerstring >>> 17) & 3)!=0);
+ // filter out invalid version
+ if (sync)
+ sync = (((headerstring >>> 19) & 3)!=1);
+
+ return sync;
+ }
+
+ /**
+ * Reads the data for the next frame. The frame is not parsed
+ * until parse frame is called.
+ */
+ int read_frame_data(int bytesize) throws BitstreamException
+ {
+ int numread = 0;
+ numread = readFully(frame_bytes, 0, bytesize);
+ framesize = bytesize;
+ wordpointer = -1;
+ bitindex = -1;
+ return numread;
+ }
+
+ /**
+ * Parses the data previously read with read_frame_data().
+ */
+ void parse_frame() throws BitstreamException
+ {
+ // Convert Bytes read to int
+ int b=0;
+ byte[] byteread = frame_bytes;
+ int bytesize = framesize;
+
+ // Check ID3v1 TAG (True only if last frame).
+ //for (int t=0;t<(byteread.length)-2;t++)
+ //{
+ // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
+ // {
+ // System.out.println("ID3v1 detected at offset "+t);
+ // throw newBitstreamException(INVALIDFRAME, null);
+ // }
+ //}
+
+ for (int k=0;kBistreamException
s.
+ *
+ * @see BitstreamException
+ *
+ * @author MDM 12/12/99
+ * @since 0.0.6
+ */
+
+public interface BitstreamErrors extends JavaLayerErrors
+{
+
+ /**
+ * An undeterminable error occurred.
+ */
+ static public final int UNKNOWN_ERROR = BITSTREAM_ERROR + 0;
+
+ /**
+ * The header describes an unknown sample rate.
+ */
+ static public final int UNKNOWN_SAMPLE_RATE = BITSTREAM_ERROR + 1;
+
+ /**
+ * A problem occurred reading from the stream.
+ */
+ static public final int STREAM_ERROR = BITSTREAM_ERROR + 2;
+
+ /**
+ * The end of the stream was reached prematurely.
+ */
+ static public final int UNEXPECTED_EOF = BITSTREAM_ERROR + 3;
+
+ /**
+ * The end of the stream was reached.
+ */
+ static public final int STREAM_EOF = BITSTREAM_ERROR + 4;
+
+ /**
+ * Frame data are missing.
+ */
+ static public final int INVALIDFRAME = BITSTREAM_ERROR + 5;
+
+ /**
+ *
+ */
+ static public final int BITSTREAM_LAST = 0x1ff;
+
+}
diff --git a/songdbj/javazoom/jl/decoder/BitstreamException.java b/songdbj/javazoom/jl/decoder/BitstreamException.java
new file mode 100644
index 0000000000..99faa8962d
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/BitstreamException.java
@@ -0,0 +1,71 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Instances of BitstreamException
are thrown
+ * when operations on a Bitstream
fail.
+ * Throwable
instance, if any, that was thrown
+ * indicating that an exceptional condition has occurred.
+ *
Decoder
class encapsulates the details of
+ * decoding an MPEG audio frame.
+ *
+ * @author MDM
+ * @version 0.0.7 12/12/99
+ * @since 0.0.5
+ */
+public class Decoder implements DecoderErrors
+{
+ static private final Params DEFAULT_PARAMS = new Params();
+
+ /**
+ * The Bistream from which the MPEG audio frames are read.
+ */
+ //private Bitstream stream;
+
+ /**
+ * The Obuffer instance that will receive the decoded
+ * PCM samples.
+ */
+ private Obuffer output;
+
+ /**
+ * Synthesis filter for the left channel.
+ */
+ private SynthesisFilter filter1;
+
+ /**
+ * Sythesis filter for the right channel.
+ */
+ private SynthesisFilter filter2;
+
+ /**
+ * The decoder used to decode layer III frames.
+ */
+ private LayerIIIDecoder l3decoder;
+ private LayerIIDecoder l2decoder;
+ private LayerIDecoder l1decoder;
+
+ private int outputFrequency;
+ private int outputChannels;
+
+ private Equalizer equalizer = new Equalizer();
+
+ private Params params;
+
+ private boolean initialized;
+
+
+ /**
+ * Creates a new Decoder
instance with default
+ * parameters.
+ */
+
+ public Decoder()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new Decoder
instance with default
+ * parameters.
+ *
+ * @param params The Params
instance that describes
+ * the customizable aspects of the decoder.
+ */
+ public Decoder(Params params0)
+ {
+ if (params0==null)
+ params0 = DEFAULT_PARAMS;
+
+ params = params0;
+
+ Equalizer eq = params.getInitialEqualizerSettings();
+ if (eq!=null)
+ {
+ equalizer.setFrom(eq);
+ }
+ }
+
+ static public Params getDefaultParams()
+ {
+ return (Params)DEFAULT_PARAMS.clone();
+ }
+
+ public void setEqualizer(Equalizer eq)
+ {
+ if (eq==null)
+ eq = Equalizer.PASS_THRU_EQ;
+
+ equalizer.setFrom(eq);
+
+ float[] factors = equalizer.getBandFactors();
+
+ if (filter1!=null)
+ filter1.setEQ(factors);
+
+ if (filter2!=null)
+ filter2.setEQ(factors);
+ }
+
+ /**
+ * Decodes one frame from an MPEG audio bitstream.
+ *
+ * @param header The header describing the frame to decode.
+ * @param bitstream The bistream that provides the bits for te body of the frame.
+ *
+ * @return A SampleBuffer containing the decoded samples.
+ */
+ public Obuffer decodeFrame(Header header, Bitstream stream)
+ throws DecoderException
+ {
+ if (!initialized)
+ {
+ initialize(header);
+ }
+
+ int layer = header.layer();
+
+ output.clear_buffer();
+
+ FrameDecoder decoder = retrieveDecoder(header, stream, layer);
+
+ decoder.decodeFrame();
+
+ output.write_buffer(1);
+
+ return output;
+ }
+
+ /**
+ * Changes the output buffer. This will take effect the next time
+ * decodeFrame() is called.
+ */
+ public void setOutputBuffer(Obuffer out)
+ {
+ output = out;
+ }
+
+ /**
+ * Retrieves the sample frequency of the PCM samples output
+ * by this decoder. This typically corresponds to the sample
+ * rate encoded in the MPEG audio stream.
+ *
+ * @param the sample rate (in Hz) of the samples written to the
+ * output buffer when decoding.
+ */
+ public int getOutputFrequency()
+ {
+ return outputFrequency;
+ }
+
+ /**
+ * Retrieves the number of channels of PCM samples output by
+ * this decoder. This usually corresponds to the number of
+ * channels in the MPEG audio stream, although it may differ.
+ *
+ * @return The number of output channels in the decoded samples: 1
+ * for mono, or 2 for stereo.
+ *
+ */
+ public int getOutputChannels()
+ {
+ return outputChannels;
+ }
+
+ /**
+ * Retrieves the maximum number of samples that will be written to
+ * the output buffer when one frame is decoded. This can be used to
+ * help calculate the size of other buffers whose size is based upon
+ * the number of samples written to the output buffer. NB: this is
+ * an upper bound and fewer samples may actually be written, depending
+ * upon the sample rate and number of channels.
+ *
+ * @return The maximum number of samples that are written to the
+ * output buffer when decoding a single frame of MPEG audio.
+ */
+ public int getOutputBlockSize()
+ {
+ return Obuffer.OBUFFERSIZE;
+ }
+
+
+ protected DecoderException newDecoderException(int errorcode)
+ {
+ return new DecoderException(errorcode, null);
+ }
+
+ protected DecoderException newDecoderException(int errorcode, Throwable throwable)
+ {
+ return new DecoderException(errorcode, throwable);
+ }
+
+ protected FrameDecoder retrieveDecoder(Header header, Bitstream stream, int layer)
+ throws DecoderException
+ {
+ FrameDecoder decoder = null;
+
+ // REVIEW: allow channel output selection type
+ // (LEFT, RIGHT, BOTH, DOWNMIX)
+ switch (layer)
+ {
+ case 3:
+ if (l3decoder==null)
+ {
+ l3decoder = new LayerIIIDecoder(stream,
+ header, filter1, filter2,
+ output, OutputChannels.BOTH_CHANNELS);
+ }
+
+ decoder = l3decoder;
+ break;
+ case 2:
+ if (l2decoder==null)
+ {
+ l2decoder = new LayerIIDecoder();
+ l2decoder.create(stream,
+ header, filter1, filter2,
+ output, OutputChannels.BOTH_CHANNELS);
+ }
+ decoder = l2decoder;
+ break;
+ case 1:
+ if (l1decoder==null)
+ {
+ l1decoder = new LayerIDecoder();
+ l1decoder.create(stream,
+ header, filter1, filter2,
+ output, OutputChannels.BOTH_CHANNELS);
+ }
+ decoder = l1decoder;
+ break;
+ }
+
+ if (decoder==null)
+ {
+ throw newDecoderException(UNSUPPORTED_LAYER, null);
+ }
+
+ return decoder;
+ }
+
+ private void initialize(Header header)
+ throws DecoderException
+ {
+
+ // REVIEW: allow customizable scale factor
+ float scalefactor = 32700.0f;
+
+ int mode = header.mode();
+ int layer = header.layer();
+ int channels = mode==Header.SINGLE_CHANNEL ? 1 : 2;
+
+
+ // set up output buffer if not set up by client.
+ if (output==null)
+ output = new SampleBuffer(header.frequency(), channels);
+
+ float[] factors = equalizer.getBandFactors();
+ filter1 = new SynthesisFilter(0, scalefactor, factors);
+
+ // REVIEW: allow mono output for stereo
+ if (channels==2)
+ filter2 = new SynthesisFilter(1, scalefactor, factors);
+
+ outputChannels = channels;
+ outputFrequency = header.frequency();
+
+ initialized = true;
+ }
+
+ /**
+ * The Params
class presents the customizable
+ * aspects of the decoder.
+ * + * Instances of this class are not thread safe. + */ + public static class Params implements Cloneable + { + private OutputChannels outputChannels = OutputChannels.BOTH; + + private Equalizer equalizer = new Equalizer(); + + public Params() + { + } + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + throw new InternalError(this+": "+ex); + } + } + + public void setOutputChannels(OutputChannels out) + { + if (out==null) + throw new NullPointerException("out"); + + outputChannels = out; + } + + public OutputChannels getOutputChannels() + { + return outputChannels; + } + + /** + * Retrieves the equalizer settings that the decoder's equalizer + * will be initialized from. + *
+ * The Equalizer
instance returned
+ * cannot be changed in real time to affect the
+ * decoder output as it is used only to initialize the decoders
+ * EQ settings. To affect the decoder's output in realtime,
+ * use the Equalizer returned from the getEqualizer() method on
+ * the decoder.
+ *
+ * @return The Equalizer
used to initialize the
+ * EQ settings of the decoder.
+ */
+ public Equalizer getInitialEqualizerSettings()
+ {
+ return equalizer;
+ }
+
+ };
+}
+
diff --git a/songdbj/javazoom/jl/decoder/DecoderErrors.java b/songdbj/javazoom/jl/decoder/DecoderErrors.java
new file mode 100644
index 0000000000..66c1935051
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/DecoderErrors.java
@@ -0,0 +1,38 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 01/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * This interface provides constants describing the error
+ * codes used by the Decoder to indicate errors.
+ *
+ * @author MDM
+ */
+public interface DecoderErrors extends JavaLayerErrors
+{
+
+ static public final int UNKNOWN_ERROR = DECODER_ERROR + 0;
+
+ /**
+ * Layer not supported by the decoder.
+ */
+ static public final int UNSUPPORTED_LAYER = DECODER_ERROR + 1;
+}
diff --git a/songdbj/javazoom/jl/decoder/DecoderException.java b/songdbj/javazoom/jl/decoder/DecoderException.java
new file mode 100644
index 0000000000..b75710870f
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/DecoderException.java
@@ -0,0 +1,61 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 01/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * The DecoderException
represents the class of
+ * errors that can occur when decoding MPEG audio.
+ *
+ * @author MDM
+ */
+public class DecoderException extends JavaLayerException
+ implements DecoderErrors
+{
+ private int errorcode = UNKNOWN_ERROR;
+
+ public DecoderException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+
+ public DecoderException(int errorcode, Throwable t)
+ {
+ this(getErrorString(errorcode), t);
+ this.errorcode = errorcode;
+ }
+
+ public int getErrorCode()
+ {
+ return errorcode;
+ }
+
+
+ static public String getErrorString(int errorcode)
+ {
+ // REVIEW: use resource file to map error codes
+ // to locale-sensitive strings.
+
+ return "Decoder errorcode "+Integer.toHexString(errorcode);
+ }
+
+
+}
+
diff --git a/songdbj/javazoom/jl/decoder/Equalizer.java b/songdbj/javazoom/jl/decoder/Equalizer.java
new file mode 100644
index 0000000000..57545a939d
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/Equalizer.java
@@ -0,0 +1,227 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+
+package javazoom.jl.decoder;
+
+/**
+ * The Equalizer
class can be used to specify
+ * equalization settings for the MPEG audio decoder.
+ *
+ * The equalizer consists of 32 band-pass filters.
+ * Each band of the equalizer can take on a fractional value between
+ * -1.0 and +1.0.
+ * At -1.0, the input signal is attenuated by 6dB, at +1.0 the signal is
+ * amplified by 6dB.
+ *
+ * @see Decoder
+ *
+ * @author MDM
+ */
+public final class Equalizer
+{
+ /**
+ * Equalizer setting to denote that a given band will not be
+ * present in the output signal.
+ */
+ static public final float BAND_NOT_PRESENT = Float.NEGATIVE_INFINITY;
+
+ static public final Equalizer PASS_THRU_EQ = new Equalizer();
+
+ private static final int BANDS = 32;
+
+ private final float[] settings = new float[BANDS];
+
+ /**
+ * Creates a new
+ *
+ * @author MDM
+ */
+public class JavaLayerException extends Exception
+{
+
+ private Throwable exception;
+
+
+ public JavaLayerException()
+ {
+ }
+
+ public JavaLayerException(String msg)
+ {
+ super(msg);
+ }
+
+ public JavaLayerException(String msg, Throwable t)
+ {
+ super(msg);
+ exception = t;
+ }
+
+ public Throwable getException()
+ {
+ return exception;
+ }
+
+
+ public void printStackTrace()
+ {
+ printStackTrace(System.err);
+ }
+
+ public void printStackTrace(PrintStream ps)
+ {
+ if (this.exception==null)
+ {
+ super.printStackTrace(ps);
+ }
+ else
+ {
+ exception.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/songdbj/javazoom/jl/decoder/JavaLayerHook.java b/songdbj/javazoom/jl/decoder/JavaLayerHook.java
new file mode 100644
index 0000000000..352059433d
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/JavaLayerHook.java
@@ -0,0 +1,36 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+import java.io.InputStream;
+
+/**
+ * The
+ * When implementing a factory that provides an AudioDevice that uses
+ * class that may not be present, the factory should dynamically link to any
+ * specific implementation classes required to instantiate or test the audio
+ * implementation. This is so that the application as a whole
+ * can run without these classes being present. The audio
+ * device implementation, however, will usually statically link to the classes
+ * required. (See the JavaSound deivce and factory for an example
+ * of this.)
+ *
+ * @see FactoryRegistry
+ *
+ * @since 0.0.8
+ * @author Mat McGowan
+ */
+public abstract class AudioDeviceFactory
+{
+ /**
+ * Creates a new
+ * Instances of this class are thread-safe.
+ *
+ * @since 0.0.8
+ * @author Mat McGowan
+ */
+
+public class FactoryRegistry extends AudioDeviceFactory
+{
+ static private FactoryRegistry instance = null;
+
+ static synchronized public FactoryRegistry systemRegistry()
+ {
+ if (instance==null)
+ {
+ instance = new FactoryRegistry();
+ instance.registerDefaultFactories();
+ }
+ return instance;
+ }
+
+
+ protected Hashtable factories = new Hashtable();
+
+ /**
+ * Registers an
+ The deal with metaint: Icy streams don't try to put
+ tags between MP3 frames the way that ID3 does. Instead, it
+ requires the client to strip metadata from the stream before
+ it hits the decoder. You get an
+
+ Originally thought that "icy" implied Icecast, but this is
+ completely wrong -- real Icecast servers, found through
+ www.icecast.net and typified by URLs with a trailing directory
+ (like CalArts School of Music - http://65.165.174.100:8000/som)
+ do not have the "ICY 200 OK" magic string or any of the
+ CRLF-separated headers. Apparently, "icy" means "Shoutcast".
+ Yep, that's weird.
+ @author Chris Adamson, invalidname@mac.com
+ */
+public class IcyInputStream
+ extends BufferedInputStream
+ implements MP3MetadataParser {
+
+ public static boolean DEBUG = false;
+
+ MP3TagParseSupport tagParseSupport;
+ /** inline tags are delimited by ';', also filter out
+ null bytes
+ */
+ protected static final String INLINE_TAG_SEPARATORS = ";\u0000";
+ /* looks like icy streams start start with
+ ICY 200 OK\r\n
+ then the tags are like
+ icy-notice1:
+ Hilariously, the inline data format is totally different
+ than the top-of-stream header. For example, here's a
+ block I saw on "Final Fantasy Radio":
+
+ * For a detailed method description, see InputStream.
+ */
+ private static class RandomAdapterInputStream extends InputStream {
+ private RandomAccessFile rf;
+
+ /**
+ * Create a new Adapter.
+ *
+ * @param rf RandomAccessFile to be used
+ */
+ public RandomAdapterInputStream( RandomAccessFile rf ) {
+ this.rf = rf;
+ }
+
+ /**
+ * Read a byte.
+ *
+ * @return Read byte or -1.
+ */
+ public int read() throws IOException {
+ return rf.read();
+ }
+
+ /**
+ * Read a byte array.
+ *
+ * @param b Byte array to be read
+ * @return Number of bytes read or -1
+ */
+ public int read( byte[] b) throws IOException {
+ return rf.read(b);
+ }
+
+ /**
+ * Read into a byte array.
+ *
+ * @param b Byte array to be read
+ * @param off Starting offset
+ * @param len Length
+ * @return Number of bytes read or -1
+ */
+ public int read( byte[] b, int off, int len) throws IOException {
+ return rf.read( b, off, len );
+ }
+
+ /**
+ * Skip a number of bytes in forward direction.
+ *
+ * @param n Number of bytes to skip
+ * @return Number of bytes skipped, or -1
+ */
+ public long skip( long n ) throws IOException {
+ return rf.skipBytes( (int) n );
+ }
+
+ /**
+ * Return the number of available bytes. Here it is the number of
+ * bytes remaining until EOF.
+ *
+ * @return Number of bytes available.
+ */
+ public int available() throws IOException {
+ return (int) (rf.length() - rf.getFilePointer());
+ }
+
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/TagAsf.java b/songdbj/net/shredzone/ifish/ltr/TagAsf.java
new file mode 100644
index 0000000000..fc68789345
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/TagAsf.java
@@ -0,0 +1,170 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+import java.util.List;
+
+import de.jarnbjo.ogg.*;
+import de.jarnbjo.vorbis.*;
+import entagged.audioformats.AudioFile;
+import entagged.audioformats.Tag;
+import entagged.audioformats.asf.AsfFileReader;
+import entagged.audioformats.exceptions.CannotReadException;
+
+/**
+ * Decodes an ASF/WMA stream. It uses parts of the
+ * Entagged software, which is
+ * copyrighted by the Entagged Development Team, and published under
+ * GPL.
+ *
+ * NOTE that due to the fact that Entagged is GPL, you MUST
+ * remove all entagged sources and this class file if you decide to use
+ * the LGPL or MPL part of the iFish licence!
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class TagAsf extends LTR {
+ private final Tag tag;
+
+ /**
+ * Create a new TagAsf object.
+ *
+ * @param in File to read
+ * @param file Reference to the file itself
+ * @throws FormatDecodeException Couldn't decode this file
+ */
+ public TagAsf( RandomAccessFile in, File file )
+ throws FormatDecodeException {
+ super( in );
+
+ try {
+ final AsfFileReader afr = new AsfFileReader();
+ final AudioFile af = afr.read( file, in );
+ tag = af.getTag();
+ }catch( CannotReadException e ) {
+ throw new FormatDecodeException( "could not decode file: " + e.toString() );
+ }catch( RuntimeException e ) {
+ throw new FormatDecodeException( "error decoding file: " + e.toString() );
+ }
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public String getType() {
+ return "ASF";
+ }
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public String getArtist() {
+ return tag.getFirstArtist().trim();
+ }
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public String getAlbum() {
+ return tag.getFirstAlbum().trim();
+ }
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public String getTitle() {
+ return tag.getFirstTitle().trim();
+ }
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public String getGenre() {
+ return tag.getFirstGenre().trim();
+ }
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public String getYear() {
+ return tag.getFirstYear().trim();
+ }
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public String getComment() {
+ return tag.getFirstComment().trim();
+ }
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public String getTrack() {
+ return tag.getFirstTrack().trim();
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java
new file mode 100644
index 0000000000..9d3182260a
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java
@@ -0,0 +1,184 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+
+/**
+ * Decodes an MP3 file with ID3v1 tag. The file is compliant to the
+ * specifications found at www.id3.org.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class TagMp3v1 extends LTRmp3 {
+ private String artist = "";
+ private String comment = "";
+ private String title = "";
+ private String album = "";
+ private String year = "";
+ private String track = "";
+ private String genre = "";
+
+ /**
+ * Create a new TagMp3v1 object.
+ *
+ * @param in File to be read
+ * @throws FormatDecodeException Description of the Exception
+ */
+ public TagMp3v1( RandomAccessFile in )
+ throws FormatDecodeException {
+ super( in );
+
+ try {
+ //--- Decode header ---
+ in.seek( in.length() - 128 ); // To the place where the tag lives
+ if( !readStringLen( 3 ).equals( "TAG" ) ) {
+ throw new FormatDecodeException( "not an id3v1 tag" );
+ }
+
+ title = readStringLen( 30, charsetV1 ).trim();
+ artist = readStringLen( 30, charsetV1 ).trim();
+ album = readStringLen( 30, charsetV1 ).trim();
+ year = readStringLen( 4, charsetV1 ).trim();
+ comment = readStringLen( 28, charsetV1 );
+
+ byte[] sto = new byte[2];
+ in.readFully( sto );
+ if( sto[0] == 0x00 ) {
+ // ID3v1.1
+ track = ( sto[1]>0 ? String.valueOf(sto[1]) : "" );
+ } else {
+ // ID3v1.0
+ comment += new String( sto, charsetV1 );
+ }
+ comment = comment.trim();
+
+ genre = decodeGenre( in.readUnsignedByte() );
+ if( genre==null ) genre="";
+
+ } catch( IOException e ) {
+ throw new FormatDecodeException( "could not decode file: " + e.toString() );
+ }catch( RuntimeException e ) {
+ throw new FormatDecodeException( "error decoding file: " + e.toString() );
+ }
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public String getType() {
+ return "MP3/id3v1";
+ }
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public String getArtist() {
+ return artist;
+ }
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public String getAlbum() {
+ return album;
+ }
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public String getGenre() {
+ return genre;
+ }
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public String getYear() {
+ return year;
+ }
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public String getTrack() {
+ return track;
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java
new file mode 100644
index 0000000000..689dd21618
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java
@@ -0,0 +1,441 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+import java.util.regex.*;
+
+/**
+ * Decodes an MP3 file with ID3v2 tag. The file is compliant to the
+ * specifications found at www.id3.org,
+ * V2.4.0 and V2.3.0. ID3 V2.2.0 is handled in a separate tag handler.
+ * Anyhow it has certain limitations regarding tag frames which could
+ * not be used or entirely used to iFish, e.g. multiple genres.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class TagMp3v2 extends LTRmp3 {
+ private int globalSize;
+ private Pattern patGenre;
+ private boolean v230 = false; // V2.3.0 detected
+
+ private String artist = "";
+ private String comment = "";
+ private String title = "";
+ private String album = "";
+ private String year = "";
+ private String track = "";
+ private String genre = "";
+
+ /**
+ * Create a new TagMp3v2 instance.
+ *
+ * @param in File to be read
+ * @throws FormatDecodeException Couldn't decode this file
+ */
+ public TagMp3v2( RandomAccessFile in )
+ throws FormatDecodeException {
+ super( in );
+
+ patGenre = Pattern.compile( "\\((\\d{1,3})\\).*" );
+
+ try {
+ //--- Decode header ---
+ if( !readStringLen( 3 ).equals( "ID3" ) ) {
+ throw new FormatDecodeException( "not an id3v2 tag" );
+ }
+
+ byte version = in.readByte();
+ byte revision = in.readByte();
+
+ if( version==0xFF || revision==0xFF ) {
+ throw new FormatDecodeException( "not an id3v2 tag" );
+ }
+
+ if( version<=0x02 || version>0x04 ) {
+ throw new FormatDecodeException( "unable to decode ID3v2."+version+"."+revision );
+ }
+
+ byte flags = in.readByte();
+ v230 = (version==0x03);
+ globalSize = readSyncsafeInt() + 10;
+
+ //--- Skip extended header ---
+ if( ( flags & 0x40 ) != 0 ) {
+ int ehsize = readAutoInt();
+ if( ehsize < 6 ) {
+ throw new FormatDecodeException( "extended header too small" );
+ }
+ in.skipBytes( ehsize - 4 );
+ }
+
+ //--- Read all frames ---
+ Frame frm;
+ while( ( frm = readFrame() ) != null ) {
+ String type = frm.getType();
+ String[] answer;
+
+ if( type.equals( "TOPE" ) || type.equals( "TPE1" ) ) {
+
+ answer = frm.getAsStringEnc();
+ artist = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "COMM" ) ) {
+
+ answer = frm.getAsStringEnc();
+ comment = (answer.length>1 ? answer[1].trim() : "");
+
+ } else if( type.equals( "TIT2" ) ) {
+
+ answer = frm.getAsStringEnc();
+ title =(answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TALB" ) ) {
+
+ answer = frm.getAsStringEnc();
+ album = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TYER" ) ) {
+
+ answer = frm.getAsStringEnc();
+ year = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TRCK" ) ) {
+
+ answer = frm.getAsStringEnc();
+ track = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TCON" ) ) {
+
+ answer = frm.getAsStringEnc();
+ if( answer.length>0 ) {
+ genre = answer[0].trim();
+ Matcher mat = patGenre.matcher( genre );
+ if( mat.matches() ) {
+ genre = decodeGenre( Integer.parseInt( mat.group( 1 ) ) );
+ if( genre==null ) genre="";
+ }
+ }
+ }
+ }
+
+ //--- Footer frame? ---
+ if( ( flags & 0x10 ) != 0 ) {
+ in.skipBytes( 10 ); // Then skip it
+ }
+ // Position is now the start of the MP3 data
+
+ } catch( IOException e ) {
+ throw new FormatDecodeException( "could not decode file: " + e.toString() );
+ }catch( RuntimeException e ) {
+ throw new FormatDecodeException( "error decoding file: " + e.toString() );
+ }
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public String getType() {
+ return ( v230 ? "MP3/id3v2.3.0" : "MP3/id3v2.4.0" );
+ }
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public String getArtist() {
+ return artist;
+ }
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public String getAlbum() {
+ return album;
+ }
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public String getGenre() {
+ return genre;
+ }
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public String getYear() {
+ return year;
+ }
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public String getTrack() {
+ return track;
+ }
+
+ /**
+ * Read an ID3v2 integer. This is a 4 byte big endian value, which is
+ * only syncsafe for ID3v2.4 or higher, but not for ID3v2.3.
+ *
+ * @return The integer read.
+ * @throws IOException If there were not enough bytes in the file.
+ */
+ protected int readAutoInt()
+ throws IOException {
+ return( v230 ? readInt() : readSyncsafeInt() );
+ }
+
+ /**
+ * Read a tag frame. A Frame object will be returned, or null if no
+ * more frames were available.
+ *
+ * @return The next frame, or null
+ * @throws IOException If premature EOF was reached.
+ */
+ protected Frame readFrame()
+ throws IOException {
+ if( in.getFilePointer() >= globalSize ) {
+ return null;
+ }
+
+ //--- Get the type ---
+ String type = readStringLen( 4 );
+ if( type.charAt(0)==0 ) { // Optional padding frame
+ in.skipBytes( (int) ( globalSize - in.getFilePointer() ) );// Skip it...
+ return null; // Return null
+ }
+
+ //--- Read the frame ---
+ int size = readAutoInt();
+ byte flag1 = in.readByte();
+ byte flag2 = in.readByte();
+
+ //--- Read the content ---
+ // Stay within reasonable boundaries. If the data part is bigger than
+ // 16K, it's not really useful for us, so we will keep the frame empty.
+ byte[] data = null;
+ if( size<=16384 ) {
+ data = new byte[size];
+ int rlen = in.read( data );
+ if( rlen != size ) {
+ throw new IOException( "unexpected EOF" );
+ }
+ }else {
+ in.skipBytes( size );
+ }
+
+ //--- Return the frame ---
+ return new Frame( type, size, flag1, flag2, data );
+ }
+
+/*--------------------------------------------------------------------*/
+
+ /**
+ * This class contains a ID3v2 frame.
+ */
+ private static class Frame {
+ private final String charset;
+ private final String type;
+// private final int size;
+// private final byte flag1;
+ private final byte flag2;
+ private byte[] data;
+ private boolean decoded = false;
+
+ /**
+ * Constructor for the Frame object
+ *
+ * @param type Frame type
+ * @param size Frame size
+ * @param flag1 Flag 1
+ * @param flag2 Flag 2
+ * @param data Frame content, may be null
+ */
+ public Frame( String type, int size, byte flag1, byte flag2, byte[] data ) {
+ charset = "ISO-8859-1";
+ this.type = type;
+// Currently unused...
+// this.size = size;
+// this.flag1 = flag1;
+ this.flag2 = flag2;
+ this.data = data;
+ }
+
+ /**
+ * Get the type.
+ *
+ * @return The type of this Frame
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Return the Frame content as String. This method is to be used for
+ * strings without a leading encoding byte. This machine's default
+ * encoding will be used instead.
+ *
+ * @return Encoded string
+ * @throws FormatDecodeException Could not read or decode frame
+ */
+ public String getAsString()
+ throws FormatDecodeException {
+ if(data==null) return null;
+ decode();
+ return new String( data );
+ }
+
+ /**
+ * Return the Frame content as encoded String. The first byte will
+ * contain the encoding type, following the string itself. Multiple
+ * strings are null-terminated. An array of all strings found, will
+ * be returned.
+ *
+ * @return Array of all strings. Might be an empty array!
+ * @throws FormatDecodeException Could not read or decode frame
+ */
+ public String[] getAsStringEnc()
+ throws FormatDecodeException {
+ if( data==null ) return new String[0];
+ decode();
+ if( data.length==0 ) return new String[0];
+ int len = data.length - 1;
+ String result = "";
+ try {
+ switch ( data[0] ) {
+ case 0x00:
+ result = new String( data, 1, len, charset );
+ break;
+ case 0x01:
+ result = new String( data, 1, len, "UTF-16" );
+ break;
+ case 0x02:
+ result = new String( data, 1, len, "UTF-16BE" );
+ break;
+ case 0x03:
+ result = new String( data, 1, len, "UTF-8" );
+ break;
+ default:
+ throw new FormatDecodeException( "unknown encoding of frame " + type );
+ }
+ }catch( UnsupportedEncodingException e ) {
+ throw new FormatDecodeException( "Java misses a basic encoding!?" );
+ }
+ return result.split( "\0" );
+ }
+
+ /**
+ * Decode a frame, unless already decoded. If the frame was
+ * unsynchronized, it will be synchronized here. Compression and
+ * encryption is not supported yet. You can invoke this method
+ * several times without any effect.
+ *
+ * @throws FormatDecodeException Description of the Exception
+ */
+ private void decode()
+ throws FormatDecodeException {
+ if( decoded ) return;
+ decoded = true;
+
+ if( ( flag2 & 0x02 ) != 0 ) { // Unsynchronize
+ byte decoded[] = new byte[data.length];
+ int pos = 0;
+ for( int ix = 0; ix < data.length - 1; ix++ ) {
+ decoded[pos++] = data[ix];
+ if( data[ix] == 0xFF && data[ix + 1] == 0x00 ) {
+ ix++;
+ }
+ }
+ data = new byte[pos];
+ System.arraycopy( decoded, 0, data, 0, pos );
+ }
+
+ if( ( flag2 & 0x08 ) != 0 ) { // Compression
+ throw new FormatDecodeException( "sorry, compression is not yet supported" );
+ }
+
+ if( ( flag2 & 0x04 ) != 0 ) { // Encryption
+ throw new FormatDecodeException( "sorry, encryption is not yet supported" );
+ }
+ }
+
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java
new file mode 100644
index 0000000000..28e623f962
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java
@@ -0,0 +1,373 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+import java.util.regex.*;
+
+/**
+ * Decodes an MP3 file with old ID3v2.00 tag. The file is compliant to the
+ * specifications found at www.id3.org.
+ * Only ID3 V2.00 up to v2.2.0 is handled here. Newer versions are separately
+ * handled in TagMp3v2.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class TagMp3v200 extends LTRmp3 {
+ private int globalSize;
+ private Pattern patGenre;
+
+ private String artist = "";
+ private String comment = "";
+ private String title = "";
+ private String album = "";
+ private String year = "";
+ private String track = "";
+ private String genre = "";
+
+ /**
+ * Create a new TagMp3v200 instance.
+ *
+ * @param in File to be read
+ * @throws FormatDecodeException Couldn't decode this file
+ */
+ public TagMp3v200( RandomAccessFile in )
+ throws FormatDecodeException {
+ super( in );
+
+ patGenre = Pattern.compile( "\\((\\d{1,3})\\).*" );
+
+ try {
+ //--- Decode header ---
+ if( !readStringLen( 3 ).equals( "ID3" ) ) {
+ throw new FormatDecodeException( "not an id3v2 tag" );
+ }
+
+ byte version = in.readByte();
+ byte revision = in.readByte();
+
+ if( version!=0x02 || revision==0xFF ) {
+ throw new FormatDecodeException( "not an id3v2.2.0 tag" );
+ }
+
+ byte flags = in.readByte();
+ globalSize = readSyncsafeInt() + 10;
+
+ //--- Read all frames ---
+ Frame frm;
+ while( ( frm = readFrame() ) != null ) {
+ String type = frm.getType();
+ String[] answer;
+
+ if( type.equals( "TOA" ) || type.equals( "TP1" ) ) {
+
+ answer = frm.getAsStringEnc();
+ artist = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "COM" ) ) {
+
+ answer = frm.getAsStringEnc();
+ comment = (answer.length>1 ? answer[1].trim() : "");
+
+ } else if( type.equals( "TT2" ) ) {
+
+ answer = frm.getAsStringEnc();
+ title =(answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TAL" ) ) {
+
+ answer = frm.getAsStringEnc();
+ album = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TYE" ) ) {
+
+ answer = frm.getAsStringEnc();
+ year = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TRK" ) ) {
+
+ answer = frm.getAsStringEnc();
+ track = (answer.length>0 ? answer[0].trim() : "");
+
+ } else if( type.equals( "TCO" ) ) {
+
+ answer = frm.getAsStringEnc();
+ if( answer.length>0 ) {
+ genre = answer[0].trim();
+ Matcher mat = patGenre.matcher( genre );
+ if( mat.matches() ) {
+ genre = decodeGenre( Integer.parseInt( mat.group( 1 ) ) );
+ if( genre==null ) genre="";
+ }
+ }
+ }
+ }
+
+ // Position is now the start of the MP3 data
+
+ } catch( IOException e ) {
+ throw new FormatDecodeException( "could not decode file: " + e.toString() );
+ }catch( RuntimeException e ) {
+ throw new FormatDecodeException( "error decoding file: " + e.toString() );
+ }
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public String getType() {
+ return "MP3/id3v2.2.0";
+ }
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public String getArtist() {
+ return artist;
+ }
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public String getAlbum() {
+ return album;
+ }
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public String getGenre() {
+ return genre;
+ }
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public String getYear() {
+ return year;
+ }
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public String getTrack() {
+ return track;
+ }
+
+ /**
+ * Read a tag frame. A Frame object will be returned, or null if no
+ * more frames were available.
+ *
+ * @return The next frame, or null
+ * @throws IOException If premature EOF was reached.
+ */
+ protected Frame readFrame()
+ throws IOException {
+ if( in.getFilePointer() >= globalSize ) {
+ return null;
+ }
+
+ //--- Get the type ---
+ String type = readStringLen( 3 );
+ if( type.charAt(0)==0 ) { // Optional padding frame
+ in.skipBytes( (int) ( globalSize - in.getFilePointer() ) );// Skip it...
+ return null; // Return null
+ }
+
+ //--- Read the frame ---
+ int size = read3Int();
+
+ //--- Read the content ---
+ // Stay within reasonable boundaries. If the data part is bigger than
+ // 16K, it's not really useful for us, so we will keep the frame empty.
+ byte[] data = null;
+ if( size<=16384 ) {
+ data = new byte[size];
+ int rlen = in.read( data );
+ if( rlen != size ) {
+ throw new IOException( "unexpected EOF" );
+ }
+ }else {
+ in.skipBytes( size );
+ }
+
+ //--- Return the frame ---
+ return new Frame( type, size, data );
+ }
+
+ /**
+ * Read an ID3v2 3 byte integer. This is a 3 byte big endian value, which is
+ * always not syncsafe.
+ *
+ * @return The integer read.
+ * @throws IOException If there were not enough bytes in the file.
+ */
+ protected int read3Int()
+ throws IOException {
+ int val = 0;
+ for( int cnt = 3; cnt > 0; cnt-- ) {
+ val <<= 8;
+ val |= ( in.readByte() & 0xFF );
+ }
+ return val;
+ }
+
+/*--------------------------------------------------------------------*/
+
+ /**
+ * This class contains a ID3v2.2.0 frame.
+ */
+ private static class Frame {
+ private final String charset;
+ private final String type;
+// private final int size;
+ private byte[] data;
+
+ /**
+ * Constructor for the Frame object
+ *
+ * @param type Frame type
+ * @param size Frame size
+ * @param data Frame content, may be null
+ */
+ public Frame( String type, int size, byte[] data ) {
+ this.charset = "ISO-8859-1";
+ this.type = type;
+// Currently unused...
+// this.size = size;
+ this.data = data;
+ }
+
+ /**
+ * Get the type.
+ *
+ * @return The type of this Frame
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Return the Frame content as String. This method is to be used for
+ * strings without a leading encoding byte. This machine's default
+ * encoding will be used instead.
+ *
+ * @return Encoded string
+ * @throws FormatDecodeException Could not read or decode frame
+ */
+ public String getAsString()
+ throws FormatDecodeException {
+ if(data==null) return null;
+ return new String( data );
+ }
+
+ /**
+ * Return the Frame content as encoded String. The first byte will
+ * contain the encoding type, following the string itself. Multiple
+ * strings are null-terminated. An array of all strings found, will
+ * be returned.
+ *
+ * @return Array of all strings. Might be an empty array!
+ * @throws FormatDecodeException Could not read or decode frame
+ */
+ public String[] getAsStringEnc()
+ throws FormatDecodeException {
+ if(data==null) return new String[0];
+ int len = data.length - 1;
+ String result = "";
+ try {
+ switch ( data[0] ) {
+ case 0x00:
+ result = new String( data, 1, len, charset );
+ break;
+ case 0x01:
+ result = new String( data, 1, len, "UTF-16" );
+ break;
+ default:
+ throw new FormatDecodeException( "unknown encoding of frame " + type );
+ }
+ }catch( UnsupportedEncodingException e ) {
+ throw new FormatDecodeException( "Java misses a basic encoding!?" );
+ }
+ return result.split( "\0" );
+ }
+
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java b/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java
new file mode 100644
index 0000000000..45b7401437
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java
@@ -0,0 +1,207 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+import de.jarnbjo.ogg.*;
+import de.jarnbjo.vorbis.*;
+
+/**
+ * Decodes an Ogg Vorbis stream. It uses the
+ * J-Ogg library, which is
+ * copyrighted by Tor-Einar Jarnbjo. His licence says that may use and
+ * modify it as will (even commercial), as long as a reference to his
+ * library is stated in the software.
+ *
+ * NOTE: Due to a bug, there is a patch required in the J-Ogg
+ * library. In class de.jarnbjo.vorbis.CommentHeader, private method
+ * addComment(), add a line
+ * GSM 06.10 audio file reader and writer.
+ The classes provided here . Ogg vorbis audio file reader based on the jorbis library.
+ The classes provided here . Mp3 audio file reader and writer.
+ The classes provided here . Standard audio file readers and writers (.aiff, .au, .wav).
+ The classes provided here . Ogg vorbis audio file reader and writer based on the pure java libraries.
+ The classes provided here . Ogg vorbis audio file reader and writer based on native libraries.
+ The classes provided here . Access to the native ogg library.
+ The classes provided here . Alternative pure java implementation of the ogg library.
+ The classes provided here .
+ * The
+ * Though it's possible to store
+ * Strings as objects in this class, it doesn't make sense
+ * as you could use ArraySet for that equally well.
+ *
+ * You shouldn't use the ArrayList specific functions
+ * like those that take index parameters.
+ *
+ * It is not possible to add Misc helper classes.
+ The classes provided here .
+ * A standard registry of file type names will
+ * be maintained by the Tritonus team.
+ *
+ * In a specification request to JavaSoft, these static methods
+ * could be integrated into
+ * Also, the file name extension should be deprecated and moved
+ * to
+ * This method is supposed to be used by user programs.
+ *
+ * In order to assure a well-filled internal list,
+ * call
+ * This method is supposed to be used by file providers.
+ * Every file reader and file writer provider should
+ * exclusively use this method for retrieving instances
+ * of
+ * This function should be AudioFileFormat.Type.equals and must
+ * be considered as a temporary workaround until it flows into the
+ * JavaSound API.
+ */
+ // IDEA: create a special "NOT_SPECIFIED" file type
+ // and a AudioFileFormat.Type.matches method.
+ public static boolean equals(AudioFileFormat.Type t1, AudioFileFormat.Type t2) {
+ return t2.toString().equals(t1.toString());
+ }
+
+}
+
+/*** AudioFileTypes.java ***/
+
diff --git a/songdbj/org/tritonus/share/sampled/AudioFormatSet.java b/songdbj/org/tritonus/share/sampled/AudioFormatSet.java
new file mode 100644
index 0000000000..8d89541d77
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/AudioFormatSet.java
@@ -0,0 +1,155 @@
+/*
+ * AudioFormatSet.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Florian Bomers
+ * This class provide convenience methods like
+ *
+ * The
+ * You shouldn't use the ArrayList specific functions
+ * like those that take index parameters.
+ *
+ * It is not possible to add
+ * Currently, the methods equals(.,.) and matches(.,.) of
+ * class AudioFormats are used. Let's hope that they will
+ * be integrated into AudioFormat.
+ */
+
+public class AudioFormatSet extends ArraySet
+ * @see AudioFormats#matches(AudioFormat, AudioFormat)
+ */
+ public AudioFormat matches(AudioFormat elem) {
+ if (elem==null) {
+ return null;
+ }
+ Iterator it=iterator();
+ while (it.hasNext()) {
+ AudioFormat thisElem=(AudioFormat) it.next();
+ if (AudioFormats.matches(elem, thisElem)) {
+ return thisElem;
+ }
+ }
+ return null;
+ }
+
+
+ // $$mp: TODO: remove; should be obsolete
+ public AudioFormat[] toAudioFormatArray() {
+ return (AudioFormat[]) toArray(EMPTY_FORMAT_ARRAY);
+ }
+
+
+ public void add(int index, AudioFormat element) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ public AudioFormat set(int index, AudioFormat element) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+}
+
+/*** AudioFormatSet.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/AudioFormats.java b/songdbj/org/tritonus/share/sampled/AudioFormats.java
new file mode 100644
index 0000000000..41ac3f37c7
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/AudioFormats.java
@@ -0,0 +1,131 @@
+/*
+ * AudioFormats.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999,2000 by Matthias Pfisterer
+ * Copyright (c) 1999 by Florian Bomers
+ * This is a proposition to be used as AudioFormat.equals.
+ * It can therefore be considered as a temporary workaround.
+ */
+ public static boolean equals(AudioFormat format1,
+ AudioFormat format2)
+ {
+ return format1.getEncoding().equals(format2.getEncoding())
+ && format1.getChannels() == format2.getChannels()
+ && format1.getSampleSizeInBits() == format2.getSampleSizeInBits()
+ && format1.getFrameSize() == format2.getFrameSize()
+ && (Math.abs(format1.getSampleRate() - format2.getSampleRate()) < 1.0e-9)
+ && (Math.abs(format1.getFrameRate() - format2.getFrameRate()) < 1.0e-9);
+ }
+
+}
+
+
+
+/*** AudioFormats.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/AudioSystemShadow.java b/songdbj/org/tritonus/share/sampled/AudioSystemShadow.java
new file mode 100644
index 0000000000..70b4e9ebd7
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/AudioSystemShadow.java
@@ -0,0 +1,115 @@
+/*
+ * AudioSystemShadow.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999, 2000 by Matthias Pfisterer
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioFormat;
+
+import org.tritonus.share.sampled.file.AudioOutputStream;
+import org.tritonus.share.sampled.file.TDataOutputStream;
+import org.tritonus.share.sampled.file.TSeekableDataOutputStream;
+import org.tritonus.share.sampled.file.TNonSeekableDataOutputStream;
+import org.tritonus.sampled.file.AiffAudioOutputStream;
+import org.tritonus.sampled.file.AuAudioOutputStream;
+import org.tritonus.sampled.file.WaveAudioOutputStream;
+
+
+
+/** Experminatal area for AudioSystem.
+ * This class is used to host features that may become part of the
+ * Java Sound API (In which case they will be moved to AudioSystem).
+ */
+public class AudioSystemShadow
+{
+ public static TDataOutputStream getDataOutputStream(File file)
+ throws IOException
+ {
+ return new TSeekableDataOutputStream(file);
+ }
+
+
+
+ public static TDataOutputStream getDataOutputStream(OutputStream stream)
+ throws IOException
+ {
+ return new TNonSeekableDataOutputStream(stream);
+ }
+
+
+
+ // TODO: lLengthInBytes actually should be lLengthInFrames (design problem of A.O.S.)
+ public static AudioOutputStream getAudioOutputStream(AudioFileFormat.Type type, AudioFormat audioFormat, long lLengthInBytes, TDataOutputStream dataOutputStream)
+ {
+ AudioOutputStream audioOutputStream = null;
+
+ if (type.equals(AudioFileFormat.Type.AIFF) ||
+ type.equals(AudioFileFormat.Type.AIFF))
+ {
+ audioOutputStream = new AiffAudioOutputStream(audioFormat, type, lLengthInBytes, dataOutputStream);
+ }
+ else if (type.equals(AudioFileFormat.Type.AU))
+ {
+ audioOutputStream = new AuAudioOutputStream(audioFormat, lLengthInBytes, dataOutputStream);
+ }
+ else if (type.equals(AudioFileFormat.Type.WAVE))
+ {
+ audioOutputStream = new WaveAudioOutputStream(audioFormat, lLengthInBytes, dataOutputStream);
+ }
+ return audioOutputStream;
+ }
+
+
+
+ public static AudioOutputStream getAudioOutputStream(AudioFileFormat.Type type, AudioFormat audioFormat, long lLengthInBytes, File file)
+ throws IOException
+ {
+ TDataOutputStream dataOutputStream = getDataOutputStream(file);
+ AudioOutputStream audioOutputStream = getAudioOutputStream(type, audioFormat, lLengthInBytes, dataOutputStream);
+ return audioOutputStream;
+ }
+
+
+
+ public static AudioOutputStream getAudioOutputStream(AudioFileFormat.Type type, AudioFormat audioFormat, long lLengthInBytes, OutputStream outputStream)
+ throws IOException
+ {
+ TDataOutputStream dataOutputStream = getDataOutputStream(outputStream);
+ AudioOutputStream audioOutputStream = getAudioOutputStream(type, audioFormat, lLengthInBytes, dataOutputStream);
+ return audioOutputStream;
+ }
+}
+
+
+/*** AudioSystemShadow.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/AudioUtils.java b/songdbj/org/tritonus/share/sampled/AudioUtils.java
new file mode 100644
index 0000000000..21c838b032
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/AudioUtils.java
@@ -0,0 +1,181 @@
+/*
+ * AudioUtils.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Matthias Pfisterer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.spi.AudioFileWriter;
+
+import org.tritonus.share.TDebug;
+import org.tritonus.share.sampled.TConversionTool;
+
+
+
+public class AudioUtils
+{
+ public static long getLengthInBytes(AudioInputStream audioInputStream)
+ {
+ return getLengthInBytes(audioInputStream.getFormat(),
+ audioInputStream.getFrameLength());
+/*
+ long lLengthInFrames = audioInputStream.getFrameLength();
+ int nFrameSize = audioInputStream.getFormat().getFrameSize();
+ if (lLengthInFrames >= 0 && nFrameSize >= 1)
+ {
+ return lLengthInFrames * nFrameSize;
+ }
+ else
+ {
+ return AudioSystem.NOT_SPECIFIED;
+ }
+*/
+ }
+
+
+
+ /**
+ * if the passed value for lLength is
+ * AudioSystem.NOT_SPECIFIED (unknown
+ * length), the length in bytes becomes
+ * AudioSystem.NOT_SPECIFIED, too.
+ */
+ public static long getLengthInBytes(AudioFormat audioFormat,
+ long lLengthInFrames)
+ {
+ int nFrameSize = audioFormat.getFrameSize();
+ if (lLengthInFrames >= 0 && nFrameSize >= 1)
+ {
+ return lLengthInFrames * nFrameSize;
+ }
+ else
+ {
+ return AudioSystem.NOT_SPECIFIED;
+ }
+ }
+
+
+
+ public static boolean containsFormat(AudioFormat sourceFormat,
+ Iterator possibleFormats)
+ {
+ while (possibleFormats.hasNext())
+ {
+ AudioFormat format = (AudioFormat) possibleFormats.next();
+ if (AudioFormats.matches(format, sourceFormat))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Conversion milliseconds -> bytes
+ */
+
+ public static long millis2Bytes(long ms, AudioFormat format) {
+ return millis2Bytes(ms, format.getFrameRate(), format.getFrameSize());
+ }
+
+ public static long millis2Bytes(long ms, float frameRate, int frameSize) {
+ return (long) (ms*frameRate/1000*frameSize);
+ }
+
+ /**
+ * Conversion milliseconds -> bytes (bytes will be frame-aligned)
+ */
+ public static long millis2BytesFrameAligned(long ms, AudioFormat format) {
+ return millis2BytesFrameAligned(ms, format.getFrameRate(), format.getFrameSize());
+ }
+
+ public static long millis2BytesFrameAligned(long ms, float frameRate, int frameSize) {
+ return ((long) (ms*frameRate/1000))*frameSize;
+ }
+
+ /**
+ * Conversion milliseconds -> frames
+ */
+ public static long millis2Frames(long ms, AudioFormat format) {
+ return millis2Frames(ms, format.getFrameRate());
+ }
+
+ public static long millis2Frames(long ms, float frameRate) {
+ return (long) (ms*frameRate/1000);
+ }
+
+ /**
+ * Conversion bytes -> milliseconds
+ */
+ public static long bytes2Millis(long bytes, AudioFormat format) {
+ return (long) (bytes/format.getFrameRate()*1000/format.getFrameSize());
+ }
+
+ /**
+ * Conversion frames -> milliseconds
+ */
+ public static long frames2Millis(long frames, AudioFormat format) {
+ return (long) (frames/format.getFrameRate()*1000);
+ }
+
+
+ //$$fb 2000-07-18: added these debugging functions
+ public static String NS_or_number(int number) {
+ return (number==AudioSystem.NOT_SPECIFIED)?"NOT_SPECIFIED":String.valueOf(number);
+ }
+ public static String NS_or_number(float number) {
+ return (number==AudioSystem.NOT_SPECIFIED)?"NOT_SPECIFIED":String.valueOf(number);
+ }
+
+ /**
+ * For debugging purposes.
+ */
+ public static String format2ShortStr(AudioFormat format) {
+ return format.getEncoding() + "-" +
+ NS_or_number(format.getChannels()) + "ch-" +
+ NS_or_number(format.getSampleSizeInBits()) + "bit-" +
+ NS_or_number(((int)format.getSampleRate())) + "Hz-"+
+ (format.isBigEndian() ? "be" : "le");
+ }
+
+}
+
+
+
+/*** AudioUtils.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/Encodings.java b/songdbj/org/tritonus/share/sampled/Encodings.java
new file mode 100644
index 0000000000..6b880d24d9
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/Encodings.java
@@ -0,0 +1,183 @@
+/*
+ * Encodings.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Florian Bomers
+ * A registry of standard encoding names will
+ * be maintained by the Tritonus team.
+ *
+ * In a specification request to JavaSoft, the static method
+ *
+ * Also, a specification request should be made to integrate
+ *
+ * Every file reader, file writer, and format converter
+ * provider should exclusively use this method for
+ * retrieving instances of
+ * This function should be AudioFormat.Encoding.equals and must
+ * be considered as a temporary work around until it flows into the
+ * JavaSound API.
+ */
+ // IDEA: create a special "NOT_SPECIFIED" encoding
+ // and a AudioFormat.Encoding.matches method.
+ public static boolean equals(AudioFormat.Encoding e1, AudioFormat.Encoding e2) {
+ return e2.toString().equals(e1.toString());
+ }
+
+
+ /**
+ * Returns all "supported" encodings.
+ * Supported means that it is possible to read or
+ * write files with this encoding, or that a converter
+ * accepts this encoding as source or target format.
+ *
+ * Currently, this method returns a best guess and
+ * the search algorithm is far from complete: with standard
+ * methods of AudioSystem, only the target encodings
+ * of the converters can be retrieved - neither
+ * the source encodings of converters nor the encodings
+ * of file readers and file writers cannot be retrieved.
+ */
+ public static AudioFormat.Encoding[] getEncodings() {
+ StringHashedSet iteratedSources=new StringHashedSet();
+ StringHashedSet retrievedTargets=new StringHashedSet();
+ Iterator sourceFormats=encodings.iterator();
+ while (sourceFormats.hasNext()) {
+ AudioFormat.Encoding source=(AudioFormat.Encoding) sourceFormats.next();
+ iterateEncodings(source, iteratedSources, retrievedTargets);
+ }
+ return (AudioFormat.Encoding[]) retrievedTargets.toArray(
+ new AudioFormat.Encoding[retrievedTargets.size()]);
+ }
+
+
+ private static void iterateEncodings(AudioFormat.Encoding source,
+ StringHashedSet iteratedSources,
+ StringHashedSet retrievedTargets) {
+ if (!iteratedSources.contains(source)) {
+ iteratedSources.add(source);
+ AudioFormat.Encoding[] targets=AudioSystem.getTargetEncodings(source);
+ for (int i=0; i
+ * It is supposed to be a replacement of the byte[] stream
+ * architecture of JavaSound, especially for chains of
+ * AudioInputStreams. Ideally, all involved AudioInputStreams
+ * handle reading into a FloatSampleBuffer.
+ *
+ * Specifications:
+ *
+ * When a cascade of AudioInputStreams use FloatSampleBuffer for
+ * processing, they may implement the interface FloatSampleInput.
+ * This signals that this stream may provide float buffers
+ * for reading. The data is not converted back to bytes,
+ * but stays in a single buffer that is passed from stream to stream.
+ * For that serves the read(FloatSampleBuffer) method, which is
+ * then used as replacement for the byte-based read functions of
+ * AudioInputStream.
+ * To summarize, here are some advantages when using a FloatSampleBuffer
+ * for streaming:
+ *
+ * Simple benchmarks showed that the processing requirements
+ * for the conversion to and from float is about the same as
+ * when converting it to shorts or ints without dithering,
+ * and significantly higher with dithering. An own implementation
+ * of a random number generator may improve this.
+ *
+ * "Lazy" deletion of samples and channels:
+ * Use the
+ * Note that the lazy mechanism implies that the arrays returned
+ * from
+ * As an example, consider a chain of converters that all act
+ * on the same instance of FloatSampleBuffer. Some converters
+ * may decrease the sample count (e.g. sample rate converter) and
+ * delete channels (e.g. PCM2PCM converter). So, processing of one
+ * block will decrease both. For the next block, all starts
+ * from the beginning. With the lazy mechanism, all float arrays
+ * are only created once for processing all blocks.
+ * Dithering: If If If
+ *
+ * Currently, the following bit sizes are supported:
+ *
+ * Only PCM formats are accepted. The method will convert all
+ * byte values from
+ *
+ * Only PCM formats are accepted. The method will convert all
+ * byte values from
+ *
+ * Only PCM formats are accepted. The method will convert all
+ * bytes from
+ *
+ * The
+ * For mono data, set
+ * E.g.:
+ * Only PCM formats are accepted. The method will convert all
+ * samples from
+ * Dithering should be used when the output resolution is significantly
+ * lower than the original resolution. This includes if the original
+ * data was 16-bit and it is now converted to 8-bit, or if the
+ * data was generated in the float domain. No dithering need to be used
+ * if the original sample data was in e.g. 8-bit and the resulting output
+ * data has a higher resolution. If dithering is used, a sensitive value
+ * is DEFAULT_DITHER_BITS.
+ *
+ * @param input a List of float arrays with the input audio data
+ * @param inOffset index in the input arrays where to start the conversion
+ * @param output the byte array that receives the converted audio data
+ * @param outByteOffset the start offset in
+ * Only PCM formats are accepted. The method will convert all
+ * samples from
+ * Dithering should be used when the output resolution is significantly
+ * lower than the original resolution. This includes if the original
+ * data was 16-bit and it is now converted to 8-bit, or if the
+ * data was generated in the float domain. No dithering need to be used
+ * if the original sample data was in e.g. 8-bit and the resulting output
+ * data has a higher resolution. If dithering is used, a sensitive value
+ * is DEFAULT_DITHER_BITS.
+ *
+ * @param input the audio data in normalized samples
+ * @param inOffset index in input where to start the conversion
+ * @param output the byte array that receives the converted audio data
+ * @param outByteOffset the start offset in
+ * Only PCM formats are accepted. The method will convert all
+ * samples from
+ * The
+ * For mono data, set
+ * E.g.: This is a reference function.
+ */
+ public static short bytesToShort16(byte highByte, byte lowByte) {
+ return (short) ((highByte<<8) | (lowByte & 0xFF));
+ }
+
+ /**
+ * Converts 2 successive bytes starting at
+ * For little endian, buffer[byteOffset] is interpreted as low byte,
+ * whereas it is interpreted as high byte in big endian.
+ * This is a reference function.
+ */
+ public static short bytesToShort16(byte[] buffer, int byteOffset, boolean bigEndian) {
+ return bigEndian?
+ ((short) ((buffer[byteOffset]<<8) | (buffer[byteOffset+1] & 0xFF))):
+ ((short) ((buffer[byteOffset+1]<<8) | (buffer[byteOffset] & 0xFF)));
+ }
+
+ /**
+ * Converts 2 bytes to a signed integer sample with 16bit range.
+ * This is a reference function.
+ */
+ public static int bytesToInt16(byte highByte, byte lowByte) {
+ return (highByte<<8) | (lowByte & 0xFF);
+ }
+
+ /**
+ * Converts 2 successive bytes starting at
+ * For little endian, buffer[byteOffset] is interpreted as low byte,
+ * whereas it is interpreted as high byte in big endian.
+ * This is a reference function.
+ */
+ public static int bytesToInt16(byte[] buffer, int byteOffset, boolean bigEndian) {
+ return bigEndian?
+ ((buffer[byteOffset]<<8) | (buffer[byteOffset+1] & 0xFF)):
+ ((buffer[byteOffset+1]<<8) | (buffer[byteOffset] & 0xFF));
+ }
+
+ /**
+ * Converts 3 successive bytes starting at
+ * For little endian, buffer[byteOffset] is interpreted as lowest byte,
+ * whereas it is interpreted as highest byte in big endian.
+ * This is a reference function.
+ */
+ public static int bytesToInt24(byte[] buffer, int byteOffset, boolean bigEndian) {
+ return bigEndian?
+ ((buffer[byteOffset]<<16) // let Java handle sign-bit
+ | ((buffer[byteOffset+1] & 0xFF)<<8) // inhibit sign-bit handling
+ | (buffer[byteOffset+2] & 0xFF)):
+ ((buffer[byteOffset+2]<<16) // let Java handle sign-bit
+ | ((buffer[byteOffset+1] & 0xFF)<<8) // inhibit sign-bit handling
+ | (buffer[byteOffset] & 0xFF));
+ }
+
+ /**
+ * Converts a 4 successive bytes starting at
+ * For little endian, buffer[byteOffset] is interpreted as lowest byte,
+ * whereas it is interpreted as highest byte in big endian.
+ * This is a reference function.
+ */
+ public static int bytesToInt32(byte[] buffer, int byteOffset, boolean bigEndian) {
+ return bigEndian?
+ ((buffer[byteOffset]<<24) // let Java handle sign-bit
+ | ((buffer[byteOffset+1] & 0xFF)<<16) // inhibit sign-bit handling
+ | ((buffer[byteOffset+2] & 0xFF)<<8) // inhibit sign-bit handling
+ | (buffer[byteOffset+3] & 0xFF)):
+ ((buffer[byteOffset+3]<<24) // let Java handle sign-bit
+ | ((buffer[byteOffset+2] & 0xFF)<<16) // inhibit sign-bit handling
+ | ((buffer[byteOffset+1] & 0xFF)<<8) // inhibit sign-bit handling
+ | (buffer[byteOffset] & 0xFF));
+ }
+
+
+ /**
+ * Converts a sample of type
+ * For little endian, buffer[byteOffset] is filled with low byte of sample,
+ * and buffer[byteOffset+1] is filled with high byte of sample.
+ * For big endian, this is reversed.
+ * This is a reference function.
+ */
+ public static void shortToBytes16(short sample, byte[] buffer, int byteOffset, boolean bigEndian) {
+ intToBytes16(sample, buffer, byteOffset, bigEndian);
+ }
+
+ /**
+ * Converts a 16 bit sample of type
+ * For little endian, buffer[byteOffset] is filled with low byte of sample,
+ * and buffer[byteOffset+1] is filled with high byte of sample + sign bit.
+ * For big endian, this is reversed.
+ * Before calling this function, it should be assured that This is a reference function.
+ */
+ public static void intToBytes16(int sample, byte[] buffer, int byteOffset, boolean bigEndian) {
+ if (bigEndian) {
+ buffer[byteOffset++]=(byte) (sample >> 8);
+ buffer[byteOffset]=(byte) (sample & 0xFF);
+ } else {
+ buffer[byteOffset++]=(byte) (sample & 0xFF);
+ buffer[byteOffset]=(byte) (sample >> 8);
+ }
+ }
+
+ /**
+ * Converts a 24 bit sample of type
+ * For little endian, buffer[byteOffset] is filled with low byte of sample,
+ * and buffer[byteOffset+2] is filled with the high byte of sample + sign bit.
+ * For big endian, this is reversed.
+ * Before calling this function, it should be assured that This is a reference function.
+ */
+ public static void intToBytes24(int sample, byte[] buffer, int byteOffset, boolean bigEndian) {
+ if (bigEndian) {
+ buffer[byteOffset++]=(byte) (sample >> 16);
+ buffer[byteOffset++]=(byte) ((sample >>> 8) & 0xFF);
+ buffer[byteOffset]=(byte) (sample & 0xFF);
+ } else {
+ buffer[byteOffset++]=(byte) (sample & 0xFF);
+ buffer[byteOffset++]=(byte) ((sample >>> 8) & 0xFF);
+ buffer[byteOffset]=(byte) (sample >> 16);
+ }
+ }
+
+
+ /**
+ * Converts a 32 bit sample of type
+ * For little endian, buffer[byteOffset] is filled with lowest byte of sample,
+ * and buffer[byteOffset+3] is filled with the high byte of sample + sign bit.
+ * For big endian, this is reversed.
+ * This is a reference function.
+ */
+ public static void intToBytes32(int sample, byte[] buffer, int byteOffset, boolean bigEndian) {
+ if (bigEndian) {
+ buffer[byteOffset++]=(byte) (sample >> 24);
+ buffer[byteOffset++]=(byte) ((sample >>> 16) & 0xFF);
+ buffer[byteOffset++]=(byte) ((sample >>> 8) & 0xFF);
+ buffer[byteOffset]=(byte) (sample & 0xFF);
+ } else {
+ buffer[byteOffset++]=(byte) (sample & 0xFF);
+ buffer[byteOffset++]=(byte) ((sample >>> 8) & 0xFF);
+ buffer[byteOffset++]=(byte) ((sample >>> 16) & 0xFF);
+ buffer[byteOffset]=(byte) (sample >> 24);
+ }
+ }
+
+
+ /////////////////////// ULAW ///////////////////////////////////////////
+
+ private static final boolean ZEROTRAP=true;
+ private static final short BIAS=0x84;
+ private static final int CLIP=32635;
+ private static final int exp_lut1[] ={
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+ };
+
+
+ /**
+ * Converts a linear signed 16bit sample to a uLaw byte.
+ * Ported to Java by fb.
+ * It is assumed that each source format can be encoded to all
+ * target formats.
+ * In the sourceFormats and targetFormats collections that are passed to
+ * the constructor of this class, fields may be set to AudioSystem.NOT_SPECIFIED.
+ * This means that it handles all values of that field, but cannot change it.
+ * This class prevents that a conversion is done (e.g. for sample rates),
+ * because the overriding class specified AudioSystem.NOT_SPECIFIED as sample rate,
+ * meaning it handles all sample rates.
+ * Overriding classes must implement at least
+ * When conversion is supported, for every target encoding,
+ * the fields sample size in bits, channels and sample rate are checked:
+ *
+ * This method is overwritten here to make use of org.tritonus.share.sampled.AudioFormats.matches
+ * and is considered temporary until AudioFormat.matches is corrected in the JavaSound API.
+ */
+ /* $$mp: if we decide to use getMatchingFormat(), this method should be
+ implemented by simply calling getMatchingFormat() and comparing the
+ result against null.
+ */
+ public boolean isConversionSupported(
+ AudioFormat targetFormat,
+ AudioFormat sourceFormat)
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out(">TFormatConversionProvider.isConversionSupported(AudioFormat, AudioFormat):");
+ TDebug.out("class: "+getClass().getName());
+ TDebug.out("checking if conversion possible");
+ TDebug.out("from: " + sourceFormat);
+ TDebug.out("to: " + targetFormat);
+ }
+ AudioFormat[] aTargetFormats = getTargetFormats(targetFormat.getEncoding(), sourceFormat);
+ for (int i = 0; i < aTargetFormats.length; i++)
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out("checking against possible target format: " + aTargetFormats[i]);
+ }
+ if (aTargetFormats[i] != null
+ && AudioFormats.matches(aTargetFormats[i], targetFormat))
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out("
+ * This method is overwritten here to make use of org.tritonus.share.sampled.AudioFormats.matches
+ * and is considered temporary until AudioFormat.matches is corrected in the JavaSound API.
+ */
+ public AudioFormat getMatchingFormat(
+ AudioFormat targetFormat,
+ AudioFormat sourceFormat)
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out(">TFormatConversionProvider.isConversionSupported(AudioFormat, AudioFormat):");
+ TDebug.out("class: "+getClass().getName());
+ TDebug.out("checking if conversion possible");
+ TDebug.out("from: " + sourceFormat);
+ TDebug.out("to: " + targetFormat);
+ }
+ AudioFormat[] aTargetFormats = getTargetFormats(targetFormat.getEncoding(), sourceFormat);
+ for (int i = 0; i < aTargetFormats.length; i++)
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out("checking against possible target format: " + aTargetFormats[i]);
+ }
+ if (aTargetFormats[i] != null
+ && AudioFormats.matches(aTargetFormats[i], targetFormat))
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out(" Overriding classes must
+ * provide a constructor that calls the protected constructor of this class and override
+ *
+ * If any of the fields is AudioSystem.NOT_SPECIFIED in both
+ * This method uses
+ * This method isn't used in TSimpleFormatConversionProvider - it is solely there
+ * for inheriting classes.
+ */
+ //$$fb 2000-08-16: moved from TEncodingFormatConversionProvider
+ protected AudioFormat replaceNotSpecified(AudioFormat sourceFormat, AudioFormat targetFormat) {
+ boolean bSetSampleSize=false;
+ boolean bSetChannels=false;
+ boolean bSetSampleRate=false;
+ boolean bSetFrameRate=false;
+ if (targetFormat.getSampleSizeInBits()==AudioSystem.NOT_SPECIFIED
+ && sourceFormat.getSampleSizeInBits()!=AudioSystem.NOT_SPECIFIED) {
+ bSetSampleSize=true;
+ }
+ if (targetFormat.getChannels()==AudioSystem.NOT_SPECIFIED
+ && sourceFormat.getChannels()!=AudioSystem.NOT_SPECIFIED) {
+ bSetChannels=true;
+ }
+ if (targetFormat.getSampleRate()==AudioSystem.NOT_SPECIFIED
+ && sourceFormat.getSampleRate()!=AudioSystem.NOT_SPECIFIED) {
+ bSetSampleRate=true;
+ }
+ if (targetFormat.getFrameRate()==AudioSystem.NOT_SPECIFIED
+ && sourceFormat.getFrameRate()!=AudioSystem.NOT_SPECIFIED) {
+ bSetFrameRate=true;
+ }
+ if (bSetSampleSize || bSetChannels || bSetSampleRate || bSetFrameRate
+ || (targetFormat.getFrameSize()==AudioSystem.NOT_SPECIFIED
+ && sourceFormat.getFrameSize()!=AudioSystem.NOT_SPECIFIED)) {
+ // create new format in place of the original target format
+ float sampleRate=bSetSampleRate?
+ sourceFormat.getSampleRate():targetFormat.getSampleRate();
+ float frameRate=bSetFrameRate?
+ sourceFormat.getFrameRate():targetFormat.getFrameRate();
+ int sampleSize=bSetSampleSize?
+ sourceFormat.getSampleSizeInBits():targetFormat.getSampleSizeInBits();
+ int channels=bSetChannels?
+ sourceFormat.getChannels():targetFormat.getChannels();
+ int frameSize=getFrameSize(
+ targetFormat.getEncoding(),
+ sampleRate,
+ sampleSize,
+ channels,
+ frameRate,
+ targetFormat.isBigEndian(),
+ targetFormat.getFrameSize());
+ targetFormat= new AudioFormat(
+ targetFormat.getEncoding(),
+ sampleRate,
+ sampleSize,
+ channels,
+ frameSize,
+ frameRate,
+ targetFormat.isBigEndian());
+ }
+ return targetFormat;
+ }
+
+ /**
+ * Calculates the frame size for the given format description.
+ * The default implementation returns AudioSystem.NOT_SPECIFIED
+ * if either
+ * If this does not reflect the way to calculate the right frame size,
+ * inheriting classes should overwrite this method if they use
+ * replaceNotSpecified(...). It is not used elsewhere in this class.
+ */
+ //$$fb 2000-08-16: added
+ protected int getFrameSize(
+ AudioFormat.Encoding encoding,
+ float sampleRate,
+ int sampleSize,
+ int channels,
+ float frameRate,
+ boolean bigEndian,
+ int oldFrameSize) {
+ if (sampleSize==AudioSystem.NOT_SPECIFIED || channels==AudioSystem.NOT_SPECIFIED) {
+ return AudioSystem.NOT_SPECIFIED;
+ }
+ return sampleSize*channels/8;
+ }
+
+
+
+}
+
+/*** TSimpleFormatConversionProvider.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java b/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java
new file mode 100644
index 0000000000..8a588e5c3e
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java
@@ -0,0 +1,271 @@
+/*
+ * TSynchronousFilteredAudioInputStream.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999,2000 by Florian Bomers Base classes for the implementation of FormatConversionProviders.
+ The classes provided here . It provides often-used functionality and the new architecture using
+ * an AudioOutputStream.
+ * There should be only one set of audio formats supported by any given
+ * class of TAudioFileWriter. This class assumes implicitely that all
+ * supported file types have a common set of audio formats they can handle.
+ *
+ * @author Matthias Pfisterer
+ * @author Florian Bomers
+ */
+
+public abstract class TAudioFileWriter
+extends AudioFileWriter
+{
+ protected static final int ALL = AudioSystem.NOT_SPECIFIED;
+
+ public static AudioFormat.Encoding PCM_SIGNED=new AudioFormat.Encoding("PCM_SIGNED");
+ public static AudioFormat.Encoding PCM_UNSIGNED=new AudioFormat.Encoding("PCM_UNSIGNED");
+
+ /** Buffer length for the loop in the write() method.
+ * This is in bytes. Perhaps it should be in frames to give an
+ * equal amount of latency.
+ */
+ private static final int BUFFER_LENGTH = 16384;
+
+ // only needed for Collection.toArray()
+ protected static final AudioFileFormat.Type[] NULL_TYPE_ARRAY = new AudioFileFormat.Type[0];
+
+
+ /** The audio file types (AudioFileFormat.Type) that can be
+ * handled by the AudioFileWriter.
+ */
+ private Collection
+ * This is the central method for checking if a FORMAT is supported.
+ * Inheriting classes can overwrite this for performance
+ * or to exclude/include special type/format combinations.
+ *
+ * This method is only called when the Like that it is possible to write to a file without knowing
+ * the length before.
+ *
+ * @author Florian Bomers
+ */
+public interface TDataOutputStream
+extends DataOutput
+{
+ public boolean supportsSeek();
+
+
+
+ public void seek(long position)
+ throws IOException;
+
+
+
+ public long getFilePointer()
+ throws IOException;
+
+
+
+ public long length()
+ throws IOException;
+
+
+ public void writeLittleEndian32(int value)
+ throws IOException;
+
+
+ public void writeLittleEndian16(short value)
+ throws IOException;
+
+ public void close()
+ throws IOException;
+}
+
+
+
+/*** TDataOutputStream.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/file/THeaderlessAudioFileWriter.java b/songdbj/org/tritonus/share/sampled/file/THeaderlessAudioFileWriter.java
new file mode 100644
index 0000000000..a9d76de505
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/file/THeaderlessAudioFileWriter.java
@@ -0,0 +1,84 @@
+/*
+ * THeaderlessAudioFileWriter.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Florian Bomers Base classes for the implementation of AudioFileReaders and AudioFileWriters.
+ The classes provided here .Equalizer
instance.
+ */
+ public Equalizer()
+ {
+ }
+
+// private Equalizer(float b1, float b2, float b3, float b4, float b5,
+// float b6, float b7, float b8, float b9, float b10, float b11,
+// float b12, float b13, float b14, float b15, float b16,
+// float b17, float b18, float b19, float b20);
+
+ public Equalizer(float[] settings)
+ {
+ setFrom(settings);
+ }
+
+ public Equalizer(EQFunction eq)
+ {
+ setFrom(eq);
+ }
+
+ public void setFrom(float[] eq)
+ {
+ reset();
+ int max = (eq.length > BANDS) ? BANDS : eq.length;
+
+ for (int i=0; iInputStreamSource
implements a
+ * Source
that provides data from an InputStream
+ *
. Seeking functionality is not supported.
+ *
+ * @author MDM
+ */
+public class InputStreamSource implements Source
+{
+ private final InputStream in;
+
+ public InputStreamSource(InputStream in)
+ {
+ if (in==null)
+ throw new NullPointerException("in");
+
+ this.in = in;
+ }
+
+ public int read(byte[] b, int offs, int len)
+ throws IOException
+ {
+ int read = in.read(b, offs, len);
+ return read;
+ }
+
+ public boolean willReadBlock()
+ {
+ return true;
+ //boolean block = (in.available()==0);
+ //return block;
+ }
+
+ public boolean isSeekable()
+ {
+ return false;
+ }
+
+ public long tell()
+ {
+ return -1;
+ }
+
+ public long seek(long to)
+ {
+ return -1;
+ }
+
+ public long length()
+ {
+ return -1;
+ }
+}
diff --git a/songdbj/javazoom/jl/decoder/JavaLayerError.java b/songdbj/javazoom/jl/decoder/JavaLayerError.java
new file mode 100644
index 0000000000..d9910bcc71
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/JavaLayerError.java
@@ -0,0 +1,31 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Work in progress.
+ *
+ * API usage errors may be handled by throwing an instance of this
+ * class, as per JMF 2.0.
+ */
+public class JavaLayerError extends Error
+{
+}
diff --git a/songdbj/javazoom/jl/decoder/JavaLayerErrors.java b/songdbj/javazoom/jl/decoder/JavaLayerErrors.java
new file mode 100644
index 0000000000..3b9c2ff9d7
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/JavaLayerErrors.java
@@ -0,0 +1,40 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Exception erorr codes for components of the JavaLayer API.
+ */
+public interface JavaLayerErrors
+{
+ /**
+ * The first bitstream error code. See the {@link DecoderErrors DecoderErrors}
+ * interface for other bitstream error codes.
+ */
+ static public final int BITSTREAM_ERROR = 0x100;
+
+ /**
+ * The first decoder error code. See the {@link DecoderErrors DecoderErrors}
+ * interface for other decoder error codes.
+ */
+ static public final int DECODER_ERROR = 0x200;
+
+}
diff --git a/songdbj/javazoom/jl/decoder/JavaLayerException.java b/songdbj/javazoom/jl/decoder/JavaLayerException.java
new file mode 100644
index 0000000000..e7a50a8340
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/JavaLayerException.java
@@ -0,0 +1,80 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+import java.io.PrintStream;
+
+
+/**
+ * The JavaLayerException is the base class for all API-level
+ * exceptions thrown by JavaLayer. To facilitate conversion and
+ * common handling of exceptions from other domains, the class
+ * can delegate some functionality to a contained Throwable instance.
+ * JavaLayerHooks
class allows developers to change
+ * the way the JavaLayer library uses Resources.
+ */
+
+public interface JavaLayerHook
+{
+ /**
+ * Retrieves the named resource. This allows resources to be
+ * obtained without specifying how they are retrieved.
+ */
+ public InputStream getResourceAsStream(String name);
+}
diff --git a/songdbj/javazoom/jl/decoder/JavaLayerUtils.java b/songdbj/javazoom/jl/decoder/JavaLayerUtils.java
new file mode 100644
index 0000000000..c9ce3838e5
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/JavaLayerUtils.java
@@ -0,0 +1,207 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 12/12/99 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+
+/**
+ * The JavaLayerUtils class is not strictly part of the JavaLayer API.
+ * It serves to provide useful methods and system-wide hooks.
+ *
+ * @author MDM
+ */
+public class JavaLayerUtils
+{
+ static private JavaLayerHook hook = null;
+
+ /**
+ * Deserializes the object contained in the given input stream.
+ * @param in The input stream to deserialize an object from.
+ * @param cls The expected class of the deserialized object.
+ */
+ static public Object deserialize(InputStream in, Class cls)
+ throws IOException
+ {
+ if (cls==null)
+ throw new NullPointerException("cls");
+
+ Object obj = deserialize(in, cls);
+ if (!cls.isInstance(obj))
+ {
+ throw new InvalidObjectException("type of deserialized instance not of required class.");
+ }
+
+ return obj;
+ }
+
+ /**
+ * Deserializes an object from the given InputStream
.
+ * The deserialization is delegated to an
+ * ObjectInputStream
instance.
+ *
+ * @param in The InputStream
to deserialize an object
+ * from.
+ *
+ * @return The object deserialized from the stream.
+ * @exception IOException is thrown if there was a problem reading
+ * the underlying stream, or an object could not be deserialized
+ * from the stream.
+ *
+ * @see java.io.ObjectInputStream
+ */
+ static public Object deserialize(InputStream in)
+ throws IOException
+ {
+ if (in==null)
+ throw new NullPointerException("in");
+
+ ObjectInputStream objIn = new ObjectInputStream(in);
+
+ Object obj;
+
+ try
+ {
+ obj = objIn.readObject();
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new InvalidClassException(ex.toString());
+ }
+
+ return obj;
+ }
+
+ /**
+ * Deserializes an array from a given InputStream
.
+ *
+ * @param in The InputStream
to
+ * deserialize an object from.
+ *
+ * @param elemType The class denoting the type of the array
+ * elements.
+ * @param length The expected length of the array, or -1 if
+ * any length is expected.
+ */
+ static public Object deserializeArray(InputStream in, Class elemType, int length)
+ throws IOException
+ {
+ if (elemType==null)
+ throw new NullPointerException("elemType");
+
+ if (length<-1)
+ throw new IllegalArgumentException("length");
+
+ Object obj = deserialize(in);
+
+ Class cls = obj.getClass();
+
+
+ if (!cls.isArray())
+ throw new InvalidObjectException("object is not an array");
+
+ Class arrayElemType = cls.getComponentType();
+ if (arrayElemType!=elemType)
+ throw new InvalidObjectException("unexpected array component type");
+
+ if (length != -1)
+ {
+ int arrayLength = Array.getLength(obj);
+ if (arrayLength!=length)
+ throw new InvalidObjectException("array length mismatch");
+ }
+
+ return obj;
+ }
+
+ static public Object deserializeArrayResource(String name, Class elemType, int length)
+ throws IOException
+ {
+ InputStream str = getResourceAsStream(name);
+ if (str==null)
+ throw new IOException("unable to load resource '"+name+"'");
+
+ Object obj = deserializeArray(str, elemType, length);
+
+ return obj;
+ }
+
+ static public void serialize(OutputStream out, Object obj)
+ throws IOException
+ {
+ if (out==null)
+ throw new NullPointerException("out");
+
+ if (obj==null)
+ throw new NullPointerException("obj");
+
+ ObjectOutputStream objOut = new ObjectOutputStream(out);
+ objOut.writeObject(obj);
+
+ }
+
+ /**
+ * Sets the system-wide JavaLayer hook.
+ */
+ static synchronized public void setHook(JavaLayerHook hook0)
+ {
+ hook = hook0;
+ }
+
+ static synchronized public JavaLayerHook getHook()
+ {
+ return hook;
+ }
+
+ /**
+ * Retrieves an InputStream for a named resource.
+ *
+ * @param name The name of the resource. This must be a simple
+ * name, and not a qualified package name.
+ *
+ * @return The InputStream for the named resource, or null if
+ * the resource has not been found. If a hook has been
+ * provided, its getResourceAsStream() method is called
+ * to retrieve the resource.
+ */
+ static synchronized public InputStream getResourceAsStream(String name)
+ {
+ InputStream is = null;
+
+ if (hook!=null)
+ {
+ is = hook.getResourceAsStream(name);
+ }
+ else
+ {
+ Class cls = JavaLayerUtils.class;
+ is = cls.getResourceAsStream(name);
+ }
+
+ return is;
+ }
+}
diff --git a/songdbj/javazoom/jl/decoder/LayerIDecoder.java b/songdbj/javazoom/jl/decoder/LayerIDecoder.java
new file mode 100644
index 0000000000..b633dd2403
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/LayerIDecoder.java
@@ -0,0 +1,444 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 12/12/99 Initial version. Adapted from javalayer.java
+ * and Subband*.java. mdm@techie.com
+ *
+ * 02/28/99 Initial version : javalayer.java by E.B
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Implements decoding of MPEG Audio Layer I frames.
+ */
+class LayerIDecoder implements FrameDecoder
+{
+ protected Bitstream stream;
+ protected Header header;
+ protected SynthesisFilter filter1, filter2;
+ protected Obuffer buffer;
+ protected int which_channels;
+ protected int mode;
+
+ protected int num_subbands;
+ protected Subband[] subbands;
+ protected Crc16 crc = null; // new Crc16[1] to enable CRC checking.
+
+ public LayerIDecoder()
+ {
+ crc = new Crc16();
+ }
+
+ public void create(Bitstream stream0, Header header0,
+ SynthesisFilter filtera, SynthesisFilter filterb,
+ Obuffer buffer0, int which_ch0)
+ {
+ stream = stream0;
+ header = header0;
+ filter1 = filtera;
+ filter2 = filterb;
+ buffer = buffer0;
+ which_channels = which_ch0;
+
+ }
+
+
+
+ public void decodeFrame()
+ {
+
+ num_subbands = header.number_of_subbands();
+ subbands = new Subband[32];
+ mode = header.mode();
+
+ createSubbands();
+
+ readAllocation();
+ readScaleFactorSelection();
+
+ if ((crc != null) || header.checksum_ok())
+ {
+ readScaleFactors();
+
+ readSampleData();
+ }
+
+ }
+
+ protected void createSubbands()
+ {
+ int i;
+ if (mode == Header.SINGLE_CHANNEL)
+ for (i = 0; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer1(i);
+ else if (mode == Header.JOINT_STEREO)
+ {
+ for (i = 0; i < header.intensity_stereo_bound(); ++i)
+ subbands[i] = new SubbandLayer1Stereo(i);
+ for (; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer1IntensityStereo(i);
+ }
+ else
+ {
+ for (i = 0; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer1Stereo(i);
+ }
+ }
+
+ protected void readAllocation()
+ {
+ // start to read audio data:
+ for (int i = 0; i < num_subbands; ++i)
+ subbands[i].read_allocation(stream, header, crc);
+
+ }
+
+ protected void readScaleFactorSelection()
+ {
+ // scale factor selection not present for layer I.
+ }
+
+ protected void readScaleFactors()
+ {
+ for (int i = 0; i < num_subbands; ++i)
+ subbands[i].read_scalefactor(stream, header);
+ }
+
+ protected void readSampleData()
+ {
+ boolean read_ready = false;
+ boolean write_ready = false;
+ int mode = header.mode();
+ int i;
+ do
+ {
+ for (i = 0; i < num_subbands; ++i)
+ read_ready = subbands[i].read_sampledata(stream);
+ do
+ {
+ for (i = 0; i < num_subbands; ++i)
+ write_ready = subbands[i].put_next_sample(which_channels,filter1, filter2);
+
+ filter1.calculate_pcm_samples(buffer);
+ if ((which_channels == OutputChannels.BOTH_CHANNELS) && (mode != Header.SINGLE_CHANNEL))
+ filter2.calculate_pcm_samples(buffer);
+ } while (!write_ready);
+ } while (!read_ready);
+
+ }
+
+ /**
+ * Abstract base class for subband classes of layer I and II
+ */
+ static abstract class Subband
+ {
+ /*
+ * Changes from version 1.1 to 1.2:
+ * - array size increased by one, although a scalefactor with index 63
+ * is illegal (to prevent segmentation faults)
+ */
+ // Scalefactors for layer I and II, Annex 3-B.1 in ISO/IEC DIS 11172:
+ public static final float scalefactors[] =
+ {
+ 2.00000000000000f, 1.58740105196820f, 1.25992104989487f, 1.00000000000000f,
+ 0.79370052598410f, 0.62996052494744f, 0.50000000000000f, 0.39685026299205f,
+ 0.31498026247372f, 0.25000000000000f, 0.19842513149602f, 0.15749013123686f,
+ 0.12500000000000f, 0.09921256574801f, 0.07874506561843f, 0.06250000000000f,
+ 0.04960628287401f, 0.03937253280921f, 0.03125000000000f, 0.02480314143700f,
+ 0.01968626640461f, 0.01562500000000f, 0.01240157071850f, 0.00984313320230f,
+ 0.00781250000000f, 0.00620078535925f, 0.00492156660115f, 0.00390625000000f,
+ 0.00310039267963f, 0.00246078330058f, 0.00195312500000f, 0.00155019633981f,
+ 0.00123039165029f, 0.00097656250000f, 0.00077509816991f, 0.00061519582514f,
+ 0.00048828125000f, 0.00038754908495f, 0.00030759791257f, 0.00024414062500f,
+ 0.00019377454248f, 0.00015379895629f, 0.00012207031250f, 0.00009688727124f,
+ 0.00007689947814f, 0.00006103515625f, 0.00004844363562f, 0.00003844973907f,
+ 0.00003051757813f, 0.00002422181781f, 0.00001922486954f, 0.00001525878906f,
+ 0.00001211090890f, 0.00000961243477f, 0.00000762939453f, 0.00000605545445f,
+ 0.00000480621738f, 0.00000381469727f, 0.00000302772723f, 0.00000240310869f,
+ 0.00000190734863f, 0.00000151386361f, 0.00000120155435f, 0.00000000000000f /* illegal scalefactor */
+ };
+
+ public abstract void read_allocation (Bitstream stream, Header header, Crc16 crc);
+ public abstract void read_scalefactor (Bitstream stream, Header header);
+ public abstract boolean read_sampledata (Bitstream stream);
+ public abstract boolean put_next_sample (int channels, SynthesisFilter filter1, SynthesisFilter filter2);
+ };
+
+ /**
+ * Class for layer I subbands in single channel mode.
+ * Used for single channel mode
+ * and in derived class for intensity stereo mode
+ */
+ static class SubbandLayer1 extends Subband
+ {
+
+ // Factors and offsets for sample requantization
+ public static final float table_factor[] = {
+ 0.0f, (1.0f/2.0f) * (4.0f/3.0f), (1.0f/4.0f) * (8.0f/7.0f), (1.0f/8.0f) * (16.0f/15.0f),
+ (1.0f/16.0f) * (32.0f/31.0f), (1.0f/32.0f) * (64.0f/63.0f), (1.0f/64.0f) * (128.0f/127.0f),
+ (1.0f/128.0f) * (256.0f/255.0f), (1.0f/256.0f) * (512.0f/511.0f),
+ (1.0f/512.0f) * (1024.0f/1023.0f), (1.0f/1024.0f) * (2048.0f/2047.0f),
+ (1.0f/2048.0f) * (4096.0f/4095.0f), (1.0f/4096.0f) * (8192.0f/8191.0f),
+ (1.0f/8192.0f) * (16384.0f/16383.0f), (1.0f/16384.0f) * (32768.0f/32767.0f)
+ };
+
+ public static final float table_offset[] = {
+ 0.0f, ((1.0f/2.0f)-1.0f) * (4.0f/3.0f), ((1.0f/4.0f)-1.0f) * (8.0f/7.0f), ((1.0f/8.0f)-1.0f) * (16.0f/15.0f),
+ ((1.0f/16.0f)-1.0f) * (32.0f/31.0f), ((1.0f/32.0f)-1.0f) * (64.0f/63.0f), ((1.0f/64.0f)-1.0f) * (128.0f/127.0f),
+ ((1.0f/128.0f)-1.0f) * (256.0f/255.0f), ((1.0f/256.0f)-1.0f) * (512.0f/511.0f),
+ ((1.0f/512.0f)-1.0f) * (1024.0f/1023.0f), ((1.0f/1024.0f)-1.0f) * (2048.0f/2047.0f),
+ ((1.0f/2048.0f)-1.0f) * (4096.0f/4095.0f), ((1.0f/4096.0f)-1.0f) * (8192.0f/8191.0f),
+ ((1.0f/8192.0f)-1.0f) * (16384.0f/16383.0f), ((1.0f/16384.0f)-1.0f) * (32768.0f/32767.0f)
+ };
+
+ protected int subbandnumber;
+ protected int samplenumber;
+ protected int allocation;
+ protected float scalefactor;
+ protected int samplelength;
+ protected float sample;
+ protected float factor, offset;
+
+ /**
+ * Construtor.
+ */
+ public SubbandLayer1(int subbandnumber)
+ {
+ this.subbandnumber = subbandnumber;
+ samplenumber = 0;
+ }
+
+ /**
+ *
+ */
+ public void read_allocation(Bitstream stream, Header header, Crc16 crc)
+ {
+ if ((allocation = stream.get_bits (4)) == 15) ;
+ // cerr << "WARNING: stream contains an illegal allocation!\n";
+ // MPEG-stream is corrupted!
+ if (crc != null)
+ crc.add_bits (allocation, 4);
+ if (allocation != 0)
+ {
+ samplelength = allocation + 1;
+ factor = table_factor[allocation];
+ offset = table_offset[allocation];
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor(Bitstream stream, Header header)
+ {
+ if (allocation != 0) scalefactor = scalefactors[stream.get_bits(6)];
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata(Bitstream stream)
+ {
+ if (allocation != 0)
+ {
+ sample = (float) (stream.get_bits(samplelength));
+ }
+ if (++samplenumber == 12)
+ {
+ samplenumber = 0;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample(int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ if ((allocation !=0) && (channels != OutputChannels.RIGHT_CHANNEL))
+ {
+ float scaled_sample = (sample * factor + offset) * scalefactor;
+ filter1.input_sample (scaled_sample, subbandnumber);
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Class for layer I subbands in joint stereo mode.
+ */
+ static class SubbandLayer1IntensityStereo extends SubbandLayer1
+ {
+ protected float channel2_scalefactor;
+
+ /**
+ * Constructor
+ */
+ public SubbandLayer1IntensityStereo(int subbandnumber)
+ {
+ super(subbandnumber);
+ }
+
+ /**
+ *
+ */
+ public void read_allocation(Bitstream stream, Header header, Crc16 crc)
+ {
+ super.read_allocation (stream, header, crc);
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor (Bitstream stream, Header header)
+ {
+ if (allocation != 0)
+ {
+ scalefactor = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor = scalefactors[stream.get_bits(6)];
+ }
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata(Bitstream stream)
+ {
+ return super.read_sampledata (stream);
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample (int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ if (allocation !=0 )
+ {
+ sample = sample * factor + offset; // requantization
+ if (channels == OutputChannels.BOTH_CHANNELS)
+ {
+ float sample1 = sample * scalefactor,
+ sample2 = sample * channel2_scalefactor;
+ filter1.input_sample(sample1, subbandnumber);
+ filter2.input_sample(sample2, subbandnumber);
+ }
+ else if (channels == OutputChannels.LEFT_CHANNEL)
+ {
+ float sample1 = sample * scalefactor;
+ filter1.input_sample(sample1, subbandnumber);
+ }
+ else
+ {
+ float sample2 = sample * channel2_scalefactor;
+ filter1.input_sample(sample2, subbandnumber);
+ }
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Class for layer I subbands in stereo mode.
+ */
+ static class SubbandLayer1Stereo extends SubbandLayer1
+ {
+ protected int channel2_allocation;
+ protected float channel2_scalefactor;
+ protected int channel2_samplelength;
+ protected float channel2_sample;
+ protected float channel2_factor, channel2_offset;
+
+
+ /**
+ * Constructor
+ */
+ public SubbandLayer1Stereo(int subbandnumber)
+ {
+ super(subbandnumber);
+ }
+
+ /**
+ *
+ */
+ public void read_allocation (Bitstream stream, Header header, Crc16 crc)
+ {
+ allocation = stream.get_bits(4);
+ channel2_allocation = stream.get_bits(4);
+ if (crc != null)
+ {
+ crc.add_bits (allocation, 4);
+ crc.add_bits (channel2_allocation, 4);
+ }
+ if (allocation != 0)
+ {
+ samplelength = allocation + 1;
+ factor = table_factor[allocation];
+ offset = table_offset[allocation];
+ }
+ if (channel2_allocation != 0)
+ {
+ channel2_samplelength = channel2_allocation + 1;
+ channel2_factor = table_factor[channel2_allocation];
+ channel2_offset = table_offset[channel2_allocation];
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor(Bitstream stream, Header header)
+ {
+ if (allocation != 0) scalefactor = scalefactors[stream.get_bits(6)];
+ if (channel2_allocation != 0) channel2_scalefactor = scalefactors[stream.get_bits(6)];
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata (Bitstream stream)
+ {
+ boolean returnvalue = super.read_sampledata(stream);
+ if (channel2_allocation != 0)
+ {
+ channel2_sample = (float) (stream.get_bits(channel2_samplelength));
+ }
+ return(returnvalue);
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample(int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ super.put_next_sample (channels, filter1, filter2);
+ if ((channel2_allocation != 0) && (channels != OutputChannels.LEFT_CHANNEL))
+ {
+ float sample2 = (channel2_sample * channel2_factor + channel2_offset) *
+ channel2_scalefactor;
+ if (channels == OutputChannels.BOTH_CHANNELS)
+ filter2.input_sample (sample2, subbandnumber);
+ else
+ filter1.input_sample (sample2, subbandnumber);
+ }
+ return true;
+ }
+ };
+
+}
diff --git a/songdbj/javazoom/jl/decoder/LayerIIDecoder.java b/songdbj/javazoom/jl/decoder/LayerIIDecoder.java
new file mode 100644
index 0000000000..7265b1f8fa
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/LayerIIDecoder.java
@@ -0,0 +1,1064 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 29/05/01 Michael Scheerer, Fixed some C++ to Java porting bugs.
+ *
+ * 16/07/01 Michael Scheerer, Catched a bug in method
+ * read_sampledata, which causes an outOfIndexException.
+ *
+ * 12/12/99 Initial version. Adapted from javalayer.java
+ * and Subband*.java. mdm@techie.com
+ *
+ * 02/28/99 Initial version : javalayer.java by E.B
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Implements decoding of MPEG Audio Layer II frames.
+ */
+class LayerIIDecoder extends LayerIDecoder implements FrameDecoder
+{
+
+ public LayerIIDecoder()
+ {
+ }
+
+
+ protected void createSubbands()
+ {
+ int i;
+ if (mode == Header.SINGLE_CHANNEL)
+ for (i = 0; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer2(i);
+ else if (mode == Header.JOINT_STEREO)
+ {
+ for (i = 0; i < header.intensity_stereo_bound(); ++i)
+ subbands[i] = new SubbandLayer2Stereo(i);
+ for (; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer2IntensityStereo(i);
+ }
+ else
+ {
+ for (i = 0; i < num_subbands; ++i)
+ subbands[i] = new SubbandLayer2Stereo(i);
+ }
+
+ }
+
+ protected void readScaleFactorSelection()
+ {
+ for (int i = 0; i < num_subbands; ++i)
+ ((SubbandLayer2)subbands[i]).read_scalefactor_selection(stream, crc);
+ }
+
+
+
+ /**
+ * Class for layer II subbands in single channel mode.
+ */
+ static class SubbandLayer2 extends Subband
+ {
+ // this table contains 3 requantized samples for each legal codeword
+ // when grouped in 5 bits, i.e. 3 quantizationsteps per sample
+ public static final float grouping_5bits[] = new float[]
+ {
+ -2.0f/3.0f, -2.0f/3.0f, -2.0f/3.0f,
+ 0.0f, -2.0f/3.0f, -2.0f/3.0f,
+ 2.0f/3.0f, -2.0f/3.0f, -2.0f/3.0f,
+ -2.0f/3.0f, 0.0f, -2.0f/3.0f,
+ 0.0f, 0.0f, -2.0f/3.0f,
+ 2.0f/3.0f, 0.0f, -2.0f/3.0f,
+ -2.0f/3.0f, 2.0f/3.0f, -2.0f/3.0f,
+ 0.0f, 2.0f/3.0f, -2.0f/3.0f,
+ 2.0f/3.0f, 2.0f/3.0f, -2.0f/3.0f,
+ -2.0f/3.0f, -2.0f/3.0f, 0.0f,
+ 0.0f, -2.0f/3.0f, 0.0f,
+ 2.0f/3.0f, -2.0f/3.0f, 0.0f,
+ -2.0f/3.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ 2.0f/3.0f, 0.0f, 0.0f,
+ -2.0f/3.0f, 2.0f/3.0f, 0.0f,
+ 0.0f, 2.0f/3.0f, 0.0f,
+ 2.0f/3.0f, 2.0f/3.0f, 0.0f,
+ -2.0f/3.0f, -2.0f/3.0f, 2.0f/3.0f,
+ 0.0f, -2.0f/3.0f, 2.0f/3.0f,
+ 2.0f/3.0f, -2.0f/3.0f, 2.0f/3.0f,
+ -2.0f/3.0f, 0.0f, 2.0f/3.0f,
+ 0.0f, 0.0f, 2.0f/3.0f,
+ 2.0f/3.0f, 0.0f, 2.0f/3.0f,
+ -2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f,
+ 0.0f, 2.0f/3.0f, 2.0f/3.0f,
+ 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f
+ };
+
+ // this table contains 3 requantized samples for each legal codeword
+ // when grouped in 7 bits, i.e. 5 quantizationsteps per sample
+ public static final float grouping_7bits[] = new float[]
+ {
+ -0.8f, -0.8f, -0.8f, -0.4f, -0.8f, -0.8f, 0.0f, -0.8f, -0.8f, 0.4f, -0.8f, -0.8f, 0.8f, -0.8f, -0.8f,
+ -0.8f, -0.4f, -0.8f, -0.4f, -0.4f, -0.8f, 0.0f, -0.4f, -0.8f, 0.4f, -0.4f, -0.8f, 0.8f, -0.4f, -0.8f,
+ -0.8f, 0.0f, -0.8f, -0.4f, 0.0f, -0.8f, 0.0f, 0.0f, -0.8f, 0.4f, 0.0f, -0.8f, 0.8f, 0.0f, -0.8f,
+ -0.8f, 0.4f, -0.8f, -0.4f, 0.4f, -0.8f, 0.0f, 0.4f, -0.8f, 0.4f, 0.4f, -0.8f, 0.8f, 0.4f, -0.8f,
+ -0.8f, 0.8f, -0.8f, -0.4f, 0.8f, -0.8f, 0.0f, 0.8f, -0.8f, 0.4f, 0.8f, -0.8f, 0.8f, 0.8f, -0.8f,
+ -0.8f, -0.8f, -0.4f, -0.4f, -0.8f, -0.4f, 0.0f, -0.8f, -0.4f, 0.4f, -0.8f, -0.4f, 0.8f, -0.8f, -0.4f,
+ -0.8f, -0.4f, -0.4f, -0.4f, -0.4f, -0.4f, 0.0f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.8f, -0.4f, -0.4f,
+ -0.8f, 0.0f, -0.4f, -0.4f, 0.0f, -0.4f, 0.0f, 0.0f, -0.4f, 0.4f, 0.0f, -0.4f, 0.8f, 0.0f, -0.4f,
+ -0.8f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, 0.0f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.8f, 0.4f, -0.4f,
+ -0.8f, 0.8f, -0.4f, -0.4f, 0.8f, -0.4f, 0.0f, 0.8f, -0.4f, 0.4f, 0.8f, -0.4f, 0.8f, 0.8f, -0.4f,
+ -0.8f, -0.8f, 0.0f, -0.4f, -0.8f, 0.0f, 0.0f, -0.8f, 0.0f, 0.4f, -0.8f, 0.0f, 0.8f, -0.8f, 0.0f,
+ -0.8f, -0.4f, 0.0f, -0.4f, -0.4f, 0.0f, 0.0f, -0.4f, 0.0f, 0.4f, -0.4f, 0.0f, 0.8f, -0.4f, 0.0f,
+ -0.8f, 0.0f, 0.0f, -0.4f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.0f, 0.0f, 0.8f, 0.0f, 0.0f,
+ -0.8f, 0.4f, 0.0f, -0.4f, 0.4f, 0.0f, 0.0f, 0.4f, 0.0f, 0.4f, 0.4f, 0.0f, 0.8f, 0.4f, 0.0f,
+ -0.8f, 0.8f, 0.0f, -0.4f, 0.8f, 0.0f, 0.0f, 0.8f, 0.0f, 0.4f, 0.8f, 0.0f, 0.8f, 0.8f, 0.0f,
+ -0.8f, -0.8f, 0.4f, -0.4f, -0.8f, 0.4f, 0.0f, -0.8f, 0.4f, 0.4f, -0.8f, 0.4f, 0.8f, -0.8f, 0.4f,
+ -0.8f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, 0.0f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.8f, -0.4f, 0.4f,
+ -0.8f, 0.0f, 0.4f, -0.4f, 0.0f, 0.4f, 0.0f, 0.0f, 0.4f, 0.4f, 0.0f, 0.4f, 0.8f, 0.0f, 0.4f,
+ -0.8f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, 0.0f, 0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 0.8f, 0.4f, 0.4f,
+ -0.8f, 0.8f, 0.4f, -0.4f, 0.8f, 0.4f, 0.0f, 0.8f, 0.4f, 0.4f, 0.8f, 0.4f, 0.8f, 0.8f, 0.4f,
+ -0.8f, -0.8f, 0.8f, -0.4f, -0.8f, 0.8f, 0.0f, -0.8f, 0.8f, 0.4f, -0.8f, 0.8f, 0.8f, -0.8f, 0.8f,
+ -0.8f, -0.4f, 0.8f, -0.4f, -0.4f, 0.8f, 0.0f, -0.4f, 0.8f, 0.4f, -0.4f, 0.8f, 0.8f, -0.4f, 0.8f,
+ -0.8f, 0.0f, 0.8f, -0.4f, 0.0f, 0.8f, 0.0f, 0.0f, 0.8f, 0.4f, 0.0f, 0.8f, 0.8f, 0.0f, 0.8f,
+ -0.8f, 0.4f, 0.8f, -0.4f, 0.4f, 0.8f, 0.0f, 0.4f, 0.8f, 0.4f, 0.4f, 0.8f, 0.8f, 0.4f, 0.8f,
+ -0.8f, 0.8f, 0.8f, -0.4f, 0.8f, 0.8f, 0.0f, 0.8f, 0.8f, 0.4f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f
+ };
+
+ // this table contains 3 requantized samples for each legal codeword
+ // when grouped in 10 bits, i.e. 9 quantizationsteps per sample
+ public static final float grouping_10bits[] =
+ {
+ -8.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f, 0.0f, -8.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 0.0f, -6.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 0.0f, -4.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f, 0.0f, -2.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, 0.0f, -8.0f/9.0f, -6.0f/9.0f, 0.0f, -8.0f/9.0f, -4.0f/9.0f, 0.0f, -8.0f/9.0f,
+ -2.0f/9.0f, 0.0f, -8.0f/9.0f, 0.0f, 0.0f, -8.0f/9.0f, 2.0f/9.0f, 0.0f, -8.0f/9.0f,
+ 4.0f/9.0f, 0.0f, -8.0f/9.0f, 6.0f/9.0f, 0.0f, -8.0f/9.0f, 8.0f/9.0f, 0.0f, -8.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 0.0f, 2.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f, 0.0f, 4.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 0.0f, 6.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 0.0f, 8.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 0.0f, -8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 0.0f, -6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 0.0f, -4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 0.0f, -2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, 0.0f, -6.0f/9.0f, -6.0f/9.0f, 0.0f, -6.0f/9.0f, -4.0f/9.0f, 0.0f, -6.0f/9.0f,
+ -2.0f/9.0f, 0.0f, -6.0f/9.0f, 0.0f, 0.0f, -6.0f/9.0f, 2.0f/9.0f, 0.0f, -6.0f/9.0f,
+ 4.0f/9.0f, 0.0f, -6.0f/9.0f, 6.0f/9.0f, 0.0f, -6.0f/9.0f, 8.0f/9.0f, 0.0f, -6.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 0.0f, 2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 0.0f, 4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 0.0f, 6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 0.0f, 8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 0.0f, -8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 0.0f, -6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 0.0f, -4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 0.0f, -2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, 0.0f, -4.0f/9.0f, -6.0f/9.0f, 0.0f, -4.0f/9.0f, -4.0f/9.0f, 0.0f, -4.0f/9.0f,
+ -2.0f/9.0f, 0.0f, -4.0f/9.0f, 0.0f, 0.0f, -4.0f/9.0f, 2.0f/9.0f, 0.0f, -4.0f/9.0f,
+ 4.0f/9.0f, 0.0f, -4.0f/9.0f, 6.0f/9.0f, 0.0f, -4.0f/9.0f, 8.0f/9.0f, 0.0f, -4.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 0.0f, 2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 0.0f, 4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 0.0f, 6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 0.0f, 8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f, 0.0f, -8.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 0.0f, -6.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 0.0f, -4.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f, 0.0f, -2.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, 0.0f, -2.0f/9.0f, -6.0f/9.0f, 0.0f, -2.0f/9.0f, -4.0f/9.0f, 0.0f, -2.0f/9.0f,
+ -2.0f/9.0f, 0.0f, -2.0f/9.0f, 0.0f, 0.0f, -2.0f/9.0f, 2.0f/9.0f, 0.0f, -2.0f/9.0f,
+ 4.0f/9.0f, 0.0f, -2.0f/9.0f, 6.0f/9.0f, 0.0f, -2.0f/9.0f, 8.0f/9.0f, 0.0f, -2.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 0.0f, 2.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f, 0.0f, 4.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 0.0f, 6.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 0.0f, 8.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, 0.0f, -6.0f/9.0f, -8.0f/9.0f, 0.0f, -4.0f/9.0f, -8.0f/9.0f, 0.0f,
+ -2.0f/9.0f, -8.0f/9.0f, 0.0f, 0.0f, -8.0f/9.0f, 0.0f, 2.0f/9.0f, -8.0f/9.0f, 0.0f,
+ 4.0f/9.0f, -8.0f/9.0f, 0.0f, 6.0f/9.0f, -8.0f/9.0f, 0.0f, 8.0f/9.0f, -8.0f/9.0f, 0.0f,
+ -8.0f/9.0f, -6.0f/9.0f, 0.0f, -6.0f/9.0f, -6.0f/9.0f, 0.0f, -4.0f/9.0f, -6.0f/9.0f, 0.0f,
+ -2.0f/9.0f, -6.0f/9.0f, 0.0f, 0.0f, -6.0f/9.0f, 0.0f, 2.0f/9.0f, -6.0f/9.0f, 0.0f,
+ 4.0f/9.0f, -6.0f/9.0f, 0.0f, 6.0f/9.0f, -6.0f/9.0f, 0.0f, 8.0f/9.0f, -6.0f/9.0f, 0.0f,
+ -8.0f/9.0f, -4.0f/9.0f, 0.0f, -6.0f/9.0f, -4.0f/9.0f, 0.0f, -4.0f/9.0f, -4.0f/9.0f, 0.0f,
+ -2.0f/9.0f, -4.0f/9.0f, 0.0f, 0.0f, -4.0f/9.0f, 0.0f, 2.0f/9.0f, -4.0f/9.0f, 0.0f,
+ 4.0f/9.0f, -4.0f/9.0f, 0.0f, 6.0f/9.0f, -4.0f/9.0f, 0.0f, 8.0f/9.0f, -4.0f/9.0f, 0.0f,
+ -8.0f/9.0f, -2.0f/9.0f, 0.0f, -6.0f/9.0f, -2.0f/9.0f, 0.0f, -4.0f/9.0f, -2.0f/9.0f, 0.0f,
+ -2.0f/9.0f, -2.0f/9.0f, 0.0f, 0.0f, -2.0f/9.0f, 0.0f, 2.0f/9.0f, -2.0f/9.0f, 0.0f,
+ 4.0f/9.0f, -2.0f/9.0f, 0.0f, 6.0f/9.0f, -2.0f/9.0f, 0.0f, 8.0f/9.0f, -2.0f/9.0f, 0.0f,
+ -8.0f/9.0f, 0.0f, 0.0f, -6.0f/9.0f, 0.0f, 0.0f, -4.0f/9.0f, 0.0f, 0.0f,
+ -2.0f/9.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f/9.0f, 0.0f, 0.0f,
+ 4.0f/9.0f, 0.0f, 0.0f, 6.0f/9.0f, 0.0f, 0.0f, 8.0f/9.0f, 0.0f, 0.0f,
+ -8.0f/9.0f, 2.0f/9.0f, 0.0f, -6.0f/9.0f, 2.0f/9.0f, 0.0f, -4.0f/9.0f, 2.0f/9.0f, 0.0f,
+ -2.0f/9.0f, 2.0f/9.0f, 0.0f, 0.0f, 2.0f/9.0f, 0.0f, 2.0f/9.0f, 2.0f/9.0f, 0.0f,
+ 4.0f/9.0f, 2.0f/9.0f, 0.0f, 6.0f/9.0f, 2.0f/9.0f, 0.0f, 8.0f/9.0f, 2.0f/9.0f, 0.0f,
+ -8.0f/9.0f, 4.0f/9.0f, 0.0f, -6.0f/9.0f, 4.0f/9.0f, 0.0f, -4.0f/9.0f, 4.0f/9.0f, 0.0f,
+ -2.0f/9.0f, 4.0f/9.0f, 0.0f, 0.0f, 4.0f/9.0f, 0.0f, 2.0f/9.0f, 4.0f/9.0f, 0.0f,
+ 4.0f/9.0f, 4.0f/9.0f, 0.0f, 6.0f/9.0f, 4.0f/9.0f, 0.0f, 8.0f/9.0f, 4.0f/9.0f, 0.0f,
+ -8.0f/9.0f, 6.0f/9.0f, 0.0f, -6.0f/9.0f, 6.0f/9.0f, 0.0f, -4.0f/9.0f, 6.0f/9.0f, 0.0f,
+ -2.0f/9.0f, 6.0f/9.0f, 0.0f, 0.0f, 6.0f/9.0f, 0.0f, 2.0f/9.0f, 6.0f/9.0f, 0.0f,
+ 4.0f/9.0f, 6.0f/9.0f, 0.0f, 6.0f/9.0f, 6.0f/9.0f, 0.0f, 8.0f/9.0f, 6.0f/9.0f, 0.0f,
+ -8.0f/9.0f, 8.0f/9.0f, 0.0f, -6.0f/9.0f, 8.0f/9.0f, 0.0f, -4.0f/9.0f, 8.0f/9.0f, 0.0f,
+ -2.0f/9.0f, 8.0f/9.0f, 0.0f, 0.0f, 8.0f/9.0f, 0.0f, 2.0f/9.0f, 8.0f/9.0f, 0.0f,
+ 4.0f/9.0f, 8.0f/9.0f, 0.0f, 6.0f/9.0f, 8.0f/9.0f, 0.0f, 8.0f/9.0f, 8.0f/9.0f, 0.0f,
+ -8.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 0.0f, -8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 0.0f, -6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 0.0f, -4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 0.0f, -2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, 0.0f, 2.0f/9.0f, -6.0f/9.0f, 0.0f, 2.0f/9.0f, -4.0f/9.0f, 0.0f, 2.0f/9.0f,
+ -2.0f/9.0f, 0.0f, 2.0f/9.0f, 0.0f, 0.0f, 2.0f/9.0f, 2.0f/9.0f, 0.0f, 2.0f/9.0f,
+ 4.0f/9.0f, 0.0f, 2.0f/9.0f, 6.0f/9.0f, 0.0f, 2.0f/9.0f, 8.0f/9.0f, 0.0f, 2.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 0.0f, 2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 0.0f, 4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 0.0f, 6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 0.0f, 8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f, 0.0f, -8.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 0.0f, -6.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 0.0f, -4.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f, 0.0f, -2.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, 0.0f, 4.0f/9.0f, -6.0f/9.0f, 0.0f, 4.0f/9.0f, -4.0f/9.0f, 0.0f, 4.0f/9.0f,
+ -2.0f/9.0f, 0.0f, 4.0f/9.0f, 0.0f, 0.0f, 4.0f/9.0f, 2.0f/9.0f, 0.0f, 4.0f/9.0f,
+ 4.0f/9.0f, 0.0f, 4.0f/9.0f, 6.0f/9.0f, 0.0f, 4.0f/9.0f, 8.0f/9.0f, 0.0f, 4.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 0.0f, 2.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f, 0.0f, 4.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 0.0f, 6.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 0.0f, 8.0f/9.0f, 4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 0.0f, -8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 0.0f, -6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 0.0f, -4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 0.0f, -2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, 0.0f, 6.0f/9.0f, -6.0f/9.0f, 0.0f, 6.0f/9.0f, -4.0f/9.0f, 0.0f, 6.0f/9.0f,
+ -2.0f/9.0f, 0.0f, 6.0f/9.0f, 0.0f, 0.0f, 6.0f/9.0f, 2.0f/9.0f, 0.0f, 6.0f/9.0f,
+ 4.0f/9.0f, 0.0f, 6.0f/9.0f, 6.0f/9.0f, 0.0f, 6.0f/9.0f, 8.0f/9.0f, 0.0f, 6.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 0.0f, 2.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 0.0f, 4.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 0.0f, 6.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 0.0f, 8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f,
+ -8.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 0.0f, -8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -8.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 0.0f, -6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 0.0f, -4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 0.0f, -2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -2.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, 0.0f, 8.0f/9.0f, -6.0f/9.0f, 0.0f, 8.0f/9.0f, -4.0f/9.0f, 0.0f, 8.0f/9.0f,
+ -2.0f/9.0f, 0.0f, 8.0f/9.0f, 0.0f, 0.0f, 8.0f/9.0f, 2.0f/9.0f, 0.0f, 8.0f/9.0f,
+ 4.0f/9.0f, 0.0f, 8.0f/9.0f, 6.0f/9.0f, 0.0f, 8.0f/9.0f, 8.0f/9.0f, 0.0f, 8.0f/9.0f,
+ -8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 0.0f, 2.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 0.0f, 4.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 4.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 0.0f, 6.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f,
+ -8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, -4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f,
+ -2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 0.0f, 8.0f/9.0f, 8.0f/9.0f, 2.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f,
+ 4.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 6.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f, 8.0f/9.0f
+ };
+
+ // data taken from ISO/IEC DIS 11172, Annexes 3-B.2[abcd] and 3-B.4:
+
+ // subbands 0-2 in tables 3-B.2a and 2b: (index is allocation)
+ public static final int table_ab1_codelength[] =
+ // bits per codeword
+ { 0, 5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+ public static final float table_ab1_groupingtables[][] =
+ // pointer to sample grouping table, or NULL-pointer if ungrouped
+ { null, grouping_5bits, null, null, null, null, null, null, null, null, null, null, null, null, null, null };
+
+ public static final float table_ab1_factor[] =
+ // factor for requantization: (real)sample * factor - 1.0 gives requantized sample
+ { 0.0f, 1.0f/2.0f, 1.0f/4.0f, 1.0f/8.0f, 1.0f/16.0f, 1.0f/32.0f, 1.0f/64.0f,
+ 1.0f/128.0f, 1.0f/256.0f, 1.0f/512.0f, 1.0f/1024.0f, 1.0f/2048.0f,
+ 1.0f/4096.0f, 1.0f/8192.0f, 1.0f/16384.0f, 1.0f/32768.0f };
+
+ public static final float table_ab1_c[] =
+ // factor c for requantization from table 3-B.4
+ { 0.0f, 1.33333333333f, 1.14285714286f, 1.06666666666f, 1.03225806452f,
+ 1.01587301587f, 1.00787401575f, 1.00392156863f, 1.00195694716f, 1.00097751711f,
+ 1.00048851979f, 1.00024420024f, 1.00012208522f, 1.00006103888f, 1.00003051851f,
+ 1.00001525902f };
+
+ public static final float table_ab1_d[] =
+ // addend d for requantization from table 3-B.4
+ { 0.0f, 0.50000000000f, 0.25000000000f, 0.12500000000f, 0.06250000000f,
+ 0.03125000000f, 0.01562500000f, 0.00781250000f, 0.00390625000f, 0.00195312500f,
+ 0.00097656250f, 0.00048828125f, 0.00024414063f, 0.00012207031f, 0.00006103516f,
+ 0.00003051758f };
+
+ // subbands 3-... tables 3-B.2a and 2b:
+ public static final float[] table_ab234_groupingtables[] =
+ { null, grouping_5bits, grouping_7bits, null, grouping_10bits, null, null, null, null, null, null, null, null, null, null, null };
+
+ // subbands 3-10 in tables 3-B.2a and 2b:
+ public static final int table_ab2_codelength[] =
+ { 0, 5, 7, 3, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 };
+ public static final float table_ab2_factor[] =
+ { 0.0f, 1.0f/2.0f, 1.0f/4.0f, 1.0f/4.0f, 1.0f/8.0f, 1.0f/8.0f, 1.0f/16.0f,
+ 1.0f/32.0f, 1.0f/64.0f, 1.0f/128.0f, 1.0f/256.0f, 1.0f/512.0f,
+ 1.0f/1024.0f, 1.0f/2048.0f, 1.0f/4096.0f, 1.0f/32768.0f };
+ public static final float table_ab2_c[] =
+ { 0.0f, 1.33333333333f, 1.60000000000f, 1.14285714286f, 1.77777777777f,
+ 1.06666666666f, 1.03225806452f, 1.01587301587f, 1.00787401575f, 1.00392156863f,
+ 1.00195694716f, 1.00097751711f, 1.00048851979f, 1.00024420024f, 1.00012208522f,
+ 1.00001525902f };
+ public static final float table_ab2_d[] =
+ { 0.0f, 0.50000000000f, 0.50000000000f, 0.25000000000f, 0.50000000000f,
+ 0.12500000000f, 0.06250000000f, 0.03125000000f, 0.01562500000f, 0.00781250000f,
+ 0.00390625000f, 0.00195312500f, 0.00097656250f, 0.00048828125f, 0.00024414063f,
+ 0.00003051758f };
+
+ // subbands 11-22 in tables 3-B.2a and 2b:
+ public static final int table_ab3_codelength[] = { 0, 5, 7, 3, 10, 4, 5, 16 };
+ public static final float table_ab3_factor[] =
+ { 0.0f, 1.0f/2.0f, 1.0f/4.0f, 1.0f/4.0f, 1.0f/8.0f, 1.0f/8.0f, 1.0f/16.0f, 1.0f/32768.0f };
+ public static final float table_ab3_c[] =
+ { 0.0f, 1.33333333333f, 1.60000000000f, 1.14285714286f, 1.77777777777f,
+ 1.06666666666f, 1.03225806452f, 1.00001525902f };
+ public static final float table_ab3_d[] =
+ { 0.0f, 0.50000000000f, 0.50000000000f, 0.25000000000f, 0.50000000000f,
+ 0.12500000000f, 0.06250000000f, 0.00003051758f };
+
+ // subbands 23-... in tables 3-B.2a and 2b:
+ public static final int table_ab4_codelength[] = { 0, 5, 7, 16 };
+ public static final float table_ab4_factor[] = { 0.0f, 1.0f/2.0f, 1.0f/4.0f, 1.0f/32768.0f };
+ public static final float table_ab4_c[] = { 0.0f, 1.33333333333f, 1.60000000000f, 1.00001525902f };
+ public static final float table_ab4_d[] = { 0.0f, 0.50000000000f, 0.50000000000f, 0.00003051758f };
+
+ // subbands in tables 3-B.2c and 2d:
+ public static final int table_cd_codelength[] =
+ { 0, 5, 7, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ public static final float table_cd_groupingtables[][] =
+ { null, grouping_5bits, grouping_7bits, grouping_10bits, null, null, null, null, null, null, null, null, null, null, null, null };
+ public static final float table_cd_factor[] =
+ { 0.0f, 1.0f/2.0f, 1.0f/4.0f, 1.0f/8.0f, 1.0f/8.0f, 1.0f/16.0f, 1.0f/32.0f, 1.0f/64.0f,
+ 1.0f/128.0f, 1.0f/256.0f, 1.0f/512.0f, 1.0f/1024.0f, 1.0f/2048.0f, 1.0f/4096.0f,
+ 1.0f/8192.0f, 1.0f/16384.0f };
+ public static final float table_cd_c[] =
+ { 0.0f, 1.33333333333f, 1.60000000000f, 1.77777777777f, 1.06666666666f,
+ 1.03225806452f, 1.01587301587f, 1.00787401575f, 1.00392156863f, 1.00195694716f,
+ 1.00097751711f, 1.00048851979f, 1.00024420024f, 1.00012208522f, 1.00006103888f,
+ 1.00003051851f };
+ public static final float table_cd_d[] =
+ { 0.0f, 0.50000000000f, 0.50000000000f, 0.50000000000f, 0.12500000000f,
+ 0.06250000000f, 0.03125000000f, 0.01562500000f, 0.00781250000f, 0.00390625000f,
+ 0.00195312500f, 0.00097656250f, 0.00048828125f, 0.00024414063f, 0.00012207031f,
+ 0.00006103516f };
+
+
+
+ protected int subbandnumber;
+ protected int allocation;
+ protected int scfsi;
+ protected float scalefactor1, scalefactor2, scalefactor3;
+ protected int[] codelength = {0};
+ protected float groupingtable[][] = new float[2][];
+ //protected float[][] groupingtable = {{0},{0}} ;
+ protected float[] factor = {0.0f};
+ protected int groupnumber;
+ protected int samplenumber;
+ protected float[] samples = new float[3];
+ protected float[] c = {0};
+ protected float[] d = {0};
+ /**
+ * Constructor
+ */
+ public SubbandLayer2(int subbandnumber)
+ {
+ this.subbandnumber = subbandnumber;
+ groupnumber = samplenumber = 0;
+ }
+
+
+ /**
+ *
+ */
+ protected int get_allocationlength (Header header)
+ {
+ if (header.version() == Header.MPEG1)
+ {
+ int channel_bitrate = header.bitrate_index();
+
+ // calculate bitrate per channel:
+ if (header.mode() != Header.SINGLE_CHANNEL)
+ if (channel_bitrate == 4)
+ channel_bitrate = 1;
+ else
+ channel_bitrate -= 4;
+
+ if (channel_bitrate == 1 || channel_bitrate == 2)
+ // table 3-B.2c or 3-B.2d
+ if (subbandnumber <= 1)
+ return 4;
+ else
+ return 3;
+ else
+ // tables 3-B.2a or 3-B.2b
+ if (subbandnumber <= 10)
+ return 4;
+ else if (subbandnumber <= 22)
+ return 3;
+ else
+ return 2;
+ }
+ else
+ { // MPEG-2 LSF -- Jeff
+
+ // table B.1 of ISO/IEC 13818-3
+ if (subbandnumber <= 3)
+ return 4;
+ else if (subbandnumber <= 10)
+ return 3;
+ else
+ return 2;
+ }
+ }
+
+ /**
+ *
+ */
+ protected void prepare_sample_reading(Header header, int allocation,
+ //float[][] groupingtable,
+ int channel,
+ float[] factor, int[] codelength,
+ float[] c, float[] d)
+ {
+ int channel_bitrate = header.bitrate_index();
+ // calculate bitrate per channel:
+ if (header.mode() != Header.SINGLE_CHANNEL)
+ if (channel_bitrate == 4)
+ channel_bitrate = 1;
+ else
+ channel_bitrate -= 4;
+
+ if (channel_bitrate == 1 || channel_bitrate == 2)
+ {
+ // table 3-B.2c or 3-B.2d
+ groupingtable[channel] = table_cd_groupingtables[allocation];
+ factor[0] = table_cd_factor[allocation];
+ codelength[0] = table_cd_codelength[allocation];
+ c[0] = table_cd_c[allocation];
+ d[0] = table_cd_d[allocation];
+ }
+ else
+ {
+ // tables 3-B.2a or 3-B.2b
+ if (subbandnumber <= 2)
+ {
+ groupingtable[channel] = table_ab1_groupingtables[allocation];
+ factor[0] = table_ab1_factor[allocation];
+ codelength[0] = table_ab1_codelength[allocation];
+ c[0] = table_ab1_c[allocation];
+ d[0] = table_ab1_d[allocation];
+ }
+ else
+ {
+ groupingtable[channel] = table_ab234_groupingtables[allocation];
+ if (subbandnumber <= 10)
+ {
+ factor[0] = table_ab2_factor[allocation];
+ codelength[0] = table_ab2_codelength[allocation];
+ c[0] = table_ab2_c[allocation];
+ d[0] = table_ab2_d[allocation];
+ }
+ else if (subbandnumber <= 22)
+ {
+ factor[0] = table_ab3_factor[allocation];
+ codelength[0] = table_ab3_codelength[allocation];
+ c[0] = table_ab3_c[allocation];
+ d[0] = table_ab3_d[allocation];
+ }
+ else
+ {
+ factor[0] = table_ab4_factor[allocation];
+ codelength[0] = table_ab4_codelength[allocation];
+ c[0] = table_ab4_c[allocation];
+ d[0] = table_ab4_d[allocation];
+ }
+ }
+ }
+ }
+
+
+ /**
+ *
+ */
+ public void read_allocation(Bitstream stream, Header header, Crc16 crc)
+ {
+ int length = get_allocationlength(header);
+ allocation = stream.get_bits(length);
+ if (crc != null)
+ crc.add_bits(allocation, length);
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor_selection (Bitstream stream, Crc16 crc)
+ {
+ if (allocation != 0)
+ {
+ scfsi = stream.get_bits(2);
+ if (crc != null) crc.add_bits(scfsi, 2);
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor (Bitstream stream, Header header)
+ {
+ if (allocation != 0)
+ {
+ switch (scfsi)
+ {
+ case 0:
+ scalefactor1 = scalefactors[stream.get_bits(6)];
+ scalefactor2 = scalefactors[stream.get_bits(6)];
+ scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+ case 1:
+ scalefactor1 = scalefactor2 = scalefactors[stream.get_bits(6)];
+ scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+ case 2:
+ scalefactor1 = scalefactor2 = scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+ case 3:
+ scalefactor1 = scalefactors[stream.get_bits(6)];
+ scalefactor2 = scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+ }
+ prepare_sample_reading(header, allocation, 0,
+ factor, codelength, c, d);
+ }
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata (Bitstream stream)
+ {
+ if (allocation != 0)
+ if (groupingtable[0] != null)
+ {
+ int samplecode = stream.get_bits(codelength[0]);
+ // create requantized samples:
+ samplecode += samplecode << 1;
+ float[] target = samples;
+ float[] source = groupingtable[0];
+ /*
+ int tmp = 0;
+ int temp = 0;
+ target[tmp++] = source[samplecode + temp];
+ temp++;
+ target[tmp++] = source[samplecode + temp];
+ temp++;
+ target[tmp] = source[samplecode + temp];
+ */
+ //Bugfix:
+ int tmp = 0;
+ int temp = samplecode;
+
+ if(temp > source.length - 3) temp = source.length - 3;
+
+ target[tmp] = source[temp];
+ temp++;tmp++;
+ target[tmp] = source[temp];
+ temp++;tmp++;
+ target[tmp] = source[temp];
+
+ // memcpy (samples, groupingtable + samplecode, 3 * sizeof (real));
+ }
+ else
+ {
+ samples[0] = (float) ((stream.get_bits(codelength[0])) * factor[0] - 1.0);
+ samples[1] = (float) ((stream.get_bits(codelength[0])) * factor[0] - 1.0);
+ samples[2] = (float) ((stream.get_bits(codelength[0])) * factor[0] - 1.0);
+ }
+
+ samplenumber = 0;
+ if (++groupnumber == 12)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample(int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ if ((allocation != 0) && (channels != OutputChannels.RIGHT_CHANNEL))
+ {
+ float sample = samples[samplenumber];
+
+ if (groupingtable[0] == null)
+ sample = (sample + d[0]) * c[0];
+ if (groupnumber <= 4)
+ sample *= scalefactor1;
+ else if (groupnumber <= 8)
+ sample *= scalefactor2;
+ else
+ sample *= scalefactor3;
+ filter1.input_sample(sample, subbandnumber);
+ }
+
+ if (++samplenumber == 3)
+ return true;
+ else
+ return false;
+ }
+ };
+
+ /**
+ * Class for layer II subbands in joint stereo mode.
+ */
+ static class SubbandLayer2IntensityStereo extends SubbandLayer2
+ {
+ protected int channel2_scfsi;
+ protected float channel2_scalefactor1, channel2_scalefactor2, channel2_scalefactor3;
+
+ /**
+ * Constructor
+ */
+ public SubbandLayer2IntensityStereo (int subbandnumber)
+ {
+ super(subbandnumber);
+ }
+
+ /**
+ *
+ */
+ public void read_allocation(Bitstream stream, Header header, Crc16 crc)
+ {
+ super.read_allocation (stream, header, crc);
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor_selection(Bitstream stream, Crc16 crc)
+ {
+ if (allocation != 0)
+ {
+ scfsi = stream.get_bits(2);
+ channel2_scfsi = stream.get_bits(2);
+ if (crc != null)
+ {
+ crc.add_bits(scfsi, 2);
+ crc.add_bits(channel2_scfsi, 2);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor(Bitstream stream, Header header)
+ {
+ if (allocation != 0)
+ {
+ super.read_scalefactor(stream, header);
+ switch (channel2_scfsi)
+ {
+ case 0:
+ channel2_scalefactor1 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor2 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 1:
+ channel2_scalefactor1 = channel2_scalefactor2 = scalefactors[stream.get_bits (6)];
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 2:
+ channel2_scalefactor1 = channel2_scalefactor2 =
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 3:
+ channel2_scalefactor1 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor2 = channel2_scalefactor3 = scalefactors[stream.get_bits (6)];
+ break;
+ }
+ }
+
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata(Bitstream stream)
+ {
+ return super.read_sampledata (stream);
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample(int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ if (allocation != 0)
+ {
+ float sample = samples[samplenumber];
+
+ if (groupingtable[0] == null)
+ sample = (sample + d[0]) * c[0];
+ if (channels == OutputChannels.BOTH_CHANNELS)
+ {
+ float sample2 = sample;
+ if (groupnumber <= 4)
+ {
+ sample *= scalefactor1;
+ sample2 *= channel2_scalefactor1;
+ }
+ else if (groupnumber <= 8)
+ {
+ sample *= scalefactor2;
+ sample2 *= channel2_scalefactor2;
+ }
+ else
+ {
+ sample *= scalefactor3;
+ sample2 *= channel2_scalefactor3;
+ }
+ filter1.input_sample(sample, subbandnumber);
+ filter2.input_sample(sample2, subbandnumber);
+ }
+ else if (channels == OutputChannels.LEFT_CHANNEL)
+ {
+ if (groupnumber <= 4)
+ sample *= scalefactor1;
+ else if (groupnumber <= 8)
+ sample *= scalefactor2;
+ else
+ sample *= scalefactor3;
+ filter1.input_sample(sample, subbandnumber);
+ }
+ else
+ {
+ if (groupnumber <= 4)
+ sample *= channel2_scalefactor1;
+ else if (groupnumber <= 8)
+ sample *= channel2_scalefactor2;
+ else
+ sample *= channel2_scalefactor3;
+ filter1.input_sample(sample, subbandnumber);
+ }
+ }
+
+ if (++samplenumber == 3)
+ return true;
+ else
+ return false;
+ }
+ };
+
+ /**
+ * Class for layer II subbands in stereo mode.
+ */
+ static class SubbandLayer2Stereo extends SubbandLayer2
+ {
+ protected int channel2_allocation;
+ protected int channel2_scfsi;
+ protected float channel2_scalefactor1, channel2_scalefactor2, channel2_scalefactor3;
+ //protected boolean channel2_grouping; ???? Never used!
+ protected int[] channel2_codelength = {0};
+ //protected float[][] channel2_groupingtable = {{0},{0}};
+ protected float[] channel2_factor = {0};
+ protected float[] channel2_samples;
+ protected float[] channel2_c = {0};
+ protected float[] channel2_d = {0};
+
+ /**
+ * Constructor
+ */
+ public SubbandLayer2Stereo(int subbandnumber)
+ {
+ super(subbandnumber);
+ channel2_samples = new float[3];
+ }
+
+ /**
+ *
+ */
+ public void read_allocation (Bitstream stream, Header header, Crc16 crc)
+ {
+ int length = get_allocationlength(header);
+ allocation = stream.get_bits(length);
+ channel2_allocation = stream.get_bits(length);
+ if (crc != null)
+ {
+ crc.add_bits(allocation, length);
+ crc.add_bits(channel2_allocation, length);
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor_selection(Bitstream stream, Crc16 crc)
+ {
+ if (allocation != 0)
+ {
+ scfsi = stream.get_bits(2);
+ if (crc != null)
+ crc.add_bits(scfsi, 2);
+ }
+ if (channel2_allocation != 0)
+ {
+ channel2_scfsi = stream.get_bits(2);
+ if (crc != null)
+ crc.add_bits(channel2_scfsi, 2);
+ }
+ }
+
+ /**
+ *
+ */
+ public void read_scalefactor(Bitstream stream, Header header)
+ {
+ super.read_scalefactor(stream, header);
+ if (channel2_allocation != 0)
+ {
+ switch (channel2_scfsi)
+ {
+ case 0:
+ channel2_scalefactor1 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor2 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 1:
+ channel2_scalefactor1 = channel2_scalefactor2 =
+ scalefactors[stream.get_bits(6)];
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 2:
+ channel2_scalefactor1 = channel2_scalefactor2 =
+ channel2_scalefactor3 = scalefactors[stream.get_bits(6)];
+ break;
+
+ case 3:
+ channel2_scalefactor1 = scalefactors[stream.get_bits(6)];
+ channel2_scalefactor2 = channel2_scalefactor3 =
+ scalefactors[stream.get_bits(6)];
+ break;
+ }
+ prepare_sample_reading(header, channel2_allocation, 1,
+ channel2_factor, channel2_codelength, channel2_c,
+ channel2_d);
+ }
+ }
+
+ /**
+ *
+ */
+ public boolean read_sampledata (Bitstream stream)
+ {
+ boolean returnvalue = super.read_sampledata(stream);
+
+ if (channel2_allocation != 0)
+ if (groupingtable[1] != null)
+ {
+ int samplecode = stream.get_bits(channel2_codelength[0]);
+ // create requantized samples:
+ samplecode += samplecode << 1;
+ /*
+ float[] target = channel2_samples;
+ float[] source = channel2_groupingtable[0];
+ int tmp = 0;
+ int temp = 0;
+ target[tmp++] = source[samplecode + temp];
+ temp++;
+ target[tmp++] = source[samplecode + temp];
+ temp++;
+ target[tmp] = source[samplecode + temp];
+ // memcpy (channel2_samples, channel2_groupingtable + samplecode, 3 * sizeof (real));
+ */
+ float[] target = channel2_samples;
+ float[] source = groupingtable[1];
+ int tmp = 0;
+ int temp = samplecode;
+ target[tmp] = source[temp];
+ temp++;tmp++;
+ target[tmp] = source[temp];
+ temp++;tmp++;
+ target[tmp] = source[temp];
+
+ }
+ else
+ {
+ channel2_samples[0] = (float) ((stream.get_bits(channel2_codelength[0])) *
+ channel2_factor[0] - 1.0);
+ channel2_samples[1] = (float) ((stream.get_bits(channel2_codelength[0])) *
+ channel2_factor[0] - 1.0);
+ channel2_samples[2] = (float) ((stream.get_bits(channel2_codelength[0])) *
+ channel2_factor[0] - 1.0);
+ }
+ return returnvalue;
+ }
+
+ /**
+ *
+ */
+ public boolean put_next_sample(int channels, SynthesisFilter filter1, SynthesisFilter filter2)
+ {
+ boolean returnvalue = super.put_next_sample(channels, filter1, filter2);
+ if ((channel2_allocation != 0) && (channels != OutputChannels.LEFT_CHANNEL))
+ {
+ float sample = channel2_samples[samplenumber - 1];
+
+ if (groupingtable[1] == null)
+ sample = (sample + channel2_d[0]) * channel2_c[0];
+
+ if (groupnumber <= 4)
+ sample *= channel2_scalefactor1;
+ else if (groupnumber <= 8)
+ sample *= channel2_scalefactor2;
+ else
+ sample *= channel2_scalefactor3;
+ if (channels == OutputChannels.BOTH_CHANNELS)
+ filter2.input_sample(sample, subbandnumber);
+ else
+ filter1.input_sample(sample, subbandnumber);
+ }
+ return returnvalue;
+ }
+ }
+}
diff --git a/songdbj/javazoom/jl/decoder/LayerIIIDecoder.java b/songdbj/javazoom/jl/decoder/LayerIIIDecoder.java
new file mode 100644
index 0000000000..602badf0f2
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/LayerIIIDecoder.java
@@ -0,0 +1,2439 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 18/06/01 Michael Scheerer, Fixed bugs which causes
+ * negative indexes in method huffmann_decode and in method
+ * dequanisize_sample.
+ *
+ * 16/07/01 Michael Scheerer, Catched a bug in method
+ * huffmann_decode, which causes an outOfIndexException.
+ * Cause : Indexnumber of 24 at SfBandIndex,
+ * which has only a length of 22. I have simply and dirty
+ * fixed the index to <= 22, because I'm not really be able
+ * to fix the bug. The Indexnumber is taken from the MP3
+ * file and the origin Ma-Player with the same code works
+ * well.
+ *
+ * 02/19/99 Java Conversion by E.B, javalayer@javazoom.net
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * Class Implementing Layer 3 Decoder.
+ *
+ * @since 0.0
+ */
+final class LayerIIIDecoder implements FrameDecoder
+{
+ final double d43 = (4.0/3.0);
+
+ public int[] scalefac_buffer;
+
+ // MDM: removed, as this wasn't being used.
+ //private float CheckSumOut1d = 0.0f;
+ private int CheckSumHuff = 0;
+ private int[] is_1d;
+ private float[][][] ro;
+ private float[][][] lr;
+ private float[] out_1d;
+ private float[][] prevblck;
+ private float[][] k;
+ private int[] nonzero;
+ private Bitstream stream;
+ private Header header;
+ private SynthesisFilter filter1, filter2;
+ private Obuffer buffer;
+ private int which_channels;
+ private BitReserve br;
+ private III_side_info_t si;
+
+ private temporaire2[] III_scalefac_t;
+ private temporaire2[] scalefac;
+ // private III_scalefac_t scalefac;
+
+ private int max_gr;
+ private int frame_start;
+ private int part2_start;
+ private int channels;
+ private int first_channel;
+ private int last_channel;
+ private int sfreq;
+
+
+ /**
+ * Constructor.
+ */
+ // REVIEW: these constructor arguments should be moved to the
+ // decodeFrame() method, where possible, so that one
+ public LayerIIIDecoder(Bitstream stream0, Header header0,
+ SynthesisFilter filtera, SynthesisFilter filterb,
+ Obuffer buffer0, int which_ch0)
+ {
+ huffcodetab.inithuff();
+ is_1d = new int[SBLIMIT*SSLIMIT+4];
+ ro = new float[2][SBLIMIT][SSLIMIT];
+ lr = new float[2][SBLIMIT][SSLIMIT];
+ out_1d = new float[SBLIMIT*SSLIMIT];
+ prevblck = new float[2][SBLIMIT*SSLIMIT];
+ k = new float[2][SBLIMIT*SSLIMIT];
+ nonzero = new int[2];
+
+ //III_scalefact_t
+ III_scalefac_t = new temporaire2[2];
+ III_scalefac_t[0] = new temporaire2();
+ III_scalefac_t[1] = new temporaire2();
+ scalefac = III_scalefac_t;
+ // L3TABLE INIT
+
+ sfBandIndex = new SBI[9]; // SZD: MPEG2.5 +3 indices
+ int[] l0 = {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576};
+ int[] s0 = {0,4,8,12,18,24,32,42,56,74,100,132,174,192};
+ int[] l1 = {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576};
+ int[] s1 = {0,4,8,12,18,26,36,48,62,80,104,136,180,192};
+ int[] l2 = {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576};
+ int[] s2 = {0,4,8,12,18,26,36,48,62,80,104,134,174,192};
+
+ int[] l3 = {0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,162,196,238,288,342,418,576};
+ int[] s3 = {0,4,8,12,16,22,30,40,52,66,84,106,136,192};
+ int[] l4 = {0,4,8,12,16,20,24,30,36,42,50,60,72,88,106,128,156,190,230,276,330,384,576};
+ int[] s4 = {0,4,8,12,16,22,28,38,50,64,80,100,126,192};
+ int[] l5 = {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576};
+ int[] s5 = {0,4,8,12,16,22,30,42,58,78,104,138,180,192};
+ // SZD: MPEG2.5
+ int[] l6 = {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576};
+ int[] s6 = {0,4,8,12,18,26,36,48,62,80,104,134,174,192};
+ int[] l7 = {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576};
+ int[] s7 = {0,4,8,12,18,26,36,48,62,80,104,134,174,192};
+ int[] l8 = {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576};
+ int[] s8 = {0,8,16,24,36,52,72,96,124,160,162,164,166,192};
+
+ sfBandIndex[0]= new SBI(l0,s0);
+ sfBandIndex[1]= new SBI(l1,s1);
+ sfBandIndex[2]= new SBI(l2,s2);
+
+ sfBandIndex[3]= new SBI(l3,s3);
+ sfBandIndex[4]= new SBI(l4,s4);
+ sfBandIndex[5]= new SBI(l5,s5);
+ //SZD: MPEG2.5
+ sfBandIndex[6]= new SBI(l6,s6);
+ sfBandIndex[7]= new SBI(l7,s7);
+ sfBandIndex[8]= new SBI(l8,s8);
+ // END OF L3TABLE INIT
+
+ if(reorder_table == null) { // SZD: generate LUT
+ reorder_table = new int[9][];
+ for(int i = 0; i < 9; i++)
+ reorder_table[i] = reorder(sfBandIndex[i].s);
+ }
+
+ // Sftable
+ int[] ll0 = {0, 6, 11, 16, 21};
+ int[] ss0 = {0, 6, 12};
+ sftable = new Sftable(ll0,ss0);
+ // END OF Sftable
+
+ // scalefac_buffer
+ scalefac_buffer = new int[54];
+ // END OF scalefac_buffer
+
+ stream = stream0;
+ header = header0;
+ filter1 = filtera;
+ filter2 = filterb;
+ buffer = buffer0;
+ which_channels = which_ch0;
+
+ frame_start = 0;
+ channels = (header.mode() == Header.SINGLE_CHANNEL) ? 1 : 2;
+ max_gr = (header.version() == Header.MPEG1) ? 2 : 1;
+
+ sfreq = header.sample_frequency() +
+ ((header.version() == Header.MPEG1) ? 3 :
+ (header.version() == Header.MPEG25_LSF) ? 6 : 0); // SZD
+
+ if (channels == 2)
+ {
+ switch (which_channels)
+ {
+ case OutputChannels.LEFT_CHANNEL:
+ case OutputChannels.DOWNMIX_CHANNELS:
+ first_channel = last_channel = 0;
+ break;
+
+ case OutputChannels.RIGHT_CHANNEL:
+ first_channel = last_channel = 1;
+ break;
+
+ case OutputChannels.BOTH_CHANNELS:
+ default:
+ first_channel = 0;
+ last_channel = 1;
+ break;
+ }
+ }
+ else
+ {
+ first_channel = last_channel = 0;
+ }
+
+ for(int ch=0;ch<2;ch++)
+ for (int j=0; j<576; j++)
+ prevblck[ch][j] = 0.0f;
+
+ nonzero[0] = nonzero[1] = 576;
+
+ br = new BitReserve();
+ si = new III_side_info_t();
+ }
+
+ /**
+ * Notify decoder that a seek is being made.
+ */
+ public void seek_notify()
+ {
+ frame_start = 0;
+ for(int ch=0;ch<2;ch++)
+ for (int j=0; j<576; j++)
+ prevblck[ch][j] = 0.0f;
+ br = new BitReserve();
+ }
+
+ public void decodeFrame()
+ {
+ decode();
+ }
+
+ /**
+ * Decode one frame, filling the buffer with the output samples.
+ */
+
+ // subband samples are buffered and passed to the
+ // SynthesisFilter in one go.
+ private float[] samples1 = new float[32];
+ private float[] samples2 = new float[32];
+
+ public void decode()
+ {
+ int nSlots = header.slots();
+ int flush_main;
+ int gr, ch, ss, sb, sb18;
+ int main_data_end;
+ int bytes_to_discard;
+ int i;
+
+ get_side_info();
+
+ for (i=0; iOutputChannels
instance
+ * corresponding to the given channel code.
+ *
+ * @param code one of the OutputChannels channel code constants.
+ *
+ * @throws IllegalArgumentException if code is not a valid
+ * channel code.
+ */
+ static public OutputChannels fromInt(int code)
+ {
+ switch (code)
+ {
+ case LEFT_CHANNEL:
+ return LEFT;
+ case RIGHT_CHANNEL:
+ return RIGHT;
+ case BOTH_CHANNELS:
+ return BOTH;
+ case DOWNMIX_CHANNELS:
+ return DOWNMIX;
+ default:
+ throw new IllegalArgumentException("Invalid channel code: "+code);
+ }
+ }
+
+ private OutputChannels(int channels)
+ {
+ outputChannels = channels;
+
+ if (channels<0 || channels>3)
+ throw new IllegalArgumentException("channels");
+ }
+
+ /**
+ * Retrieves the code representing the desired output channels.
+ * Will be one of LEFT_CHANNEL, RIGHT_CHANNEL, BOTH_CHANNELS
+ * or DOWNMIX_CHANNELS.
+ *
+ * @return the channel code represented by this instance.
+ */
+ public int getChannelsOutputCode()
+ {
+ return outputChannels;
+ }
+
+ /**
+ * Retrieves the number of output channels represented
+ * by this channel output type.
+ *
+ * @return The number of output channels for this channel output
+ * type. This will be 2 for BOTH_CHANNELS only, and 1
+ * for all other types.
+ */
+ public int getChannelCount()
+ {
+ int count = (outputChannels==BOTH_CHANNELS) ? 2 : 1;
+ return count;
+ }
+
+
+ public boolean equals(Object o)
+ {
+ boolean equals = false;
+
+ if (o instanceof OutputChannels)
+ {
+ OutputChannels oc = (OutputChannels)o;
+ equals = (oc.outputChannels == outputChannels);
+ }
+
+ return equals;
+ }
+
+ public int hashCode()
+ {
+ return outputChannels;
+ }
+
+}
diff --git a/songdbj/javazoom/jl/decoder/SampleBuffer.java b/songdbj/javazoom/jl/decoder/SampleBuffer.java
new file mode 100644
index 0000000000..ba4bfa060f
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/SampleBuffer.java
@@ -0,0 +1,132 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 12/12/99 Initial Version based on FileObuffer. mdm@techie.com.
+ *
+ * FileObuffer:
+ * 15/02/99 Java Conversion by E.B ,javalayer@javazoom.net
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+/**
+ * The SampleBuffer
class implements an output buffer
+ * that provides storage for a fixed size block of samples.
+ */
+public class SampleBuffer extends Obuffer
+{
+ private short[] buffer;
+ private int[] bufferp;
+ private int channels;
+ private int frequency;
+
+ /**
+ * Constructor
+ */
+ public SampleBuffer(int sample_frequency, int number_of_channels)
+ {
+ buffer = new short[OBUFFERSIZE];
+ bufferp = new int[MAXCHANNELS];
+ channels = number_of_channels;
+ frequency = sample_frequency;
+
+ for (int i = 0; i < number_of_channels; ++i)
+ bufferp[i] = (short)i;
+
+ }
+
+ public int getChannelCount()
+ {
+ return this.channels;
+ }
+
+ public int getSampleFrequency()
+ {
+ return this.frequency;
+ }
+
+ public short[] getBuffer()
+ {
+ return this.buffer;
+ }
+
+ public int getBufferLength()
+ {
+ return bufferp[0];
+ }
+
+ /**
+ * Takes a 16 Bit PCM sample.
+ */
+ public void append(int channel, short value)
+ {
+ buffer[bufferp[channel]] = value;
+ bufferp[channel] += channels;
+ }
+
+ public void appendSamples(int channel, float[] f)
+ {
+ int pos = bufferp[channel];
+
+ short s;
+ float fs;
+ for (int i=0; i<32;)
+ {
+ fs = f[i++];
+ fs = (fs>32767.0f ? 32767.0f
+ : (fs < -32767.0f ? -32767.0f : fs));
+
+ s = (short)fs;
+ buffer[pos] = s;
+ pos += channels;
+ }
+
+ bufferp[channel] = pos;
+ }
+
+
+ /**
+ * Write the samples to the file (Random Acces).
+ */
+ public void write_buffer(int val)
+ {
+
+ //for (int i = 0; i < channels; ++i)
+ // bufferp[i] = (short)i;
+
+ }
+
+ public void close()
+ {}
+
+ /**
+ *
+ */
+ public void clear_buffer()
+ {
+ for (int i = 0; i < channels; ++i)
+ bufferp[i] = (short)i;
+ }
+
+ /**
+ *
+ */
+ public void set_stop_flag()
+ {}
+}
diff --git a/songdbj/javazoom/jl/decoder/Source.java b/songdbj/javazoom/jl/decoder/Source.java
new file mode 100644
index 0000000000..9d6a5d732e
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/Source.java
@@ -0,0 +1,49 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.decoder;
+
+import java.io.IOException;
+
+/**
+ * Work in progress.
+ *
+ * Class to describe a seekable data source.
+ *
+ */
+public interface Source
+{
+
+ public static final long LENGTH_UNKNOWN = -1;
+
+ public int read(byte[] b, int offs, int len)
+ throws IOException;
+
+
+ public boolean willReadBlock();
+
+ public boolean isSeekable();
+
+ public long length();
+
+ public long tell();
+
+ public long seek(long pos);
+
+}
diff --git a/songdbj/javazoom/jl/decoder/SynthesisFilter.java b/songdbj/javazoom/jl/decoder/SynthesisFilter.java
new file mode 100644
index 0000000000..581ab03cc2
--- /dev/null
+++ b/songdbj/javazoom/jl/decoder/SynthesisFilter.java
@@ -0,0 +1,1817 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 04/01/00 Fixes for running under build 23xx Microsoft JVM. mdm.
+ *
+ * 19/12/99 Performance improvements to compute_pcm_samples().
+ * Mat McGowan. mdm@techie.com.
+ *
+ * 16/02/99 Java Conversion by E.B , javalayer@javazoom.net
+ *
+ * @(#) synthesis_filter.h 1.8, last edit: 6/15/94 16:52:00
+ * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
+ * @(#) Berlin University of Technology
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+package javazoom.jl.decoder;
+
+import java.io.IOException;
+
+/**
+ * A class for the synthesis filter bank.
+ * This class does a fast downsampling from 32, 44.1 or 48 kHz to 8 kHz, if ULAW is defined.
+ * Frequencies above 4 kHz are removed by ignoring higher subbands.
+ */
+final class SynthesisFilter
+{
+ private float[] v1;
+ private float[] v2;
+ private float[] actual_v; // v1 or v2
+ private int actual_write_pos; // 0-15
+ private float[] samples; // 32 new subband samples
+ private int channel;
+ private float scalefactor;
+ private float[] eq;
+
+ /**
+ * Quality value for controlling CPU usage/quality tradeoff.
+ */
+ /*
+ private int quality;
+
+ private int v_inc;
+
+
+
+ public static final int HIGH_QUALITY = 1;
+ public static final int MEDIUM_QUALITY = 2;
+ public static final int LOW_QUALITY = 4;
+ */
+
+ /**
+ * Contructor.
+ * The scalefactor scales the calculated float pcm samples to short values
+ * (raw pcm samples are in [-1.0, 1.0], if no violations occur).
+ */
+ public SynthesisFilter(int channelnumber, float factor, float[] eq0)
+ {
+ if (d==null)
+ {
+ d = load_d();
+ d16 = splitArray(d, 16);
+ }
+
+ v1 = new float[512];
+ v2 = new float[512];
+ samples = new float[32];
+ channel = channelnumber;
+ scalefactor = factor;
+ setEQ(eq);
+ //setQuality(HIGH_QUALITY);
+
+ reset();
+ }
+
+ public void setEQ(float[] eq0)
+ {
+ this.eq = eq0;
+ if (eq==null)
+ {
+ eq = new float[32];
+ for (int i=0; i<32; i++)
+ eq[i] = 1.0f;
+ }
+ if (eq.length<32)
+ {
+ throw new IllegalArgumentException("eq0");
+ }
+
+ }
+
+ /*
+ private void setQuality(int quality0)
+ {
+ switch (quality0)
+ {
+ case HIGH_QUALITY:
+ case MEDIUM_QUALITY:
+ case LOW_QUALITY:
+ v_inc = 16 * quality0;
+ quality = quality0;
+ break;
+ default :
+ throw new IllegalArgumentException("Unknown quality value");
+ }
+ }
+
+ public int getQuality()
+ {
+ return quality;
+ }
+ */
+
+ /**
+ * Reset the synthesis filter.
+ */
+ public void reset()
+ {
+ //float[] floatp;
+ // float[] floatp2;
+
+ // initialize v1[] and v2[]:
+ //for (floatp = v1 + 512, floatp2 = v2 + 512; floatp > v1; )
+ // *--floatp = *--floatp2 = 0.0;
+ for (int p=0;p<512;p++)
+ v1[p] = v2[p] = 0.0f;
+
+ // initialize samples[]:
+ //for (floatp = samples + 32; floatp > samples; )
+ // *--floatp = 0.0;
+ for (int p2=0;p2<32;p2++)
+ samples[p2] = 0.0f;
+
+ actual_v = v1;
+ actual_write_pos = 15;
+ }
+
+
+ /**
+ * Inject Sample.
+ */
+ public void input_sample(float sample, int subbandnumber)
+ {
+ samples[subbandnumber] = eq[subbandnumber]*sample;
+ }
+
+ public void input_samples(float[] s)
+ {
+ for (int i=31; i>=0; i--)
+ {
+ samples[i] = s[i]*eq[i];
+ }
+ }
+
+ /**
+ * Compute new values via a fast cosine transform.
+ */
+ private void compute_new_v()
+ {
+ // p is fully initialized from x1
+ //float[] p = _p;
+ // pp is fully initialized from p
+ //float[] pp = _pp;
+
+ //float[] new_v = _new_v;
+
+ //float[] new_v = new float[32]; // new V[0-15] and V[33-48] of Figure 3-A.2 in ISO DIS 11172-3
+ //float[] p = new float[16];
+ //float[] pp = new float[16];
+
+ /*
+ for (int i=31; i>=0; i--)
+ {
+ new_v[i] = 0.0f;
+ }
+ */
+
+ float new_v0, new_v1, new_v2, new_v3, new_v4, new_v5, new_v6, new_v7, new_v8, new_v9;
+ float new_v10, new_v11, new_v12, new_v13, new_v14, new_v15, new_v16, new_v17, new_v18, new_v19;
+ float new_v20, new_v21, new_v22, new_v23, new_v24, new_v25, new_v26, new_v27, new_v28, new_v29;
+ float new_v30, new_v31;
+
+ new_v0 = new_v1 = new_v2 = new_v3 = new_v4 = new_v5 = new_v6 = new_v7 = new_v8 = new_v9 =
+ new_v10 = new_v11 = new_v12 = new_v13 = new_v14 = new_v15 = new_v16 = new_v17 = new_v18 = new_v19 =
+ new_v20 = new_v21 = new_v22 = new_v23 = new_v24 = new_v25 = new_v26 = new_v27 = new_v28 = new_v29 =
+ new_v30 = new_v31 = 0.0f;
+
+
+// float[] new_v = new float[32]; // new V[0-15] and V[33-48] of Figure 3-A.2 in ISO DIS 11172-3
+// float[] p = new float[16];
+// float[] pp = new float[16];
+
+ float[] s = samples;
+
+ float s0 = s[0];
+ float s1 = s[1];
+ float s2 = s[2];
+ float s3 = s[3];
+ float s4 = s[4];
+ float s5 = s[5];
+ float s6 = s[6];
+ float s7 = s[7];
+ float s8 = s[8];
+ float s9 = s[9];
+ float s10 = s[10];
+ float s11 = s[11];
+ float s12 = s[12];
+ float s13 = s[13];
+ float s14 = s[14];
+ float s15 = s[15];
+ float s16 = s[16];
+ float s17 = s[17];
+ float s18 = s[18];
+ float s19 = s[19];
+ float s20 = s[20];
+ float s21 = s[21];
+ float s22 = s[22];
+ float s23 = s[23];
+ float s24 = s[24];
+ float s25 = s[25];
+ float s26 = s[26];
+ float s27 = s[27];
+ float s28 = s[28];
+ float s29 = s[29];
+ float s30 = s[30];
+ float s31 = s[31];
+
+ float p0 = s0 + s31;
+ float p1 = s1 + s30;
+ float p2 = s2 + s29;
+ float p3 = s3 + s28;
+ float p4 = s4 + s27;
+ float p5 = s5 + s26;
+ float p6 = s6 + s25;
+ float p7 = s7 + s24;
+ float p8 = s8 + s23;
+ float p9 = s9 + s22;
+ float p10 = s10 + s21;
+ float p11 = s11 + s20;
+ float p12 = s12 + s19;
+ float p13 = s13 + s18;
+ float p14 = s14 + s17;
+ float p15 = s15 + s16;
+
+ float pp0 = p0 + p15;
+ float pp1 = p1 + p14;
+ float pp2 = p2 + p13;
+ float pp3 = p3 + p12;
+ float pp4 = p4 + p11;
+ float pp5 = p5 + p10;
+ float pp6 = p6 + p9;
+ float pp7 = p7 + p8;
+ float pp8 = (p0 - p15) * cos1_32;
+ float pp9 = (p1 - p14) * cos3_32;
+ float pp10 = (p2 - p13) * cos5_32;
+ float pp11 = (p3 - p12) * cos7_32;
+ float pp12 = (p4 - p11) * cos9_32;
+ float pp13 = (p5 - p10) * cos11_32;
+ float pp14 = (p6 - p9) * cos13_32;
+ float pp15 = (p7 - p8) * cos15_32;
+
+ p0 = pp0 + pp7;
+ p1 = pp1 + pp6;
+ p2 = pp2 + pp5;
+ p3 = pp3 + pp4;
+ p4 = (pp0 - pp7) * cos1_16;
+ p5 = (pp1 - pp6) * cos3_16;
+ p6 = (pp2 - pp5) * cos5_16;
+ p7 = (pp3 - pp4) * cos7_16;
+ p8 = pp8 + pp15;
+ p9 = pp9 + pp14;
+ p10 = pp10 + pp13;
+ p11 = pp11 + pp12;
+ p12 = (pp8 - pp15) * cos1_16;
+ p13 = (pp9 - pp14) * cos3_16;
+ p14 = (pp10 - pp13) * cos5_16;
+ p15 = (pp11 - pp12) * cos7_16;
+
+
+ pp0 = p0 + p3;
+ pp1 = p1 + p2;
+ pp2 = (p0 - p3) * cos1_8;
+ pp3 = (p1 - p2) * cos3_8;
+ pp4 = p4 + p7;
+ pp5 = p5 + p6;
+ pp6 = (p4 - p7) * cos1_8;
+ pp7 = (p5 - p6) * cos3_8;
+ pp8 = p8 + p11;
+ pp9 = p9 + p10;
+ pp10 = (p8 - p11) * cos1_8;
+ pp11 = (p9 - p10) * cos3_8;
+ pp12 = p12 + p15;
+ pp13 = p13 + p14;
+ pp14 = (p12 - p15) * cos1_8;
+ pp15 = (p13 - p14) * cos3_8;
+
+ p0 = pp0 + pp1;
+ p1 = (pp0 - pp1) * cos1_4;
+ p2 = pp2 + pp3;
+ p3 = (pp2 - pp3) * cos1_4;
+ p4 = pp4 + pp5;
+ p5 = (pp4 - pp5) * cos1_4;
+ p6 = pp6 + pp7;
+ p7 = (pp6 - pp7) * cos1_4;
+ p8 = pp8 + pp9;
+ p9 = (pp8 - pp9) * cos1_4;
+ p10 = pp10 + pp11;
+ p11 = (pp10 - pp11) * cos1_4;
+ p12 = pp12 + pp13;
+ p13 = (pp12 - pp13) * cos1_4;
+ p14 = pp14 + pp15;
+ p15 = (pp14 - pp15) * cos1_4;
+
+ // this is pretty insane coding
+ float tmp1;
+ new_v19/*36-17*/ = -(new_v4 = (new_v12 = p7) + p5) - p6;
+ new_v27/*44-17*/ = -p6 - p7 - p4;
+ new_v6 = (new_v10 = (new_v14 = p15) + p11) + p13;
+ new_v17/*34-17*/ = -(new_v2 = p15 + p13 + p9) - p14;
+ new_v21/*38-17*/ = (tmp1 = -p14 - p15 - p10 - p11) - p13;
+ new_v29/*46-17*/ = -p14 - p15 - p12 - p8;
+ new_v25/*42-17*/ = tmp1 - p12;
+ new_v31/*48-17*/ = -p0;
+ new_v0 = p1;
+ new_v23/*40-17*/ = -(new_v8 = p3) - p2;
+
+ p0 = (s0 - s31) * cos1_64;
+ p1 = (s1 - s30) * cos3_64;
+ p2 = (s2 - s29) * cos5_64;
+ p3 = (s3 - s28) * cos7_64;
+ p4 = (s4 - s27) * cos9_64;
+ p5 = (s5 - s26) * cos11_64;
+ p6 = (s6 - s25) * cos13_64;
+ p7 = (s7 - s24) * cos15_64;
+ p8 = (s8 - s23) * cos17_64;
+ p9 = (s9 - s22) * cos19_64;
+ p10 = (s10 - s21) * cos21_64;
+ p11 = (s11 - s20) * cos23_64;
+ p12 = (s12 - s19) * cos25_64;
+ p13 = (s13 - s18) * cos27_64;
+ p14 = (s14 - s17) * cos29_64;
+ p15 = (s15 - s16) * cos31_64;
+
+
+ pp0 = p0 + p15;
+ pp1 = p1 + p14;
+ pp2 = p2 + p13;
+ pp3 = p3 + p12;
+ pp4 = p4 + p11;
+ pp5 = p5 + p10;
+ pp6 = p6 + p9;
+ pp7 = p7 + p8;
+ pp8 = (p0 - p15) * cos1_32;
+ pp9 = (p1 - p14) * cos3_32;
+ pp10 = (p2 - p13) * cos5_32;
+ pp11 = (p3 - p12) * cos7_32;
+ pp12 = (p4 - p11) * cos9_32;
+ pp13 = (p5 - p10) * cos11_32;
+ pp14 = (p6 - p9) * cos13_32;
+ pp15 = (p7 - p8) * cos15_32;
+
+
+ p0 = pp0 + pp7;
+ p1 = pp1 + pp6;
+ p2 = pp2 + pp5;
+ p3 = pp3 + pp4;
+ p4 = (pp0 - pp7) * cos1_16;
+ p5 = (pp1 - pp6) * cos3_16;
+ p6 = (pp2 - pp5) * cos5_16;
+ p7 = (pp3 - pp4) * cos7_16;
+ p8 = pp8 + pp15;
+ p9 = pp9 + pp14;
+ p10 = pp10 + pp13;
+ p11 = pp11 + pp12;
+ p12 = (pp8 - pp15) * cos1_16;
+ p13 = (pp9 - pp14) * cos3_16;
+ p14 = (pp10 - pp13) * cos5_16;
+ p15 = (pp11 - pp12) * cos7_16;
+
+
+ pp0 = p0 + p3;
+ pp1 = p1 + p2;
+ pp2 = (p0 - p3) * cos1_8;
+ pp3 = (p1 - p2) * cos3_8;
+ pp4 = p4 + p7;
+ pp5 = p5 + p6;
+ pp6 = (p4 - p7) * cos1_8;
+ pp7 = (p5 - p6) * cos3_8;
+ pp8 = p8 + p11;
+ pp9 = p9 + p10;
+ pp10 = (p8 - p11) * cos1_8;
+ pp11 = (p9 - p10) * cos3_8;
+ pp12 = p12 + p15;
+ pp13 = p13 + p14;
+ pp14 = (p12 - p15) * cos1_8;
+ pp15 = (p13 - p14) * cos3_8;
+
+
+ p0 = pp0 + pp1;
+ p1 = (pp0 - pp1) * cos1_4;
+ p2 = pp2 + pp3;
+ p3 = (pp2 - pp3) * cos1_4;
+ p4 = pp4 + pp5;
+ p5 = (pp4 - pp5) * cos1_4;
+ p6 = pp6 + pp7;
+ p7 = (pp6 - pp7) * cos1_4;
+ p8 = pp8 + pp9;
+ p9 = (pp8 - pp9) * cos1_4;
+ p10 = pp10 + pp11;
+ p11 = (pp10 - pp11) * cos1_4;
+ p12 = pp12 + pp13;
+ p13 = (pp12 - pp13) * cos1_4;
+ p14 = pp14 + pp15;
+ p15 = (pp14 - pp15) * cos1_4;
+
+
+ // manually doing something that a compiler should handle sucks
+ // coding like this is hard to read
+ float tmp2;
+ new_v5 = (new_v11 = (new_v13 = (new_v15 = p15) + p7) + p11)
+ + p5 + p13;
+ new_v7 = (new_v9 = p15 + p11 + p3) + p13;
+ new_v16/*33-17*/ = -(new_v1 = (tmp1 = p13 + p15 + p9) + p1) - p14;
+ new_v18/*35-17*/ = -(new_v3 = tmp1 + p5 + p7) - p6 - p14;
+
+ new_v22/*39-17*/ = (tmp1 = -p10 - p11 - p14 - p15)
+ - p13 - p2 - p3;
+ new_v20/*37-17*/ = tmp1 - p13 - p5 - p6 - p7;
+ new_v24/*41-17*/ = tmp1 - p12 - p2 - p3;
+ new_v26/*43-17*/ = tmp1 - p12 - (tmp2 = p4 + p6 + p7);
+ new_v30/*47-17*/ = (tmp1 = -p8 - p12 - p14 - p15) - p0;
+ new_v28/*45-17*/ = tmp1 - tmp2;
+
+ // insert V[0-15] (== new_v[0-15]) into actual v:
+ // float[] x2 = actual_v + actual_write_pos;
+ float dest[] = actual_v;
+
+ int pos = actual_write_pos;
+
+ dest[0 + pos] = new_v0;
+ dest[16 + pos] = new_v1;
+ dest[32 + pos] = new_v2;
+ dest[48 + pos] = new_v3;
+ dest[64 + pos] = new_v4;
+ dest[80 + pos] = new_v5;
+ dest[96 + pos] = new_v6;
+ dest[112 + pos] = new_v7;
+ dest[128 + pos] = new_v8;
+ dest[144 + pos] = new_v9;
+ dest[160 + pos] = new_v10;
+ dest[176 + pos] = new_v11;
+ dest[192 + pos] = new_v12;
+ dest[208 + pos] = new_v13;
+ dest[224 + pos] = new_v14;
+ dest[240 + pos] = new_v15;
+
+ // V[16] is always 0.0:
+ dest[256 + pos] = 0.0f;
+
+ // insert V[17-31] (== -new_v[15-1]) into actual v:
+ dest[272 + pos] = -new_v15;
+ dest[288 + pos] = -new_v14;
+ dest[304 + pos] = -new_v13;
+ dest[320 + pos] = -new_v12;
+ dest[336 + pos] = -new_v11;
+ dest[352 + pos] = -new_v10;
+ dest[368 + pos] = -new_v9;
+ dest[384 + pos] = -new_v8;
+ dest[400 + pos] = -new_v7;
+ dest[416 + pos] = -new_v6;
+ dest[432 + pos] = -new_v5;
+ dest[448 + pos] = -new_v4;
+ dest[464 + pos] = -new_v3;
+ dest[480 + pos] = -new_v2;
+ dest[496 + pos] = -new_v1;
+
+ // insert V[32] (== -new_v[0]) into other v:
+ dest = (actual_v==v1) ? v2 : v1;
+
+ dest[0 + pos] = -new_v0;
+ // insert V[33-48] (== new_v[16-31]) into other v:
+ dest[16 + pos] = new_v16;
+ dest[32 + pos] = new_v17;
+ dest[48 + pos] = new_v18;
+ dest[64 + pos] = new_v19;
+ dest[80 + pos] = new_v20;
+ dest[96 + pos] = new_v21;
+ dest[112 + pos] = new_v22;
+ dest[128 + pos] = new_v23;
+ dest[144 + pos] = new_v24;
+ dest[160 + pos] = new_v25;
+ dest[176 + pos] = new_v26;
+ dest[192 + pos] = new_v27;
+ dest[208 + pos] = new_v28;
+ dest[224 + pos] = new_v29;
+ dest[240 + pos] = new_v30;
+ dest[256 + pos] = new_v31;
+
+ // insert V[49-63] (== new_v[30-16]) into other v:
+ dest[272 + pos] = new_v30;
+ dest[288 + pos] = new_v29;
+ dest[304 + pos] = new_v28;
+ dest[320 + pos] = new_v27;
+ dest[336 + pos] = new_v26;
+ dest[352 + pos] = new_v25;
+ dest[368 + pos] = new_v24;
+ dest[384 + pos] = new_v23;
+ dest[400 + pos] = new_v22;
+ dest[416 + pos] = new_v21;
+ dest[432 + pos] = new_v20;
+ dest[448 + pos] = new_v19;
+ dest[464 + pos] = new_v18;
+ dest[480 + pos] = new_v17;
+ dest[496 + pos] = new_v16;
+/*
+ }
+ else
+ {
+ v1[0 + actual_write_pos] = -new_v0;
+ // insert V[33-48] (== new_v[16-31]) into other v:
+ v1[16 + actual_write_pos] = new_v16;
+ v1[32 + actual_write_pos] = new_v17;
+ v1[48 + actual_write_pos] = new_v18;
+ v1[64 + actual_write_pos] = new_v19;
+ v1[80 + actual_write_pos] = new_v20;
+ v1[96 + actual_write_pos] = new_v21;
+ v1[112 + actual_write_pos] = new_v22;
+ v1[128 + actual_write_pos] = new_v23;
+ v1[144 + actual_write_pos] = new_v24;
+ v1[160 + actual_write_pos] = new_v25;
+ v1[176 + actual_write_pos] = new_v26;
+ v1[192 + actual_write_pos] = new_v27;
+ v1[208 + actual_write_pos] = new_v28;
+ v1[224 + actual_write_pos] = new_v29;
+ v1[240 + actual_write_pos] = new_v30;
+ v1[256 + actual_write_pos] = new_v31;
+
+ // insert V[49-63] (== new_v[30-16]) into other v:
+ v1[272 + actual_write_pos] = new_v30;
+ v1[288 + actual_write_pos] = new_v29;
+ v1[304 + actual_write_pos] = new_v28;
+ v1[320 + actual_write_pos] = new_v27;
+ v1[336 + actual_write_pos] = new_v26;
+ v1[352 + actual_write_pos] = new_v25;
+ v1[368 + actual_write_pos] = new_v24;
+ v1[384 + actual_write_pos] = new_v23;
+ v1[400 + actual_write_pos] = new_v22;
+ v1[416 + actual_write_pos] = new_v21;
+ v1[432 + actual_write_pos] = new_v20;
+ v1[448 + actual_write_pos] = new_v19;
+ v1[464 + actual_write_pos] = new_v18;
+ v1[480 + actual_write_pos] = new_v17;
+ v1[496 + actual_write_pos] = new_v16;
+ }
+*/
+ }
+
+ /**
+ * Compute new values via a fast cosine transform.
+ */
+ private void compute_new_v_old()
+ {
+ // p is fully initialized from x1
+ //float[] p = _p;
+ // pp is fully initialized from p
+ //float[] pp = _pp;
+
+ //float[] new_v = _new_v;
+
+ float[] new_v = new float[32]; // new V[0-15] and V[33-48] of Figure 3-A.2 in ISO DIS 11172-3
+ float[] p = new float[16];
+ float[] pp = new float[16];
+
+
+ for (int i=31; i>=0; i--)
+ {
+ new_v[i] = 0.0f;
+ }
+
+// float[] new_v = new float[32]; // new V[0-15] and V[33-48] of Figure 3-A.2 in ISO DIS 11172-3
+// float[] p = new float[16];
+// float[] pp = new float[16];
+
+ float[] x1 = samples;
+
+ p[0] = x1[0] + x1[31];
+ p[1] = x1[1] + x1[30];
+ p[2] = x1[2] + x1[29];
+ p[3] = x1[3] + x1[28];
+ p[4] = x1[4] + x1[27];
+ p[5] = x1[5] + x1[26];
+ p[6] = x1[6] + x1[25];
+ p[7] = x1[7] + x1[24];
+ p[8] = x1[8] + x1[23];
+ p[9] = x1[9] + x1[22];
+ p[10] = x1[10] + x1[21];
+ p[11] = x1[11] + x1[20];
+ p[12] = x1[12] + x1[19];
+ p[13] = x1[13] + x1[18];
+ p[14] = x1[14] + x1[17];
+ p[15] = x1[15] + x1[16];
+
+ pp[0] = p[0] + p[15];
+ pp[1] = p[1] + p[14];
+ pp[2] = p[2] + p[13];
+ pp[3] = p[3] + p[12];
+ pp[4] = p[4] + p[11];
+ pp[5] = p[5] + p[10];
+ pp[6] = p[6] + p[9];
+ pp[7] = p[7] + p[8];
+ pp[8] = (p[0] - p[15]) * cos1_32;
+ pp[9] = (p[1] - p[14]) * cos3_32;
+ pp[10] = (p[2] - p[13]) * cos5_32;
+ pp[11] = (p[3] - p[12]) * cos7_32;
+ pp[12] = (p[4] - p[11]) * cos9_32;
+ pp[13] = (p[5] - p[10]) * cos11_32;
+ pp[14] = (p[6] - p[9]) * cos13_32;
+ pp[15] = (p[7] - p[8]) * cos15_32;
+
+ p[0] = pp[0] + pp[7];
+ p[1] = pp[1] + pp[6];
+ p[2] = pp[2] + pp[5];
+ p[3] = pp[3] + pp[4];
+ p[4] = (pp[0] - pp[7]) * cos1_16;
+ p[5] = (pp[1] - pp[6]) * cos3_16;
+ p[6] = (pp[2] - pp[5]) * cos5_16;
+ p[7] = (pp[3] - pp[4]) * cos7_16;
+ p[8] = pp[8] + pp[15];
+ p[9] = pp[9] + pp[14];
+ p[10] = pp[10] + pp[13];
+ p[11] = pp[11] + pp[12];
+ p[12] = (pp[8] - pp[15]) * cos1_16;
+ p[13] = (pp[9] - pp[14]) * cos3_16;
+ p[14] = (pp[10] - pp[13]) * cos5_16;
+ p[15] = (pp[11] - pp[12]) * cos7_16;
+
+
+ pp[0] = p[0] + p[3];
+ pp[1] = p[1] + p[2];
+ pp[2] = (p[0] - p[3]) * cos1_8;
+ pp[3] = (p[1] - p[2]) * cos3_8;
+ pp[4] = p[4] + p[7];
+ pp[5] = p[5] + p[6];
+ pp[6] = (p[4] - p[7]) * cos1_8;
+ pp[7] = (p[5] - p[6]) * cos3_8;
+ pp[8] = p[8] + p[11];
+ pp[9] = p[9] + p[10];
+ pp[10] = (p[8] - p[11]) * cos1_8;
+ pp[11] = (p[9] - p[10]) * cos3_8;
+ pp[12] = p[12] + p[15];
+ pp[13] = p[13] + p[14];
+ pp[14] = (p[12] - p[15]) * cos1_8;
+ pp[15] = (p[13] - p[14]) * cos3_8;
+
+ p[0] = pp[0] + pp[1];
+ p[1] = (pp[0] - pp[1]) * cos1_4;
+ p[2] = pp[2] + pp[3];
+ p[3] = (pp[2] - pp[3]) * cos1_4;
+ p[4] = pp[4] + pp[5];
+ p[5] = (pp[4] - pp[5]) * cos1_4;
+ p[6] = pp[6] + pp[7];
+ p[7] = (pp[6] - pp[7]) * cos1_4;
+ p[8] = pp[8] + pp[9];
+ p[9] = (pp[8] - pp[9]) * cos1_4;
+ p[10] = pp[10] + pp[11];
+ p[11] = (pp[10] - pp[11]) * cos1_4;
+ p[12] = pp[12] + pp[13];
+ p[13] = (pp[12] - pp[13]) * cos1_4;
+ p[14] = pp[14] + pp[15];
+ p[15] = (pp[14] - pp[15]) * cos1_4;
+
+ // this is pretty insane coding
+ float tmp1;
+ new_v[36-17] = -(new_v[4] = (new_v[12] = p[7]) + p[5]) - p[6];
+ new_v[44-17] = -p[6] - p[7] - p[4];
+ new_v[6] = (new_v[10] = (new_v[14] = p[15]) + p[11]) + p[13];
+ new_v[34-17] = -(new_v[2] = p[15] + p[13] + p[9]) - p[14];
+ new_v[38-17] = (tmp1 = -p[14] - p[15] - p[10] - p[11]) - p[13];
+ new_v[46-17] = -p[14] - p[15] - p[12] - p[8];
+ new_v[42-17] = tmp1 - p[12];
+ new_v[48-17] = -p[0];
+ new_v[0] = p[1];
+ new_v[40-17] = -(new_v[8] = p[3]) - p[2];
+
+ p[0] = (x1[0] - x1[31]) * cos1_64;
+ p[1] = (x1[1] - x1[30]) * cos3_64;
+ p[2] = (x1[2] - x1[29]) * cos5_64;
+ p[3] = (x1[3] - x1[28]) * cos7_64;
+ p[4] = (x1[4] - x1[27]) * cos9_64;
+ p[5] = (x1[5] - x1[26]) * cos11_64;
+ p[6] = (x1[6] - x1[25]) * cos13_64;
+ p[7] = (x1[7] - x1[24]) * cos15_64;
+ p[8] = (x1[8] - x1[23]) * cos17_64;
+ p[9] = (x1[9] - x1[22]) * cos19_64;
+ p[10] = (x1[10] - x1[21]) * cos21_64;
+ p[11] = (x1[11] - x1[20]) * cos23_64;
+ p[12] = (x1[12] - x1[19]) * cos25_64;
+ p[13] = (x1[13] - x1[18]) * cos27_64;
+ p[14] = (x1[14] - x1[17]) * cos29_64;
+ p[15] = (x1[15] - x1[16]) * cos31_64;
+
+
+ pp[0] = p[0] + p[15];
+ pp[1] = p[1] + p[14];
+ pp[2] = p[2] + p[13];
+ pp[3] = p[3] + p[12];
+ pp[4] = p[4] + p[11];
+ pp[5] = p[5] + p[10];
+ pp[6] = p[6] + p[9];
+ pp[7] = p[7] + p[8];
+ pp[8] = (p[0] - p[15]) * cos1_32;
+ pp[9] = (p[1] - p[14]) * cos3_32;
+ pp[10] = (p[2] - p[13]) * cos5_32;
+ pp[11] = (p[3] - p[12]) * cos7_32;
+ pp[12] = (p[4] - p[11]) * cos9_32;
+ pp[13] = (p[5] - p[10]) * cos11_32;
+ pp[14] = (p[6] - p[9]) * cos13_32;
+ pp[15] = (p[7] - p[8]) * cos15_32;
+
+
+ p[0] = pp[0] + pp[7];
+ p[1] = pp[1] + pp[6];
+ p[2] = pp[2] + pp[5];
+ p[3] = pp[3] + pp[4];
+ p[4] = (pp[0] - pp[7]) * cos1_16;
+ p[5] = (pp[1] - pp[6]) * cos3_16;
+ p[6] = (pp[2] - pp[5]) * cos5_16;
+ p[7] = (pp[3] - pp[4]) * cos7_16;
+ p[8] = pp[8] + pp[15];
+ p[9] = pp[9] + pp[14];
+ p[10] = pp[10] + pp[13];
+ p[11] = pp[11] + pp[12];
+ p[12] = (pp[8] - pp[15]) * cos1_16;
+ p[13] = (pp[9] - pp[14]) * cos3_16;
+ p[14] = (pp[10] - pp[13]) * cos5_16;
+ p[15] = (pp[11] - pp[12]) * cos7_16;
+
+
+ pp[0] = p[0] + p[3];
+ pp[1] = p[1] + p[2];
+ pp[2] = (p[0] - p[3]) * cos1_8;
+ pp[3] = (p[1] - p[2]) * cos3_8;
+ pp[4] = p[4] + p[7];
+ pp[5] = p[5] + p[6];
+ pp[6] = (p[4] - p[7]) * cos1_8;
+ pp[7] = (p[5] - p[6]) * cos3_8;
+ pp[8] = p[8] + p[11];
+ pp[9] = p[9] + p[10];
+ pp[10] = (p[8] - p[11]) * cos1_8;
+ pp[11] = (p[9] - p[10]) * cos3_8;
+ pp[12] = p[12] + p[15];
+ pp[13] = p[13] + p[14];
+ pp[14] = (p[12] - p[15]) * cos1_8;
+ pp[15] = (p[13] - p[14]) * cos3_8;
+
+
+ p[0] = pp[0] + pp[1];
+ p[1] = (pp[0] - pp[1]) * cos1_4;
+ p[2] = pp[2] + pp[3];
+ p[3] = (pp[2] - pp[3]) * cos1_4;
+ p[4] = pp[4] + pp[5];
+ p[5] = (pp[4] - pp[5]) * cos1_4;
+ p[6] = pp[6] + pp[7];
+ p[7] = (pp[6] - pp[7]) * cos1_4;
+ p[8] = pp[8] + pp[9];
+ p[9] = (pp[8] - pp[9]) * cos1_4;
+ p[10] = pp[10] + pp[11];
+ p[11] = (pp[10] - pp[11]) * cos1_4;
+ p[12] = pp[12] + pp[13];
+ p[13] = (pp[12] - pp[13]) * cos1_4;
+ p[14] = pp[14] + pp[15];
+ p[15] = (pp[14] - pp[15]) * cos1_4;
+
+
+ // manually doing something that a compiler should handle sucks
+ // coding like this is hard to read
+ float tmp2;
+ new_v[5] = (new_v[11] = (new_v[13] = (new_v[15] = p[15]) + p[7]) + p[11])
+ + p[5] + p[13];
+ new_v[7] = (new_v[9] = p[15] + p[11] + p[3]) + p[13];
+ new_v[33-17] = -(new_v[1] = (tmp1 = p[13] + p[15] + p[9]) + p[1]) - p[14];
+ new_v[35-17] = -(new_v[3] = tmp1 + p[5] + p[7]) - p[6] - p[14];
+
+ new_v[39-17] = (tmp1 = -p[10] - p[11] - p[14] - p[15])
+ - p[13] - p[2] - p[3];
+ new_v[37-17] = tmp1 - p[13] - p[5] - p[6] - p[7];
+ new_v[41-17] = tmp1 - p[12] - p[2] - p[3];
+ new_v[43-17] = tmp1 - p[12] - (tmp2 = p[4] + p[6] + p[7]);
+ new_v[47-17] = (tmp1 = -p[8] - p[12] - p[14] - p[15]) - p[0];
+ new_v[45-17] = tmp1 - tmp2;
+
+ // insert V[0-15] (== new_v[0-15]) into actual v:
+ x1 = new_v;
+ // float[] x2 = actual_v + actual_write_pos;
+ float[] dest = actual_v;
+
+ dest[0 + actual_write_pos] = x1[0];
+ dest[16 + actual_write_pos] = x1[1];
+ dest[32 + actual_write_pos] = x1[2];
+ dest[48 + actual_write_pos] = x1[3];
+ dest[64 + actual_write_pos] = x1[4];
+ dest[80 + actual_write_pos] = x1[5];
+ dest[96 + actual_write_pos] = x1[6];
+ dest[112 + actual_write_pos] = x1[7];
+ dest[128 + actual_write_pos] = x1[8];
+ dest[144 + actual_write_pos] = x1[9];
+ dest[160 + actual_write_pos] = x1[10];
+ dest[176 + actual_write_pos] = x1[11];
+ dest[192 + actual_write_pos] = x1[12];
+ dest[208 + actual_write_pos] = x1[13];
+ dest[224 + actual_write_pos] = x1[14];
+ dest[240 + actual_write_pos] = x1[15];
+
+ // V[16] is always 0.0:
+ dest[256 + actual_write_pos] = 0.0f;
+
+ // insert V[17-31] (== -new_v[15-1]) into actual v:
+ dest[272 + actual_write_pos] = -x1[15];
+ dest[288 + actual_write_pos] = -x1[14];
+ dest[304 + actual_write_pos] = -x1[13];
+ dest[320 + actual_write_pos] = -x1[12];
+ dest[336 + actual_write_pos] = -x1[11];
+ dest[352 + actual_write_pos] = -x1[10];
+ dest[368 + actual_write_pos] = -x1[9];
+ dest[384 + actual_write_pos] = -x1[8];
+ dest[400 + actual_write_pos] = -x1[7];
+ dest[416 + actual_write_pos] = -x1[6];
+ dest[432 + actual_write_pos] = -x1[5];
+ dest[448 + actual_write_pos] = -x1[4];
+ dest[464 + actual_write_pos] = -x1[3];
+ dest[480 + actual_write_pos] = -x1[2];
+ dest[496 + actual_write_pos] = -x1[1];
+
+ // insert V[32] (== -new_v[0]) into other v:
+
+ }
+
+ /**
+ * Compute PCM Samples.
+ */
+
+ private float[] _tmpOut = new float[32];
+
+
+ private void compute_pcm_samples0(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ float pcm_sample;
+ final float[] dp = d16[i];
+ pcm_sample = (float)(((vp[0 + dvp] * dp[0]) +
+ (vp[15 + dvp] * dp[1]) +
+ (vp[14 + dvp] * dp[2]) +
+ (vp[13 + dvp] * dp[3]) +
+ (vp[12 + dvp] * dp[4]) +
+ (vp[11 + dvp] * dp[5]) +
+ (vp[10 + dvp] * dp[6]) +
+ (vp[9 + dvp] * dp[7]) +
+ (vp[8 + dvp] * dp[8]) +
+ (vp[7 + dvp] * dp[9]) +
+ (vp[6 + dvp] * dp[10]) +
+ (vp[5 + dvp] * dp[11]) +
+ (vp[4 + dvp] * dp[12]) +
+ (vp[3 + dvp] * dp[13]) +
+ (vp[2 + dvp] * dp[14]) +
+ (vp[1 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples1(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[1 + dvp] * dp[0]) +
+ (vp[0 + dvp] * dp[1]) +
+ (vp[15 + dvp] * dp[2]) +
+ (vp[14 + dvp] * dp[3]) +
+ (vp[13 + dvp] * dp[4]) +
+ (vp[12 + dvp] * dp[5]) +
+ (vp[11 + dvp] * dp[6]) +
+ (vp[10 + dvp] * dp[7]) +
+ (vp[9 + dvp] * dp[8]) +
+ (vp[8 + dvp] * dp[9]) +
+ (vp[7 + dvp] * dp[10]) +
+ (vp[6 + dvp] * dp[11]) +
+ (vp[5 + dvp] * dp[12]) +
+ (vp[4 + dvp] * dp[13]) +
+ (vp[3 + dvp] * dp[14]) +
+ (vp[2 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples2(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[2 + dvp] * dp[0]) +
+ (vp[1 + dvp] * dp[1]) +
+ (vp[0 + dvp] * dp[2]) +
+ (vp[15 + dvp] * dp[3]) +
+ (vp[14 + dvp] * dp[4]) +
+ (vp[13 + dvp] * dp[5]) +
+ (vp[12 + dvp] * dp[6]) +
+ (vp[11 + dvp] * dp[7]) +
+ (vp[10 + dvp] * dp[8]) +
+ (vp[9 + dvp] * dp[9]) +
+ (vp[8 + dvp] * dp[10]) +
+ (vp[7 + dvp] * dp[11]) +
+ (vp[6 + dvp] * dp[12]) +
+ (vp[5 + dvp] * dp[13]) +
+ (vp[4 + dvp] * dp[14]) +
+ (vp[3 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples3(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ int idx = 0;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[3 + dvp] * dp[0]) +
+ (vp[2 + dvp] * dp[1]) +
+ (vp[1 + dvp] * dp[2]) +
+ (vp[0 + dvp] * dp[3]) +
+ (vp[15 + dvp] * dp[4]) +
+ (vp[14 + dvp] * dp[5]) +
+ (vp[13 + dvp] * dp[6]) +
+ (vp[12 + dvp] * dp[7]) +
+ (vp[11 + dvp] * dp[8]) +
+ (vp[10 + dvp] * dp[9]) +
+ (vp[9 + dvp] * dp[10]) +
+ (vp[8 + dvp] * dp[11]) +
+ (vp[7 + dvp] * dp[12]) +
+ (vp[6 + dvp] * dp[13]) +
+ (vp[5 + dvp] * dp[14]) +
+ (vp[4 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples4(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[4 + dvp] * dp[0]) +
+ (vp[3 + dvp] * dp[1]) +
+ (vp[2 + dvp] * dp[2]) +
+ (vp[1 + dvp] * dp[3]) +
+ (vp[0 + dvp] * dp[4]) +
+ (vp[15 + dvp] * dp[5]) +
+ (vp[14 + dvp] * dp[6]) +
+ (vp[13 + dvp] * dp[7]) +
+ (vp[12 + dvp] * dp[8]) +
+ (vp[11 + dvp] * dp[9]) +
+ (vp[10 + dvp] * dp[10]) +
+ (vp[9 + dvp] * dp[11]) +
+ (vp[8 + dvp] * dp[12]) +
+ (vp[7 + dvp] * dp[13]) +
+ (vp[6 + dvp] * dp[14]) +
+ (vp[5 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples5(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[5 + dvp] * dp[0]) +
+ (vp[4 + dvp] * dp[1]) +
+ (vp[3 + dvp] * dp[2]) +
+ (vp[2 + dvp] * dp[3]) +
+ (vp[1 + dvp] * dp[4]) +
+ (vp[0 + dvp] * dp[5]) +
+ (vp[15 + dvp] * dp[6]) +
+ (vp[14 + dvp] * dp[7]) +
+ (vp[13 + dvp] * dp[8]) +
+ (vp[12 + dvp] * dp[9]) +
+ (vp[11 + dvp] * dp[10]) +
+ (vp[10 + dvp] * dp[11]) +
+ (vp[9 + dvp] * dp[12]) +
+ (vp[8 + dvp] * dp[13]) +
+ (vp[7 + dvp] * dp[14]) +
+ (vp[6 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples6(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[6 + dvp] * dp[0]) +
+ (vp[5 + dvp] * dp[1]) +
+ (vp[4 + dvp] * dp[2]) +
+ (vp[3 + dvp] * dp[3]) +
+ (vp[2 + dvp] * dp[4]) +
+ (vp[1 + dvp] * dp[5]) +
+ (vp[0 + dvp] * dp[6]) +
+ (vp[15 + dvp] * dp[7]) +
+ (vp[14 + dvp] * dp[8]) +
+ (vp[13 + dvp] * dp[9]) +
+ (vp[12 + dvp] * dp[10]) +
+ (vp[11 + dvp] * dp[11]) +
+ (vp[10 + dvp] * dp[12]) +
+ (vp[9 + dvp] * dp[13]) +
+ (vp[8 + dvp] * dp[14]) +
+ (vp[7 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples7(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[7 + dvp] * dp[0]) +
+ (vp[6 + dvp] * dp[1]) +
+ (vp[5 + dvp] * dp[2]) +
+ (vp[4 + dvp] * dp[3]) +
+ (vp[3 + dvp] * dp[4]) +
+ (vp[2 + dvp] * dp[5]) +
+ (vp[1 + dvp] * dp[6]) +
+ (vp[0 + dvp] * dp[7]) +
+ (vp[15 + dvp] * dp[8]) +
+ (vp[14 + dvp] * dp[9]) +
+ (vp[13 + dvp] * dp[10]) +
+ (vp[12 + dvp] * dp[11]) +
+ (vp[11 + dvp] * dp[12]) +
+ (vp[10 + dvp] * dp[13]) +
+ (vp[9 + dvp] * dp[14]) +
+ (vp[8 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples8(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[8 + dvp] * dp[0]) +
+ (vp[7 + dvp] * dp[1]) +
+ (vp[6 + dvp] * dp[2]) +
+ (vp[5 + dvp] * dp[3]) +
+ (vp[4 + dvp] * dp[4]) +
+ (vp[3 + dvp] * dp[5]) +
+ (vp[2 + dvp] * dp[6]) +
+ (vp[1 + dvp] * dp[7]) +
+ (vp[0 + dvp] * dp[8]) +
+ (vp[15 + dvp] * dp[9]) +
+ (vp[14 + dvp] * dp[10]) +
+ (vp[13 + dvp] * dp[11]) +
+ (vp[12 + dvp] * dp[12]) +
+ (vp[11 + dvp] * dp[13]) +
+ (vp[10 + dvp] * dp[14]) +
+ (vp[9 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples9(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[9 + dvp] * dp[0]) +
+ (vp[8 + dvp] * dp[1]) +
+ (vp[7 + dvp] * dp[2]) +
+ (vp[6 + dvp] * dp[3]) +
+ (vp[5 + dvp] * dp[4]) +
+ (vp[4 + dvp] * dp[5]) +
+ (vp[3 + dvp] * dp[6]) +
+ (vp[2 + dvp] * dp[7]) +
+ (vp[1 + dvp] * dp[8]) +
+ (vp[0 + dvp] * dp[9]) +
+ (vp[15 + dvp] * dp[10]) +
+ (vp[14 + dvp] * dp[11]) +
+ (vp[13 + dvp] * dp[12]) +
+ (vp[12 + dvp] * dp[13]) +
+ (vp[11 + dvp] * dp[14]) +
+ (vp[10 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+
+ private void compute_pcm_samples10(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[10 + dvp] * dp[0]) +
+ (vp[9 + dvp] * dp[1]) +
+ (vp[8 + dvp] * dp[2]) +
+ (vp[7 + dvp] * dp[3]) +
+ (vp[6 + dvp] * dp[4]) +
+ (vp[5 + dvp] * dp[5]) +
+ (vp[4 + dvp] * dp[6]) +
+ (vp[3 + dvp] * dp[7]) +
+ (vp[2 + dvp] * dp[8]) +
+ (vp[1 + dvp] * dp[9]) +
+ (vp[0 + dvp] * dp[10]) +
+ (vp[15 + dvp] * dp[11]) +
+ (vp[14 + dvp] * dp[12]) +
+ (vp[13 + dvp] * dp[13]) +
+ (vp[12 + dvp] * dp[14]) +
+ (vp[11 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples11(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[11 + dvp] * dp[0]) +
+ (vp[10 + dvp] * dp[1]) +
+ (vp[9 + dvp] * dp[2]) +
+ (vp[8 + dvp] * dp[3]) +
+ (vp[7 + dvp] * dp[4]) +
+ (vp[6 + dvp] * dp[5]) +
+ (vp[5 + dvp] * dp[6]) +
+ (vp[4 + dvp] * dp[7]) +
+ (vp[3 + dvp] * dp[8]) +
+ (vp[2 + dvp] * dp[9]) +
+ (vp[1 + dvp] * dp[10]) +
+ (vp[0 + dvp] * dp[11]) +
+ (vp[15 + dvp] * dp[12]) +
+ (vp[14 + dvp] * dp[13]) +
+ (vp[13 + dvp] * dp[14]) +
+ (vp[12 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples12(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[12 + dvp] * dp[0]) +
+ (vp[11 + dvp] * dp[1]) +
+ (vp[10 + dvp] * dp[2]) +
+ (vp[9 + dvp] * dp[3]) +
+ (vp[8 + dvp] * dp[4]) +
+ (vp[7 + dvp] * dp[5]) +
+ (vp[6 + dvp] * dp[6]) +
+ (vp[5 + dvp] * dp[7]) +
+ (vp[4 + dvp] * dp[8]) +
+ (vp[3 + dvp] * dp[9]) +
+ (vp[2 + dvp] * dp[10]) +
+ (vp[1 + dvp] * dp[11]) +
+ (vp[0 + dvp] * dp[12]) +
+ (vp[15 + dvp] * dp[13]) +
+ (vp[14 + dvp] * dp[14]) +
+ (vp[13 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples13(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[13 + dvp] * dp[0]) +
+ (vp[12 + dvp] * dp[1]) +
+ (vp[11 + dvp] * dp[2]) +
+ (vp[10 + dvp] * dp[3]) +
+ (vp[9 + dvp] * dp[4]) +
+ (vp[8 + dvp] * dp[5]) +
+ (vp[7 + dvp] * dp[6]) +
+ (vp[6 + dvp] * dp[7]) +
+ (vp[5 + dvp] * dp[8]) +
+ (vp[4 + dvp] * dp[9]) +
+ (vp[3 + dvp] * dp[10]) +
+ (vp[2 + dvp] * dp[11]) +
+ (vp[1 + dvp] * dp[12]) +
+ (vp[0 + dvp] * dp[13]) +
+ (vp[15 + dvp] * dp[14]) +
+ (vp[14 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples14(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ final float[] dp = d16[i];
+ float pcm_sample;
+
+ pcm_sample = (float)(((vp[14 + dvp] * dp[0]) +
+ (vp[13 + dvp] * dp[1]) +
+ (vp[12 + dvp] * dp[2]) +
+ (vp[11 + dvp] * dp[3]) +
+ (vp[10 + dvp] * dp[4]) +
+ (vp[9 + dvp] * dp[5]) +
+ (vp[8 + dvp] * dp[6]) +
+ (vp[7 + dvp] * dp[7]) +
+ (vp[6 + dvp] * dp[8]) +
+ (vp[5 + dvp] * dp[9]) +
+ (vp[4 + dvp] * dp[10]) +
+ (vp[3 + dvp] * dp[11]) +
+ (vp[2 + dvp] * dp[12]) +
+ (vp[1 + dvp] * dp[13]) +
+ (vp[0 + dvp] * dp[14]) +
+ (vp[15 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+
+ dvp += 16;
+ } // for
+ }
+ private void compute_pcm_samples15(Obuffer buffer)
+ {
+ final float[] vp = actual_v;
+
+ //int inc = v_inc;
+ final float[] tmpOut = _tmpOut;
+ int dvp =0;
+
+ // fat chance of having this loop unroll
+ for( int i=0; i<32; i++)
+ {
+ float pcm_sample;
+ final float dp[] = d16[i];
+ pcm_sample = (float)(((vp[15 + dvp] * dp[0]) +
+ (vp[14 + dvp] * dp[1]) +
+ (vp[13 + dvp] * dp[2]) +
+ (vp[12 + dvp] * dp[3]) +
+ (vp[11 + dvp] * dp[4]) +
+ (vp[10 + dvp] * dp[5]) +
+ (vp[9 + dvp] * dp[6]) +
+ (vp[8 + dvp] * dp[7]) +
+ (vp[7 + dvp] * dp[8]) +
+ (vp[6 + dvp] * dp[9]) +
+ (vp[5 + dvp] * dp[10]) +
+ (vp[4 + dvp] * dp[11]) +
+ (vp[3 + dvp] * dp[12]) +
+ (vp[2 + dvp] * dp[13]) +
+ (vp[1 + dvp] * dp[14]) +
+ (vp[0 + dvp] * dp[15])
+ ) * scalefactor);
+
+ tmpOut[i] = pcm_sample;
+ dvp += 16;
+ } // for
+ }
+
+private void compute_pcm_samples(Obuffer buffer)
+{
+
+ switch (actual_write_pos)
+ {
+ case 0:
+ compute_pcm_samples0(buffer);
+ break;
+ case 1:
+ compute_pcm_samples1(buffer);
+ break;
+ case 2:
+ compute_pcm_samples2(buffer);
+ break;
+ case 3:
+ compute_pcm_samples3(buffer);
+ break;
+ case 4:
+ compute_pcm_samples4(buffer);
+ break;
+ case 5:
+ compute_pcm_samples5(buffer);
+ break;
+ case 6:
+ compute_pcm_samples6(buffer);
+ break;
+ case 7:
+ compute_pcm_samples7(buffer);
+ break;
+ case 8:
+ compute_pcm_samples8(buffer);
+ break;
+ case 9:
+ compute_pcm_samples9(buffer);
+ break;
+ case 10:
+ compute_pcm_samples10(buffer);
+ break;
+ case 11:
+ compute_pcm_samples11(buffer);
+ break;
+ case 12:
+ compute_pcm_samples12(buffer);
+ break;
+ case 13:
+ compute_pcm_samples13(buffer);
+ break;
+ case 14:
+ compute_pcm_samples14(buffer);
+ break;
+ case 15:
+ compute_pcm_samples15(buffer);
+ break;
+ }
+
+ if (buffer!=null)
+ {
+ buffer.appendSamples(channel, _tmpOut);
+ }
+
+/*
+ // MDM: I was considering putting in quality control for
+ // low-spec CPUs, but the performance gain (about 10-15%)
+ // did not justify the considerable drop in audio quality.
+ switch (inc)
+ {
+ case 16:
+ buffer.appendSamples(channel, tmpOut);
+ break;
+ case 32:
+ for (int i=0; i<16; i++)
+ {
+ buffer.append(channel, (short)tmpOut[i]);
+ buffer.append(channel, (short)tmpOut[i]);
+ }
+ break;
+ case 64:
+ for (int i=0; i<8; i++)
+ {
+ buffer.append(channel, (short)tmpOut[i]);
+ buffer.append(channel, (short)tmpOut[i]);
+ buffer.append(channel, (short)tmpOut[i]);
+ buffer.append(channel, (short)tmpOut[i]);
+ }
+ break;
+
+ }
+*/
+ }
+
+ /**
+ * Calculate 32 PCM samples and put the into the Obuffer-object.
+ */
+
+ public void calculate_pcm_samples(Obuffer buffer)
+ {
+ compute_new_v();
+ compute_pcm_samples(buffer);
+
+ actual_write_pos = (actual_write_pos + 1) & 0xf;
+ actual_v = (actual_v == v1) ? v2 : v1;
+
+ // initialize samples[]:
+ //for (register float *floatp = samples + 32; floatp > samples; )
+ // *--floatp = 0.0f;
+
+ // MDM: this may not be necessary. The Layer III decoder always
+ // outputs 32 subband samples, but I haven't checked layer I & II.
+ for (int p=0;p<32;p++)
+ samples[p] = 0.0f;
+ }
+
+
+ private static final double MY_PI = 3.14159265358979323846;
+ private static final float cos1_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI / 64.0)));
+ private static final float cos3_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 3.0 / 64.0)));
+ private static final float cos5_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 5.0 / 64.0)));
+ private static final float cos7_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 7.0 / 64.0)));
+ private static final float cos9_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 9.0 / 64.0)));
+ private static final float cos11_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 11.0 / 64.0)));
+ private static final float cos13_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 13.0 / 64.0)));
+ private static final float cos15_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 15.0 / 64.0)));
+ private static final float cos17_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 17.0 / 64.0)));
+ private static final float cos19_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 19.0 / 64.0)));
+ private static final float cos21_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 21.0 / 64.0)));
+ private static final float cos23_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 23.0 / 64.0)));
+ private static final float cos25_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 25.0 / 64.0)));
+ private static final float cos27_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 27.0 / 64.0)));
+ private static final float cos29_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 29.0 / 64.0)));
+ private static final float cos31_64 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 31.0 / 64.0)));
+ private static final float cos1_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI / 32.0)));
+ private static final float cos3_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 3.0 / 32.0)));
+ private static final float cos5_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 5.0 / 32.0)));
+ private static final float cos7_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 7.0 / 32.0)));
+ private static final float cos9_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 9.0 / 32.0)));
+ private static final float cos11_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 11.0 / 32.0)));
+ private static final float cos13_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 13.0 / 32.0)));
+ private static final float cos15_32 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 15.0 / 32.0)));
+ private static final float cos1_16 =(float) (1.0 / (2.0 * Math.cos(MY_PI / 16.0)));
+ private static final float cos3_16 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 3.0 / 16.0)));
+ private static final float cos5_16 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 5.0 / 16.0)));
+ private static final float cos7_16 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 7.0 / 16.0)));
+ private static final float cos1_8 =(float) (1.0 / (2.0 * Math.cos(MY_PI / 8.0)));
+ private static final float cos3_8 =(float) (1.0 / (2.0 * Math.cos(MY_PI * 3.0 / 8.0)));
+ private static final float cos1_4 =(float) (1.0 / (2.0 * Math.cos(MY_PI / 4.0)));
+
+ // Note: These values are not in the same order
+ // as in Annex 3-B.3 of the ISO/IEC DIS 11172-3
+ // private float d[] = {0.000000000, -4.000442505};
+
+ private static float d[] = null;
+
+ /**
+ * d[] split into subarrays of length 16. This provides for
+ * more faster access by allowing a block of 16 to be addressed
+ * with constant offset.
+ **/
+ private static float d16[][] = null;
+
+ /**
+ * Loads the data for the d[] from the resource SFd.ser.
+ * @return the loaded values for d[].
+ */
+ static private float[] load_d()
+ {
+ try
+ {
+ Class elemType = Float.TYPE;
+ Object o = JavaLayerUtils.deserializeArrayResource("sfd.ser", elemType, 512);
+ return (float[])o;
+ }
+ catch (IOException ex)
+ {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ /**
+ * Converts a 1D array into a number of smaller arrays. This is used
+ * to achieve offset + constant indexing into an array. Each sub-array
+ * represents a block of values of the original array.
+ * @param array The array to split up into blocks.
+ * @param blockSize The size of the blocks to split the array
+ * into. This must be an exact divisor of
+ * the length of the array, or some data
+ * will be lost from the main array.
+ *
+ * @return An array of arrays in which each element in the returned
+ * array will be of length blockSize
.
+ */
+ static private float[][] splitArray(final float[] array, final int blockSize)
+ {
+ int size = array.length / blockSize;
+ float[][] split = new float[size][];
+ for (int i=0; iAudioDevice
interface provides an abstraction for
+ * a device capable of sounding audio samples. Samples are written to
+ * the device wia the write() method. The device assumes
+ * that these samples are signed 16-bit samples taken at the output frequency
+ * of the decoder. If the decoder outputs more than one channel, the samples for
+ * each channel are assumed to appear consecutively, with the lower numbered
+ * channels preceeding higher-numbered channels. E.g. if there are two
+ * channels, the samples will appear in this order:
+ *
+ *
+ * @since 0.0.8
+ * @author Mat McGowan
+ */
+public interface AudioDevice
+{
+ /**
+ * Prepares the AudioDevice for playback of audio samples.
+ * @param decoder The decoder that will be providing the audio
+ * samples.
+ *
+ * If the audio device is already open, this method returns silently.
+ *
+ */
+ public void open(Decoder decoder) throws JavaLayerException;
+
+ /**
+ * Retrieves the open state of this audio device.
+ *
+ * @return
+ *
+ * l0, r0, l1, r1, l2, r2...
+ *
+ * where
+ * lx indicates the xth sample on channel 0
+ * rx indicates the xth sample on channel 1
+ *
true
if this audio device is open and playing
+ * audio samples, or false
otherwise.
+ */
+ public boolean isOpen();
+
+ /**
+ * Writes a number of samples to this AudioDevice
.
+ *
+ * @param samples The array of signed 16-bit samples to write
+ * to the audio device.
+ * @param offs The offset of the first sample.
+ * @param len The number of samples to write.
+ *
+ * This method may return prior to the samples actually being played
+ * by the audio device.
+ */
+ public void write(short[] samples, int offs, int len) throws JavaLayerException;
+
+
+ /**
+ * Closes this audio device. Any currently playing audio is stopped
+ * as soon as possible. Any previously written audio data that has not been heard
+ * is discarded.
+ *
+ * The implementation should ensure that any threads currently blocking
+ * on the device (e.g. during a write
or flush
+ * operation should be unblocked by this method.
+ */
+ public void close();
+
+
+ /**
+ * Blocks until all audio samples previously written to this audio device have
+ * been heard.
+ */
+ public void flush();
+
+ /**
+ * Retrieves the current playback position in milliseconds.
+ */
+ public int getPosition();
+}
diff --git a/songdbj/javazoom/jl/player/AudioDeviceBase.java b/songdbj/javazoom/jl/player/AudioDeviceBase.java
new file mode 100644
index 0000000000..d9c84f08e7
--- /dev/null
+++ b/songdbj/javazoom/jl/player/AudioDeviceBase.java
@@ -0,0 +1,177 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 29/01/00 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player;
+
+import javazoom.jl.decoder.Decoder;
+import javazoom.jl.decoder.JavaLayerException;
+
+/**
+ * The AudioDeviceBase
class provides a simple thread-safe
+ * implementation of the AudioDevice
interface.
+ * Template methods are provided for subclasses to override and
+ * in doing so provide the implementation for the main operations
+ * of the AudioDevice
interface.
+ *
+ * @since 0.0.8
+ * @author Mat McGowan
+ */
+/*
+ * REVIEW: It is desirable to be able to use the decoder whe
+ * in the implementation of open(), but the decoder
+ * has not yet read a frame, and so much of the
+ * desired information (sample rate, channels etc.)
+ * are not available.
+ */
+public abstract class AudioDeviceBase implements AudioDevice
+{
+ private boolean open = false;
+
+ private Decoder decoder = null;
+
+ /**
+ * Opens this audio device.
+ *
+ * @param decoder The decoder that will provide audio data
+ * to this audio device.
+ */
+ public synchronized void open(Decoder decoder) throws JavaLayerException
+ {
+ if (!isOpen())
+ {
+ this.decoder = decoder;
+ openImpl();
+ setOpen(true);
+ }
+ }
+
+ /**
+ * Template method to provide the
+ * implementation for the opening of the audio device.
+ */
+ protected void openImpl() throws JavaLayerException
+ {
+ }
+
+ /**
+ * Sets the open state for this audio device.
+ */
+ protected void setOpen(boolean open)
+ {
+ this.open = open;
+ }
+
+ /**
+ * Determines if this audio device is open or not.
+ *
+ * @return true
if the audio device is open,
+ * false
if it is not.
+ */
+ public synchronized boolean isOpen()
+ {
+ return open;
+ }
+
+ /**
+ * Closes this audio device. If the device is currently playing
+ * audio, playback is stopped immediately without flushing
+ * any buffered audio data.
+ */
+ public synchronized void close()
+ {
+ if (isOpen())
+ {
+ closeImpl();
+ setOpen(false);
+ decoder = null;
+ }
+ }
+
+ /**
+ * Template method to provide the implementation for
+ * closing the audio device.
+ */
+ protected void closeImpl()
+ {
+ }
+
+ /**
+ * Writes audio data to this audio device. Audio data is
+ * assumed to be in the output format of the decoder. This
+ * method may return before the data has actually been sounded
+ * by the device if the device buffers audio samples.
+ *
+ * @param samples The samples to write to the audio device.
+ * @param offs The offset into the array of the first sample to write.
+ * @param len The number of samples from the array to write.
+ * @throws JavaLayerException if the audio data could not be
+ * written to the audio device.
+ * If the audio device is not open, this method does nthing.
+ */
+ public void write(short[] samples, int offs, int len)
+ throws JavaLayerException
+ {
+ if (isOpen())
+ {
+ writeImpl(samples, offs, len);
+ }
+ }
+
+ /**
+ * Template method to provide the implementation for
+ * writing audio samples to the audio device.
+ */
+ protected void writeImpl(short[] samples, int offs, int len)
+ throws JavaLayerException
+ {
+ }
+
+ /**
+ * Waits for any buffered audio samples to be played by the
+ * audio device. This method should only be called prior
+ * to closing the device.
+ */
+ public void flush()
+ {
+ if (isOpen())
+ {
+ flushImpl();
+ }
+ }
+
+ /**
+ * Template method to provide the implementation for
+ * flushing any buffered audio data.
+ */
+ protected void flushImpl()
+ {
+ }
+
+ /**
+ * Retrieves the decoder that provides audio data to this
+ * audio device.
+ *
+ * @return The associated decoder.
+ */
+ protected Decoder getDecoder()
+ {
+ return decoder;
+ }
+}
diff --git a/songdbj/javazoom/jl/player/AudioDeviceFactory.java b/songdbj/javazoom/jl/player/AudioDeviceFactory.java
new file mode 100644
index 0000000000..2d502d2aad
--- /dev/null
+++ b/songdbj/javazoom/jl/player/AudioDeviceFactory.java
@@ -0,0 +1,87 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 29/01/00 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player;
+
+import javazoom.jl.decoder.JavaLayerException;
+
+/**
+ * An AudioDeviceFactory
class is responsible for creating
+ * a specific AudioDevice
implementation. A factory implementation
+ * can be as simple or complex as desired and may support just one implementation
+ * or may return several implementations depending upon the execution
+ * environment.
+ * AudioDevice
.
+ *
+ * @return a new instance of a specific class of AudioDevice
.
+ * @throws JavaLayerException if an instance of AudioDevice could not
+ * be created.
+ */
+ public abstract AudioDevice createAudioDevice() throws JavaLayerException;
+
+ /**
+ * Creates an instance of an AudioDevice implementation.
+ * @param loader The ClassLoader
to use to
+ * load the named class, or null to use the
+ * system class loader.
+ * @param name The name of the class to load.
+ * @return A newly-created instance of the audio device class.
+ */
+ protected AudioDevice instantiate(ClassLoader loader, String name)
+ throws ClassNotFoundException,
+ IllegalAccessException,
+ InstantiationException
+ {
+ AudioDevice dev = null;
+
+ Class cls = null;
+ if (loader==null)
+ {
+ cls = Class.forName(name);
+ }
+ else
+ {
+ cls = loader.loadClass(name);
+ }
+
+ Object o = cls.newInstance();
+ dev = (AudioDevice)o;
+
+ return dev;
+ }
+}
diff --git a/songdbj/javazoom/jl/player/FactoryRegistry.java b/songdbj/javazoom/jl/player/FactoryRegistry.java
new file mode 100644
index 0000000000..8919995802
--- /dev/null
+++ b/songdbj/javazoom/jl/player/FactoryRegistry.java
@@ -0,0 +1,129 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 29/01/00 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javazoom.jl.decoder.JavaLayerException;
+
+/**
+ * The FactoryRegistry
class stores the factories
+ * for all the audio device implementations available in the system.
+ * AudioDeviceFactory
instance
+ * with this registry.
+ */
+ public void addFactory(AudioDeviceFactory factory)
+ {
+ factories.put(factory.getClass(), factory);
+ }
+
+ public void removeFactoryType(Class cls)
+ {
+ factories.remove(cls);
+ }
+
+ public void removeFactory(AudioDeviceFactory factory)
+ {
+ factories.remove(factory.getClass());
+ }
+
+ public AudioDevice createAudioDevice() throws JavaLayerException
+ {
+ AudioDevice device = null;
+ AudioDeviceFactory[] factories = getFactoriesPriority();
+
+ if (factories==null)
+ throw new JavaLayerException(this+": no factories registered");
+
+ JavaLayerException lastEx = null;
+ for (int i=0; (device==null) && (iNullAudioDevice
implements a silent, no-op
+ * audio device. This is useful for testing purposes.
+ *
+ * @since 0.0.8
+ * @author Mat McGowan
+ */
+public class NullAudioDevice extends AudioDeviceBase
+{
+
+ public int getPosition()
+ {
+ return 0;
+ }
+}
diff --git a/songdbj/javazoom/jl/player/Player.java b/songdbj/javazoom/jl/player/Player.java
new file mode 100644
index 0000000000..32fb1f3051
--- /dev/null
+++ b/songdbj/javazoom/jl/player/Player.java
@@ -0,0 +1,251 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 29/01/00 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player;
+
+import java.io.InputStream;
+
+import javazoom.jl.decoder.Bitstream;
+import javazoom.jl.decoder.BitstreamException;
+import javazoom.jl.decoder.Decoder;
+import javazoom.jl.decoder.Header;
+import javazoom.jl.decoder.JavaLayerException;
+import javazoom.jl.decoder.SampleBuffer;
+
+/**
+ * The Player
class implements a simple player for playback
+ * of an MPEG audio stream.
+ *
+ * @author Mat McGowan
+ * @since 0.0.8
+ */
+
+// REVIEW: the audio device should not be opened until the
+// first MPEG audio frame has been decoded.
+public class Player
+{
+ /**
+ * The current frame number.
+ */
+ private int frame = 0;
+
+ /**
+ * The MPEG audio bitstream.
+ */
+ // javac blank final bug.
+ /*final*/ private Bitstream bitstream;
+
+ /**
+ * The MPEG audio decoder.
+ */
+ /*final*/ private Decoder decoder;
+
+ /**
+ * The AudioDevice the audio samples are written to.
+ */
+ private AudioDevice audio;
+
+ /**
+ * Has the player been closed?
+ */
+ private boolean closed = false;
+
+ /**
+ * Has the player played back all frames from the stream?
+ */
+ private boolean complete = false;
+
+ private int lastPosition = 0;
+
+ /**
+ * Creates a new Player
instance.
+ */
+ public Player(InputStream stream) throws JavaLayerException
+ {
+ this(stream, null);
+ }
+
+ public Player(InputStream stream, AudioDevice device) throws JavaLayerException
+ {
+ bitstream = new Bitstream(stream);
+ decoder = new Decoder();
+
+ if (device!=null)
+ {
+ audio = device;
+ }
+ else
+ {
+ FactoryRegistry r = FactoryRegistry.systemRegistry();
+ audio = r.createAudioDevice();
+ }
+ audio.open(decoder);
+ }
+
+ public void play() throws JavaLayerException
+ {
+ play(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Plays a number of MPEG audio frames.
+ *
+ * @param frames The number of frames to play.
+ * @return true if the last frame was played, or false if there are
+ * more frames.
+ */
+ public boolean play(int frames) throws JavaLayerException
+ {
+ boolean ret = true;
+
+ while (frames-- > 0 && ret)
+ {
+ ret = decodeFrame();
+ }
+
+ if (!ret)
+ {
+ // last frame, ensure all data flushed to the audio device.
+ AudioDevice out = audio;
+ if (out!=null)
+ {
+ out.flush();
+ synchronized (this)
+ {
+ complete = (!closed);
+ close();
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Cloases this player. Any audio currently playing is stopped
+ * immediately.
+ */
+ public synchronized void close()
+ {
+ AudioDevice out = audio;
+ if (out!=null)
+ {
+ closed = true;
+ audio = null;
+ // this may fail, so ensure object state is set up before
+ // calling this method.
+ out.close();
+ lastPosition = out.getPosition();
+ try
+ {
+ bitstream.close();
+ }
+ catch (BitstreamException ex)
+ {
+ }
+ }
+ }
+
+ /**
+ * Returns the completed status of this player.
+ *
+ * @return true if all available MPEG audio frames have been
+ * decoded, or false otherwise.
+ */
+ public synchronized boolean isComplete()
+ {
+ return complete;
+ }
+
+ /**
+ * Retrieves the position in milliseconds of the current audio
+ * sample being played. This method delegates to the
+ * AudioDevice
that is used by this player to sound
+ * the decoded audio samples.
+ */
+ public int getPosition()
+ {
+ int position = lastPosition;
+
+ AudioDevice out = audio;
+ if (out!=null)
+ {
+ position = out.getPosition();
+ }
+ return position;
+ }
+
+ /**
+ * Decodes a single frame.
+ *
+ * @return true if there are no more frames to decode, false otherwise.
+ */
+ protected boolean decodeFrame() throws JavaLayerException
+ {
+ try
+ {
+ AudioDevice out = audio;
+ if (out==null)
+ return false;
+
+ Header h = bitstream.readFrame();
+
+ if (h==null)
+ return false;
+
+ // sample buffer set when decoder constructed
+ SampleBuffer output = (SampleBuffer)decoder.decodeFrame(h, bitstream);
+
+ synchronized (this)
+ {
+ out = audio;
+ if (out!=null)
+ {
+ out.write(output.getBuffer(), 0, output.getBufferLength());
+ }
+ }
+
+ bitstream.closeFrame();
+ }
+ catch (RuntimeException ex)
+ {
+ throw new JavaLayerException("Exception decoding audio frame", ex);
+ }
+/*
+ catch (IOException ex)
+ {
+ System.out.println("exception decoding audio frame: "+ex);
+ return false;
+ }
+ catch (BitstreamException bitex)
+ {
+ System.out.println("exception decoding audio frame: "+bitex);
+ return false;
+ }
+ catch (DecoderException decex)
+ {
+ System.out.println("exception decoding audio frame: "+decex);
+ return false;
+ }
+*/
+ return true;
+ }
+
+
+}
diff --git a/songdbj/javazoom/jl/player/PlayerApplet.java b/songdbj/javazoom/jl/player/PlayerApplet.java
new file mode 100644
index 0000000000..d7c7dc2ffc
--- /dev/null
+++ b/songdbj/javazoom/jl/player/PlayerApplet.java
@@ -0,0 +1,246 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ * 29/01/00 Initial version. mdm@techie.com
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player;
+
+import java.applet.Applet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javazoom.jl.decoder.JavaLayerException;
+
+/**
+ * A simple applet that plays an MPEG audio file.
+ * The URL (relative to the document base)
+ * is passed as the "audioURL" parameter.
+ *
+ * @author Mat McGowan
+ * @since 0.0.8
+ */
+public class PlayerApplet extends Applet implements Runnable
+{
+ static public final String AUDIO_PARAMETER = "audioURL";
+
+ /**
+ * The Player used to play the MPEG audio file.
+ */
+ private Player player = null;
+
+ /**
+ * The thread that runs the player.
+ */
+ private Thread playerThread = null;
+
+ private String fileName = null;
+
+
+ /**
+ * Retrieves the AudioDevice
instance that will
+ * be used to sound the audio data.
+ *
+ * @return an audio device instance that will be used to
+ * sound the audio stream.
+ */
+ protected AudioDevice getAudioDevice() throws JavaLayerException
+ {
+ return FactoryRegistry.systemRegistry().createAudioDevice();
+ }
+
+ /**
+ * Retrieves the InputStream that provides the MPEG audio
+ * stream data.
+ *
+ * @return an InputStream from which the MPEG audio data
+ * is read, or null if an error occurs.
+ */
+ protected InputStream getAudioStream()
+ {
+ InputStream in = null;
+
+ try
+ {
+ URL url = getAudioURL();
+ if (url!=null)
+ in = url.openStream();
+ }
+ catch (IOException ex)
+ {
+ System.err.println(ex);
+ }
+ return in;
+ }
+
+ protected String getAudioFileName()
+ {
+ String urlString = fileName;
+ if (urlString==null)
+ {
+ urlString = getParameter(AUDIO_PARAMETER);
+ }
+ return urlString;
+ }
+
+ protected URL getAudioURL()
+ {
+ String urlString = getAudioFileName();
+ URL url = null;
+ if (urlString!=null)
+ {
+ try
+ {
+ url = new URL(getDocumentBase(), urlString);
+ }
+ catch (Exception ex)
+ {
+ System.err.println(ex);
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Sets the URL of the audio stream to play.
+ */
+ public void setFileName(String name)
+ {
+ fileName = name;
+ }
+
+ public String getFileName()
+ {
+ return fileName;
+ }
+
+ /**
+ * Stops the audio player. If the player is already stopped
+ * this method is a no-op.
+ */
+ protected void stopPlayer() throws JavaLayerException
+ {
+ if (player!=null)
+ {
+ player.close();
+ player = null;
+ playerThread = null;
+ }
+ }
+
+ /**
+ * Decompresses audio data from an InputStream and plays it
+ * back through an AudioDevice. The playback is run on a newly
+ * created thread.
+ *
+ * @param in The InputStream that provides the MPEG audio data.
+ * @param dev The AudioDevice to use to sound the decompressed data.
+ *
+ * @throws JavaLayerException if there was a problem decoding
+ * or playing the audio data.
+ */
+ protected void play(InputStream in, AudioDevice dev) throws JavaLayerException
+ {
+ stopPlayer();
+
+ if (in!=null && dev!=null)
+ {
+ player = new Player(in, dev);
+ playerThread = createPlayerThread();
+ playerThread.start();
+ }
+ }
+
+ /**
+ * Creates a new thread used to run the audio player.
+ * @return A new Thread that, once started, runs the audio player.
+ */
+ protected Thread createPlayerThread()
+ {
+ return new Thread(this, "Audio player thread");
+ }
+
+ /**
+ * Initializes this applet.
+ */
+ public void init()
+ {
+ }
+
+ /**
+ * Starts this applet. An input stream and audio device
+ * are created and passed to the play() method.
+ */
+ public void start()
+ {
+ String name = getAudioFileName();
+ try
+ {
+ InputStream in = getAudioStream();
+ AudioDevice dev = getAudioDevice();
+ play(in, dev);
+ }
+ catch (JavaLayerException ex)
+ {
+ synchronized (System.err)
+ {
+ System.err.println("Unable to play "+name);
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+
+ /**
+ * Stops this applet. If audio is currently playing, it is
+ * stopped.
+ */
+ public void stop()
+ {
+ try
+ {
+ stopPlayer();
+ }
+ catch (JavaLayerException ex)
+ {
+ System.err.println(ex);
+ }
+ }
+
+ public void destroy()
+ {
+ }
+
+ /**
+ * The run method for the audio player thread. Simply calls
+ * play() on the player to play the entire stream.
+ */
+ public void run()
+ {
+ if (player!=null)
+ {
+ try
+ {
+ player.play();
+ }
+ catch (JavaLayerException ex)
+ {
+ System.err.println("Problem playing audio: "+ex);
+ }
+ }
+ }
+}
diff --git a/songdbj/javazoom/jl/player/advanced/AdvancedPlayer.java b/songdbj/javazoom/jl/player/advanced/AdvancedPlayer.java
new file mode 100644
index 0000000000..45a31e46e1
--- /dev/null
+++ b/songdbj/javazoom/jl/player/advanced/AdvancedPlayer.java
@@ -0,0 +1,242 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player.advanced;
+
+import java.io.InputStream;
+
+import javazoom.jl.decoder.Bitstream;
+import javazoom.jl.decoder.BitstreamException;
+import javazoom.jl.decoder.Decoder;
+import javazoom.jl.decoder.Header;
+import javazoom.jl.decoder.JavaLayerException;
+import javazoom.jl.decoder.SampleBuffer;
+import javazoom.jl.player.AudioDevice;
+import javazoom.jl.player.FactoryRegistry;
+
+/**
+ * a hybrid of javazoom.jl.player.Player tweeked to include play(startFrame, endFrame)
+ * hopefully this will be included in the api
+ */
+public class AdvancedPlayer
+{
+ /** The MPEG audio bitstream.*/
+ private Bitstream bitstream;
+ /** The MPEG audio decoder. */
+ private Decoder decoder;
+ /** The AudioDevice the audio samples are written to. */
+ private AudioDevice audio;
+ /** Has the player been closed? */
+ private boolean closed = false;
+ /** Has the player played back all frames from the stream? */
+ private boolean complete = false;
+ private int lastPosition = 0;
+ /** Listener for the playback process */
+ private PlaybackListener listener;
+
+ /**
+ * Creates a new Player
instance.
+ */
+ public AdvancedPlayer(InputStream stream) throws JavaLayerException
+ {
+ this(stream, null);
+ }
+
+ public AdvancedPlayer(InputStream stream, AudioDevice device) throws JavaLayerException
+ {
+ bitstream = new Bitstream(stream);
+
+ if (device!=null) audio = device;
+ else audio = FactoryRegistry.systemRegistry().createAudioDevice();
+ audio.open(decoder = new Decoder());
+ }
+
+ public void play() throws JavaLayerException
+ {
+ play(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Plays a number of MPEG audio frames.
+ *
+ * @param frames The number of frames to play.
+ * @return true if the last frame was played, or false if there are
+ * more frames.
+ */
+ public boolean play(int frames) throws JavaLayerException
+ {
+ boolean ret = true;
+
+ // report to listener
+ if(listener != null) listener.playbackStarted(createEvent(PlaybackEvent.STARTED));
+
+ while (frames-- > 0 && ret)
+ {
+ ret = decodeFrame();
+ }
+
+// if (!ret)
+ {
+ // last frame, ensure all data flushed to the audio device.
+ AudioDevice out = audio;
+ if (out != null)
+ {
+// System.out.println(audio.getPosition());
+ out.flush();
+// System.out.println(audio.getPosition());
+ synchronized (this)
+ {
+ complete = (!closed);
+ close();
+ }
+
+ // report to listener
+ if(listener != null) listener.playbackFinished(createEvent(out, PlaybackEvent.STOPPED));
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Cloases this player. Any audio currently playing is stopped
+ * immediately.
+ */
+ public synchronized void close()
+ {
+ AudioDevice out = audio;
+ if (out != null)
+ {
+ closed = true;
+ audio = null;
+ // this may fail, so ensure object state is set up before
+ // calling this method.
+ out.close();
+ lastPosition = out.getPosition();
+ try
+ {
+ bitstream.close();
+ }
+ catch (BitstreamException ex)
+ {}
+ }
+ }
+
+ /**
+ * Decodes a single frame.
+ *
+ * @return true if there are no more frames to decode, false otherwise.
+ */
+ protected boolean decodeFrame() throws JavaLayerException
+ {
+ try
+ {
+ AudioDevice out = audio;
+ if (out == null) return false;
+
+ Header h = bitstream.readFrame();
+ if (h == null) return false;
+
+ // sample buffer set when decoder constructed
+ SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream);
+
+ synchronized (this)
+ {
+ out = audio;
+ if(out != null)
+ {
+ out.write(output.getBuffer(), 0, output.getBufferLength());
+ }
+ }
+
+ bitstream.closeFrame();
+ }
+ catch (RuntimeException ex)
+ {
+ throw new JavaLayerException("Exception decoding audio frame", ex);
+ }
+ return true;
+ }
+
+ /**
+ * skips over a single frame
+ * @return false if there are no more frames to decode, true otherwise.
+ */
+ protected boolean skipFrame() throws JavaLayerException
+ {
+ Header h = bitstream.readFrame();
+ if (h == null) return false;
+ bitstream.closeFrame();
+ return true;
+ }
+
+ /**
+ * Plays a range of MPEG audio frames
+ * @param start The first frame to play
+ * @param end The last frame to play
+ * @return true if the last frame was played, or false if there are more frames.
+ */
+ public boolean play(final int start, final int end) throws JavaLayerException
+ {
+ boolean ret = true;
+ int offset = start;
+ while (offset-- > 0 && ret) ret = skipFrame();
+ return play(end - start);
+ }
+
+ /**
+ * Constructs a PlaybackEvent
+ */
+ private PlaybackEvent createEvent(int id)
+ {
+ return createEvent(audio, id);
+ }
+
+ /**
+ * Constructs a PlaybackEvent
+ */
+ private PlaybackEvent createEvent(AudioDevice dev, int id)
+ {
+ return new PlaybackEvent(this, id, dev.getPosition());
+ }
+
+ /**
+ * sets the PlaybackListener
+ */
+ public void setPlayBackListener(PlaybackListener listener)
+ {
+ this.listener = listener;
+ }
+
+ /**
+ * gets the PlaybackListener
+ */
+ public PlaybackListener getPlayBackListener()
+ {
+ return listener;
+ }
+
+ /**
+ * closes the player and notifies PlaybackListener
+ */
+ public void stop()
+ {
+ listener.playbackFinished(createEvent(PlaybackEvent.STOPPED));
+ close();
+ }
+}
\ No newline at end of file
diff --git a/songdbj/javazoom/jl/player/advanced/PlaybackEvent.java b/songdbj/javazoom/jl/player/advanced/PlaybackEvent.java
new file mode 100644
index 0000000000..08e3cae958
--- /dev/null
+++ b/songdbj/javazoom/jl/player/advanced/PlaybackEvent.java
@@ -0,0 +1,51 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player.advanced;
+
+/**
+ * An event which indicates a Player
has performed an 'playback action'
+ * @author Paul Stanton (http://wanto.f2o.org/)
+ */
+public class PlaybackEvent
+{
+ public static int STOPPED = 1;
+ public static int STARTED = 2;
+
+ private AdvancedPlayer source;
+ private int frame;
+ private int id;
+
+ public PlaybackEvent(AdvancedPlayer source, int id, int frame)
+ {
+ this.id = id;
+ this.source = source;
+ this.frame = frame;
+ }
+
+ public int getId(){return id;}
+ public void setId(int id){this.id = id;}
+
+ public int getFrame(){return frame;}
+ public void setFrame(int frame){this.frame = frame;}
+
+ public AdvancedPlayer getSource(){return source;}
+ public void setSource(AdvancedPlayer source){this.source = source;}
+
+}
diff --git a/songdbj/javazoom/jl/player/advanced/PlaybackListener.java b/songdbj/javazoom/jl/player/advanced/PlaybackListener.java
new file mode 100644
index 0000000000..9b042988b8
--- /dev/null
+++ b/songdbj/javazoom/jl/player/advanced/PlaybackListener.java
@@ -0,0 +1,30 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player.advanced;
+
+/**
+ * Listener for javalayer Player playback
+ * @author Paul Stanton (http://wanto.f2o.org/)
+ */
+public abstract class PlaybackListener
+{
+ public void playbackStarted(PlaybackEvent evt){}
+ public void playbackFinished(PlaybackEvent evt){}
+}
diff --git a/songdbj/javazoom/jl/player/advanced/jlap.java b/songdbj/javazoom/jl/player/advanced/jlap.java
new file mode 100644
index 0000000000..beedea6716
--- /dev/null
+++ b/songdbj/javazoom/jl/player/advanced/jlap.java
@@ -0,0 +1,116 @@
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.jl.player.advanced;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javazoom.jl.decoder.JavaLayerException;
+
+/**
+ * This class implements a sample player using Playback listener.
+ */
+public class jlap
+{
+
+ public static void main(String[] args)
+ {
+ jlap test = new jlap();
+ if (args.length != 1)
+ {
+ test.showUsage();
+ System.exit(0);
+ }
+ else
+ {
+ try
+ {
+ test.play(args[0]);
+ }
+ catch (Exception ex)
+ {
+ System.err.println(ex.getMessage());
+ System.exit(0);
+ }
+ }
+ }
+
+ public void play(String filename) throws JavaLayerException, IOException
+ {
+ InfoListener lst = new InfoListener();
+ playMp3(new File(filename), lst);
+ }
+
+ public void showUsage()
+ {
+ System.out.println("Usage: jla jlp
class implements a simple command-line
+ * player for MPEG audio files.
+ *
+ * @author Mat McGowan (mdm@techie.com)
+ */
+public class jlp
+{
+ private String fFilename = null;
+ private boolean remote = false;
+
+ public static void main(String[] args)
+ {
+ int retval = 0;
+ try
+ {
+ jlp player = createInstance(args);
+ if (player!=null)
+ player.play();
+ }
+ catch (Exception ex)
+ {
+ System.err.println(ex);
+ ex.printStackTrace(System.err);
+ retval = 1;
+ }
+ System.exit(retval);
+ }
+
+ static public jlp createInstance(String[] args)
+ {
+ jlp player = new jlp();
+ if (!player.parseArgs(args))
+ player = null;
+ return player;
+ }
+
+ private jlp()
+ {
+ }
+
+ public jlp(String filename)
+ {
+ init(filename);
+ }
+
+ protected void init(String filename)
+ {
+ fFilename = filename;
+ }
+
+ protected boolean parseArgs(String[] args)
+ {
+ boolean parsed = false;
+ if (args.length == 1)
+ {
+ init(args[0]);
+ parsed = true;
+ remote = false;
+ }
+ else if (args.length == 2)
+ {
+ if (!(args[0].equals("-url")))
+ {
+ showUsage();
+ }
+ else
+ {
+ init(args[1]);
+ parsed = true;
+ remote = true;
+ }
+ }
+ else
+ {
+ showUsage();
+ }
+ return parsed;
+ }
+
+ public void showUsage()
+ {
+ System.out.println("Usage: jlp [-url]
+ *
+ */
+ public Map properties()
+ {
+ properties.put("mp3.frame",new Long(currentFrame));
+ properties.put("mp3.frame.bitrate",new Integer(currentBitrate));
+ properties.put("mp3.frame.size.bytes",new Integer(currentFramesize));
+ properties.put("mp3.position.byte",new Long(currentByte));
+ properties.put("mp3.position.microseconds",new Long(currentMicrosecond));
+ properties.put("mp3.equalizer",m_equalizer_values);
+ // Optionnal shoutcast stream meta-data.
+ if (shoutlst != null)
+ {
+ String surl = shoutlst.getStreamUrl();
+ String stitle = shoutlst.getStreamTitle();
+ if ((stitle != null) && (stitle.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamTitle",stitle);
+ if ((surl != null) && (surl.trim().length()>0)) properties.put("mp3.shoutcast.metadata.StreamUrl",surl);
+ }
+ return properties;
+ }
+
+ public void execute()
+ {
+ if (TDebug.TraceAudioConverter) TDebug.out("execute() : begin");
+ try
+ {
+ // Following line hangs when FrameSize is available in AudioFormat.
+ Header header = null;
+ if (m_header == null) header = m_bitstream.readFrame();
+ else header = m_header;
+ if (TDebug.TraceAudioConverter) TDebug.out("execute() : header = "+header);
+ if (header == null)
+ {
+ if (TDebug.TraceAudioConverter)
+ {
+ TDebug.out("header is null (end of mpeg stream)");
+ }
+ getCircularBuffer().close();
+ return;
+ }
+ currentFrame++;
+ currentBitrate = header.bitrate_instant();
+ currentFramesize = header.calculate_framesize();
+ currentByte = currentByte + currentFramesize;
+ currentMicrosecond = (long) (currentFrame* header.ms_per_frame()*1000.0f);
+ for (int b=0;b
For instance :
+ *
mp3.shoutcast.metadata.StreamTitle=Current song playing in stream.
+ *
mp3.shoutcast.metadata.StreamUrl=Url info.
+ *
AudioFileFormat parameters.
+ *
+ *
+ *
MP3 parameters.
+ *
+ *
+ */
+ public Map properties()
+ {
+ return super.properties();
+ }
+}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java
new file mode 100644
index 0000000000..54440551a1
--- /dev/null
+++ b/songdbj/javazoom/spi/mpeg/sampled/file/MpegAudioFileReader.java
@@ -0,0 +1,772 @@
+/*
+ * MpegAudioFileReader.
+ *
+ * 12/31/04 : mp3spi.weak system property added to skip controls.
+ *
+ * 11/29/04 : ID3v2.2, v2.3 & v2.4 support improved.
+ * "mp3.id3tag.composer" (TCOM/TCM) added
+ * "mp3.id3tag.grouping" (TIT1/TT1) added
+ * "mp3.id3tag.disc" (TPA/TPOS) added
+ * "mp3.id3tag.encoded" (TEN/TENC) added
+ * "mp3.id3tag.v2.version" added
+ *
+ * 11/28/04 : String encoding bug fix in chopSubstring method.
+ *
+ * JavaZOOM : mp3spi@javazoom.net
+ * http://www.javazoom.net
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.spi.mpeg.sampled.file;
+
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessControlException;
+import java.util.HashMap;
+
+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 javazoom.jl.decoder.Bitstream;
+import javazoom.jl.decoder.Header;
+import javazoom.spi.mpeg.sampled.file.tag.IcyInputStream;
+import javazoom.spi.mpeg.sampled.file.tag.MP3Tag;
+
+import org.tritonus.share.TDebug;
+import org.tritonus.share.sampled.file.TAudioFileReader;
+
+/**
+ * This class implements AudioFileReader for MP3 SPI.
+ */
+public class MpegAudioFileReader extends TAudioFileReader
+{
+ private final int SYNC = 0xFFE00000;
+ private String weak = null;
+ private final AudioFormat.Encoding[][] sm_aEncodings =
+ {
+ {MpegEncoding.MPEG2L1, MpegEncoding.MPEG2L2, MpegEncoding.MPEG2L3},
+ {MpegEncoding.MPEG1L1, MpegEncoding.MPEG1L2, MpegEncoding.MPEG1L3},
+ {MpegEncoding.MPEG2DOT5L1, MpegEncoding.MPEG2DOT5L2, MpegEncoding.MPEG2DOT5L3},
+
+ };
+
+ private static final int INITAL_READ_LENGTH = 64000;
+ private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
+
+ private static final String[] id3v1genres = {
+ "Blues"
+ , "Classic Rock"
+ , "Country"
+ , "Dance"
+ , "Disco"
+ , "Funk"
+ , "Grunge"
+ , "Hip-Hop"
+ , "Jazz"
+ , "Metal"
+ , "New Age"
+ , "Oldies"
+ , "Other"
+ , "Pop"
+ , "R&B"
+ , "Rap"
+ , "Reggae"
+ , "Rock"
+ , "Techno"
+ , "Industrial"
+ , "Alternative"
+ , "Ska"
+ , "Death Metal"
+ , "Pranks"
+ , "Soundtrack"
+ , "Euro-Techno"
+ , "Ambient"
+ , "Trip-Hop"
+ , "Vocal"
+ , "Jazz+Funk"
+ , "Fusion"
+ , "Trance"
+ , "Classical"
+ , "Instrumental"
+ , "Acid"
+ , "House"
+ , "Game"
+ , "Sound Clip"
+ , "Gospel"
+ , "Noise"
+ , "AlternRock"
+ , "Bass"
+ , "Soul"
+ , "Punk"
+ , "Space"
+ , "Meditative"
+ , "Instrumental Pop"
+ , "Instrumental Rock"
+ , "Ethnic"
+ , "Gothic"
+ , "Darkwave"
+ , "Techno-Industrial"
+ , "Electronic"
+ , "Pop-Folk"
+ , "Eurodance"
+ , "Dream"
+ , "Southern Rock"
+ , "Comedy"
+ , "Cult"
+ , "Gangsta"
+ , "Top 40"
+ , "Christian Rap"
+ , "Pop/Funk"
+ , "Jungle"
+ , "Native American"
+ , "Cabaret"
+ , "New Wave"
+ , "Psychadelic"
+ , "Rave"
+ , "Showtunes"
+ , "Trailer"
+ , "Lo-Fi"
+ , "Tribal"
+ , "Acid Punk"
+ , "Acid Jazz"
+ , "Polka"
+ , "Retro"
+ , "Musical"
+ , "Rock & Roll"
+ , "Hard Rock"
+ , "Folk"
+ , "Folk-Rock"
+ , "National Folk"
+ , "Swing"
+ , "Fast Fusion"
+ , "Bebob"
+ , "Latin"
+ , "Revival"
+ , "Celtic"
+ , "Bluegrass"
+ , "Avantgarde"
+ , "Gothic Rock"
+ , "Progressive Rock"
+ , "Psychedelic Rock"
+ , "Symphonic Rock"
+ , "Slow Rock"
+ , "Big Band"
+ , "Chorus"
+ , "Easy Listening"
+ , "Acoustic"
+ , "Humour"
+ , "Speech"
+ , "Chanson"
+ , "Opera"
+ , "Chamber Music"
+ , "Sonata"
+ , "Symphony"
+ , "Booty Brass"
+ , "Primus"
+ , "Porn Groove"
+ , "Satire"
+ , "Slow Jam"
+ , "Club"
+ , "Tango"
+ , "Samba"
+ , "Folklore"
+ , "Ballad"
+ , "Power Ballad"
+ , "Rhythmic Soul"
+ , "Freestyle"
+ , "Duet"
+ , "Punk Rock"
+ , "Drum Solo"
+ , "A Capela"
+ , "Euro-House"
+ , "Dance Hall"
+ , "Goa"
+ , "Drum & Bass"
+ , "Club-House"
+ , "Hardcore"
+ , "Terror"
+ , "Indie"
+ , "BritPop"
+ , "Negerpunk"
+ , "Polsk Punk"
+ , "Beat"
+ , "Christian Gangsta Rap"
+ , "Heavy Metal"
+ , "Black Metal"
+ , "Crossover"
+ , "Contemporary Christian"
+ , "Christian Rock"
+ , "Merengue"
+ , "Salsa"
+ , "Thrash Metal"
+ , "Anime"
+ , "JPop"
+ , "SynthPop"
+ };
+
+ public MpegAudioFileReader()
+ {
+ super(MARK_LIMIT, true);
+ if (TDebug.TraceAudioFileReader) TDebug.out(">MpegAudioFileReader(1.9.2-FINAL)");
+ try
+ {
+ weak = System.getProperty("mp3spi.weak");
+ }
+ catch(AccessControlException e)
+ {}
+ }
+
+ /**
+ * Returns AudioFileFormat from File.
+ */
+ public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException
+ {
+ return super.getAudioFileFormat(file);
+ }
+
+ /**
+ * Returns AudioFileFormat from URL.
+ */
+ public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioFileFormat(URL): begin"); }
+ long lFileLengthInBytes = AudioSystem.NOT_SPECIFIED;
+ URLConnection conn = url.openConnection();
+ // Tell shoucast server (if any) that SPI support shoutcast stream.
+ conn.setRequestProperty ("Icy-Metadata", "1");
+ InputStream inputStream = conn.getInputStream();
+ AudioFileFormat audioFileFormat = null;
+ try
+ {
+ audioFileFormat = getAudioFileFormat(inputStream, lFileLengthInBytes);
+ }
+ finally
+ {
+ inputStream.close();
+ }
+ if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioFileFormat(URL): end"); }
+ return audioFileFormat;
+ }
+
+ /**
+ * Returns AudioFileFormat from inputstream and medialength.
+ */
+ public AudioFileFormat getAudioFileFormat(InputStream inputStream, long mediaLength) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out(">MpegAudioFileReader.getAudioFileFormat(InputStream inputStream, long mediaLength): begin");
+ HashMap aff_properties = new HashMap();
+ HashMap af_properties = new HashMap();
+ int mLength = (int) mediaLength;
+ int size = inputStream.available();
+ PushbackInputStream pis = new PushbackInputStream(inputStream, MARK_LIMIT);
+ byte head[] = new byte[12];
+ pis.read(head);
+ if (TDebug.TraceAudioFileReader)
+ {
+ TDebug.out("InputStream : "+inputStream + " =>" + new String(head));
+ }
+ /*
+ * Check for WAV, AU, and AIFF file formats.
+ *
+ * Next check for Shoutcast (supported) and OGG (unsupported) streams.
+ *
+ * Note -- the check for WAV files will reject Broadcast WAV files.
+ * This may be incorrect as broadcast WAV files may contain MPEG data.
+ * Need to investigate.
+ *
+ */
+ if ((head[0] == 'R') && (head[1] == 'I') && (head[2] == 'F') && (head[3] == 'F') && (head[8] == 'W') && (head[9] == 'A') && (head[10] == 'V') && (head[11] == 'E'))
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("WAV stream found");
+ if (weak == null) throw new UnsupportedAudioFileException("WAV stream found");
+ }
+ else if ((head[0] == '.') && (head[1] == 's') && (head[2] == 'n') && (head[3] == 'd'))
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("AU stream found");
+ if (weak == null) throw new UnsupportedAudioFileException("AU stream found");
+ }
+ else if ((head[0] == 'F') && (head[1] == 'O') && (head[2] == 'R') && (head[3] == 'M') && (head[8] == 'A') && (head[9] == 'I') && (head[10] == 'F') && (head[11] == 'F'))
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("AIFF stream found");
+ if (weak == null) throw new UnsupportedAudioFileException("AIFF stream found");
+ }
+ // Shoutcast stream ?
+ else if (((head[0] == 'I') | (head[0] == 'i')) && ((head[1] == 'C') | (head[1] == 'c')) && ((head[2] == 'Y') | (head[2] == 'y')))
+ {
+ pis.unread(head);
+ // Load shoutcast meta data.
+ loadShoutcastInfo(pis, aff_properties);
+ }
+ // Ogg stream ?
+ else if (((head[0] == 'O') | (head[0] == 'o')) && ((head[1] == 'G') | (head[1] == 'g')) && ((head[2] == 'G') | (head[2] == 'g')))
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("Ogg stream found");
+ if (weak == null) throw new UnsupportedAudioFileException("Ogg stream found");
+ }
+ // No, so pushback.
+ else
+ {
+ pis.unread(head);
+ }
+ // MPEG header info.
+ int nVersion = AudioSystem.NOT_SPECIFIED;
+ int nLayer = AudioSystem.NOT_SPECIFIED;
+ int nSFIndex = AudioSystem.NOT_SPECIFIED;
+ int nMode = AudioSystem.NOT_SPECIFIED;
+ int FrameSize = AudioSystem.NOT_SPECIFIED;
+ int nFrameSize = AudioSystem.NOT_SPECIFIED;
+ int nFrequency = AudioSystem.NOT_SPECIFIED;
+ int nTotalFrames = AudioSystem.NOT_SPECIFIED;
+ float FrameRate = AudioSystem.NOT_SPECIFIED;
+ int BitRate = AudioSystem.NOT_SPECIFIED;
+ int nChannels = AudioSystem.NOT_SPECIFIED;
+ int nHeader = AudioSystem.NOT_SPECIFIED;
+ int nTotalMS = AudioSystem.NOT_SPECIFIED;
+ boolean nVBR = false;
+ AudioFormat.Encoding encoding = null;
+ try
+ {
+ Bitstream m_bitstream = new Bitstream(pis);
+ aff_properties.put("mp3.header.pos",new Integer(m_bitstream.header_pos()));
+ Header m_header = m_bitstream.readFrame();
+ // nVersion = 0 => MPEG2-LSF (Including MPEG2.5), nVersion = 1 => MPEG1
+ nVersion = m_header.version();
+ if (nVersion == 2) aff_properties.put("mp3.version.mpeg",Float.toString(2.5f));
+ else aff_properties.put("mp3.version.mpeg",Integer.toString(2-nVersion));
+ // nLayer = 1,2,3
+ nLayer = m_header.layer();
+ aff_properties.put("mp3.version.layer",Integer.toString(nLayer));
+ nSFIndex = m_header.sample_frequency();
+ nMode = m_header.mode();
+ aff_properties.put("mp3.mode",new Integer(nMode));
+ nChannels = nMode == 3 ? 1 : 2;
+ aff_properties.put("mp3.channels",new Integer(nChannels));
+ nVBR = m_header.vbr();
+ af_properties.put("vbr",new Boolean(nVBR));
+ aff_properties.put("mp3.vbr",new Boolean(nVBR));
+ aff_properties.put("mp3.vbr.scale",new Integer(m_header.vbr_scale()));
+ FrameSize = m_header.calculate_framesize();
+ aff_properties.put("mp3.framesize.bytes",new Integer(FrameSize));
+ if (FrameSize < 0) throw new UnsupportedAudioFileException("Invalid FrameSize : " + FrameSize);
+ nFrequency = m_header.frequency();
+ aff_properties.put("mp3.frequency.hz",new Integer(nFrequency));
+ FrameRate = (float) ((1.0 / (m_header.ms_per_frame())) * 1000.0);
+ aff_properties.put("mp3.framerate.fps",new Float(FrameRate));
+ if (FrameRate < 0) throw new UnsupportedAudioFileException("Invalid FrameRate : " + FrameRate);
+ if (mLength != AudioSystem.NOT_SPECIFIED)
+ {
+ aff_properties.put("mp3.length.bytes",new Integer(mLength));
+ nTotalFrames = m_header.max_number_of_frames(mLength);
+ aff_properties.put("mp3.length.frames",new Integer(nTotalFrames));
+ }
+ BitRate = m_header.bitrate();
+ af_properties.put("bitrate",new Integer(BitRate));
+ aff_properties.put("mp3.bitrate.nominal.bps",new Integer(BitRate));
+ nHeader = m_header.getSyncHeader();
+ encoding = sm_aEncodings[nVersion][nLayer - 1];
+ aff_properties.put("mp3.version.encoding",encoding.toString());
+ if (mLength != AudioSystem.NOT_SPECIFIED)
+ {
+ nTotalMS = Math.round(m_header.total_ms(mLength));
+ aff_properties.put("duration",new Long((long)nTotalMS*1000L));
+ }
+ aff_properties.put("mp3.copyright",new Boolean(m_header.copyright()));
+ aff_properties.put("mp3.original",new Boolean(m_header.original()));
+ aff_properties.put("mp3.crc",new Boolean(m_header.checksums()));
+ aff_properties.put("mp3.padding",new Boolean(m_header.padding()));
+ InputStream id3v2 = m_bitstream.getRawID3v2();
+ if (id3v2 != null)
+ {
+ aff_properties.put("mp3.id3tag.v2",id3v2);
+ parseID3v2Frames(id3v2,aff_properties);
+ }
+ if (TDebug.TraceAudioFileReader) TDebug.out(m_header.toString());
+ }
+ catch (Exception e)
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream:" + e.getMessage());
+ throw new UnsupportedAudioFileException("not a MPEG stream:" + e.getMessage());
+ }
+
+ // Deeper checks ?
+ int cVersion = (nHeader >> 19) & 0x3;
+ if (cVersion == 1)
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream: wrong version");
+ throw new UnsupportedAudioFileException("not a MPEG stream: wrong version");
+ }
+
+ int cSFIndex = (nHeader >> 10) & 0x3;
+ if (cSFIndex == 3)
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("not a MPEG stream: wrong sampling rate");
+ throw new UnsupportedAudioFileException("not a MPEG stream: wrong sampling rate");
+ }
+
+ // Look up for ID3v1 tag
+ if ((size == mediaLength) && (mediaLength != AudioSystem.NOT_SPECIFIED))
+ {
+ FileInputStream fis = (FileInputStream) inputStream;
+ byte[] id3v1 = new byte[128];
+ long bytesSkipped = fis.skip(inputStream.available()-id3v1.length);
+ int read = fis.read(id3v1,0,id3v1.length);
+ if ((id3v1[0]=='T') && (id3v1[1]=='A') && (id3v1[2]=='G'))
+ {
+ parseID3v1Frames(id3v1, aff_properties);
+ }
+ }
+
+ AudioFormat format = new MpegAudioFormat(encoding, (float) nFrequency, AudioSystem.NOT_SPECIFIED // SampleSizeInBits - The size of a sample
+ , nChannels // Channels - The number of channels
+ , -1 // The number of bytes in each frame
+ , FrameRate // FrameRate - The number of frames played or recorded per second
+ , true
+ , af_properties);
+ return new MpegAudioFileFormat(MpegFileFormatType.MP3, format, nTotalFrames, mLength,aff_properties);
+ }
+
+ /**
+ * Returns AudioInputStream from file.
+ */
+ public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(File file)");
+ InputStream inputStream = new FileInputStream(file);
+ try
+ {
+ return getAudioInputStream(inputStream);
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ }
+
+ /**
+ * Returns AudioInputStream from url.
+ */
+ public AudioInputStream getAudioInputStream(URL url)
+ throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioInputStream(URL): begin"); }
+ long lFileLengthInBytes = AudioSystem.NOT_SPECIFIED;
+ URLConnection conn = url.openConnection();
+ // Tell shoucast server (if any) that SPI support shoutcast stream.
+ boolean isShout = false;
+ int toRead=4;
+ byte[] head = new byte[toRead];
+
+ conn.setRequestProperty ("Icy-Metadata", "1");
+ BufferedInputStream bInputStream = new BufferedInputStream(conn.getInputStream());
+ bInputStream.mark(toRead);
+ int read = bInputStream.read(head,0,toRead);
+ if ((read>2) && (((head[0] == 'I') | (head[0] == 'i')) && ((head[1] == 'C') | (head[1] == 'c')) && ((head[2] == 'Y') | (head[2] == 'y')))) isShout = true;
+ bInputStream.reset();
+ InputStream inputStream = null;
+ // Is is a shoutcast server ?
+ if (isShout == true)
+ {
+ // Yes
+ IcyInputStream icyStream = new IcyInputStream(bInputStream);
+ icyStream.addTagParseListener(IcyListener.getInstance());
+ inputStream = icyStream;
+ }
+ else
+ {
+ // No, is Icecast 2 ?
+ String metaint = conn.getHeaderField("icy-metaint");
+ if (metaint != null)
+ {
+ // Yes, it might be icecast 2 mp3 stream.
+ IcyInputStream icyStream = new IcyInputStream(bInputStream,metaint);
+ icyStream.addTagParseListener(IcyListener.getInstance());
+ inputStream = icyStream;
+ }
+ else
+ {
+ // No
+ inputStream = bInputStream;
+ }
+ }
+ AudioInputStream audioInputStream = null;
+ try
+ {
+ audioInputStream = getAudioInputStream(inputStream, lFileLengthInBytes);
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ inputStream.close();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ inputStream.close();
+ throw e;
+ }
+ if (TDebug.TraceAudioFileReader) {TDebug.out("MpegAudioFileReader.getAudioInputStream(URL): end"); }
+ return audioInputStream;
+ }
+
+ /**
+ * Return the AudioInputStream from the given InputStream.
+ */
+ public AudioInputStream getAudioInputStream(InputStream inputStream) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("MpegAudioFileReader.getAudioInputStream(InputStream inputStream)");
+ if (!inputStream.markSupported()) inputStream = new BufferedInputStream(inputStream);
+ return super.getAudioInputStream(inputStream);
+ }
+
+ /**
+ * Parser ID3v1 frames
+ * @param frames
+ * @param props
+ */
+ protected void parseID3v1Frames(byte[] frames, HashMap props)
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("Parsing ID3v1");
+ String tag = null;
+ try
+ {
+ tag = new String(frames, 0, frames.length, "ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ tag = new String(frames, 0, frames.length);
+ if (TDebug.TraceAudioFileReader) TDebug.out("Cannot use ISO-8859-1");
+ }
+ if (TDebug.TraceAudioFileReader) TDebug.out("ID3v1 frame dump='"+tag+"'");
+ int start = 3;
+ String titlev1 = chopSubstring(tag, start, start += 30);
+ String titlev2 = (String) props.get("title");
+ if (((titlev2==null) || (titlev2.length()==0)) && (titlev1 != null)) props.put("title",titlev1);
+ String artistv1 = chopSubstring(tag, start, start += 30);
+ String artistv2 = (String) props.get("author");
+ if (((artistv2==null) || (artistv2.length()==0)) && (artistv1 != null)) props.put("author",artistv1);
+ String albumv1 = chopSubstring(tag, start, start += 30);
+ String albumv2 = (String) props.get("album");
+ if (((albumv2==null) || (albumv2.length()==0)) && (albumv1 != null)) props.put("album",albumv1);
+ String yearv1 = chopSubstring(tag, start, start += 4);
+ String yearv2 = (String) props.get("year");
+ if (((yearv2==null) || (yearv2.length()==0)) && (yearv1 != null)) props.put("date",yearv1);
+ String commentv1 = chopSubstring(tag, start, start += 28);
+ String commentv2 = (String) props.get("comment");
+ if (((commentv2==null) || (commentv2.length()==0)) && (commentv1 != null)) props.put("comment",commentv1);
+ String trackv1 = ""+((int) (frames[126] & 0xff));
+ String trackv2 = (String) props.get("mp3.id3tag.track");
+ if (((trackv2==null) || (trackv2.length()==0)) && (trackv1 != null)) props.put("mp3.id3tag.track",trackv1);
+
+ int genrev1 = (int) (frames[127] & 0xff);
+ if ((genrev1 >=0) && (genrev1
For instance :
+ *
mp3.shoutcast.metadata.icy-irc=#shoutcast
+ *
mp3.shoutcast.metadata.icy-metaint=8192
+ *
mp3.shoutcast.metadata.icy-genre=Trance Techno Dance
+ *
mp3.shoutcast.metadata.icy-url=http://www.di.fm
+ *
and so on ...
+ *
+ *
+ */
+ public Map properties()
+ {
+ return super.properties();
+ }
+}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java
new file mode 100644
index 0000000000..6306d9ec80
--- /dev/null
+++ b/songdbj/javazoom/spi/mpeg/sampled/file/MpegEncoding.java
@@ -0,0 +1,47 @@
+/*
+ * MpegEncoding.
+ *
+ * JavaZOOM : mp3spi@javazoom.net
+ * http://www.javazoom.net
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.spi.mpeg.sampled.file;
+
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * Encodings used by the MPEG audio decoder.
+ */
+public class MpegEncoding extends AudioFormat.Encoding
+{
+ public static final AudioFormat.Encoding MPEG1L1 = new MpegEncoding("MPEG1L1");
+ public static final AudioFormat.Encoding MPEG1L2 = new MpegEncoding("MPEG1L2");
+ public static final AudioFormat.Encoding MPEG1L3 = new MpegEncoding("MPEG1L3");
+ public static final AudioFormat.Encoding MPEG2L1 = new MpegEncoding("MPEG2L1");
+ public static final AudioFormat.Encoding MPEG2L2 = new MpegEncoding("MPEG2L2");
+ public static final AudioFormat.Encoding MPEG2L3 = new MpegEncoding("MPEG2L3");
+ public static final AudioFormat.Encoding MPEG2DOT5L1 = new MpegEncoding("MPEG2DOT5L1");
+ public static final AudioFormat.Encoding MPEG2DOT5L2 = new MpegEncoding("MPEG2DOT5L2");
+ public static final AudioFormat.Encoding MPEG2DOT5L3 = new MpegEncoding("MPEG2DOT5L3");
+
+ public MpegEncoding(String strName)
+ {
+ super(strName);
+ }
+}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java b/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java
new file mode 100644
index 0000000000..2c59ad8621
--- /dev/null
+++ b/songdbj/javazoom/spi/mpeg/sampled/file/MpegFileFormatType.java
@@ -0,0 +1,40 @@
+/*
+ * MpegFileFormatType.
+ *
+ * JavaZOOM : mp3spi@javazoom.net
+ * http://www.javazoom.net
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.spi.mpeg.sampled.file;
+
+import javax.sound.sampled.AudioFileFormat;
+
+/**
+ * FileFormatTypes used by the MPEG audio decoder.
+ */
+public class MpegFileFormatType extends AudioFileFormat.Type
+{
+ public static final AudioFileFormat.Type MPEG = new MpegFileFormatType("MPEG", "mpeg");
+ public static final AudioFileFormat.Type MP3 = new MpegFileFormatType("MP3", "mp3");
+
+ public MpegFileFormatType(String strName, String strExtension)
+ {
+ super(strName, strExtension);
+ }
+}
diff --git a/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java b/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java
new file mode 100644
index 0000000000..22aa4439fe
--- /dev/null
+++ b/songdbj/javazoom/spi/mpeg/sampled/file/tag/IcyInputStream.java
@@ -0,0 +1,412 @@
+/*
+ * IcyInputStream.
+ *
+ * jicyshout : http://sourceforge.net/projects/jicyshout/
+ *
+ * JavaZOOM : mp3spi@javazoom.net
+ * http://www.javazoom.net
+ *
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+package javazoom.spi.mpeg.sampled.file.tag;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+/** An BufferedInputStream that parses Shoutcast's "icy" metadata
+ from the stream. Gets headers at the beginning and if the
+ "icy-metaint" tag is found, it parses and strips in-stream
+ metadata.
+ icy-metaint
name/val in the beginning of the
+ stream iff you sent "Icy-Metadata" with value "1" in the
+ request headers (SimpleMP3DataSource does this if the
+ "parseStreamMetadata" boolean is true). If this is the case
+ then the value of icy-metaint is the amount of real data
+ between metadata blocks. Each block begins with an int
+ indicating how much metadata there is -- the block is this
+ value times 16 (it can be, and often is, 0).
+
This stream requires Winamp
\r\n
+ icy-notice2:SHOUTcast Distributed Network Audio Server/win32 v1.8.2
\r\n
+ icy-name:Core-upt Radio\r\n
+ icy-genre:Punk Ska Emo\r\n
+ icy-url:http://www.core-uptrecords.com\r\n
+ icy-pub:1\r\n
+ icy-metaint:8192\r\n
+ icy-br:56\r\n
+ \r\n (signifies end of headers)
+ we only get icy-metaint if the http request that created
+ this stream sent the header "icy-metadata:1"
+ //
+ in in-line metadata, we read a byte that tells us how
+ many 16-byte blocks there are (presumably, we still use
+ \r\n for the separator... the block is padded out with
+ 0x00's that we can ignore)
+
+ // when server is full/down/etc, we get the following for
+ // one of the notice lines:
+ icy-notice2:This server has reached its user limit
+ or
+ icy-notice2:The resource requested is currently unavailable
+ */
+ /** Tags that have been discovered in the stream.
+ */
+ HashMap tags;
+ /** Buffer for readCRLF line... note this limits lines to
+ 1024 chars (I've read that WinAmp barfs at 128, so
+ this is generous)
+ */
+ protected byte[] crlfBuffer = new byte[1024];
+ /** value of the "metaint" tag, which tells us how many bytes
+ of real data are between the metadata tags. if -1, this stream
+ does not have metadata after the header.
+ */
+ protected int metaint = -1;
+ /** how many bytes of real data remain before the next
+ block of metadata. Only meaningful if metaint != -1.
+ */
+ protected int bytesUntilNextMetadata = -1;
+ // TODO: comment for constructor
+ /** Reads the initial headers of the stream and adds
+ tags appropriatly. Gets set up to find, read,
+ and strip blocks of in-line metadata if the
+ icy-metaint
header is found.
+ */
+ public IcyInputStream(InputStream in) throws IOException {
+ super(in);
+ tags = new HashMap();
+ tagParseSupport = new MP3TagParseSupport();
+ // read the initial tags here, including the metaint
+ // and set the counter for how far we read until
+ // the next metadata block (if any).
+ readInitialHeaders();
+ IcyTag metaIntTag = (IcyTag) getTag("icy-metaint");
+ if (DEBUG) System.out.println("METATAG:"+metaIntTag);
+ if (metaIntTag != null) {
+ String metaIntString = metaIntTag.getValueAsString();
+ try {
+ metaint = Integer.parseInt(metaIntString.trim());
+ if (DEBUG) System.out.println("METAINT:"+metaint);
+ bytesUntilNextMetadata = metaint;
+ }
+ catch (NumberFormatException nfe) {
+ }
+ }
+ }
+
+ /**
+ * IcyInputStream constructor for know meta-interval (Icecast 2)
+ * @param in
+ * @param metaint
+ * @throws IOException
+ */
+ public IcyInputStream(InputStream in, String metaIntString) throws IOException {
+ super(in);
+ tags = new HashMap();
+ tagParseSupport = new MP3TagParseSupport();
+ try
+ {
+ metaint = Integer.parseInt(metaIntString.trim());
+ if (DEBUG) System.out.println("METAINT:"+metaint);
+ bytesUntilNextMetadata = metaint;
+ }
+ catch (NumberFormatException nfe) {
+ }
+ }
+
+ /** Assuming we're at the top of the stream, read lines one
+ by one until we hit a completely blank \r\n. Parse the
+ data as IcyTags.
+ */
+ protected void readInitialHeaders() throws IOException {
+ String line = null;
+ while (!((line = readCRLFLine()).equals(""))) {
+ int colonIndex = line.indexOf(':');
+ // does it have a ':' separator
+ if (colonIndex == -1)
+ continue;
+ IcyTag tag =
+ new IcyTag(
+ line.substring(0, colonIndex),
+ line.substring(colonIndex + 1));
+ //System.out.println(tag);
+ addTag(tag);
+ }
+ }
+ /** Read everything up to the next CRLF, return it as
+ a String.
+ */
+ protected String readCRLFLine() throws IOException {
+ int i = 0;
+ for (; i < crlfBuffer.length; i++) {
+ byte aByte = (byte) read();
+ if (aByte == '\r') {
+ // possible end of line
+ byte anotherByte = (byte) read();
+ i++; // since we read again
+ if (anotherByte == '\n') {
+ break; // break out of while
+ }
+ else {
+ // oops, not end of line - put these in array
+ crlfBuffer[i - 1] = aByte;
+ crlfBuffer[i] = anotherByte;
+ }
+ }
+ else {
+ // if not \r
+ crlfBuffer[i] = aByte;
+ }
+ } // for
+ // get the string from the byte[]. i is 1 too high because of
+ // read-ahead in crlf block
+ return new String(crlfBuffer, 0, i - 1);
+ }
+ /** Reads and returns a single byte.
+ If the next byte is a metadata block, then that
+ block is read, stripped, and parsed before reading
+ and returning the first byte after the metadata block.
+ */
+ public int read() throws IOException {
+ if (bytesUntilNextMetadata > 0) {
+ bytesUntilNextMetadata--;
+ return super.read();
+ }
+ else if (bytesUntilNextMetadata == 0) {
+ // we need to read next metadata block
+ readMetadata();
+ bytesUntilNextMetadata = metaint - 1;
+ // -1 because we read byte on next line
+ return super.read();
+ }
+ else {
+ // no metadata in this stream
+ return super.read();
+ }
+ }
+ /** Reads a block of bytes. If the next byte is known
+ to be a block of metadata, then that is read, parsed,
+ and stripped, and then a block of bytes is read and
+ returned.
+ Otherwise, it may read up to but
+ not into the next metadata block if
+ bytesUntilNextMetadata < length
+ */
+ public int read(byte[] buf, int offset, int length) throws IOException {
+ // if not on metadata, do the usual read so long as we
+ // don't read past metadata
+ if (bytesUntilNextMetadata > 0) {
+ int adjLength = Math.min(length, bytesUntilNextMetadata);
+ int got = super.read(buf, offset, adjLength);
+ bytesUntilNextMetadata -= got;
+ return got;
+ }
+ else if (bytesUntilNextMetadata == 0) {
+ // read/parse the metadata
+ readMetadata();
+ // now as above, except that we reset
+ // bytesUntilNextMetadata differently
+
+ //int adjLength = Math.min(length, bytesUntilNextMetadata);
+ //int got = super.read(buf, offset, adjLength);
+ //bytesUntilNextMetadata = metaint - got;
+
+ // Chop Fix - JavaZOOM (3 lines above seem buggy)
+ bytesUntilNextMetadata = metaint;
+ int adjLength = Math.min(length, bytesUntilNextMetadata);
+ int got = super.read(buf, offset, adjLength);
+ bytesUntilNextMetadata -= got;
+ // End fix - JavaZOOM
+
+
+ return got;
+ }
+ else {
+ // not even reading metadata
+ return super.read(buf, offset, length);
+ }
+ }
+ /** trivial return read (buf, 0, buf.length)
+ */
+ public int read(byte[] buf) throws IOException {
+ return read(buf, 0, buf.length);
+ }
+ /** Read the next segment of metadata. The stream must
+ be right on the segment, ie, the next byte to read is
+ the metadata block count. The metadata is parsed and
+ new tags are added with addTag(), which fires events
+ */
+ protected void readMetadata() throws IOException {
+ int blockCount = super.read();
+ if (DEBUG) System.out.println("BLOCKCOUNT:"+blockCount);
+ // System.out.println ("blocks to read: " + blockCount);
+ int byteCount = (blockCount * 16); // 16 bytes per block
+ if (byteCount < 0)
+ return; // WTF?!
+ byte[] metadataBlock = new byte[byteCount];
+ int index = 0;
+ // build an array of this metadata
+ while (byteCount > 0) {
+ int bytesRead = super.read(metadataBlock, index, byteCount);
+ index += bytesRead;
+ byteCount -= bytesRead;
+ }
+ // now parse it
+ if (blockCount > 0)
+ parseInlineIcyTags(metadataBlock);
+ } // readMetadata
+ /** Parse metadata from an in-stream "block" of bytes, add
+ a tag for each one.
+
+ StreamTitle='Final Fantasy 8 - Nobuo Uematsu - Blue Fields';StreamUrl='';
+
+ In other words:
+
+
+ */
+ protected void parseInlineIcyTags(byte[] tagBlock) {
+ String blockString = new String(tagBlock);
+ if (DEBUG) System.out.println("BLOCKSTR:"+blockString);
+ StringTokenizer izer =
+ new StringTokenizer(blockString, INLINE_TAG_SEPARATORS);
+ int i = 0;
+ while (izer.hasMoreTokens()) {
+ String tagString = izer.nextToken();
+ int separatorIdx = tagString.indexOf('=');
+ if (separatorIdx == -1)
+ continue; // bogus tagString if no '='
+ // try to strip single-quotes around value, if present
+ int valueStartIdx =
+ (tagString.charAt(separatorIdx + 1) == '\'')
+ ? separatorIdx + 2
+ : separatorIdx + 1;
+ int valueEndIdx =
+ (tagString.charAt(tagString.length() - 1)) == '\''
+ ? tagString.length() - 1
+ : tagString.length();
+ String name = tagString.substring(0, separatorIdx);
+ String value = tagString.substring(valueStartIdx, valueEndIdx);
+ // System.out.println (i++ + " " + name + ":" + value);
+ IcyTag tag = new IcyTag(name, value);
+ addTag(tag);
+ }
+ }
+ /** adds the tag to the HashMap of tags we have encountered
+ either in-stream or as headers, replacing any previous
+ tag with this name.
+ */
+ protected void addTag(IcyTag tag) {
+ tags.put(tag.getName(), tag);
+ // fire this as an event too
+ tagParseSupport.fireTagParsed(this, tag);
+ }
+ /** Get the named tag from the HashMap of headers and
+ in-line tags. Null if no such tag has been encountered.
+ */
+ public MP3Tag getTag(String tagName) {
+ return (MP3Tag) tags.get(tagName);
+ }
+ /** Get all tags (headers or in-stream) encountered thus far.
+ */
+ public MP3Tag[] getTags() {
+ return (MP3Tag[]) tags.values().toArray(new MP3Tag[0]);
+ }
+ /** Returns a HashMap of all headers and in-stream tags
+ parsed so far.
+ */
+ public HashMap getTagHash() {
+ return tags;
+ }
+ /** Adds a TagParseListener to be notified when this stream
+ parses MP3Tags.
+ */
+ public void addTagParseListener(TagParseListener tpl) {
+ tagParseSupport.addTagParseListener(tpl);
+ }
+ /** Removes a TagParseListener, so it won't be notified when
+ this stream parses MP3Tags.
+ */
+ public void removeTagParseListener(TagParseListener tpl) {
+ tagParseSupport.removeTagParseListener(tpl);
+ }
+ /** Quickie unit-test.
+ */
+ public static void main(String args[]) {
+ byte[] chow = new byte[200];
+ if (args.length != 1) {
+ //System.out.println("Usage: IcyInputStream
+ *
+ */
+ public Map properties()
+ {
+ properties.put("ogg.position.byte",new Long(currentBytes));
+ return properties;
+ }
+ /**
+ * Main loop.
+ */
+ public void execute()
+ {
+ if(TDebug.TraceAudioConverter)
+ {
+ switch(playState)
+ {
+ case playState_NeedHeaders:
+ TDebug.out("playState = playState_NeedHeaders");
+ break;
+ case playState_ReadData:
+ TDebug.out("playState = playState_ReadData");
+ break;
+ case playState_WriteData:
+ TDebug.out("playState = playState_WriteData");
+ break;
+ case playState_Done:
+ TDebug.out("playState = playState_Done");
+ break;
+ case playState_BufferFull:
+ TDebug.out("playState = playState_BufferFull");
+ break;
+ case playState_Corrupt:
+ TDebug.out("playState = playState_Corrupt");
+ break;
+ }
+ }
+ // This code was developed by the jCraft group, as JOrbisPlayer.java, slightly
+ // modified by jOggPlayer developer and adapted by JavaZOOM to suit the JavaSound
+ // SPI. Then further modified by Tom Kimpton to correctly play ogg files that
+ // would hang the player.
+ switch(playState)
+ {
+ case playState_NeedHeaders:
+ try
+ {
+ // Headers (+ Comments).
+ readHeaders();
+ }
+ catch(IOException ioe)
+ {
+ playState = playState_Corrupt;
+ return;
+ }
+ playState = playState_ReadData;
+ break;
+
+ case playState_ReadData:
+ int result;
+ index = oggSyncState_.buffer(bufferSize_);
+ buffer = oggSyncState_.data;
+ bytes = readFromStream(buffer, index, bufferSize_);
+ if(TDebug.TraceAudioConverter) TDebug.out("More data : " + bytes);
+ if(bytes == -1)
+ {
+ playState = playState_Done;
+ if(TDebug.TraceAudioConverter) TDebug.out("Ogg Stream empty. Settings playState to playState_Done.");
+ break;
+ }
+ else
+ {
+ oggSyncState_.wrote(bytes);
+ if(bytes == 0)
+ {
+ if((oggPage_.eos() != 0) || (oggStreamState_.e_o_s != 0) || (oggPacket_.e_o_s != 0))
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("oggSyncState wrote 0 bytes: settings playState to playState_Done.");
+ playState = playState_Done;
+ }
+ if(TDebug.TraceAudioConverter) TDebug.out("oggSyncState wrote 0 bytes: but stream not yet empty.");
+ break;
+ }
+ }
+
+ result = oggSyncState_.pageout(oggPage_);
+ if(result == 0)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Setting playState to playState_ReadData.");
+ playState = playState_ReadData;
+ break;
+ } // need more data
+ if(result == -1)
+ { // missing or corrupt data at this page position
+ if(TDebug.TraceAudioConverter) TDebug.out("Corrupt or missing data in bitstream; setting playState to playState_ReadData");
+ playState = playState_ReadData;
+ break;
+ }
+
+ oggStreamState_.pagein(oggPage_);
+
+ if(TDebug.TraceAudioConverter) TDebug.out("Setting playState to playState_WriteData.");
+ playState = playState_WriteData;
+ break;
+
+ case playState_WriteData:
+ // Decoding !
+ if(TDebug.TraceAudioConverter) TDebug.out("Decoding");
+ while(true)
+ {
+ result = oggStreamState_.packetout(oggPacket_);
+ if(result == 0)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Packetout returned 0, going to read state.");
+ playState = playState_ReadData;
+ break;
+ } // need more data
+ else if(result == -1)
+ {
+ // missing or corrupt data at this page position
+ // no reason to complain; already complained above
+ if(TDebug.TraceAudioConverter) TDebug.out("Corrupt or missing data in packetout bitstream; going to read state...");
+ // playState = playState_ReadData;
+ // break;
+ continue;
+ }
+ else
+ {
+ // we have a packet. Decode it
+ if(vorbisBlock.synthesis(oggPacket_) == 0)
+ { // test for success!
+ vorbisDspState.synthesis_blockin(vorbisBlock);
+ }
+ else
+ {
+ //if(TDebug.TraceAudioConverter) TDebug.out("vorbisBlock.synthesis() returned !0, going to read state");
+ if(TDebug.TraceAudioConverter) TDebug.out("VorbisBlock.synthesis() returned !0, continuing.");
+ continue;
+ }
+
+ outputSamples();
+ if(playState == playState_BufferFull)
+ return;
+
+ } // else result != -1
+ } // while(true)
+ if(oggPage_.eos() != 0)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Settings playState to playState_Done.");
+ playState = playState_Done;
+ }
+ break;
+ case playState_BufferFull:
+ continueFromBufferFull();
+ break;
+
+ case playState_Corrupt:
+ if(TDebug.TraceAudioConverter) TDebug.out("Corrupt Song.");
+ // drop through to playState_Done...
+ case playState_Done:
+ oggStreamState_.clear();
+ vorbisBlock.clear();
+ vorbisDspState.clear();
+ vorbisInfo.clear();
+ oggSyncState_.clear();
+ if(TDebug.TraceAudioConverter) TDebug.out("Done Song.");
+ try
+ {
+ if(oggBitStream_ != null)
+ {
+ oggBitStream_.close();
+ }
+ getCircularBuffer().close();
+ }
+ catch(Exception e)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out(e.getMessage());
+ }
+ break;
+ } // switch
+ }
+
+ /**
+ * This routine was extracted so that when the output buffer fills up,
+ * we can break out of the loop, let the music channel drain, then
+ * continue from where we were.
+ */
+ private void outputSamples()
+ {
+ int samples;
+ while((samples = vorbisDspState.synthesis_pcmout(_pcmf, _index)) > 0)
+ {
+ float[][] pcmf = _pcmf[0];
+ bout = (samples < convsize ? samples : convsize);
+ double fVal = 0.0;
+ // convert doubles to 16 bit signed ints (host order) and
+ // interleave
+ for(i = 0; i < vorbisInfo.channels; i++)
+ {
+ int pointer = i * 2;
+ //int ptr=i;
+ int mono = _index[i];
+ for(int j = 0; j < bout; j++)
+ {
+ fVal = pcmf[i][mono + j] * 32767.;
+ int val = (int) (fVal);
+ if(val > 32767)
+ {
+ val = 32767;
+ }
+ if(val < -32768)
+ {
+ val = -32768;
+ }
+ if(val < 0)
+ {
+ val = val | 0x8000;
+ }
+ convbuffer[pointer] = (byte) (val);
+ convbuffer[pointer + 1] = (byte) (val >>> 8);
+ pointer += 2 * (vorbisInfo.channels);
+ }
+ }
+ if(TDebug.TraceAudioConverter) TDebug.out("about to write: " + 2 * vorbisInfo.channels * bout);
+ if(getCircularBuffer().availableWrite() < 2 * vorbisInfo.channels * bout)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Too much data in this data packet, better return, let the channel drain, and try again...");
+ playState = playState_BufferFull;
+ return;
+ }
+ getCircularBuffer().write(convbuffer, 0, 2 * vorbisInfo.channels * bout);
+ if(bytes < bufferSize_)
+ if(TDebug.TraceAudioConverter) TDebug.out("Finished with final buffer of music?");
+ if(vorbisDspState.synthesis_read(bout) != 0)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("VorbisDspState.synthesis_read returned -1.");
+ }
+ } // while(samples...)
+ playState = playState_ReadData;
+ }
+
+ private void continueFromBufferFull()
+ {
+ if(getCircularBuffer().availableWrite() < 2 * vorbisInfo.channels * bout)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Too much data in this data packet, better return, let the channel drain, and try again...");
+ // Don't change play state.
+ return;
+ }
+ getCircularBuffer().write(convbuffer, 0, 2 * vorbisInfo.channels * bout);
+ // Don't change play state. Let outputSamples change play state, if necessary.
+ outputSamples();
+ }
+ /**
+ * Reads headers and comments.
+ */
+ private void readHeaders() throws IOException
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("readHeaders(");
+ index = oggSyncState_.buffer(bufferSize_);
+ buffer = oggSyncState_.data;
+ bytes = readFromStream(buffer, index, bufferSize_);
+ if(bytes == -1)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Cannot get any data from selected Ogg bitstream.");
+ throw new IOException("Cannot get any data from selected Ogg bitstream.");
+ }
+ oggSyncState_.wrote(bytes);
+ if(oggSyncState_.pageout(oggPage_) != 1)
+ {
+ if(bytes < bufferSize_)
+ {
+ throw new IOException("EOF");
+ }
+ if(TDebug.TraceAudioConverter) TDebug.out("Input does not appear to be an Ogg bitstream.");
+ throw new IOException("Input does not appear to be an Ogg bitstream.");
+ }
+ oggStreamState_.init(oggPage_.serialno());
+ vorbisInfo.init();
+ vorbisComment.init();
+ if(oggStreamState_.pagein(oggPage_) < 0)
+ {
+ // error; stream version mismatch perhaps
+ if(TDebug.TraceAudioConverter) TDebug.out("Error reading first page of Ogg bitstream data.");
+ throw new IOException("Error reading first page of Ogg bitstream data.");
+ }
+ if(oggStreamState_.packetout(oggPacket_) != 1)
+ {
+ // no page? must not be vorbis
+ if(TDebug.TraceAudioConverter) TDebug.out("Error reading initial header packet.");
+ throw new IOException("Error reading initial header packet.");
+ }
+ if(vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_) < 0)
+ {
+ // error case; not a vorbis header
+ if(TDebug.TraceAudioConverter) TDebug.out("This Ogg bitstream does not contain Vorbis audio data.");
+ throw new IOException("This Ogg bitstream does not contain Vorbis audio data.");
+ }
+ //int i = 0;
+ i = 0;
+ while(i < 2)
+ {
+ while(i < 2)
+ {
+ int result = oggSyncState_.pageout(oggPage_);
+ if(result == 0)
+ {
+ break;
+ } // Need more data
+ if(result == 1)
+ {
+ oggStreamState_.pagein(oggPage_);
+ while(i < 2)
+ {
+ result = oggStreamState_.packetout(oggPacket_);
+ if(result == 0)
+ {
+ break;
+ }
+ if(result == -1)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Corrupt secondary header. Exiting.");
+ throw new IOException("Corrupt secondary header. Exiting.");
+ }
+ vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_);
+ i++;
+ }
+ }
+ }
+ index = oggSyncState_.buffer(bufferSize_);
+ buffer = oggSyncState_.data;
+ bytes = readFromStream(buffer, index, bufferSize_);
+ if(bytes == -1)
+ {
+ break;
+ }
+ if(bytes == 0 && i < 2)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("End of file before finding all Vorbis headers!");
+ throw new IOException("End of file before finding all Vorbis headers!");
+ }
+ oggSyncState_.wrote(bytes);
+ }
+
+ byte[][] ptr = vorbisComment.user_comments;
+ String currComment = "";
+
+ for(int j = 0; j < ptr.length; j++)
+ {
+ if(ptr[j] == null)
+ {
+ break;
+ }
+ currComment = (new String(ptr[j], 0, ptr[j].length - 1)).trim();
+ if(TDebug.TraceAudioConverter) TDebug.out("Comment: " + currComment);
+ }
+ convsize = bufferSize_ / vorbisInfo.channels;
+ vorbisDspState.synthesis_init(vorbisInfo);
+ vorbisBlock.init(vorbisDspState);
+ _pcmf = new float[1][][];
+ _index = new int[vorbisInfo.channels];
+ }
+
+ /**
+ * Reads from the oggBitStream_ a specified number of Bytes(bufferSize_) worth
+ * starting at index and puts them in the specified buffer[].
+ *
+ * @param buffer
+ * @param index
+ * @param bufferSize_
+ * @return the number of bytes read or -1 if error.
+ */
+ private int readFromStream(byte[] buffer, int index, int bufferSize_)
+ {
+ int bytes = 0;
+ try
+ {
+ bytes = oggBitStream_.read(buffer, index, bufferSize_);
+ }
+ catch(Exception e)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Cannot Read Selected Song");
+ bytes = -1;
+ }
+ currentBytes = currentBytes + bytes;
+ return bytes;
+ }
+
+ /**
+ * Close the stream.
+ */
+ public void close() throws IOException
+ {
+ super.close();
+ oggBitStream_.close();
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/convert/VorbisFormatConversionProvider.java b/songdbj/javazoom/spi/vorbis/sampled/convert/VorbisFormatConversionProvider.java
new file mode 100644
index 0000000000..d1321f2590
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/convert/VorbisFormatConversionProvider.java
@@ -0,0 +1,244 @@
+/*
+ * VorbisFormatConversionProvider.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.convert;
+
+import java.util.Arrays;
+import javazoom.spi.vorbis.sampled.file.VorbisEncoding;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+
+import org.tritonus.share.sampled.convert.TMatrixFormatConversionProvider;
+
+/**
+ * ConversionProvider for VORBIS files.
+ */
+public class VorbisFormatConversionProvider extends TMatrixFormatConversionProvider
+{
+ private static final AudioFormat[] INPUT_FORMATS =
+ {
+ new AudioFormat(VorbisEncoding.VORBISENC, 32000.0F, -1, 1, -1, -1, false), // 0
+ new AudioFormat(VorbisEncoding.VORBISENC, 32000.0F, -1, 2, -1, -1, false), // 1
+ new AudioFormat(VorbisEncoding.VORBISENC, 44100.0F, -1, 1, -1, -1, false), // 2
+ new AudioFormat(VorbisEncoding.VORBISENC, 44100.0F, -1, 2, -1, -1, false), // 3
+ new AudioFormat(VorbisEncoding.VORBISENC, 48000.0F, -1, 1, -1, -1, false), // 4
+ new AudioFormat(VorbisEncoding.VORBISENC, 48000.0F, -1, 2, -1, -1, false), // 5
+
+ new AudioFormat(VorbisEncoding.VORBISENC, 16000.0F, -1, 1, -1, -1, false), // 18
+ new AudioFormat(VorbisEncoding.VORBISENC, 16000.0F, -1, 2, -1, -1, false), // 19
+ new AudioFormat(VorbisEncoding.VORBISENC, 22050.0F, -1, 1, -1, -1, false), // 20
+ new AudioFormat(VorbisEncoding.VORBISENC, 22050.0F, -1, 2, -1, -1, false), // 21
+ new AudioFormat(VorbisEncoding.VORBISENC, 24000.0F, -1, 1, -1, -1, false), // 22
+ new AudioFormat(VorbisEncoding.VORBISENC, 24000.0F, -1, 2, -1, -1, false), // 23
+
+ new AudioFormat(VorbisEncoding.VORBISENC, 8000.0F, -1, 1, -1, -1, false), // 36
+ new AudioFormat(VorbisEncoding.VORBISENC, 8000.0F, -1, 2, -1, -1, false), // 37
+ new AudioFormat(VorbisEncoding.VORBISENC, 11025.0F, -1, 1, -1, -1, false), // 38
+ new AudioFormat(VorbisEncoding.VORBISENC, 11025.0F, -1, 2, -1, -1, false), // 39
+ new AudioFormat(VorbisEncoding.VORBISENC, 12000.0F, -1, 1, -1, -1, false), // 40
+ new AudioFormat(VorbisEncoding.VORBISENC, 12000.0F, -1, 2, -1, -1, false), // 41
+ };
+
+ private static final AudioFormat[] OUTPUT_FORMATS =
+ {
+ new AudioFormat(8000.0F, 16, 1, true, false), // 0
+ new AudioFormat(8000.0F, 16, 1, true, true), // 1
+ new AudioFormat(8000.0F, 16, 2, true, false), // 2
+ new AudioFormat(8000.0F, 16, 2, true, true), // 3
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(8000.0F, 24, 1, true, false),
+ new AudioFormat(8000.0F, 24, 1, true, true),
+ new AudioFormat(8000.0F, 24, 2, true, false),
+ new AudioFormat(8000.0F, 24, 2, true, true),
+ new AudioFormat(8000.0F, 32, 1, true, false),
+ new AudioFormat(8000.0F, 32, 1, true, true),
+ new AudioFormat(8000.0F, 32, 2, true, false),
+ new AudioFormat(8000.0F, 32, 2, true, true),
+ */
+ new AudioFormat(11025.0F, 16, 1, true, false), // 4
+ new AudioFormat(11025.0F, 16, 1, true, true), // 5
+ new AudioFormat(11025.0F, 16, 2, true, false), // 6
+ new AudioFormat(11025.0F, 16, 2, true, true), // 7
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(11025.0F, 24, 1, true, false),
+ new AudioFormat(11025.0F, 24, 1, true, true),
+ new AudioFormat(11025.0F, 24, 2, true, false),
+ new AudioFormat(11025.0F, 24, 2, true, true),
+ new AudioFormat(11025.0F, 32, 1, true, false),
+ new AudioFormat(11025.0F, 32, 1, true, true),
+ new AudioFormat(11025.0F, 32, 2, true, false),
+ new AudioFormat(11025.0F, 32, 2, true, true),
+ */
+ new AudioFormat(12000.0F, 16, 1, true, false), // 8
+ new AudioFormat(12000.0F, 16, 1, true, true), // 9
+ new AudioFormat(12000.0F, 16, 2, true, false), // 10
+ new AudioFormat(12000.0F, 16, 2, true, true), // 11
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(12000.0F, 24, 1, true, false),
+ new AudioFormat(12000.0F, 24, 1, true, true),
+ new AudioFormat(12000.0F, 24, 2, true, false),
+ new AudioFormat(12000.0F, 24, 2, true, true),
+ new AudioFormat(12000.0F, 32, 1, true, false),
+ new AudioFormat(12000.0F, 32, 1, true, true),
+ new AudioFormat(12000.0F, 32, 2, true, false),
+ new AudioFormat(12000.0F, 32, 2, true, true),
+ */
+ new AudioFormat(16000.0F, 16, 1, true, false), // 12
+ new AudioFormat(16000.0F, 16, 1, true, true), // 13
+ new AudioFormat(16000.0F, 16, 2, true, false), // 14
+ new AudioFormat(16000.0F, 16, 2, true, true), // 15
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(16000.0F, 24, 1, true, false),
+ new AudioFormat(16000.0F, 24, 1, true, true),
+ new AudioFormat(16000.0F, 24, 2, true, false),
+ new AudioFormat(16000.0F, 24, 2, true, true),
+ new AudioFormat(16000.0F, 32, 1, true, false),
+ new AudioFormat(16000.0F, 32, 1, true, true),
+ new AudioFormat(16000.0F, 32, 2, true, false),
+ new AudioFormat(16000.0F, 32, 2, true, true),
+ */
+ new AudioFormat(22050.0F, 16, 1, true, false), // 16
+ new AudioFormat(22050.0F, 16, 1, true, true), // 17
+ new AudioFormat(22050.0F, 16, 2, true, false), // 18
+ new AudioFormat(22050.0F, 16, 2, true, true), // 19
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(22050.0F, 24, 1, true, false),
+ new AudioFormat(22050.0F, 24, 1, true, true),
+ new AudioFormat(22050.0F, 24, 2, true, false),
+ new AudioFormat(22050.0F, 24, 2, true, true),
+ new AudioFormat(22050.0F, 32, 1, true, false),
+ new AudioFormat(22050.0F, 32, 1, true, true),
+ new AudioFormat(22050.0F, 32, 2, true, false),
+ new AudioFormat(22050.0F, 32, 2, true, true),
+ */
+ new AudioFormat(24000.0F, 16, 1, true, false), // 20
+ new AudioFormat(24000.0F, 16, 1, true, true), // 21
+ new AudioFormat(24000.0F, 16, 2, true, false), // 22
+ new AudioFormat(24000.0F, 16, 2, true, true), // 23
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(24000.0F, 24, 1, true, false),
+ new AudioFormat(24000.0F, 24, 1, true, true),
+ new AudioFormat(24000.0F, 24, 2, true, false),
+ new AudioFormat(24000.0F, 24, 2, true, true),
+ new AudioFormat(24000.0F, 32, 1, true, false),
+ new AudioFormat(24000.0F, 32, 1, true, true),
+ new AudioFormat(24000.0F, 32, 2, true, false),
+ new AudioFormat(24000.0F, 32, 2, true, true),
+ */
+ new AudioFormat(32000.0F, 16, 1, true, false), // 24
+ new AudioFormat(32000.0F, 16, 1, true, true), // 25
+ new AudioFormat(32000.0F, 16, 2, true, false), // 26
+ new AudioFormat(32000.0F, 16, 2, true, true), // 27
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(32000.0F, 24, 1, true, false),
+ new AudioFormat(32000.0F, 24, 1, true, true),
+ new AudioFormat(32000.0F, 24, 2, true, false),
+ new AudioFormat(32000.0F, 24, 2, true, true),
+ new AudioFormat(32000.0F, 32, 1, true, false),
+ new AudioFormat(32000.0F, 32, 1, true, true),
+ new AudioFormat(32000.0F, 32, 2, true, false),
+ new AudioFormat(32000.0F, 32, 2, true, true),
+ */
+ new AudioFormat(44100.0F, 16, 1, true, false), // 28
+ new AudioFormat(44100.0F, 16, 1, true, true), // 29
+ new AudioFormat(44100.0F, 16, 2, true, false), // 30
+ new AudioFormat(44100.0F, 16, 2, true, true), // 31
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(44100.0F, 24, 1, true, false),
+ new AudioFormat(44100.0F, 24, 1, true, true),
+ new AudioFormat(44100.0F, 24, 2, true, false),
+ new AudioFormat(44100.0F, 24, 2, true, true),
+ new AudioFormat(44100.0F, 32, 1, true, false),
+ new AudioFormat(44100.0F, 32, 1, true, true),
+ new AudioFormat(44100.0F, 32, 2, true, false),
+ new AudioFormat(44100.0F, 32, 2, true, true),
+ */
+ new AudioFormat(48000.0F, 16, 1, true, false), // 32
+ new AudioFormat(48000.0F, 16, 1, true, true), // 33
+ new AudioFormat(48000.0F, 16, 2, true, false), // 34
+ new AudioFormat(48000.0F, 16, 2, true, true), // 35
+ /* 24 and 32 bit not yet possible
+ new AudioFormat(48000.0F, 24, 1, true, false),
+ new AudioFormat(48000.0F, 24, 1, true, true),
+ new AudioFormat(48000.0F, 24, 2, true, false),
+ new AudioFormat(48000.0F, 24, 2, true, true),
+ new AudioFormat(48000.0F, 32, 1, true, false),
+ new AudioFormat(48000.0F, 32, 1, true, true),
+ new AudioFormat(48000.0F, 32, 2, true, false),
+ new AudioFormat(48000.0F, 32, 2, true, true),
+ */
+ };
+
+ private static final boolean t = true;
+ private static final boolean f = false;
+
+ /*
+ * One row for each source format.
+ */
+ private static final boolean[][] CONVERSIONS =
+ {
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,t,t,f,f,f,f, f,f,f,f,f,f}, // 0
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,t,t,f,f, f,f,f,f,f,f}, // 1
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,t,t, f,f,f,f,f,f}, // 2
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, t,t,f,f,f,f}, // 3
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,t,t,f,f}, // 4
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,t,t}, // 5
+
+ {f,f,f,f,f,f,f,f,f,f, f,f,t,t,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 18
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,t,t,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 19
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,t,t,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 20
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,t,t, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 21
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, t,t,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 22
+ {f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,t,t,f,f,f,f,f,f, f,f,f,f,f,f}, // 23
+
+ {t,t,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 36
+ {f,f,t,t,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 37
+ {f,f,f,f,t,t,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 38
+ {f,f,f,f,f,f,t,t,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 39
+ {f,f,f,f,f,f,f,f,t,t, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 40
+ {f,f,f,f,f,f,f,f,f,f, t,t,f,f,f,f,f,f,f,f, f,f,f,f,f,f,f,f,f,f, f,f,f,f,f,f}, // 41
+
+ };
+
+ /**
+ * Constructor.
+ */
+ public VorbisFormatConversionProvider()
+ {
+ super(Arrays.asList(INPUT_FORMATS), Arrays.asList(OUTPUT_FORMATS), CONVERSIONS);
+ }
+
+ /**
+ * Returns converted AudioInputStream.
+ */
+ public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream audioInputStream)
+ {
+ if (isConversionSupported(targetFormat, audioInputStream.getFormat()))
+ {
+ return new DecodedVorbisAudioInputStream(targetFormat, audioInputStream);
+ }
+ else
+ {
+ throw new IllegalArgumentException("conversion not supported");
+ }
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileFormat.java b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileFormat.java
new file mode 100644
index 0000000000..28b7c92a2a
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileFormat.java
@@ -0,0 +1,85 @@
+/*
+ * VorbisAudioFileFormat.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.file;
+
+import java.util.Map;
+
+import javax.sound.sampled.AudioFormat;
+
+import org.tritonus.share.sampled.file.TAudioFileFormat;
+
+/**
+ * @author JavaZOOM
+ */
+public class VorbisAudioFileFormat extends TAudioFileFormat
+{
+ /**
+ * Contructor.
+ * @param type
+ * @param audioFormat
+ * @param nLengthInFrames
+ * @param nLengthInBytes
+ */
+ public VorbisAudioFileFormat(Type type, AudioFormat audioFormat, int nLengthInFrames, int nLengthInBytes, Map properties)
+ {
+ super(type, audioFormat, nLengthInFrames, nLengthInBytes, properties);
+ }
+
+ /**
+ * Ogg Vorbis audio file format parameters.
+ * Some parameters might be unavailable. So availability test is required before reading any parameter.
+ *
+ *
AudioFileFormat parameters.
+ *
+ *
+ *
Ogg Vorbis parameters.
+ *
+ *
+ */
+ public Map properties()
+ {
+ return super.properties();
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileReader.java b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileReader.java
new file mode 100644
index 0000000000..40bc9cadee
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFileReader.java
@@ -0,0 +1,502 @@
+/*
+ * VorbisAudioFileReader.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.file;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+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 org.tritonus.share.TDebug;
+import org.tritonus.share.sampled.file.TAudioFileReader;
+
+import com.jcraft.jogg.Packet;
+import com.jcraft.jogg.Page;
+import com.jcraft.jogg.StreamState;
+import com.jcraft.jogg.SyncState;
+import com.jcraft.jorbis.Block;
+import com.jcraft.jorbis.Comment;
+import com.jcraft.jorbis.DspState;
+import com.jcraft.jorbis.Info;
+import com.jcraft.jorbis.JOrbisException;
+import com.jcraft.jorbis.VorbisFile;
+
+/**
+ * This class implements the AudioFileReader class and provides an
+ * Ogg Vorbis file reader for use with the Java Sound Service Provider Interface.
+ */
+public class VorbisAudioFileReader extends TAudioFileReader
+{
+ private SyncState oggSyncState_ = null;
+ private StreamState oggStreamState_ = null;
+ private Page oggPage_ = null;
+ private Packet oggPacket_ = null;
+ private Info vorbisInfo = null;
+ private Comment vorbisComment = null;
+ private DspState vorbisDspState = null;
+ private Block vorbisBlock = null;
+ private int bufferMultiple_ = 4;
+ private int bufferSize_ = bufferMultiple_ * 256 * 2;
+ private int convsize = bufferSize_ * 2;
+ private byte[] convbuffer = new byte[convsize];
+ private byte[] buffer = null;
+ private int bytes = 0;
+ private int rate = 0;
+ private int channels = 0;
+
+ private int index = 0;
+ private InputStream oggBitStream_ = null;
+
+ private static final int INITAL_READ_LENGTH = 64000;
+ private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
+
+ public VorbisAudioFileReader()
+ {
+ super(MARK_LIMIT, true);
+ }
+
+ /**
+ * Return the AudioFileFormat from the given file.
+ */
+ public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioFileFormat(File file)");
+ InputStream inputStream = null;
+ try
+ {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ inputStream.mark(MARK_LIMIT);
+ AudioFileFormat aff = getAudioFileFormat(inputStream);
+ inputStream.reset();
+ // Get Vorbis file info such as length in seconds.
+ VorbisFile vf = new VorbisFile(file.getAbsolutePath());
+ return getAudioFileFormat(inputStream,(int) file.length(), (int) Math.round((vf.time_total(-1))*1000));
+ }
+ catch (JOrbisException e)
+ {
+ throw new IOException(e.getMessage());
+ }
+ finally
+ {
+ if (inputStream != null) inputStream.close();
+ }
+ }
+
+ /**
+ * Return the AudioFileFormat from the given URL.
+ */
+ public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioFileFormat(URL url)");
+ InputStream inputStream = url.openStream();
+ try
+ {
+ return getAudioFileFormat(inputStream);
+ }
+ finally
+ {
+ if (inputStream != null) inputStream.close();
+ }
+ }
+
+ /**
+ * Return the AudioFileFormat from the given InputStream.
+ */
+ public AudioFileFormat getAudioFileFormat(InputStream inputStream) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioFileFormat(InputStream inputStream)");
+ try
+ {
+ if (!inputStream.markSupported()) inputStream = new BufferedInputStream(inputStream);
+ inputStream.mark(MARK_LIMIT);
+ return getAudioFileFormat(inputStream, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
+ }
+ finally
+ {
+ inputStream.reset();
+ }
+ }
+
+ /**
+ * Return the AudioFileFormat from the given InputStream and length in bytes.
+ */
+ public AudioFileFormat getAudioFileFormat(InputStream inputStream, long medialength) throws UnsupportedAudioFileException, IOException
+ {
+ return getAudioFileFormat(inputStream, (int) medialength, AudioSystem.NOT_SPECIFIED);
+ }
+
+
+ /**
+ * Return the AudioFileFormat from the given InputStream, length in bytes and length in milliseconds.
+ */
+ protected AudioFileFormat getAudioFileFormat(InputStream bitStream, int mediaLength, int totalms) throws UnsupportedAudioFileException, IOException
+ {
+ HashMap aff_properties = new HashMap();
+ HashMap af_properties = new HashMap();
+ if (totalms == AudioSystem.NOT_SPECIFIED)
+ {
+ totalms = 0;
+ }
+ if (totalms <= 0)
+ {
+ totalms = 0;
+ }
+ else
+ {
+ aff_properties.put("duration",new Long(totalms*1000));
+ }
+ oggBitStream_ = bitStream;
+ init_jorbis();
+ index = 0;
+ try
+ {
+ readHeaders(aff_properties, af_properties);
+ }
+ catch (IOException ioe)
+ {
+ if (TDebug.TraceAudioFileReader)
+ {
+ TDebug.out(ioe.getMessage());
+ }
+ throw new UnsupportedAudioFileException(ioe.getMessage());
+ }
+
+ String dmp = vorbisInfo.toString();
+ if (TDebug.TraceAudioFileReader)
+ {
+ TDebug.out(dmp);
+ }
+ int ind = dmp.lastIndexOf("bitrate:");
+ int minbitrate = -1;
+ int nominalbitrate = -1;
+ int maxbitrate = -1;
+ if (ind != -1)
+ {
+ dmp = dmp.substring(ind + 8, dmp.length());
+ StringTokenizer st = new StringTokenizer(dmp, ",");
+ if (st.hasMoreTokens())
+ {
+ minbitrate = Integer.parseInt(st.nextToken());
+ }
+ if (st.hasMoreTokens())
+ {
+ nominalbitrate = Integer.parseInt(st.nextToken());
+ }
+ if (st.hasMoreTokens())
+ {
+ maxbitrate = Integer.parseInt(st.nextToken());
+ }
+ }
+ if (nominalbitrate > 0) af_properties.put("bitrate",new Integer(nominalbitrate));
+ af_properties.put("vbr",new Boolean(true));
+
+ if (minbitrate > 0) aff_properties.put("ogg.bitrate.min.bps",new Integer(minbitrate));
+ if (maxbitrate > 0) aff_properties.put("ogg.bitrate.max.bps",new Integer(maxbitrate));
+ if (nominalbitrate > 0) aff_properties.put("ogg.bitrate.nominal.bps",new Integer(nominalbitrate));
+ if (vorbisInfo.channels > 0) aff_properties.put("ogg.channels",new Integer(vorbisInfo.channels));
+ if (vorbisInfo.rate > 0) aff_properties.put("ogg.frequency.hz",new Integer(vorbisInfo.rate));
+ if (mediaLength > 0) aff_properties.put("ogg.length.bytes",new Integer(mediaLength));
+ aff_properties.put("ogg.version",new Integer(vorbisInfo.version));
+
+ AudioFormat.Encoding encoding = VorbisEncoding.VORBISENC;
+ AudioFormat format = new VorbisAudioFormat(encoding, vorbisInfo.rate, AudioSystem.NOT_SPECIFIED, vorbisInfo.channels, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED, true,af_properties);
+ AudioFileFormat.Type type = VorbisFileFormatType.OGG;
+ return new VorbisAudioFileFormat(VorbisFileFormatType.OGG, format, AudioSystem.NOT_SPECIFIED, mediaLength,aff_properties);
+ }
+
+ /**
+ * Return the AudioInputStream from the given InputStream.
+ */
+ public AudioInputStream getAudioInputStream(InputStream inputStream) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(InputStream inputStream)");
+ return getAudioInputStream(inputStream, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
+ }
+
+ /**
+ * Return the AudioInputStream from the given InputStream.
+ */
+ public AudioInputStream getAudioInputStream(InputStream inputStream, int medialength, int totalms) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(InputStream inputStreamint medialength, int totalms)");
+ try
+ {
+ if (!inputStream.markSupported()) inputStream = new BufferedInputStream(inputStream);
+ inputStream.mark(MARK_LIMIT);
+ AudioFileFormat audioFileFormat = getAudioFileFormat(inputStream, medialength, totalms);
+ inputStream.reset();
+ return new AudioInputStream(inputStream, audioFileFormat.getFormat(), audioFileFormat.getFrameLength());
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ inputStream.reset();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ inputStream.reset();
+ throw e;
+ }
+ }
+
+ /**
+ * Return the AudioInputStream from the given File.
+ */
+ public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(File file)");
+ InputStream inputStream = new FileInputStream(file);
+ try
+ {
+ return getAudioInputStream(inputStream);
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ }
+
+ /**
+ * Return the AudioInputStream from the given URL.
+ */
+ public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException
+ {
+ if (TDebug.TraceAudioFileReader) TDebug.out("getAudioInputStream(URL url)");
+ InputStream inputStream = url.openStream();
+ try
+ {
+ return getAudioInputStream(inputStream);
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ if (inputStream != null) inputStream.close();
+ throw e;
+ }
+ }
+
+ /**
+ * Reads headers and comments.
+ */
+ private void readHeaders(HashMap aff_properties, HashMap af_properties) throws IOException
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("readHeaders(");
+ index = oggSyncState_.buffer(bufferSize_);
+ buffer = oggSyncState_.data;
+ bytes = readFromStream(buffer, index, bufferSize_);
+ if(bytes == -1)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Cannot get any data from selected Ogg bitstream.");
+ throw new IOException("Cannot get any data from selected Ogg bitstream.");
+ }
+ oggSyncState_.wrote(bytes);
+ if(oggSyncState_.pageout(oggPage_) != 1)
+ {
+ if(bytes < bufferSize_)
+ {
+ throw new IOException("EOF");
+ }
+ if(TDebug.TraceAudioConverter) TDebug.out("Input does not appear to be an Ogg bitstream.");
+ throw new IOException("Input does not appear to be an Ogg bitstream.");
+ }
+ oggStreamState_.init(oggPage_.serialno());
+ vorbisInfo.init();
+ vorbisComment.init();
+ aff_properties.put("ogg.serial",new Integer(oggPage_.serialno()));
+ if(oggStreamState_.pagein(oggPage_) < 0)
+ {
+ // error; stream version mismatch perhaps
+ if(TDebug.TraceAudioConverter) TDebug.out("Error reading first page of Ogg bitstream data.");
+ throw new IOException("Error reading first page of Ogg bitstream data.");
+ }
+ if(oggStreamState_.packetout(oggPacket_) != 1)
+ {
+ // no page? must not be vorbis
+ if(TDebug.TraceAudioConverter) TDebug.out("Error reading initial header packet.");
+ throw new IOException("Error reading initial header packet.");
+ }
+ if(vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_) < 0)
+ {
+ // error case; not a vorbis header
+ if(TDebug.TraceAudioConverter) TDebug.out("This Ogg bitstream does not contain Vorbis audio data.");
+ throw new IOException("This Ogg bitstream does not contain Vorbis audio data.");
+ }
+ int i = 0;
+ while(i < 2)
+ {
+ while(i < 2)
+ {
+ int result = oggSyncState_.pageout(oggPage_);
+ if(result == 0)
+ {
+ break;
+ } // Need more data
+ if(result == 1)
+ {
+ oggStreamState_.pagein(oggPage_);
+ while(i < 2)
+ {
+ result = oggStreamState_.packetout(oggPacket_);
+ if(result == 0)
+ {
+ break;
+ }
+ if(result == -1)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("Corrupt secondary header. Exiting.");
+ throw new IOException("Corrupt secondary header. Exiting.");
+ }
+ vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_);
+ i++;
+ }
+ }
+ }
+ index = oggSyncState_.buffer(bufferSize_);
+ buffer = oggSyncState_.data;
+ bytes = readFromStream(buffer, index, bufferSize_);
+ if(bytes == -1)
+ {
+ break;
+ }
+ if(bytes == 0 && i < 2)
+ {
+ if(TDebug.TraceAudioConverter) TDebug.out("End of file before finding all Vorbis headers!");
+ throw new IOException("End of file before finding all Vorbis headers!");
+ }
+ oggSyncState_.wrote(bytes);
+ }
+ // Read Ogg Vorbis comments.
+ byte[][] ptr = vorbisComment.user_comments;
+ String currComment = "";
+ int c = 0;
+ for(int j = 0; j < ptr.length; j++)
+ {
+ if(ptr[j] == null)
+ {
+ break;
+ }
+ currComment = (new String(ptr[j], 0, ptr[j].length - 1)).trim();
+ if(TDebug.TraceAudioConverter) TDebug.out(currComment);
+ if (currComment.toLowerCase().startsWith("artist"))
+ {
+ aff_properties.put("author",currComment.substring(7));
+ }
+ else if (currComment.toLowerCase().startsWith("title"))
+ {
+ aff_properties.put("title",currComment.substring(6));
+ }
+ else if (currComment.toLowerCase().startsWith("album"))
+ {
+ aff_properties.put("album",currComment.substring(6));
+ }
+ else if (currComment.toLowerCase().startsWith("date"))
+ {
+ aff_properties.put("date",currComment.substring(5));
+ }
+ else if (currComment.toLowerCase().startsWith("copyright"))
+ {
+ aff_properties.put("copyright",currComment.substring(10));
+ }
+ else if (currComment.toLowerCase().startsWith("comment"))
+ {
+ aff_properties.put("comment",currComment.substring(8));
+ }
+ else if (currComment.toLowerCase().startsWith("genre"))
+ {
+ aff_properties.put("ogg.comment.genre",currComment.substring(6));
+ }
+ else if (currComment.toLowerCase().startsWith("tracknumber"))
+ {
+ aff_properties.put("ogg.comment.track",currComment.substring(12));
+ }
+ else
+ {
+ c++;
+ aff_properties.put("ogg.comment.ext."+c,currComment);
+ }
+ aff_properties.put("ogg.comment.encodedby",new String(vorbisComment.vendor, 0, vorbisComment.vendor.length - 1));
+ }
+ }
+
+ /**
+ * Reads from the oggBitStream_ a specified number of Bytes(bufferSize_) worth
+ * starting at index and puts them in the specified buffer[].
+ *
+ * @return the number of bytes read or -1 if error.
+ */
+ private int readFromStream(byte[] buffer, int index, int bufferSize_)
+ {
+ int bytes = 0;
+ try
+ {
+ bytes = oggBitStream_.read(buffer, index, bufferSize_);
+ }
+ catch (Exception e)
+ {
+ if (TDebug.TraceAudioFileReader)
+ {
+ TDebug.out("Cannot Read Selected Song");
+ }
+ bytes = -1;
+ }
+ return bytes;
+ }
+
+ /**
+ * Initializes all the jOrbis and jOgg vars that are used for song playback.
+ */
+ private void init_jorbis()
+ {
+ oggSyncState_ = new SyncState();
+ oggStreamState_ = new StreamState();
+ oggPage_ = new Page();
+ oggPacket_ = new Packet();
+ vorbisInfo = new Info();
+ vorbisComment = new Comment();
+ vorbisDspState = new DspState();
+ vorbisBlock = new Block(vorbisDspState);
+ buffer = null;
+ bytes = 0;
+ oggSyncState_.init();
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFormat.java b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFormat.java
new file mode 100644
index 0000000000..829ab2f8cd
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisAudioFormat.java
@@ -0,0 +1,66 @@
+/*
+ * VorbisAudioFormat.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.file;
+
+import java.util.Map;
+
+import javax.sound.sampled.AudioFormat;
+
+import org.tritonus.share.sampled.TAudioFormat;
+
+/**
+ * @author JavaZOOM
+ */
+public class VorbisAudioFormat extends TAudioFormat
+{
+ /**
+ * Constructor.
+ * @param encoding
+ * @param nFrequency
+ * @param SampleSizeInBits
+ * @param nChannels
+ * @param FrameSize
+ * @param FrameRate
+ * @param isBigEndian
+ * @param properties
+ */
+ public VorbisAudioFormat(AudioFormat.Encoding encoding, float nFrequency, int SampleSizeInBits, int nChannels, int FrameSize, float FrameRate, boolean isBigEndian, Map properties)
+ {
+ super(encoding, nFrequency, SampleSizeInBits, nChannels, FrameSize, FrameRate, isBigEndian, properties);
+ }
+
+ /**
+ * Ogg Vorbis audio format parameters.
+ * Some parameters might be unavailable. So availability test is required before reading any parameter.
+ *
+ *
For instance :
+ *
ogg.comment.ext.1=Something
+ *
ogg.comment.ext.2=Another comment
+ *
AudioFormat parameters.
+ *
+ *
+ */
+ public Map properties()
+ {
+ return super.properties();
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/file/VorbisEncoding.java b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisEncoding.java
new file mode 100644
index 0000000000..7800f1556d
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisEncoding.java
@@ -0,0 +1,41 @@
+/*
+ * VorbisEncoding.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.file;
+
+import javax.sound.sampled.AudioFormat;
+
+/**
+ * Encodings used by the VORBIS audio decoder.
+ */
+public class VorbisEncoding extends AudioFormat.Encoding
+{
+ public static final AudioFormat.Encoding VORBISENC = new VorbisEncoding("VORBISENC");
+
+ /**
+ * Constructors.
+ */
+ public VorbisEncoding(String name)
+ {
+ super(name);
+ }
+}
diff --git a/songdbj/javazoom/spi/vorbis/sampled/file/VorbisFileFormatType.java b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisFileFormatType.java
new file mode 100644
index 0000000000..f006bbfe1d
--- /dev/null
+++ b/songdbj/javazoom/spi/vorbis/sampled/file/VorbisFileFormatType.java
@@ -0,0 +1,41 @@
+/*
+ * VorbisFileFormatType.
+ *
+ * JavaZOOM : vorbisspi@javazoom.net
+ * http://www.javazoom.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package javazoom.spi.vorbis.sampled.file;
+
+import javax.sound.sampled.AudioFileFormat;
+
+/**
+ * FileFormatTypes used by the VORBIS audio decoder.
+ */
+public class VorbisFileFormatType extends AudioFileFormat.Type
+{
+ public static final AudioFileFormat.Type VORBIS = new VorbisFileFormatType("VORBIS", "ogg");
+ public static final AudioFileFormat.Type OGG = new VorbisFileFormatType("OGG", "ogg");
+ /**
+ * Constructor.
+ */
+ public VorbisFileFormatType(String name, String extension)
+ {
+ super(name, extension);
+ }
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java b/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java
new file mode 100644
index 0000000000..72522eaa4d
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java
@@ -0,0 +1,66 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+/**
+ * This exception signals that the Tag could not be decoded for various
+ * reasons.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class FormatDecodeException extends Exception {
+ private static final long serialVersionUID = 3690758397339187507L;
+
+ /**
+ * Constructor for the FormatDecodeException object
+ *
+ * @param msg A message
+ */
+ public FormatDecodeException( String msg ) {
+ super( msg );
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/LTR.java b/songdbj/net/shredzone/ifish/ltr/LTR.java
new file mode 100644
index 0000000000..8a38676583
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/LTR.java
@@ -0,0 +1,251 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+
+/**
+ * The Base Class for the Lightweight Tag Reader. LTR was made to read
+ * the basic tag information of MP3 (IDv1, IDv2), Ogg Vorbis and ASF/WMA
+ * files. It is lightweight because it is optimized for speed, and is
+ * only able to read the tag information, but unable to edit them.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public abstract class LTR {
+ protected RandomAccessFile in; // The file to be checked
+
+ /**
+ * Create a new LTR object. It is connected to the file to be decoded,
+ * by a RandomAccessFile. The cursor position will be changed during
+ * recognizing and decoding.
+ *
+ * @param in RandomAccessFile to be used
+ * @throws FormatDecodeException Description of the Exception
+ */
+ public LTR( RandomAccessFile in )
+ throws FormatDecodeException {
+ this.in = in;
+ try {
+ in.seek( 0 ); // To the beginning of file
+ } catch( IOException e ) {
+ throw new FormatDecodeException( "couldn't seek: " + e.toString() );
+ }
+ }
+
+ /**
+ * Create an LTR object for a file. If the file given, was not
+ * recognized or did not contain any tags, null will be returned.
+ *
+ * @param file File to open
+ * @return LTR to this file, or null
+ * @exception IOException Description of the Exception
+ */
+ public static LTR create( File file ) {
+ RandomAccessFile in = null;
+ LTR result = null;
+
+ try {
+ in = new RandomAccessFile( file, "r" );
+
+ try {
+ result = new TagOggVorbis( in );
+ return result;
+ } catch( FormatDecodeException e ) {}
+
+ try {
+ result = new TagMp3v2( in );
+ return result;
+ } catch( FormatDecodeException e ) {}
+
+ try {
+ result = new TagMp3v200( in );
+ return result;
+ } catch( FormatDecodeException e ) {}
+
+ try {
+ result = new TagAsf( in, file );
+ return result;
+ }catch( FormatDecodeException e ) {}
+
+ try {
+ // Always check ID3v1 *after* ID3v2, because a lot of ID3v2
+ // files also contain ID3v1 tags with limited content.
+ result = new TagMp3v1( in );
+ return result;
+ } catch( FormatDecodeException e ) {}
+ }catch(IOException e) {
+ return null;
+ } finally {
+ try {
+ if( in!=null ) in.close();
+ } catch(IOException e) {
+ System.out.println("Failed to close file.");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public abstract String getType();
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public abstract String getArtist();
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public abstract String getAlbum();
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public abstract String getTitle();
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public abstract String getGenre();
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public abstract String getYear();
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public abstract String getComment();
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public abstract String getTrack();
+
+ /**
+ * Read a String of a certain length from the file. It will always use
+ * "ISO-8859-1" encoding!
+ *
+ * @param length Maximum number of bytes to read
+ * @return String that was read
+ * @throws IOException If EOF was already reached
+ */
+ protected String readStringLen( int length )
+ throws IOException {
+ return readStringLen( length, "ISO-8859-1" );
+ }
+
+ /**
+ * Read a String of a certain length from the file. The length will
+ * not be exceeded. No null termination is required. Anyhow an
+ * IOException will be thrown if the EOF was reached before invocation.
+ *
+ * @param length Maximum number of bytes to read
+ * @param charset Charset to be used
+ * @return String that was read
+ * @throws IOException If EOF was already reached
+ */
+ protected String readStringLen( int length, String charset )
+ throws IOException {
+ byte[] buf = new byte[length];
+ int readlength = in.read( buf );
+ if( readlength < 0 ) {
+ throw new IOException( "Unexpected EOF" );
+ }
+ return new String( buf, 0, readlength, charset );
+ }
+
+ /**
+ * Return a string representation of the LTR content.
+ *
+ * @return String representation
+ */
+ public String toString() {
+ StringBuffer buff = new StringBuffer();
+ buff.append( getType() );
+ buff.append( "[ART='" );
+ buff.append( getArtist() );
+ buff.append( "' ALB='" );
+ buff.append( getAlbum() );
+ buff.append( "' TIT='" );
+ buff.append( getTitle() );
+ buff.append( "' TRK='" );
+ buff.append( getTrack() );
+ buff.append( "' GEN='" );
+ buff.append( getGenre() );
+ buff.append( "' YR='" );
+ buff.append( getYear() );
+ buff.append( "' CMT='" );
+ buff.append( getComment() );
+ buff.append( "']" );
+ return buff.toString();
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/LTRmp3.java b/songdbj/net/shredzone/ifish/ltr/LTRmp3.java
new file mode 100644
index 0000000000..1b31c405ba
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/LTRmp3.java
@@ -0,0 +1,165 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+
+/**
+ * The Base Class for mp3 decoding of the Lightweight Tag Reader.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public abstract class LTRmp3 extends LTR {
+
+ private final static String[] genres = {
+ //--- Genres as specified in ID3v1 ---
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
+ "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other",
+ "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
+ "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack",
+ "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
+ "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
+ "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
+ "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
+ "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
+ "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream",
+ "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
+ "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
+ "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
+ "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
+ "Rock & Roll", "Hard Rock",
+
+ //--- This are WinAmp extensions ---
+ "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebop",
+ "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
+ "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
+ "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
+ "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass",
+ "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
+ "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
+ "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
+ };
+
+ protected final String charsetV1 = "ISO-8859-1";
+ protected final String charsetV2 = "ISO-8859-1";
+
+ /**
+ * Constructor for the LTRmp3 object
+ *
+ * @param in File to be used
+ * @throws FormatDecodeException Could not decode file
+ */
+ public LTRmp3( RandomAccessFile in )
+ throws FormatDecodeException {
+ super( in );
+ }
+
+ /**
+ * Decode the mp3 numerical Genre code and convert it to a human
+ * readable string. The genre is decoded according to the
+ * specifications found at www.id3.org,
+ * as well as the WinAmp extensions.
+ *
+ * @param id Genre ID
+ * @return ID String, null if the genre ID was unknown
+ */
+ protected String decodeGenre( int id ) {
+ if( id>=genres.length ) return null;
+ return genres[id];
+ }
+
+ /**
+ * Read an ID3v2 integer. This is a 4 byte big endian value, which is
+ * always not syncsafe.
+ *
+ * @return The integer read.
+ * @throws IOException If there were not enough bytes in the file.
+ */
+ protected int readInt()
+ throws IOException {
+ int val = 0;
+ for( int cnt = 4; cnt > 0; cnt-- ) {
+ val <<= 8;
+ val |= ( in.readByte() & 0xFF );
+ }
+ return val;
+ }
+
+ /**
+ * Read an ID3v2 syncsafe integer. This is a 4 byte big endian value
+ * with the bit 7 of each byte always being 0.
+ *
+ * @return The syncsafe integer read.
+ * @throws IOException If there were not enough bytes in the file.
+ */
+ protected int readSyncsafeInt()
+ throws IOException {
+ int val = 0;
+ for( int cnt = 4; cnt > 0; cnt-- ) {
+ val <<= 7;
+ val |= ( readSyncsafe() & 0x7F );
+ }
+ return val;
+ }
+
+ /**
+ * Read a syncsafe byte. It is made sure that a byte is available in
+ * the file, and that bit 7 is 0. An IOException is thrown otherwise.
+ *
+ * @return The byte read.
+ * @throws IOException If premature EOF was reached or byte was not
+ * syncsafe.
+ */
+ protected byte readSyncsafe()
+ throws IOException {
+ byte read = in.readByte();
+ if(( read & 0x80 ) != 0 ) {
+ throw new IOException( "not syncsafe" );
+ }
+ return read;
+ }
+
+}
diff --git a/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java b/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java
new file mode 100644
index 0000000000..f86699b0f1
--- /dev/null
+++ b/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java
@@ -0,0 +1,249 @@
+/*
+ * iFish -- An iRiver iHP jukebox database creation tool
+ *
+ * Copyright (c) 2004 Richard "Shred" Körber
+ * http://www.shredzone.net/go/ifish
+ *
+ *-----------------------------------------------------------------------
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is IFISH.
+ *
+ * The Initial Developer of the Original Code is
+ * Richard "Shred" Körber.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+package net.shredzone.ifish.ltr;
+
+import java.io.*;
+import java.util.*;
+
+import de.jarnbjo.ogg.*;
+
+/**
+ * Replacement file reader class. The original J-Ogg FileStream has the
+ * major disadvantage that it reads the entire file, even though we just
+ * need a few byte from it. This FastFileStream will only read as little
+ * information as possible.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class OggFastFileStream implements PhysicalOggStream {
+ private InputStream sourceStream;
+ private boolean closed = false;
+ private int contentLength = 0;
+ private int position = 0;
+ private HashMap logicalStreams = new HashMap();
+ private OggPage firstPage;
+
+ /**
+ * Constructor for the OggFastFileStream object
+ *
+ * @param in RandomAccessFile to be read
+ * @throws OggFormatException Bad format
+ * @throws IOException IO error
+ */
+ public OggFastFileStream( RandomAccessFile in )
+ throws OggFormatException, IOException {
+ this.sourceStream = new RandomAdapterInputStream( in );
+ contentLength = (int) in.length();
+ firstPage = OggPage.create( sourceStream );
+ position += firstPage.getTotalLength();
+ LogicalOggStreamImpl los = new LogicalOggStreamImpl( this, firstPage.getStreamSerialNumber() );
+ logicalStreams.put( new Integer( firstPage.getStreamSerialNumber() ), los );
+ los.checkFormat( firstPage );
+ }
+
+ /**
+ * Get a collection of the logical streams.
+ *
+ * @return Collection
+ */
+ public Collection getLogicalStreams() {
+ return logicalStreams.values();
+ }
+
+ /**
+ * Checks if the file is open.
+ *
+ * @return true: open, false: closed
+ */
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ /**
+ * Closes the stream
+ *
+ * @throws IOException IO error
+ */
+ public void close()
+ throws IOException {
+ closed = true;
+ sourceStream.close();
+ }
+
+ /**
+ * Get the content length
+ *
+ * @return The content length
+ */
+ public int getContentLength() {
+ return contentLength;
+ }
+
+ /**
+ * Get the current position
+ *
+ * @return Position
+ */
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * Get an OggPage.
+ *
+ * @param index Index to be fetched
+ * @return The oggPage value
+ * @throws IOException IO Error
+ */
+ 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;
+ }
+ }
+
+ /**
+ * Move the stream to a certain time position.
+ *
+ * @param granulePosition The new position
+ * @throws IOException
+ */
+ public void setTime( long granulePosition )
+ throws IOException {
+ throw new UnsupportedOperationException( "not supported" );
+ }
+
+ /**
+ * Is this FileStream seekable? We pretend we are not, so J-Ogg
+ * will not get some stupid thoughts... ;)
+ *
+ * @return false
+ */
+ public boolean isSeekable() {
+ return false;
+ }
+
+/*--------------------------------------------------------------------*/
+
+ /**
+ * This class repairs a design flaw in JDK1.0. A RandomAccessFile
+ * is not derived from InputStream, though it provides the same API.
+ * This Adapter gives an InputStream view of a Random Access File.
+ *
+ * key = key.toUpperCase();
+ *
+ * after the method declaration header.
+ *
+ * @author Richard Körber <dev@shredzone.de>
+ * @version $Id$
+ */
+public class TagOggVorbis extends LTR {
+ private CommentHeader cmt;
+
+ /**
+ * Create a new TagOggVorbis object.
+ *
+ * @param in File to read
+ * @throws FormatDecodeException Couldn't decode this file
+ */
+ public TagOggVorbis( RandomAccessFile in )
+ throws FormatDecodeException {
+ super( in );
+
+ try {
+ //--- Check for Ogg Signature ---
+ // I expected J-Ogg to do this check, but it has been commented
+ // out because very old ogg files do not seem to have this header.
+ // We will ignore those files.
+ if( !readStringLen(4).equals("OggS") )
+ throw new FormatDecodeException( "not an Ogg file" );
+ in.seek(0);
+
+ //--- Get the Comment Header ---
+ OggFastFileStream fs = new OggFastFileStream(in);
+ LogicalOggStream los = (LogicalOggStream) fs.getLogicalStreams().iterator().next();
+ if( los.getFormat() != LogicalOggStream.FORMAT_VORBIS )
+ throw new FormatDecodeException( "not a plain Ogg Vorbis file" );
+ VorbisStream vos = new VorbisStream( los );
+ cmt = vos.getCommentHeader();
+
+ }catch( OggFormatException e ) {
+ throw new FormatDecodeException( "not an Ogg file" );
+ }catch( VorbisFormatException e ) {
+ throw new FormatDecodeException( "not an Ogg Vorbis file" );
+ }catch( IOException e ) {
+ throw new FormatDecodeException( "could not decode file: " + e.toString() );
+ }catch( RuntimeException e ) {
+ throw new FormatDecodeException( "error decoding file: " + e.toString() );
+ }
+ }
+
+ /**
+ * Get the type of this file. This is usually the compression format
+ * itself (e.g. "OGG" or "MP3"). If there are different taggings for
+ * this format, the used tag format is appended after a slash (e.g.
+ * "MP3/id3v2").
+ *
+ * @return The type
+ */
+ public String getType() {
+ return "OGG";
+ }
+
+ /**
+ * Get the artist.
+ *
+ * @return The artist (never null)
+ */
+ public String getArtist() {
+ String val = cmt.getArtist();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the album.
+ *
+ * @return The album (never null)
+ */
+ public String getAlbum() {
+ String val = cmt.getAlbum();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the title.
+ *
+ * @return The title (never null)
+ */
+ public String getTitle() {
+ String val = cmt.getTitle();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the genre.
+ *
+ * @return The genre (never null)
+ */
+ public String getGenre() {
+ String val = cmt.getGenre();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the year.
+ *
+ * @return The year (never null)
+ */
+ public String getYear() {
+ String val = cmt.getDate();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the comment.
+ *
+ * @return The comment (never null)
+ */
+ public String getComment() {
+ String val = cmt.getDescription();
+ if( val==null ) {
+ // *sigh* The Ogg Vorbis documentation does not explicitely
+ // state the comment types. So there are some ogg writers
+ // around that use COMMENT instead of DESCRIPTION. We will
+ // use COMMENT if DESCRIPTION was empty.
+ val = cmt.getComment("COMMENT");
+ }
+ if( val==null )
+ val="";
+ return val.trim();
+ }
+
+ /**
+ * Get the track.
+ *
+ * @return The track (never null)
+ */
+ public String getTrack() {
+ String val = cmt.getTrackNumber();
+ if( val==null ) val="";
+ return val.trim();
+ }
+
+}
diff --git a/songdbj/org/tritonus/file/AiffAudioFileReader.java b/songdbj/org/tritonus/file/AiffAudioFileReader.java
new file mode 100644
index 0000000000..139ba05425
--- /dev/null
+++ b/songdbj/org/tritonus/file/AiffAudioFileReader.java
@@ -0,0 +1,244 @@
+/*
+ * AiffAudioFileReader.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Florian Bomers contains(Object elem)
and get(Object elem)
+ * methods can be called with Strings as elem
parameter.
+ * For get(Object elem)
, the object that has been added
+ * is returned, and not its String representation.
+ * null
elements.
+ */
+
+public class StringHashedSetAudioFileFormat.Type
. The static
+ * instances of AIFF, AIFC, AU, SND, and WAVE types in class
+ * AudioFileFormat.Type
should be retrieved
+ * using this method, too (internally).
+ * At best, the protected constructor of that class
+ * should also be replaced to be a private constructor.
+ * Like this it will be prevented that developers create
+ * instances of Type, which causes problems with the
+ * equals method. In fact, the equals method should be redefined anyway
+ * so that it compares the names and not the objects.
+ * AudioFileFormat
. There are some file formats
+ * which have varying extensions depending, e.g. on the encoding.
+ * An example for this is MPEG: the special encoding Mpeg 1, layer 3
+ * has the extension mp3, whereas other Mpeg files use mpeg or mpg.
+ * This could be implemented with 2 methods in AudioFileFormat
:
+ *
+ *
+ * @author Florian Bomers
+ */
+public class AudioFileTypes extends AudioFileFormat.Type {
+
+ /** contains all known types */
+ private static StringHashedSet types = new StringHashedSet();
+
+ // initially add the standard types
+ static {
+ types.add(AudioFileFormat.Type.AIFF);
+ types.add(AudioFileFormat.Type.AIFC);
+ types.add(AudioFileFormat.Type.AU);
+ types.add(AudioFileFormat.Type.SND);
+ types.add(AudioFileFormat.Type.WAVE);
+ }
+
+ AudioFileTypes(String name, String ext) {
+ super(name, ext);
+ }
+
+ /**
+ * Use this method to retrieve an instance of
+ * AudioFileFormat.Type
of the specified
+ * name. If no type of this name is in the internally
+ * maintained list, null
is returned.
+ * AudioSystem.getAudioFileTypes()
+ * at initialization time.
+ *
+ * @see #getType(String, String)
+ */
+ public static AudioFileFormat.Type getType(String name) {
+ return getType(name, null);
+ }
+
+ /**
+ * Use this method to retrieve an instance of
+ * AudioFileFormat.Type
of the specified
+ * name. If it does not exist in the internal list
+ * of types, a new type is created and returned.
+ * If it a type of that name already exists (regardless
+ * of extension), it is returned. In this case it can
+ * not be guaranteed that the extension is the same as
+ * passed as parameter.
+ * If extension
is null
,
+ * this method returns null
if the
+ * type of the specified name does not exist in the
+ * internal list.
+ * AudioFileFormat.Type
.
+ */
+ public static AudioFileFormat.Type getType(String name, String extension) {
+ AudioFileFormat.Type res=(AudioFileFormat.Type) types.get(name);
+ if (res==null) {
+ // it is not already in the string set.
+ if (extension==null) {
+ return null;
+ }
+ // Create a new type instance.
+ res=new AudioFileTypes(name, extension);
+ // and save it for the future
+ types.add(res);
+ }
+ return res;
+ }
+
+ /**
+ * Tests for equality of 2 file types. They are equal when their names match.
+ * getAudioFormat(AudioFormat)
and
+ * matches(AudioFormat)
.
+ * contains(Object elem)
and get(Object elem)
+ * fail, if elem is not an instance of AudioFormat.
+ * null
elements.
+ * elem
.
+ * The first matching format is returned. If no element
+ * matches elem
, null
is returned.
+ *
+ * Exceptions:
+ *
+ *
+ * This is a proposition to be used as AudioFormat.matches.
+ * It can therefore be considered as a temporary workaround.
+ */
+ // IDEA: create a special "NOT_SPECIFIED" encoding
+ // and a AudioFormat.Encoding.matches method.
+ public static boolean matches(AudioFormat format1,
+ AudioFormat format2)
+ {
+ //$$fb 19 Dec 99: endian must be checked, too.
+ //
+ // we do have a problem with redundant elements:
+ // e.g.
+ // encoding=ALAW || ULAW -> bigEndian and samplesizeinbits don't matter
+ // sample size in bits == 8 -> bigEndian doesn't matter
+ // sample size in bits > 8 -> PCM is always signed.
+ // This is an overall issue in JavaSound, I think.
+ // At present, it is not consistently implemented to support these
+ // redundancies and implicit definitions
+ //
+ // As a workaround of this issue I return in the converters
+ // all combinations, e.g. for ULAW I return bigEndian and !bigEndian formats.
+/* old version
+*/
+ // as proposed by florian
+ return format1.getEncoding().equals(format2.getEncoding())
+ && (format2.getSampleSizeInBits()<=8
+ || format1.getSampleSizeInBits()==AudioSystem.NOT_SPECIFIED
+ || format2.getSampleSizeInBits()==AudioSystem.NOT_SPECIFIED
+ || format1.isBigEndian()==format2.isBigEndian())
+ && doMatch(format1.getChannels(),format2.getChannels())
+ && doMatch(format1.getSampleSizeInBits(), format2.getSampleSizeInBits())
+ && doMatch(format1.getFrameSize(), format2.getFrameSize())
+ && doMatch(format1.getSampleRate(), format2.getSampleRate())
+ && doMatch(format1.getFrameRate(),format2.getFrameRate());
+ }
+
+ /**
+ * Tests for exact equality of 2 AudioFormats.
+ * This is the behaviour of AudioFormat.matches in JavaSound 1.0.
+ *
+ * In other words: If SampleSizeInBits is AudioSystem.NOT_SPECIFIED
+ * in either format or both formats have a SampleSizeInBits<8,
+ * endianness does not matter.
+ * getEncoding
should be integrated into
+ * AudioFormat.Encoding(String name)
(possibly
+ * renamed to getInstance(String name)
.
+ * The static instances of ULAW, ALAW PCM_UNSIGNED and PCM_SIGNED
+ * encodings in that class should be retrieved using that function,
+ * too (internally).
+ * At best, the protected constructor of that class
+ * should also be replaced to be a private constructor.
+ * Like this it will be prevented that developers create their own
+ * instances of Encoding, which causes problems with the
+ * equals method. In fact, the equals method should be redefined anyway
+ * so that it compares the names and not the objects.
+ * getEncodings()
into AudioSystem (this is
+ * especially annoying as the relevant methods already exist
+ * in the provider interfaces of file readers, file writers and
+ * converters).
+ *
+ * @author Florian Bomers
+ */
+public class Encodings extends AudioFormat.Encoding {
+
+ /** contains all known encodings */
+ private static StringHashedSet encodings = new StringHashedSet();
+
+ // initially add the standard encodings
+ static {
+ encodings.add(AudioFormat.Encoding.PCM_SIGNED);
+ encodings.add(AudioFormat.Encoding.PCM_UNSIGNED);
+ encodings.add(AudioFormat.Encoding.ULAW);
+ encodings.add(AudioFormat.Encoding.ALAW);
+ }
+
+ Encodings(String name) {
+ super(name);
+ }
+
+ /**
+ * Use this method for retrieving an instance of
+ * AudioFormat.Encoding
of the specified
+ * name. A standard registry of encoding names will
+ * be maintained by the Tritonus team.
+ * AudioFormat.Encoding
.
+ */
+ /*
+ MP2000/09/11:
+ perhaps it is not a good idea to allow user programs the creation of new
+ encodings. The problem with it is that a plain typo will produce an encoding
+ object that is not supported. Instead, some indication of an error should be
+ signaled to the user program. And, there should be a second interface for
+ service providers allowing them to register encodings supported by themselves.
+
+ $$fb2000/09/26:
+ The problem is what you see as second issue: it can never be assured
+ that this class knows all available encodings. So at the moment, there
+ is no choice than to allow users to create any Encoding they wish.
+ The encodings database will simplify things.
+ A problem with an interface to retrieve supported encodings (or API
+ function in spi.FormatConversionProvider) is that this requires
+ loading of all providers very early. Hmmm, maybe this is necessary in any
+ case when the user issues something like AudioSystem.isConversionSupported.
+ */
+ public static AudioFormat.Encoding getEncoding(String name) {
+ AudioFormat.Encoding res=(AudioFormat.Encoding) encodings.get(name);
+ if (res==null) {
+ // it is not already in the string set. Create a new encoding instance.
+ res=new Encodings(name);
+ // and save it for the future
+ encodings.add(res);
+ }
+ return res;
+ }
+
+ /**
+ * Tests for equality of 2 encodings. They are equal when their strings match.
+ *
+ *
+ *
+ * However, backwards compatibility must always be retained, so
+ * even when an AudioInputStream implements FloatSampleInput,
+ * it must work the same way when any of the byte-based read methods
+ * is called.
+ * As an example, consider the following set-up:
+ *
+ *
+ * So, what happens when a block of samples is read from pcmAIS2 ?
+ *
+ *
+ * initFromByteArray
method of
+ * the float buffer to initialize it with the 8 bit data.
+ * convertToByteArray
method of
+ * the float buffer to fill the byte buffer with the
+ * resulting samples.
+ *
+ *
+ *
+ *
+ *
+ * The lazy mechanism can save many array instantiation (and copy-) operations
+ * for the sake of performance. All relevant methods exist in a second
+ * version which allows explicitely to disable lazy deletion.
+ * sampleCount
is reduced. A subsequent
+ * increase of the sample count (which will occur frequently), will check
+ * that and eventually reuse the existing array.
+ * reset
functions to clear the memory and remove
+ * hidden samples and channels.
+ * getChannel(int)
may have a greater size
+ * than getSampleCount(). Consequently, be sure to never rely on the
+ * length field of the sample arrays.
+ *
+ * Having lazy disabled would require for each chunk that is processed
+ *
+ *
+ *
+ * By default, this class uses dithering for reduction
+ * of sample width (e.g. original data was 16bit, target
+ * data is 8bit). As dithering may be needed in other cases
+ * (especially when the float samples are processed using DSP
+ * algorithms), or it is preferred to switch it off,
+ * dithering can be explicitely switched on or off with
+ * the method setDitherMode(int).
+ * For a discussion about dithering, see
+ *
+ * here and
+ *
+ * here.
+ *
+ * @author Florian Bomers
+ */
+
+public class FloatSampleBuffer {
+
+ /** Whether the functions without lazy parameter are lazy or not. */
+ private static final boolean LAZY_DEFAULT=true;
+
+ private ArrayListbuffer
.
+ */
+ public FloatSampleBuffer(byte[] buffer, int offset, int byteCount,
+ AudioFormat format) {
+ this(format.getChannels(),
+ byteCount/(format.getSampleSizeInBits()/8*format.getChannels()),
+ format.getSampleRate());
+ initFromByteArray(buffer, offset, byteCount, format);
+ }
+
+ protected void init(int channelCount, int sampleCount, float sampleRate) {
+ init(channelCount, sampleCount, sampleRate, LAZY_DEFAULT);
+ }
+
+ protected void init(int channelCount, int sampleCount, float sampleRate, boolean lazy) {
+ if (channelCount<0 || sampleCount<0) {
+ throw new IllegalArgumentException(
+ "invalid parameters in initialization of FloatSampleBuffer.");
+ }
+ setSampleRate(sampleRate);
+ if (getSampleCount()!=sampleCount || getChannelCount()!=channelCount) {
+ createChannels(channelCount, sampleCount, lazy);
+ }
+ }
+
+ private void createChannels(int channelCount, int sampleCount, boolean lazy) {
+ this.sampleCount=sampleCount;
+ // lazy delete of all channels. Intentionally lazy !
+ this.channelCount=0;
+ for (int ch=0; chbyteCount / format.getFrameSize()
.
+ * If LAZY_DEFAULT is true, it will use lazy deletion.
+ *
+ * @throws IllegalArgumentException
+ */
+ public void initFromByteArray(byte[] buffer, int offset, int byteCount,
+ AudioFormat format) {
+ initFromByteArray(buffer, offset, byteCount, format, LAZY_DEFAULT);
+ }
+
+
+ /**
+ * Resets this buffer with the audio data specified
+ * in the arguments. This FloatSampleBuffer's sample count
+ * will be set to byteCount / format.getFrameSize()
.
+ *
+ * @param lazy if true, then existing channels will be tried to be re-used
+ * to minimize garbage collection.
+ * @throws IllegalArgumentException
+ */
+ public void initFromByteArray(byte[] buffer, int offset, int byteCount,
+ AudioFormat format, boolean lazy) {
+ if (offset+byteCount>buffer.length) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.initFromByteArray: buffer too small.");
+ }
+
+ int thisSampleCount = byteCount/format.getFrameSize();
+ init(format.getChannels(), thisSampleCount, format.getSampleRate(), lazy);
+
+ // save format for automatic dithering mode
+ originalFormatType = FloatSampleTools.getFormatType(format);
+
+ FloatSampleTools.byte2float(buffer, offset,
+ channels, 0, sampleCount, format);
+ }
+
+ /**
+ * Resets this sample buffer with the data in source
.
+ */
+ public void initFromFloatSampleBuffer(FloatSampleBuffer source) {
+ init(source.getChannelCount(), source.getSampleCount(), source.getSampleRate());
+ for (int ch=0; chbuffer
must be large enough to hold all data.
+ *
+ * @throws IllegalArgumentException when buffer is too small or format
doesn't match
+ * @return number of bytes written to buffer
+ */
+ public int convertToByteArray(byte[] buffer, int offset, AudioFormat format) {
+ int byteCount = getByteArrayBufferSize(format);
+ if (offset + byteCount > buffer.length) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.convertToByteArray: buffer too small.");
+ }
+ if (format.getSampleRate()!=getSampleRate()) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.convertToByteArray: different samplerates.");
+ }
+ if (format.getChannels()!=getChannelCount()) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.convertToByteArray: different channel count.");
+ }
+ FloatSampleTools.float2byte(channels, 0, buffer, offset, getSampleCount(),
+ format, getConvertDitherBits(FloatSampleTools.getFormatType(format)));
+
+ return byteCount;
+ }
+
+
+ /**
+ * Creates a new byte[] buffer, fills it with the audio data, and returns it.
+ * @throws IllegalArgumentException when sample rate or channels do not match
+ * @see #convertToByteArray(byte[], int, AudioFormat)
+ */
+ public byte[] convertToByteArray(AudioFormat format) {
+ // throws exception when sampleRate doesn't match
+ // creates a new byte[] buffer and returns it
+ byte[] res = new byte[getByteArrayBufferSize(format)];
+ convertToByteArray(res, 0, format);
+ return res;
+ }
+
+ //////////////////////////////// actions /////////////////////////////////
+
+ /**
+ * Resizes this buffer.
+ * keepOldSamples
is true, as much as possible samples are
+ * retained. If the buffer is enlarged, silence is added at the end.
+ * If keepOldSamples
is false, existing samples are discarded
+ * and the buffer contains random samples.
+ */
+ public void changeSampleCount(int newSampleCount, boolean keepOldSamples) {
+ int oldSampleCount=getSampleCount();
+ if (oldSampleCount==newSampleCount) {
+ return;
+ }
+ Object[] oldChannels=null;
+ if (keepOldSamples) {
+ oldChannels=getAllChannels();
+ }
+ init(getChannelCount(), newSampleCount, getSampleRate());
+ if (keepOldSamples) {
+ // copy old channels and eventually silence out new samples
+ int copyCount=newSampleCountindex
.
+ * silent
is true, the new channel will be silent.
+ * Otherwise it will contain random data.
+ * lazy
is true, hidden channels which have at least getSampleCount()
+ * elements will be examined for reusage as inserted channel.
+ * If lazy
is false, still hidden channels are reused,
+ * but it is assured that the inserted channel has exactly getSampleCount() elements,
+ * thus not wasting memory.
+ */
+ public void insertChannel(int index, boolean silent, boolean lazy) {
+ int physSize=channels.size();
+ int virtSize=getChannelCount();
+ float[] newChannel=null;
+ if (physSize>virtSize) {
+ // there are hidden channels. Try to use one.
+ for (int ch=virtSize; ch
+ * It copies the first channel to all newly created channels.
+ * @param targetChannelCount the number of channels that this sample buffer
+ * will have after expanding. NOT the number of
+ * channels to add !
+ * @exception IllegalArgumentException if this buffer does not have one
+ * channel before calling this method.
+ */
+ public void expandChannel(int targetChannelCount) {
+ // even more sanity...
+ if (getChannelCount()!=1) {
+ throw new IllegalArgumentException(
+ "FloatSampleBuffer: can only expand channels for mono signals.");
+ }
+ for (int ch=1; ch
+ * The volume is NOT lowered !
+ * Be aware, this might cause clipping when converting back
+ * to integer samples.
+ */
+ public void mixDownChannels() {
+ float[] firstChannel=getChannel(0);
+ int sampleCount=getSampleCount();
+ int channelCount=getChannelCount();
+ for (int ch=channelCount-1; ch>0; ch--) {
+ float[] thisChannel=getChannel(ch);
+ for (int i=0; isrcBuffer
is read from index srcOffset
+ * to (srcOffset + (lengthInSamples * format.getFrameSize()))input
+ * @param format input buffer's audio format
+ * @param floatOffset the offset where to write the float samples
+ * @param frameCount number of samples to write to this sample buffer
+ */
+ public void setSamplesFromBytes(byte[] input, int inByteOffset, AudioFormat format,
+ int floatOffset, int frameCount) {
+ if (floatOffset < 0 || frameCount < 0 || inByteOffset < 0) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.setSamplesFromBytes: negative inByteOffset, floatOffset, or frameCount");
+ }
+ if (inByteOffset + (frameCount * format.getFrameSize()) > input.length) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.setSamplesFromBytes: input buffer too small.");
+ }
+ if (floatOffset + frameCount > getSampleCount()) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer.setSamplesFromBytes: frameCount too large");
+ }
+
+ FloatSampleTools.byte2float(input, inByteOffset, channels, floatOffset, frameCount, format);
+ }
+
+ //////////////////////////////// properties /////////////////////////////////
+
+ public int getChannelCount() {
+ return channelCount;
+ }
+
+ public int getSampleCount() {
+ return sampleCount;
+ }
+
+ public float getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * Sets the sample rate of this buffer.
+ * NOTE: no conversion is done. The samples are only re-interpreted.
+ */
+ public void setSampleRate(float sampleRate) {
+ if (sampleRate<=0) {
+ throw new IllegalArgumentException
+ ("Invalid samplerate for FloatSampleBuffer.");
+ }
+ this.sampleRate=sampleRate;
+ }
+
+ /**
+ * NOTE: the returned array may be larger than sampleCount. So in any case,
+ * sampleCount is to be respected.
+ */
+ public float[] getChannel(int channel) {
+ if (channel<0 || channel>=getChannelCount()) {
+ throw new IllegalArgumentException(
+ "FloatSampleBuffer: invalid channel number.");
+ }
+ return (float[]) channels.get(channel);
+ }
+
+ public Object[] getAllChannels() {
+ Object[] res=new Object[getChannelCount()];
+ for (int ch=0; ch
+ */
+ public void setDitherMode(int mode) {
+ if (mode!=DITHER_MODE_AUTOMATIC
+ && mode!=DITHER_MODE_ON
+ && mode!=DITHER_MODE_OFF) {
+ throw new IllegalArgumentException("Illegal DitherMode");
+ }
+ this.ditherMode=mode;
+ }
+
+ public int getDitherMode() {
+ return ditherMode;
+ }
+
+
+ /**
+ * @return the ditherBits parameter for the float2byte functions
+ */
+ protected float getConvertDitherBits(int newFormatType) {
+ // let's see whether dithering is necessary
+ boolean doDither = false;
+ switch (ditherMode) {
+ case DITHER_MODE_AUTOMATIC:
+ doDither=(originalFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK)>
+ (newFormatType & FloatSampleTools.F_SAMPLE_WIDTH_MASK);
+ break;
+ case DITHER_MODE_ON:
+ doDither=true;
+ break;
+ case DITHER_MODE_OFF:
+ doDither=false;
+ break;
+ }
+ return doDither?ditherBits:0.0f;
+ }
+}
diff --git a/songdbj/org/tritonus/share/sampled/FloatSampleTools.java b/songdbj/org/tritonus/share/sampled/FloatSampleTools.java
new file mode 100644
index 0000000000..76913ba39e
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/FloatSampleTools.java
@@ -0,0 +1,696 @@
+/*
+ * FloatSampleTools.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000,2004 by Florian Bomers
+ *
+ * 8-bit data can be unsigned or signed. All other data is only
+ * supported in signed encoding.
+ *
+ * @see FloatSampleBuffer
+ * @author Florian Bomers
+ */
+
+public class FloatSampleTools {
+
+ /** default number of bits to be dithered: 0.7f */
+ public static final float DEFAULT_DITHER_BITS = 0.7f;
+
+ private static Random random = null;
+
+ // sample width (must be in order !)
+ static final int F_8=1;
+ static final int F_16=2;
+ static final int F_24=3;
+ static final int F_32=4;
+ static final int F_SAMPLE_WIDTH_MASK=F_8 | F_16 | F_24 | F_32;
+
+ // format bit-flags
+ static final int F_SIGNED=8;
+ static final int F_BIGENDIAN=16;
+
+ // supported formats
+ static final int CT_8S=F_8 | F_SIGNED;
+ static final int CT_8U=F_8;
+ static final int CT_16SB=F_16 | F_SIGNED | F_BIGENDIAN;
+ static final int CT_16SL=F_16 | F_SIGNED;
+ static final int CT_24SB=F_24 | F_SIGNED | F_BIGENDIAN;
+ static final int CT_24SL=F_24 | F_SIGNED;
+ static final int CT_32SB=F_32 | F_SIGNED | F_BIGENDIAN;
+ static final int CT_32SL=F_32 | F_SIGNED;
+
+ // ////////////////////////////// initialization /////////////////////////////// //
+
+ /** prevent instanciation */
+ private FloatSampleTools() {
+ }
+
+
+ // /////////////////// FORMAT / FORMAT TYPE /////////////////////////////////// //
+
+ /**
+ * only allow "packed" samples -- currently no support for 18, 20, 24_32 bits.
+ * @throws IllegalArgumentException
+ */
+ static void checkSupportedSampleSize(int ssib, int channels, int frameSize) {
+ if ((ssib*channels) != frameSize * 8) {
+ throw new IllegalArgumentException("unsupported sample size: "+ssib
+ +" stored in "+(frameSize/channels)+" bytes.");
+ }
+ }
+
+
+ /**
+ * Get the formatType code from the given format.
+ * @throws IllegalArgumentException
+ */
+ static int getFormatType(AudioFormat format) {
+ boolean signed = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);
+ if (!signed &&
+ !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
+ throw new IllegalArgumentException
+ ("unsupported encoding: only PCM encoding supported.");
+ }
+ if (!signed && format.getSampleSizeInBits() != 8) {
+ throw new IllegalArgumentException
+ ("unsupported encoding: only 8-bit can be unsigned");
+ }
+ checkSupportedSampleSize(format.getSampleSizeInBits(),
+ format.getChannels(),
+ format.getFrameSize());
+
+ int formatType = getFormatType(format.getSampleSizeInBits(),
+ signed, format.isBigEndian());
+ return formatType;
+ }
+
+ /**
+ * @throws IllegalArgumentException
+ */
+ static int getFormatType(int ssib, boolean signed, boolean bigEndian) {
+ int bytesPerSample=ssib/8;
+ int res=0;
+ if (ssib==8) {
+ res=F_8;
+ } else if (ssib==16) {
+ res=F_16;
+ } else if (ssib==24) {
+ res=F_24;
+ } else if (ssib==32) {
+ res=F_32;
+ }
+ if (res==0) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer: unsupported sample size of "
+ +ssib+" bits per sample.");
+ }
+ if (!signed && bytesPerSample>1) {
+ throw new IllegalArgumentException
+ ("FloatSampleBuffer: unsigned samples larger than "
+ +"8 bit are not supported");
+ }
+ if (signed) {
+ res|=F_SIGNED;
+ }
+ if (bigEndian && (ssib!=8)) {
+ res|=F_BIGENDIAN;
+ }
+ return res;
+ }
+
+ static int getSampleSize(int formatType) {
+ switch (formatType & F_SAMPLE_WIDTH_MASK) {
+ case F_8: return 1;
+ case F_16: return 2;
+ case F_24: return 3;
+ case F_32: return 4;
+ }
+ return 0;
+ }
+
+ /**
+ * Return a string representation of this format
+ */
+ static String formatType2Str(int formatType) {
+ String res=""+formatType+": ";
+ switch (formatType & F_SAMPLE_WIDTH_MASK) {
+ case F_8:
+ res+="8bit";
+ break;
+ case F_16:
+ res+="16bit";
+ break;
+ case F_24:
+ res+="24bit";
+ break;
+ case F_32:
+ res+="32bit";
+ break;
+ }
+ res+=((formatType & F_SIGNED)==F_SIGNED)?" signed":" unsigned";
+ if ((formatType & F_SAMPLE_WIDTH_MASK)!=F_8) {
+ res+=((formatType & F_BIGENDIAN)==F_BIGENDIAN)?
+ " big endian":" little endian";
+ }
+ return res;
+ }
+
+
+ // /////////////////// BYTE 2 FLOAT /////////////////////////////////// //
+
+ private static final float twoPower7=128.0f;
+ private static final float twoPower15=32768.0f;
+ private static final float twoPower23=8388608.0f;
+ private static final float twoPower31=2147483648.0f;
+
+ private static final float invTwoPower7=1/twoPower7;
+ private static final float invTwoPower15=1/twoPower15;
+ private static final float invTwoPower23=1/twoPower23;
+ private static final float invTwoPower31=1/twoPower31;
+
+
+ /**
+ * Conversion function to convert an interleaved byte array to
+ * a List of interleaved float arrays. The float arrays will contain normalized
+ * samples in the range [-1.0f, +1.0f]. The input array
+ * provides bytes in the format specified in format
.
+ * input[inByteOffset]
to
+ * input[inByteOffset + (frameCount * format.getFrameSize()) - 1]
+ * to floats from
+ * output(n)[outOffset]
to
+ * output(n)[outOffset + frameCount - 1]
+ *
+ * @param input the audio data in an byte array
+ * @param inByteOffset index in input where to start the conversion
+ * @param output list of float[] arrays which receive the converted audio data.
+ * if the list does not contain enough elements, or individual float arrays
+ * are not large enough, they are created.
+ * @param outOffset the start offset in output
+ * @param frameCount number of frames to be converted
+ * @param format the input format. Only packed PCM is allowed
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #byte2floatInterleaved(byte[],int,float[],int,int,AudioFormat)
+ */
+ public static void byte2float(byte[] input, int inByteOffset,
+ Listformat
.
+ * input[inByteOffset]
to
+ * input[inByteOffset + (frameCount * format.getFrameSize()) - 1]
+ * to floats from
+ * output[outOffset]
to
+ * output[outOffset + (frameCount * format.getChannels()) - 1]
+ *
+ * @param input the audio data in an byte array
+ * @param inByteOffset index in input where to start the conversion
+ * @param output the float array that receives the converted audio data
+ * @param outOffset the start offset in output
+ * @param frameCount number of frames to be converted
+ * @param format the input format. Only packed PCM is allowed
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #byte2float(byte[],int,List,int,int,AudioFormat)
+ */
+ public static void byte2floatInterleaved(byte[] input, int inByteOffset,
+ float[] output, int outOffset, int frameCount,
+ AudioFormat format) {
+
+ byte2floatGeneric(input, inByteOffset, format.getFrameSize() / format.getChannels(),
+ output, outOffset, frameCount * format.getChannels(),
+ format);
+ }
+
+
+
+ /**
+ * Generic conversion function to convert a byte array to
+ * a float array.
+ * input[inByteOffset]
to
+ * input[inByteOffset + (sampleCount * (inByteStep - 1)]
+ * to samples from
+ * output[outOffset]
to
+ * output[outOffset+sampleCount-1]
.
+ * format
's channel count is ignored.
+ * inByteOffset
to format.getFrameSize()
.
+ * For converting interleaved input data, multiply sampleCount
+ * by the number of channels and set inByteStep to
+ * format.getFrameSize() / format.getChannels()
.
+ *
+ * @param sampleCount number of samples to be written to output
+ * @param inByteStep how many bytes advance for each output sample in output
.
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #byte2floatInterleaved(byte[],int,float[],int,int,AudioFormat)
+ * @see #byte2float(byte[],int,List,int,int,AudioFormat)
+ */
+ static void byte2floatGeneric(byte[] input, int inByteOffset, int inByteStep,
+ float[] output, int outOffset, int sampleCount,
+ AudioFormat format) {
+ int formatType = getFormatType(format);
+
+ byte2floatGeneric(input, inByteOffset, inByteStep,
+ output, outOffset, sampleCount,
+ formatType);
+ }
+
+
+ /**
+ * Central conversion function from
+ * a byte array to a normalized float array. In order to accomodate
+ * interleaved and non-interleaved
+ * samples, this method takes inByteStep as parameter which
+ * can be used to flexibly convert the data.
+ *
+ * mono->mono: inByteStep=format.getFrameSize()
+ * interleaved_stereo->interleaved_stereo: inByteStep=format.getFrameSize()/2,
+ * sampleCount*2
+ * stereo->2 mono arrays:
+ * ---inByteOffset=0, outOffset=0, inByteStep=format.getFrameSize()
+ * ---inByteOffset=format.getFrameSize()/2, outOffset=1, inByteStep=format.getFrameSize()
+ */
+ static void byte2floatGeneric(byte[] input, int inByteOffset, int inByteStep,
+ float[] output, int outOffset, int sampleCount,
+ int formatType) {
+ //if (TDebug.TraceAudioConverter) {
+ // TDebug.out("FloatSampleTools.byte2floatGeneric, formatType="
+ // +formatType2Str(formatType));
+ //}
+ int endCount = outOffset + sampleCount;
+ int inIndex = inByteOffset;
+ for (int outIndex = outOffset; outIndex < endCount; outIndex++, inIndex+=inByteStep) {
+ // do conversion
+ switch (formatType) {
+ case CT_8S:
+ output[outIndex]=
+ ((float) input[inIndex])*invTwoPower7;
+ break;
+ case CT_8U:
+ output[outIndex]=
+ ((float) ((input[inIndex] & 0xFF)-128))*invTwoPower7;
+ break;
+ case CT_16SB:
+ output[outIndex]=
+ ((float) ((input[inIndex]<<8)
+ | (input[inIndex+1] & 0xFF)))*invTwoPower15;
+ break;
+ case CT_16SL:
+ output[outIndex]=
+ ((float) ((input[inIndex+1]<<8)
+ | (input[inIndex] & 0xFF)))*invTwoPower15;
+ break;
+ case CT_24SB:
+ output[outIndex]=
+ ((float) ((input[inIndex]<<16)
+ | ((input[inIndex+1] & 0xFF)<<8)
+ | (input[inIndex+2] & 0xFF)))*invTwoPower23;
+ break;
+ case CT_24SL:
+ output[outIndex]=
+ ((float) ((input[inIndex+2]<<16)
+ | ((input[inIndex+1] & 0xFF)<<8)
+ | (input[inIndex] & 0xFF)))*invTwoPower23;
+ break;
+ case CT_32SB:
+ output[outIndex]=
+ ((float) ((input[inIndex]<<24)
+ | ((input[inIndex+1] & 0xFF)<<16)
+ | ((input[inIndex+2] & 0xFF)<<8)
+ | (input[inIndex+3] & 0xFF)))*invTwoPower31;
+ break;
+ case CT_32SL:
+ output[outIndex]=
+ ((float) ((input[inIndex+3]<<24)
+ | ((input[inIndex+2] & 0xFF)<<16)
+ | ((input[inIndex+1] & 0xFF)<<8)
+ | (input[inIndex] & 0xFF)))*invTwoPower31;
+ break;
+ default:
+ throw new IllegalArgumentException
+ ("unsupported format="+formatType2Str(formatType));
+ }
+ }
+ }
+
+ // /////////////////// FLOAT 2 BYTE /////////////////////////////////// //
+
+ private static byte quantize8(float sample, float ditherBits) {
+ if (ditherBits!=0) {
+ sample+=random.nextFloat()*ditherBits;
+ }
+ if (sample>=127.0f) {
+ return (byte) 127;
+ } else if (sample<=-128.0f) {
+ return (byte) -128;
+ } else {
+ return (byte) (sample<0?(sample-0.5f):(sample+0.5f));
+ }
+ }
+
+ private static int quantize16(float sample, float ditherBits) {
+ if (ditherBits!=0) {
+ sample+=random.nextFloat()*ditherBits;
+ }
+ if (sample>=32767.0f) {
+ return 32767;
+ } else if (sample<=-32768.0f) {
+ return -32768;
+ } else {
+ return (int) (sample<0?(sample-0.5f):(sample+0.5f));
+ }
+ }
+
+ private static int quantize24(float sample, float ditherBits) {
+ if (ditherBits!=0) {
+ sample+=random.nextFloat()*ditherBits;
+ }
+ if (sample>=8388607.0f) {
+ return 8388607;
+ } else if (sample<=-8388608.0f) {
+ return -8388608;
+ } else {
+ return (int) (sample<0?(sample-0.5f):(sample+0.5f));
+ }
+ }
+
+ private static int quantize32(float sample, float ditherBits) {
+ if (ditherBits!=0) {
+ sample+=random.nextFloat()*ditherBits;
+ }
+ if (sample>=2147483647.0f) {
+ return 2147483647;
+ } else if (sample<=-2147483648.0f) {
+ return -2147483648;
+ } else {
+ return (int) (sample<0?(sample-0.5f):(sample+0.5f));
+ }
+ }
+
+
+ /**
+ * Conversion function to convert a non-interleaved float audio data to
+ * an interleaved byte array. The float arrays contains normalized
+ * samples in the range [-1.0f, +1.0f]. The output array
+ * will receive bytes in the format specified in format
.
+ * Exactly format.getChannels()
channels are converted
+ * regardless of the number of elements in input
. If input
+ * does not provide enough channels, an IllegalArgumentException is thrown.
+ *
+ * which may create infinite loops if the latter is overwritten.
+ * input(n)[inOffset]
to
+ * input(n)[inOffset + frameCount - 1]
+ * to byte values from output[outByteOffset]
to
+ * output[outByteOffset + (frameCount * format.getFrameSize()) - 1]
+ * output
+ * @param frameCount number of frames to be converted.
+ * @param format the output format. Only packed PCM is allowed
+ * @param ditherBits if 0, do not dither. Otherwise the number of bits to be dithered
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #DEFAULT_DITHER_BITS
+ * @see #float2byteInterleaved(float[],int,byte[],int,int,AudioFormat,float)
+ */
+ //public static void float2byte(Listformat
.
+ * input[inOffset]
to
+ * input[inOffset + (frameCount * format.getChannels()) - 1]
+ * to byte values from output[outByteOffset]
to
+ * output[outByteOffset + (frameCount * format.getFrameSize()) - 1]
+ * output
+ * @param frameCount number of frames to be converted.
+ * @param format the output format. Only packed PCM is allowed
+ * @param ditherBits if 0, do not dither. Otherwise the number of bits to be dithered
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #DEFAULT_DITHER_BITS
+ * @see #float2byte(List,int,byte[],int,int,AudioFormat,float)
+ */
+ public static void float2byteInterleaved(float[] input, int inOffset,
+ byte[] output, int outByteOffset,
+ int frameCount,
+ AudioFormat format, float ditherBits) {
+ float2byteGeneric(input, inOffset,
+ output, outByteOffset, format.getFrameSize() / format.getChannels(),
+ frameCount * format.getChannels(),
+ format, ditherBits);
+ }
+
+
+
+ /**
+ * Generic conversion function to convert a float array to
+ * a byte array.
+ * input[inOffset]
to
+ * input[inOffset+sampleCount-1]
+ * to byte values from output[outByteOffset]
to
+ * output[outByteOffset + (sampleCount * (outByteStep - 1)]
.
+ * format
's channel count is ignored.
+ * outByteOffset
to format.getFrameSize()
.
+ * For converting interleaved input data, multiply sampleCount
+ * by the number of channels and set outByteStep to
+ * format.getFrameSize() / format.getChannels()
.
+ *
+ * @param sampleCount number of samples in input to be converted.
+ * @param outByteStep how many bytes advance for each input sample in input
.
+ * @throws IllegalArgumentException if one of the parameters is out of bounds
+ *
+ * @see #float2byteInterleaved(float[],int,byte[],int,int,AudioFormat,float)
+ * @see #float2byte(List,int,byte[],int,int,AudioFormat,float)
+ */
+ static void float2byteGeneric(float[] input, int inOffset,
+ byte[] output, int outByteOffset, int outByteStep,
+ int sampleCount,
+ AudioFormat format, float ditherBits) {
+ int formatType = getFormatType(format);
+
+ float2byteGeneric(input, inOffset,
+ output, outByteOffset, outByteStep,
+ sampleCount,
+ formatType, ditherBits);
+ }
+
+
+ /**
+ * Central conversion function from normalized float array to
+ * a byte array. In order to accomodate interleaved and non-interleaved
+ * samples, this method takes outByteStep as parameter which
+ * can be used to flexibly convert the data.
+ *
+ * mono->mono: outByteStep=format.getFrameSize()
+ * interleaved stereo->interleaved stereo: outByteStep=format.getFrameSize()/2, sampleCount*2
+ * 2 mono arrays->stereo:
+ * ---inOffset=0, outByteOffset=0, outByteStep=format.getFrameSize()
+ * ---inOffset=1, outByteOffset=format.getFrameSize()/2, outByteStep=format.getFrameSize()
+ */
+ static void float2byteGeneric(float[] input, int inOffset,
+ byte[] output, int outByteOffset, int outByteStep,
+ int sampleCount, int formatType, float ditherBits) {
+ //if (TDebug.TraceAudioConverter) {
+ // TDebug.out("FloatSampleBuffer.float2byteGeneric, formatType="
+ // +"formatType2Str(formatType));
+ //}
+
+ if (inOffset < 0
+ || inOffset + sampleCount > input.length
+ || sampleCount < 0) {
+ throw new IllegalArgumentException("invalid input index: "
+ +"input.length="+input.length
+ +" inOffset="+inOffset
+ +" sampleCount="+sampleCount);
+ }
+ if (outByteOffset < 0
+ || outByteOffset + (sampleCount * outByteStep) > output.length
+ || outByteStep < getSampleSize(formatType)) {
+ throw new IllegalArgumentException("invalid output index: "
+ +"output.length="+output.length
+ +" outByteOffset="+outByteOffset
+ +" sampleCount="+sampleCount
+ +" format="+formatType2Str(formatType));
+ }
+
+ if (ditherBits!=0.0f && random==null) {
+ // create the random number generator for dithering
+ random=new Random();
+ }
+ int endSample = inOffset + sampleCount;
+ int iSample;
+ int outIndex = outByteOffset;
+ for (int inIndex = inOffset;
+ inIndex < endSample;
+ inIndex++, outIndex+=outByteStep) {
+ // do conversion
+ switch (formatType) {
+ case CT_8S:
+ output[outIndex]=quantize8(input[inIndex]*twoPower7, ditherBits);
+ break;
+ case CT_8U:
+ output[outIndex]=(byte) (quantize8((input[inIndex]*twoPower7), ditherBits)+128);
+ break;
+ case CT_16SB:
+ iSample=quantize16(input[inIndex]*twoPower15, ditherBits);
+ output[outIndex]=(byte) (iSample >> 8);
+ output[outIndex+1]=(byte) (iSample & 0xFF);
+ break;
+ case CT_16SL:
+ iSample=quantize16(input[inIndex]*twoPower15, ditherBits);
+ output[outIndex+1]=(byte) (iSample >> 8);
+ output[outIndex]=(byte) (iSample & 0xFF);
+ break;
+ case CT_24SB:
+ iSample=quantize24(input[inIndex]*twoPower23, ditherBits);
+ output[outIndex]=(byte) (iSample >> 16);
+ output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF);
+ output[outIndex+2]=(byte) (iSample & 0xFF);
+ break;
+ case CT_24SL:
+ iSample=quantize24(input[inIndex]*twoPower23, ditherBits);
+ output[outIndex+2]=(byte) (iSample >> 16);
+ output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF);
+ output[outIndex]=(byte) (iSample & 0xFF);
+ break;
+ case CT_32SB:
+ iSample=quantize32(input[inIndex]*twoPower31, ditherBits);
+ output[outIndex]=(byte) (iSample >> 24);
+ output[outIndex+1]=(byte) ((iSample >>> 16) & 0xFF);
+ output[outIndex+2]=(byte) ((iSample >>> 8) & 0xFF);
+ output[outIndex+3]=(byte) (iSample & 0xFF);
+ break;
+ case CT_32SL:
+ iSample=quantize32(input[inIndex]*twoPower31, ditherBits);
+ output[outIndex+3]=(byte) (iSample >> 24);
+ output[outIndex+2]=(byte) ((iSample >>> 16) & 0xFF);
+ output[outIndex+1]=(byte) ((iSample >>> 8) & 0xFF);
+ output[outIndex]=(byte) (iSample & 0xFF);
+ break;
+ default:
+ throw new IllegalArgumentException
+ ("unsupported format="+formatType2Str(formatType));
+ }
+ }
+ }
+}
diff --git a/songdbj/org/tritonus/share/sampled/TAudioFormat.java b/songdbj/org/tritonus/share/sampled/TAudioFormat.java
new file mode 100644
index 0000000000..7911d5e005
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/TAudioFormat.java
@@ -0,0 +1,110 @@
+/*
+ * TAudioFormat.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2003 by Matthias Pfisterer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sound.sampled.AudioFormat;
+
+
+
+public class TAudioFormat
+extends AudioFormat
+{
+ private Mapshort
.
+ * byteOffset
in
+ * buffer
to a signed sample of type short
.
+ * byteOffset
in
+ * buffer
to a signed integer sample with 16bit range.
+ * byteOffset
in
+ * buffer
to a signed integer sample with 24bit range.
+ * byteOffset
in
+ * buffer
to a signed 32bit integer sample.
+ * short
to 2 bytes in an array.
+ * sample
is interpreted as signed (as Java does).
+ * int
to 2 bytes in an array.
+ * sample
is interpreted as signed (as Java does).
+ * sample
+ * is in the 16bit range - it will not be clipped.
+ * int
to 3 bytes in an array.
+ * sample
is interpreted as signed (as Java does).
+ * sample
+ * is in the 24bit range - it will not be clipped.
+ * int
to 4 bytes in an array.
+ * sample
is interpreted as signed (as Java does).
+ *
Originally by:
+ * Craig Reese: IDA/Supercomputing Research Center
+ * Joe Campbell: Department of Defense
+ * 29 September 1989
+ */
+ public static byte linear2ulaw(int sample) {
+ int sign, exponent, mantissa, ulawbyte;
+
+ if (sample>32767) sample=32767;
+ else if (sample<-32768) sample=-32768;
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0) sample = -sample; /* get magnitude */
+ if (sample > CLIP) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut1[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+ if (ZEROTRAP)
+ if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */
+ return((byte) ulawbyte);
+ }
+
+ /* u-law to linear conversion table */
+ private static short[] u2l = {
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
+ -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
+ -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
+ -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+ };
+ public static short ulaw2linear(byte ulawbyte) {
+ return u2l[ulawbyte & 0xFF];
+ }
+
+
+
+ /**
+ * Converts a buffer of signed 16bit big endian samples to uLaw.
+ * The uLaw bytes overwrite the original 16 bit values.
+ * The first byte-offset of the uLaw bytes is byteOffset.
+ * It will be written sampleCount/2 bytes.
+ */
+ public static void pcm162ulaw(byte[] buffer, int byteOffset, int sampleCount, boolean bigEndian) {
+ int shortIndex=byteOffset;
+ int ulawIndex=shortIndex;
+ if (bigEndian) {
+ while (sampleCount>0) {
+ buffer[ulawIndex++]=linear2ulaw
+ (bytesToInt16(buffer[shortIndex], buffer[shortIndex+1]));
+ shortIndex++;
+ shortIndex++;
+ sampleCount--;
+ }
+ } else {
+ while (sampleCount>0) {
+ buffer[ulawIndex++]=linear2ulaw
+ (bytesToInt16(buffer[shortIndex+1], buffer[shortIndex]));
+ shortIndex++;
+ shortIndex++;
+ sampleCount--;
+ }
+ }
+ }
+
+ /**
+ * Fills outBuffer with ulaw samples.
+ * reading starts from inBuffer[inByteOffset].
+ * writing starts at outBuffer[outByteOffset].
+ * There will be sampleCount*2 bytes read from inBuffer;
+ * There will be sampleCount bytes written to outBuffer.
+ */
+ public static void pcm162ulaw(byte[] inBuffer, int inByteOffset,
+ byte[] outBuffer, int outByteOffset,
+ int sampleCount, boolean bigEndian) {
+ int shortIndex=inByteOffset;
+ int ulawIndex=outByteOffset;
+ if (bigEndian) {
+ while (sampleCount>0) {
+ outBuffer[ulawIndex++]=linear2ulaw
+ (bytesToInt16(inBuffer[shortIndex], inBuffer[shortIndex+1]));
+ shortIndex++;
+ shortIndex++;
+ sampleCount--;
+ }
+ } else {
+ while (sampleCount>0) {
+ outBuffer[ulawIndex++]=linear2ulaw
+ (bytesToInt16(inBuffer[shortIndex+1], inBuffer[shortIndex]));
+ shortIndex++;
+ shortIndex++;
+ sampleCount--;
+ }
+ }
+ }
+
+ // TODO: either direct 8bit pcm to ulaw, or better conversion from 8bit to 16bit
+ /**
+ * Converts a buffer of 8bit samples to uLaw.
+ * The uLaw bytes overwrite the original 8 bit values.
+ * The first byte-offset of the uLaw bytes is byteOffset.
+ * It will be written sampleCount bytes.
+ */
+ public static void pcm82ulaw(byte[] buffer, int byteOffset, int sampleCount, boolean signed) {
+ sampleCount+=byteOffset;
+ if (signed) {
+ for (int i=byteOffset; i
Originally by:
+ *
+ * Craig Reese: IDA/Supercomputing Research Center
+ * 29 September 1989
+ *
+ * References:
+ *
+ *
+ */
+ /*
+ private static final int exp_lut2[] = {
+ 0,132,396,924,1980,4092,8316,16764
+};
+
+ public static short _ulaw2linear(int ulawbyte) {
+ int sign, exponent, mantissa, sample;
+
+ ulawbyte = ~ulawbyte;
+ sign = (ulawbyte & 0x80);
+ exponent = (ulawbyte >> 4) & 0x07;
+ mantissa = ulawbyte & 0x0F;
+ sample = exp_lut2[exponent] + (mantissa << (exponent + 3));
+ if (sign != 0) sample = -sample;
+ return((short) sample);
+}*/
+
+
+ /* u- to A-law conversions: copied from CCITT G.711 specifications */
+ /*
+ private static byte[] _u2a = {
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, (byte) 128};
+ */
+
+ /* u-law to A-law conversion */
+ /*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ */
+ /*
+ public static byte _ulaw2alaw(byte sample) {
+ sample &= 0xff;
+ return (byte) (((sample & 0x80)!=0) ? (0xD5 ^ (_u2a[(0x7F ^ sample) & 0x7F] - 1)) :
+ (0x55 ^ (_u2a[(0x7F ^ sample) & 0x7F] - 1)));
+}*/
+
+ /* A- to u-law conversions */
+ /*
+ private static byte[] _a2u = {
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+ */
+
+ /*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ */
+ /*
+ public static byte _alaw2ulaw(byte sample) {
+ sample &= 0xff;
+ return (byte) (((sample & 0x80)!=0) ? (0xFF ^ _a2u[(sample ^ 0xD5) & 0x7F]) :
+ (0x7F ^ _a2u[(sample ^ 0x55) & 0x7F]));
+}
+
+ public static void print_a2u() {
+ System.out.println("\tprivate static byte[] a2u = {");
+ for (int i=-128; i<128; i++) {
+ if (((i+128) % 16)==0) {
+ System.out.print("\t\t");
+ }
+ byte b=(byte) i;
+ System.out.print(_alaw2ulaw(b)+", ");
+ if (((i+128) % 16)==15) {
+ System.out.println("");
+ }
+}
+ System.out.println("\t};");
+}
+
+ public static void print_u2a() {
+ System.out.println("\tprivate static byte[] u2a = {");
+ for (int i=-128; i<128; i++) {
+ if (((i+128) % 16)==0) {
+ System.out.print("\t\t");
+ }
+ byte b=(byte) i;
+ System.out.print(_ulaw2alaw(b)+", ");
+ if (((i+128) % 16)==15) {
+ System.out.println("");
+ }
+}
+ System.out.println("\t};");
+}
+ */
+
+}
+
+
+/*** TConversionTool.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/TVolumeUtils.java b/songdbj/org/tritonus/share/sampled/TVolumeUtils.java
new file mode 100644
index 0000000000..0eaf1388da
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/TVolumeUtils.java
@@ -0,0 +1,55 @@
+/*
+ * TVolumeUtils.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999 by Matthias Pfisterer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled;
+
+
+
+public class TVolumeUtils
+{
+ private static final double FACTOR1 = 20.0 / Math.log(10.0);
+ private static final double FACTOR2 = 1 / 20.0;
+
+
+
+ public static double lin2log(double dLinear)
+ {
+ return FACTOR1 * Math.log(dLinear);
+ }
+
+
+
+ public static double log2lin(double dLogarithmic)
+ {
+ return Math.pow(10.0, dLogarithmic * FACTOR2);
+ }
+}
+
+
+
+/*** TVolumeUtils.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java b/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java
new file mode 100644
index 0000000000..83349439eb
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java
@@ -0,0 +1,256 @@
+/*
+ * TAsynchronousFilteredAudioInputStream.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999, 2000 by Matthias Pfisterer
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled.convert;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+
+import org.tritonus.share.TDebug;
+import org.tritonus.share.TCircularBuffer;
+
+
+
+/** Base class for asynchronus converters.
+ This class serves as base class for
+ converters that do not have a fixed
+ ratio between the size of a block of input
+ data and the size of a block of output data.
+ These types of converters therefore need an
+ internal buffer, which is realized in this
+ class.
+
+ @author Matthias Pfisterer
+*/
+public abstract class TAsynchronousFilteredAudioInputStream
+extends TAudioInputStream
+implements TCircularBuffer.Trigger
+{
+ private static final int DEFAULT_BUFFER_SIZE = 327670;
+ private static final int DEFAULT_MIN_AVAILABLE = 4096;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+
+ private TCircularBuffer m_circularBuffer;
+ private int m_nMinAvailable;
+ private byte[] m_abSingleByte;
+
+
+
+ /** Constructor.
+ This constructor uses the default buffer size and the default
+ min available amount.
+
+ @param lLength length of this stream in frames. May be
+ AudioSystem.NOT_SPECIFIED.
+ */
+ public TAsynchronousFilteredAudioInputStream(AudioFormat outputFormat, long lLength)
+ {
+ this(outputFormat, lLength,
+ DEFAULT_BUFFER_SIZE,
+ DEFAULT_MIN_AVAILABLE);
+ }
+
+
+
+ /** Constructor.
+ With this constructor, the buffer size and the minimum
+ available amount can be specified as parameters.
+
+ @param lLength length of this stream in frames. May be
+ AudioSystem.NOT_SPECIFIED.
+
+ @param nBufferSize size of the circular buffer in bytes.
+ */
+ public TAsynchronousFilteredAudioInputStream(
+ AudioFormat outputFormat, long lLength,
+ int nBufferSize,
+ int nMinAvailable)
+ {
+ /* The usage of a ByteArrayInputStream is a hack.
+ * (the infamous "JavaOne hack", because I did it on June
+ * 6th 2000 in San Francisco, only hours before a
+ * JavaOne session where I wanted to show mp3 playback
+ * with Java Sound.) It is necessary because in the FCS
+ * version of the Sun jdk1.3, the constructor of
+ * AudioInputStream throws an exception if its first
+ * argument is null. So we have to pass a dummy non-null
+ * value.
+ */
+ super(new ByteArrayInputStream(EMPTY_BYTE_ARRAY),
+ outputFormat,
+ lLength);
+ if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.
+ *
+ * AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream)
+ * and provide a constructor that calls the protected constructor of this class.
+ *
+ * @author Florian Bomers
+ */
+public abstract class TEncodingFormatConversionProvider
+extends TSimpleFormatConversionProvider
+{
+ protected TEncodingFormatConversionProvider(
+ Collection
+ *
+ * For this, replaceNotSpecified(sourceFormat, targetFormat)
in the base
+ * class TSimpleFormatConversionProvider is used - and accordingly, the frameSize
+ * is recalculated with getFrameSize(...)
if a field with AudioSystem.NOT_SPECIFIED
+ * is replaced. Inheriting classes may wish to override this method if the
+ * default mode of calculating the frame size is not appropriate.
+ */
+ public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
+ if (TDebug.TraceAudioConverter) {
+ TDebug.out(">TEncodingFormatConversionProvider.getTargetFormats(AudioFormat.Encoding, AudioFormat):");
+ TDebug.out("checking if conversion possible");
+ TDebug.out("from: " + sourceFormat);
+ TDebug.out("to: " + targetEncoding);
+ }
+ if (isConversionSupported(targetEncoding, sourceFormat)) {
+ // TODO: check that no duplicates may occur...
+ ArraySetgetTargetFormats(AudioFormat.Encoding, AudioFormat)
+ * which may create infinite loops if the latter is overwritten.
+ * AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream)
.
+ * The latter method should be able to handle the case that all fields are NOT_SPECIFIED
+ * and provide appropriate default values.
+ *
+ * @author Matthias Pfisterer
+ */
+
+// todo:
+// - declare a constant ALL_BUT_SAME_VALUE (==-2) or so that can be used in format lists
+// - consistent implementation of replacing NOT_SPECIFIED when not given in conversion
+
+public abstract class TSimpleFormatConversionProvider
+extends TFormatConversionProvider
+{
+ private CollectiontargetFormat
with the corresponding value in sourceFormat
.
+ * If targetFormat
does not contain any fields with AudioSystem.NOT_SPECIFIED,
+ * it is returned unmodified. The endian-ness and encoding remain the same in all cases.
+ * sourceFormat
and
+ * targetFormat
, it will remain not specified.
+ * getFrameSize(...)
(see below) to set the new frameSize,
+ * if a new AudioFormat instance is created.
+ * sampleSize
or channels
is AudioSystem.NOT_SPECIFIED,
+ * otherwise sampleSize*channels/8
is returned.
+ *
+ * It provides all the transformation of frame sizes.
+ * It does NOT handle different sample rates of original stream and this stream !
+ *
+ * @author Florian Bomers
+ */
+public abstract class TSynchronousFilteredAudioInputStream
+extends TAudioInputStream {
+
+ private AudioInputStream originalStream;
+ private AudioFormat originalFormat;
+ /** 1 if original format's frame size is NOT_SPECIFIED */
+ private int originalFrameSize;
+ /** 1 if original format's frame size is NOT_SPECIFIED */
+ private int newFrameSize;
+
+ /**
+ * The intermediate buffer used during convert actions
+ * (if not convertInPlace is used).
+ * It remains until this audioStream is closed or destroyed
+ * and grows with the time - it always has the size of the
+ * largest intermediate buffer ever needed.
+ */
+ protected byte[] buffer=null;
+
+ /**
+ * For use of the more efficient method convertInPlace.
+ * it will be set to true when (frameSizeFactor==1)
+ */
+ private boolean m_bConvertInPlace = false;
+
+ public TSynchronousFilteredAudioInputStream(AudioInputStream audioInputStream, AudioFormat newFormat) {
+ // the super class will do nothing... we override everything
+ super(audioInputStream, newFormat, audioInputStream.getFrameLength());
+ originalStream=audioInputStream;
+ originalFormat=audioInputStream.getFormat();
+ originalFrameSize=(originalFormat.getFrameSize()<=0) ?
+ 1 : originalFormat.getFrameSize();
+ newFrameSize=(getFormat().getFrameSize()<=0) ?
+ 1 : getFormat().getFrameSize();
+ if (TDebug.TraceAudioConverter) {
+ TDebug.out("TSynchronousFilteredAudioInputStream: original format ="
+ +AudioUtils.format2ShortStr(originalFormat));
+ TDebug.out("TSynchronousFilteredAudioInputStream: converted format="
+ +AudioUtils.format2ShortStr(getFormat()));
+ }
+ //$$fb 2000-07-17: convert in place has to be enabled explicitly with "enableConvertInPlace"
+ //if (getFormat().getFrameSize() == originalFormat.getFrameSize()) {
+ // m_bConvertInPlace = true;
+ //}
+ m_bConvertInPlace = false;
+ }
+
+ protected boolean enableConvertInPlace() {
+ if (newFrameSize >= originalFrameSize) {
+ m_bConvertInPlace = true;
+ }
+ return m_bConvertInPlace;
+ }
+
+
+ /**
+ * Override this method to do the actual conversion.
+ * inBuffer starts always at index 0 (it is an internal buffer)
+ * You should always override this.
+ * inFrameCount is the number of frames in inBuffer. These
+ * frames are of the format originalFormat.
+ * @return the resulting number of frames converted and put into
+ * outBuffer. The return value is in the format of this stream.
+ */
+ protected abstract int convert(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int inFrameCount);
+
+
+
+ /**
+ * Override this method to provide in-place conversion of samples.
+ * To use it, call "enableConvertInPlace()". It will only be used when
+ * input bytes per frame >= output bytes per frame.
+ * This method must always convert frameCount frames, so no return value is necessary.
+ */
+ protected void convertInPlace(byte[] buffer, int byteOffset, int frameCount) {
+ throw new RuntimeException("Illegal call to convertInPlace");
+ }
+
+ public int read()
+ throws IOException {
+ if (newFrameSize != 1) {
+ throw new IOException("frame size must be 1 to read a single byte");
+ }
+ // very ugly, but efficient. Who uses this method anyway ?
+ // TODO: use an instance variable
+ byte[] temp = new byte[1];
+ int result = read(temp);
+ if (result == -1) {
+ return -1;
+ }
+ if (result == 0) {
+ // what in this case ??? Let's hope it never occurs.
+ return -1;
+ }
+ return temp[0] & 0xFF;
+ }
+
+
+
+ private void clearBuffer() {
+ buffer = null;
+ }
+
+ public AudioInputStream getOriginalStream() {
+ return originalStream;
+ }
+
+ public AudioFormat getOriginalFormat() {
+ return originalFormat;
+ }
+
+ /**
+ * Read nLength bytes that will be the converted samples
+ * of the original InputStream.
+ * When nLength is not an integral number of frames,
+ * this method may read less than nLength bytes.
+ */
+ public int read(byte[] abData, int nOffset, int nLength)
+ throws IOException {
+ // number of frames that we have to read from the underlying stream.
+ int nFrameLength = nLength/newFrameSize;
+
+ // number of bytes that we need to read from underlying stream.
+ int originalBytes = nFrameLength * originalFrameSize;
+
+ if (TDebug.TraceAudioConverter) {
+ TDebug.out("> TSynchronousFilteredAIS.read(buffer["+abData.length+"], "
+ +nOffset+" ,"+nLength+" bytes ^="+nFrameLength+" frames)");
+ }
+ int nFramesConverted = 0;
+
+ // set up buffer to read
+ byte readBuffer[];
+ int readOffset;
+ if (m_bConvertInPlace) {
+ readBuffer=abData;
+ readOffset=nOffset;
+ } else {
+ // assert that the buffer fits
+ if (buffer == null || buffer.length < originalBytes) {
+ buffer = new byte[originalBytes];
+ }
+ readBuffer=buffer;
+ readOffset=0;
+ }
+ int nBytesRead = originalStream.read(readBuffer, readOffset, originalBytes);
+ if (nBytesRead == -1) {
+ // end of stream
+ clearBuffer();
+ return -1;
+ }
+ int nFramesRead = nBytesRead / originalFrameSize;
+ if (TDebug.TraceAudioConverter) {
+ TDebug.out("original.read returned "
+ +nBytesRead+" bytes ^="+nFramesRead+" frames");
+ }
+ if (m_bConvertInPlace) {
+ convertInPlace(abData, nOffset, nFramesRead);
+ nFramesConverted=nFramesRead;
+ } else {
+ nFramesConverted = convert(buffer, abData, nOffset, nFramesRead);
+ }
+ if (TDebug.TraceAudioConverter) {
+ TDebug.out("< converted "+nFramesConverted+" frames");
+ }
+ return nFramesConverted*newFrameSize;
+ }
+
+
+ public long skip(long nSkip)
+ throws IOException {
+ // only returns integral frames
+ long skipFrames = nSkip / newFrameSize;
+ long originalSkippedBytes = originalStream.skip(skipFrames*originalFrameSize);
+ long skippedFrames = originalSkippedBytes/originalFrameSize;
+ return skippedFrames * newFrameSize;
+ }
+
+
+ public int available()
+ throws IOException {
+ int origAvailFrames = originalStream.available()/originalFrameSize;
+ return origAvailFrames*newFrameSize;
+ }
+
+
+ public void close()
+ throws IOException {
+ originalStream.close();
+ clearBuffer();
+ }
+
+
+
+ public void mark(int readlimit) {
+ int readLimitFrames=readlimit/newFrameSize;
+ originalStream.mark(readLimitFrames*originalFrameSize);
+ }
+
+
+
+ public void reset()
+ throws IOException {
+ originalStream.reset();
+ }
+
+
+ public boolean markSupported() {
+ return originalStream.markSupported();
+ }
+
+
+ private int getFrameSize() {
+ return getFormat().getFrameSize();
+ }
+
+}
+
+
+/*** TSynchronousFilteredAudioInputStream.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/convert/package.html b/songdbj/org/tritonus/share/sampled/convert/package.html
new file mode 100644
index 0000000000..d0cc35c408
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/convert/package.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ fileType
+ * is in the list of supported file types ! Overriding
+ * classes need not check this.
+ */
+ //$$fb 2000-08-16 changed name, changed documentation. Semantics !
+ protected boolean isAudioFormatSupportedImpl(
+ AudioFormat audioFormat,
+ AudioFileFormat.Type fileType)
+ {
+ if (TDebug.TraceAudioFileWriter)
+ {
+ TDebug.out("> TAudioFileWriter.isAudioFormatSupportedImpl(): format to test: " + audioFormat);
+ TDebug.out("class: "+getClass().getName());
+ }
+ Iterator audioFormats = getSupportedAudioFormats(fileType);
+ while (audioFormats.hasNext())
+ {
+ AudioFormat handledFormat = (AudioFormat) audioFormats.next();
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("matching against format : " + handledFormat); }
+ if (AudioFormats.matches(handledFormat, audioFormat))
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("<...succeeded."); }
+ return true;
+ }
+ }
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
+ return false;
+ }
+
+
+
+ protected abstract AudioOutputStream getAudioOutputStream(
+ AudioFormat audioFormat,
+ long lLengthInBytes,
+ AudioFileFormat.Type fileType,
+ TDataOutputStream dataOutputStream)
+ throws IOException;
+
+ private AudioFormat findConvertableFormat(
+ AudioFormat inputFormat,
+ AudioFileFormat.Type fileType)
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("TAudioFileWriter.findConvertableFormat(): input format: " + inputFormat); }
+ if (!isFileTypeSupported(fileType)) {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< input file type is not supported."); }
+ return null;
+ }
+ AudioFormat.Encoding inputEncoding = inputFormat.getEncoding();
+ if ((inputEncoding.equals(PCM_SIGNED) || inputEncoding.equals(PCM_UNSIGNED))
+ && inputFormat.getSampleSizeInBits() == 8)
+ {
+ AudioFormat outputFormat = convertFormat(inputFormat, true, false);
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
+ if (isAudioFormatSupportedImpl(outputFormat, fileType))
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
+ return outputFormat;
+ }
+ //$$fb 2000-08-16: added trial of other endianness for 8bit. We try harder !
+ outputFormat = convertFormat(inputFormat, false, true);
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
+ if (isAudioFormatSupportedImpl(outputFormat, fileType))
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
+ return outputFormat;
+ }
+ outputFormat = convertFormat(inputFormat, true, true);
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
+ if (isAudioFormatSupportedImpl(outputFormat, fileType))
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
+ return outputFormat;
+ }
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
+ return null;
+ }
+ else if (inputEncoding.equals(PCM_SIGNED) &&
+ (inputFormat.getSampleSizeInBits() == 16 ||
+ inputFormat.getSampleSizeInBits() == 24 ||
+ inputFormat.getSampleSizeInBits() == 32) )
+ {
+ // TODO: possible to allow all sample sized > 8 bit?
+ // $$ fb: don't think that this is necessary. Well, let's talk about that in 5 years :)
+ AudioFormat outputFormat = convertFormat(inputFormat, false, true);
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
+ if (isAudioFormatSupportedImpl(outputFormat, fileType))
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
+ return outputFormat;
+ }
+ else
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
+ return null;
+ }
+ }
+ else
+ {
+ if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
+ return null;
+ }
+ }
+
+ // $$fb 2000-08-16: added convenience method
+ private AudioFormat convertFormat(AudioFormat format, boolean changeSign, boolean changeEndian) {
+ AudioFormat.Encoding enc=PCM_SIGNED;
+ if (format.getEncoding().equals(PCM_UNSIGNED)!=changeSign) {
+ enc=PCM_UNSIGNED;
+ }
+ return new AudioFormat(
+ enc,
+ format.getSampleRate(),
+ format.getSampleSizeInBits(),
+ format.getChannels(),
+ format.getFrameSize(),
+ format.getFrameRate(),
+ format.isBigEndian() ^ changeEndian);
+ }
+
+}
+
+
+
+/*** TAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/file/TAudioOutputStream.java b/songdbj/org/tritonus/share/sampled/file/TAudioOutputStream.java
new file mode 100644
index 0000000000..e54316c0a6
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/file/TAudioOutputStream.java
@@ -0,0 +1,197 @@
+/*
+ * TAudioOutputStream.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 2000 by Matthias Pfisterer
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+|<--- this code is formatted to fit into 80 columns --->|
+*/
+
+package org.tritonus.share.sampled.file;
+
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+
+import org.tritonus.share.TDebug;
+
+
+/**
+ * Base class for classes implementing AudioOutputStream.
+ *
+ * @author Matthias Pfisterer
+ */
+
+public abstract class TAudioOutputStream
+implements AudioOutputStream
+{
+ private AudioFormat m_audioFormat;
+ private long m_lLength; // in bytes
+ private long m_lCalculatedLength;
+ private TDataOutputStream m_dataOutputStream;
+ private boolean m_bDoBackPatching;
+ private boolean m_bHeaderWritten;
+
+
+
+ protected TAudioOutputStream(AudioFormat audioFormat,
+ long lLength,
+ TDataOutputStream dataOutputStream,
+ boolean bDoBackPatching)
+ {
+ m_audioFormat = audioFormat;
+ m_lLength = lLength;
+ m_lCalculatedLength = 0;
+ m_dataOutputStream = dataOutputStream;
+ m_bDoBackPatching = bDoBackPatching;
+ m_bHeaderWritten = false;
+ }
+
+
+
+ public AudioFormat getFormat()
+ {
+ return m_audioFormat;
+ }
+
+
+
+ /** Gives length of the stream.
+ * This value is in bytes. It may be AudioSystem.NOT_SPECIFIED
+ * to express that the length is unknown.
+ */
+ public long getLength()
+ {
+ return m_lLength;
+ }
+
+
+
+ /** Gives number of bytes already written.
+ */
+ // IDEA: rename this to BytesWritten or something like that ?
+ public long getCalculatedLength()
+ {
+ return m_lCalculatedLength;
+ }
+
+ protected TDataOutputStream getDataOutputStream()
+ {
+ return m_dataOutputStream;
+ }
+
+
+ /** Writes audio data to the destination (file or output stream).
+ */
+ // IDEA: use long?
+ public int write(byte[] abData, int nOffset, int nLength)
+ throws IOException
+ {
+ if (TDebug.TraceAudioOutputStream)
+ {
+ TDebug.out("TAudioOutputStream.write(): wanted length: " + nLength);
+ }
+ if (! m_bHeaderWritten)
+ {
+ writeHeader();
+ m_bHeaderWritten = true;
+ }
+ // $$fb added
+ // check that total writes do not exceed specified length
+ long lTotalLength=getLength();
+ if (lTotalLength!=AudioSystem.NOT_SPECIFIED && (m_lCalculatedLength+nLength)>lTotalLength) {
+ if (TDebug.TraceAudioOutputStream) {
+ TDebug.out("TAudioOutputStream.write(): requested more bytes to write than possible.");
+ }
+ nLength=(int) (lTotalLength-m_lCalculatedLength);
+ // sanity
+ if (nLength<0) {
+ nLength=0;
+ }
+ }
+ // TODO: throw an exception if nLength==0 ? (to indicate end of file ?)
+ if (nLength>0) {
+ m_dataOutputStream.write(abData, nOffset, nLength);
+ m_lCalculatedLength += nLength;
+ }
+ if (TDebug.TraceAudioOutputStream)
+ {
+ TDebug.out("TAudioOutputStream.write(): calculated (total) length: " + m_lCalculatedLength+" bytes = "+(m_lCalculatedLength/getFormat().getFrameSize())+" frames");
+ }
+ return nLength;
+ }
+
+
+
+ /** Writes the header of the audio file.
+ */
+ protected abstract void writeHeader()
+ throws IOException;
+
+
+
+ /** Closes the stream.
+ * This does write remaining buffered data to the destination,
+ * backpatch the header, if necessary, and closes the destination.
+ */
+ public void close()
+ throws IOException
+ {
+ if (TDebug.TraceAudioOutputStream)
+ {
+ TDebug.out("TAudioOutputStream.close(): called");
+ }
+ // flush?
+ if (m_bDoBackPatching)
+ {
+ if (TDebug.TraceAudioOutputStream)
+ {
+ TDebug.out("TAudioOutputStream.close(): patching header");
+ }
+ patchHeader();
+ }
+ m_dataOutputStream.close();
+ }
+
+
+
+ protected void patchHeader()
+ throws IOException
+ {
+ TDebug.out("TAudioOutputStream.patchHeader(): called");
+ // DO NOTHING
+ }
+
+
+
+ protected void setLengthFromCalculatedLength()
+ {
+ m_lLength = m_lCalculatedLength;
+ }
+}
+
+
+
+/*** TAudioOutputStream.java ***/
diff --git a/songdbj/org/tritonus/share/sampled/file/TDataOutputStream.java b/songdbj/org/tritonus/share/sampled/file/TDataOutputStream.java
new file mode 100644
index 0000000000..eacc00a2e2
--- /dev/null
+++ b/songdbj/org/tritonus/share/sampled/file/TDataOutputStream.java
@@ -0,0 +1,79 @@
+/*
+ * TDataOutputStream.java
+ *
+ * This file is part of Tritonus: http://www.tritonus.org/
+ */
+
+/*
+ * Copyright (c) 1999 by Florian Bomers