KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > MP3DirectoryGenerator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.generation;
17
18 import org.xml.sax.SAXException JavaDoc;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.RandomAccessFile JavaDoc;
23
24 /**
25  * @cocoon.sitemap.component.documentation
26  * An extension of DirectoryGenerators that adds extra attributes for MP3
27  * files.
28  *
29  * @cocoon.sitemap.component.name mp3directory
30  * @cocoon.sitemap.component.label content
31  * @cocoon.sitemap.component.logger sitemap.generator.mp3directory
32  * @cocoon.sitemap.component.documentation.caching
33  * Uses the last modification date of the directory and the contained files
34  *
35  *
36  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
37  * @version CVS $Id: MP3DirectoryGenerator.java 225247 2005-07-26 07:37:04Z cziegeler $
38  */

39 public class MP3DirectoryGenerator extends DirectoryGenerator
40 {
41     // MP3 Constants
42
private static final int VERSION_MPEG1 = 3;
43     // private static final int VERSION_MPEG2 = 2;
44
private static final int MODE_DUAL_CHANNEL = 2;
45     private static final int MODE_JOINT_STEREO = 1;
46     private static final int MODE_SINGLE_CHANNEL = 3;
47     private static final int MODE_STEREO = 0;
48     private static final int VBR_FRAMES_FLAG = 1;
49     // private static final int VBR_BYTES_FLAG = 2;
50
// private static final int VBR_TOC_FLAG = 4;
51
// private static final int VBR_SCALE_FLAG = 8;
52

53     // Attributes
54
protected final static String JavaDoc MP3_FREQUENCY_ATTR_NAME = "frequency";
55     protected final static String JavaDoc MP3_BITRATE_ATTR_NAME = "bitrate";
56     protected final static String JavaDoc MP3_MODE_ATTR_NAME = "mode";
57     protected final static String JavaDoc MP3_VBR_ATTR_NAME = "variable-rate";
58
59     protected final static String JavaDoc MP3_TITLE_ATTR_NAME = "title";
60     protected final static String JavaDoc MP3_ARTIST_ATTR_NAME = "artist";
61     protected final static String JavaDoc MP3_ALBUM_ATTR_NAME = "album";
62     protected final static String JavaDoc MP3_YEAR_ATTR_NAME = "year";
63     protected final static String JavaDoc MP3_COMMENT_ATTR_NAME = "comment";
64     protected final static String JavaDoc MP3_TRACK_ATTR_NAME = "track";
65     protected final static String JavaDoc MP3_GENRE_ATTR_NAME = "genre";
66
67     /**
68      * Extends the <code>setNodeAttributes</code> method from the
69      * <code>DirectoryGenerator</code> by adding MP3 tag attributes
70      * if the path is a MP3 file with valid tag.
71      */

72     protected void setNodeAttributes(File JavaDoc path) throws SAXException JavaDoc {
73         super.setNodeAttributes(path);
74         if (path.isDirectory()) {
75             return;
76         }
77
78         RandomAccessFile JavaDoc in = null;
79         try {
80             in = new RandomAccessFile JavaDoc(path, "r");
81             setID3HeaderAttributes(in);
82             setID3TagAttributes(in);
83         } catch (IOException JavaDoc e) {
84             getLogger().debug("Could not set attributes for " + path, e);
85         } finally {
86             if(in != null) try{ in.close(); }catch(IOException JavaDoc ignored){}
87         }
88     }
89
90     /**
91      * Read ID3 Tag
92      */

93     private void setID3TagAttributes(RandomAccessFile JavaDoc in) throws IOException JavaDoc {
94         String JavaDoc s;
95
96         // TAG takes 128 bytes
97
if (in.length() < 128) return;
98         in.seek(in.length() - 128);
99         byte [] buf = new byte[128];
100         // Read TAG
101
if (in.read(buf,0, 128) != 128) return;
102         // Check TAG presence
103
if(buf[0] != 'T' || buf[1] != 'A' || buf[2] != 'G') return;
104
105         s = getID3TagValue(buf, 3, 30);
106         if(s.length() > 0)
107             attributes.addAttribute("", MP3_TITLE_ATTR_NAME, MP3_TITLE_ATTR_NAME, "CDATA", s);
108         s = getID3TagValue(buf, 33,30);
109         if(s.length() > 0)
110             attributes.addAttribute("", MP3_ARTIST_ATTR_NAME, MP3_ARTIST_ATTR_NAME, "CDATA", s);
111         s = getID3TagValue(buf, 63,30);
112         if(s.length() > 0)
113             attributes.addAttribute("", MP3_ALBUM_ATTR_NAME, MP3_ALBUM_ATTR_NAME, "CDATA", s);
114         s = getID3TagValue(buf, 93, 4);
115         if(s.length() > 0)
116             attributes.addAttribute("", MP3_YEAR_ATTR_NAME, MP3_YEAR_ATTR_NAME, "CDATA", s);
117         s = getID3TagValue(buf, 97,29);
118         if(s.length() > 0)
119             attributes.addAttribute("", MP3_COMMENT_ATTR_NAME, MP3_COMMENT_ATTR_NAME, "CDATA", s);
120         if(buf[126] > 0)
121             attributes.addAttribute("", MP3_TRACK_ATTR_NAME, MP3_TRACK_ATTR_NAME, "CDATA",
122                 Byte.toString(buf[126]));
123         if(buf[127] > 0)
124             attributes.addAttribute("", MP3_GENRE_ATTR_NAME, MP3_GENRE_ATTR_NAME, "CDATA",
125                 Byte.toString(buf[127]));
126     }
127
128     private String JavaDoc getID3TagValue(byte[] buf, int offset, int length) {
129         String JavaDoc s = new String JavaDoc(buf, offset, length);
130         int index = s.indexOf(0x00);
131         if (index != -1) {
132             s = s.substring(0, index);
133         }
134         return s.trim();
135     }
136
137     private void setID3HeaderAttributes(RandomAccessFile JavaDoc in) throws IOException JavaDoc {
138         byte[] buffer = new byte[4];
139
140         // http://floach.pimpin.net/grd/mp3info/frmheader/index.html
141
if (in.read(buffer, 0, 3) != 3) {
142             return;
143         }
144         int header = ((buffer[0] << 16) & 0x00FF0000) | ((buffer[1] << 8) & 0x0000FF00) | ((buffer[2] << 0) & 0x000000FF);
145         do {
146             header <<= 8;
147             if (in.read(buffer, 3, 1) != 1) {
148                 return;
149             }
150             header |= (buffer[3] & 0x000000FF);
151         } while (!isSyncMark(header));
152
153         int version = (header >>> 19) & 3;
154         int layer = 4 - (header >>> 17) & 3;
155         // int protection = (header >>> 16) & 1;
156
int bitrate = (header >>> 12) & 0xF;
157         int frequency = (header >>> 10) & 3;
158         // Value 3 is reserved
159
if (frequency == 3) {
160             return;
161         }
162         // int padding = (header >>> 9) & 1;
163
int mode = ((header >>> 6) & 3);
164
165         attributes.addAttribute("", MP3_FREQUENCY_ATTR_NAME, MP3_FREQUENCY_ATTR_NAME, "CDATA",
166             frequencyString(version, frequency));
167         attributes.addAttribute("", MP3_MODE_ATTR_NAME, MP3_MODE_ATTR_NAME, "CDATA",
168             mode(mode));
169
170         int frames = getVBRHeaderFrames(in, version, mode);
171         if (frames != -1) {
172             // get average frame size by deviding fileSize by the number of frames
173
float medFrameSize = (float)in.length() / frames;
174             // This does not work properly: (version == VERSION_MPEG1? 12000.0:144000.0)
175
bitrate = (int)(medFrameSize * frequency(version, frequency) / 144000.0);
176             attributes.addAttribute("", MP3_BITRATE_ATTR_NAME, MP3_BITRATE_ATTR_NAME, "CDATA",
177                 Integer.toString(bitrate));
178         } else {
179             attributes.addAttribute("", MP3_BITRATE_ATTR_NAME, MP3_BITRATE_ATTR_NAME, "CDATA",
180                 bitrate(version, layer, bitrate));
181         }
182     }
183
184     private static boolean isSyncMark(int header) {
185         boolean sync = ((header & 0xFFF00000) == 0xFFF00000);
186         // filter out invalid sample rate
187
if (sync) sync = ((header >>> 10) & 3) != 3;
188         // filter out invalid layer
189
if (sync) sync = ((header >>> 17) & 3) != 0;
190         // filter out invalid version
191
if (sync) sync = ((header >>> 19) & 3) != 1;
192         return sync;
193     }
194
195     private int getVBRHeaderFrames(RandomAccessFile JavaDoc in, int version, int mode) throws IOException JavaDoc {
196         byte[] buffer = new byte[12];
197
198         // Try to detect VBR header
199
int skip;
200         if (version == VERSION_MPEG1) {
201             if (mode == MODE_SINGLE_CHANNEL) skip = 17;
202             else skip = 32;
203         } else { // mpeg version 2 or 2.5
204
if (mode == MODE_SINGLE_CHANNEL) skip = 9;
205             else skip = 17;
206         }
207         while (skip > 0) {
208             if (in.read() == -1) return -1;
209             skip --;
210         }
211
212         if (in.read(buffer, 0, 12) != 12) {
213             return -1;
214         }
215         if (buffer[0] != 'X' || buffer[1] != 'i' || buffer[2] != 'n' || buffer[3] != 'g'){
216             return -1;
217         }
218
219         attributes.addAttribute("", MP3_VBR_ATTR_NAME, MP3_VBR_ATTR_NAME, "CDATA",
220             "yes");
221
222         int flags =
223             ((buffer[4] & 0xFF) << 24) |
224             ((buffer[5] & 0xFF) << 16) |
225             ((buffer[6] & 0xFF) << 8) |
226              (buffer[7] & 0xFF);
227
228         if ((flags & VBR_FRAMES_FLAG) == VBR_FRAMES_FLAG){
229             int frames =
230                 ((buffer[ 8] & 0xFF) << 24) |
231                 ((buffer[ 9] & 0xFF) << 16) |
232                 ((buffer[10] & 0xFF) << 8) |
233                  (buffer[11] & 0xFF);
234             return frames;
235         } else {
236             return -1;
237         }
238     }
239
240     // version - layer - bitrate index
241
private static final String JavaDoc bitrates[][][] = {
242       {
243         // MPEG2 - layer 1
244
{"free format", "32", "48", "56", "64", "80", "96", "112", "128", "144", "160", "176", "192", "224", "256", "forbidden"},
245         // MPEG2 - layer 2
246
{"free format", "8", "16", "24", "32", "40", "48", "56", "64", "80", "96", "112", "128", "144", "160", "forbidden"},
247         // MPEG2 - layer 3
248
{"free format", "8", "16", "24", "32", "40", "48", "56", "64", "80", "96", "112", "128", "144", "160", "forbidden"}
249       },
250       {
251         // MPEG1 - layer 1
252
{"free format", "32", "64", "96", "128", "160", "192", "224", "256", "288", "320", "352", "384", "416", "448", "forbidden"},
253         // MPEG1 - layer 2
254
{"free format", "32", "48", "56", "64", "80", "96", "112", "128", "160", "192", "224", "256", "320", "384", "forbidden"},
255         // MPEG1 - layer 3
256
{"free format", "32", "40", "48", "56", "64", "80" , "96", "112", "128", "160", "192", "224", "256", "320", "forbidden"}
257       }
258     };
259
260     private static String JavaDoc bitrate(int version, int layer, int bitrate_index) {
261         return bitrates[version & 1][layer - 1][bitrate_index];
262     }
263
264     private static String JavaDoc mode(int mode) {
265         switch(mode)
266         {
267         case MODE_STEREO:
268             return "Stereo";
269         case MODE_JOINT_STEREO:
270             return "Joint stereo";
271         case MODE_DUAL_CHANNEL:
272             return "Dual channel";
273         case MODE_SINGLE_CHANNEL:
274             return "Single channel";
275         }
276         return null;
277     }
278
279     private static final int frequencies[][] = {
280         {32000, 16000, 8000}, //MPEG 2.5
281
{ 0, 0, 0}, //reserved
282
{22050, 24000, 16000}, //MPEG 2
283
{44100, 48000, 32000} //MPEG 1
284
};
285
286     private static int frequency(int version, int frequency) {
287         return frequencies[version][frequency];
288     }
289
290     private static String JavaDoc frequencyString(int version, int frequency) {
291         return String.valueOf((float)frequency(version, frequency)/1000);
292     }
293 }
294
Popular Tags