public void adapt(File file, ParserContext ctx) throws IOException {
long SECS = 1000;
long MINS = 60 * SECS;
long HRS = 60 * MINS;
// Add the MetaData to the tree!
DataSource ftk = new FileDataSource(file);
ctx.fireStartParseEvent("mp3");
writeFileInfo(file, ctx);
try {
boolean headFound = false;
// read the ID3v2.x tag...
ftk.setPosition(0);
String id3 = FXUtil.getFixedStringValue(ftk, 3);
if (id3.equalsIgnoreCase("ID3")) {
headFound = true;
ctx.fireStartParseEvent("header");
byte[] ver = ftk.getData(1);
byte[] subVer = ftk.getData(1);
byte[] fg = ftk.getData(1);
byte[] s = ftk.getData(4);
int version = (int) FXUtil.getNumericalValue(ver, true);
int subVersion = (int) FXUtil.getNumericalValue(subVer, true);
int flags = (int) FXUtil.getNumericalValue(fg, true);
int size = (int) FXUtil.getNumericalValue(s, true);
boolean unsync = BitFieldUtil.isSet(flags, 64);
boolean ext = BitFieldUtil.isSet(flags, 32);
boolean exp = BitFieldUtil.isSet(flags, 16);
boolean foot = BitFieldUtil.isSet(flags, 8);
size = getSyncsafeInt(size);
ctx.fireParseEvent("tag-version", "ID3v2." + version + "."
+ subVersion);
// if there is an extended header then that needs to be read...
if (ext) {
byte[] extS = ftk.getData(4);
int extSize = (int) FXUtil.getNumericalValue(extS, true);
extSize = getSyncsafeInt(extSize);
// move past the crap...
ftk.setPosition(ftk.getPosition() + extSize - 4);
}
// allow for the footer to be present
if (foot) {
size -= 10;
}
// now read the frames...
int read = 0;
while (read < size) {
String frameType = FXUtil.getFixedStringValue(ftk, 4);
byte[] fS = ftk.getData(4);
byte[] frameFlags = ftk.getData(2);
int frameSize = (int) FXUtil.getNumericalValue(fS, true);
frameSize = getSyncsafeInt(frameSize);
String data = FXUtil.getFixedStringValue(ftk, frameSize);
ID3v2Tag v2tag = MP3Util.getTag(frameType, data);
if (v2tag != null) {
HashMap map = v2tag.getMap();
if (map != null) {
ctx.fireParseEvent(v2tag.getName(), v2tag
.getValue(), false, map);
} else {
ctx.fireParseEvent(v2tag.getName(), v2tag
.getValue());
}
}
read += frameSize + 10;
}
ctx.fireEndParseEvent("header");
}
// read the ID3v1.x tag...
if (!headFound) {
ftk.setPosition(file.length() - 128);
String tag = FXUtil.getFixedStringValue(ftk, 3);
if (tag.equalsIgnoreCase("TAG")) {
ctx.fireStartParseEvent("header");
ctx.fireParseEvent("tag-version", "ID3v1.x");
ctx.fireParseEvent("version", "1.x");
id3v11Element.read(ftk, ctx);
ctx.fireEndParseEvent("header");
headFound = true;
}
}
// what to do if headFound = false?
if (!headFound) {
ctx.fireStartParseEvent("header");
ctx.fireStartParseEvent("!-- no header found--");
ctx.fireEndParseEvent("header");
}
// now scan the music file - as a stream - to find a frame start
// index point
// MP3's can be cut in half and still work, like worms!
boolean seek = true;
int seekTo = 0;
byte previous = 0x00;
byte current = 0x00;
while (seekTo < file.length() && seek) {
ftk.setPosition(seekTo);
byte[] c = ftk.getData(1024);
for (int i = 0; i < c.length; i++) {
previous = current;
current = c[i];
seekTo++;
if (previous == (byte) 0xFF) {
// posible frame buffer index point...
// if the current has the most significant 3 bits set
// then we are in!
if ((current & 0xE0) == 0xE0) {
seekTo -= 2;
seek = false;
break;
}
}
}
}
// the above line finds the next sync index point - now we are good
// to go!
ftk.setPosition(seekTo);
int fsync = (int) FXUtil.getNumericalValue(ftk, 1, false);
int type = (int) FXUtil.getNumericalValue(ftk, 1, false);
int version = BitFieldUtil.getNumber(type, 5, 4);
int layer = BitFieldUtil.getNumber(type, 3, 2);
boolean pro = BitFieldUtil.isSet(type, 0x01);
int bitRate = (int) FXUtil.getNumericalValue(ftk, 1, false);
int bitRateIndex = BitFieldUtil.getNumber(bitRate, 8, 5);
int realBitRate = getBitRate(version, layer, bitRateIndex);
int sampleRateIndex = BitFieldUtil.getNumber(bitRate, 4, 3);
int realSampleRate = getSampleRate(version, layer, sampleRateIndex);
boolean pad = BitFieldUtil.isSet(bitRate, 0x02);
boolean priv = BitFieldUtil.isSet(bitRate, 0x01);
int mode = (int) FXUtil.getNumericalValue(ftk, 1, false);
int channels = BitFieldUtil.getNumber(mode, 8, 7);
int modeExt = BitFieldUtil.getNumber(mode, 6, 5);
boolean copyright = BitFieldUtil.isSet(mode, 0x08);
boolean original = BitFieldUtil.isSet(mode, 0x04);
int emph = BitFieldUtil.getNumber(mode, 2, 1);
long durationMs = getDuration(realBitRate, realSampleRate,
channels, file.length());
long durationHrs = durationMs / HRS;
long durationMins = (durationMs - (durationHrs * HRS)) / MINS;
long durationSecs = (durationMs - (durationHrs * HRS) - (durationMins * MINS))
/ SECS;
long durationMsecs = durationMs - (durationHrs * HRS)
- (durationMins * MINS) - (durationSecs * SECS);
ctx.fireStartParseEvent("MPEG");
ctx.fireParseEvent("version-name", getVersionName(version));
ctx.fireParseEvent("version", getVersionNumber(version));
ctx.fireParseEvent("layer-name", getLayerName(layer));
ctx.fireParseEvent("layer", getLayerNumber(layer));
ctx.fireParseEvent("protection", pro);
ctx.fireParseEvent("bit-rate", realBitRate);
ctx.fireParseEvent("bit-rate-unit", "kbps");
ctx.fireParseEvent("sample-rate", realSampleRate);
ctx.fireParseEvent("sample-rate-unit", "Hz");
ctx.fireParseEvent("mode", getMode(channels));
ctx.fireParseEvent("channels", getChannels(channels));
if (channels == 1) {
ctx.fireParseEvent("mode-extension", getModeExtension(layer,
modeExt));
}
ctx.fireStartParseEvent("duration");
ctx.fireParseEvent("total-milliseconds", durationMs);
ctx.fireParseEvent("hours", durationHrs);
ctx.fireParseEvent("minutes", durationMins);
ctx.fireParseEvent("seconds", durationSecs);
ctx.fireParseEvent("ms", durationMsecs);
ctx.fireEndParseEvent("duration");
ctx.fireParseEvent("copyright", copyright);
ctx.fireParseEvent("original", original);
ctx.fireParseEvent("emph", getEmph(emph));
ctx.fireEndParseEvent("MPEG");
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
ftk.close();
}
ctx.fireEndParseEvent("mp3");
}