package javazoom.jlGui.tag; /** * OggVorbisInfo. * *----------------------------------------------------------------------- * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *---------------------------------------------------------------------- */ import java.io.*; import java.util.*; import com.jcraft.jorbis.*; /** * This class gives information (audio format and comments) about Ogg Vorbis file. */ public class OggVorbisInfo implements TagInfo { private int serial = 0; private int channels = 0; private int version = 0; private int rate = 0; private int minbitrate = 0; private int maxbitrate = 0; private int averagebitrate = 0; private int nominalbitrate = 0; private long totalms = 0; private String vendor = null; private String location = null; private long size = 0; private int track = -1; private String year = null; private String genre = null; private String title = null; private String artist = null; private String album = null; private Vector comments = null; private byte[] header; private byte[] packet; private long[] crc_lookup = new long[256]; /** * Constructor. * @param input * @throws IOException * @throws JOrbisException */ public OggVorbisInfo(String input) throws IOException, JOrbisException { VorbisFile vorbisfile = null; File file = new File(input); size = file.length(); location = input; FileInputStream fis = new FileInputStream(input); checkAudioFormat(fis); fis.close(); vorbisfile = new VorbisFile(input); this.loadInfo(vorbisfile); } /** * Check InputStream is Ogg Vorbis Stream. * @param s * @throws JOrbisException * @throws IOException */ private void checkAudioFormat(InputStream s) throws JOrbisException, IOException { for (int i = 0; i < 256; i++) { crc_lookup[i] = _ogg_crc_entry(i); // read in the minimal packet header } byte[] head = new byte[27]; int bytes = s.read(head); if (bytes < 27) { throw new JOrbisException("Not enough bytes in header"); } if (!"OggS".equals(new String(head, 0, 4))) { throw new JOrbisException("Not a valid Ogg Vorbis file"); } int headerbytes = (touint(head[26])) + 27; // get the rest of the header byte[] head_rest = new byte[touint(head[26])]; bytes += s.read(head_rest); header = new byte[headerbytes]; Arrays.fill(header, (byte) 0); // copy the whole header into header System.arraycopy(head, 0, header, 0, 27); System.arraycopy(head_rest, 0, header, 27, headerbytes - 27); if (bytes < headerbytes) { String error = "Error reading vorbis file: " + "Not enough bytes for header + seg table"; throw new JOrbisException(error); } int bodybytes = 0; for (int i = 0; i < header[26]; i++) { bodybytes += touint(header[27 + i]); } packet = new byte[bodybytes]; Arrays.fill(packet, (byte) 0); bytes += s.read(packet); if (bytes < headerbytes + bodybytes) { String error = "Error reading vorbis file: " + "Not enough bytes for header + body"; throw new JOrbisException(error); } byte[] oldsum = new byte[4]; System.arraycopy(header, 22, oldsum, 0, 4); // read existing checksum Arrays.fill(header, 22, 22 + 4, (byte) 0); // clear for calculation of checksum byte[] newsum = checksum(); if (! (new String(oldsum)).equals(new String(newsum))) { // Checksum Failed. } } /** * Computes Ogg CRC. * @param index * @return */ private long _ogg_crc_entry(long index) { long r; r = index << 24; for (int i = 0; i < 8; i++) { if ( (r & 0x80000000L) != 0) { r = (r << 1) ^ 0x04c11db7L; } else { r <<= 1; } } return (r & 0xffffffff); } /** * Computes Checksum. * @return */ private byte[] checksum() { long crc_reg = 0; for (int i = 0; i < header.length; i++) { int tmp = (int) ( ( (crc_reg >>> 24) & 0xff) ^ touint(header[i])); crc_reg = (crc_reg << 8) ^ crc_lookup[tmp]; crc_reg &= 0xffffffff; } for (int i = 0; i < packet.length; i++) { int tmp = (int) ( ( (crc_reg >>> 24) & 0xff) ^ touint(packet[i])); crc_reg = (crc_reg << 8) ^ crc_lookup[tmp]; crc_reg &= 0xffffffff; } byte[] sum = new byte[4]; sum[0] = (byte) (crc_reg & 0xffL); sum[1] = (byte) ( (crc_reg >>> 8) & 0xffL); sum[2] = (byte) ( (crc_reg >>> 16) & 0xffL); sum[3] = (byte) ( (crc_reg >>> 24) & 0xffL); return sum; } /** * Convert to uint. * @param n * @return */ private int touint(byte n) { return (n & 0xff); } /** * Read OggVorbis information for Ogg Stream only. * @param vorbisfile */ private void loadInfo(VorbisFile vorbisfile) { int links = vorbisfile.streams(); serial = vorbisfile.serialnumber( -1); averagebitrate = vorbisfile.bitrate( -1); totalms = (long) Math.round(vorbisfile.time_total(-1)); Comment[] commentsarray = vorbisfile.getComment(); Info[] infos = vorbisfile.getInfo(); comments = new Vector(); for (int i = 0; i < links; i++) { Info info = infos[i]; Comment comment = commentsarray[i]; channels = info.channels; rate = info.rate; version = info.version; String infoStr = info.toString(); infoStr = infoStr.substring(infoStr.indexOf("bitrate:") + 8, infoStr.length()); StringTokenizer st = new StringTokenizer(infoStr, ","); if (st.hasMoreTokens()) { minbitrate = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { nominalbitrate = Integer.parseInt(st.nextToken()); } if (st.hasMoreTokens()) { maxbitrate = Integer.parseInt(st.nextToken()); } vendor = comment.getVendor(); for (int c = 0; c < comment.comments; c++) { String cmt = comment.getComment(c); int ind = cmt.indexOf("="); if (ind != -1) { String key = (cmt.substring(0,ind)).trim(); String value = cmt.substring(ind+1,cmt.length()); if (key.equalsIgnoreCase("artist")) artist = value; else if (key.equalsIgnoreCase("album")) album = value; else if (key.equalsIgnoreCase("title")) title = value; else if (key.equalsIgnoreCase("year")) year = value; else if (key.equalsIgnoreCase("genre")) genre = value; else if (key.equalsIgnoreCase("track")) track = Integer.parseInt(value); else { comments.add(cmt); } } else { comments.add(cmt); } } } } public int getSerial() { return serial; } public int getChannels() { return channels; } public int getVersion() { return version; } public int getMinBitrate() { return minbitrate; } public int getMaxBitrate() { return maxbitrate; } public int getAverageBitrate() { return averagebitrate; } public long getSize() { return size; } public String getVendor() { return vendor; } public String getLocation() { return location; } /*-- TagInfo Implementation --*/ public int getSamplingRate() { return rate; } public int getBitRate() { return nominalbitrate; } public long getPlayTime() { return totalms; } public String getTitle() { return title; } public String getArtist() { return artist; } public String getAlbum() { return album; } public int getTrack() { return track; } public String getGenre() { return genre; } public Vector getComment() { return comments; } public String getYear() { return year; } }