diff options
Diffstat (limited to 'songdbj/de')
35 files changed, 5972 insertions, 0 deletions
diff --git a/songdbj/de/jarnbjo/ogg/BasicStream.java b/songdbj/de/jarnbjo/ogg/BasicStream.java new file mode 100644 index 0000000000..9939524d6c --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/BasicStream.java | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.3 2004/09/21 12:09:45 shred | ||
22 | * *** empty log message *** | ||
23 | * | ||
24 | * Revision 1.2 2004/09/21 06:38:45 shred | ||
25 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
26 | * | ||
27 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
28 | * First Import | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.ogg; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | import java.io.InputStream; | ||
37 | import java.util.Collection; | ||
38 | import java.util.HashMap; | ||
39 | import java.util.LinkedList; | ||
40 | |||
41 | /** | ||
42 | * Implementation of the <code>PhysicalOggStream</code> interface for reading | ||
43 | * an Ogg stream from a URL. This class performs | ||
44 | * no internal caching, and will not read data from the network before | ||
45 | * requested to do so. It is intended to be used in non-realtime applications | ||
46 | * like file download managers or similar. | ||
47 | */ | ||
48 | |||
49 | public class BasicStream implements PhysicalOggStream { | ||
50 | |||
51 | private boolean closed=false; | ||
52 | private InputStream sourceStream; | ||
53 | private Object drainLock=new Object(); | ||
54 | private LinkedList pageCache=new LinkedList(); | ||
55 | private long numberOfSamples=-1; | ||
56 | private int position=0; | ||
57 | |||
58 | private HashMap logicalStreams=new HashMap(); | ||
59 | private OggPage firstPage; | ||
60 | |||
61 | public BasicStream(InputStream sourceStream) throws OggFormatException, IOException { | ||
62 | firstPage=OggPage.create(sourceStream); | ||
63 | position+=firstPage.getTotalLength(); | ||
64 | LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber()); | ||
65 | logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los); | ||
66 | los.checkFormat(firstPage); | ||
67 | } | ||
68 | |||
69 | public Collection getLogicalStreams() { | ||
70 | return logicalStreams.values(); | ||
71 | } | ||
72 | |||
73 | public boolean isOpen() { | ||
74 | return !closed; | ||
75 | } | ||
76 | |||
77 | public void close() throws IOException { | ||
78 | closed=true; | ||
79 | sourceStream.close(); | ||
80 | } | ||
81 | |||
82 | public int getContentLength() { | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | public int getPosition() { | ||
87 | return position; | ||
88 | } | ||
89 | |||
90 | int pageNumber=2; | ||
91 | |||
92 | public OggPage getOggPage(int index) throws IOException { | ||
93 | if(firstPage!=null) { | ||
94 | OggPage tmp=firstPage; | ||
95 | firstPage=null; | ||
96 | return tmp; | ||
97 | } | ||
98 | else { | ||
99 | OggPage page=OggPage.create(sourceStream); | ||
100 | position+=page.getTotalLength(); | ||
101 | return page; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | private LogicalOggStream getLogicalStream(int serialNumber) { | ||
106 | return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); | ||
107 | } | ||
108 | |||
109 | public void setTime(long granulePosition) throws IOException { | ||
110 | throw new UnsupportedOperationException("Method not supported by this class"); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * @return always <code>false</code> | ||
115 | */ | ||
116 | |||
117 | public boolean isSeekable() { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.1 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.ogg; | ||
31 | |||
32 | import java.io.*; | ||
33 | import java.net.*; | ||
34 | import java.util.*; | ||
35 | |||
36 | /** | ||
37 | * Implementation of the <code>PhysicalOggStream</code> interface for reading | ||
38 | * and caching an Ogg stream from a URL. This class reads the data as fast as | ||
39 | * possible from the URL, caches it locally either in memory or on disk, and | ||
40 | * supports seeking within the available data. | ||
41 | */ | ||
42 | |||
43 | public class CachedUrlStream implements PhysicalOggStream { | ||
44 | |||
45 | private boolean closed=false; | ||
46 | private URLConnection source; | ||
47 | private InputStream sourceStream; | ||
48 | private Object drainLock=new Object(); | ||
49 | private RandomAccessFile drain; | ||
50 | private byte[] memoryCache; | ||
51 | private ArrayList pageOffsets=new ArrayList(); | ||
52 | private ArrayList pageLengths=new ArrayList(); | ||
53 | private long numberOfSamples=-1; | ||
54 | private long cacheLength; | ||
55 | |||
56 | private HashMap logicalStreams=new HashMap(); | ||
57 | |||
58 | private LoaderThread loaderThread; | ||
59 | |||
60 | /** | ||
61 | * Creates an instance of this class, using a memory cache. | ||
62 | */ | ||
63 | |||
64 | public CachedUrlStream(URL source) throws OggFormatException, IOException { | ||
65 | this(source, null); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Creates an instance of this class, using the specified file as cache. The | ||
70 | * file is not automatically deleted when this class is disposed. | ||
71 | */ | ||
72 | |||
73 | public CachedUrlStream(URL source, RandomAccessFile drain) throws OggFormatException, IOException { | ||
74 | |||
75 | this.source=source.openConnection(); | ||
76 | |||
77 | if(drain==null) { | ||
78 | int contentLength=this.source.getContentLength(); | ||
79 | if(contentLength==-1) { | ||
80 | throw new IOException("The URLConncetion's content length must be set when operating with a in-memory cache."); | ||
81 | } | ||
82 | memoryCache=new byte[contentLength]; | ||
83 | } | ||
84 | |||
85 | this.drain=drain; | ||
86 | this.sourceStream=this.source.getInputStream(); | ||
87 | |||
88 | loaderThread=new LoaderThread(sourceStream, drain, memoryCache); | ||
89 | new Thread(loaderThread).start(); | ||
90 | |||
91 | while(!loaderThread.isBosDone() || pageOffsets.size()<20) { | ||
92 | System.out.print("pageOffsets.size(): "+pageOffsets.size()+"\r"); | ||
93 | try { | ||
94 | Thread.sleep(200); | ||
95 | } | ||
96 | catch (InterruptedException ex) { | ||
97 | } | ||
98 | } | ||
99 | System.out.println(); | ||
100 | System.out.println("caching "+pageOffsets.size()+"/20 pages\r"); | ||
101 | } | ||
102 | |||
103 | public Collection getLogicalStreams() { | ||
104 | return logicalStreams.values(); | ||
105 | } | ||
106 | |||
107 | public boolean isOpen() { | ||
108 | return !closed; | ||
109 | } | ||
110 | |||
111 | public void close() throws IOException { | ||
112 | closed=true; | ||
113 | sourceStream.close(); | ||
114 | } | ||
115 | |||
116 | public long getCacheLength() { | ||
117 | return cacheLength; | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { | ||
122 | return getNextPage(false); | ||
123 | } | ||
124 | |||
125 | private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { | ||
126 | return OggPage.create(sourceStream, skipData); | ||
127 | } | ||
128 | */ | ||
129 | |||
130 | public OggPage getOggPage(int index) throws IOException { | ||
131 | synchronized(drainLock) { | ||
132 | Long offset=(Long)pageOffsets.get(index); | ||
133 | Long length=(Long)pageLengths.get(index); | ||
134 | if(offset!=null) { | ||
135 | if(drain!=null) { | ||
136 | drain.seek(offset.longValue()); | ||
137 | return OggPage.create(drain); | ||
138 | } | ||
139 | else { | ||
140 | byte[] tmpArray=new byte[length.intValue()]; | ||
141 | System.arraycopy(memoryCache, offset.intValue(), tmpArray, 0, length.intValue()); | ||
142 | return OggPage.create(tmpArray); | ||
143 | } | ||
144 | } | ||
145 | else { | ||
146 | return null; | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | private LogicalOggStream getLogicalStream(int serialNumber) { | ||
152 | return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); | ||
153 | } | ||
154 | |||
155 | public void setTime(long granulePosition) throws IOException { | ||
156 | for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) { | ||
157 | LogicalOggStream los=(LogicalOggStream)iter.next(); | ||
158 | los.setTime(granulePosition); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | public class LoaderThread implements Runnable { | ||
163 | |||
164 | private InputStream source; | ||
165 | private RandomAccessFile drain; | ||
166 | private byte[] memoryCache; | ||
167 | |||
168 | private boolean bosDone=false; | ||
169 | |||
170 | private int pageNumber; | ||
171 | |||
172 | public LoaderThread(InputStream source, RandomAccessFile drain, byte[] memoryCache) { | ||
173 | this.source=source; | ||
174 | this.drain=drain; | ||
175 | this.memoryCache=memoryCache; | ||
176 | } | ||
177 | |||
178 | public void run() { | ||
179 | try { | ||
180 | boolean eos=false; | ||
181 | byte[] buffer=new byte[8192]; | ||
182 | while(!eos) { | ||
183 | OggPage op=OggPage.create(source); | ||
184 | synchronized (drainLock) { | ||
185 | int listSize=pageOffsets.size(); | ||
186 | |||
187 | long pos= | ||
188 | listSize>0? | ||
189 | ((Long)pageOffsets.get(listSize-1)).longValue()+ | ||
190 | ((Long)pageLengths.get(listSize-1)).longValue(): | ||
191 | 0; | ||
192 | |||
193 | byte[] arr1=op.getHeader(); | ||
194 | byte[] arr2=op.getSegmentTable(); | ||
195 | byte[] arr3=op.getData(); | ||
196 | |||
197 | if(drain!=null) { | ||
198 | drain.seek(pos); | ||
199 | drain.write(arr1); | ||
200 | drain.write(arr2); | ||
201 | drain.write(arr3); | ||
202 | } | ||
203 | else { | ||
204 | System.arraycopy(arr1, 0, memoryCache, (int)pos, arr1.length); | ||
205 | System.arraycopy(arr2, 0, memoryCache, (int)pos+arr1.length, arr2.length); | ||
206 | System.arraycopy(arr3, 0, memoryCache, (int)pos+arr1.length+arr2.length, arr3.length); | ||
207 | } | ||
208 | |||
209 | pageOffsets.add(new Long(pos)); | ||
210 | pageLengths.add(new Long(arr1.length+arr2.length+arr3.length)); | ||
211 | } | ||
212 | |||
213 | if(!op.isBos()) { | ||
214 | bosDone=true; | ||
215 | //System.out.println("bosDone=true;"); | ||
216 | } | ||
217 | if(op.isEos()) { | ||
218 | eos=true; | ||
219 | } | ||
220 | |||
221 | LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); | ||
222 | if(los==null) { | ||
223 | los=new LogicalOggStreamImpl(CachedUrlStream.this, op.getStreamSerialNumber()); | ||
224 | logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); | ||
225 | los.checkFormat(op); | ||
226 | } | ||
227 | |||
228 | los.addPageNumberMapping(pageNumber); | ||
229 | los.addGranulePosition(op.getAbsoluteGranulePosition()); | ||
230 | |||
231 | pageNumber++; | ||
232 | cacheLength=op.getAbsoluteGranulePosition(); | ||
233 | //System.out.println("read page: "+pageNumber); | ||
234 | } | ||
235 | } | ||
236 | catch(EndOfOggStreamException e) { | ||
237 | // ok | ||
238 | } | ||
239 | catch(IOException e) { | ||
240 | e.printStackTrace(); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | public boolean isBosDone() { | ||
245 | return bosDone; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | public boolean isSeekable() { | ||
250 | return true; | ||
251 | } | ||
252 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2005/02/09 23:10:47 shred | ||
22 | * Serial UID für jarnbjo | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | package de.jarnbjo.ogg; | ||
33 | |||
34 | import java.io.IOException; | ||
35 | |||
36 | /** | ||
37 | * Exception thrown when reaching the end of an Ogg stream | ||
38 | */ | ||
39 | |||
40 | public class EndOfOggStreamException extends IOException { | ||
41 | private static final long serialVersionUID = 3907210438109444408L; | ||
42 | |||
43 | public EndOfOggStreamException() { | ||
44 | } | ||
45 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.1 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.ogg; | ||
31 | |||
32 | import java.io.*; | ||
33 | import java.util.*; | ||
34 | |||
35 | /** | ||
36 | * Implementation of the <code>PhysicalOggStream</code> interface for accessing | ||
37 | * normal disk files. | ||
38 | */ | ||
39 | |||
40 | public class FileStream implements PhysicalOggStream { | ||
41 | |||
42 | private boolean closed=false; | ||
43 | private RandomAccessFile source; | ||
44 | private long[] pageOffsets; | ||
45 | private long numberOfSamples=-1; | ||
46 | |||
47 | private HashMap logicalStreams=new HashMap(); | ||
48 | |||
49 | /** | ||
50 | * Creates access to the specified file through the <code>PhysicalOggStream</code> interface. | ||
51 | * The specified source file must have been opened for reading. | ||
52 | * | ||
53 | * @param source the file to read from | ||
54 | * | ||
55 | * @throws OggFormatException if the stream format is incorrect | ||
56 | * @throws IOException if some other IO error occurs when reading the file | ||
57 | */ | ||
58 | |||
59 | public FileStream(RandomAccessFile source) throws OggFormatException, IOException { | ||
60 | this.source=source; | ||
61 | |||
62 | ArrayList po=new ArrayList(); | ||
63 | int pageNumber=0; | ||
64 | try { | ||
65 | while(true) { | ||
66 | po.add(new Long(this.source.getFilePointer())); | ||
67 | |||
68 | // skip data if pageNumber>0 | ||
69 | OggPage op=getNextPage(pageNumber>0); | ||
70 | if(op==null) { | ||
71 | break; | ||
72 | } | ||
73 | |||
74 | LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); | ||
75 | if(los==null) { | ||
76 | los=new LogicalOggStreamImpl(this, op.getStreamSerialNumber()); | ||
77 | logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); | ||
78 | } | ||
79 | |||
80 | if(pageNumber==0) { | ||
81 | los.checkFormat(op); | ||
82 | } | ||
83 | |||
84 | los.addPageNumberMapping(pageNumber); | ||
85 | los.addGranulePosition(op.getAbsoluteGranulePosition()); | ||
86 | |||
87 | if(pageNumber>0) { | ||
88 | this.source.seek(this.source.getFilePointer()+op.getTotalLength()); | ||
89 | } | ||
90 | |||
91 | pageNumber++; | ||
92 | } | ||
93 | } | ||
94 | catch(EndOfOggStreamException e) { | ||
95 | // ok | ||
96 | } | ||
97 | catch(IOException e) { | ||
98 | throw e; | ||
99 | } | ||
100 | //System.out.println("pageNumber: "+pageNumber); | ||
101 | this.source.seek(0L); | ||
102 | pageOffsets=new long[po.size()]; | ||
103 | int i=0; | ||
104 | Iterator iter=po.iterator(); | ||
105 | while(iter.hasNext()) { | ||
106 | pageOffsets[i++]=((Long)iter.next()).longValue(); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | public Collection getLogicalStreams() { | ||
111 | return logicalStreams.values(); | ||
112 | } | ||
113 | |||
114 | public boolean isOpen() { | ||
115 | return !closed; | ||
116 | } | ||
117 | |||
118 | public void close() throws IOException { | ||
119 | closed=true; | ||
120 | source.close(); | ||
121 | } | ||
122 | |||
123 | private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { | ||
124 | return getNextPage(false); | ||
125 | } | ||
126 | |||
127 | private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { | ||
128 | return OggPage.create(source, skipData); | ||
129 | } | ||
130 | |||
131 | public OggPage getOggPage(int index) throws IOException { | ||
132 | source.seek(pageOffsets[index]); | ||
133 | return OggPage.create(source); | ||
134 | } | ||
135 | |||
136 | private LogicalOggStream getLogicalStream(int serialNumber) { | ||
137 | return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); | ||
138 | } | ||
139 | |||
140 | public void setTime(long granulePosition) throws IOException { | ||
141 | for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) { | ||
142 | LogicalOggStream los=(LogicalOggStream)iter.next(); | ||
143 | los.setTime(granulePosition); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * @return always <code>true</code> | ||
149 | */ | ||
150 | |||
151 | public boolean isSeekable() { | ||
152 | return true; | ||
153 | } | ||
154 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | package de.jarnbjo.ogg; | ||
33 | |||
34 | import java.io.IOException; | ||
35 | |||
36 | /** | ||
37 | * Interface providing access to a logical Ogg stream as part of a | ||
38 | * physical Ogg stream. | ||
39 | */ | ||
40 | |||
41 | |||
42 | public interface LogicalOggStream { | ||
43 | |||
44 | public static final String FORMAT_UNKNOWN = "application/octet-stream"; | ||
45 | |||
46 | public static final String FORMAT_VORBIS = "audio/x-vorbis"; | ||
47 | public static final String FORMAT_FLAC = "audio/x-flac"; | ||
48 | public static final String FORMAT_THEORA = "video/x-theora"; | ||
49 | |||
50 | /** | ||
51 | * <i>Note:</i> To read from the stream, you must use either | ||
52 | * this method or the method <code>getNextOggPacket</code>. | ||
53 | * Mixing calls to the two methods will cause data corruption. | ||
54 | * | ||
55 | * @return the next Ogg page | ||
56 | * | ||
57 | * @see #getNextOggPacket() | ||
58 | * | ||
59 | * @throws OggFormatException if the ogg stream is corrupted | ||
60 | * @throws IOException if some other IO error occurs | ||
61 | */ | ||
62 | |||
63 | public OggPage getNextOggPage() throws OggFormatException, IOException; | ||
64 | |||
65 | /** | ||
66 | * <i>Note:</i> To read from the stream, you must use either | ||
67 | * this method or the method <code>getNextOggPage</code>. | ||
68 | * Mixing calls to the two methods will cause data corruption. | ||
69 | * | ||
70 | * @return the next packet as a byte array | ||
71 | * | ||
72 | * @see #getNextOggPage() | ||
73 | * | ||
74 | * @throws OggFormatException if the ogg stream is corrupted | ||
75 | * @throws IOException if some other IO error occurs | ||
76 | */ | ||
77 | |||
78 | public byte[] getNextOggPacket() throws OggFormatException, IOException; | ||
79 | |||
80 | /** | ||
81 | * Checks if this stream is open for reading. | ||
82 | * | ||
83 | * @return <code>true</code> if this stream is open for reading, | ||
84 | * <code>false</code> otherwise | ||
85 | */ | ||
86 | |||
87 | public boolean isOpen(); | ||
88 | |||
89 | /** | ||
90 | * Closes this stream. After invoking this method, no further access | ||
91 | * to the streams data is possible. | ||
92 | * | ||
93 | * @throws IOException if an IO error occurs | ||
94 | */ | ||
95 | |||
96 | public void close() throws IOException; | ||
97 | |||
98 | /** | ||
99 | * Sets the stream's position to the beginning of the stream. | ||
100 | * This method does not work if the physical Ogg stream is not | ||
101 | * seekable. | ||
102 | * | ||
103 | * @throws OggFormatException if the ogg stream is corrupted | ||
104 | * @throws IOException if some other IO error occurs | ||
105 | */ | ||
106 | |||
107 | public void reset() throws OggFormatException, IOException; | ||
108 | |||
109 | /** | ||
110 | * This method does not work if the physical Ogg stream is not | ||
111 | * seekable. | ||
112 | * | ||
113 | * @return the granule position of the last page within | ||
114 | * this stream | ||
115 | */ | ||
116 | |||
117 | public long getMaximumGranulePosition(); | ||
118 | |||
119 | /** | ||
120 | * This method is invoked on all logical streams when | ||
121 | * calling the same method on the physical stream. The | ||
122 | * same restrictions as mentioned there apply. | ||
123 | * This method does not work if the physical Ogg stream is not | ||
124 | * seekable. | ||
125 | * | ||
126 | * @param granulePosition | ||
127 | * | ||
128 | * @see PhysicalOggStream#setTime(long) | ||
129 | * | ||
130 | * @throws IOException if an IO error occurs | ||
131 | */ | ||
132 | |||
133 | public void setTime(long granulePosition) throws IOException; | ||
134 | |||
135 | /** | ||
136 | * @return the last parsed granule position of this stream | ||
137 | */ | ||
138 | |||
139 | public long getTime(); | ||
140 | |||
141 | /** | ||
142 | * @return the content type of this stream | ||
143 | * | ||
144 | * @see #FORMAT_UNKNOWN | ||
145 | * @see #FORMAT_VORBIS | ||
146 | * @see #FORMAT_FLAC | ||
147 | * @see #FORMAT_THEORA | ||
148 | */ | ||
149 | |||
150 | public String getFormat(); | ||
151 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/03/31 00:23:04 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:26 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | package de.jarnbjo.ogg; | ||
36 | |||
37 | import java.io.*; | ||
38 | import java.util.*; | ||
39 | |||
40 | public class LogicalOggStreamImpl implements LogicalOggStream { | ||
41 | |||
42 | private PhysicalOggStream source; | ||
43 | private int serialNumber; | ||
44 | |||
45 | private ArrayList pageNumberMapping=new ArrayList(); | ||
46 | private ArrayList granulePositions=new ArrayList(); | ||
47 | |||
48 | private int pageIndex=0; | ||
49 | private OggPage currentPage; | ||
50 | private int currentSegmentIndex; | ||
51 | |||
52 | private boolean open=true; | ||
53 | |||
54 | private String format=FORMAT_UNKNOWN; | ||
55 | |||
56 | public LogicalOggStreamImpl(PhysicalOggStream source, int serialNumber) { | ||
57 | this.source=source; | ||
58 | this.serialNumber=serialNumber; | ||
59 | } | ||
60 | |||
61 | public void addPageNumberMapping(int physicalPageNumber) { | ||
62 | pageNumberMapping.add(new Integer(physicalPageNumber)); | ||
63 | } | ||
64 | |||
65 | public void addGranulePosition(long granulePosition) { | ||
66 | granulePositions.add(new Long(granulePosition)); | ||
67 | } | ||
68 | |||
69 | public synchronized void reset() throws OggFormatException, IOException { | ||
70 | currentPage=null; | ||
71 | currentSegmentIndex=0; | ||
72 | pageIndex=0; | ||
73 | } | ||
74 | |||
75 | public synchronized OggPage getNextOggPage() throws EndOfOggStreamException, OggFormatException, IOException { | ||
76 | if(source.isSeekable()) { | ||
77 | currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); | ||
78 | } | ||
79 | else { | ||
80 | currentPage=source.getOggPage(-1); | ||
81 | } | ||
82 | return currentPage; | ||
83 | } | ||
84 | |||
85 | public synchronized byte[] getNextOggPacket() throws EndOfOggStreamException, OggFormatException, IOException { | ||
86 | ByteArrayOutputStream res=new ByteArrayOutputStream(); | ||
87 | int segmentLength=0; | ||
88 | |||
89 | if(currentPage==null) { | ||
90 | currentPage=getNextOggPage(); | ||
91 | } | ||
92 | |||
93 | do { | ||
94 | if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) { | ||
95 | currentSegmentIndex=0; | ||
96 | |||
97 | if(!currentPage.isEos()) { | ||
98 | if(source.isSeekable() && pageNumberMapping.size()<=pageIndex) { | ||
99 | while(pageNumberMapping.size()<=pageIndex+10) { | ||
100 | try { | ||
101 | Thread.sleep(1000); | ||
102 | } | ||
103 | catch (InterruptedException ex) { | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | currentPage=getNextOggPage(); | ||
108 | |||
109 | if(res.size()==0 && currentPage.isContinued()) { | ||
110 | boolean done=false; | ||
111 | while(!done) { | ||
112 | if(currentPage.getSegmentLengths()[currentSegmentIndex++]!=255) { | ||
113 | done=true; | ||
114 | } | ||
115 | if(currentSegmentIndex>currentPage.getSegmentTable().length) { | ||
116 | currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | else { | ||
122 | throw new EndOfOggStreamException(); | ||
123 | } | ||
124 | } | ||
125 | segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex]; | ||
126 | res.write(currentPage.getData(), currentPage.getSegmentOffsets()[currentSegmentIndex], segmentLength); | ||
127 | currentSegmentIndex++; | ||
128 | } while(segmentLength==255); | ||
129 | |||
130 | return res.toByteArray(); | ||
131 | } | ||
132 | |||
133 | public boolean isOpen() { | ||
134 | return open; | ||
135 | } | ||
136 | |||
137 | public void close() throws IOException { | ||
138 | open=false; | ||
139 | } | ||
140 | |||
141 | public long getMaximumGranulePosition() { | ||
142 | Long mgp=(Long)granulePositions.get(granulePositions.size()-1); | ||
143 | return mgp.longValue(); | ||
144 | } | ||
145 | |||
146 | public synchronized long getTime() { | ||
147 | return currentPage!=null?currentPage.getAbsoluteGranulePosition():-1; | ||
148 | } | ||
149 | |||
150 | public synchronized void setTime(long granulePosition) throws IOException { | ||
151 | |||
152 | int page=0; | ||
153 | for(page=0; page<granulePositions.size(); page++) { | ||
154 | Long gp=(Long)granulePositions.get(page); | ||
155 | if(gp.longValue()>granulePosition) { | ||
156 | break; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pageIndex=page; | ||
161 | currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); | ||
162 | currentSegmentIndex=0; | ||
163 | int segmentLength=0; | ||
164 | do { | ||
165 | if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) { | ||
166 | currentSegmentIndex=0; | ||
167 | if(pageIndex>=pageNumberMapping.size()) { | ||
168 | throw new EndOfOggStreamException(); | ||
169 | } | ||
170 | currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); | ||
171 | } | ||
172 | segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex]; | ||
173 | currentSegmentIndex++; | ||
174 | } while(segmentLength==255); | ||
175 | } | ||
176 | |||
177 | public void checkFormat(OggPage page) { | ||
178 | byte[] data=page.getData(); | ||
179 | |||
180 | if(data.length>=7 && | ||
181 | data[1]==0x76 && | ||
182 | data[2]==0x6f && | ||
183 | data[3]==0x72 && | ||
184 | data[4]==0x62 && | ||
185 | data[5]==0x69 && | ||
186 | data[6]==0x73) { | ||
187 | |||
188 | format=FORMAT_VORBIS; | ||
189 | } | ||
190 | else if(data.length>=7 && | ||
191 | data[1]==0x74 && | ||
192 | data[2]==0x68 && | ||
193 | data[3]==0x65 && | ||
194 | data[4]==0x6f && | ||
195 | data[5]==0x72 && | ||
196 | data[6]==0x61) { | ||
197 | |||
198 | format=FORMAT_THEORA; | ||
199 | } | ||
200 | else if (data.length==4 && | ||
201 | data[0]==0x66 && | ||
202 | data[1]==0x4c && | ||
203 | data[2]==0x61 && | ||
204 | data[3]==0x43) { | ||
205 | |||
206 | format=FORMAT_FLAC; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | public String getFormat() { | ||
211 | return format; | ||
212 | } | ||
213 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/ogg/OggFormatException.java b/songdbj/de/jarnbjo/ogg/OggFormatException.java new file mode 100644 index 0000000000..a6b2466b92 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/OggFormatException.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2005/02/09 23:10:47 shred | ||
22 | * Serial UID für jarnbjo | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | package de.jarnbjo.ogg; | ||
33 | |||
34 | import java.io.IOException; | ||
35 | |||
36 | /** | ||
37 | * Exception thrown when trying to read a corrupted Ogg stream. | ||
38 | */ | ||
39 | |||
40 | public class OggFormatException extends IOException { | ||
41 | private static final long serialVersionUID = 3544953238333175349L; | ||
42 | |||
43 | public OggFormatException() { | ||
44 | super(); | ||
45 | } | ||
46 | |||
47 | public OggFormatException(String message) { | ||
48 | super(message); | ||
49 | } | ||
50 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/ogg/OggPage.java b/songdbj/de/jarnbjo/ogg/OggPage.java new file mode 100644 index 0000000000..cc965cc7a9 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/OggPage.java | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/31 00:23:04 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | package de.jarnbjo.ogg; | ||
36 | |||
37 | import java.io.*; | ||
38 | |||
39 | import de.jarnbjo.util.io.*; | ||
40 | |||
41 | /** | ||
42 | * <p>An instance of this class represents an ogg page read from an ogg file | ||
43 | * or network stream. It has no public constructor, but instances can be | ||
44 | * created by the <code>create</code> methods, supplying a JMF stream or | ||
45 | * a <code>RandomAccessFile</code> | ||
46 | * which is positioned at the beginning of an Ogg page.</p> | ||
47 | * | ||
48 | * <p>Furtheron, the class provides methods for accessing the raw page data, | ||
49 | * as well as data attributes like segmenting information, sequence number, | ||
50 | * stream serial number, chechsum and wether this page is the beginning or | ||
51 | * end of a logical bitstream (BOS, EOS) and if the page data starts with a | ||
52 | * continued packet or a fresh data packet.</p> | ||
53 | */ | ||
54 | |||
55 | public class OggPage { | ||
56 | |||
57 | private int version; | ||
58 | private boolean continued, bos, eos; | ||
59 | private long absoluteGranulePosition; | ||
60 | private int streamSerialNumber, pageSequenceNumber, pageCheckSum; | ||
61 | private int[] segmentOffsets; | ||
62 | private int[] segmentLengths; | ||
63 | private int totalLength; | ||
64 | private byte[] header, segmentTable, data; | ||
65 | |||
66 | protected OggPage() { | ||
67 | } | ||
68 | |||
69 | private OggPage( | ||
70 | int version, | ||
71 | boolean continued, | ||
72 | boolean bos, | ||
73 | boolean eos, | ||
74 | long absoluteGranulePosition, | ||
75 | int streamSerialNumber, | ||
76 | int pageSequenceNumber, | ||
77 | int pageCheckSum, | ||
78 | int[] segmentOffsets, | ||
79 | int[] segmentLengths, | ||
80 | int totalLength, | ||
81 | byte[] header, | ||
82 | byte[] segmentTable, | ||
83 | byte[] data) { | ||
84 | |||
85 | this.version=version; | ||
86 | this.continued=continued; | ||
87 | this.bos=bos; | ||
88 | this.eos=eos; | ||
89 | this.absoluteGranulePosition=absoluteGranulePosition; | ||
90 | this.streamSerialNumber=streamSerialNumber; | ||
91 | this.pageSequenceNumber=pageSequenceNumber; | ||
92 | this.pageCheckSum=pageCheckSum; | ||
93 | this.segmentOffsets=segmentOffsets; | ||
94 | this.segmentLengths=segmentLengths; | ||
95 | this.totalLength=totalLength; | ||
96 | this.header=header; | ||
97 | this.segmentTable=segmentTable; | ||
98 | this.data=data; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * this method equals to create(RandomAccessFile source, false) | ||
103 | * | ||
104 | * @see #create(RandomAccessFile, boolean) | ||
105 | */ | ||
106 | |||
107 | public static OggPage create(RandomAccessFile source) throws IOException, EndOfOggStreamException, OggFormatException { | ||
108 | return create(source, false); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * This method is called to read data from the current position in the | ||
113 | * specified RandomAccessFile and create a new OggPage instance based on the data | ||
114 | * read. If the parameter <code>skipData</code> is set to <code>true</code>, | ||
115 | * the actual page segments (page data) is skipped and not read into | ||
116 | * memory. This mode is useful when scanning through an ogg file to build | ||
117 | * a seek table. | ||
118 | * | ||
119 | * @param source the source from which the ogg page is generated | ||
120 | * @param skipData if set to <code>true</code>, the actual page data is not read into memory | ||
121 | * @return an ogg page created by reading data from the specified source, starting at the current position | ||
122 | * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page | ||
123 | * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source | ||
124 | * @throws IOException if some other I/O error is detected when reading from the source | ||
125 | * | ||
126 | * @see #create(RandomAccessFile) | ||
127 | */ | ||
128 | |||
129 | public static OggPage create(RandomAccessFile source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { | ||
130 | return create((Object)source, skipData); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * this method equals to create(InputStream source, false) | ||
135 | * | ||
136 | * @see #create(InputStream, boolean) | ||
137 | */ | ||
138 | |||
139 | public static OggPage create(InputStream source) throws IOException, EndOfOggStreamException, OggFormatException { | ||
140 | return create(source, false); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * This method is called to read data from the current position in the | ||
145 | * specified InpuStream and create a new OggPage instance based on the data | ||
146 | * read. If the parameter <code>skipData</code> is set to <code>true</code>, | ||
147 | * the actual page segments (page data) is skipped and not read into | ||
148 | * memory. This mode is useful when scanning through an ogg file to build | ||
149 | * a seek table. | ||
150 | * | ||
151 | * @param source the source from which the ogg page is generated | ||
152 | * @param skipData if set to <code>true</code>, the actual page data is not read into memory | ||
153 | * @return an ogg page created by reading data from the specified source, starting at the current position | ||
154 | * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page | ||
155 | * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source | ||
156 | * @throws IOException if some other I/O error is detected when reading from the source | ||
157 | * | ||
158 | * @see #create(InputStream) | ||
159 | */ | ||
160 | |||
161 | public static OggPage create(InputStream source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { | ||
162 | return create((Object)source, skipData); | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * this method equals to create(byte[] source, false) | ||
167 | * | ||
168 | * @see #create(byte[], boolean) | ||
169 | */ | ||
170 | |||
171 | public static OggPage create(byte[] source) throws IOException, EndOfOggStreamException, OggFormatException { | ||
172 | return create(source, false); | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * This method is called to | ||
177 | * create a new OggPage instance based on the specified byte array. | ||
178 | * | ||
179 | * @param source the source from which the ogg page is generated | ||
180 | * @param skipData if set to <code>true</code>, the actual page data is not read into memory | ||
181 | * @return an ogg page created by reading data from the specified source, starting at the current position | ||
182 | * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page | ||
183 | * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source | ||
184 | * @throws IOException if some other I/O error is detected when reading from the source | ||
185 | * | ||
186 | * @see #create(byte[]) | ||
187 | */ | ||
188 | |||
189 | public static OggPage create(byte[] source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { | ||
190 | return create((Object)source, skipData); | ||
191 | } | ||
192 | |||
193 | private static OggPage create(Object source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { | ||
194 | |||
195 | try { | ||
196 | int sourceOffset=27; | ||
197 | |||
198 | byte[] header=new byte[27]; | ||
199 | if(source instanceof RandomAccessFile) { | ||
200 | RandomAccessFile raf=(RandomAccessFile)source; | ||
201 | if(raf.getFilePointer()==raf.length()) { | ||
202 | return null; | ||
203 | } | ||
204 | raf.readFully(header); | ||
205 | } | ||
206 | else if(source instanceof InputStream) { | ||
207 | readFully((InputStream)source, header); | ||
208 | } | ||
209 | else if(source instanceof byte[]) { | ||
210 | System.arraycopy((byte[])source, 0, header, 0, 27); | ||
211 | } | ||
212 | |||
213 | BitInputStream bdSource=new ByteArrayBitInputStream(header); | ||
214 | |||
215 | int capture=bdSource.getInt(32); | ||
216 | |||
217 | if(capture!=0x5367674f) { | ||
218 | //throw new FormatException("Ogg page does not start with 'OggS' (0x4f676753)"); | ||
219 | |||
220 | /* | ||
221 | ** This condition is IMHO an error, but older Ogg files often contain | ||
222 | ** pages with a different capture than OggS. I am not sure how to | ||
223 | ** manage these pages, but the decoder seems to work properly, if | ||
224 | ** the incorrect capture is simply ignored. | ||
225 | */ | ||
226 | |||
227 | String cs=Integer.toHexString(capture); | ||
228 | while(cs.length()<8) { | ||
229 | cs="0"+cs; | ||
230 | } | ||
231 | cs=cs.substring(6, 8)+cs.substring(4, 6)+cs.substring(2, 4)+cs.substring(0, 2); | ||
232 | char c1=(char)(Integer.valueOf(cs.substring(0, 2), 16).intValue()); | ||
233 | char c2=(char)(Integer.valueOf(cs.substring(2, 4), 16).intValue()); | ||
234 | char c3=(char)(Integer.valueOf(cs.substring(4, 6), 16).intValue()); | ||
235 | char c4=(char)(Integer.valueOf(cs.substring(6, 8), 16).intValue()); | ||
236 | System.out.println("Ogg packet header is 0x"+cs+" ("+c1+c2+c3+c4+"), should be 0x4f676753 (OggS)"); | ||
237 | } | ||
238 | |||
239 | int version=bdSource.getInt(8); | ||
240 | byte tmp=(byte)bdSource.getInt(8); | ||
241 | boolean bf1=(tmp&1)!=0; | ||
242 | boolean bos=(tmp&2)!=0; | ||
243 | boolean eos=(tmp&4)!=0; | ||
244 | long absoluteGranulePosition=bdSource.getLong(64); | ||
245 | int streamSerialNumber=bdSource.getInt(32); | ||
246 | int pageSequenceNumber=bdSource.getInt(32); | ||
247 | int pageCheckSum=bdSource.getInt(32); | ||
248 | int pageSegments=bdSource.getInt(8); | ||
249 | |||
250 | //System.out.println("OggPage: "+streamSerialNumber+" / "+absoluteGranulePosition+" / "+pageSequenceNumber); | ||
251 | |||
252 | int[] segmentOffsets=new int[pageSegments]; | ||
253 | int[] segmentLengths=new int[pageSegments]; | ||
254 | int totalLength=0; | ||
255 | |||
256 | byte[] segmentTable=new byte[pageSegments]; | ||
257 | byte[] tmpBuf=new byte[1]; | ||
258 | |||
259 | for(int i=0; i<pageSegments; i++) { | ||
260 | int l=0; | ||
261 | if(source instanceof RandomAccessFile) { | ||
262 | l=((int)((RandomAccessFile)source).readByte()&0xff); | ||
263 | } | ||
264 | else if(source instanceof InputStream) { | ||
265 | l=(int)((InputStream)source).read(); | ||
266 | } | ||
267 | else if(source instanceof byte[]) { | ||
268 | l=(int)((byte[])source)[sourceOffset++]; | ||
269 | l&=255; | ||
270 | } | ||
271 | segmentTable[i]=(byte)l; | ||
272 | segmentLengths[i]=l; | ||
273 | segmentOffsets[i]=totalLength; | ||
274 | totalLength+=l; | ||
275 | } | ||
276 | |||
277 | byte[] data=null; | ||
278 | |||
279 | if(!skipData) { | ||
280 | |||
281 | //System.out.println("createPage: "+absoluteGranulePosition*1000/44100); | ||
282 | |||
283 | data=new byte[totalLength]; | ||
284 | //source.read(data, 0, totalLength); | ||
285 | if(source instanceof RandomAccessFile) { | ||
286 | ((RandomAccessFile)source).readFully(data); | ||
287 | } | ||
288 | else if(source instanceof InputStream) { | ||
289 | readFully((InputStream)source, data); | ||
290 | } | ||
291 | else if(source instanceof byte[]) { | ||
292 | System.arraycopy(source, sourceOffset, data, 0, totalLength); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | return new OggPage(version, bf1, bos, eos, absoluteGranulePosition, streamSerialNumber, pageSequenceNumber, pageCheckSum, segmentOffsets, segmentLengths, totalLength, header, segmentTable, data); | ||
297 | } | ||
298 | catch(EOFException e) { | ||
299 | throw new EndOfOggStreamException(); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | private static void readFully(InputStream source, byte[] buffer) throws IOException { | ||
304 | int total=0; | ||
305 | while(total<buffer.length) { | ||
306 | int read=source.read(buffer, total, buffer.length-total); | ||
307 | if(read==-1) { | ||
308 | throw new EndOfOggStreamException(); | ||
309 | } | ||
310 | total+=read; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * Returns the absolute granule position of the last complete | ||
316 | * packet contained in this Ogg page, or -1 if the page contains a single | ||
317 | * packet, which is not completed on this page. For pages containing Vorbis | ||
318 | * data, this value is the sample index within the Vorbis stream. The Vorbis | ||
319 | * stream does not necessarily start with sample index 0. | ||
320 | * | ||
321 | * @return the absolute granule position of the last packet completed on | ||
322 | * this page | ||
323 | */ | ||
324 | |||
325 | |||
326 | public long getAbsoluteGranulePosition() { | ||
327 | return absoluteGranulePosition; | ||
328 | } | ||
329 | |||
330 | /** | ||
331 | * Returns the stream serial number of this ogg page. | ||
332 | * | ||
333 | * @return this page's serial number | ||
334 | */ | ||
335 | |||
336 | public int getStreamSerialNumber() { | ||
337 | return streamSerialNumber; | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * Return the sequnce number of this ogg page. | ||
342 | * | ||
343 | * @return this page's sequence number | ||
344 | */ | ||
345 | |||
346 | public int getPageSequenceNumber() { | ||
347 | return pageSequenceNumber; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Return the check sum of this ogg page. | ||
352 | * | ||
353 | * @return this page's check sum | ||
354 | */ | ||
355 | |||
356 | public int getPageCheckSum() { | ||
357 | return pageCheckSum; | ||
358 | } | ||
359 | |||
360 | /** | ||
361 | * @return the total number of bytes in the page data | ||
362 | */ | ||
363 | |||
364 | |||
365 | public int getTotalLength() { | ||
366 | if(data!=null) { | ||
367 | return 27+segmentTable.length+data.length; | ||
368 | } | ||
369 | else { | ||
370 | return totalLength; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * @return a ByteBuffer containing the page data | ||
376 | */ | ||
377 | |||
378 | |||
379 | public byte[] getData() { | ||
380 | return data; | ||
381 | } | ||
382 | |||
383 | public byte[] getHeader() { | ||
384 | return header; | ||
385 | } | ||
386 | |||
387 | public byte[] getSegmentTable() { | ||
388 | return segmentTable; | ||
389 | } | ||
390 | |||
391 | public int[] getSegmentOffsets() { | ||
392 | return segmentOffsets; | ||
393 | } | ||
394 | |||
395 | public int[] getSegmentLengths() { | ||
396 | return segmentLengths; | ||
397 | } | ||
398 | |||
399 | /** | ||
400 | * @return <code>true</code> if this page begins with a continued packet | ||
401 | */ | ||
402 | |||
403 | public boolean isContinued() { | ||
404 | return continued; | ||
405 | } | ||
406 | |||
407 | /** | ||
408 | * @return <code>true</code> if this page begins with a fresh packet | ||
409 | */ | ||
410 | |||
411 | public boolean isFresh() { | ||
412 | return !continued; | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * @return <code>true</code> if this page is the beginning of a logical stream | ||
417 | */ | ||
418 | |||
419 | public boolean isBos() { | ||
420 | return bos; | ||
421 | } | ||
422 | |||
423 | /** | ||
424 | * @return <code>true</code> if this page is the end of a logical stream | ||
425 | */ | ||
426 | |||
427 | public boolean isEos() { | ||
428 | return eos; | ||
429 | } | ||
430 | |||
431 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.1 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.1 2003/03/31 00:23:04 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | package de.jarnbjo.ogg; | ||
33 | |||
34 | import java.io.*; | ||
35 | import java.net.*; | ||
36 | import java.util.*; | ||
37 | |||
38 | /** | ||
39 | * Implementation of the <code>PhysicalOggStream</code> interface for reading | ||
40 | * an Ogg stream from a URL. This class performs | ||
41 | * no internal caching, and will not read data from the network before | ||
42 | * requested to do so. It is intended to be used in non-realtime applications | ||
43 | * like file download managers or similar. | ||
44 | */ | ||
45 | |||
46 | public class OnDemandUrlStream implements PhysicalOggStream { | ||
47 | |||
48 | private boolean closed=false; | ||
49 | private URLConnection source; | ||
50 | private InputStream sourceStream; | ||
51 | private Object drainLock=new Object(); | ||
52 | private LinkedList pageCache=new LinkedList(); | ||
53 | private long numberOfSamples=-1; | ||
54 | private int contentLength=0; | ||
55 | private int position=0; | ||
56 | |||
57 | private HashMap logicalStreams=new HashMap(); | ||
58 | private OggPage firstPage; | ||
59 | |||
60 | private static final int PAGECACHE_SIZE = 20; | ||
61 | |||
62 | public OnDemandUrlStream(URL source) throws OggFormatException, IOException { | ||
63 | this.source=source.openConnection(); | ||
64 | this.sourceStream=this.source.getInputStream(); | ||
65 | |||
66 | contentLength=this.source.getContentLength(); | ||
67 | |||
68 | firstPage=OggPage.create(sourceStream); | ||
69 | position+=firstPage.getTotalLength(); | ||
70 | LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber()); | ||
71 | logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los); | ||
72 | los.checkFormat(firstPage); | ||
73 | } | ||
74 | |||
75 | public Collection getLogicalStreams() { | ||
76 | return logicalStreams.values(); | ||
77 | } | ||
78 | |||
79 | public boolean isOpen() { | ||
80 | return !closed; | ||
81 | } | ||
82 | |||
83 | public void close() throws IOException { | ||
84 | closed=true; | ||
85 | sourceStream.close(); | ||
86 | } | ||
87 | |||
88 | public int getContentLength() { | ||
89 | return contentLength; | ||
90 | } | ||
91 | |||
92 | public int getPosition() { | ||
93 | return position; | ||
94 | } | ||
95 | |||
96 | int pageNumber=2; | ||
97 | |||
98 | public OggPage getOggPage(int index) throws IOException { | ||
99 | if(firstPage!=null) { | ||
100 | OggPage tmp=firstPage; | ||
101 | firstPage=null; | ||
102 | return tmp; | ||
103 | } | ||
104 | else { | ||
105 | OggPage page=OggPage.create(sourceStream); | ||
106 | position+=page.getTotalLength(); | ||
107 | return page; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | private LogicalOggStream getLogicalStream(int serialNumber) { | ||
112 | return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); | ||
113 | } | ||
114 | |||
115 | public void setTime(long granulePosition) throws IOException { | ||
116 | throw new UnsupportedOperationException("Method not supported by this class"); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * @return always <code>false</code> | ||
121 | */ | ||
122 | |||
123 | public boolean isSeekable() { | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/31 00:23:04 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | package de.jarnbjo.ogg; | ||
36 | |||
37 | import java.io.IOException; | ||
38 | import java.util.Collection; | ||
39 | |||
40 | /** | ||
41 | * Interface providing access to a physical Ogg stream. Typically this is | ||
42 | * a file. | ||
43 | */ | ||
44 | |||
45 | public interface PhysicalOggStream { | ||
46 | |||
47 | /** | ||
48 | * Returns a collection of objects implementing <code>LogicalOggStream</code> | ||
49 | * for accessing the separate logical streams within this physical Ogg stream. | ||
50 | * | ||
51 | * @return a collection of objects implementing <code>LogicalOggStream</code> | ||
52 | * which are representing the logical streams contained within this | ||
53 | * physical stream | ||
54 | * | ||
55 | * @see LogicalOggStream | ||
56 | */ | ||
57 | |||
58 | public Collection getLogicalStreams(); | ||
59 | |||
60 | /** | ||
61 | * Return the Ogg page with the absolute index <code>index</code>, | ||
62 | * independent from the logical structure of this stream or if the | ||
63 | * index parameter is -1, the next Ogg page is returned. | ||
64 | * This method should only be used by implementations of <code>LogicalOggStream</code> | ||
65 | * to access the raw pages. | ||
66 | * | ||
67 | * @param index the absolute index starting from 0 at the beginning of | ||
68 | * the file or stream or -1 to get the next page in a non-seekable | ||
69 | * stream | ||
70 | * | ||
71 | * @return the Ogg page with the physical absolute index <code>index</code> | ||
72 | * | ||
73 | * @throws OggFormatException if the ogg stream is corrupted | ||
74 | * @throws IOException if some other IO error occurs | ||
75 | */ | ||
76 | |||
77 | public OggPage getOggPage(int index) throws OggFormatException, IOException; | ||
78 | |||
79 | /** | ||
80 | * Checks if this stream is open for reading. | ||
81 | * | ||
82 | * @return <code>true</code> if this stream is open for reading, | ||
83 | * <code>false</code> otherwise | ||
84 | */ | ||
85 | |||
86 | public boolean isOpen(); | ||
87 | |||
88 | /** | ||
89 | * Closes this stream. After invoking this method, no further access | ||
90 | * to the streams data is possible. | ||
91 | * | ||
92 | * @throws IOException | ||
93 | */ | ||
94 | |||
95 | public void close() throws IOException; | ||
96 | |||
97 | /** | ||
98 | * Sets this stream's (and its logical stream's) position to the granule | ||
99 | * position. The next packet read from any logical stream will be the | ||
100 | * first packet beginning on the first page with a granule position higher | ||
101 | * than the argument.<br><br> | ||
102 | * | ||
103 | * At the moment, this method only works correctly for Ogg files with | ||
104 | * a single logical Vorbis stream, and due to the different interpretations | ||
105 | * of the granule position, depending on mixed content, this method will | ||
106 | * never be able to work for mixed streams. Chained and interleaved streams are | ||
107 | * also not yet supported. Actually, this method is only a hack to support | ||
108 | * seeking from JMF, but may of course be abused otherwise too :) | ||
109 | * | ||
110 | * @param granulePosition | ||
111 | * | ||
112 | * @throws OggFormatException if the ogg stream is corrupted | ||
113 | * @throws IOException if some other IO error occurs | ||
114 | */ | ||
115 | |||
116 | public void setTime(long granulePosition) throws OggFormatException, IOException; | ||
117 | |||
118 | /** | ||
119 | * @return <code>true</code> if the stream is seekable, <code>false</code> | ||
120 | * otherwise | ||
121 | */ | ||
122 | |||
123 | public boolean isSeekable(); | ||
124 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.1 2003/04/10 19:48:22 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | package de.jarnbjo.ogg; | ||
30 | |||
31 | import java.io.*; | ||
32 | import java.net.*; | ||
33 | import java.util.*; | ||
34 | |||
35 | /** | ||
36 | * Implementation of the <code>PhysicalOggStream</code> interface for reading | ||
37 | * an Ogg stream from a URL. This class performs only the necessary caching | ||
38 | * to provide continous playback. Seeking within the stream is not supported. | ||
39 | */ | ||
40 | |||
41 | public class UncachedUrlStream implements PhysicalOggStream { | ||
42 | |||
43 | private boolean closed=false; | ||
44 | private URLConnection source; | ||
45 | private InputStream sourceStream; | ||
46 | private Object drainLock=new Object(); | ||
47 | private LinkedList pageCache=new LinkedList(); | ||
48 | private long numberOfSamples=-1; | ||
49 | |||
50 | private HashMap logicalStreams=new HashMap(); | ||
51 | |||
52 | private LoaderThread loaderThread; | ||
53 | |||
54 | private static final int PAGECACHE_SIZE = 10; | ||
55 | |||
56 | /** Creates an instance of the <code>PhysicalOggStream</code> interface | ||
57 | * suitable for reading an Ogg stream from a URL. | ||
58 | */ | ||
59 | |||
60 | public UncachedUrlStream(URL source) throws OggFormatException, IOException { | ||
61 | |||
62 | this.source=source.openConnection(); | ||
63 | this.sourceStream=this.source.getInputStream(); | ||
64 | |||
65 | loaderThread=new LoaderThread(sourceStream, pageCache); | ||
66 | new Thread(loaderThread).start(); | ||
67 | |||
68 | while(!loaderThread.isBosDone() || pageCache.size()<PAGECACHE_SIZE) { | ||
69 | try { | ||
70 | Thread.sleep(200); | ||
71 | } | ||
72 | catch (InterruptedException ex) { | ||
73 | } | ||
74 | //System.out.print("caching "+pageCache.size()+"/"+PAGECACHE_SIZE+" pages\r"); | ||
75 | } | ||
76 | //System.out.println(); | ||
77 | } | ||
78 | |||
79 | public Collection getLogicalStreams() { | ||
80 | return logicalStreams.values(); | ||
81 | } | ||
82 | |||
83 | public boolean isOpen() { | ||
84 | return !closed; | ||
85 | } | ||
86 | |||
87 | public void close() throws IOException { | ||
88 | closed=true; | ||
89 | sourceStream.close(); | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | public long getCacheLength() { | ||
94 | return cacheLength; | ||
95 | } | ||
96 | */ | ||
97 | |||
98 | /* | ||
99 | private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { | ||
100 | return getNextPage(false); | ||
101 | } | ||
102 | |||
103 | private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { | ||
104 | return OggPage.create(sourceStream, skipData); | ||
105 | } | ||
106 | */ | ||
107 | |||
108 | public OggPage getOggPage(int index) throws IOException { | ||
109 | while(pageCache.size()==0) { | ||
110 | try { | ||
111 | Thread.sleep(100); | ||
112 | } | ||
113 | catch (InterruptedException ex) { | ||
114 | } | ||
115 | } | ||
116 | synchronized(drainLock) { | ||
117 | //OggPage page=(OggPage)pageCache.getFirst(); | ||
118 | //pageCache.removeFirst(); | ||
119 | //return page; | ||
120 | return (OggPage)pageCache.removeFirst(); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | private LogicalOggStream getLogicalStream(int serialNumber) { | ||
125 | return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); | ||
126 | } | ||
127 | |||
128 | public void setTime(long granulePosition) throws IOException { | ||
129 | throw new UnsupportedOperationException("Method not supported by this class"); | ||
130 | } | ||
131 | |||
132 | public class LoaderThread implements Runnable { | ||
133 | |||
134 | private InputStream source; | ||
135 | private LinkedList pageCache; | ||
136 | private RandomAccessFile drain; | ||
137 | private byte[] memoryCache; | ||
138 | |||
139 | private boolean bosDone=false; | ||
140 | |||
141 | private int pageNumber; | ||
142 | |||
143 | public LoaderThread(InputStream source, LinkedList pageCache) { | ||
144 | this.source=source; | ||
145 | this.pageCache=pageCache; | ||
146 | } | ||
147 | |||
148 | public void run() { | ||
149 | try { | ||
150 | boolean eos=false; | ||
151 | byte[] buffer=new byte[8192]; | ||
152 | while(!eos) { | ||
153 | OggPage op=OggPage.create(source); | ||
154 | synchronized (drainLock) { | ||
155 | pageCache.add(op); | ||
156 | } | ||
157 | |||
158 | if(!op.isBos()) { | ||
159 | bosDone=true; | ||
160 | } | ||
161 | if(op.isEos()) { | ||
162 | eos=true; | ||
163 | } | ||
164 | |||
165 | LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); | ||
166 | if(los==null) { | ||
167 | los=new LogicalOggStreamImpl(UncachedUrlStream.this, op.getStreamSerialNumber()); | ||
168 | logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); | ||
169 | los.checkFormat(op); | ||
170 | } | ||
171 | |||
172 | //los.addPageNumberMapping(pageNumber); | ||
173 | //los.addGranulePosition(op.getAbsoluteGranulePosition()); | ||
174 | |||
175 | pageNumber++; | ||
176 | |||
177 | while(pageCache.size()>PAGECACHE_SIZE) { | ||
178 | try { | ||
179 | Thread.sleep(200); | ||
180 | } | ||
181 | catch (InterruptedException ex) { | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | catch(EndOfOggStreamException e) { | ||
187 | // ok | ||
188 | } | ||
189 | catch(IOException e) { | ||
190 | e.printStackTrace(); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | public boolean isBosDone() { | ||
195 | return bosDone; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * @return always <code>false</code> | ||
201 | */ | ||
202 | |||
203 | public boolean isSeekable() { | ||
204 | return false; | ||
205 | } | ||
206 | |||
207 | } \ 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 @@ | |||
1 | package de.jarnbjo.util.audio; | ||
2 | |||
3 | import java.io.*; | ||
4 | import javax.sound.sampled.*; | ||
5 | |||
6 | public class FadeableAudioInputStream extends AudioInputStream { | ||
7 | |||
8 | private AudioInputStream stream; | ||
9 | private boolean fading=false; | ||
10 | private double phi=0.0; | ||
11 | |||
12 | public FadeableAudioInputStream(AudioInputStream stream) throws IOException { | ||
13 | super(stream, stream.getFormat(), -1L); | ||
14 | } | ||
15 | |||
16 | public void fadeOut() { | ||
17 | fading=true; | ||
18 | phi=0.0; | ||
19 | } | ||
20 | |||
21 | public int read(byte[] b) throws IOException { | ||
22 | return read(b, 0, b.length); | ||
23 | } | ||
24 | |||
25 | public int read(byte[] b, int offset, int length) throws IOException { | ||
26 | int read=super.read(b, offset, length); | ||
27 | |||
28 | //System.out.println("read "+read); | ||
29 | |||
30 | if(fading) { | ||
31 | int j=0, l=0, r=0; | ||
32 | double gain=0.0; | ||
33 | |||
34 | for(int i=offset; i<offset+read; i+=4) { | ||
35 | j=i; | ||
36 | l=((int)b[j++])&0xff; | ||
37 | l|=((int)b[j++])<<8; | ||
38 | r=((int)b[j++])&0xff; | ||
39 | r|=((int)b[j])<<8; | ||
40 | |||
41 | if(phi<Math.PI/2) { | ||
42 | phi+=0.000015; | ||
43 | } | ||
44 | |||
45 | gain=Math.cos(phi); | ||
46 | //System.out.println("gain "+gain); | ||
47 | |||
48 | l=(int)(l*gain); | ||
49 | r=(int)(r*gain); | ||
50 | |||
51 | j=i; | ||
52 | b[j++]=(byte)(l&0xff); | ||
53 | b[j++]=(byte)((l>>8)&0xff); | ||
54 | b[j++]=(byte)(r&0xff); | ||
55 | b[j++]=(byte)((r>>8)&0xff); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | return read; | ||
60 | } | ||
61 | |||
62 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/util/io/BitInputStream.java b/songdbj/de/jarnbjo/util/io/BitInputStream.java new file mode 100644 index 0000000000..89cadb8380 --- /dev/null +++ b/songdbj/de/jarnbjo/util/io/BitInputStream.java | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.5 2003/04/10 19:48:31 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.4 2003/03/16 20:57:06 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.3 2003/03/16 20:56:56 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | * Revision 1.2 2003/03/16 01:11:39 jarnbjo | ||
34 | * no message | ||
35 | * | ||
36 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
37 | * no message | ||
38 | * | ||
39 | */ | ||
40 | |||
41 | package de.jarnbjo.util.io; | ||
42 | |||
43 | import java.io.IOException; | ||
44 | |||
45 | /** | ||
46 | * An interface with methods allowing bit-wise reading from | ||
47 | * an input stream. All methods in this interface are optional | ||
48 | * and an implementation not support a method or a specific state | ||
49 | * (e.g. endian) will throw an UnspportedOperationException if | ||
50 | * such a method is being called. This should be speicified in | ||
51 | * the implementation documentation. | ||
52 | */ | ||
53 | |||
54 | public interface BitInputStream { | ||
55 | |||
56 | /** | ||
57 | * constant for setting this stream's mode to little endian | ||
58 | * | ||
59 | * @see #setEndian(int) | ||
60 | */ | ||
61 | |||
62 | public static final int LITTLE_ENDIAN = 0; | ||
63 | |||
64 | /** | ||
65 | * constant for setting this stream's mode to big endian | ||
66 | * | ||
67 | * @see #setEndian(int) | ||
68 | */ | ||
69 | |||
70 | public static final int BIG_ENDIAN = 1; | ||
71 | |||
72 | /** | ||
73 | * reads one bit (as a boolean) from the input stream | ||
74 | * | ||
75 | * @return <code>true</code> if the next bit is 1, | ||
76 | * <code>false</code> otherwise | ||
77 | * | ||
78 | * @throws IOException if an I/O error occurs | ||
79 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
80 | */ | ||
81 | |||
82 | public boolean getBit() throws IOException; | ||
83 | |||
84 | /** | ||
85 | * reads <code>bits</code> number of bits from the input | ||
86 | * stream | ||
87 | * | ||
88 | * @return the unsigned integer value read from the stream | ||
89 | * | ||
90 | * @throws IOException if an I/O error occurs | ||
91 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
92 | */ | ||
93 | |||
94 | public int getInt(int bits) throws IOException; | ||
95 | |||
96 | /** | ||
97 | * reads <code>bits</code> number of bits from the input | ||
98 | * stream | ||
99 | * | ||
100 | * @return the signed integer value read from the stream | ||
101 | * | ||
102 | * @throws IOException if an I/O error occurs | ||
103 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
104 | */ | ||
105 | |||
106 | public int getSignedInt(int bits) throws IOException; | ||
107 | |||
108 | /** | ||
109 | * reads a huffman codeword based on the <code>root</code> | ||
110 | * parameter and returns the decoded value | ||
111 | * | ||
112 | * @param root the root of the Huffman tree used to decode the codeword | ||
113 | * @return the decoded unsigned integer value read from the stream | ||
114 | * | ||
115 | * @throws IOException if an I/O error occurs | ||
116 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
117 | */ | ||
118 | |||
119 | public int getInt(HuffmanNode root) throws IOException; | ||
120 | |||
121 | /** | ||
122 | * reads an integer encoded as "signed rice" as described in | ||
123 | * the FLAC audio format specification | ||
124 | * | ||
125 | * @param order | ||
126 | * @return the decoded integer value read from the stream | ||
127 | * | ||
128 | * @throws IOException if an I/O error occurs | ||
129 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
130 | */ | ||
131 | |||
132 | public int readSignedRice(int order) throws IOException; | ||
133 | |||
134 | /** | ||
135 | * fills the array from <code>offset</code> with <code>len</code> | ||
136 | * integers encoded as "signed rice" as described in | ||
137 | * the FLAC audio format specification | ||
138 | * | ||
139 | * @param order | ||
140 | * @param buffer | ||
141 | * @param offset | ||
142 | * @param len | ||
143 | * @return the decoded integer value read from the stream | ||
144 | * | ||
145 | * @throws IOException if an I/O error occurs | ||
146 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
147 | */ | ||
148 | |||
149 | public void readSignedRice(int order, int[] buffer, int offset, int len) throws IOException; | ||
150 | |||
151 | /** | ||
152 | * reads <code>bits</code> number of bits from the input | ||
153 | * stream | ||
154 | * | ||
155 | * @return the unsigned long value read from the stream | ||
156 | * | ||
157 | * @throws IOException if an I/O error occurs | ||
158 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
159 | */ | ||
160 | |||
161 | public long getLong(int bits) throws IOException; | ||
162 | |||
163 | /** | ||
164 | * causes the read pointer to be moved to the beginning | ||
165 | * of the next byte, remaining bits in the current byte | ||
166 | * are discarded | ||
167 | * | ||
168 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
169 | */ | ||
170 | |||
171 | public void align(); | ||
172 | |||
173 | /** | ||
174 | * changes the endian mode used when reading bit-wise from | ||
175 | * the stream, changing the mode mid-stream will cause the | ||
176 | * read cursor to move to the beginning of the next byte | ||
177 | * (as if calling the <code>allign</code> method | ||
178 | * | ||
179 | * @see #align() | ||
180 | * | ||
181 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
182 | */ | ||
183 | |||
184 | public void setEndian(int endian); | ||
185 | } \ 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 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/10 19:48:31 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:39 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.1 2003/03/03 21:02:20 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | package de.jarnbjo.util.io; | ||
36 | |||
37 | import java.io.IOException; | ||
38 | |||
39 | /** | ||
40 | * Implementation of the <code>BitInputStream</code> interface, | ||
41 | * using a byte array as data source. | ||
42 | */ | ||
43 | |||
44 | public class ByteArrayBitInputStream implements BitInputStream { | ||
45 | |||
46 | private byte[] source; | ||
47 | private byte currentByte; | ||
48 | |||
49 | private int endian; | ||
50 | |||
51 | private int byteIndex=0; | ||
52 | private int bitIndex=0; | ||
53 | |||
54 | public ByteArrayBitInputStream(byte[] source) { | ||
55 | this(source, LITTLE_ENDIAN); | ||
56 | } | ||
57 | |||
58 | public ByteArrayBitInputStream(byte[] source, int endian) { | ||
59 | this.endian=endian; | ||
60 | this.source=source; | ||
61 | currentByte=source[0]; | ||
62 | bitIndex=(endian==LITTLE_ENDIAN)?0:7; | ||
63 | } | ||
64 | |||
65 | public boolean getBit() throws IOException { | ||
66 | if(endian==LITTLE_ENDIAN) { | ||
67 | if(bitIndex>7) { | ||
68 | bitIndex=0; | ||
69 | currentByte=source[++byteIndex]; | ||
70 | } | ||
71 | return (currentByte&(1<<(bitIndex++)))!=0; | ||
72 | } | ||
73 | else { | ||
74 | if(bitIndex<0) { | ||
75 | bitIndex=7; | ||
76 | currentByte=source[++byteIndex]; | ||
77 | } | ||
78 | return (currentByte&(1<<(bitIndex--)))!=0; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | public int getInt(int bits) throws IOException { | ||
83 | if(bits>32) { | ||
84 | throw new IllegalArgumentException("Argument \"bits\" must be <= 32"); | ||
85 | } | ||
86 | int res=0; | ||
87 | if(endian==LITTLE_ENDIAN) { | ||
88 | for(int i=0; i<bits; i++) { | ||
89 | if(getBit()) { | ||
90 | res|=(1<<i); | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | else { | ||
95 | if(bitIndex<0) { | ||
96 | bitIndex=7; | ||
97 | currentByte=source[++byteIndex]; | ||
98 | } | ||
99 | if(bits<=bitIndex+1) { | ||
100 | int ci=((int)currentByte)&0xff; | ||
101 | int offset=1+bitIndex-bits; | ||
102 | int mask=((1<<bits)-1)<<offset; | ||
103 | res=(ci&mask)>>offset; | ||
104 | bitIndex-=bits; | ||
105 | } | ||
106 | else { | ||
107 | res=(((int)currentByte)&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1); | ||
108 | bits-=bitIndex+1; | ||
109 | currentByte=source[++byteIndex]; | ||
110 | while(bits>=8) { | ||
111 | bits-=8; | ||
112 | res|=(((int)source[byteIndex])&0xff)<<bits; | ||
113 | currentByte=source[++byteIndex]; | ||
114 | } | ||
115 | if(bits>0) { | ||
116 | int ci=((int)source[byteIndex])&0xff; | ||
117 | res|=(ci>>(8-bits))&((1<<bits)-1); | ||
118 | bitIndex=7-bits; | ||
119 | } | ||
120 | else { | ||
121 | currentByte=source[--byteIndex]; | ||
122 | bitIndex=-1; | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return res; | ||
128 | } | ||
129 | |||
130 | public int getSignedInt(int bits) throws IOException { | ||
131 | int raw=getInt(bits); | ||
132 | if(raw>=1<<(bits-1)) { | ||
133 | raw-=1<<bits; | ||
134 | } | ||
135 | return raw; | ||
136 | } | ||
137 | |||
138 | public int getInt(HuffmanNode root) throws IOException { | ||
139 | while(root.value==null) { | ||
140 | if(bitIndex>7) { | ||
141 | bitIndex=0; | ||
142 | currentByte=source[++byteIndex]; | ||
143 | } | ||
144 | root=(currentByte&(1<<(bitIndex++)))!=0?root.o1:root.o0; | ||
145 | } | ||
146 | return root.value.intValue(); | ||
147 | } | ||
148 | |||
149 | public long getLong(int bits) throws IOException { | ||
150 | if(bits>64) { | ||
151 | throw new IllegalArgumentException("Argument \"bits\" must be <= 64"); | ||
152 | } | ||
153 | long res=0; | ||
154 | if(endian==LITTLE_ENDIAN) { | ||
155 | for(int i=0; i<bits; i++) { | ||
156 | if(getBit()) { | ||
157 | res|=(1L<<i); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | else { | ||
162 | for(int i=bits-1; i>=0; i--) { | ||
163 | if(getBit()) { | ||
164 | res|=(1L<<i); | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | return res; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * <p>reads an integer encoded as "signed rice" as described in | ||
173 | * the FLAC audio format specification</p> | ||
174 | * | ||
175 | * <p><b>not supported for little endian</b></p> | ||
176 | * | ||
177 | * @param order | ||
178 | * @return the decoded integer value read from the stream | ||
179 | * | ||
180 | * @throws IOException if an I/O error occurs | ||
181 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
182 | */ | ||
183 | |||
184 | public int readSignedRice(int order) throws IOException { | ||
185 | |||
186 | int msbs=-1, lsbs=0, res=0; | ||
187 | |||
188 | if(endian==LITTLE_ENDIAN) { | ||
189 | // little endian | ||
190 | throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode"); | ||
191 | } | ||
192 | else { | ||
193 | // big endian | ||
194 | |||
195 | byte cb=source[byteIndex]; | ||
196 | do { | ||
197 | msbs++; | ||
198 | if(bitIndex<0) { | ||
199 | bitIndex=7; | ||
200 | byteIndex++; | ||
201 | cb=source[byteIndex]; | ||
202 | } | ||
203 | } while((cb&(1<<bitIndex--))==0); | ||
204 | |||
205 | int bits=order; | ||
206 | |||
207 | if(bitIndex<0) { | ||
208 | bitIndex=7; | ||
209 | byteIndex++; | ||
210 | } | ||
211 | if(bits<=bitIndex+1) { | ||
212 | int ci=((int)source[byteIndex])&0xff; | ||
213 | int offset=1+bitIndex-bits; | ||
214 | int mask=((1<<bits)-1)<<offset; | ||
215 | lsbs=(ci&mask)>>offset; | ||
216 | bitIndex-=bits; | ||
217 | } | ||
218 | else { | ||
219 | lsbs=(((int)source[byteIndex])&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1); | ||
220 | bits-=bitIndex+1; | ||
221 | byteIndex++; | ||
222 | while(bits>=8) { | ||
223 | bits-=8; | ||
224 | lsbs|=(((int)source[byteIndex])&0xff)<<bits; | ||
225 | byteIndex++; | ||
226 | } | ||
227 | if(bits>0) { | ||
228 | int ci=((int)source[byteIndex])&0xff; | ||
229 | lsbs|=(ci>>(8-bits))&((1<<bits)-1); | ||
230 | bitIndex=7-bits; | ||
231 | } | ||
232 | else { | ||
233 | byteIndex--; | ||
234 | bitIndex=-1; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | res=(msbs<<order)|lsbs; | ||
239 | } | ||
240 | |||
241 | return (res&1)==1?-(res>>1)-1:(res>>1); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * <p>fills the array from <code>offset</code> with <code>len</code> | ||
246 | * integers encoded as "signed rice" as described in | ||
247 | * the FLAC audio format specification</p> | ||
248 | * | ||
249 | * <p><b>not supported for little endian</b></p> | ||
250 | * | ||
251 | * @param order | ||
252 | * @param buffer | ||
253 | * @param offset | ||
254 | * @param len | ||
255 | * @return the decoded integer value read from the stream | ||
256 | * | ||
257 | * @throws IOException if an I/O error occurs | ||
258 | * @throws UnsupportedOperationException if the method is not supported by the implementation | ||
259 | */ | ||
260 | |||
261 | public void readSignedRice(int order, int[] buffer, int off, int len) throws IOException { | ||
262 | |||
263 | if(endian==LITTLE_ENDIAN) { | ||
264 | // little endian | ||
265 | throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode"); | ||
266 | } | ||
267 | else { | ||
268 | // big endian | ||
269 | for(int i=off; i<off+len; i++) { | ||
270 | |||
271 | int msbs=-1, lsbs=0; | ||
272 | |||
273 | byte cb=source[byteIndex]; | ||
274 | do { | ||
275 | msbs++; | ||
276 | if(bitIndex<0) { | ||
277 | bitIndex=7; | ||
278 | byteIndex++; | ||
279 | cb=source[byteIndex]; | ||
280 | } | ||
281 | } while((cb&(1<<bitIndex--))==0); | ||
282 | |||
283 | int bits=order; | ||
284 | |||
285 | if(bitIndex<0) { | ||
286 | bitIndex=7; | ||
287 | byteIndex++; | ||
288 | } | ||
289 | if(bits<=bitIndex+1) { | ||
290 | int ci=((int)source[byteIndex])&0xff; | ||
291 | int offset=1+bitIndex-bits; | ||
292 | int mask=((1<<bits)-1)<<offset; | ||
293 | lsbs=(ci&mask)>>offset; | ||
294 | bitIndex-=bits; | ||
295 | } | ||
296 | else { | ||
297 | lsbs=(((int)source[byteIndex])&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1); | ||
298 | bits-=bitIndex+1; | ||
299 | byteIndex++; | ||
300 | while(bits>=8) { | ||
301 | bits-=8; | ||
302 | lsbs|=(((int)source[byteIndex])&0xff)<<bits; | ||
303 | byteIndex++; | ||
304 | } | ||
305 | if(bits>0) { | ||
306 | int ci=((int)source[byteIndex])&0xff; | ||
307 | lsbs|=(ci>>(8-bits))&((1<<bits)-1); | ||
308 | bitIndex=7-bits; | ||
309 | } | ||
310 | else { | ||
311 | byteIndex--; | ||
312 | bitIndex=-1; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | int res=(msbs<<order)|lsbs; | ||
317 | buffer[i]=(res&1)==1?-(res>>1)-1:(res>>1); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | |||
322 | public void align() { | ||
323 | if(endian==BIG_ENDIAN && bitIndex>=0) { | ||
324 | bitIndex=7; | ||
325 | byteIndex++; | ||
326 | } | ||
327 | else if(endian==LITTLE_ENDIAN && bitIndex<=7) { | ||
328 | bitIndex=0; | ||
329 | byteIndex++; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | public void setEndian(int endian) { | ||
334 | if(this.endian==BIG_ENDIAN && endian==LITTLE_ENDIAN) { | ||
335 | bitIndex=0; | ||
336 | byteIndex++; | ||
337 | } | ||
338 | else if(this.endian==LITTLE_ENDIAN && endian==BIG_ENDIAN) { | ||
339 | bitIndex=7; | ||
340 | byteIndex++; | ||
341 | } | ||
342 | this.endian=endian; | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * @return the byte array used as a source for this instance | ||
347 | */ | ||
348 | |||
349 | public byte[] getSource() { | ||
350 | return source; | ||
351 | } | ||
352 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/util/io/HuffmanNode.java b/songdbj/de/jarnbjo/util/io/HuffmanNode.java new file mode 100644 index 0000000000..88600a4ddd --- /dev/null +++ b/songdbj/de/jarnbjo/util/io/HuffmanNode.java | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/04/10 19:48:31 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | package de.jarnbjo.util.io; | ||
30 | |||
31 | import java.io.IOException; | ||
32 | import de.jarnbjo.util.io.BitInputStream; | ||
33 | |||
34 | /** | ||
35 | * Representation of a node in a Huffman tree, used to read | ||
36 | * Huffman compressed codewords from e.g. a Vorbis stream. | ||
37 | */ | ||
38 | |||
39 | final public class HuffmanNode { | ||
40 | |||
41 | private HuffmanNode parent; | ||
42 | private int depth=0; | ||
43 | protected HuffmanNode o0, o1; | ||
44 | protected Integer value; | ||
45 | private boolean full=false; | ||
46 | |||
47 | /** | ||
48 | * creates a new Huffman tree root node | ||
49 | */ | ||
50 | |||
51 | public HuffmanNode() { | ||
52 | this(null); | ||
53 | } | ||
54 | |||
55 | protected HuffmanNode(HuffmanNode parent) { | ||
56 | this.parent=parent; | ||
57 | if(parent!=null) { | ||
58 | depth=parent.getDepth()+1; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | protected HuffmanNode(HuffmanNode parent, int value) { | ||
63 | this(parent); | ||
64 | this.value=new Integer(value); | ||
65 | full=true; | ||
66 | } | ||
67 | |||
68 | protected int read(BitInputStream bis) throws IOException { | ||
69 | HuffmanNode iter=this; | ||
70 | while(iter.value==null) { | ||
71 | iter=bis.getBit()?iter.o1:iter.o0; | ||
72 | } | ||
73 | return iter.value.intValue(); | ||
74 | } | ||
75 | |||
76 | protected HuffmanNode get0() { | ||
77 | return o0==null?set0(new HuffmanNode(this)):o0; | ||
78 | } | ||
79 | |||
80 | protected HuffmanNode get1() { | ||
81 | return o1==null?set1(new HuffmanNode(this)):o1; | ||
82 | } | ||
83 | |||
84 | protected Integer getValue() { | ||
85 | return value; | ||
86 | } | ||
87 | |||
88 | private HuffmanNode getParent() { | ||
89 | return parent; | ||
90 | } | ||
91 | |||
92 | protected int getDepth() { | ||
93 | return depth; | ||
94 | } | ||
95 | |||
96 | private boolean isFull() { | ||
97 | return full?true:(full=o0!=null&&o0.isFull()&&o1!=null&&o1.isFull()); | ||
98 | } | ||
99 | |||
100 | private HuffmanNode set0(HuffmanNode value) { | ||
101 | return o0=value; | ||
102 | } | ||
103 | |||
104 | private HuffmanNode set1(HuffmanNode value) { | ||
105 | return o1=value; | ||
106 | } | ||
107 | |||
108 | private void setValue(Integer value) { | ||
109 | full=true; | ||
110 | this.value=value; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * creates a new tree node at the first free location at the given | ||
115 | * depth, and assigns the value to it | ||
116 | * | ||
117 | * @param depth the tree depth of the new node (codeword length in bits) | ||
118 | * @param value the node's new value | ||
119 | */ | ||
120 | |||
121 | public boolean setNewValue(int depth, int value) { | ||
122 | if(isFull()) { | ||
123 | return false; | ||
124 | } | ||
125 | if(depth==1) { | ||
126 | if(o0==null) { | ||
127 | set0(new HuffmanNode(this, value)); | ||
128 | return true; | ||
129 | } | ||
130 | else if(o1==null) { | ||
131 | set1(new HuffmanNode(this, value)); | ||
132 | return true; | ||
133 | } | ||
134 | else { | ||
135 | return false; | ||
136 | } | ||
137 | } | ||
138 | else { | ||
139 | return get0().setNewValue(depth-1, value)? | ||
140 | true: | ||
141 | get1().setNewValue(depth-1, value); | ||
142 | } | ||
143 | } | ||
144 | } | ||
diff --git a/songdbj/de/jarnbjo/vorbis/AudioPacket.java b/songdbj/de/jarnbjo/vorbis/AudioPacket.java new file mode 100644 index 0000000000..90a54073c1 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/AudioPacket.java | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | import de.jarnbjo.util.io.BitInputStream; | ||
38 | |||
39 | class AudioPacket { | ||
40 | |||
41 | private int modeNumber; | ||
42 | private Mode mode; | ||
43 | private Mapping mapping; | ||
44 | private int n; // block size | ||
45 | private boolean blockFlag, previousWindowFlag, nextWindowFlag; | ||
46 | |||
47 | private int windowCenter, leftWindowStart, leftWindowEnd, leftN, rightWindowStart, rightWindowEnd, rightN; | ||
48 | private float[] window; | ||
49 | private float[][] pcm; | ||
50 | private int[][] pcmInt; | ||
51 | |||
52 | private Floor[] channelFloors; | ||
53 | private boolean[] noResidues; | ||
54 | |||
55 | private final static float[][] windows=new float[8][]; | ||
56 | |||
57 | protected AudioPacket(final VorbisStream vorbis, final BitInputStream source) throws VorbisFormatException, IOException { | ||
58 | |||
59 | final SetupHeader sHeader=vorbis.getSetupHeader(); | ||
60 | final IdentificationHeader iHeader=vorbis.getIdentificationHeader(); | ||
61 | final Mode[] modes=sHeader.getModes(); | ||
62 | final Mapping[] mappings=sHeader.getMappings(); | ||
63 | final Residue[] residues=sHeader.getResidues(); | ||
64 | final int channels=iHeader.getChannels(); | ||
65 | |||
66 | if(source.getInt(1)!=0) { | ||
67 | throw new VorbisFormatException("Packet type mismatch when trying to create an audio packet."); | ||
68 | } | ||
69 | |||
70 | modeNumber=source.getInt(Util.ilog(modes.length-1)); | ||
71 | |||
72 | try { | ||
73 | mode=modes[modeNumber]; | ||
74 | } | ||
75 | catch(ArrayIndexOutOfBoundsException e) { | ||
76 | throw new VorbisFormatException("Reference to invalid mode in audio packet."); | ||
77 | } | ||
78 | |||
79 | mapping=mappings[mode.getMapping()]; | ||
80 | |||
81 | final int[] magnitudes=mapping.getMagnitudes(); | ||
82 | final int[] angles=mapping.getAngles(); | ||
83 | |||
84 | blockFlag=mode.getBlockFlag(); | ||
85 | |||
86 | final int blockSize0=iHeader.getBlockSize0(); | ||
87 | final int blockSize1=iHeader.getBlockSize1(); | ||
88 | |||
89 | n=blockFlag?blockSize1:blockSize0; | ||
90 | |||
91 | if(blockFlag) { | ||
92 | previousWindowFlag=source.getBit(); | ||
93 | nextWindowFlag=source.getBit(); | ||
94 | } | ||
95 | |||
96 | windowCenter=n/2; | ||
97 | |||
98 | if(blockFlag && !previousWindowFlag) { | ||
99 | leftWindowStart=n/4-blockSize0/4; | ||
100 | leftWindowEnd=n/4+blockSize0/4; | ||
101 | leftN=blockSize0/2; | ||
102 | } | ||
103 | else { | ||
104 | leftWindowStart=0; | ||
105 | leftWindowEnd=n/2; | ||
106 | leftN=windowCenter; | ||
107 | } | ||
108 | |||
109 | if(blockFlag && !nextWindowFlag) { | ||
110 | rightWindowStart=n*3/4-blockSize0/4; | ||
111 | rightWindowEnd=n*3/4+blockSize0/4; | ||
112 | rightN=blockSize0/2; | ||
113 | } | ||
114 | else { | ||
115 | rightWindowStart=windowCenter; | ||
116 | rightWindowEnd=n; | ||
117 | rightN=n/2; | ||
118 | } | ||
119 | |||
120 | window=getComputedWindow();//new double[n]; | ||
121 | |||
122 | channelFloors=new Floor[channels]; | ||
123 | noResidues=new boolean[channels]; | ||
124 | |||
125 | pcm=new float[channels][n]; | ||
126 | pcmInt=new int[channels][n]; | ||
127 | |||
128 | boolean allFloorsEmpty=true; | ||
129 | |||
130 | for(int i=0; i<channels; i++) { | ||
131 | int submapNumber=mapping.getMux()[i]; | ||
132 | int floorNumber=mapping.getSubmapFloors()[submapNumber]; | ||
133 | Floor decodedFloor=sHeader.getFloors()[floorNumber].decodeFloor(vorbis, source); | ||
134 | channelFloors[i]=decodedFloor; | ||
135 | noResidues[i]=decodedFloor==null; | ||
136 | if(decodedFloor!=null) { | ||
137 | allFloorsEmpty=false; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | if(allFloorsEmpty) { | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | for(int i=0; i<magnitudes.length; i++) { | ||
146 | if(!noResidues[magnitudes[i]] || | ||
147 | !noResidues[angles[i]]) { | ||
148 | |||
149 | noResidues[magnitudes[i]]=false; | ||
150 | noResidues[angles[i]]=false; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | Residue[] decodedResidues=new Residue[mapping.getSubmaps()]; | ||
155 | |||
156 | for(int i=0; i<mapping.getSubmaps(); i++) { | ||
157 | int ch=0; | ||
158 | boolean[] doNotDecodeFlags=new boolean[channels]; | ||
159 | for(int j=0; j<channels; j++) { | ||
160 | if(mapping.getMux()[j]==i) { | ||
161 | doNotDecodeFlags[ch++]=noResidues[j]; | ||
162 | } | ||
163 | } | ||
164 | int residueNumber=mapping.getSubmapResidues()[i]; | ||
165 | Residue residue=residues[residueNumber]; | ||
166 | |||
167 | residue.decodeResidue(vorbis, source, mode, ch, doNotDecodeFlags, pcm); | ||
168 | } | ||
169 | |||
170 | |||
171 | for(int i=mapping.getCouplingSteps()-1; i>=0; i--) { | ||
172 | double newA=0, newM=0; | ||
173 | final float[] magnitudeVector=pcm[magnitudes[i]]; | ||
174 | final float[] angleVector=pcm[angles[i]]; | ||
175 | for(int j=0; j<magnitudeVector.length; j++) { | ||
176 | float a=angleVector[j]; | ||
177 | float m=magnitudeVector[j]; | ||
178 | if(a>0) { | ||
179 | //magnitudeVector[j]=m; | ||
180 | angleVector[j]=m>0?m-a:m+a; | ||
181 | } | ||
182 | else { | ||
183 | magnitudeVector[j]=m>0?m+a:m-a; | ||
184 | angleVector[j]=m; | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | for(int i=0; i<channels; i++) { | ||
190 | if(channelFloors[i]!=null) { | ||
191 | channelFloors[i].computeFloor(pcm[i]); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // perform an inverse mdct to all channels | ||
196 | |||
197 | for(int i=0; i<channels; i++) { | ||
198 | MdctFloat mdct=blockFlag?iHeader.getMdct1():iHeader.getMdct0(); | ||
199 | mdct.imdct(pcm[i], window, pcmInt[i]); | ||
200 | } | ||
201 | |||
202 | } | ||
203 | |||
204 | private float[] getComputedWindow() { | ||
205 | int ix=(blockFlag?4:0)+(previousWindowFlag?2:0)+(nextWindowFlag?1:0); | ||
206 | float[] w=windows[ix]; | ||
207 | if(w==null) { | ||
208 | w=new float[n]; | ||
209 | |||
210 | for(int i=0;i<leftN;i++){ | ||
211 | float x=(float)((i+.5)/leftN*Math.PI/2.); | ||
212 | x=(float)Math.sin(x); | ||
213 | x*=x; | ||
214 | x*=(float)Math.PI/2.; | ||
215 | x=(float)Math.sin(x); | ||
216 | w[i+leftWindowStart]=x; | ||
217 | } | ||
218 | |||
219 | for(int i=leftWindowEnd; i<rightWindowStart; w[i++]=1.0f); | ||
220 | |||
221 | for(int i=0;i<rightN;i++){ | ||
222 | float x=(float)((rightN-i-.5)/rightN*Math.PI/2.); | ||
223 | x=(float)Math.sin(x); | ||
224 | x*=x; | ||
225 | x*=(float)Math.PI/2.; | ||
226 | x=(float)Math.sin(x); | ||
227 | w[i+rightWindowStart]=x; | ||
228 | } | ||
229 | |||
230 | windows[ix]=w; | ||
231 | } | ||
232 | return w; | ||
233 | } | ||
234 | |||
235 | protected int getNumberOfSamples() { | ||
236 | return rightWindowStart-leftWindowStart; | ||
237 | } | ||
238 | |||
239 | protected int getPcm(final AudioPacket previousPacket, final int[][] buffer) { | ||
240 | int channels=pcm.length; | ||
241 | int val; | ||
242 | |||
243 | // copy left window flank and mix with right window flank from | ||
244 | // the previous audio packet | ||
245 | for(int i=0; i<channels; i++) { | ||
246 | int j1=0, j2=previousPacket.rightWindowStart; | ||
247 | final int[] ppcm=previousPacket.pcmInt[i]; | ||
248 | final int[] tpcm=pcmInt[i]; | ||
249 | final int[] target=buffer[i]; | ||
250 | |||
251 | for(int j=leftWindowStart; j<leftWindowEnd; j++) { | ||
252 | val=ppcm[j2++]+tpcm[j]; | ||
253 | if(val>32767) val=32767; | ||
254 | if(val<-32768) val=-32768; | ||
255 | target[j1++]=val; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | // use System.arraycopy to copy the middle part (if any) | ||
260 | // of the window | ||
261 | if(leftWindowEnd+1<rightWindowStart) { | ||
262 | for(int i=0; i<channels; i++) { | ||
263 | System.arraycopy(pcmInt[i], leftWindowEnd, buffer[i], leftWindowEnd-leftWindowStart, rightWindowStart-leftWindowEnd); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | return rightWindowStart-leftWindowStart; | ||
268 | } | ||
269 | |||
270 | protected void getPcm(final AudioPacket previousPacket, final byte[] buffer) { | ||
271 | int channels=pcm.length; | ||
272 | int val; | ||
273 | |||
274 | // copy left window flank and mix with right window flank from | ||
275 | // the previous audio packet | ||
276 | for(int i=0; i<channels; i++) { | ||
277 | int ix=0, j2=previousPacket.rightWindowStart; | ||
278 | final int[] ppcm=previousPacket.pcmInt[i]; | ||
279 | final int[] tpcm=pcmInt[i]; | ||
280 | for(int j=leftWindowStart; j<leftWindowEnd; j++) { | ||
281 | val=ppcm[j2++]+tpcm[j]; | ||
282 | if(val>32767) val=32767; | ||
283 | if(val<-32768) val=-32768; | ||
284 | buffer[ix+(i*2)+1]=(byte)(val&0xff); | ||
285 | buffer[ix+(i*2)]=(byte)((val>>8)&0xff); | ||
286 | ix+=channels*2; | ||
287 | } | ||
288 | |||
289 | ix=(leftWindowEnd-leftWindowStart)*channels*2; | ||
290 | for(int j=leftWindowEnd; j<rightWindowStart; j++) { | ||
291 | val=tpcm[j]; | ||
292 | if(val>32767) val=32767; | ||
293 | if(val<-32768) val=-32768; | ||
294 | buffer[ix+(i*2)+1]=(byte)(val&0xff); | ||
295 | buffer[ix+(i*2)]=(byte)((val>>8)&0xff); | ||
296 | ix+=channels*2; | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | protected float[] getWindow() { | ||
302 | return window; | ||
303 | } | ||
304 | |||
305 | protected int getLeftWindowStart() { | ||
306 | return leftWindowStart; | ||
307 | } | ||
308 | |||
309 | protected int getLeftWindowEnd() { | ||
310 | return leftWindowEnd; | ||
311 | } | ||
312 | |||
313 | protected int getRightWindowStart() { | ||
314 | return rightWindowStart; | ||
315 | } | ||
316 | |||
317 | protected int getRightWindowEnd() { | ||
318 | return rightWindowEnd; | ||
319 | } | ||
320 | |||
321 | public int[][] getPcm() { | ||
322 | return pcmInt; | ||
323 | } | ||
324 | |||
325 | public float[][] getFreqencyDomain() { | ||
326 | return pcm; | ||
327 | } | ||
328 | } | ||
diff --git a/songdbj/de/jarnbjo/vorbis/CodeBook.java b/songdbj/de/jarnbjo/vorbis/CodeBook.java new file mode 100644 index 0000000000..c865b120ca --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/CodeBook.java | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.3 2003/04/10 19:49:04 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | package de.jarnbjo.vorbis; | ||
37 | |||
38 | import java.io.IOException; | ||
39 | import java.util.Arrays; | ||
40 | |||
41 | import de.jarnbjo.util.io.BitInputStream; | ||
42 | import de.jarnbjo.util.io.HuffmanNode; | ||
43 | |||
44 | class CodeBook { | ||
45 | |||
46 | private HuffmanNode huffmanRoot; | ||
47 | private int dimensions, entries; | ||
48 | |||
49 | private int[] entryLengths; | ||
50 | private float[][] valueVector; | ||
51 | |||
52 | protected CodeBook(BitInputStream source) throws VorbisFormatException, IOException { | ||
53 | |||
54 | // check sync | ||
55 | if(source.getInt(24)!=0x564342) { | ||
56 | throw new VorbisFormatException("The code book sync pattern is not correct."); | ||
57 | } | ||
58 | |||
59 | dimensions=source.getInt(16); | ||
60 | entries=source.getInt(24); | ||
61 | |||
62 | entryLengths=new int[entries]; | ||
63 | |||
64 | boolean ordered=source.getBit(); | ||
65 | |||
66 | if(ordered) { | ||
67 | int cl=source.getInt(5)+1; | ||
68 | for(int i=0; i<entryLengths.length; ) { | ||
69 | int num=source.getInt(Util.ilog(entryLengths.length-i)); | ||
70 | if(i+num>entryLengths.length) { | ||
71 | throw new VorbisFormatException("The codebook entry length list is longer than the actual number of entry lengths."); | ||
72 | } | ||
73 | Arrays.fill(entryLengths, i, i+num, cl); | ||
74 | cl++; | ||
75 | i+=num; | ||
76 | } | ||
77 | } | ||
78 | else { | ||
79 | // !ordered | ||
80 | boolean sparse=source.getBit(); | ||
81 | |||
82 | if(sparse) { | ||
83 | for(int i=0; i<entryLengths.length; i++) { | ||
84 | if(source.getBit()) { | ||
85 | entryLengths[i]=source.getInt(5)+1; | ||
86 | } | ||
87 | else { | ||
88 | entryLengths[i]=-1; | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | else { | ||
93 | // !sparse | ||
94 | for(int i=0; i<entryLengths.length; i++) { | ||
95 | entryLengths[i]=source.getInt(5)+1; | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (!createHuffmanTree(entryLengths)) { | ||
101 | throw new VorbisFormatException("An exception was thrown when building the codebook Huffman tree."); | ||
102 | } | ||
103 | |||
104 | int codeBookLookupType=source.getInt(4); | ||
105 | |||
106 | switch(codeBookLookupType) { | ||
107 | case 0: | ||
108 | // codebook has no scalar vectors to be calculated | ||
109 | break; | ||
110 | case 1: | ||
111 | case 2: | ||
112 | float codeBookMinimumValue=Util.float32unpack(source.getInt(32)); | ||
113 | float codeBookDeltaValue=Util.float32unpack(source.getInt(32)); | ||
114 | |||
115 | int codeBookValueBits=source.getInt(4)+1; | ||
116 | boolean codeBookSequenceP=source.getBit(); | ||
117 | |||
118 | int codeBookLookupValues=0; | ||
119 | |||
120 | if(codeBookLookupType==1) { | ||
121 | codeBookLookupValues=Util.lookup1Values(entries, dimensions); | ||
122 | } | ||
123 | else { | ||
124 | codeBookLookupValues=entries*dimensions; | ||
125 | } | ||
126 | |||
127 | int codeBookMultiplicands[]=new int[codeBookLookupValues]; | ||
128 | |||
129 | for(int i=0; i<codeBookMultiplicands.length; i++) { | ||
130 | codeBookMultiplicands[i]=source.getInt(codeBookValueBits); | ||
131 | } | ||
132 | |||
133 | valueVector=new float[entries][dimensions]; | ||
134 | |||
135 | if(codeBookLookupType==1) { | ||
136 | for(int i=0; i<entries; i++) { | ||
137 | float last=0; | ||
138 | int indexDivisor=1; | ||
139 | for(int j=0; j<dimensions; j++) { | ||
140 | int multiplicandOffset= | ||
141 | (i/indexDivisor)%codeBookLookupValues; | ||
142 | valueVector[i][j]= | ||
143 | codeBookMultiplicands[multiplicandOffset]*codeBookDeltaValue+codeBookMinimumValue+last; | ||
144 | if(codeBookSequenceP) { | ||
145 | last=valueVector[i][j]; | ||
146 | } | ||
147 | indexDivisor*=codeBookLookupValues; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | else { | ||
152 | throw new UnsupportedOperationException(); | ||
153 | /** @todo implement */ | ||
154 | } | ||
155 | break; | ||
156 | default: | ||
157 | throw new VorbisFormatException("Unsupported codebook lookup type: "+codeBookLookupType); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | private static long totalTime=0; | ||
162 | |||
163 | private boolean createHuffmanTree(int[] entryLengths) { | ||
164 | huffmanRoot=new HuffmanNode(); | ||
165 | for(int i=0; i<entryLengths.length; i++) { | ||
166 | int el=entryLengths[i]; | ||
167 | if(el>0) { | ||
168 | if(!huffmanRoot.setNewValue(el, i)) { | ||
169 | return false; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | return true; | ||
174 | } | ||
175 | |||
176 | protected int getDimensions() { | ||
177 | return dimensions; | ||
178 | } | ||
179 | |||
180 | protected int getEntries() { | ||
181 | return entries; | ||
182 | } | ||
183 | |||
184 | protected HuffmanNode getHuffmanRoot() { | ||
185 | return huffmanRoot; | ||
186 | } | ||
187 | |||
188 | //public float[] readVQ(ReadableBitChannel source) throws IOException { | ||
189 | // return valueVector[readInt(source)]; | ||
190 | //} | ||
191 | |||
192 | protected int readInt(final BitInputStream source) throws IOException { | ||
193 | return source.getInt(huffmanRoot); | ||
194 | /* | ||
195 | HuffmanNode node; | ||
196 | for(node=huffmanRoot; node.value==null; node=source.getBit()?node.o1:node.o0); | ||
197 | return node.value.intValue(); | ||
198 | */ | ||
199 | } | ||
200 | |||
201 | protected void readVvAdd(float[][] a, BitInputStream source, int offset, int length) | ||
202 | throws VorbisFormatException, IOException { | ||
203 | |||
204 | int i,j;//k;//entry; | ||
205 | int chptr=0; | ||
206 | int ch=a.length; | ||
207 | |||
208 | if(ch==0) { | ||
209 | return; | ||
210 | } | ||
211 | |||
212 | int lim=(offset+length)/ch; | ||
213 | |||
214 | for(i=offset/ch;i<lim;){ | ||
215 | final float[] ve=valueVector[source.getInt(huffmanRoot)]; | ||
216 | for(j=0;j<dimensions;j++){ | ||
217 | a[chptr++][i]+=ve[j]; | ||
218 | if(chptr==ch){ | ||
219 | chptr=0; | ||
220 | i++; | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | public void readVAdd(double[] a, ReadableBitChannel source, int offset, int length) | ||
228 | throws FormatException, IOException { | ||
229 | |||
230 | int i,j,entry; | ||
231 | int t; | ||
232 | |||
233 | if(dimensions>8){ | ||
234 | for(i=0;i<length;){ | ||
235 | entry = readInt(source); | ||
236 | //if(entry==-1)return(-1); | ||
237 | //t=entry*dimensions; | ||
238 | for(j=0;j<dimensions;){ | ||
239 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | else{ | ||
244 | for(i=0;i<length;){ | ||
245 | entry=readInt(source); | ||
246 | //if(entry==-1)return(-1); | ||
247 | //t=entry*dim; | ||
248 | j=0; | ||
249 | switch(dimensions){ | ||
250 | case 8: | ||
251 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
252 | case 7: | ||
253 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
254 | case 6: | ||
255 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
256 | case 5: | ||
257 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
258 | case 4: | ||
259 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
260 | case 3: | ||
261 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
262 | case 2: | ||
263 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
264 | case 1: | ||
265 | a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)]; | ||
266 | case 0: | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | */ | ||
273 | |||
274 | |||
275 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/CommentHeader.java b/songdbj/de/jarnbjo/vorbis/CommentHeader.java new file mode 100644 index 0000000000..dd00ebca38 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/CommentHeader.java | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.*; | ||
33 | |||
34 | import java.util.*; | ||
35 | |||
36 | import de.jarnbjo.util.io.BitInputStream; | ||
37 | |||
38 | /** | ||
39 | */ | ||
40 | |||
41 | public class CommentHeader { | ||
42 | |||
43 | public static final String TITLE = "TITLE"; | ||
44 | public static final String ARTIST = "ARTIST"; | ||
45 | public static final String ALBUM = "ALBUM"; | ||
46 | public static final String TRACKNUMBER = "TRACKNUMBER"; | ||
47 | public static final String VERSION = "VERSION"; | ||
48 | public static final String PERFORMER = "PERFORMER"; | ||
49 | public static final String COPYRIGHT = "COPYRIGHT"; | ||
50 | public static final String LICENSE = "LICENSE"; | ||
51 | public static final String ORGANIZATION = "ORGANIZATION"; | ||
52 | public static final String DESCRIPTION = "DESCRIPTION"; | ||
53 | public static final String GENRE = "GENRE"; | ||
54 | public static final String DATE = "DATE"; | ||
55 | public static final String LOCATION = "LOCATION"; | ||
56 | public static final String CONTACT = "CONTACT"; | ||
57 | public static final String ISRC = "ISRC"; | ||
58 | |||
59 | private String vendor; | ||
60 | private HashMap comments=new HashMap(); | ||
61 | private boolean framingBit; | ||
62 | |||
63 | private static final long HEADER = 0x736962726f76L; // 'vorbis' | ||
64 | |||
65 | public CommentHeader(BitInputStream source) throws VorbisFormatException, IOException { | ||
66 | if(source.getLong(48)!=HEADER) { | ||
67 | throw new VorbisFormatException("The identification header has an illegal leading."); | ||
68 | } | ||
69 | |||
70 | vendor=getString(source); | ||
71 | |||
72 | int ucLength=source.getInt(32); | ||
73 | |||
74 | for(int i=0; i<ucLength; i++) { | ||
75 | String comment=getString(source); | ||
76 | int ix=comment.indexOf('='); | ||
77 | String key=comment.substring(0, ix); | ||
78 | String value=comment.substring(ix+1); | ||
79 | //comments.put(key, value); | ||
80 | addComment(key, value); | ||
81 | } | ||
82 | |||
83 | framingBit=source.getInt(8)!=0; | ||
84 | } | ||
85 | |||
86 | private void addComment(String key, String value) { | ||
87 | key = key.toUpperCase(); // Comment keys are case insensitive | ||
88 | ArrayList al=(ArrayList)comments.get(key); | ||
89 | if(al==null) { | ||
90 | al=new ArrayList(); | ||
91 | comments.put(key, al); | ||
92 | } | ||
93 | al.add(value); | ||
94 | } | ||
95 | |||
96 | public String getVendor() { | ||
97 | return vendor; | ||
98 | } | ||
99 | |||
100 | public String getComment(String key) { | ||
101 | ArrayList al=(ArrayList)comments.get(key); | ||
102 | return al==null?(String)null:(String)al.get(0); | ||
103 | } | ||
104 | |||
105 | public String[] getComments(String key) { | ||
106 | ArrayList al=(ArrayList)comments.get(key); | ||
107 | return al==null?new String[0]:(String[])al.toArray(new String[al.size()]); | ||
108 | } | ||
109 | |||
110 | public String getTitle() { | ||
111 | return getComment(TITLE); | ||
112 | } | ||
113 | |||
114 | public String[] getTitles() { | ||
115 | return getComments(TITLE); | ||
116 | } | ||
117 | |||
118 | public String getVersion() { | ||
119 | return getComment(VERSION); | ||
120 | } | ||
121 | |||
122 | public String[] getVersions() { | ||
123 | return getComments(VERSION); | ||
124 | } | ||
125 | |||
126 | public String getAlbum() { | ||
127 | return getComment(ALBUM); | ||
128 | } | ||
129 | |||
130 | public String[] getAlbums() { | ||
131 | return getComments(ALBUM); | ||
132 | } | ||
133 | |||
134 | public String getTrackNumber() { | ||
135 | return getComment(TRACKNUMBER); | ||
136 | } | ||
137 | |||
138 | public String[] getTrackNumbers() { | ||
139 | return getComments(TRACKNUMBER); | ||
140 | } | ||
141 | |||
142 | public String getArtist() { | ||
143 | return getComment(ARTIST); | ||
144 | } | ||
145 | |||
146 | public String[] getArtists() { | ||
147 | return getComments(ARTIST); | ||
148 | } | ||
149 | |||
150 | public String getPerformer() { | ||
151 | return getComment(PERFORMER); | ||
152 | } | ||
153 | |||
154 | public String[] getPerformers() { | ||
155 | return getComments(PERFORMER); | ||
156 | } | ||
157 | |||
158 | public String getCopyright() { | ||
159 | return getComment(COPYRIGHT); | ||
160 | } | ||
161 | |||
162 | public String[] getCopyrights() { | ||
163 | return getComments(COPYRIGHT); | ||
164 | } | ||
165 | |||
166 | public String getLicense() { | ||
167 | return getComment(LICENSE); | ||
168 | } | ||
169 | |||
170 | public String[] getLicenses() { | ||
171 | return getComments(LICENSE); | ||
172 | } | ||
173 | |||
174 | public String getOrganization() { | ||
175 | return getComment(ORGANIZATION); | ||
176 | } | ||
177 | |||
178 | public String[] getOrganizations() { | ||
179 | return getComments(ORGANIZATION); | ||
180 | } | ||
181 | |||
182 | public String getDescription() { | ||
183 | return getComment(DESCRIPTION); | ||
184 | } | ||
185 | |||
186 | public String[] getDescriptions() { | ||
187 | return getComments(DESCRIPTION); | ||
188 | } | ||
189 | |||
190 | public String getGenre() { | ||
191 | return getComment(GENRE); | ||
192 | } | ||
193 | |||
194 | public String[] getGenres() { | ||
195 | return getComments(GENRE); | ||
196 | } | ||
197 | |||
198 | public String getDate() { | ||
199 | return getComment(DATE); | ||
200 | } | ||
201 | |||
202 | public String[] getDates() { | ||
203 | return getComments(DATE); | ||
204 | } | ||
205 | |||
206 | public String getLocation() { | ||
207 | return getComment(LOCATION); | ||
208 | } | ||
209 | |||
210 | public String[] getLocations() { | ||
211 | return getComments(LOCATION); | ||
212 | } | ||
213 | |||
214 | public String getContact() { | ||
215 | return getComment(CONTACT); | ||
216 | } | ||
217 | |||
218 | public String[] getContacts() { | ||
219 | return getComments(CONTACT); | ||
220 | } | ||
221 | |||
222 | public String getIsrc() { | ||
223 | return getComment(ISRC); | ||
224 | } | ||
225 | |||
226 | public String[] getIsrcs() { | ||
227 | return getComments(ISRC); | ||
228 | } | ||
229 | |||
230 | |||
231 | private String getString(BitInputStream source) throws IOException, VorbisFormatException { | ||
232 | |||
233 | int length=source.getInt(32); | ||
234 | |||
235 | byte[] strArray=new byte[length]; | ||
236 | |||
237 | for(int i=0; i<length; i++) { | ||
238 | strArray[i]=(byte)source.getInt(8); | ||
239 | } | ||
240 | |||
241 | return new String(strArray, "UTF-8"); | ||
242 | } | ||
243 | |||
244 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Floor.java b/songdbj/de/jarnbjo/vorbis/Floor.java new file mode 100644 index 0000000000..5be2798dfb --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Floor.java | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/10 19:49:04 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | import de.jarnbjo.util.io.BitInputStream; | ||
38 | |||
39 | |||
40 | public abstract class Floor { | ||
41 | |||
42 | public final static float[] DB_STATIC_TABLE={ | ||
43 | 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, | ||
44 | 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, | ||
45 | 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.128753e-07f, | ||
46 | 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, | ||
47 | 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, | ||
48 | 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, | ||
49 | 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, | ||
50 | 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, | ||
51 | 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, | ||
52 | 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, | ||
53 | 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, | ||
54 | 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, | ||
55 | 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, | ||
56 | 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, | ||
57 | 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, | ||
58 | 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, | ||
59 | 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, | ||
60 | 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, | ||
61 | 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, | ||
62 | 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, | ||
63 | 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, | ||
64 | 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, | ||
65 | 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, | ||
66 | 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, | ||
67 | 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, | ||
68 | 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, | ||
69 | 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, | ||
70 | 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, | ||
71 | 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, | ||
72 | 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, | ||
73 | 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, | ||
74 | 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, | ||
75 | 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, | ||
76 | 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, | ||
77 | 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, | ||
78 | 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, | ||
79 | 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, | ||
80 | 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, | ||
81 | 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, | ||
82 | 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, | ||
83 | 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, | ||
84 | 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, | ||
85 | 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, | ||
86 | 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, | ||
87 | 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, | ||
88 | 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, | ||
89 | 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, | ||
90 | 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, | ||
91 | 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, | ||
92 | 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, | ||
93 | 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, | ||
94 | 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, | ||
95 | 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, | ||
96 | 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, | ||
97 | 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, | ||
98 | 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, | ||
99 | 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, | ||
100 | 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, | ||
101 | 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, | ||
102 | 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, | ||
103 | 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, | ||
104 | 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, | ||
105 | 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, | ||
106 | 0.82788260f, 0.88168307f, 0.9389798f, 1.0f}; | ||
107 | |||
108 | static Floor createInstance(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
109 | |||
110 | int type=source.getInt(16); | ||
111 | switch(type) { | ||
112 | case 0: | ||
113 | return new Floor0(source, header); | ||
114 | case 1: | ||
115 | return new Floor1(source, header); | ||
116 | default: | ||
117 | throw new VorbisFormatException("Floor type "+type+" is not supported."); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | abstract int getType(); | ||
122 | abstract Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException; | ||
123 | abstract void computeFloor(float[] vector); | ||
124 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Floor0.java b/songdbj/de/jarnbjo/vorbis/Floor0.java new file mode 100644 index 0000000000..4e94b27b73 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Floor0.java | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.IOException; | ||
33 | |||
34 | import de.jarnbjo.util.io.BitInputStream; | ||
35 | |||
36 | class Floor0 extends Floor { | ||
37 | |||
38 | private int order, rate, barkMapSize, amplitudeBits, amplitudeOffset; | ||
39 | private int bookList[]; | ||
40 | |||
41 | protected Floor0(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
42 | |||
43 | order=source.getInt(8); | ||
44 | rate=source.getInt(16); | ||
45 | barkMapSize=source.getInt(16); | ||
46 | amplitudeBits=source.getInt(6); | ||
47 | amplitudeOffset=source.getInt(8); | ||
48 | |||
49 | int bookCount=source.getInt(4)+1; | ||
50 | bookList=new int[bookCount]; | ||
51 | |||
52 | for(int i=0; i<bookList.length; i++) { | ||
53 | bookList[i]=source.getInt(8); | ||
54 | if(bookList[i]>header.getCodeBooks().length) { | ||
55 | throw new VorbisFormatException("A floor0_book_list entry is higher than the code book count."); | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | protected int getType() { | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | protected Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException { | ||
65 | /** @todo implement */ | ||
66 | throw new UnsupportedOperationException(); | ||
67 | } | ||
68 | |||
69 | protected void computeFloor(float[] vector) { | ||
70 | /** @todo implement */ | ||
71 | throw new UnsupportedOperationException(); | ||
72 | } | ||
73 | |||
74 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Floor1.java b/songdbj/de/jarnbjo/vorbis/Floor1.java new file mode 100644 index 0000000000..69a118b44e --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Floor1.java | |||
@@ -0,0 +1,324 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$multip | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.IOException; | ||
33 | import java.util.*; | ||
34 | |||
35 | import de.jarnbjo.util.io.BitInputStream; | ||
36 | |||
37 | |||
38 | class Floor1 extends Floor implements Cloneable { | ||
39 | |||
40 | private int[] partitionClassList; | ||
41 | private int maximumClass, multiplier, rangeBits; | ||
42 | private int[] classDimensions; | ||
43 | private int[] classSubclasses; | ||
44 | private int[] classMasterbooks; | ||
45 | private int[][] subclassBooks; | ||
46 | private int[] xList; | ||
47 | private int[] yList; | ||
48 | private int[] lowNeighbours, highNeighbours; | ||
49 | //private boolean[] step2Flags; | ||
50 | |||
51 | private static final int[] RANGES = {256, 128, 86, 64}; | ||
52 | |||
53 | private Floor1() { | ||
54 | } | ||
55 | |||
56 | protected Floor1(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
57 | |||
58 | maximumClass=-1; | ||
59 | int partitions=source.getInt(5); | ||
60 | partitionClassList=new int[partitions]; | ||
61 | |||
62 | for(int i=0; i<partitionClassList.length; i++) { | ||
63 | partitionClassList[i]=source.getInt(4); | ||
64 | if(partitionClassList[i]>maximumClass) { | ||
65 | maximumClass=partitionClassList[i]; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | |||
70 | classDimensions=new int[maximumClass+1]; | ||
71 | classSubclasses=new int[maximumClass+1]; | ||
72 | classMasterbooks=new int[maximumClass+1]; | ||
73 | subclassBooks=new int[maximumClass+1][]; | ||
74 | |||
75 | int xListLength=2; | ||
76 | |||
77 | for(int i=0; i<=maximumClass; i++) { | ||
78 | classDimensions[i]=source.getInt(3)+1; | ||
79 | xListLength+=classDimensions[i]; | ||
80 | classSubclasses[i]=source.getInt(2); | ||
81 | |||
82 | if(classDimensions[i] > header.getCodeBooks().length || | ||
83 | classSubclasses[i] > header.getCodeBooks().length) { | ||
84 | throw new VorbisFormatException("There is a class dimension or class subclasses entry higher than the number of codebooks in the setup header."); | ||
85 | } | ||
86 | if(classSubclasses[i]!=0) { | ||
87 | classMasterbooks[i]=source.getInt(8); | ||
88 | } | ||
89 | subclassBooks[i]=new int[1<<classSubclasses[i]]; | ||
90 | for(int j=0; j<subclassBooks[i].length; j++) { | ||
91 | subclassBooks[i][j]=source.getInt(8)-1; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | multiplier=source.getInt(2)+1; | ||
96 | rangeBits=source.getInt(4); | ||
97 | |||
98 | //System.out.println("multiplier: "+multiplier); | ||
99 | //System.out.println("rangeBits: "+rangeBits); | ||
100 | |||
101 | //System.out.println("xListLength: "+xListLength); | ||
102 | |||
103 | int floorValues=0; | ||
104 | |||
105 | ArrayList alXList=new ArrayList(); | ||
106 | |||
107 | alXList.add(new Integer(0)); | ||
108 | alXList.add(new Integer(1<<rangeBits)); | ||
109 | |||
110 | //System.out.println("partitions: "+partitions); | ||
111 | //System.out.println("classDimensions.length: "+classDimensions.length); | ||
112 | |||
113 | for(int i=0; i<partitions; i++) { | ||
114 | for(int j=0; j<classDimensions[partitionClassList[i]]; j++) { | ||
115 | alXList.add(new Integer(source.getInt(rangeBits))); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | xList=new int[alXList.size()]; | ||
120 | lowNeighbours=new int[xList.length]; | ||
121 | highNeighbours=new int[xList.length]; | ||
122 | |||
123 | Iterator iter=alXList.iterator(); | ||
124 | for(int i=0; i<xList.length; i++) { | ||
125 | xList[i]=((Integer)iter.next()).intValue(); | ||
126 | } | ||
127 | |||
128 | for(int i=0; i<xList.length; i++) { | ||
129 | lowNeighbours[i]=Util.lowNeighbour(xList, i); | ||
130 | highNeighbours[i]=Util.highNeighbour(xList, i); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | protected int getType() { | ||
135 | return 1; | ||
136 | } | ||
137 | |||
138 | protected Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException { | ||
139 | |||
140 | //System.out.println("decodeFloor"); | ||
141 | if(!source.getBit()) { | ||
142 | //System.out.println("null"); | ||
143 | return null; | ||
144 | } | ||
145 | |||
146 | Floor1 clone=(Floor1)clone(); | ||
147 | |||
148 | clone.yList=new int[xList.length]; | ||
149 | |||
150 | int range=RANGES[multiplier-1]; | ||
151 | |||
152 | clone.yList[0]=source.getInt(Util.ilog(range-1)); | ||
153 | clone.yList[1]=source.getInt(Util.ilog(range-1)); | ||
154 | |||
155 | int offset=2; | ||
156 | |||
157 | for(int i=0; i<partitionClassList.length; i++) { | ||
158 | int cls=partitionClassList[i]; | ||
159 | int cdim=classDimensions[cls]; | ||
160 | int cbits=classSubclasses[cls]; | ||
161 | int csub=(1<<cbits)-1; | ||
162 | int cval=0; | ||
163 | if(cbits>0) { | ||
164 | cval=source.getInt(vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].getHuffmanRoot()); | ||
165 | //cval=vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].readInt(source); | ||
166 | //System.out.println("cval: "+cval); | ||
167 | } | ||
168 | //System.out.println("0: "+cls+" "+cdim+" "+cbits+" "+csub+" "+cval); | ||
169 | for(int j=0; j<cdim; j++) { | ||
170 | //System.out.println("a: "+cls+" "+cval+" "+csub); | ||
171 | int book=subclassBooks[cls][cval&csub]; | ||
172 | cval>>>=cbits; | ||
173 | if(book>=0) { | ||
174 | clone.yList[j+offset]=source.getInt(vorbis.getSetupHeader().getCodeBooks()[book].getHuffmanRoot()); | ||
175 | //clone.yList[j+offset]=vorbis.getSetupHeader().getCodeBooks()[book].readInt(source); | ||
176 | //System.out.println("b: "+(j+offset)+" "+book+" "+clone.yList[j+offset]); | ||
177 | //System.out.println(""); | ||
178 | } | ||
179 | else { | ||
180 | clone.yList[j+offset]=0; | ||
181 | } | ||
182 | } | ||
183 | offset+=cdim; | ||
184 | } | ||
185 | |||
186 | //System.out.println(""); | ||
187 | //for(int i=0; i<clone.xList.length; i++) { | ||
188 | // System.out.println(i+" = "+clone.xList[i]); | ||
189 | //} | ||
190 | |||
191 | //System.out.println(""); | ||
192 | //for(int i=0; i<clone.yList.length; i++) { | ||
193 | // System.out.println(i+" = "+clone.yList[i]); | ||
194 | //} | ||
195 | |||
196 | //System.out.println("offset: "+offset); | ||
197 | //System.out.println("yList.length: "+clone.yList.length); | ||
198 | |||
199 | //System.exit(0); | ||
200 | |||
201 | return clone; | ||
202 | } | ||
203 | |||
204 | protected void computeFloor(final float[] vector) { | ||
205 | |||
206 | int n=vector.length; | ||
207 | final int values=xList.length; | ||
208 | final boolean[] step2Flags=new boolean[values]; | ||
209 | |||
210 | final int range=RANGES[multiplier-1]; | ||
211 | |||
212 | for(int i=2; i<values; i++) { | ||
213 | final int lowNeighbourOffset=lowNeighbours[i];//Util.lowNeighbour(xList, i); | ||
214 | final int highNeighbourOffset=highNeighbours[i];//Util.highNeighbour(xList, i); | ||
215 | final int predicted=Util.renderPoint( | ||
216 | xList[lowNeighbourOffset], xList[highNeighbourOffset], | ||
217 | yList[lowNeighbourOffset], yList[highNeighbourOffset], | ||
218 | xList[i]); | ||
219 | final int val=yList[i]; | ||
220 | final int highRoom=range-predicted; | ||
221 | final int lowRoom=predicted; | ||
222 | final int room=highRoom<lowRoom?highRoom*2:lowRoom*2; | ||
223 | if(val!=0) { | ||
224 | step2Flags[lowNeighbourOffset]=true; | ||
225 | step2Flags[highNeighbourOffset]=true; | ||
226 | step2Flags[i]=true; | ||
227 | if(val>=room) { | ||
228 | yList[i]=highRoom>lowRoom? | ||
229 | val-lowRoom+predicted: | ||
230 | -val+highRoom+predicted-1; | ||
231 | } | ||
232 | else { | ||
233 | yList[i]=(val&1)==1? | ||
234 | predicted-((val+1)>>1): | ||
235 | predicted+(val>>1); | ||
236 | } | ||
237 | } | ||
238 | else { | ||
239 | step2Flags[i]=false; | ||
240 | yList[i]=predicted; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | final int[] xList2=new int[values]; | ||
245 | |||
246 | System.arraycopy(xList, 0, xList2, 0, values); | ||
247 | sort(xList2, yList, step2Flags); | ||
248 | |||
249 | int hx=0, hy=0, lx=0, ly=yList[0]*multiplier; | ||
250 | |||
251 | float[] vector2=new float[vector.length]; | ||
252 | float[] vector3=new float[vector.length]; | ||
253 | Arrays.fill(vector2, 1.0f); | ||
254 | System.arraycopy(vector, 0, vector3, 0, vector.length); | ||
255 | |||
256 | for(int i=1; i<values; i++) { | ||
257 | if(step2Flags[i]) { | ||
258 | hy=yList[i]*multiplier; | ||
259 | hx=xList2[i]; | ||
260 | Util.renderLine(lx, ly, hx, hy, vector); | ||
261 | Util.renderLine(lx, ly, hx, hy, vector2); | ||
262 | lx=hx; | ||
263 | ly=hy; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | final float r=DB_STATIC_TABLE[hy]; | ||
268 | for(; hx<n/2; vector[hx++]=r); | ||
269 | } | ||
270 | |||
271 | public Object clone() { | ||
272 | Floor1 clone=new Floor1(); | ||
273 | clone.classDimensions=classDimensions; | ||
274 | clone.classMasterbooks=classMasterbooks; | ||
275 | clone.classSubclasses=classSubclasses; | ||
276 | clone.maximumClass=maximumClass; | ||
277 | clone.multiplier=multiplier; | ||
278 | clone.partitionClassList=partitionClassList; | ||
279 | clone.rangeBits=rangeBits; | ||
280 | clone.subclassBooks=subclassBooks; | ||
281 | clone.xList=xList; | ||
282 | clone.yList=yList; | ||
283 | clone.lowNeighbours=lowNeighbours; | ||
284 | clone.highNeighbours=highNeighbours; | ||
285 | return clone; | ||
286 | } | ||
287 | |||
288 | private final static void sort(int x[], int y[], boolean b[]) { | ||
289 | int off=0; | ||
290 | int len=x.length; | ||
291 | int lim=len+off; | ||
292 | int itmp; | ||
293 | boolean btmp; | ||
294 | // Insertion sort on smallest arrays | ||
295 | for (int i=off; i<lim; i++) { | ||
296 | for (int j=i; j>off && x[j-1]>x[j]; j--) { | ||
297 | itmp=x[j]; | ||
298 | x[j]=x[j-1]; | ||
299 | x[j-1]=itmp; | ||
300 | itmp=y[j]; | ||
301 | y[j]=y[j-1]; | ||
302 | y[j-1]=itmp; | ||
303 | btmp=b[j]; | ||
304 | b[j]=b[j-1]; | ||
305 | b[j-1]=btmp; | ||
306 | //swap(x, j, j-1); | ||
307 | //swap(y, j, j-1); | ||
308 | //swap(b, j, j-1); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | private final static void swap(int x[], int a, int b) { | ||
314 | int t = x[a]; | ||
315 | x[a] = x[b]; | ||
316 | x[b] = t; | ||
317 | } | ||
318 | |||
319 | private final static void swap(boolean x[], int a, int b) { | ||
320 | boolean t = x[a]; | ||
321 | x[a] = x[b]; | ||
322 | x[b] = t; | ||
323 | } | ||
324 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java b/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java new file mode 100644 index 0000000000..1e18163385 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/IdentificationHeader.java | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.3 2003/03/31 00:20:16 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | package de.jarnbjo.vorbis; | ||
37 | |||
38 | import java.io.IOException; | ||
39 | |||
40 | import de.jarnbjo.util.io.BitInputStream; | ||
41 | |||
42 | /** | ||
43 | */ | ||
44 | |||
45 | public class IdentificationHeader { | ||
46 | |||
47 | private int version, channels, sampleRate, bitrateMaximum, bitrateNominal, bitrateMinimum, blockSize0, blockSize1; | ||
48 | private boolean framingFlag; | ||
49 | private MdctFloat[] mdct=new MdctFloat[2]; | ||
50 | //private MdctLong[] mdctInt=new MdctLong[2]; | ||
51 | |||
52 | private static final long HEADER = 0x736962726f76L; // 'vorbis' | ||
53 | |||
54 | public IdentificationHeader(BitInputStream source) throws VorbisFormatException, IOException { | ||
55 | //equalizer=new Equalizer(); | ||
56 | //equalizer.pack(); | ||
57 | //equalizer.show(); | ||
58 | |||
59 | long leading=source.getLong(48); | ||
60 | if(leading!=HEADER) { | ||
61 | throw new VorbisFormatException("The identification header has an illegal leading."); | ||
62 | } | ||
63 | version=source.getInt(32); | ||
64 | channels=source.getInt(8); | ||
65 | sampleRate=source.getInt(32); | ||
66 | bitrateMaximum=source.getInt(32); | ||
67 | bitrateNominal=source.getInt(32); | ||
68 | bitrateMinimum=source.getInt(32); | ||
69 | int bs=source.getInt(8); | ||
70 | blockSize0=1<<(bs&0xf); | ||
71 | blockSize1=1<<(bs>>4); | ||
72 | |||
73 | mdct[0]=new MdctFloat(blockSize0); | ||
74 | mdct[1]=new MdctFloat(blockSize1); | ||
75 | //mdctInt[0]=new MdctLong(blockSize0); | ||
76 | //mdctInt[1]=new MdctLong(blockSize1); | ||
77 | |||
78 | framingFlag=source.getInt(8)!=0; | ||
79 | } | ||
80 | |||
81 | public int getSampleRate() { | ||
82 | return sampleRate; | ||
83 | } | ||
84 | |||
85 | public int getMaximumBitrate() { | ||
86 | return bitrateMaximum; | ||
87 | } | ||
88 | |||
89 | public int getNominalBitrate() { | ||
90 | return bitrateNominal; | ||
91 | } | ||
92 | |||
93 | public int getMinimumBitrate() { | ||
94 | return bitrateMinimum; | ||
95 | } | ||
96 | |||
97 | public int getChannels() { | ||
98 | return channels; | ||
99 | } | ||
100 | |||
101 | public int getBlockSize0() { | ||
102 | return blockSize0; | ||
103 | } | ||
104 | |||
105 | public int getBlockSize1() { | ||
106 | return blockSize1; | ||
107 | } | ||
108 | |||
109 | protected MdctFloat getMdct0() { | ||
110 | return mdct[0]; | ||
111 | } | ||
112 | |||
113 | protected MdctFloat getMdct1() { | ||
114 | return mdct[1]; | ||
115 | } | ||
116 | |||
117 | public int getVersion() { | ||
118 | return version; | ||
119 | } | ||
120 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Mapping.java b/songdbj/de/jarnbjo/vorbis/Mapping.java new file mode 100644 index 0000000000..24a2eaa19a --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Mapping.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.IOException; | ||
33 | |||
34 | import de.jarnbjo.util.io.BitInputStream; | ||
35 | |||
36 | abstract class Mapping { | ||
37 | |||
38 | protected static Mapping createInstance(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
39 | |||
40 | int type=source.getInt(16); | ||
41 | switch(type) { | ||
42 | case 0: | ||
43 | //System.out.println("mapping type 0"); | ||
44 | return new Mapping0(vorbis, source, header); | ||
45 | default: | ||
46 | throw new VorbisFormatException("Mapping type "+type+" is not supported."); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | protected abstract int getType(); | ||
51 | protected abstract int[] getAngles(); | ||
52 | protected abstract int[] getMagnitudes() ; | ||
53 | protected abstract int[] getMux(); | ||
54 | protected abstract int[] getSubmapFloors(); | ||
55 | protected abstract int[] getSubmapResidues(); | ||
56 | protected abstract int getCouplingSteps(); | ||
57 | protected abstract int getSubmaps(); | ||
58 | |||
59 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Mapping0.java b/songdbj/de/jarnbjo/vorbis/Mapping0.java new file mode 100644 index 0000000000..e8fde4686f --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Mapping0.java | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.IOException; | ||
33 | |||
34 | import de.jarnbjo.util.io.BitInputStream; | ||
35 | |||
36 | class Mapping0 extends Mapping { | ||
37 | |||
38 | private int[] magnitudes, angles, mux, submapFloors, submapResidues; | ||
39 | |||
40 | protected Mapping0(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
41 | |||
42 | int submaps=1; | ||
43 | |||
44 | if(source.getBit()) { | ||
45 | submaps=source.getInt(4)+1; | ||
46 | } | ||
47 | |||
48 | //System.out.println("submaps: "+submaps); | ||
49 | |||
50 | int channels=vorbis.getIdentificationHeader().getChannels(); | ||
51 | int ilogChannels=Util.ilog(channels-1); | ||
52 | |||
53 | //System.out.println("ilogChannels: "+ilogChannels); | ||
54 | |||
55 | if(source.getBit()) { | ||
56 | int couplingSteps=source.getInt(8)+1; | ||
57 | magnitudes=new int[couplingSteps]; | ||
58 | angles=new int[couplingSteps]; | ||
59 | |||
60 | for(int i=0; i<couplingSteps; i++) { | ||
61 | magnitudes[i]=source.getInt(ilogChannels); | ||
62 | angles[i]=source.getInt(ilogChannels); | ||
63 | if(magnitudes[i]==angles[i] || magnitudes[i]>=channels || angles[i]>=channels) { | ||
64 | System.err.println(magnitudes[i]); | ||
65 | System.err.println(angles[i]); | ||
66 | throw new VorbisFormatException("The channel magnitude and/or angle mismatch."); | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | else { | ||
71 | magnitudes=new int[0]; | ||
72 | angles=new int[0]; | ||
73 | } | ||
74 | |||
75 | if(source.getInt(2)!=0) { | ||
76 | throw new VorbisFormatException("A reserved mapping field has an invalid value."); | ||
77 | } | ||
78 | |||
79 | mux=new int[channels]; | ||
80 | if(submaps>1) { | ||
81 | for(int i=0; i<channels; i++) { | ||
82 | mux[i]=source.getInt(4); | ||
83 | if(mux[i]>submaps) { | ||
84 | throw new VorbisFormatException("A mapping mux value is higher than the number of submaps"); | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | else { | ||
89 | for(int i=0; i<channels; i++) { | ||
90 | mux[i]=0; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | submapFloors=new int[submaps]; | ||
95 | submapResidues=new int[submaps]; | ||
96 | |||
97 | int floorCount=header.getFloors().length; | ||
98 | int residueCount=header.getResidues().length; | ||
99 | |||
100 | for(int i=0; i<submaps; i++) { | ||
101 | source.getInt(8); // discard time placeholder | ||
102 | submapFloors[i]=source.getInt(8); | ||
103 | submapResidues[i]=source.getInt(8); | ||
104 | |||
105 | if(submapFloors[i]>floorCount) { | ||
106 | throw new VorbisFormatException("A mapping floor value is higher than the number of floors."); | ||
107 | } | ||
108 | |||
109 | if(submapResidues[i]>residueCount) { | ||
110 | throw new VorbisFormatException("A mapping residue value is higher than the number of residues."); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | protected int getType() { | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | protected int[] getAngles() { | ||
120 | return angles; | ||
121 | } | ||
122 | |||
123 | protected int[] getMagnitudes() { | ||
124 | return magnitudes; | ||
125 | } | ||
126 | |||
127 | protected int[] getMux() { | ||
128 | return mux; | ||
129 | } | ||
130 | |||
131 | protected int[] getSubmapFloors() { | ||
132 | return submapFloors; | ||
133 | } | ||
134 | |||
135 | protected int[] getSubmapResidues() { | ||
136 | return submapResidues; | ||
137 | } | ||
138 | |||
139 | protected int getCouplingSteps() { | ||
140 | return angles.length; | ||
141 | } | ||
142 | |||
143 | protected int getSubmaps() { | ||
144 | return submapFloors.length; | ||
145 | } | ||
146 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/MdctFloat.java b/songdbj/de/jarnbjo/vorbis/MdctFloat.java new file mode 100644 index 0000000000..4f354b259b --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/MdctFloat.java | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 12:09:45 shred | ||
22 | * *** empty log message *** | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.3 2003/04/10 19:49:04 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | package de.jarnbjo.vorbis; | ||
37 | |||
38 | class MdctFloat { | ||
39 | static private final float cPI3_8=0.38268343236508977175f; | ||
40 | static private final float cPI2_8=0.70710678118654752441f; | ||
41 | static private final float cPI1_8=0.92387953251128675613f; | ||
42 | |||
43 | private int n; | ||
44 | private int log2n; | ||
45 | |||
46 | private float[] trig; | ||
47 | private int[] bitrev; | ||
48 | |||
49 | private float[] equalizer; | ||
50 | |||
51 | private float scale; | ||
52 | |||
53 | private int itmp1, itmp2, itmp3, itmp4, itmp5, itmp6, itmp7, itmp8, itmp9; | ||
54 | private float dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, dtmp6, dtmp7, dtmp8, dtmp9; | ||
55 | |||
56 | protected MdctFloat(int n) { | ||
57 | bitrev=new int[n/4]; | ||
58 | trig=new float[n+n/4]; | ||
59 | |||
60 | int n2=n>>>1; | ||
61 | log2n=(int)Math.rint(Math.log(n)/Math.log(2)); | ||
62 | this.n=n; | ||
63 | |||
64 | int AE=0; | ||
65 | int AO=1; | ||
66 | int BE=AE+n/2; | ||
67 | int BO=BE+1; | ||
68 | int CE=BE+n/2; | ||
69 | int CO=CE+1; | ||
70 | // trig lookups... | ||
71 | for(int i=0;i<n/4;i++){ | ||
72 | trig[AE+i*2]=(float)Math.cos((Math.PI/n)*(4*i)); | ||
73 | trig[AO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i)); | ||
74 | trig[BE+i*2]=(float)Math.cos((Math.PI/(2*n))*(2*i+1)); | ||
75 | trig[BO+i*2]=(float)Math.sin((Math.PI/(2*n))*(2*i+1)); | ||
76 | } | ||
77 | for(int i=0;i<n/8;i++){ | ||
78 | trig[CE+i*2]=(float)Math.cos((Math.PI/n)*(4*i+2)); | ||
79 | trig[CO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i+2)); | ||
80 | } | ||
81 | |||
82 | { | ||
83 | int mask=(1<<(log2n-1))-1; | ||
84 | int msb=1<<(log2n-2); | ||
85 | for(int i=0;i<n/8;i++){ | ||
86 | int acc=0; | ||
87 | for(int j=0;msb>>>j!=0;j++) | ||
88 | if(((msb>>>j)&i)!=0)acc|=1<<j; | ||
89 | bitrev[i*2]=((~acc)&mask); | ||
90 | // bitrev[i*2]=((~acc)&mask)-1; | ||
91 | bitrev[i*2+1]=acc; | ||
92 | } | ||
93 | } | ||
94 | scale=4.f/n; | ||
95 | } | ||
96 | |||
97 | //void clear(){ | ||
98 | //} | ||
99 | |||
100 | //void forward(float[] in, float[] out){ | ||
101 | //} | ||
102 | |||
103 | private float[] _x=new float[1024]; | ||
104 | private float[] _w=new float[1024]; | ||
105 | |||
106 | protected void setEqualizer(float[] equalizer) { | ||
107 | this.equalizer=equalizer; | ||
108 | } | ||
109 | |||
110 | protected float[] getEqualizer() { | ||
111 | return equalizer; | ||
112 | } | ||
113 | |||
114 | protected synchronized void imdct(final float[] frq, final float[] window, final int[] pcm) {//, float[] out){ | ||
115 | |||
116 | float[] in=frq;//, out=buf; | ||
117 | if(_x.length<n/2){_x=new float[n/2];} | ||
118 | if(_w.length<n/2){_w=new float[n/2];} | ||
119 | final float[] x=_x; | ||
120 | final float[] w=_w; | ||
121 | int n2=n>>1; | ||
122 | int n4=n>>2; | ||
123 | int n8=n>>3; | ||
124 | |||
125 | if(equalizer!=null) { | ||
126 | for(int i=0; i<n; i++) { | ||
127 | frq[i]*=equalizer[i]; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | // rotate + step 1 | ||
132 | { | ||
133 | int inO=-1; | ||
134 | int xO=0; | ||
135 | int A=n2; | ||
136 | |||
137 | int i; | ||
138 | for(i=0;i<n8;i++) { | ||
139 | dtmp1=in[inO+=2]; | ||
140 | dtmp2=in[inO+=2]; | ||
141 | dtmp3=trig[--A]; | ||
142 | dtmp4=trig[--A]; | ||
143 | x[xO++]=-dtmp2*dtmp3 - dtmp1*dtmp4; | ||
144 | x[xO++]= dtmp1*dtmp3 - dtmp2*dtmp4; | ||
145 | //A-=2; | ||
146 | //x[xO++]=-in[inO+2]*trig[A+1] - in[inO]*trig[A]; | ||
147 | //x[xO++]= in[inO]*trig[A+1] - in[inO+2]*trig[A]; | ||
148 | //inO+=4; | ||
149 | } | ||
150 | |||
151 | inO=n2;//-4; | ||
152 | |||
153 | for(i=0;i<n8;i++) { | ||
154 | dtmp1=in[inO-=2]; | ||
155 | dtmp2=in[inO-=2]; | ||
156 | dtmp3=trig[--A]; | ||
157 | dtmp4=trig[--A]; | ||
158 | x[xO++]=dtmp2*dtmp3 + dtmp1*dtmp4; | ||
159 | x[xO++]=dtmp2*dtmp4 - dtmp1*dtmp3; | ||
160 | //A-=2; | ||
161 | //x[xO++]=in[inO]*trig[A+1] + in[inO+2]*trig[A]; | ||
162 | //x[xO++]=in[inO]*trig[A] - in[inO+2]*trig[A+1]; | ||
163 | //inO-=4; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | float[] xxx=kernel(x,w,n,n2,n4,n8); | ||
168 | int xx=0; | ||
169 | |||
170 | // step 8 | ||
171 | |||
172 | { | ||
173 | int B=n2; | ||
174 | int o1=n4,o2=o1-1; | ||
175 | int o3=n4+n2,o4=o3-1; | ||
176 | |||
177 | for(int i=0;i<n4;i++){ | ||
178 | dtmp1=xxx[xx++]; | ||
179 | dtmp2=xxx[xx++]; | ||
180 | dtmp3=trig[B++]; | ||
181 | dtmp4=trig[B++]; | ||
182 | |||
183 | float temp1= (dtmp1* dtmp4 - dtmp2 * dtmp3); | ||
184 | float temp2=-(dtmp1 * dtmp3 + dtmp2 * dtmp4); | ||
185 | |||
186 | /* | ||
187 | float temp1= (xxx[xx] * trig[B+1] - xxx[xx+1] * trig[B]);//*32767.0f; | ||
188 | float temp2=-(xxx[xx] * trig[B] + xxx[xx+1] * trig[B+1]);//*32767.0f; | ||
189 | */ | ||
190 | |||
191 | //if(temp1>32767.0f) temp1=32767.0f; | ||
192 | //if(temp1<-32768.0f) temp1=-32768.0f; | ||
193 | //if(temp2>32767.0f) temp2=32767.0f; | ||
194 | //if(temp2<-32768.0f) temp2=-32768.0f; | ||
195 | |||
196 | pcm[o1]=(int)(-temp1*window[o1]); | ||
197 | pcm[o2]=(int)( temp1*window[o2]); | ||
198 | pcm[o3]=(int)( temp2*window[o3]); | ||
199 | pcm[o4]=(int)( temp2*window[o4]); | ||
200 | |||
201 | o1++; | ||
202 | o2--; | ||
203 | o3++; | ||
204 | o4--; | ||
205 | //xx+=2; | ||
206 | //B+=2; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | private float[] kernel(float[] x, float[] w, | ||
212 | int n, int n2, int n4, int n8){ | ||
213 | // step 2 | ||
214 | |||
215 | int xA=n4; | ||
216 | int xB=0; | ||
217 | int w2=n4; | ||
218 | int A=n2; | ||
219 | |||
220 | for(int i=0;i<n4;){ | ||
221 | float x0=x[xA] - x[xB]; | ||
222 | float x1; | ||
223 | w[w2+i]=x[xA++]+x[xB++]; | ||
224 | |||
225 | x1=x[xA]-x[xB]; | ||
226 | A-=4; | ||
227 | |||
228 | w[i++]= x0 * trig[A] + x1 * trig[A+1]; | ||
229 | w[i]= x1 * trig[A] - x0 * trig[A+1]; | ||
230 | |||
231 | w[w2+i]=x[xA++]+x[xB++]; | ||
232 | i++; | ||
233 | } | ||
234 | |||
235 | // step 3 | ||
236 | |||
237 | { | ||
238 | for(int i=0;i<log2n-3;i++){ | ||
239 | int k0=n>>>(i+2); | ||
240 | int k1=1<<(i+3); | ||
241 | int wbase=n2-2; | ||
242 | |||
243 | A=0; | ||
244 | float[] temp; | ||
245 | |||
246 | for(int r=0;r<(k0>>>2);r++){ | ||
247 | int w1=wbase; | ||
248 | w2=w1-(k0>>1); | ||
249 | float AEv= trig[A],wA; | ||
250 | float AOv= trig[A+1],wB; | ||
251 | wbase-=2; | ||
252 | |||
253 | k0++; | ||
254 | for(int s=0;s<(2<<i);s++){ | ||
255 | dtmp1=w[w1]; | ||
256 | dtmp2=w[w2]; | ||
257 | wB=dtmp1-dtmp2; | ||
258 | x[w1]=dtmp1+dtmp2; | ||
259 | dtmp1=w[++w1]; | ||
260 | dtmp2=w[++w2]; | ||
261 | wA=dtmp1-dtmp2; | ||
262 | x[w1]=dtmp1+dtmp2; | ||
263 | x[w2] =wA*AEv - wB*AOv; | ||
264 | x[w2-1]=wB*AEv + wA*AOv; | ||
265 | |||
266 | /* | ||
267 | wB =w[w1] -w[w2]; | ||
268 | x[w1] =w[w1] +w[w2]; | ||
269 | |||
270 | wA =w[++w1] -w[++w2]; | ||
271 | x[w1] =w[w1] +w[w2]; | ||
272 | |||
273 | x[w2] =wA*AEv - wB*AOv; | ||
274 | x[w2-1]=wB*AEv + wA*AOv; | ||
275 | */ | ||
276 | |||
277 | w1-=k0; | ||
278 | w2-=k0; | ||
279 | } | ||
280 | k0--; | ||
281 | A+=k1; | ||
282 | } | ||
283 | |||
284 | temp=w; | ||
285 | w=x; | ||
286 | x=temp; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | // step 4, 5, 6, 7 | ||
291 | { | ||
292 | int C=n; | ||
293 | int bit=0; | ||
294 | int x1=0; | ||
295 | int x2=n2-1; | ||
296 | |||
297 | for(int i=0;i<n8;i++) { | ||
298 | int t1=bitrev[bit++]; | ||
299 | int t2=bitrev[bit++]; | ||
300 | |||
301 | float wA=w[t1]-w[t2+1]; | ||
302 | float wB=w[t1-1]+w[t2]; | ||
303 | float wC=w[t1]+w[t2+1]; | ||
304 | float wD=w[t1-1]-w[t2]; | ||
305 | |||
306 | float wACE=wA* trig[C]; | ||
307 | float wBCE=wB* trig[C++]; | ||
308 | float wACO=wA* trig[C]; | ||
309 | float wBCO=wB* trig[C++]; | ||
310 | |||
311 | x[x1++]=( wC+wACO+wBCE)*16383.0f; | ||
312 | x[x2--]=(-wD+wBCO-wACE)*16383.0f; | ||
313 | x[x1++]=( wD+wBCO-wACE)*16383.0f; | ||
314 | x[x2--]=( wC-wACO-wBCE)*16383.0f; | ||
315 | } | ||
316 | } | ||
317 | return x; | ||
318 | } | ||
319 | |||
320 | } | ||
321 | |||
diff --git a/songdbj/de/jarnbjo/vorbis/Mode.java b/songdbj/de/jarnbjo/vorbis/Mode.java new file mode 100644 index 0000000000..ab88944a25 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Mode.java | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.*; | ||
33 | |||
34 | import de.jarnbjo.util.io.*; | ||
35 | |||
36 | class Mode { | ||
37 | |||
38 | private boolean blockFlag; | ||
39 | private int windowType, transformType, mapping; | ||
40 | |||
41 | protected Mode(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
42 | blockFlag=source.getBit(); | ||
43 | windowType=source.getInt(16); | ||
44 | transformType=source.getInt(16); | ||
45 | mapping=source.getInt(8); | ||
46 | |||
47 | if(windowType!=0) { | ||
48 | throw new VorbisFormatException("Window type = "+windowType+", != 0"); | ||
49 | } | ||
50 | |||
51 | if(transformType!=0) { | ||
52 | throw new VorbisFormatException("Transform type = "+transformType+", != 0"); | ||
53 | } | ||
54 | |||
55 | if(mapping>header.getMappings().length) { | ||
56 | throw new VorbisFormatException("Mode mapping number is higher than total number of mappings."); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | protected boolean getBlockFlag() { | ||
61 | return blockFlag; | ||
62 | } | ||
63 | |||
64 | protected int getWindowType() { | ||
65 | return windowType; | ||
66 | } | ||
67 | |||
68 | protected int getTransformType() { | ||
69 | return transformType; | ||
70 | } | ||
71 | |||
72 | protected int getMapping() { | ||
73 | return mapping; | ||
74 | } | ||
75 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Residue.java b/songdbj/de/jarnbjo/vorbis/Residue.java new file mode 100644 index 0000000000..78c28fa5ed --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Residue.java | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/04 08:33:02 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | import java.util.HashMap; | ||
37 | |||
38 | import de.jarnbjo.util.io.*; | ||
39 | |||
40 | |||
41 | abstract class Residue { | ||
42 | |||
43 | protected int begin, end; | ||
44 | protected int partitionSize; // grouping | ||
45 | protected int classifications; // partitions | ||
46 | protected int classBook; // groupbook | ||
47 | protected int[] cascade; // secondstages | ||
48 | protected int[][] books; | ||
49 | protected HashMap looks=new HashMap(); | ||
50 | |||
51 | protected Residue() { | ||
52 | } | ||
53 | |||
54 | protected Residue(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
55 | begin=source.getInt(24); | ||
56 | end=source.getInt(24); | ||
57 | partitionSize=source.getInt(24)+1; | ||
58 | classifications=source.getInt(6)+1; | ||
59 | classBook=source.getInt(8); | ||
60 | |||
61 | cascade=new int[classifications]; | ||
62 | |||
63 | int acc=0; | ||
64 | |||
65 | for(int i=0; i<classifications; i++) { | ||
66 | int highBits=0, lowBits=0; | ||
67 | lowBits=source.getInt(3); | ||
68 | if(source.getBit()) { | ||
69 | highBits=source.getInt(5); | ||
70 | } | ||
71 | cascade[i]=(highBits<<3)|lowBits; | ||
72 | acc+=Util.icount(cascade[i]); | ||
73 | } | ||
74 | |||
75 | books=new int[classifications][8]; | ||
76 | |||
77 | for(int i=0; i<classifications; i++) { | ||
78 | for(int j=0; j<8; j++) { | ||
79 | if((cascade[i]&(1<<j))!=0) { | ||
80 | books[i][j]=source.getInt(8); | ||
81 | if(books[i][j]>header.getCodeBooks().length) { | ||
82 | throw new VorbisFormatException("Reference to invalid codebook entry in residue header."); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | |||
90 | protected static Residue createInstance(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
91 | |||
92 | int type=source.getInt(16); | ||
93 | switch(type) { | ||
94 | case 0: | ||
95 | //System.out.println("residue type 0"); | ||
96 | return new Residue0(source, header); | ||
97 | case 1: | ||
98 | //System.out.println("residue type 1"); | ||
99 | return new Residue2(source, header); | ||
100 | case 2: | ||
101 | //System.out.println("residue type 2"); | ||
102 | return new Residue2(source, header); | ||
103 | default: | ||
104 | throw new VorbisFormatException("Residue type "+type+" is not supported."); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | protected abstract int getType(); | ||
109 | protected abstract void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException; | ||
110 | //public abstract double[][] getDecodedVectors(); | ||
111 | |||
112 | protected int getBegin() { | ||
113 | return begin; | ||
114 | } | ||
115 | |||
116 | protected int getEnd() { | ||
117 | return end; | ||
118 | } | ||
119 | |||
120 | protected int getPartitionSize() { | ||
121 | return partitionSize; | ||
122 | } | ||
123 | |||
124 | protected int getClassifications() { | ||
125 | return classifications; | ||
126 | } | ||
127 | |||
128 | protected int getClassBook() { | ||
129 | return classBook; | ||
130 | } | ||
131 | |||
132 | protected int[] getCascade() { | ||
133 | return cascade; | ||
134 | } | ||
135 | |||
136 | protected int[][] getBooks() { | ||
137 | return books; | ||
138 | } | ||
139 | |||
140 | protected final void fill(Residue clone) { | ||
141 | clone.begin=begin; | ||
142 | clone.books=books; | ||
143 | clone.cascade=cascade; | ||
144 | clone.classBook=classBook; | ||
145 | clone.classifications=classifications; | ||
146 | clone.end=end; | ||
147 | clone.partitionSize=partitionSize; | ||
148 | } | ||
149 | |||
150 | protected Look getLook(VorbisStream source, Mode key) { | ||
151 | //return new Look(source, key); | ||
152 | Look look=(Look)looks.get(key); | ||
153 | if(look==null) { | ||
154 | look=new Look(source, key); | ||
155 | looks.put(key, look); | ||
156 | } | ||
157 | return look; | ||
158 | } | ||
159 | |||
160 | |||
161 | class Look { | ||
162 | int map; | ||
163 | int parts; | ||
164 | int stages; | ||
165 | CodeBook[] fullbooks; | ||
166 | CodeBook phrasebook; | ||
167 | int[][] partbooks; | ||
168 | int partvals; | ||
169 | int[][] decodemap; | ||
170 | int postbits; | ||
171 | int phrasebits; | ||
172 | int frames; | ||
173 | |||
174 | protected Look (VorbisStream source, Mode mode) { | ||
175 | int dim=0, acc=0, maxstage=0; | ||
176 | |||
177 | map=mode.getMapping(); | ||
178 | parts=Residue.this.getClassifications(); | ||
179 | fullbooks=source.getSetupHeader().getCodeBooks(); | ||
180 | phrasebook=fullbooks[Residue.this.getClassBook()]; | ||
181 | dim=phrasebook.getDimensions(); | ||
182 | |||
183 | partbooks=new int[parts][]; | ||
184 | |||
185 | for(int j=0;j<parts;j++) { | ||
186 | int stages=Util.ilog(Residue.this.getCascade()[j]); | ||
187 | if(stages!=0) { | ||
188 | if(stages>maxstage) { | ||
189 | maxstage=stages; | ||
190 | } | ||
191 | partbooks[j]=new int[stages]; | ||
192 | for(int k=0; k<stages; k++){ | ||
193 | if((Residue.this.getCascade()[j]&(1<<k))!=0){ | ||
194 | partbooks[j][k]=Residue.this.getBooks()[j][k]; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | partvals=(int)Math.rint(Math.pow(parts, dim)); | ||
201 | stages=maxstage; | ||
202 | |||
203 | decodemap=new int[partvals][]; | ||
204 | |||
205 | for(int j=0;j<partvals;j++){ | ||
206 | int val=j; | ||
207 | int mult=partvals/parts; | ||
208 | decodemap[j]=new int[dim]; | ||
209 | |||
210 | for(int k=0;k<dim;k++){ | ||
211 | int deco=val/mult; | ||
212 | val-=deco*mult; | ||
213 | mult/=parts; | ||
214 | decodemap[j][k]=deco; | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | |||
219 | protected int[][] getDecodeMap() { | ||
220 | return decodemap; | ||
221 | } | ||
222 | |||
223 | protected int getFrames() { | ||
224 | return frames; | ||
225 | } | ||
226 | |||
227 | protected int getMap() { | ||
228 | return map; | ||
229 | } | ||
230 | |||
231 | protected int[][] getPartBooks() { | ||
232 | return partbooks; | ||
233 | } | ||
234 | |||
235 | protected int getParts() { | ||
236 | return parts; | ||
237 | } | ||
238 | |||
239 | protected int getPartVals() { | ||
240 | return partvals; | ||
241 | } | ||
242 | |||
243 | protected int getPhraseBits() { | ||
244 | return phrasebits; | ||
245 | } | ||
246 | |||
247 | protected CodeBook getPhraseBook() { | ||
248 | return phrasebook; | ||
249 | } | ||
250 | |||
251 | protected int getPostBits() { | ||
252 | return postbits; | ||
253 | } | ||
254 | |||
255 | protected int getStages() { | ||
256 | return stages; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Residue0.java b/songdbj/de/jarnbjo/vorbis/Residue0.java new file mode 100644 index 0000000000..7dc0dfa765 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Residue0.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | import de.jarnbjo.util.io.BitInputStream; | ||
38 | |||
39 | class Residue0 extends Residue { | ||
40 | |||
41 | protected Residue0(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
42 | super(source, header); | ||
43 | } | ||
44 | |||
45 | protected int getType() { | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException { | ||
50 | /** @todo implement */ | ||
51 | throw new UnsupportedOperationException(); | ||
52 | } | ||
53 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Residue1.java b/songdbj/de/jarnbjo/vorbis/Residue1.java new file mode 100644 index 0000000000..f31147c072 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Residue1.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | import de.jarnbjo.util.io.BitInputStream; | ||
38 | |||
39 | class Residue1 extends Residue { | ||
40 | |||
41 | protected Residue1(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
42 | super(source, header); | ||
43 | } | ||
44 | |||
45 | protected int getType() { | ||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException { | ||
50 | /** @todo implement */ | ||
51 | throw new UnsupportedOperationException(); | ||
52 | } | ||
53 | |||
54 | |||
55 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Residue2.java b/songdbj/de/jarnbjo/vorbis/Residue2.java new file mode 100644 index 0000000000..666d2cd017 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Residue2.java | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
22 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | import de.jarnbjo.util.io.BitInputStream; | ||
38 | |||
39 | class Residue2 extends Residue { | ||
40 | |||
41 | private double[][] decodedVectors; | ||
42 | |||
43 | private Residue2() { | ||
44 | } | ||
45 | |||
46 | protected Residue2(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException { | ||
47 | super(source, header); | ||
48 | } | ||
49 | |||
50 | protected int getType() { | ||
51 | return 2; | ||
52 | } | ||
53 | |||
54 | protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException { | ||
55 | |||
56 | Look look=getLook(vorbis, mode); | ||
57 | |||
58 | CodeBook codeBook=vorbis.getSetupHeader().getCodeBooks()[getClassBook()]; | ||
59 | |||
60 | int classvalsPerCodeword=codeBook.getDimensions(); | ||
61 | int nToRead=getEnd()-getBegin(); | ||
62 | int partitionsToRead=nToRead/getPartitionSize(); // partvals | ||
63 | |||
64 | int samplesPerPartition=getPartitionSize(); | ||
65 | int partitionsPerWord=look.getPhraseBook().getDimensions(); | ||
66 | |||
67 | int partWords=(partitionsToRead+partitionsPerWord-1)/partitionsPerWord; | ||
68 | |||
69 | int realCh=0; | ||
70 | for(int i=0; i<doNotDecodeFlags.length; i++) { | ||
71 | if(!doNotDecodeFlags[i]) { | ||
72 | realCh++; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | float[][] realVectors=new float[realCh][]; | ||
77 | |||
78 | realCh=0; | ||
79 | for(int i=0; i<doNotDecodeFlags.length; i++) { | ||
80 | if(!doNotDecodeFlags[i]) { | ||
81 | realVectors[realCh++]=vectors[i]; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | int[][] partword=new int[partWords][]; | ||
86 | for(int s=0;s<look.getStages();s++){ | ||
87 | for(int i=0,l=0;i<partitionsToRead;l++){ | ||
88 | if(s==0){ | ||
89 | //int temp=look.getPhraseBook().readInt(source); | ||
90 | int temp=source.getInt(look.getPhraseBook().getHuffmanRoot()); | ||
91 | if(temp==-1){ | ||
92 | throw new VorbisFormatException(""); | ||
93 | } | ||
94 | partword[l]=look.getDecodeMap()[temp]; | ||
95 | if(partword[l]==null){ | ||
96 | throw new VorbisFormatException(""); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | for(int k=0;k<partitionsPerWord && i<partitionsToRead;k++,i++){ | ||
101 | int offset=begin+i*samplesPerPartition; | ||
102 | if((cascade[partword[l][k]]&(1<<s))!=0){ | ||
103 | CodeBook stagebook=vorbis.getSetupHeader().getCodeBooks()[look.getPartBooks()[partword[l][k]][s]]; | ||
104 | if(stagebook!=null){ | ||
105 | stagebook.readVvAdd(realVectors, source, offset, samplesPerPartition); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | |||
114 | public Object clone() { | ||
115 | Residue2 clone=new Residue2(); | ||
116 | fill(clone); | ||
117 | return clone; | ||
118 | } | ||
119 | |||
120 | protected double[][] getDecodedVectors() { | ||
121 | return decodedVectors; | ||
122 | } | ||
123 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/SetupHeader.java b/songdbj/de/jarnbjo/vorbis/SetupHeader.java new file mode 100644 index 0000000000..56e400f348 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/SetupHeader.java | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | package de.jarnbjo.vorbis; | ||
31 | |||
32 | import java.io.*; | ||
33 | |||
34 | import de.jarnbjo.util.io.*; | ||
35 | |||
36 | class SetupHeader { | ||
37 | |||
38 | private static final long HEADER = 0x736962726f76L; // 'vorbis' | ||
39 | |||
40 | private CodeBook[] codeBooks; | ||
41 | private Floor[] floors; | ||
42 | private Residue[] residues; | ||
43 | private Mapping[] mappings; | ||
44 | private Mode[] modes; | ||
45 | |||
46 | public SetupHeader(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException { | ||
47 | |||
48 | if(source.getLong(48)!=HEADER) { | ||
49 | throw new VorbisFormatException("The setup header has an illegal leading."); | ||
50 | } | ||
51 | |||
52 | // read code books | ||
53 | |||
54 | int codeBookCount=source.getInt(8)+1; | ||
55 | codeBooks=new CodeBook[codeBookCount]; | ||
56 | |||
57 | for(int i=0; i<codeBooks.length; i++) { | ||
58 | codeBooks[i]=new CodeBook(source); | ||
59 | } | ||
60 | |||
61 | // read the time domain transformations, | ||
62 | // these should all be 0 | ||
63 | |||
64 | int timeCount=source.getInt(6)+1; | ||
65 | for(int i=0; i<timeCount; i++) { | ||
66 | if(source.getInt(16)!=0) { | ||
67 | throw new VorbisFormatException("Time domain transformation != 0"); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | // read floor entries | ||
72 | |||
73 | int floorCount=source.getInt(6)+1; | ||
74 | floors=new Floor[floorCount]; | ||
75 | |||
76 | for(int i=0; i<floorCount; i++) { | ||
77 | floors[i]=Floor.createInstance(source, this); | ||
78 | } | ||
79 | |||
80 | // read residue entries | ||
81 | |||
82 | int residueCount=source.getInt(6)+1; | ||
83 | residues=new Residue[residueCount]; | ||
84 | |||
85 | for(int i=0; i<residueCount; i++) { | ||
86 | residues[i]=Residue.createInstance(source, this); | ||
87 | } | ||
88 | |||
89 | // read mapping entries | ||
90 | |||
91 | int mappingCount=source.getInt(6)+1; | ||
92 | mappings=new Mapping[mappingCount]; | ||
93 | |||
94 | for(int i=0; i<mappingCount; i++) { | ||
95 | mappings[i]=Mapping.createInstance(vorbis, source, this); | ||
96 | } | ||
97 | |||
98 | // read mode entries | ||
99 | |||
100 | int modeCount=source.getInt(6)+1; | ||
101 | modes=new Mode[modeCount]; | ||
102 | |||
103 | for(int i=0; i<modeCount; i++) { | ||
104 | modes[i]=new Mode(source, this); | ||
105 | } | ||
106 | |||
107 | if(!source.getBit()) { | ||
108 | throw new VorbisFormatException("The setup header framing bit is incorrect."); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | public CodeBook[] getCodeBooks() { | ||
113 | return codeBooks; | ||
114 | } | ||
115 | |||
116 | public Floor[] getFloors() { | ||
117 | return floors; | ||
118 | } | ||
119 | |||
120 | public Residue[] getResidues() { | ||
121 | return residues; | ||
122 | } | ||
123 | |||
124 | public Mapping[] getMappings() { | ||
125 | return mappings; | ||
126 | } | ||
127 | |||
128 | public Mode[] getModes() { | ||
129 | return modes; | ||
130 | } | ||
131 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/Util.java b/songdbj/de/jarnbjo/vorbis/Util.java new file mode 100644 index 0000000000..7e31c9495e --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/Util.java | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.3 2003/04/10 19:49:04 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | final public class Util { | ||
36 | |||
37 | public static final int ilog(int x) { | ||
38 | int res=0; | ||
39 | for(; x>0; x>>=1, res++); | ||
40 | return res; | ||
41 | } | ||
42 | |||
43 | public static final float float32unpack(int x) { | ||
44 | float mantissa=x&0x1fffff; | ||
45 | float e=(x&0x7fe00000)>>21; | ||
46 | if((x&0x80000000)!=0) { | ||
47 | mantissa=-mantissa; | ||
48 | } | ||
49 | return mantissa*(float)Math.pow(2.0, e-788.0); | ||
50 | } | ||
51 | |||
52 | public static final int lookup1Values(int a, int b) { | ||
53 | int res=(int)Math.pow(Math.E, Math.log(a)/b); | ||
54 | return intPow(res+1, b)<=a?res+1:res; | ||
55 | } | ||
56 | |||
57 | public static final int intPow(int base, int e) { | ||
58 | int res=1; | ||
59 | for(; e>0; e--, res*=base); | ||
60 | return res; | ||
61 | } | ||
62 | |||
63 | public static final boolean isBitSet(int value, int bit) { | ||
64 | return (value&(1<<bit))!=0; | ||
65 | } | ||
66 | |||
67 | public static final int icount(int value) { | ||
68 | int res=0; | ||
69 | while(value>0) { | ||
70 | res+=value&1; | ||
71 | value>>=1; | ||
72 | } | ||
73 | return res; | ||
74 | } | ||
75 | |||
76 | public static final int lowNeighbour(int[] v, int x) { | ||
77 | int max=-1, n=0; | ||
78 | for(int i=0; i<v.length && i<x; i++) { | ||
79 | if(v[i]>max && v[i]<v[x]) { | ||
80 | max=v[i]; | ||
81 | n=i; | ||
82 | } | ||
83 | } | ||
84 | return n; | ||
85 | } | ||
86 | |||
87 | public static final int highNeighbour(int[] v, int x) { | ||
88 | int min=Integer.MAX_VALUE, n=0; | ||
89 | for(int i=0; i<v.length && i<x; i++) { | ||
90 | if(v[i]<min && v[i]>v[x]) { | ||
91 | min=v[i]; | ||
92 | n=i; | ||
93 | } | ||
94 | } | ||
95 | return n; | ||
96 | } | ||
97 | |||
98 | public static final int renderPoint(int x0, int x1, int y0, int y1, int x) { | ||
99 | int dy=y1-y0; | ||
100 | int ady=dy<0?-dy:dy; | ||
101 | int off=(ady*(x-x0))/(x1-x0); | ||
102 | return dy<0?y0-off:y0+off; | ||
103 | } | ||
104 | |||
105 | public static final void renderLine(final int x0, final int y0, final int x1, final int y1, final float[] v) { | ||
106 | final int dy=y1-y0; | ||
107 | final int adx=x1-x0; | ||
108 | final int base=dy/adx; | ||
109 | final int sy=dy<0?base-1:base+1; | ||
110 | int x=x0; | ||
111 | int y=y0; | ||
112 | int err=0; | ||
113 | final int ady=(dy<0?-dy:dy)-(base>0?base*adx:-base*adx); | ||
114 | |||
115 | v[x]*=Floor.DB_STATIC_TABLE[y]; | ||
116 | for(x=x0+1; x<x1; x++) { | ||
117 | err+=ady; | ||
118 | if(err>=adx) { | ||
119 | err-=adx; | ||
120 | v[x]*=Floor.DB_STATIC_TABLE[y+=sy]; | ||
121 | } | ||
122 | else { | ||
123 | v[x]*=Floor.DB_STATIC_TABLE[y+=base]; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java b/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java new file mode 100644 index 0000000000..b1bc999947 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/VorbisAudioFileReader.java | |||
@@ -0,0 +1,217 @@ | |||
1 | package de.jarnbjo.vorbis; | ||
2 | |||
3 | /* | ||
4 | * $ProjectName$ | ||
5 | * $ProjectRevision$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * $Id$ | ||
8 | * ----------------------------------------------------------- | ||
9 | * | ||
10 | * $Author$ | ||
11 | * | ||
12 | * Description: | ||
13 | * | ||
14 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
15 | * ----------------------------------------------------------- | ||
16 | * | ||
17 | * Change History | ||
18 | * ----------------------------------------------------------- | ||
19 | * $Log$ | ||
20 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
21 | * Songdb java version, source. only 1.5 compatible | ||
22 | * | ||
23 | * Revision 1.2 2004/09/21 06:39:06 shred | ||
24 | * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) | ||
25 | * | ||
26 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
27 | * First Import | ||
28 | * | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | import java.io.File; | ||
33 | import java.io.IOException; | ||
34 | import java.io.InputStream; | ||
35 | import java.io.RandomAccessFile; | ||
36 | import java.net.URL; | ||
37 | import java.util.Collection; | ||
38 | |||
39 | import javax.sound.sampled.AudioFileFormat; | ||
40 | import javax.sound.sampled.AudioFormat; | ||
41 | import javax.sound.sampled.AudioInputStream; | ||
42 | import javax.sound.sampled.AudioSystem; | ||
43 | import javax.sound.sampled.UnsupportedAudioFileException; | ||
44 | import javax.sound.sampled.spi.AudioFileReader; | ||
45 | |||
46 | import de.jarnbjo.ogg.BasicStream; | ||
47 | import de.jarnbjo.ogg.EndOfOggStreamException; | ||
48 | import de.jarnbjo.ogg.FileStream; | ||
49 | import de.jarnbjo.ogg.LogicalOggStream; | ||
50 | import de.jarnbjo.ogg.OggFormatException; | ||
51 | import de.jarnbjo.ogg.PhysicalOggStream; | ||
52 | import de.jarnbjo.ogg.UncachedUrlStream; | ||
53 | |||
54 | public class VorbisAudioFileReader extends AudioFileReader { | ||
55 | |||
56 | public VorbisAudioFileReader() { | ||
57 | } | ||
58 | |||
59 | public AudioFileFormat getAudioFileFormat(File file) throws IOException, UnsupportedAudioFileException { | ||
60 | try { | ||
61 | return getAudioFileFormat(new FileStream(new RandomAccessFile(file, "r"))); | ||
62 | } | ||
63 | catch(OggFormatException e) { | ||
64 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | public AudioFileFormat getAudioFileFormat(InputStream stream) throws IOException, UnsupportedAudioFileException { | ||
69 | try { | ||
70 | return getAudioFileFormat(new BasicStream(stream)); | ||
71 | } | ||
72 | catch(OggFormatException e) { | ||
73 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | public AudioFileFormat getAudioFileFormat(URL url) throws IOException, UnsupportedAudioFileException { | ||
78 | try { | ||
79 | return getAudioFileFormat(new UncachedUrlStream(url)); | ||
80 | } | ||
81 | catch(OggFormatException e) { | ||
82 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | private AudioFileFormat getAudioFileFormat(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException { | ||
87 | try { | ||
88 | Collection streams=oggStream.getLogicalStreams(); | ||
89 | if(streams.size()!=1) { | ||
90 | throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported."); | ||
91 | } | ||
92 | |||
93 | LogicalOggStream los=(LogicalOggStream)streams.iterator().next(); | ||
94 | if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) { | ||
95 | throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported."); | ||
96 | } | ||
97 | |||
98 | VorbisStream vs=new VorbisStream(los); | ||
99 | |||
100 | AudioFormat audioFormat=new AudioFormat( | ||
101 | (float)vs.getIdentificationHeader().getSampleRate(), | ||
102 | 16, | ||
103 | vs.getIdentificationHeader().getChannels(), | ||
104 | true, true); | ||
105 | |||
106 | return new AudioFileFormat(VorbisFormatType.getInstance(), audioFormat, AudioSystem.NOT_SPECIFIED); | ||
107 | } | ||
108 | catch(OggFormatException e) { | ||
109 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
110 | } | ||
111 | catch(VorbisFormatException e) { | ||
112 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | |||
117 | |||
118 | public AudioInputStream getAudioInputStream(File file) throws IOException, UnsupportedAudioFileException { | ||
119 | try { | ||
120 | return getAudioInputStream(new FileStream(new RandomAccessFile(file, "r"))); | ||
121 | } | ||
122 | catch(OggFormatException e) { | ||
123 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | public AudioInputStream getAudioInputStream(InputStream stream) throws IOException, UnsupportedAudioFileException { | ||
128 | try { | ||
129 | return getAudioInputStream(new BasicStream(stream)); | ||
130 | } | ||
131 | catch(OggFormatException e) { | ||
132 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | public AudioInputStream getAudioInputStream(URL url) throws IOException, UnsupportedAudioFileException { | ||
137 | try { | ||
138 | return getAudioInputStream(new UncachedUrlStream(url)); | ||
139 | } | ||
140 | catch(OggFormatException e) { | ||
141 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | private AudioInputStream getAudioInputStream(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException { | ||
146 | try { | ||
147 | Collection streams=oggStream.getLogicalStreams(); | ||
148 | if(streams.size()!=1) { | ||
149 | throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported."); | ||
150 | } | ||
151 | |||
152 | LogicalOggStream los=(LogicalOggStream)streams.iterator().next(); | ||
153 | if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) { | ||
154 | throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported."); | ||
155 | } | ||
156 | |||
157 | VorbisStream vs=new VorbisStream(los); | ||
158 | |||
159 | AudioFormat audioFormat=new AudioFormat( | ||
160 | (float)vs.getIdentificationHeader().getSampleRate(), | ||
161 | 16, | ||
162 | vs.getIdentificationHeader().getChannels(), | ||
163 | true, true); | ||
164 | |||
165 | return new AudioInputStream(new VorbisInputStream(vs), audioFormat, -1); | ||
166 | } | ||
167 | catch(OggFormatException e) { | ||
168 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
169 | } | ||
170 | catch(VorbisFormatException e) { | ||
171 | throw new UnsupportedAudioFileException(e.getMessage()); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | |||
176 | public static class VorbisFormatType extends AudioFileFormat.Type { | ||
177 | |||
178 | private static final VorbisFormatType instance=new VorbisFormatType(); | ||
179 | |||
180 | private VorbisFormatType() { | ||
181 | super("VORBIS", "ogg"); | ||
182 | } | ||
183 | |||
184 | public static AudioFileFormat.Type getInstance() { | ||
185 | return instance; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | public static class VorbisInputStream extends InputStream { | ||
190 | |||
191 | private VorbisStream source; | ||
192 | private byte[] buffer=new byte[8192]; | ||
193 | |||
194 | public VorbisInputStream(VorbisStream source) { | ||
195 | this.source=source; | ||
196 | } | ||
197 | |||
198 | public int read() throws IOException { | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | public int read(byte[] buffer) throws IOException { | ||
203 | return read(buffer, 0, buffer.length); | ||
204 | } | ||
205 | |||
206 | public int read(byte[] buffer, int offset, int length) throws IOException { | ||
207 | try { | ||
208 | return source.readPcm(buffer, offset, length); | ||
209 | } | ||
210 | catch(EndOfOggStreamException e) { | ||
211 | return -1; | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | |||
216 | |||
217 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java b/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java new file mode 100644 index 0000000000..5214298378 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/VorbisFormatException.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.2 2005/02/09 23:10:47 shred | ||
22 | * Serial UID für jarnbjo | ||
23 | * | ||
24 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
25 | * First Import | ||
26 | * | ||
27 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | package de.jarnbjo.vorbis; | ||
34 | |||
35 | import java.io.IOException; | ||
36 | |||
37 | /** | ||
38 | * Exception thrown when trying to read a corrupted Vorbis stream. | ||
39 | */ | ||
40 | |||
41 | public class VorbisFormatException extends IOException { | ||
42 | private static final long serialVersionUID = 3616453405694834743L; | ||
43 | |||
44 | public VorbisFormatException() { | ||
45 | super(); | ||
46 | } | ||
47 | |||
48 | public VorbisFormatException(String message) { | ||
49 | super(message); | ||
50 | } | ||
51 | } \ No newline at end of file | ||
diff --git a/songdbj/de/jarnbjo/vorbis/VorbisStream.java b/songdbj/de/jarnbjo/vorbis/VorbisStream.java new file mode 100644 index 0000000000..36659c7106 --- /dev/null +++ b/songdbj/de/jarnbjo/vorbis/VorbisStream.java | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * $ProjectName$ | ||
3 | * $ProjectRevision$ | ||
4 | * ----------------------------------------------------------- | ||
5 | * $Id$ | ||
6 | * ----------------------------------------------------------- | ||
7 | * | ||
8 | * $Author$ | ||
9 | * | ||
10 | * Description: | ||
11 | * | ||
12 | * Copyright 2002-2003 Tor-Einar Jarnbjo | ||
13 | * ----------------------------------------------------------- | ||
14 | * | ||
15 | * Change History | ||
16 | * ----------------------------------------------------------- | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2005/07/11 15:42:36 hcl | ||
19 | * Songdb java version, source. only 1.5 compatible | ||
20 | * | ||
21 | * Revision 1.1.1.1 2004/04/04 22:09:12 shred | ||
22 | * First Import | ||
23 | * | ||
24 | * Revision 1.4 2003/04/10 19:49:04 jarnbjo | ||
25 | * no message | ||
26 | * | ||
27 | * Revision 1.3 2003/03/31 00:20:16 jarnbjo | ||
28 | * no message | ||
29 | * | ||
30 | * Revision 1.2 2003/03/16 01:11:12 jarnbjo | ||
31 | * no message | ||
32 | * | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | package de.jarnbjo.vorbis; | ||
37 | |||
38 | import java.io.*; | ||
39 | import java.util.*; | ||
40 | |||
41 | import de.jarnbjo.ogg.*; | ||
42 | import de.jarnbjo.util.io.*; | ||
43 | |||
44 | /** | ||
45 | */ | ||
46 | |||
47 | public class VorbisStream { | ||
48 | |||
49 | private LogicalOggStream oggStream; | ||
50 | private IdentificationHeader identificationHeader; | ||
51 | private CommentHeader commentHeader; | ||
52 | private SetupHeader setupHeader; | ||
53 | |||
54 | private AudioPacket lastAudioPacket, nextAudioPacket; | ||
55 | private LinkedList audioPackets=new LinkedList(); | ||
56 | private byte[] currentPcm; | ||
57 | private int currentPcmIndex; | ||
58 | private int currentPcmLimit; | ||
59 | |||
60 | private static final int IDENTIFICATION_HEADER = 1; | ||
61 | private static final int COMMENT_HEADER = 3; | ||
62 | private static final int SETUP_HEADER = 5; | ||
63 | |||
64 | private int bitIndex=0; | ||
65 | private byte lastByte=(byte)0; | ||
66 | private boolean initialized=false; | ||
67 | |||
68 | private Object streamLock=new Object(); | ||
69 | private int pageCounter=0; | ||
70 | |||
71 | private int currentBitRate=0; | ||
72 | |||
73 | private long currentGranulePosition; | ||
74 | |||
75 | public static final int BIG_ENDIAN = 0; | ||
76 | public static final int LITTLE_ENDIAN = 1; | ||
77 | |||
78 | public VorbisStream() { | ||
79 | } | ||
80 | |||
81 | public VorbisStream(LogicalOggStream oggStream) throws VorbisFormatException, IOException { | ||
82 | this.oggStream=oggStream; | ||
83 | |||
84 | for(int i=0; i<3; i++) { | ||
85 | BitInputStream source=new ByteArrayBitInputStream(oggStream.getNextOggPacket()); | ||
86 | int headerType=source.getInt(8); | ||
87 | switch(headerType) { | ||
88 | case IDENTIFICATION_HEADER: | ||
89 | identificationHeader=new IdentificationHeader(source); | ||
90 | break; | ||
91 | case COMMENT_HEADER: | ||
92 | commentHeader=new CommentHeader(source); | ||
93 | break; | ||
94 | case SETUP_HEADER: | ||
95 | setupHeader=new SetupHeader(this, source); | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if(identificationHeader==null) { | ||
101 | throw new VorbisFormatException("The file has no identification header."); | ||
102 | } | ||
103 | |||
104 | if(commentHeader==null) { | ||
105 | throw new VorbisFormatException("The file has no commentHeader."); | ||
106 | } | ||
107 | |||
108 | if(setupHeader==null) { | ||
109 | throw new VorbisFormatException("The file has no setup header."); | ||
110 | } | ||
111 | |||
112 | //currentPcm=new int[identificationHeader.getChannels()][16384]; | ||
113 | currentPcm=new byte[identificationHeader.getChannels()*identificationHeader.getBlockSize1()*2]; | ||
114 | //new BufferThread().start(); | ||
115 | } | ||
116 | |||
117 | public IdentificationHeader getIdentificationHeader() { | ||
118 | return identificationHeader; | ||
119 | } | ||
120 | |||
121 | public CommentHeader getCommentHeader() { | ||
122 | return commentHeader; | ||
123 | } | ||
124 | |||
125 | protected SetupHeader getSetupHeader() { | ||
126 | return setupHeader; | ||
127 | } | ||
128 | |||
129 | public boolean isOpen() { | ||
130 | return oggStream.isOpen(); | ||
131 | } | ||
132 | |||
133 | public void close() throws IOException { | ||
134 | oggStream.close(); | ||
135 | } | ||
136 | |||
137 | |||
138 | public int readPcm(byte[] buffer, int offset, int length) throws IOException { | ||
139 | synchronized (streamLock) { | ||
140 | final int channels=identificationHeader.getChannels(); | ||
141 | |||
142 | if(lastAudioPacket==null) { | ||
143 | lastAudioPacket=getNextAudioPacket(); | ||
144 | } | ||
145 | if(currentPcm==null || currentPcmIndex>=currentPcmLimit) { | ||
146 | AudioPacket ap=getNextAudioPacket(); | ||
147 | try { | ||
148 | ap.getPcm(lastAudioPacket, currentPcm); | ||
149 | currentPcmLimit=ap.getNumberOfSamples()*identificationHeader.getChannels()*2; | ||
150 | } | ||
151 | catch(ArrayIndexOutOfBoundsException e) { | ||
152 | return 0; | ||
153 | } | ||
154 | currentPcmIndex=0; | ||
155 | lastAudioPacket=ap; | ||
156 | } | ||
157 | int written=0; | ||
158 | int i=0; | ||
159 | int arrIx=0; | ||
160 | for(i=currentPcmIndex; i<currentPcmLimit && arrIx<length; i++) { | ||
161 | buffer[offset+arrIx++]=currentPcm[i]; | ||
162 | written++; | ||
163 | } | ||
164 | currentPcmIndex=i; | ||
165 | return written; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | |||
170 | private AudioPacket getNextAudioPacket() throws VorbisFormatException, IOException { | ||
171 | pageCounter++; | ||
172 | byte[] data=oggStream.getNextOggPacket(); | ||
173 | AudioPacket res=null; | ||
174 | while(res==null) { | ||
175 | try { | ||
176 | res=new AudioPacket(this, new ByteArrayBitInputStream(data)); | ||
177 | } | ||
178 | catch(ArrayIndexOutOfBoundsException e) { | ||
179 | // ignore and continue with next packet | ||
180 | } | ||
181 | } | ||
182 | currentGranulePosition+=res.getNumberOfSamples(); | ||
183 | currentBitRate=data.length*8*identificationHeader.getSampleRate()/res.getNumberOfSamples(); | ||
184 | return res; | ||
185 | } | ||
186 | |||
187 | public long getCurrentGranulePosition() { | ||
188 | return currentGranulePosition; | ||
189 | } | ||
190 | |||
191 | public int getCurrentBitRate() { | ||
192 | return currentBitRate; | ||
193 | } | ||
194 | |||
195 | public byte[] processPacket(byte[] packet) throws VorbisFormatException, IOException { | ||
196 | if(packet.length==0) { | ||
197 | throw new VorbisFormatException("Cannot decode a vorbis packet with length = 0"); | ||
198 | } | ||
199 | if(((int)packet[0]&1)==1) { | ||
200 | // header packet | ||
201 | BitInputStream source=new ByteArrayBitInputStream(packet); | ||
202 | switch(source.getInt(8)) { | ||
203 | case IDENTIFICATION_HEADER: | ||
204 | identificationHeader=new IdentificationHeader(source); | ||
205 | break; | ||
206 | case COMMENT_HEADER: | ||
207 | commentHeader=new CommentHeader(source); | ||
208 | break; | ||
209 | case SETUP_HEADER: | ||
210 | setupHeader=new SetupHeader(this, source); | ||
211 | break; | ||
212 | } | ||
213 | return null; | ||
214 | } | ||
215 | else { | ||
216 | // audio packet | ||
217 | if(identificationHeader==null || | ||
218 | commentHeader==null || | ||
219 | setupHeader==null) { | ||
220 | |||
221 | throw new VorbisFormatException("Cannot decode audio packet before all three header packets have been decoded."); | ||
222 | } | ||
223 | |||
224 | AudioPacket ap=new AudioPacket(this, new ByteArrayBitInputStream(packet)); | ||
225 | currentGranulePosition+=ap.getNumberOfSamples(); | ||
226 | |||
227 | if(lastAudioPacket==null) { | ||
228 | lastAudioPacket=ap; | ||
229 | return null; | ||
230 | } | ||
231 | |||
232 | byte[] res=new byte[identificationHeader.getChannels()*ap.getNumberOfSamples()*2]; | ||
233 | |||
234 | try { | ||
235 | ap.getPcm(lastAudioPacket, res); | ||
236 | } | ||
237 | catch(IndexOutOfBoundsException e) { | ||
238 | java.util.Arrays.fill(res, (byte)0); | ||
239 | } | ||
240 | |||
241 | lastAudioPacket=ap; | ||
242 | |||
243 | return res; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | } \ No newline at end of file | ||