Source code: com/tuneology/avm/InfoReader.java
1 /*
2 InfoReader.java
3
4 Copyright (C) 2002 Fran Taylor
5
6 This file is part of java-avm.
7
8 java-avm is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 java-avm is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with java-avm; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA
22
23 $Id: InfoReader.java,v 1.6 2002/11/05 08:15:32 xnarf Exp $
24
25 */
26
27 package com.tuneology.avm;
28
29 import java.io.*;
30 import java.util.*;
31 import helliker.id3.*;
32
33 /**
34 * This class is used to read and write meta-information
35 * to media files.
36 *
37 * @version $Id: InfoReader.java,v 1.6 2002/11/05 08:15:32 xnarf Exp $
38 *
39 * @author Fran Taylor
40 */
41
42 public class InfoReader {
43 /**
44 * Returns the size that the file will have after it has been
45 * converted according to the specified preferences.
46 *
47 * @param ent
48 * @param prefs
49 * @return the estimated size in bytes.
50 */
51 public static long calculateSize(TocEntry ent, FormatPrefs prefs) {
52 return (((long) ent.len) * ((long) prefs.bitRate) * 8) / 75;
53 }
54 /**
55 * Returns the size that the album will have after it has been
56 * converted according to the specified preferences.
57 *
58 * @param info
59 * @param prefs
60 * @return the estimages size in bytes;
61 */
62 public static long calculateSize(DiscInfo info, FormatPrefs prefs) {
63 long retval = 0;
64 for(int i = 0; i < info.tocs.length; i++)
65 retval += calculateSize(info.tocs[i], prefs);
66 return retval;
67 }
68 /**
69 * Reads header information from the specified file and fills in the supplied TocEntry
70 * object.
71 */
72 public static void readFileInfo(File f, final TocEntry info) throws Exception {
73 info.fileSize = f.length();
74 switch(AudioConverter.getFileFormat(f)) {
75 case FormatPrefs.MP3_FORMAT:
76 MP3File mp3 = new MP3File(f.getCanonicalPath());
77 info.title = mp3.getTitle().trim();
78 info.album = mp3.getAlbum().trim();
79 info.artist = mp3.getArtist().trim();
80 info.rate = mp3.getBitRate() * 1024;
81 info.len = (int) (mp3.getPlayingTime() * 75);
82 info.genre = mp3.getGenre();
83 info.arranger = mp3.getComposer().trim();
84 info.comment = mp3.getComment().trim();
85 try {
86 info.year = Integer.parseInt(mp3.getYear());
87 }
88 catch (NumberFormatException ex) { }
89 return;
90 case FormatPrefs.OGG_FORMAT:
91 // someday we will use libogg but in the meantime
92 // we can run ogginfo and parse what it prints out
93 Vector cmd = getOggInfoCmd();
94 cmd.add(f.getCanonicalPath());
95 Native.runCommand(cmd, null, new Native.LineReader() {
96 public boolean processLine(String str, boolean e) throws IOException {
97 if (str.indexOf("Unable to open") != -1) { throw new IOException(str); }
98 else if (str.indexOf("Nominal bitrate:") != -1) {
99 StringTokenizer tok = new StringTokenizer(str, " :.");
100 tok.nextToken();
101 tok.nextToken();
102 String rt = tok.nextToken();
103 info.rate = Integer.parseInt(rt) * 1024;
104 return true;
105 }
106 if (!started) {
107 if (str.indexOf("comments section") != -1) {
108 started = true;
109 }
110 return true;
111 }
112 int eq = str.indexOf("=");
113 if (eq != -1) {
114 String key = str.substring(0, eq).trim();
115 String val = str.substring(eq + 1);
116 if (key.equals("title")) { info.title = val; }
117 else if (key.equals("artist")) { info.artist = val; }
118 else if (key.equals("album")) { info.album = val; }
119 else if (key.equals("genre")) { info.genre = val; }
120 return true;
121 } else if (str.indexOf("Playback length") != -1) {
122 eq = str.indexOf(":");
123 if (eq != -1) {
124 String tm = str.substring(eq + 1);
125 StringTokenizer tok = new StringTokenizer(tm, ":ms ");
126 String m = tok.nextToken();
127 String s = tok.nextToken();
128 info.len = ((Integer.parseInt(m) * 60) + Integer.parseInt(s)) * 75;
129 }
130 }
131 return true;
132 }
133 boolean started = false;
134 }, false);
135 break;
136 }
137 }
138 /**
139 * Writes changed meta-info to the music file.
140 * Currently only supports MP3.
141 */
142 public static void writeInfo(File f, TocEntry ent) throws Exception {
143 switch(AudioConverter.getFileFormat(f)) {
144 case FormatPrefs.MP3_FORMAT:
145 MP3File mp3 = new MP3File(f.getCanonicalPath());
146 mp3.setTitle(ent.title);
147 mp3.setArtist(ent.artist);
148 mp3.setAlbum(ent.album);
149 mp3.setYear(Integer.toString(ent.year));
150 mp3.setComment(ent.comment);
151 mp3.setGenre(ent.genre);
152 mp3.setComposer(ent.arranger);
153 mp3.writeTags();
154 break;
155 case FormatPrefs.OGG_FORMAT:
156 case FormatPrefs.WMA_FORMAT:
157 break;
158 }
159 }
160 /**
161 *
162 * @return the command for running ogginfo.
163 */
164 static public Vector getOggInfoCmd() {
165 return Native.parseUserInput(oggInfoCmd);
166 }
167 /**
168 * Sets the path for the ogginfo command.
169 *
170 * @param cmd the command for running ogginfo.
171 */
172 public static void setOggInfoCmd(String cmd) { oggInfoCmd = cmd; }
173 /**
174 * Returns the list of ID3 genres.
175 *
176 * @return a string array of ID3 genres.
177 */
178 public static String[] getId3GenreArray() { return genreArray; }
179 private static int parseInt(String str, int defval) {
180 try {
181 return Integer.parseInt(str);
182 } catch (Exception ex) {
183 return defval;
184 }
185 }
186 private static String getGenre(int genre) {
187 if ((genre < 0) || (genre >= genreArray.length)) {
188 return "Unknown";
189 } else {
190 return genreArray[genre];
191 }
192 }
193 private static String getString(byte tag[], int p1, int p2) {
194 int endChar;
195 for(endChar = 0; endChar < p2; endChar++) if (tag[p1 + endChar] == 0) break;
196 return new String(tag,p1,endChar);
197 }
198 private static void encodeString(byte[] buf, int pos, String str, int len) {
199 byte[] bytes;
200 try {
201 bytes = str.getBytes(encoding);
202 } catch (UnsupportedEncodingException e) {
203 return;
204 }
205 byte[] data = new byte[len];
206 System.arraycopy(bytes, 0, data, 0, Math.min(bytes.length, len));
207 System.arraycopy(data, 0, buf, pos, len);
208 }
209 private static int getInt(byte[] wavbuf, int ind) {
210 int val = ((((int) wavbuf[ind]) & 0xff) |
211 ((((int) wavbuf[ind + 1]) & 0xff) << 8) |
212 ((((int) wavbuf[ind + 2]) & 0xff) << 16) |
213 ((((int) wavbuf[ind + 3]) & 0xff) << 24));
214 return val;
215 }
216 private static int getLen(byte[] wavbuf) { return getInt(wavbuf, 4); }
217 private static int getBytesPerSec(byte[] wavbuf) { return getInt(wavbuf, 28); }
218 /**
219 * This function processes the name/value pairs produced by ogginfo.
220 */
221 private static void processOggInfoKey(TocEntry ent, String key, String val) {
222 }
223 private static String oggInfoCmd;
224 private static final String encoding = "UTF-8";
225 private static final String[] genreArray = {"Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age",
226 "Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska",
227 "Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion",
228 "Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","AlternRock",
229 "Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic",
230 "Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy",
231 "Cult","Gangsta","Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave",
232 "Psychedelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro",
233 "Musical","Rock & Roll","Hard Rock","Folk","Folk-Rock","National Folk","Swing","Fast Fusion","Bebob",
234 "Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock",
235 "Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson",
236 "Opera","Chamber Music","Sonata","Symphony","Booty Brass","Primus","Porn Groove","Satire","Slow Jam",
237 "Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhytmic Soul","Freestyle","Duet","Punk Rock",
238 "Drum Solo","Acapella","Euro-House","Dance Hall"}; // > 125 Unknown
239 private static int[] brateList = {-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1};
240 }
241
242
243 /*
244 Local Variables:
245 mode:java
246 indent-tabs-mode:nil
247 c-basic-offset:4
248 c-indent-level:4
249 c-continued-statement-offset:4
250 c-brace-offset:-4
251 c-brace-imaginary-offset:-4
252 c-argdecl-indent:0
253 c-label-offset:0
254 End:
255 */