public boolean parse(byte[] data, int ofs) { final int origOfs = ofs; int fh32 = (data[ofs] << 24) | ((data[ofs + 1] & 0xFF) << 16) | ((data[ofs + 2] & 0xFF) << 8) | (data[ofs + 3] & 0xFF); FrameHeader fh = new FrameHeader(fh32); ofs += 4 + fh.getSideInfoSize(); boolean xingTag = (data[ofs] == 0x58) // 'Xing' && (data[ofs + 1] == 0x69) && (data[ofs + 2] == 0x6E) && (data[ofs + 3] == 0x67); boolean infoTag = (data[ofs] == 0x49) // 'Info' && (data[ofs + 1] == 0x6E) && (data[ofs + 2] == 0x66) && (data[ofs + 3] == 0x6F); if (!xingTag && !infoTag) { return false; } ofs += 4; this.xingTag = xingTag; this.infoTag = infoTag; this.frameSize = fh.getFrameSize(); System.arraycopy(data, origOfs, bb, 0, frameSize); int flags = data[ofs + 3] & 0xFF; ofs += 4; if ((flags & 0x01) != 0) ofs += 4; // skip frame count if ((flags & 0x02) != 0) ofs += 4; // skip byte count if ((flags & 0x04) != 0) ofs += 100; // skip seek table if ((flags & 0x08) != 0) ofs += 4; // skip VBR scale int tagEndOfs = ofs + 0x24; int crc = 0; for (int i = origOfs; i < tagEndOfs - 2; i++) { crc = CRC16.updateLAME(crc, data[i] & 0xFF); } this.lameTag = (data[ofs] == 0x4C) && (data[ofs + 1] == 0x41) && (data[ofs + 2] == 0x4D) && (data[ofs + 3] == 0x45); if (!this.lameTag) { this.lameTag = (data[ofs] == 0x47) && (data[ofs + 1] == 0x4F) && (data[ofs + 2] == 0x47) && (data[ofs + 3] == 0x4F); } this.lameTag |= ((((data[tagEndOfs - 2] << 8) | (data[tagEndOfs - 1] & 0xFF)) ^ crc) & 0xFFFF) == 0; if (this.lameTag) { lameTagOfs = ofs - origOfs - 4; } ofs += 0x15; int t = data[ofs + 1] & 0xFF; encDelay = ((data[ofs] & 0xFF) << 4) | (t >>> 4); encPadding = ((t & 0x0F) << 8) | (data[ofs + 2] & 0xFF); if (!this.lameTag) { if (encDelay > 2880 || encPadding > 2304) { encDelay = 576; encPadding = 0; } } return true; }
private void setupMPEG(String fname) { File f = new File(fname); // read xing tag XingInfo xi = null; try { xi = new XingInfo(f); setInt(BITRATE, xi.getBitRate() * 1000); setInt(SAMPLERATE, xi.getSampleRate()); setInt(CHANNELS, xi.isMono() ? 1 : 2); setInt(LENGTH, xi.getLength()); setString(CODEC, xi.toString()); } catch (Throwable ex) { // ignore.. } finally { if (xi != null) { xi.close(); } } // read frame headers if no xing tag if (xi == null) { FrameHeader fh = null; try { fh = new FrameHeader(f); // fh.examineAll(); setInt(BITRATE, fh.getBitRate() * 1000); setInt(SAMPLERATE, fh.getSampleRate()); setInt(CHANNELS, fh.isMono() ? 1 : 2); setInt(LENGTH, fh.getLength()); setString(CODEC, fh.toString()); } catch (Throwable ex) { // ignore.. } finally { if (fh != null) { fh.close(); } } } // read id3v1 tags try { ID3v1 v1 = new ID3v1(f); String t; t = v1.getArtist(); if (t != null && t.trim().length() != 0) { setString(PERFORMER, t); } t = v1.getAlbum(); if (t != null && t.trim().length() != 0) { setString(ALBUM, t); } t = v1.getComment(); if (t != null && t.trim().length() != 0) { setString(COMMENT, t); } t = v1.getYear(); if (t != null && t.trim().length() != 0) { setString(RECORDING_DATE, t); } t = v1.getTitle(); if (t != null && t.trim().length() != 0) { setString(TITLE, t); } t = ID3v1.getGenreString(v1.getGenre()); if (t != null && t.trim().length() != 0) { setString(GENRE, t); } int trknum = v1.getTrkNum(); if (trknum != -1) { setInt(TRKNUM, trknum); } } catch (Throwable ex) { // ignore } // read id3v2 tags try { ID3v2 v2 = new ID3v2(f); String[][] keys = { {"TPE1", PERFORMER}, {"TPE2", BAND}, {"TPE3", CONDUCTOR}, {"TPE4", REMIXER}, {"TALB", ALBUM}, {"TIT1", SECTION}, {"TIT2", TITLE}, {"TIT3", SUBTITLE}, // TODO: possiblity to append frames into one field {"TIME", RECORDING_DATE}, {"TDAT", RECORDING_DATE}, {"TYER", RECORDING_DATE}, {"TCOM", ARTIST}, {"TEXT", LYRICS}, {"TCON", GENRE}, {"TKEY", KEY}, {"TLAN", LANGUAGE}, }; for (int i = 0; i < keys.length; i++) { String frame = keys[i][0]; String key = keys[i][1]; try { TextFrame tf = (TextFrame) v2.getFrame(frame); if (tf != null) { setString(key, tf.getText()); } } catch (Exception ex) { // ignore } } try { TextFrame tf = (TextFrame) v2.getFrame("TRCK"); if (tf != null) { String t = tf.getText(); if (t.indexOf("/") != -1) { t = t.substring(0, t.indexOf("/")); } setInt(TRKNUM, Integer.parseInt(t)); } } catch (Exception ex) { // ignore } } catch (Throwable ex) { // ignore } // fall back to track basename for title if (getString(TITLE) == null) { int i; String title = fname; // strip directory name i = title.lastIndexOf('/'); if (i != 0) { title = title.substring(i + 1); } // strip file extension i = title.lastIndexOf('.'); if (i != 0) { title = title.substring(0, i); } setString(TITLE, title); } }
public static int createHeaderFrame( FrameHeader toBeSimilar, boolean vbr, float kbps, int frameCount, int musicBytes, int vbrScale, byte[] seektable, int encDelay, int encPadding, XingInfoLameTagFrame srcTag, byte[] dest, final int dofs, final int maskATH) { int fh32 = toBeSimilar.getHeader32() | 0x00010000; // disable CRC if any int frameSize = 0; int tagOffset = 0; { // calculate optimal header frame size FrameHeader tmp = new FrameHeader(); float minDist = 9999; for (int i = 1; i < 15; i++) { int th32 = (fh32 & 0xFFFF0FFF) | (i << 12); tmp.setHeader32(th32); if (tmp.getFrameSize() >= 0xC0) { int ikbps = tmp.getBitrateKBPS(); float dist = Math.abs(kbps - ikbps); if (dist < minDist) { minDist = dist; fh32 = th32; frameSize = tmp.getFrameSize(); tagOffset = tmp.getSideInfoSize() + 4; } } } } tagOffset += dofs; Arrays.fill(dest, dofs, dofs + frameSize, (byte) 0); dest[dofs] = (byte) (fh32 >>> 24); dest[dofs + 1] = (byte) (fh32 >>> 16); dest[dofs + 2] = (byte) (fh32 >>> 8); dest[dofs + 3] = (byte) fh32; if (vbr) { dest[tagOffset++] = (byte) 0x58; // X dest[tagOffset++] = (byte) 0x69; // i dest[tagOffset++] = (byte) 0x6E; // n dest[tagOffset++] = (byte) 0x67; // g } else { dest[tagOffset++] = (byte) 0x49; // I dest[tagOffset++] = (byte) 0x6E; // n dest[tagOffset++] = (byte) 0x66; // f dest[tagOffset++] = (byte) 0x6F; // o } dest[tagOffset++] = 0; dest[tagOffset++] = 0; dest[tagOffset++] = 0; dest[tagOffset++] = 0x0F; dest[tagOffset++] = (byte) (frameCount >>> 24); dest[tagOffset++] = (byte) (frameCount >>> 16); dest[tagOffset++] = (byte) (frameCount >>> 8); dest[tagOffset++] = (byte) frameCount; int temp = frameSize + musicBytes; dest[tagOffset++] = (byte) (temp >>> 24); dest[tagOffset++] = (byte) (temp >>> 16); dest[tagOffset++] = (byte) (temp >>> 8); dest[tagOffset++] = (byte) temp; System.arraycopy(seektable, 0, dest, tagOffset, 100); tagOffset += 100; dest[tagOffset++] = (byte) (vbrScale >>> 24); dest[tagOffset++] = (byte) (vbrScale >>> 16); dest[tagOffset++] = (byte) (vbrScale >>> 8); dest[tagOffset++] = (byte) vbrScale; if (srcTag != null && srcTag.isValid() && srcTag.lameTagPresent()) { System.arraycopy(srcTag.bb, srcTag.lameTagOfs, dest, tagOffset - 4, 40); tagOffset += 4; // delete LAME's replaygain tag for (int i = 0; i < 8; i++) { dest[tagOffset + 0x07 + i] = 0; } // deleting no-gap flags ... dest[tagOffset + 0x0F] &= maskATH; } else { dest[tagOffset++] = (byte) 0x4C; // L dest[tagOffset++] = (byte) 0x41; // A dest[tagOffset++] = (byte) 0x4D; // M dest[tagOffset++] = (byte) 0x45; // E } encDelay = Math.max(0, Math.min(encDelay, 4095)); encPadding = Math.max(0, Math.min(encPadding, 4095)); // write encDelay / encPadding ... dest[tagOffset + 0x11] = (byte) (encDelay >>> 4); dest[tagOffset + 0x12] = (byte) ((encDelay & 0xF) << 4 | (encPadding >>> 8)); dest[tagOffset + 0x13] = (byte) encPadding; temp = frameSize + musicBytes; dest[tagOffset + 0x18] = (byte) (temp >>> 24); dest[tagOffset + 0x19] = (byte) (temp >>> 16); dest[tagOffset + 0x1A] = (byte) (temp >>> 8); dest[tagOffset + 0x1B] = (byte) temp; int crc = 0; for (int i = 0; i < 190; i++) { crc = CRC16.updateLAME(crc, dest[dofs + i] & 0xFF); } dest[tagOffset + 0x1E] = (byte) (crc >>> 8); dest[tagOffset + 0x1F] = (byte) crc; return frameSize; }