diff options
author | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-11 15:42:37 +0000 |
---|---|---|
committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-11 15:42:37 +0000 |
commit | 9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e (patch) | |
tree | 4c304cd4151020bd5494d279ee68a105ae3a5a3a /songdbj/net/shredzone/ifish | |
parent | dfa8ecbe609ca8ea194d08560a44fb9a92e94b4b (diff) | |
download | rockbox-9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e.tar.gz rockbox-9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e.zip |
Songdb java version, source. only 1.5 compatible
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'songdbj/net/shredzone/ifish')
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java | 66 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/LTR.java | 251 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/LTRmp3.java | 165 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java | 249 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/TagAsf.java | 170 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/TagMp3v1.java | 184 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/TagMp3v2.java | 441 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/TagMp3v200.java | 373 | ||||
-rw-r--r-- | songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java | 207 |
9 files changed, 2106 insertions, 0 deletions
diff --git a/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java b/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java new file mode 100644 index 0000000000..72522eaa4d --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/FormatDecodeException.java | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | /** | ||
48 | * This exception signals that the Tag could not be decoded for various | ||
49 | * reasons. | ||
50 | * | ||
51 | * @author Richard Körber <dev@shredzone.de> | ||
52 | * @version $Id$ | ||
53 | */ | ||
54 | public class FormatDecodeException extends Exception { | ||
55 | private static final long serialVersionUID = 3690758397339187507L; | ||
56 | |||
57 | /** | ||
58 | * Constructor for the FormatDecodeException object | ||
59 | * | ||
60 | * @param msg A message | ||
61 | */ | ||
62 | public FormatDecodeException( String msg ) { | ||
63 | super( msg ); | ||
64 | } | ||
65 | |||
66 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/LTR.java b/songdbj/net/shredzone/ifish/ltr/LTR.java new file mode 100644 index 0000000000..8a38676583 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/LTR.java | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | |||
49 | /** | ||
50 | * The Base Class for the Lightweight Tag Reader. LTR was made to read | ||
51 | * the basic tag information of MP3 (IDv1, IDv2), Ogg Vorbis and ASF/WMA | ||
52 | * files. It is lightweight because it is optimized for speed, and is | ||
53 | * only able to read the tag information, but unable to edit them. | ||
54 | * | ||
55 | * @author Richard Körber <dev@shredzone.de> | ||
56 | * @version $Id$ | ||
57 | */ | ||
58 | public abstract class LTR { | ||
59 | protected RandomAccessFile in; // The file to be checked | ||
60 | |||
61 | /** | ||
62 | * Create a new LTR object. It is connected to the file to be decoded, | ||
63 | * by a RandomAccessFile. The cursor position will be changed during | ||
64 | * recognizing and decoding. | ||
65 | * | ||
66 | * @param in RandomAccessFile to be used | ||
67 | * @throws FormatDecodeException Description of the Exception | ||
68 | */ | ||
69 | public LTR( RandomAccessFile in ) | ||
70 | throws FormatDecodeException { | ||
71 | this.in = in; | ||
72 | try { | ||
73 | in.seek( 0 ); // To the beginning of file | ||
74 | } catch( IOException e ) { | ||
75 | throw new FormatDecodeException( "couldn't seek: " + e.toString() ); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Create an LTR object for a file. If the file given, was not | ||
81 | * recognized or did not contain any tags, null will be returned. | ||
82 | * | ||
83 | * @param file File to open | ||
84 | * @return LTR to this file, or null | ||
85 | * @exception IOException Description of the Exception | ||
86 | */ | ||
87 | public static LTR create( File file ) { | ||
88 | RandomAccessFile in = null; | ||
89 | LTR result = null; | ||
90 | |||
91 | try { | ||
92 | in = new RandomAccessFile( file, "r" ); | ||
93 | |||
94 | try { | ||
95 | result = new TagOggVorbis( in ); | ||
96 | return result; | ||
97 | } catch( FormatDecodeException e ) {} | ||
98 | |||
99 | try { | ||
100 | result = new TagMp3v2( in ); | ||
101 | return result; | ||
102 | } catch( FormatDecodeException e ) {} | ||
103 | |||
104 | try { | ||
105 | result = new TagMp3v200( in ); | ||
106 | return result; | ||
107 | } catch( FormatDecodeException e ) {} | ||
108 | |||
109 | try { | ||
110 | result = new TagAsf( in, file ); | ||
111 | return result; | ||
112 | }catch( FormatDecodeException e ) {} | ||
113 | |||
114 | try { | ||
115 | // Always check ID3v1 *after* ID3v2, because a lot of ID3v2 | ||
116 | // files also contain ID3v1 tags with limited content. | ||
117 | result = new TagMp3v1( in ); | ||
118 | return result; | ||
119 | } catch( FormatDecodeException e ) {} | ||
120 | }catch(IOException e) { | ||
121 | return null; | ||
122 | } finally { | ||
123 | try { | ||
124 | if( in!=null ) in.close(); | ||
125 | } catch(IOException e) { | ||
126 | System.out.println("Failed to close file."); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return null; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Get the type of this file. This is usually the compression format | ||
135 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
136 | * this format, the used tag format is appended after a slash (e.g. | ||
137 | * "MP3/id3v2"). | ||
138 | * | ||
139 | * @return The type | ||
140 | */ | ||
141 | public abstract String getType(); | ||
142 | |||
143 | /** | ||
144 | * Get the artist. | ||
145 | * | ||
146 | * @return The artist (never null) | ||
147 | */ | ||
148 | public abstract String getArtist(); | ||
149 | |||
150 | /** | ||
151 | * Get the album. | ||
152 | * | ||
153 | * @return The album (never null) | ||
154 | */ | ||
155 | public abstract String getAlbum(); | ||
156 | |||
157 | /** | ||
158 | * Get the title. | ||
159 | * | ||
160 | * @return The title (never null) | ||
161 | */ | ||
162 | public abstract String getTitle(); | ||
163 | |||
164 | /** | ||
165 | * Get the genre. | ||
166 | * | ||
167 | * @return The genre (never null) | ||
168 | */ | ||
169 | public abstract String getGenre(); | ||
170 | |||
171 | /** | ||
172 | * Get the year. | ||
173 | * | ||
174 | * @return The year (never null) | ||
175 | */ | ||
176 | public abstract String getYear(); | ||
177 | |||
178 | /** | ||
179 | * Get the comment. | ||
180 | * | ||
181 | * @return The comment (never null) | ||
182 | */ | ||
183 | public abstract String getComment(); | ||
184 | |||
185 | /** | ||
186 | * Get the track. | ||
187 | * | ||
188 | * @return The track (never null) | ||
189 | */ | ||
190 | public abstract String getTrack(); | ||
191 | |||
192 | /** | ||
193 | * Read a String of a certain length from the file. It will always use | ||
194 | * "ISO-8859-1" encoding! | ||
195 | * | ||
196 | * @param length Maximum number of bytes to read | ||
197 | * @return String that was read | ||
198 | * @throws IOException If EOF was already reached | ||
199 | */ | ||
200 | protected String readStringLen( int length ) | ||
201 | throws IOException { | ||
202 | return readStringLen( length, "ISO-8859-1" ); | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Read a String of a certain length from the file. The length will | ||
207 | * not be exceeded. No null termination is required. Anyhow an | ||
208 | * IOException will be thrown if the EOF was reached before invocation. | ||
209 | * | ||
210 | * @param length Maximum number of bytes to read | ||
211 | * @param charset Charset to be used | ||
212 | * @return String that was read | ||
213 | * @throws IOException If EOF was already reached | ||
214 | */ | ||
215 | protected String readStringLen( int length, String charset ) | ||
216 | throws IOException { | ||
217 | byte[] buf = new byte[length]; | ||
218 | int readlength = in.read( buf ); | ||
219 | if( readlength < 0 ) { | ||
220 | throw new IOException( "Unexpected EOF" ); | ||
221 | } | ||
222 | return new String( buf, 0, readlength, charset ); | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * Return a string representation of the LTR content. | ||
227 | * | ||
228 | * @return String representation | ||
229 | */ | ||
230 | public String toString() { | ||
231 | StringBuffer buff = new StringBuffer(); | ||
232 | buff.append( getType() ); | ||
233 | buff.append( "[ART='" ); | ||
234 | buff.append( getArtist() ); | ||
235 | buff.append( "' ALB='" ); | ||
236 | buff.append( getAlbum() ); | ||
237 | buff.append( "' TIT='" ); | ||
238 | buff.append( getTitle() ); | ||
239 | buff.append( "' TRK='" ); | ||
240 | buff.append( getTrack() ); | ||
241 | buff.append( "' GEN='" ); | ||
242 | buff.append( getGenre() ); | ||
243 | buff.append( "' YR='" ); | ||
244 | buff.append( getYear() ); | ||
245 | buff.append( "' CMT='" ); | ||
246 | buff.append( getComment() ); | ||
247 | buff.append( "']" ); | ||
248 | return buff.toString(); | ||
249 | } | ||
250 | |||
251 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/LTRmp3.java b/songdbj/net/shredzone/ifish/ltr/LTRmp3.java new file mode 100644 index 0000000000..1b31c405ba --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/LTRmp3.java | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | |||
49 | /** | ||
50 | * The Base Class for mp3 decoding of the Lightweight Tag Reader. | ||
51 | * | ||
52 | * @author Richard Körber <dev@shredzone.de> | ||
53 | * @version $Id$ | ||
54 | */ | ||
55 | public abstract class LTRmp3 extends LTR { | ||
56 | |||
57 | private final static String[] genres = { | ||
58 | //--- Genres as specified in ID3v1 --- | ||
59 | "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", | ||
60 | "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", | ||
61 | "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", | ||
62 | "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", | ||
63 | "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", | ||
64 | "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", | ||
65 | "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", | ||
66 | "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", | ||
67 | "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", | ||
68 | "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", | ||
69 | "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", | ||
70 | "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", | ||
71 | "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", | ||
72 | "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", | ||
73 | "Rock & Roll", "Hard Rock", | ||
74 | |||
75 | //--- This are WinAmp extensions --- | ||
76 | "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebop", | ||
77 | "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", | ||
78 | "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", | ||
79 | "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", | ||
80 | "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", | ||
81 | "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", | ||
82 | "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", | ||
83 | "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", | ||
84 | }; | ||
85 | |||
86 | protected final String charsetV1 = "ISO-8859-1"; | ||
87 | protected final String charsetV2 = "ISO-8859-1"; | ||
88 | |||
89 | /** | ||
90 | * Constructor for the LTRmp3 object | ||
91 | * | ||
92 | * @param in File to be used | ||
93 | * @throws FormatDecodeException Could not decode file | ||
94 | */ | ||
95 | public LTRmp3( RandomAccessFile in ) | ||
96 | throws FormatDecodeException { | ||
97 | super( in ); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Decode the mp3 numerical Genre code and convert it to a human | ||
102 | * readable string. The genre is decoded according to the | ||
103 | * specifications found at <a href="http://www.id3.org">www.id3.org</a>, | ||
104 | * as well as the WinAmp extensions. | ||
105 | * | ||
106 | * @param id Genre ID | ||
107 | * @return ID String, null if the genre ID was unknown | ||
108 | */ | ||
109 | protected String decodeGenre( int id ) { | ||
110 | if( id>=genres.length ) return null; | ||
111 | return genres[id]; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Read an ID3v2 integer. This is a 4 byte big endian value, which is | ||
116 | * always not syncsafe. | ||
117 | * | ||
118 | * @return The integer read. | ||
119 | * @throws IOException If there were not enough bytes in the file. | ||
120 | */ | ||
121 | protected int readInt() | ||
122 | throws IOException { | ||
123 | int val = 0; | ||
124 | for( int cnt = 4; cnt > 0; cnt-- ) { | ||
125 | val <<= 8; | ||
126 | val |= ( in.readByte() & 0xFF ); | ||
127 | } | ||
128 | return val; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * Read an ID3v2 syncsafe integer. This is a 4 byte big endian value | ||
133 | * with the bit 7 of each byte always being 0. | ||
134 | * | ||
135 | * @return The syncsafe integer read. | ||
136 | * @throws IOException If there were not enough bytes in the file. | ||
137 | */ | ||
138 | protected int readSyncsafeInt() | ||
139 | throws IOException { | ||
140 | int val = 0; | ||
141 | for( int cnt = 4; cnt > 0; cnt-- ) { | ||
142 | val <<= 7; | ||
143 | val |= ( readSyncsafe() & 0x7F ); | ||
144 | } | ||
145 | return val; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Read a syncsafe byte. It is made sure that a byte is available in | ||
150 | * the file, and that bit 7 is 0. An IOException is thrown otherwise. | ||
151 | * | ||
152 | * @return The byte read. | ||
153 | * @throws IOException If premature EOF was reached or byte was not | ||
154 | * syncsafe. | ||
155 | */ | ||
156 | protected byte readSyncsafe() | ||
157 | throws IOException { | ||
158 | byte read = in.readByte(); | ||
159 | if(( read & 0x80 ) != 0 ) { | ||
160 | throw new IOException( "not syncsafe" ); | ||
161 | } | ||
162 | return read; | ||
163 | } | ||
164 | |||
165 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java b/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java new file mode 100644 index 0000000000..f86699b0f1 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/OggFastFileStream.java | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | import java.util.*; | ||
49 | |||
50 | import de.jarnbjo.ogg.*; | ||
51 | |||
52 | /** | ||
53 | * Replacement file reader class. The original J-Ogg FileStream has the | ||
54 | * major disadvantage that it reads the entire file, even though we just | ||
55 | * need a few byte from it. This FastFileStream will only read as little | ||
56 | * information as possible. | ||
57 | * | ||
58 | * @author Richard Körber <dev@shredzone.de> | ||
59 | * @version $Id$ | ||
60 | */ | ||
61 | public class OggFastFileStream implements PhysicalOggStream { | ||
62 | private InputStream sourceStream; | ||
63 | private boolean closed = false; | ||
64 | private int contentLength = 0; | ||
65 | private int position = 0; | ||
66 | private HashMap logicalStreams = new HashMap(); | ||
67 | private OggPage firstPage; | ||
68 | |||
69 | /** | ||
70 | * Constructor for the OggFastFileStream object | ||
71 | * | ||
72 | * @param in RandomAccessFile to be read | ||
73 | * @throws OggFormatException Bad format | ||
74 | * @throws IOException IO error | ||
75 | */ | ||
76 | public OggFastFileStream( RandomAccessFile in ) | ||
77 | throws OggFormatException, IOException { | ||
78 | this.sourceStream = new RandomAdapterInputStream( in ); | ||
79 | contentLength = (int) in.length(); | ||
80 | firstPage = OggPage.create( sourceStream ); | ||
81 | position += firstPage.getTotalLength(); | ||
82 | LogicalOggStreamImpl los = new LogicalOggStreamImpl( this, firstPage.getStreamSerialNumber() ); | ||
83 | logicalStreams.put( new Integer( firstPage.getStreamSerialNumber() ), los ); | ||
84 | los.checkFormat( firstPage ); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Get a collection of the logical streams. | ||
89 | * | ||
90 | * @return Collection | ||
91 | */ | ||
92 | public Collection getLogicalStreams() { | ||
93 | return logicalStreams.values(); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Checks if the file is open. | ||
98 | * | ||
99 | * @return true: open, false: closed | ||
100 | */ | ||
101 | public boolean isOpen() { | ||
102 | return !closed; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Closes the stream | ||
107 | * | ||
108 | * @throws IOException IO error | ||
109 | */ | ||
110 | public void close() | ||
111 | throws IOException { | ||
112 | closed = true; | ||
113 | sourceStream.close(); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * Get the content length | ||
118 | * | ||
119 | * @return The content length | ||
120 | */ | ||
121 | public int getContentLength() { | ||
122 | return contentLength; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Get the current position | ||
127 | * | ||
128 | * @return Position | ||
129 | */ | ||
130 | public int getPosition() { | ||
131 | return position; | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Get an OggPage. | ||
136 | * | ||
137 | * @param index Index to be fetched | ||
138 | * @return The oggPage value | ||
139 | * @throws IOException IO Error | ||
140 | */ | ||
141 | public OggPage getOggPage( int index ) | ||
142 | throws IOException { | ||
143 | if( firstPage != null ) { | ||
144 | OggPage tmp = firstPage; | ||
145 | firstPage = null; | ||
146 | return tmp; | ||
147 | } else { | ||
148 | OggPage page = OggPage.create( sourceStream ); | ||
149 | position += page.getTotalLength(); | ||
150 | return page; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Move the stream to a certain time position. | ||
156 | * | ||
157 | * @param granulePosition The new position | ||
158 | * @throws IOException | ||
159 | */ | ||
160 | public void setTime( long granulePosition ) | ||
161 | throws IOException { | ||
162 | throw new UnsupportedOperationException( "not supported" ); | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * Is this FileStream seekable? We pretend we are not, so J-Ogg | ||
167 | * will not get some stupid thoughts... ;) | ||
168 | * | ||
169 | * @return false | ||
170 | */ | ||
171 | public boolean isSeekable() { | ||
172 | return false; | ||
173 | } | ||
174 | |||
175 | /*--------------------------------------------------------------------*/ | ||
176 | |||
177 | /** | ||
178 | * This class repairs a design flaw in JDK1.0. A RandomAccessFile | ||
179 | * is not derived from InputStream, though it provides the same API. | ||
180 | * This Adapter gives an InputStream view of a Random Access File. | ||
181 | * <p> | ||
182 | * For a detailed method description, see InputStream. | ||
183 | */ | ||
184 | private static class RandomAdapterInputStream extends InputStream { | ||
185 | private RandomAccessFile rf; | ||
186 | |||
187 | /** | ||
188 | * Create a new Adapter. | ||
189 | * | ||
190 | * @param rf RandomAccessFile to be used | ||
191 | */ | ||
192 | public RandomAdapterInputStream( RandomAccessFile rf ) { | ||
193 | this.rf = rf; | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * Read a byte. | ||
198 | * | ||
199 | * @return Read byte or -1. | ||
200 | */ | ||
201 | public int read() throws IOException { | ||
202 | return rf.read(); | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Read a byte array. | ||
207 | * | ||
208 | * @param b Byte array to be read | ||
209 | * @return Number of bytes read or -1 | ||
210 | */ | ||
211 | public int read( byte[] b) throws IOException { | ||
212 | return rf.read(b); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Read into a byte array. | ||
217 | * | ||
218 | * @param b Byte array to be read | ||
219 | * @param off Starting offset | ||
220 | * @param len Length | ||
221 | * @return Number of bytes read or -1 | ||
222 | */ | ||
223 | public int read( byte[] b, int off, int len) throws IOException { | ||
224 | return rf.read( b, off, len ); | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * Skip a number of bytes in forward direction. | ||
229 | * | ||
230 | * @param n Number of bytes to skip | ||
231 | * @return Number of bytes skipped, or -1 | ||
232 | */ | ||
233 | public long skip( long n ) throws IOException { | ||
234 | return rf.skipBytes( (int) n ); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * Return the number of available bytes. Here it is the number of | ||
239 | * bytes remaining until EOF. | ||
240 | * | ||
241 | * @return Number of bytes available. | ||
242 | */ | ||
243 | public int available() throws IOException { | ||
244 | return (int) (rf.length() - rf.getFilePointer()); | ||
245 | } | ||
246 | |||
247 | } | ||
248 | |||
249 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/TagAsf.java b/songdbj/net/shredzone/ifish/ltr/TagAsf.java new file mode 100644 index 0000000000..fc68789345 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/TagAsf.java | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | import java.util.List; | ||
49 | |||
50 | import de.jarnbjo.ogg.*; | ||
51 | import de.jarnbjo.vorbis.*; | ||
52 | import entagged.audioformats.AudioFile; | ||
53 | import entagged.audioformats.Tag; | ||
54 | import entagged.audioformats.asf.AsfFileReader; | ||
55 | import entagged.audioformats.exceptions.CannotReadException; | ||
56 | |||
57 | /** | ||
58 | * Decodes an ASF/WMA stream. It uses parts of the | ||
59 | * <a href="http://entagged.sf.net/">Entagged</a> software, which is | ||
60 | * copyrighted by the Entagged Development Team, and published under | ||
61 | * GPL. | ||
62 | * <p> | ||
63 | * <em>NOTE</em> that due to the fact that Entagged is GPL, you <em>MUST</em> | ||
64 | * remove all entagged sources and this class file if you decide to use | ||
65 | * the LGPL or MPL part of the iFish licence! | ||
66 | * | ||
67 | * @author Richard Körber <dev@shredzone.de> | ||
68 | * @version $Id$ | ||
69 | */ | ||
70 | public class TagAsf extends LTR { | ||
71 | private final Tag tag; | ||
72 | |||
73 | /** | ||
74 | * Create a new TagAsf object. | ||
75 | * | ||
76 | * @param in File to read | ||
77 | * @param file Reference to the file itself | ||
78 | * @throws FormatDecodeException Couldn't decode this file | ||
79 | */ | ||
80 | public TagAsf( RandomAccessFile in, File file ) | ||
81 | throws FormatDecodeException { | ||
82 | super( in ); | ||
83 | |||
84 | try { | ||
85 | final AsfFileReader afr = new AsfFileReader(); | ||
86 | final AudioFile af = afr.read( file, in ); | ||
87 | tag = af.getTag(); | ||
88 | }catch( CannotReadException e ) { | ||
89 | throw new FormatDecodeException( "could not decode file: " + e.toString() ); | ||
90 | }catch( RuntimeException e ) { | ||
91 | throw new FormatDecodeException( "error decoding file: " + e.toString() ); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Get the type of this file. This is usually the compression format | ||
97 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
98 | * this format, the used tag format is appended after a slash (e.g. | ||
99 | * "MP3/id3v2"). | ||
100 | * | ||
101 | * @return The type | ||
102 | */ | ||
103 | public String getType() { | ||
104 | return "ASF"; | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * Get the artist. | ||
109 | * | ||
110 | * @return The artist (never null) | ||
111 | */ | ||
112 | public String getArtist() { | ||
113 | return tag.getFirstArtist().trim(); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * Get the album. | ||
118 | * | ||
119 | * @return The album (never null) | ||
120 | */ | ||
121 | public String getAlbum() { | ||
122 | return tag.getFirstAlbum().trim(); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Get the title. | ||
127 | * | ||
128 | * @return The title (never null) | ||
129 | */ | ||
130 | public String getTitle() { | ||
131 | return tag.getFirstTitle().trim(); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Get the genre. | ||
136 | * | ||
137 | * @return The genre (never null) | ||
138 | */ | ||
139 | public String getGenre() { | ||
140 | return tag.getFirstGenre().trim(); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Get the year. | ||
145 | * | ||
146 | * @return The year (never null) | ||
147 | */ | ||
148 | public String getYear() { | ||
149 | return tag.getFirstYear().trim(); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Get the comment. | ||
154 | * | ||
155 | * @return The comment (never null) | ||
156 | */ | ||
157 | public String getComment() { | ||
158 | return tag.getFirstComment().trim(); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * Get the track. | ||
163 | * | ||
164 | * @return The track (never null) | ||
165 | */ | ||
166 | public String getTrack() { | ||
167 | return tag.getFirstTrack().trim(); | ||
168 | } | ||
169 | |||
170 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java new file mode 100644 index 0000000000..9d3182260a --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v1.java | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | |||
49 | /** | ||
50 | * Decodes an MP3 file with ID3v1 tag. The file is compliant to the | ||
51 | * specifications found at <a href="http://www.id3.org">www.id3.org</a>. | ||
52 | * | ||
53 | * @author Richard Körber <dev@shredzone.de> | ||
54 | * @version $Id$ | ||
55 | */ | ||
56 | public class TagMp3v1 extends LTRmp3 { | ||
57 | private String artist = ""; | ||
58 | private String comment = ""; | ||
59 | private String title = ""; | ||
60 | private String album = ""; | ||
61 | private String year = ""; | ||
62 | private String track = ""; | ||
63 | private String genre = ""; | ||
64 | |||
65 | /** | ||
66 | * Create a new TagMp3v1 object. | ||
67 | * | ||
68 | * @param in File to be read | ||
69 | * @throws FormatDecodeException Description of the Exception | ||
70 | */ | ||
71 | public TagMp3v1( RandomAccessFile in ) | ||
72 | throws FormatDecodeException { | ||
73 | super( in ); | ||
74 | |||
75 | try { | ||
76 | //--- Decode header --- | ||
77 | in.seek( in.length() - 128 ); // To the place where the tag lives | ||
78 | if( !readStringLen( 3 ).equals( "TAG" ) ) { | ||
79 | throw new FormatDecodeException( "not an id3v1 tag" ); | ||
80 | } | ||
81 | |||
82 | title = readStringLen( 30, charsetV1 ).trim(); | ||
83 | artist = readStringLen( 30, charsetV1 ).trim(); | ||
84 | album = readStringLen( 30, charsetV1 ).trim(); | ||
85 | year = readStringLen( 4, charsetV1 ).trim(); | ||
86 | comment = readStringLen( 28, charsetV1 ); | ||
87 | |||
88 | byte[] sto = new byte[2]; | ||
89 | in.readFully( sto ); | ||
90 | if( sto[0] == 0x00 ) { | ||
91 | // ID3v1.1 | ||
92 | track = ( sto[1]>0 ? String.valueOf(sto[1]) : "" ); | ||
93 | } else { | ||
94 | // ID3v1.0 | ||
95 | comment += new String( sto, charsetV1 ); | ||
96 | } | ||
97 | comment = comment.trim(); | ||
98 | |||
99 | genre = decodeGenre( in.readUnsignedByte() ); | ||
100 | if( genre==null ) genre=""; | ||
101 | |||
102 | } catch( IOException e ) { | ||
103 | throw new FormatDecodeException( "could not decode file: " + e.toString() ); | ||
104 | }catch( RuntimeException e ) { | ||
105 | throw new FormatDecodeException( "error decoding file: " + e.toString() ); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Get the type of this file. This is usually the compression format | ||
111 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
112 | * this format, the used tag format is appended after a slash (e.g. | ||
113 | * "MP3/id3v2"). | ||
114 | * | ||
115 | * @return The type | ||
116 | */ | ||
117 | public String getType() { | ||
118 | return "MP3/id3v1"; | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * Get the artist. | ||
123 | * | ||
124 | * @return The artist (never null) | ||
125 | */ | ||
126 | public String getArtist() { | ||
127 | return artist; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Get the album. | ||
132 | * | ||
133 | * @return The album (never null) | ||
134 | */ | ||
135 | public String getAlbum() { | ||
136 | return album; | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Get the title. | ||
141 | * | ||
142 | * @return The title (never null) | ||
143 | */ | ||
144 | public String getTitle() { | ||
145 | return title; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Get the genre. | ||
150 | * | ||
151 | * @return The genre (never null) | ||
152 | */ | ||
153 | public String getGenre() { | ||
154 | return genre; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * Get the year. | ||
159 | * | ||
160 | * @return The year (never null) | ||
161 | */ | ||
162 | public String getYear() { | ||
163 | return year; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Get the comment. | ||
168 | * | ||
169 | * @return The comment (never null) | ||
170 | */ | ||
171 | public String getComment() { | ||
172 | return comment; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * Get the track. | ||
177 | * | ||
178 | * @return The track (never null) | ||
179 | */ | ||
180 | public String getTrack() { | ||
181 | return track; | ||
182 | } | ||
183 | |||
184 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java new file mode 100644 index 0000000000..689dd21618 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v2.java | |||
@@ -0,0 +1,441 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | import java.util.regex.*; | ||
49 | |||
50 | /** | ||
51 | * Decodes an MP3 file with ID3v2 tag. The file is compliant to the | ||
52 | * specifications found at <a href="http://www.id3.org">www.id3.org</a>, | ||
53 | * V2.4.0 and V2.3.0. ID3 V2.2.0 is handled in a separate tag handler. | ||
54 | * Anyhow it has certain limitations regarding tag frames which could | ||
55 | * not be used or entirely used to iFish, e.g. multiple genres. | ||
56 | * | ||
57 | * @author Richard Körber <dev@shredzone.de> | ||
58 | * @version $Id$ | ||
59 | */ | ||
60 | public class TagMp3v2 extends LTRmp3 { | ||
61 | private int globalSize; | ||
62 | private Pattern patGenre; | ||
63 | private boolean v230 = false; // V2.3.0 detected | ||
64 | |||
65 | private String artist = ""; | ||
66 | private String comment = ""; | ||
67 | private String title = ""; | ||
68 | private String album = ""; | ||
69 | private String year = ""; | ||
70 | private String track = ""; | ||
71 | private String genre = ""; | ||
72 | |||
73 | /** | ||
74 | * Create a new TagMp3v2 instance. | ||
75 | * | ||
76 | * @param in File to be read | ||
77 | * @throws FormatDecodeException Couldn't decode this file | ||
78 | */ | ||
79 | public TagMp3v2( RandomAccessFile in ) | ||
80 | throws FormatDecodeException { | ||
81 | super( in ); | ||
82 | |||
83 | patGenre = Pattern.compile( "\\((\\d{1,3})\\).*" ); | ||
84 | |||
85 | try { | ||
86 | //--- Decode header --- | ||
87 | if( !readStringLen( 3 ).equals( "ID3" ) ) { | ||
88 | throw new FormatDecodeException( "not an id3v2 tag" ); | ||
89 | } | ||
90 | |||
91 | byte version = in.readByte(); | ||
92 | byte revision = in.readByte(); | ||
93 | |||
94 | if( version==0xFF || revision==0xFF ) { | ||
95 | throw new FormatDecodeException( "not an id3v2 tag" ); | ||
96 | } | ||
97 | |||
98 | if( version<=0x02 || version>0x04 ) { | ||
99 | throw new FormatDecodeException( "unable to decode ID3v2."+version+"."+revision ); | ||
100 | } | ||
101 | |||
102 | byte flags = in.readByte(); | ||
103 | v230 = (version==0x03); | ||
104 | globalSize = readSyncsafeInt() + 10; | ||
105 | |||
106 | //--- Skip extended header --- | ||
107 | if( ( flags & 0x40 ) != 0 ) { | ||
108 | int ehsize = readAutoInt(); | ||
109 | if( ehsize < 6 ) { | ||
110 | throw new FormatDecodeException( "extended header too small" ); | ||
111 | } | ||
112 | in.skipBytes( ehsize - 4 ); | ||
113 | } | ||
114 | |||
115 | //--- Read all frames --- | ||
116 | Frame frm; | ||
117 | while( ( frm = readFrame() ) != null ) { | ||
118 | String type = frm.getType(); | ||
119 | String[] answer; | ||
120 | |||
121 | if( type.equals( "TOPE" ) || type.equals( "TPE1" ) ) { | ||
122 | |||
123 | answer = frm.getAsStringEnc(); | ||
124 | artist = (answer.length>0 ? answer[0].trim() : ""); | ||
125 | |||
126 | } else if( type.equals( "COMM" ) ) { | ||
127 | |||
128 | answer = frm.getAsStringEnc(); | ||
129 | comment = (answer.length>1 ? answer[1].trim() : ""); | ||
130 | |||
131 | } else if( type.equals( "TIT2" ) ) { | ||
132 | |||
133 | answer = frm.getAsStringEnc(); | ||
134 | title =(answer.length>0 ? answer[0].trim() : ""); | ||
135 | |||
136 | } else if( type.equals( "TALB" ) ) { | ||
137 | |||
138 | answer = frm.getAsStringEnc(); | ||
139 | album = (answer.length>0 ? answer[0].trim() : ""); | ||
140 | |||
141 | } else if( type.equals( "TYER" ) ) { | ||
142 | |||
143 | answer = frm.getAsStringEnc(); | ||
144 | year = (answer.length>0 ? answer[0].trim() : ""); | ||
145 | |||
146 | } else if( type.equals( "TRCK" ) ) { | ||
147 | |||
148 | answer = frm.getAsStringEnc(); | ||
149 | track = (answer.length>0 ? answer[0].trim() : ""); | ||
150 | |||
151 | } else if( type.equals( "TCON" ) ) { | ||
152 | |||
153 | answer = frm.getAsStringEnc(); | ||
154 | if( answer.length>0 ) { | ||
155 | genre = answer[0].trim(); | ||
156 | Matcher mat = patGenre.matcher( genre ); | ||
157 | if( mat.matches() ) { | ||
158 | genre = decodeGenre( Integer.parseInt( mat.group( 1 ) ) ); | ||
159 | if( genre==null ) genre=""; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | //--- Footer frame? --- | ||
166 | if( ( flags & 0x10 ) != 0 ) { | ||
167 | in.skipBytes( 10 ); // Then skip it | ||
168 | } | ||
169 | // Position is now the start of the MP3 data | ||
170 | |||
171 | } catch( IOException e ) { | ||
172 | throw new FormatDecodeException( "could not decode file: " + e.toString() ); | ||
173 | }catch( RuntimeException e ) { | ||
174 | throw new FormatDecodeException( "error decoding file: " + e.toString() ); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * Get the type of this file. This is usually the compression format | ||
180 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
181 | * this format, the used tag format is appended after a slash (e.g. | ||
182 | * "MP3/id3v2"). | ||
183 | * | ||
184 | * @return The type | ||
185 | */ | ||
186 | public String getType() { | ||
187 | return ( v230 ? "MP3/id3v2.3.0" : "MP3/id3v2.4.0" ); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Get the artist. | ||
192 | * | ||
193 | * @return The artist (never null) | ||
194 | */ | ||
195 | public String getArtist() { | ||
196 | return artist; | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * Get the album. | ||
201 | * | ||
202 | * @return The album (never null) | ||
203 | */ | ||
204 | public String getAlbum() { | ||
205 | return album; | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * Get the title. | ||
210 | * | ||
211 | * @return The title (never null) | ||
212 | */ | ||
213 | public String getTitle() { | ||
214 | return title; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * Get the genre. | ||
219 | * | ||
220 | * @return The genre (never null) | ||
221 | */ | ||
222 | public String getGenre() { | ||
223 | return genre; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * Get the year. | ||
228 | * | ||
229 | * @return The year (never null) | ||
230 | */ | ||
231 | public String getYear() { | ||
232 | return year; | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * Get the comment. | ||
237 | * | ||
238 | * @return The comment (never null) | ||
239 | */ | ||
240 | public String getComment() { | ||
241 | return comment; | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * Get the track. | ||
246 | * | ||
247 | * @return The track (never null) | ||
248 | */ | ||
249 | public String getTrack() { | ||
250 | return track; | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * Read an ID3v2 integer. This is a 4 byte big endian value, which is | ||
255 | * only syncsafe for ID3v2.4 or higher, but not for ID3v2.3. | ||
256 | * | ||
257 | * @return The integer read. | ||
258 | * @throws IOException If there were not enough bytes in the file. | ||
259 | */ | ||
260 | protected int readAutoInt() | ||
261 | throws IOException { | ||
262 | return( v230 ? readInt() : readSyncsafeInt() ); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Read a tag frame. A Frame object will be returned, or null if no | ||
267 | * more frames were available. | ||
268 | * | ||
269 | * @return The next frame, or null | ||
270 | * @throws IOException If premature EOF was reached. | ||
271 | */ | ||
272 | protected Frame readFrame() | ||
273 | throws IOException { | ||
274 | if( in.getFilePointer() >= globalSize ) { | ||
275 | return null; | ||
276 | } | ||
277 | |||
278 | //--- Get the type --- | ||
279 | String type = readStringLen( 4 ); | ||
280 | if( type.charAt(0)==0 ) { // Optional padding frame | ||
281 | in.skipBytes( (int) ( globalSize - in.getFilePointer() ) );// Skip it... | ||
282 | return null; // Return null | ||
283 | } | ||
284 | |||
285 | //--- Read the frame --- | ||
286 | int size = readAutoInt(); | ||
287 | byte flag1 = in.readByte(); | ||
288 | byte flag2 = in.readByte(); | ||
289 | |||
290 | //--- Read the content --- | ||
291 | // Stay within reasonable boundaries. If the data part is bigger than | ||
292 | // 16K, it's not really useful for us, so we will keep the frame empty. | ||
293 | byte[] data = null; | ||
294 | if( size<=16384 ) { | ||
295 | data = new byte[size]; | ||
296 | int rlen = in.read( data ); | ||
297 | if( rlen != size ) { | ||
298 | throw new IOException( "unexpected EOF" ); | ||
299 | } | ||
300 | }else { | ||
301 | in.skipBytes( size ); | ||
302 | } | ||
303 | |||
304 | //--- Return the frame --- | ||
305 | return new Frame( type, size, flag1, flag2, data ); | ||
306 | } | ||
307 | |||
308 | /*--------------------------------------------------------------------*/ | ||
309 | |||
310 | /** | ||
311 | * This class contains a ID3v2 frame. | ||
312 | */ | ||
313 | private static class Frame { | ||
314 | private final String charset; | ||
315 | private final String type; | ||
316 | // private final int size; | ||
317 | // private final byte flag1; | ||
318 | private final byte flag2; | ||
319 | private byte[] data; | ||
320 | private boolean decoded = false; | ||
321 | |||
322 | /** | ||
323 | * Constructor for the Frame object | ||
324 | * | ||
325 | * @param type Frame type | ||
326 | * @param size Frame size | ||
327 | * @param flag1 Flag 1 | ||
328 | * @param flag2 Flag 2 | ||
329 | * @param data Frame content, may be null | ||
330 | */ | ||
331 | public Frame( String type, int size, byte flag1, byte flag2, byte[] data ) { | ||
332 | charset = "ISO-8859-1"; | ||
333 | this.type = type; | ||
334 | // Currently unused... | ||
335 | // this.size = size; | ||
336 | // this.flag1 = flag1; | ||
337 | this.flag2 = flag2; | ||
338 | this.data = data; | ||
339 | } | ||
340 | |||
341 | /** | ||
342 | * Get the type. | ||
343 | * | ||
344 | * @return The type of this Frame | ||
345 | */ | ||
346 | public String getType() { | ||
347 | return type; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Return the Frame content as String. This method is to be used for | ||
352 | * strings without a leading encoding byte. This machine's default | ||
353 | * encoding will be used instead. | ||
354 | * | ||
355 | * @return Encoded string | ||
356 | * @throws FormatDecodeException Could not read or decode frame | ||
357 | */ | ||
358 | public String getAsString() | ||
359 | throws FormatDecodeException { | ||
360 | if(data==null) return null; | ||
361 | decode(); | ||
362 | return new String( data ); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * Return the Frame content as encoded String. The first byte will | ||
367 | * contain the encoding type, following the string itself. Multiple | ||
368 | * strings are null-terminated. An array of all strings found, will | ||
369 | * be returned. | ||
370 | * | ||
371 | * @return Array of all strings. Might be an empty array! | ||
372 | * @throws FormatDecodeException Could not read or decode frame | ||
373 | */ | ||
374 | public String[] getAsStringEnc() | ||
375 | throws FormatDecodeException { | ||
376 | if( data==null ) return new String[0]; | ||
377 | decode(); | ||
378 | if( data.length==0 ) return new String[0]; | ||
379 | int len = data.length - 1; | ||
380 | String result = ""; | ||
381 | try { | ||
382 | switch ( data[0] ) { | ||
383 | case 0x00: | ||
384 | result = new String( data, 1, len, charset ); | ||
385 | break; | ||
386 | case 0x01: | ||
387 | result = new String( data, 1, len, "UTF-16" ); | ||
388 | break; | ||
389 | case 0x02: | ||
390 | result = new String( data, 1, len, "UTF-16BE" ); | ||
391 | break; | ||
392 | case 0x03: | ||
393 | result = new String( data, 1, len, "UTF-8" ); | ||
394 | break; | ||
395 | default: | ||
396 | throw new FormatDecodeException( "unknown encoding of frame " + type ); | ||
397 | } | ||
398 | }catch( UnsupportedEncodingException e ) { | ||
399 | throw new FormatDecodeException( "Java misses a basic encoding!?" ); | ||
400 | } | ||
401 | return result.split( "\0" ); | ||
402 | } | ||
403 | |||
404 | /** | ||
405 | * Decode a frame, unless already decoded. If the frame was | ||
406 | * unsynchronized, it will be synchronized here. Compression and | ||
407 | * encryption is not supported yet. You can invoke this method | ||
408 | * several times without any effect. | ||
409 | * | ||
410 | * @throws FormatDecodeException Description of the Exception | ||
411 | */ | ||
412 | private void decode() | ||
413 | throws FormatDecodeException { | ||
414 | if( decoded ) return; | ||
415 | decoded = true; | ||
416 | |||
417 | if( ( flag2 & 0x02 ) != 0 ) { // Unsynchronize | ||
418 | byte decoded[] = new byte[data.length]; | ||
419 | int pos = 0; | ||
420 | for( int ix = 0; ix < data.length - 1; ix++ ) { | ||
421 | decoded[pos++] = data[ix]; | ||
422 | if( data[ix] == 0xFF && data[ix + 1] == 0x00 ) { | ||
423 | ix++; | ||
424 | } | ||
425 | } | ||
426 | data = new byte[pos]; | ||
427 | System.arraycopy( decoded, 0, data, 0, pos ); | ||
428 | } | ||
429 | |||
430 | if( ( flag2 & 0x08 ) != 0 ) { // Compression | ||
431 | throw new FormatDecodeException( "sorry, compression is not yet supported" ); | ||
432 | } | ||
433 | |||
434 | if( ( flag2 & 0x04 ) != 0 ) { // Encryption | ||
435 | throw new FormatDecodeException( "sorry, encryption is not yet supported" ); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | } | ||
440 | |||
441 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java b/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java new file mode 100644 index 0000000000..28e623f962 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/TagMp3v200.java | |||
@@ -0,0 +1,373 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | import java.util.regex.*; | ||
49 | |||
50 | /** | ||
51 | * Decodes an MP3 file with old ID3v2.00 tag. The file is compliant to the | ||
52 | * specifications found at <a href="http://www.id3.org/id3v2-00.txt">www.id3.org</a>. | ||
53 | * Only ID3 V2.00 up to v2.2.0 is handled here. Newer versions are separately | ||
54 | * handled in TagMp3v2. | ||
55 | * | ||
56 | * @author Richard Körber <dev@shredzone.de> | ||
57 | * @version $Id$ | ||
58 | */ | ||
59 | public class TagMp3v200 extends LTRmp3 { | ||
60 | private int globalSize; | ||
61 | private Pattern patGenre; | ||
62 | |||
63 | private String artist = ""; | ||
64 | private String comment = ""; | ||
65 | private String title = ""; | ||
66 | private String album = ""; | ||
67 | private String year = ""; | ||
68 | private String track = ""; | ||
69 | private String genre = ""; | ||
70 | |||
71 | /** | ||
72 | * Create a new TagMp3v200 instance. | ||
73 | * | ||
74 | * @param in File to be read | ||
75 | * @throws FormatDecodeException Couldn't decode this file | ||
76 | */ | ||
77 | public TagMp3v200( RandomAccessFile in ) | ||
78 | throws FormatDecodeException { | ||
79 | super( in ); | ||
80 | |||
81 | patGenre = Pattern.compile( "\\((\\d{1,3})\\).*" ); | ||
82 | |||
83 | try { | ||
84 | //--- Decode header --- | ||
85 | if( !readStringLen( 3 ).equals( "ID3" ) ) { | ||
86 | throw new FormatDecodeException( "not an id3v2 tag" ); | ||
87 | } | ||
88 | |||
89 | byte version = in.readByte(); | ||
90 | byte revision = in.readByte(); | ||
91 | |||
92 | if( version!=0x02 || revision==0xFF ) { | ||
93 | throw new FormatDecodeException( "not an id3v2.2.0 tag" ); | ||
94 | } | ||
95 | |||
96 | byte flags = in.readByte(); | ||
97 | globalSize = readSyncsafeInt() + 10; | ||
98 | |||
99 | //--- Read all frames --- | ||
100 | Frame frm; | ||
101 | while( ( frm = readFrame() ) != null ) { | ||
102 | String type = frm.getType(); | ||
103 | String[] answer; | ||
104 | |||
105 | if( type.equals( "TOA" ) || type.equals( "TP1" ) ) { | ||
106 | |||
107 | answer = frm.getAsStringEnc(); | ||
108 | artist = (answer.length>0 ? answer[0].trim() : ""); | ||
109 | |||
110 | } else if( type.equals( "COM" ) ) { | ||
111 | |||
112 | answer = frm.getAsStringEnc(); | ||
113 | comment = (answer.length>1 ? answer[1].trim() : ""); | ||
114 | |||
115 | } else if( type.equals( "TT2" ) ) { | ||
116 | |||
117 | answer = frm.getAsStringEnc(); | ||
118 | title =(answer.length>0 ? answer[0].trim() : ""); | ||
119 | |||
120 | } else if( type.equals( "TAL" ) ) { | ||
121 | |||
122 | answer = frm.getAsStringEnc(); | ||
123 | album = (answer.length>0 ? answer[0].trim() : ""); | ||
124 | |||
125 | } else if( type.equals( "TYE" ) ) { | ||
126 | |||
127 | answer = frm.getAsStringEnc(); | ||
128 | year = (answer.length>0 ? answer[0].trim() : ""); | ||
129 | |||
130 | } else if( type.equals( "TRK" ) ) { | ||
131 | |||
132 | answer = frm.getAsStringEnc(); | ||
133 | track = (answer.length>0 ? answer[0].trim() : ""); | ||
134 | |||
135 | } else if( type.equals( "TCO" ) ) { | ||
136 | |||
137 | answer = frm.getAsStringEnc(); | ||
138 | if( answer.length>0 ) { | ||
139 | genre = answer[0].trim(); | ||
140 | Matcher mat = patGenre.matcher( genre ); | ||
141 | if( mat.matches() ) { | ||
142 | genre = decodeGenre( Integer.parseInt( mat.group( 1 ) ) ); | ||
143 | if( genre==null ) genre=""; | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Position is now the start of the MP3 data | ||
150 | |||
151 | } catch( IOException e ) { | ||
152 | throw new FormatDecodeException( "could not decode file: " + e.toString() ); | ||
153 | }catch( RuntimeException e ) { | ||
154 | throw new FormatDecodeException( "error decoding file: " + e.toString() ); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Get the type of this file. This is usually the compression format | ||
160 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
161 | * this format, the used tag format is appended after a slash (e.g. | ||
162 | * "MP3/id3v2"). | ||
163 | * | ||
164 | * @return The type | ||
165 | */ | ||
166 | public String getType() { | ||
167 | return "MP3/id3v2.2.0"; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Get the artist. | ||
172 | * | ||
173 | * @return The artist (never null) | ||
174 | */ | ||
175 | public String getArtist() { | ||
176 | return artist; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Get the album. | ||
181 | * | ||
182 | * @return The album (never null) | ||
183 | */ | ||
184 | public String getAlbum() { | ||
185 | return album; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Get the title. | ||
190 | * | ||
191 | * @return The title (never null) | ||
192 | */ | ||
193 | public String getTitle() { | ||
194 | return title; | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Get the genre. | ||
199 | * | ||
200 | * @return The genre (never null) | ||
201 | */ | ||
202 | public String getGenre() { | ||
203 | return genre; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * Get the year. | ||
208 | * | ||
209 | * @return The year (never null) | ||
210 | */ | ||
211 | public String getYear() { | ||
212 | return year; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Get the comment. | ||
217 | * | ||
218 | * @return The comment (never null) | ||
219 | */ | ||
220 | public String getComment() { | ||
221 | return comment; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * Get the track. | ||
226 | * | ||
227 | * @return The track (never null) | ||
228 | */ | ||
229 | public String getTrack() { | ||
230 | return track; | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * Read a tag frame. A Frame object will be returned, or null if no | ||
235 | * more frames were available. | ||
236 | * | ||
237 | * @return The next frame, or null | ||
238 | * @throws IOException If premature EOF was reached. | ||
239 | */ | ||
240 | protected Frame readFrame() | ||
241 | throws IOException { | ||
242 | if( in.getFilePointer() >= globalSize ) { | ||
243 | return null; | ||
244 | } | ||
245 | |||
246 | //--- Get the type --- | ||
247 | String type = readStringLen( 3 ); | ||
248 | if( type.charAt(0)==0 ) { // Optional padding frame | ||
249 | in.skipBytes( (int) ( globalSize - in.getFilePointer() ) );// Skip it... | ||
250 | return null; // Return null | ||
251 | } | ||
252 | |||
253 | //--- Read the frame --- | ||
254 | int size = read3Int(); | ||
255 | |||
256 | //--- Read the content --- | ||
257 | // Stay within reasonable boundaries. If the data part is bigger than | ||
258 | // 16K, it's not really useful for us, so we will keep the frame empty. | ||
259 | byte[] data = null; | ||
260 | if( size<=16384 ) { | ||
261 | data = new byte[size]; | ||
262 | int rlen = in.read( data ); | ||
263 | if( rlen != size ) { | ||
264 | throw new IOException( "unexpected EOF" ); | ||
265 | } | ||
266 | }else { | ||
267 | in.skipBytes( size ); | ||
268 | } | ||
269 | |||
270 | //--- Return the frame --- | ||
271 | return new Frame( type, size, data ); | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * Read an ID3v2 3 byte integer. This is a 3 byte big endian value, which is | ||
276 | * always not syncsafe. | ||
277 | * | ||
278 | * @return The integer read. | ||
279 | * @throws IOException If there were not enough bytes in the file. | ||
280 | */ | ||
281 | protected int read3Int() | ||
282 | throws IOException { | ||
283 | int val = 0; | ||
284 | for( int cnt = 3; cnt > 0; cnt-- ) { | ||
285 | val <<= 8; | ||
286 | val |= ( in.readByte() & 0xFF ); | ||
287 | } | ||
288 | return val; | ||
289 | } | ||
290 | |||
291 | /*--------------------------------------------------------------------*/ | ||
292 | |||
293 | /** | ||
294 | * This class contains a ID3v2.2.0 frame. | ||
295 | */ | ||
296 | private static class Frame { | ||
297 | private final String charset; | ||
298 | private final String type; | ||
299 | // private final int size; | ||
300 | private byte[] data; | ||
301 | |||
302 | /** | ||
303 | * Constructor for the Frame object | ||
304 | * | ||
305 | * @param type Frame type | ||
306 | * @param size Frame size | ||
307 | * @param data Frame content, may be null | ||
308 | */ | ||
309 | public Frame( String type, int size, byte[] data ) { | ||
310 | this.charset = "ISO-8859-1"; | ||
311 | this.type = type; | ||
312 | // Currently unused... | ||
313 | // this.size = size; | ||
314 | this.data = data; | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * Get the type. | ||
319 | * | ||
320 | * @return The type of this Frame | ||
321 | */ | ||
322 | public String getType() { | ||
323 | return type; | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * Return the Frame content as String. This method is to be used for | ||
328 | * strings without a leading encoding byte. This machine's default | ||
329 | * encoding will be used instead. | ||
330 | * | ||
331 | * @return Encoded string | ||
332 | * @throws FormatDecodeException Could not read or decode frame | ||
333 | */ | ||
334 | public String getAsString() | ||
335 | throws FormatDecodeException { | ||
336 | if(data==null) return null; | ||
337 | return new String( data ); | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * Return the Frame content as encoded String. The first byte will | ||
342 | * contain the encoding type, following the string itself. Multiple | ||
343 | * strings are null-terminated. An array of all strings found, will | ||
344 | * be returned. | ||
345 | * | ||
346 | * @return Array of all strings. Might be an empty array! | ||
347 | * @throws FormatDecodeException Could not read or decode frame | ||
348 | */ | ||
349 | public String[] getAsStringEnc() | ||
350 | throws FormatDecodeException { | ||
351 | if(data==null) return new String[0]; | ||
352 | int len = data.length - 1; | ||
353 | String result = ""; | ||
354 | try { | ||
355 | switch ( data[0] ) { | ||
356 | case 0x00: | ||
357 | result = new String( data, 1, len, charset ); | ||
358 | break; | ||
359 | case 0x01: | ||
360 | result = new String( data, 1, len, "UTF-16" ); | ||
361 | break; | ||
362 | default: | ||
363 | throw new FormatDecodeException( "unknown encoding of frame " + type ); | ||
364 | } | ||
365 | }catch( UnsupportedEncodingException e ) { | ||
366 | throw new FormatDecodeException( "Java misses a basic encoding!?" ); | ||
367 | } | ||
368 | return result.split( "\0" ); | ||
369 | } | ||
370 | |||
371 | } | ||
372 | |||
373 | } | ||
diff --git a/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java b/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java new file mode 100644 index 0000000000..45b7401437 --- /dev/null +++ b/songdbj/net/shredzone/ifish/ltr/TagOggVorbis.java | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * iFish -- An iRiver iHP jukebox database creation tool | ||
3 | * | ||
4 | * Copyright (c) 2004 Richard "Shred" Körber | ||
5 | * http://www.shredzone.net/go/ifish | ||
6 | * | ||
7 | *----------------------------------------------------------------------- | ||
8 | * ***** BEGIN LICENSE BLOCK ***** | ||
9 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | ||
10 | * | ||
11 | * The contents of this file are subject to the Mozilla Public License Version | ||
12 | * 1.1 (the "License"); you may not use this file except in compliance with | ||
13 | * the License. You may obtain a copy of the License at | ||
14 | * http://www.mozilla.org/MPL/ | ||
15 | * | ||
16 | * Software distributed under the License is distributed on an "AS IS" basis, | ||
17 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | ||
18 | * for the specific language governing rights and limitations under the | ||
19 | * License. | ||
20 | * | ||
21 | * The Original Code is IFISH. | ||
22 | * | ||
23 | * The Initial Developer of the Original Code is | ||
24 | * Richard "Shred" Körber. | ||
25 | * Portions created by the Initial Developer are Copyright (C) 2004 | ||
26 | * the Initial Developer. All Rights Reserved. | ||
27 | * | ||
28 | * Contributor(s): | ||
29 | * | ||
30 | * Alternatively, the contents of this file may be used under the terms of | ||
31 | * either the GNU General Public License Version 2 or later (the "GPL"), or | ||
32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
33 | * in which case the provisions of the GPL or the LGPL are applicable instead | ||
34 | * of those above. If you wish to allow use of your version of this file only | ||
35 | * under the terms of either the GPL or the LGPL, and not to allow others to | ||
36 | * use your version of this file under the terms of the MPL, indicate your | ||
37 | * decision by deleting the provisions above and replace them with the notice | ||
38 | * and other provisions required by the GPL or the LGPL. If you do not delete | ||
39 | * the provisions above, a recipient may use your version of this file under | ||
40 | * the terms of any one of the MPL, the GPL or the LGPL. | ||
41 | * | ||
42 | * ***** END LICENSE BLOCK ***** | ||
43 | */ | ||
44 | |||
45 | package net.shredzone.ifish.ltr; | ||
46 | |||
47 | import java.io.*; | ||
48 | import de.jarnbjo.ogg.*; | ||
49 | import de.jarnbjo.vorbis.*; | ||
50 | |||
51 | /** | ||
52 | * Decodes an Ogg Vorbis stream. It uses the | ||
53 | * <a href="http://www.j-ogg.de/">J-Ogg</a> library, which is | ||
54 | * copyrighted by Tor-Einar Jarnbjo. His licence says that may use and | ||
55 | * modify it as will (even commercial), as long as a reference to his | ||
56 | * library is stated in the software. | ||
57 | * <p> | ||
58 | * <b>NOTE:</b> Due to a bug, there is a patch required in the J-Ogg | ||
59 | * library. In class de.jarnbjo.vorbis.CommentHeader, private method | ||
60 | * <tt>addComment()</tt>, add a line | ||
61 | * <pre> | ||
62 | * key = key.toUpperCase(); | ||
63 | * </pre> | ||
64 | * after the method declaration header. | ||
65 | * | ||
66 | * @author Richard Körber <dev@shredzone.de> | ||
67 | * @version $Id$ | ||
68 | */ | ||
69 | public class TagOggVorbis extends LTR { | ||
70 | private CommentHeader cmt; | ||
71 | |||
72 | /** | ||
73 | * Create a new TagOggVorbis object. | ||
74 | * | ||
75 | * @param in File to read | ||
76 | * @throws FormatDecodeException Couldn't decode this file | ||
77 | */ | ||
78 | public TagOggVorbis( RandomAccessFile in ) | ||
79 | throws FormatDecodeException { | ||
80 | super( in ); | ||
81 | |||
82 | try { | ||
83 | //--- Check for Ogg Signature --- | ||
84 | // I expected J-Ogg to do this check, but it has been commented | ||
85 | // out because very old ogg files do not seem to have this header. | ||
86 | // We will ignore those files. | ||
87 | if( !readStringLen(4).equals("OggS") ) | ||
88 | throw new FormatDecodeException( "not an Ogg file" ); | ||
89 | in.seek(0); | ||
90 | |||
91 | //--- Get the Comment Header --- | ||
92 | OggFastFileStream fs = new OggFastFileStream(in); | ||
93 | LogicalOggStream los = (LogicalOggStream) fs.getLogicalStreams().iterator().next(); | ||
94 | if( los.getFormat() != LogicalOggStream.FORMAT_VORBIS ) | ||
95 | throw new FormatDecodeException( "not a plain Ogg Vorbis file" ); | ||
96 | VorbisStream vos = new VorbisStream( los ); | ||
97 | cmt = vos.getCommentHeader(); | ||
98 | |||
99 | }catch( OggFormatException e ) { | ||
100 | throw new FormatDecodeException( "not an Ogg file" ); | ||
101 | }catch( VorbisFormatException e ) { | ||
102 | throw new FormatDecodeException( "not an Ogg Vorbis file" ); | ||
103 | }catch( IOException e ) { | ||
104 | throw new FormatDecodeException( "could not decode file: " + e.toString() ); | ||
105 | }catch( RuntimeException e ) { | ||
106 | throw new FormatDecodeException( "error decoding file: " + e.toString() ); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Get the type of this file. This is usually the compression format | ||
112 | * itself (e.g. "OGG" or "MP3"). If there are different taggings for | ||
113 | * this format, the used tag format is appended after a slash (e.g. | ||
114 | * "MP3/id3v2"). | ||
115 | * | ||
116 | * @return The type | ||
117 | */ | ||
118 | public String getType() { | ||
119 | return "OGG"; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Get the artist. | ||
124 | * | ||
125 | * @return The artist (never null) | ||
126 | */ | ||
127 | public String getArtist() { | ||
128 | String val = cmt.getArtist(); | ||
129 | if( val==null ) val=""; | ||
130 | return val.trim(); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Get the album. | ||
135 | * | ||
136 | * @return The album (never null) | ||
137 | */ | ||
138 | public String getAlbum() { | ||
139 | String val = cmt.getAlbum(); | ||
140 | if( val==null ) val=""; | ||
141 | return val.trim(); | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * Get the title. | ||
146 | * | ||
147 | * @return The title (never null) | ||
148 | */ | ||
149 | public String getTitle() { | ||
150 | String val = cmt.getTitle(); | ||
151 | if( val==null ) val=""; | ||
152 | return val.trim(); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Get the genre. | ||
157 | * | ||
158 | * @return The genre (never null) | ||
159 | */ | ||
160 | public String getGenre() { | ||
161 | String val = cmt.getGenre(); | ||
162 | if( val==null ) val=""; | ||
163 | return val.trim(); | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Get the year. | ||
168 | * | ||
169 | * @return The year (never null) | ||
170 | */ | ||
171 | public String getYear() { | ||
172 | String val = cmt.getDate(); | ||
173 | if( val==null ) val=""; | ||
174 | return val.trim(); | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * Get the comment. | ||
179 | * | ||
180 | * @return The comment (never null) | ||
181 | */ | ||
182 | public String getComment() { | ||
183 | String val = cmt.getDescription(); | ||
184 | if( val==null ) { | ||
185 | // *sigh* The Ogg Vorbis documentation does not explicitely | ||
186 | // state the comment types. So there are some ogg writers | ||
187 | // around that use COMMENT instead of DESCRIPTION. We will | ||
188 | // use COMMENT if DESCRIPTION was empty. | ||
189 | val = cmt.getComment("COMMENT"); | ||
190 | } | ||
191 | if( val==null ) | ||
192 | val=""; | ||
193 | return val.trim(); | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * Get the track. | ||
198 | * | ||
199 | * @return The track (never null) | ||
200 | */ | ||
201 | public String getTrack() { | ||
202 | String val = cmt.getTrackNumber(); | ||
203 | if( val==null ) val=""; | ||
204 | return val.trim(); | ||
205 | } | ||
206 | |||
207 | } | ||