diff options
author | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-11 15:42:37 +0000 |
---|---|---|
committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-11 15:42:37 +0000 |
commit | 9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e (patch) | |
tree | 4c304cd4151020bd5494d279ee68a105ae3a5a3a | |
parent | dfa8ecbe609ca8ea194d08560a44fb9a92e94b4b (diff) | |
download | rockbox-9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e.tar.gz rockbox-9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e.zip |
Songdb java version, source. only 1.5 compatible
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
306 files changed, 63685 insertions, 0 deletions
diff --git a/songdbj/AlbumEntry.java b/songdbj/AlbumEntry.java new file mode 100644 index 0000000000..14c209547b --- /dev/null +++ b/songdbj/AlbumEntry.java @@ -0,0 +1,76 @@ +import java.util.*; +import java.io.*; + +public class AlbumEntry extends Entry implements Comparable { + protected String name; + protected ArtistEntry artist; + protected Vector songs; + protected int songcount; + + public AlbumEntry(String n) { + name=n; + songs=new Vector(); + artist=null; + songcount=0; + } + + protected class SongSorter implements Comparator { + public int compare(Object o1, Object o2) { + SongEntry s1=(SongEntry)o1; + SongEntry s2=(SongEntry)o2; + int track1=s1.getTrack(),track2=s2.getTrack(); + if(track1>track2) + return 1; + else if(track1<track2) + return -1; + return s1.getFile().getFile().getName().compareTo(s2.getFile().getFile().getName()); + } + } + + public void addSong(SongEntry e) { + songs.add(e); + e.setAlbum(this); + e.setArtist(artist); + songcount++; + Collections.sort(songs,new SongSorter()); + } + + public int size() { return songcount; } + public void setArtist(ArtistEntry a) { + a.addAlbum(this); + if(artist!=null&&artist!=a&&!artist.getName().equals("<various artists>")) { + artist.removeAlbum(this); + artist=TagDatabase.getInstance().getArtistEntry("<various artists>"); + } + else + artist=a; + } + public ArtistEntry getArtist() { return artist; } + + public int compareTo(Object o) { + return String.CASE_INSENSITIVE_ORDER.compare(name,((AlbumEntry)o).getName()); + } + + public String getName() { return name; } + public Collection getSongs() { return songs; } + public void write(DataOutputStream w) throws IOException { + int x; + w.writeBytes(name); + for(x=TagDatabase.getInstance().albumlen-name.length();x>0;x--) + w.write(0); + w.writeInt(artist.getOffset()); + Iterator i2 = songs.iterator(); + x=0; + while(i2.hasNext()) { + Entry e = (Entry) i2.next(); + w.writeInt(e.getOffset()); + x++; + } + for(;x<TagDatabase.getInstance().songarraylen;x++) + w.writeInt(0); + } + public static int entrySize() { + TagDatabase td=TagDatabase.getInstance(); + return td.albumlen+4+td.songarraylen*4; + } +}
\ No newline at end of file diff --git a/songdbj/ArtistEntry.java b/songdbj/ArtistEntry.java new file mode 100644 index 0000000000..fcaaac7dee --- /dev/null +++ b/songdbj/ArtistEntry.java @@ -0,0 +1,56 @@ +import java.util.*; +import java.io.*; + +public class ArtistEntry extends Entry implements Comparable { + protected String name; + protected Vector albums; + protected int albumcount; + + public ArtistEntry(String n) { + name=n; + albums=new Vector(); + albumcount=0; + } + + public void addAlbum(AlbumEntry e) { + if(!albums.contains(e)) { + albums.add(e); + e.setArtist(this); + albumcount++; + Collections.sort(albums); + } + } + + public void removeAlbum(AlbumEntry e) { + albums.remove(e); + albumcount--; + } + + public int size() { return albumcount; } + + public int compareTo(Object o) { + return String.CASE_INSENSITIVE_ORDER.compare(name,((ArtistEntry)o).getName()); + } + + public String getName() { return name; } + public Collection getAlbums() { return albums; } + public void write(DataOutputStream w) throws IOException { + int x; + w.writeBytes(name); + for(x=TagDatabase.getInstance().artistlen-name.length();x>0;x--) + w.write(0); + Iterator i2 = albums.iterator(); + x=0; + while(i2.hasNext()) { + Entry e = (Entry) i2.next(); + w.writeInt(e.getOffset()); + x++; + } + for(;x<TagDatabase.getInstance().albumarraylen;x++) + w.writeInt(0); + } + public static int entrySize() { + TagDatabase td=TagDatabase.getInstance(); + return td.artistlen+4*td.albumarraylen; + } +}
\ No newline at end of file diff --git a/songdbj/Entry.java b/songdbj/Entry.java new file mode 100644 index 0000000000..19ead66c06 --- /dev/null +++ b/songdbj/Entry.java @@ -0,0 +1,14 @@ +import java.io.*; + +public abstract class Entry { + protected int offset; + + public Entry() { + offset=-1; + } + + public void setOffset(int pos) { offset=pos; } + public int getOffset() { return offset; } + + public abstract void write(DataOutputStream w) throws IOException; +}
\ No newline at end of file diff --git a/songdbj/FileEntry.java b/songdbj/FileEntry.java new file mode 100644 index 0000000000..9af5b3d70a --- /dev/null +++ b/songdbj/FileEntry.java @@ -0,0 +1,155 @@ +import java.io.*; + +public class FileEntry extends Entry implements Comparable { + protected String filename; + protected int hash; + protected SongEntry sentry; + protected RundbEntry rentry; + protected File file; + + public FileEntry(File f) throws FileNotFoundException, IOException { + filename=convertPath(f.getAbsolutePath()); + file=f; + sentry=null; + rentry=null; + } + + public int compareTo(Object o) { + return String.CASE_INSENSITIVE_ORDER.compare(filename,((FileEntry)o).getFilename()); + } + + public String getFilename() { return filename; } + + public File getFile() { return file; } + + protected void calcHash() throws FileNotFoundException, IOException { + DataInputStream r = new DataInputStream(new FileInputStream(file)); + byte[] buf = new byte[32768]; + if(sentry!=null) + r.skip(sentry.getFirstFrameOffset()); + r.read(buf); + hash=CalcCRC32(buf); + r.close(); + } + + public int getHash() { return hash; } + + public static String add(String t) { + String add=TagDatabase.getInstance().add; + if(add!=null) + return add+t; + else + return t; + } + + public static String convertPath(String t) { + String temp = add(strip(t)).replace('\\','/'); + if (temp.charAt(0)!='/') + temp="/"+temp; + return temp; + } + + public static String strip(String t) { + return stripPrefix(stripDriveletter(stripPrefix(t))); + } + + public static String stripPrefix(String t) { + String prefix=TagDatabase.getInstance().strip; + if(prefix!=null&&t.toLowerCase().startsWith(prefix.toLowerCase())) { + return t.substring(prefix.length()); + } + return t; + } + + public static String stripDriveletter(String t) { + if(t.indexOf(':')==1) { // second char is ':' + return t.substring(2); + } + return t; + } + + public void setSongEntry(SongEntry e) { sentry=e; try { calcHash(); } catch(Exception d) { } } + public void setRundbEntry(RundbEntry e) { rentry=e; } + public SongEntry getSongEntry() { return sentry; } + public RundbEntry getRundbEntry() { return rentry; } + public int getSongEntryOffset() { + if(sentry!=null) + return sentry.getOffset(); + else + return -1; + } + public int getRundbEntryOffset() { +/* if(rentry!=null) + return rentry.getOffset(); + else*/ + return -1; + } + public void write(DataOutputStream w) throws IOException { + String name=getFilename(); + w.writeBytes(name); + for(int x=TagDatabase.getInstance().filelen-name.length();x>0;x--) + w.write(0); + w.writeInt(hash); + w.writeInt(getSongEntryOffset()); + w.writeInt(getRundbEntryOffset()); + } + + public static int entrySize() { + return TagDatabase.getInstance().filelen+12; + } + + static final int crc_table[] = + { // CRC32 lookup table for polynomial 0x04C11DB7 + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + }; + + public static int CalcCRC32(byte[] buf) { + int i; + int crc = 0xffffffff; + for (i = 0; i < buf.length; i++) + crc = (crc << 8) ^ crc_table[(int)((crc >> 24) ^ buf[i]) & 0xFF]; + return crc; + } +}
\ No newline at end of file diff --git a/songdbj/MpegInfo.java b/songdbj/MpegInfo.java new file mode 100644 index 0000000000..6f57879883 --- /dev/null +++ b/songdbj/MpegInfo.java @@ -0,0 +1,367 @@ +/* + * MpegInfo. + * + * JavaZOOM : jlgui@javazoom.net + * http://www.javazoom.net + * + *----------------------------------------------------------------------- + * 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.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.tritonus.share.sampled.file.TAudioFileFormat; + +/** + * This class gives information (audio format and comments) about MPEG file or URL. + */ +public class MpegInfo implements TagInfo +{ + protected int channels = -1; + protected String channelsMode = null; + protected String version = null; + protected int rate = 0; + protected String layer = null; + protected String emphasis = null; + protected int nominalbitrate = 0; + protected long total = 0; + protected String vendor = null; + protected String location = null; + protected long size = 0; + protected boolean copyright = false; + protected boolean crc = false; + protected boolean original = false; + protected boolean priv = false; + protected boolean vbr = false; + protected int track = -1; + protected int offset = 0; + protected String year = null; + protected String genre = null; + protected String title = null; + protected String artist = null; + protected String album = null; + protected Vector comments = null; + + /** + * Constructor. + */ + public MpegInfo() + { + super(); + } + + /** + * Load and parse MPEG info from File. + * @param input + * @throws IOException + */ + public void load(File input) throws IOException, UnsupportedAudioFileException + { + size = input.length(); + location = input.getPath(); + loadInfo(input); + } + + /** + * Load and parse MPEG info from URL. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + public void load(URL input) throws IOException, UnsupportedAudioFileException + { + location = input.toString(); + loadInfo(input); + } + + /** + * Load and parse MPEG info from InputStream. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + public void load(InputStream input) throws IOException, UnsupportedAudioFileException + { + loadInfo(input); + } + + /** + * Load info from input stream. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(InputStream input) throws IOException, UnsupportedAudioFileException + { + AudioFileFormat aff = AudioSystem.getAudioFileFormat(input); + loadInfo(aff); + } + + /** + * Load MP3 info from file. + * @param file + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(File file) throws IOException, UnsupportedAudioFileException + { + InputStream in = new BufferedInputStream(new FileInputStream(file)); + loadInfo(in); + in.close(); + } + + /** + * Load info from AudioFileFormat. + * @param aff + */ + protected void loadInfo(AudioFileFormat aff) throws UnsupportedAudioFileException + { + String type = aff.getType().toString(); + if (!type.equalsIgnoreCase("mp3")) throw new UnsupportedAudioFileException("Not MP3 audio format"); + if (aff instanceof TAudioFileFormat) + { + Map props = ((TAudioFileFormat) aff).properties(); + if (props.containsKey("mp3.channels")) channels = ((Integer)props.get("mp3.channels")).intValue(); + if (props.containsKey("mp3.frequency.hz")) rate = ((Integer)props.get("mp3.frequency.hz")).intValue(); + if (props.containsKey("mp3.bitrate.nominal.bps")) nominalbitrate = ((Integer)props.get("mp3.bitrate.nominal.bps")).intValue(); + if (props.containsKey("mp3.version.layer")) layer = "Layer "+(String)props.get("mp3.version.layer"); + if (props.containsKey("mp3.version.mpeg")) + { + version = (String)props.get("mp3.version.mpeg"); + if (version.equals("1")) version = "MPEG1"; + else if (version.equals("2")) version = "MPEG2-LSF"; + else if (version.equals("2.5")) version = "MPEG2.5-LSF"; + } + if (props.containsKey("mp3.mode")) + { + int mode = ((Integer)props.get("mp3.mode")).intValue(); + if (mode==0) channelsMode = "Stereo"; + else if (mode==1) channelsMode = "Joint Stereo"; + else if (mode==2) channelsMode = "Dual Channel"; + else if (mode==3) channelsMode = "Single Channel"; + } + if (props.containsKey("mp3.crc")) crc = ((Boolean)props.get("mp3.crc")).booleanValue(); + if (props.containsKey("mp3.vbr")) vbr = ((Boolean)props.get("mp3.vbr")).booleanValue(); + if (props.containsKey("mp3.copyright")) copyright = ((Boolean)props.get("mp3.copyright")).booleanValue(); + if (props.containsKey("mp3.original")) original = ((Boolean)props.get("mp3.original")).booleanValue(); + emphasis="none"; + + if (props.containsKey("title")) title = (String)props.get("title"); + if (props.containsKey("author")) artist = (String)props.get("author"); + if (props.containsKey("album")) album = (String)props.get("album"); + if (props.containsKey("date")) year = (String)props.get("date"); + if (props.containsKey("duration")) total = (long) Math.round((((Long)props.get("duration")).longValue())/1000000); + if (props.containsKey("mp3.id3tag.genre")) genre = (String)props.get("mp3.id3tag.genre"); + + if (props.containsKey("mp3.header.pos")) { + offset = ((Integer)props.get("mp3.header.pos")).intValue(); + } + else + offset = 0; + if (props.containsKey("mp3.id3tag.track")) + { + try + { + track = Integer.parseInt((String)props.get("mp3.id3tag.track")); + } + catch (NumberFormatException e1) + { + // Not a number + } + } + } + } + + /** + * Load MP3 info from URL. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(URL input) throws IOException, UnsupportedAudioFileException + { + AudioFileFormat aff = AudioSystem.getAudioFileFormat(input); + loadInfo(aff); + loadShoutastInfo(aff); + } + + /** + * Load Shoutcast info from AudioFileFormat. + * @param aff + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadShoutastInfo(AudioFileFormat aff) throws IOException, UnsupportedAudioFileException + { + String type = aff.getType().toString(); + if (!type.equalsIgnoreCase("mp3")) throw new UnsupportedAudioFileException("Not MP3 audio format"); + if (aff instanceof TAudioFileFormat) + { + Map props = ((TAudioFileFormat) aff).properties(); + // Try shoutcast meta data (if any). + Iterator it = props.keySet().iterator(); + comments = new Vector(); + while (it.hasNext()) + { + String key = (String) it.next(); + if (key.startsWith("mp3.shoutcast.metadata.")) + { + String value = (String) props.get(key); + key = key.substring(23,key.length()); + if (key.equalsIgnoreCase("icy-name")) + { + title = value; + } + else if (key.equalsIgnoreCase("icy-genre")) + { + genre = value; + } + else + { + comments.add(key+"="+value); + } + } + } + } + } + + public boolean getVBR() + { + return vbr; + } + + public int getChannels() + { + return channels; + } + + public String getVersion() + { + return version; + } + + public String getEmphasis() + { + return emphasis; + } + + public boolean getCopyright() + { + return copyright; + } + + public boolean getCRC() + { + return crc; + } + + public boolean getOriginal() + { + return original; + } + + public String getLayer() + { + return layer; + } + + public long getSize() + { + return size; + } + + public String getLocation() + { + return location; + } + + /*-- TagInfo Implementation --*/ + + public int getSamplingRate() + { + return rate; + } + + public int getBitRate() + { + return nominalbitrate; + } + + public long getPlayTime() + { + return total; + } + + 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; + } + + /** + * Get channels mode. + * @return + */ + public String getChannelsMode() + { + return channelsMode; + } + + public int getFirstFrameOffset() { + return offset; + } + +}
\ No newline at end of file diff --git a/songdbj/OggVorbisInfo.java b/songdbj/OggVorbisInfo.java new file mode 100644 index 0000000000..ab07299e77 --- /dev/null +++ b/songdbj/OggVorbisInfo.java @@ -0,0 +1,311 @@ +/* + * OggVorbisInfo. + * + * JavaZOOM : jlgui@javazoom.net + * http://www.javazoom.net + * + *----------------------------------------------------------------------- + * 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.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.net.URL; +import java.util.Map; +import java.util.Vector; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.tritonus.share.sampled.file.TAudioFileFormat; + +/** + * This class gives information (audio format and comments) about Ogg Vorbis file or URL. + */ +public class OggVorbisInfo implements TagInfo +{ + protected int serial = 0; + protected int channels = 0; + protected int version = 0; + protected int rate = 0; + protected int minbitrate = 0; + protected int maxbitrate = 0; + protected int averagebitrate = 0; + protected int nominalbitrate = 0; + protected long totalms = 0; + protected String vendor = ""; + protected String location = null; + + protected long size = 0; + protected int track = -1; + protected String year = null; + protected String genre = null; + protected String title = null; + protected String artist = null; + protected String album = null; + protected Vector comments = new Vector(); + + + /*** + * Constructor. + */ + public OggVorbisInfo() + { + super(); + } + + /** + * Load and parse Ogg Vorbis info from File. + * @param input + * @throws IOException + */ + public void load(File input) throws IOException, UnsupportedAudioFileException + { + size = input.length(); + location = input.getPath(); + loadInfo(input); + } + + /** + * Load and parse Ogg Vorbis info from URL. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + public void load(URL input) throws IOException, UnsupportedAudioFileException + { + location = input.toString(); + loadInfo(input); + } + + /** + * Load and parse Ogg Vorbis info from InputStream. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + public void load(InputStream input) throws IOException, UnsupportedAudioFileException + { + loadInfo(input); + } + + /** + * Load info from input stream. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(InputStream input) throws IOException, UnsupportedAudioFileException + { + AudioFileFormat aff = AudioSystem.getAudioFileFormat(input); + loadInfo(aff); + } + + /** + * Load Ogg Vorbis info from file. + * @param file + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(File file) throws IOException, UnsupportedAudioFileException + { + InputStream in = new BufferedInputStream(new FileInputStream(file)); + loadInfo(in); + in.close(); + } + + /** + * Load Ogg Vorbis info from URL. + * @param input + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(URL input) throws IOException, UnsupportedAudioFileException + { + AudioFileFormat aff = AudioSystem.getAudioFileFormat(input); + loadInfo(aff); + loadExtendedInfo(aff); + } + + /** + * Load info from AudioFileFormat. + * @param aff + * @throws UnsupportedAudioFileException + */ + protected void loadInfo(AudioFileFormat aff) throws UnsupportedAudioFileException + { + String type = aff.getType().toString(); + if (!type.equalsIgnoreCase("ogg")) throw new UnsupportedAudioFileException("Not Ogg Vorbis audio format"); + if (aff instanceof TAudioFileFormat) + { + Map props = ((TAudioFileFormat) aff).properties(); + if (props.containsKey("ogg.channels")) channels = ((Integer)props.get("ogg.channels")).intValue(); + if (props.containsKey("ogg.frequency.hz")) rate = ((Integer)props.get("ogg.frequency.hz")).intValue(); + if (props.containsKey("ogg.bitrate.nominal.bps")) nominalbitrate = ((Integer)props.get("ogg.bitrate.nominal.bps")).intValue(); + averagebitrate = nominalbitrate; + if (props.containsKey("ogg.bitrate.max.bps")) maxbitrate = ((Integer)props.get("ogg.bitrate.max.bps")).intValue(); + if (props.containsKey("ogg.bitrate.min.bps")) minbitrate = ((Integer)props.get("ogg.bitrate.min.bps")).intValue(); + if (props.containsKey("ogg.version")) version = ((Integer)props.get("ogg.version")).intValue(); + if (props.containsKey("ogg.serial")) serial = ((Integer)props.get("ogg.serial")).intValue(); + if (props.containsKey("ogg.comment.encodedby")) vendor = (String)props.get("ogg.comment.encodedby"); + + if (props.containsKey("copyright")) comments.add((String)props.get("copyright")); + if (props.containsKey("title")) title = (String)props.get("title"); + if (props.containsKey("author")) artist = (String)props.get("author"); + if (props.containsKey("album")) album = (String)props.get("album"); + if (props.containsKey("date")) year = (String)props.get("date"); + if (props.containsKey("comment")) comments.add((String)props.get("comment")); + if (props.containsKey("duration")) totalms = (long) Math.round((((Long)props.get("duration")).longValue())/1000000); + if (props.containsKey("ogg.comment.genre")) genre = (String)props.get("ogg.comment.genre"); + if (props.containsKey("ogg.comment.track")) + { + try + { + track = Integer.parseInt((String)props.get("ogg.comment.track")); + } + catch (NumberFormatException e1) + { + // Not a number + } + } + if (props.containsKey("ogg.comment.ext.1")) comments.add((String)props.get("ogg.comment.ext.1")); + if (props.containsKey("ogg.comment.ext.2")) comments.add((String)props.get("ogg.comment.ext.2")); + if (props.containsKey("ogg.comment.ext.3")) comments.add((String)props.get("ogg.comment.ext.3")); + } + } + + /** + * Load extended info from AudioFileFormat. + * @param aff + * @throws IOException + * @throws UnsupportedAudioFileException + */ + protected void loadExtendedInfo(AudioFileFormat aff) throws IOException, UnsupportedAudioFileException + { + String type = aff.getType().toString(); + if (!type.equalsIgnoreCase("ogg")) throw new UnsupportedAudioFileException("Not Ogg Vorbis audio format"); + if (aff instanceof TAudioFileFormat) + { + Map props = ((TAudioFileFormat) aff).properties(); + // How to load icecast meta data (if any) ?? + } + } + + 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; + } + + public int getFirstFrameOffset() { + return 0; + } +}
\ No newline at end of file diff --git a/songdbj/RundbEntry.java b/songdbj/RundbEntry.java new file mode 100644 index 0000000000..c13cbe4924 --- /dev/null +++ b/songdbj/RundbEntry.java @@ -0,0 +1,28 @@ +import java.io.*; + +public class RundbEntry extends Entry { + protected FileEntry file; + protected short rating, voladj; + protected int playcount,lastplayed; + + public RundbEntry(FileEntry f) { + file=f; + rating=0; + voladj=0; + playcount=0; + lastplayed=0; + } + + public void write(DataOutputStream w) throws IOException { + w.writeInt(file.getOffset()); + w.writeInt(file.getHash()); + w.writeShort(rating); + w.writeShort(voladj); + w.writeInt(playcount); + w.writeInt(lastplayed); + } + + public static int entrySize() { + return 20; + } +}
\ No newline at end of file diff --git a/songdbj/RuntimeDatabase.java b/songdbj/RuntimeDatabase.java new file mode 100644 index 0000000000..e96e8207cc --- /dev/null +++ b/songdbj/RuntimeDatabase.java @@ -0,0 +1,81 @@ +import java.util.*; +import java.io.*; +import java.lang.reflect.Array; + +/* + TreeSet for runtimedatabase with entry hash used in compareto + fix commandline interface. +*/ + +public class RuntimeDatabase { + protected static RuntimeDatabase instance=null; + protected TreeMap entries; + protected int entrycount; + public static final int headersize = 8; + + protected RuntimeDatabase() { + entries=new TreeMap(); + } + + public static RuntimeDatabase getInstance() { + if(instance==null) + instance=new RuntimeDatabase(); + return instance; + } + + public RundbEntry getEntry(FileEntry file) { + Integer key = new Integer(file.getHash()); + if(!entries.containsKey(key)) { + RundbEntry e = new RundbEntry(file); + entries.put(key,e); + return e; + } + else + return (RundbEntry)entries.get(key); + } + + protected void calcOffsets() { + Collection values = entries.values(); + Iterator i; + int offset=headersize; + i=values.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.setOffset(offset); + offset+=RundbEntry.entrySize(); + } + entrycount=values.size(); + } + + public int isDirty() { + return 0; + } + + protected void writeHeader(DataOutputStream w) throws IOException { + w.write('R'); + w.write('R'); + w.write('D'); + w.write(0x1); + w.writeInt(entrycount); + } + + public void prepareWrite() { + System.out.println("Calculating Runtime Database Offsets.."); + calcOffsets(); + } + + public void writeDatabase(File f) throws IOException { + int x; + Iterator i; + DataOutputStream w = new DataOutputStream(new FileOutputStream(f)); + System.out.println("Writing runtime database.."); + writeHeader(w); + i=entries.values().iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.write(w); + } + w.flush(); + w.close(); + } +}
\ No newline at end of file diff --git a/songdbj/SongDB.java b/songdbj/SongDB.java new file mode 100644 index 0000000000..4b7f2aae03 --- /dev/null +++ b/songdbj/SongDB.java @@ -0,0 +1,74 @@ +import java.io.*; +import java.lang.reflect.Array; + +public class SongDB { + + public static final void main(String[] args) { + TagDatabase td = TagDatabase.getInstance(); + File tdfile = new File("rockbox.tagdb"); + // RuntimeDatabase rd = RuntimeDatabase.getInstance(); + int i = 0, j; + String arg,path = null; + + while (i < args.length) { + arg = args[i++]; + if (arg.equals("--dirisnotalbumname")) { + td.dirisalbumname=false; + } + else if(arg.equals("--dirisalbum")) { + td.dirisalbum=true; + } + else if(arg.equals("--dontshowduplicates")) { + td.showduplicates=false; + } + else if(arg.equals("--strip")) { + if (i < args.length) + td.strip = args[i++]; + else { + System.err.println("--strip requires a path"); + System.exit(0); + } + } + else if(arg.equals("--add")) { + if (i < args.length) + td.add = args[i++]; + else { + System.err.println("--add requires a path"); + System.exit(0); + } + } + else { + if(path!=null) { + System.err.println("you can't specify more than one path!"); + System.exit(0); + } + path = arg; + } + } + if (i != args.length||path==null) { + System.out.println("Usage: SongDB [--showduplicates] [--strip <directory>] [--add <directory>] [--dirisnotalbumname] [--dirisalbum] <directory>"); + return; + } + if(tdfile.exists()&&!tdfile.canWrite()) { + System.out.println("rockbox.tagdb is not writable."); + return; + } + try { + tdfile.createNewFile(); + } + catch(Exception e) { + System.out.println("Error while trying to create rockbox.tagdb: "+e.getMessage()); + return; + } + td.add(new File(path)); + try { + td.prepareWrite(); + // rd.prepareWrite(); + td.writeDatabase(new File("rockbox.tagdb")); + // rd.writeDatabase(new File("rockbox.rundb")); + } + catch(IOException e) { + System.out.println(e); + } + } +}
\ No newline at end of file diff --git a/songdbj/SongEntry.java b/songdbj/SongEntry.java new file mode 100644 index 0000000000..cf6f887a7b --- /dev/null +++ b/songdbj/SongEntry.java @@ -0,0 +1,167 @@ +import java.util.*; +import java.io.*; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.lang.NumberFormatException; +import net.shredzone.ifish.ltr.LTR; + +public class SongEntry extends Entry implements Comparable { + protected TagInfo info; + protected LTR tag; + protected ArtistEntry artist; + protected AlbumEntry album; + protected FileEntry file; + + public SongEntry(FileEntry f) { + file=f; + file.setSongEntry(this); + readTagInfo(); + } + + public void setAlbum(AlbumEntry a) { album=a; } + public void setArtist(ArtistEntry a) { artist=a; } + public AlbumEntry getAlbum() { return album; } + public ArtistEntry getArtist() { return artist; } + public FileEntry getFile() { return file; } + + public int compareTo(Object o) { + return String.CASE_INSENSITIVE_ORDER.compare(getName(),((SongEntry)o).getName()); + } + + public String getName() { + String title=tag.getTitle(); + if(title==null) + title = stripExt(file.getFile().getName()); + title=title.trim(); + if(title.equals("")) + title = stripExt(file.getFile().getName()); + return title; + } + + public static String stripExt(String t) { + return t.substring(0,t.lastIndexOf('.')); + } + + public String getAlbumTag() { + String album=tag.getAlbum(); + if(album==null) + album = "<no album tag>"; + album=album.trim(); + if(album.equals("")) + album = "<no album tag>"; + if(TagDatabase.getInstance().dirisalbumname&&album.equals("<no album tag>")) { + album = file.getFile().getParentFile().getName(); + } + return album; + } + + public String getArtistTag() { + String artist=tag.getArtist(); + if(artist==null) + artist = "<no artist tag>"; + artist=artist.trim(); + if(artist.equals("")) + artist = "<no artist tag>"; + return artist; + } + + public String getGenreTag() { + String genre=tag.getGenre(); + if(genre==null) + genre = "<no genre tag>"; + genre=genre.trim(); + if(genre.equals("")) + genre = "<no genre tag>"; + return genre; + } + + public int getYear() { + try { + return Integer.parseInt(tag.getYear()); + } catch(NumberFormatException e) { + return 0; + } + } + + public int getTrack() { + try { + return Integer.parseInt(tag.getTrack()); + } catch(NumberFormatException e) { + return 0; + } + } + + public int getBitRate() { if(info==null) return -1; return info.getBitRate()/1000; } + + public int getPlayTime() { if(info==null) return -1; return (int)info.getPlayTime(); } + + public int getSamplingRate() { if(info==null) return -1; return info.getSamplingRate(); } + + public int getFirstFrameOffset() { if(info==null) return 0; return info.getFirstFrameOffset(); } + + public boolean gotTagInfo() { return tag!=null; } + + protected void readTagInfo() { + // Check Mpeg format. + try + { + info = new MpegInfo(); + info.load(file.getFile()); + } +/* catch (IOException ex) + { + //ex.printStackTrace(); + System.out.println(ex); + info = null; + }*/ + catch (Exception ex) + { + // Error.. + info = null; + } + + if (info == null) + { + // Check Ogg Vorbis format. + try + { + info = new OggVorbisInfo(); + info.load(file.getFile()); + } + /*catch (IOException ex) + { + //ex.printStackTrace(); + System.out.println(ex); + info = null; + }*/ + catch (Exception ex) + { + // Not Ogg Vorbis Format + //System.out.println("Failed reading tag for "+location.getAbsolutePath()+", tried mp3 and vorbis."); + info = null; + } + } + tag = LTR.create(file.getFile()); + } + + public void write(DataOutputStream w) throws IOException { + String name=getName(); + w.writeBytes(name); + for(int x=TagDatabase.getInstance().songlen-name.length();x>0;x--) + w.write(0); + w.writeInt(artist.getOffset()); + w.writeInt(album.getOffset()); + w.writeInt(file.getOffset()); + w.writeBytes(getGenreTag()); + for(int x=TagDatabase.getInstance().genrelen-getGenreTag().length();x>0;x--) + w.write(0); + w.writeShort(getBitRate()); + w.writeShort(getYear()); + w.writeInt(getPlayTime()); + w.writeShort(getTrack()); + w.writeShort(getSamplingRate()); + } + public static int entrySize() { + TagDatabase td=TagDatabase.getInstance(); + return td.songlen+12+td.genrelen+12; + } +}
\ No newline at end of file diff --git a/songdbj/TagDatabase.java b/songdbj/TagDatabase.java new file mode 100644 index 0000000000..36c2c09f37 --- /dev/null +++ b/songdbj/TagDatabase.java @@ -0,0 +1,377 @@ +import java.util.*; +import java.io.*; +import java.lang.reflect.Array; + +/* + TreeSet for runtimedatabase with entry hash used in compareto + fix commandline interface. +*/ + +public class TagDatabase { + protected static TagDatabase instance=null; + protected TreeMap songs; + protected TreeMap files; + protected TreeMap filehashes; + protected TreeMap albums; + protected TreeMap artists; + protected int artiststart,albumstart,songstart,filestart; + protected int artistcount,albumcount,songcount,filecount; + public int artistlen,albumlen,songlen,genrelen,filelen,songarraylen,albumarraylen; + public String strip,add; + public boolean haveOldDatabase,dirisalbum,dirisalbumname,showduplicates; + protected Vector sortedsongs,sortedfiles,sortedalbums,sortedartists; + + protected TagDatabase() { + songs=new TreeMap(); + files=new TreeMap(); + filehashes=new TreeMap(); + albums=new TreeMap(); + artists=new TreeMap(); + strip=null; + add=null; + haveOldDatabase=false; + dirisalbum=false; + dirisalbumname=true; + showduplicates=true; + } + + public static TagDatabase getInstance() { + if(instance==null) + instance=new TagDatabase(); + return instance; + } + + public void removeFileEntry(File file) { + String key = file.getAbsolutePath(); + files.remove(key); + } + + public FileEntry getFileEntry(File file) throws FileNotFoundException, IOException { + String key = file.getAbsolutePath(); + if(!files.containsKey(key)) { + FileEntry f = new FileEntry(file); + files.put(key,f); + return f; + } + else + return (FileEntry)files.get(key); + } + + public ArtistEntry getArtistEntry(String name) { + String key = name.toLowerCase(); + if(!artists.containsKey(key)) { + ArtistEntry a = new ArtistEntry(name); + artists.put(key,a); + return a; + } + else + return (ArtistEntry)artists.get(key); + } + + public String getAlbumKey(String name, String directory) { + if(dirisalbum) + return directory; + else + return name.toLowerCase()+"___"+directory; + } + + public AlbumEntry getAlbumEntry(String name,String directory) { + String key = getAlbumKey(name,directory); + if(!albums.containsKey(key)) { + AlbumEntry a = new AlbumEntry(name); + albums.put(key,a); + return a; + } + else + return (AlbumEntry)albums.get(key); + } + + public void removeSongEntry(FileEntry file) { + String key = file.getFilename(); + songs.remove(key); + file.setSongEntry(null); + } + + public SongEntry getSongEntry(FileEntry file) { + String key = file.getFilename(); + if(!songs.containsKey(key)) { + SongEntry s = new SongEntry(file); + songs.put(key,s); + return s; + } + else + return (SongEntry)songs.get(key); + } + + private class SongFilter implements FileFilter { + public boolean accept(File f) { + if(f.isDirectory()) // always accept directories. + return true; + String name=f.getName(); + return name.endsWith(".mp3")||name.endsWith(".ogg"); + } + } + + public void add(File f) { + if(!f.isDirectory()) { + if(f.isFile()) { + addSong(f); + } + } + else { + File[] files = f.listFiles(new SongFilter()); + int length=Array.getLength(files); + System.out.println(FileEntry.convertPath(f.getAbsolutePath())); + for(int i=0;i<length;i++) { + add(files[i]); + } + } + } + + protected FileEntry addSong(File f) { + FileEntry file = null; + try { + file = getFileEntry(f); + } + catch(Exception e) { + return null; + } + SongEntry song = getSongEntry(file); + if(!song.gotTagInfo()) { + removeSongEntry(file); + return null; + } + ArtistEntry artist = getArtistEntry(song.getArtistTag()); + AlbumEntry album = getAlbumEntry(song.getAlbumTag(),f.getParent()); + album.setArtist(artist); + album.addSong(song); + return file; + } + + protected int align(int len) { + while((len&3)!=0) len++; + return len; + } + + protected void calcLimits() { + ArtistEntry longartist=null,longalbumarray=null; + AlbumEntry longalbum=null, longsongarray=null; + SongEntry longsong=null,longgenre=null; + FileEntry longfile=null; + Iterator i; + artistlen=0; + albumarraylen=0; + i=sortedartists.iterator(); + while(i.hasNext()) { + ArtistEntry artist = (ArtistEntry) i.next(); + int length=artist.getName().length(); + int albumcount=artist.size(); + if(length > artistlen) { + artistlen=align(length); + longartist=artist; + } + if(albumcount> albumarraylen) { + albumarraylen=albumcount; + longalbumarray=artist; + } + } + artistcount=sortedartists.size(); + if(longartist!=null) + System.out.println("Artist with longest name ("+artistlen+") :"+longartist.getName()); + if(longalbumarray!=null) + System.out.println("Artist with most albums ("+albumarraylen+") :"+longalbumarray.getName()); + albumlen=0; + songarraylen=0; + i=sortedalbums.iterator(); + while(i.hasNext()) { + AlbumEntry album = (AlbumEntry) i.next(); + int length=album.getName().length(); + int songcount=album.size(); + if(length > albumlen) { + albumlen=align(length); + longalbum=album; + } + if(songcount> songarraylen) { + songarraylen=songcount; + longsongarray=album; + } + } + albumcount=sortedalbums.size(); + if(longalbum!=null) + System.out.println("Album with longest name ("+albumlen+") :"+longalbum.getName()); + if(longsongarray!=null) + System.out.println("Album with most songs ("+songarraylen+") :"+longsongarray.getName()); + filelen=0; + i=sortedfiles.iterator(); + while(i.hasNext()) { + FileEntry file = (FileEntry) i.next(); + int length=file.getFilename().length(); + if(length> filelen) { + filelen=align(length); + longfile=file; + } + } + filecount=sortedfiles.size(); + if(longfile!=null) + System.out.println("File with longest filename ("+filelen+") :"+longfile.getFilename()); + songlen=0; + genrelen=0; + i=sortedsongs.iterator(); + while(i.hasNext()) { + SongEntry song = (SongEntry) i.next(); + int tlength=song.getName().length(); + int glength=song.getGenreTag().length(); + if(tlength> songlen) { + songlen=align(tlength); + longsong=song; + } + if(glength> genrelen) { + genrelen=align(glength); + longgenre=song; + } + } + songcount=sortedsongs.size(); + if(longsong!=null) + System.out.println("Song with longest name ("+songlen+") :"+longsong.getName()); + if(longsong!=null) + System.out.println("Song with longest genre ("+genrelen+") :"+longgenre.getGenreTag()); + System.out.println("Artistcount: "+artistcount); + System.out.println("Albumcount : "+albumcount); + System.out.println("Songcount : "+songcount); + System.out.println("Filecount : "+filecount); + artiststart=68; + albumstart=artiststart+artistcount*ArtistEntry.entrySize(); + songstart=albumstart+albumcount*AlbumEntry.entrySize(); + filestart=songstart+songcount*SongEntry.entrySize(); + } + + protected void calcOffsets() { + Iterator i; + int offset=artiststart; + i=sortedartists.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.setOffset(offset); + offset+=ArtistEntry.entrySize(); + } +// assert(offset==albumstart); + i=sortedalbums.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.setOffset(offset); + offset+=AlbumEntry.entrySize(); + } +// assert(offset==songstart); + i=sortedsongs.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.setOffset(offset); + offset+=SongEntry.entrySize(); + } +// assert(offset==filestart); + i=sortedfiles.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.setOffset(offset); + offset+=FileEntry.entrySize(); + } + } + + protected void calcHashes() { + Iterator i; + i=sortedfiles.iterator(); + while(i.hasNext()) { + FileEntry file = (FileEntry) i.next(); + Integer key = new Integer(file.getHash()); + if(!filehashes.containsKey(key)) + filehashes.put(key,file); + else { + System.out.println("Duplicate hash:"); + System.out.println(((FileEntry)filehashes.get(key)).getFilename()); + System.out.println(file.getFilename()); + } + } + } + + protected void writeHeader(DataOutputStream w) throws IOException { + w.write('R'); + w.write('D'); + w.write('B'); + w.write(0x3); + w.writeInt(artiststart); + w.writeInt(albumstart); + w.writeInt(songstart); + w.writeInt(filestart); + w.writeInt(artistcount); + w.writeInt(albumcount); + w.writeInt(songcount); + w.writeInt(filecount); + w.writeInt(artistlen); + w.writeInt(albumlen); + w.writeInt(songlen); + w.writeInt(genrelen); + w.writeInt(filelen); + w.writeInt(songarraylen); + w.writeInt(albumarraylen); + w.writeInt(RuntimeDatabase.getInstance().isDirty()); + } + + public void prepareWrite() { + System.out.println("Sorting artists.."); + sortedartists=new Vector(); + sortedartists.addAll(artists.values()); + Collections.sort(sortedartists); + System.out.println("Sorting albums.."); + sortedalbums=new Vector(); + sortedalbums.addAll(albums.values()); + Collections.sort(sortedalbums); + System.out.println("Sorting songs.."); + sortedsongs=new Vector(); + sortedsongs.addAll(songs.values()); + Collections.sort(sortedsongs); + System.out.println("Sorting files.."); + sortedfiles=new Vector(); + sortedfiles.addAll(files.values()); + Collections.sort(sortedfiles); + System.out.println("Calculating tag database limits.."); + calcLimits(); + System.out.println("Calculating tag database offsets.."); + calcOffsets(); + if(showduplicates) { + System.out.println("Comparing file hashes.."); + calcHashes(); + } + } + + public void writeDatabase(File f) throws IOException { + int x; + Iterator i; + DataOutputStream w = new DataOutputStream(new FileOutputStream(f)); + System.out.println("Writing tag database.."); + writeHeader(w); + + i=sortedartists.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.write(w); + } + i=sortedalbums.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.write(w); + } + i=sortedsongs.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.write(w); + } + i=sortedfiles.iterator(); + while(i.hasNext()) { + Entry e = (Entry) i.next(); + e.write(w); + } + // done... + w.flush(); + w.close(); + } +}
\ No newline at end of file diff --git a/songdbj/TagInfo.java b/songdbj/TagInfo.java new file mode 100644 index 0000000000..2259226025 --- /dev/null +++ b/songdbj/TagInfo.java @@ -0,0 +1,112 @@ +/* + * TagInfo. + * + * JavaZOOM : jlgui@javazoom.net + * http://www.javazoom.net + * + *----------------------------------------------------------------------- + * 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.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Vector; + +import javax.sound.sampled.UnsupportedAudioFileException; + +/** + * This interface define needed features for song information. + * Adapted from Scott Pennell interface. + */ +public interface TagInfo +{ + + public void load(InputStream input) throws IOException, UnsupportedAudioFileException; + + public void load(URL input) throws IOException, UnsupportedAudioFileException; + + public void load(File input) throws IOException, UnsupportedAudioFileException; + + /** + * Get Sampling Rate + * @return + */ + public int getSamplingRate(); + + /** + * Get Nominal Bitrate + * @return bitrate in bps + */ + public int getBitRate(); + + /** + * Get channels. + * @return channels + */ + public int getChannels(); + + /** + * Get play time in seconds. + * @return + */ + public long getPlayTime(); + + /** + * Get the title of the song. + * @return the title of the song + */ + public String getTitle(); + + /** + * Get the artist that performed the song + * @return the artist that performed the song + */ + public String getArtist(); + + /** + * Get the name of the album upon which the song resides + * @return the album name + */ + public String getAlbum(); + + /** + * Get the track number of this track on the album + * @return the track number + */ + public int getTrack(); + + /** + * Get the genre string of the music + * @return the genre string + */ + public String getGenre(); + + /** + * Get the year the track was released + * @return the year the track was released + */ + public String getYear(); + + /** + * Get any comments provided about the song + * @return the comments + */ + public Vector getComment(); + + public int getFirstFrameOffset(); +}
\ No newline at end of file diff --git a/songdbj/build.sh b/songdbj/build.sh new file mode 100755 index 0000000000..e8b6b850a0 --- /dev/null +++ b/songdbj/build.sh @@ -0,0 +1,2 @@ +javac -d classes -cp . -source 1.5 -target 1.5 `find -name '*.java'` +jar cvfm SongDB.jar classes/META-INF/MANIFEST.MF -C classes/ . diff --git a/songdbj/classes/META-INF/MANIFEST.MF b/songdbj/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..14ecfed112 --- /dev/null +++ b/songdbj/classes/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Created-By: Apache Ant 1.5.1 +Main-Class: SongDB + diff --git a/songdbj/classes/META-INF/services/javax.sound.sampled.spi.AudioFileReader b/songdbj/classes/META-INF/services/javax.sound.sampled.spi.AudioFileReader new file mode 100644 index 0000000000..48c0ea73ca --- /dev/null +++ b/songdbj/classes/META-INF/services/javax.sound.sampled.spi.AudioFileReader @@ -0,0 +1,4 @@ +# for the javalayer mp3 decoder +javazoom.spi.mpeg.sampled.file.MpegAudioFileReader +# for the vorbis decoder +javazoom.spi.vorbis.sampled.file.VorbisAudioFileReader
\ No newline at end of file diff --git a/songdbj/classes/META-INF/services/javax.sound.sampled.spi.FormatConversionProvider b/songdbj/classes/META-INF/services/javax.sound.sampled.spi.FormatConversionProvider new file mode 100644 index 0000000000..f7f32618ac --- /dev/null +++ b/songdbj/classes/META-INF/services/javax.sound.sampled.spi.FormatConversionProvider @@ -0,0 +1,3 @@ +# for the javalayer mp3 decoder +javazoom.spi.mpeg.sampled.convert.MpegFormatConversionProvider +org.tritonus.sampled.convert.vorbis.VorbisFormatConversionProvider diff --git a/songdbj/com/jcraft/jogg/Buffer.java b/songdbj/com/jcraft/jogg/Buffer.java new file mode 100644 index 0000000000..a40a9def9c --- /dev/null +++ b/songdbj/com/jcraft/jogg/Buffer.java @@ -0,0 +1,541 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jogg; + +public class Buffer{ + private static final int BUFFER_INCREMENT=256; + + private static final int[] mask={ + 0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff + }; + + int ptr=0; + byte[] buffer=null; + int endbit=0; + int endbyte=0; + int storage=0; + + public void writeinit(){ + buffer=new byte[BUFFER_INCREMENT]; + ptr=0; + buffer[0]=(byte)'\0'; + storage=BUFFER_INCREMENT; + } + + public void write(byte[] s){ + for(int i=0; i<s.length; i++){ + if(s[i]==0)break; + write(s[i],8); + } + } + + public void read(byte[] s, int bytes){ + int i=0; + while(bytes--!=0){ + s[i++]=(byte)(read(8)); + } + } + + void reset(){ + ptr=0; + buffer[0]=(byte)'\0'; + endbit=endbyte=0; + } + + public void writeclear(){ + buffer=null; + } + + public void readinit(byte[] buf, int bytes){ + readinit(buf, 0, bytes); + } + + public void readinit(byte[] buf, int start, int bytes){ +//System.err.println("readinit: start="+start+", bytes="+bytes); +//for(int i=0;i<bytes; i++){ +//System.err.println(i+": "+Integer.toHexString(buf[i+start])); +//} + ptr=start; + buffer=buf; + endbit=endbyte=0; + storage=bytes; + } + + public void write(int value, int bits){ +//System.err.println("write: "+Integer.toHexString(value)+", bits="+bits+" ptr="+ptr+", storage="+storage+", endbyte="+endbyte); + if(endbyte+4>=storage){ + byte[] foo=new byte[storage+BUFFER_INCREMENT]; + System.arraycopy(buffer, 0, foo, 0, storage); + buffer=foo; + storage+=BUFFER_INCREMENT; + } + + value&=mask[bits]; + bits+=endbit; + buffer[ptr]|=(byte)(value<<endbit); + + if(bits>=8){ + buffer[ptr+1]=(byte)(value>>>(8-endbit)); + if(bits>=16){ + buffer[ptr+2]=(byte)(value>>>(16-endbit)); + if(bits>=24){ + buffer[ptr+3]=(byte)(value>>>(24-endbit)); + if(bits>=32){ + if(endbit>0) + buffer[ptr+4]=(byte)(value>>>(32-endbit)); + else + buffer[ptr+4]=0; + } + } + } + } + + endbyte+=bits/8; + ptr+=bits/8; + endbit=bits&7; + } + + public int look(int bits){ + int ret; + int m=mask[bits]; + + bits+=endbit; + +//System.err.println("look ptr:"+ptr+", bits="+bits+", endbit="+endbit+", storage="+storage); + + if(endbyte+4>=storage){ + if(endbyte+(bits-1)/8>=storage)return(-1); + } + + ret=((buffer[ptr])&0xff)>>>endbit; +// ret=((byte)(buffer[ptr]))>>>endbit; + if(bits>8){ + ret|=((buffer[ptr+1])&0xff)<<(8-endbit); +// ret|=((byte)(buffer[ptr+1]))<<(8-endbit); + if(bits>16){ + ret|=((buffer[ptr+2])&0xff)<<(16-endbit); +// ret|=((byte)(buffer[ptr+2]))<<(16-endbit); + if(bits>24){ + ret|=((buffer[ptr+3])&0xff)<<(24-endbit); +//System.err.print("ret="+Integer.toHexString(ret)+", ((byte)(buffer[ptr+3]))="+Integer.toHexString(((buffer[ptr+3])&0xff))); +// ret|=((byte)(buffer[ptr+3]))<<(24-endbit); +//System.err.println(" ->ret="+Integer.toHexString(ret)); + if(bits>32 && endbit!=0){ + ret|=((buffer[ptr+4])&0xff)<<(32-endbit); +// ret|=((byte)(buffer[ptr+4]))<<(32-endbit); + } + } + } + } + return(m&ret); + } + + public int look1(){ + if(endbyte>=storage)return(-1); + return((buffer[ptr]>>endbit)&1); + } + + public void adv(int bits){ + bits+=endbit; + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + } + + public void adv1(){ + ++endbit; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + } + + public int read(int bits){ +//System.err.println(this+" read: bits="+bits+", storage="+storage+", endbyte="+endbyte); +//System.err.println(this+" read: bits="+bits+", storage="+storage+", endbyte="+endbyte+ +// ", ptr="+ptr+", endbit="+endbit+", buf[ptr]="+buffer[ptr]); + + int ret; + int m=mask[bits]; + + bits+=endbit; + + if(endbyte+4>=storage){ + ret=-1; + if(endbyte+(bits-1)/8>=storage){ + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return(ret); + } + } + +/* + ret=(byte)(buffer[ptr]>>>endbit); + if(bits>8){ + ret|=(buffer[ptr+1]<<(8-endbit)); + if(bits>16){ + ret|=(buffer[ptr+2]<<(16-endbit)); + if(bits>24){ + ret|=(buffer[ptr+3]<<(24-endbit)); + if(bits>32 && endbit>0){ + ret|=(buffer[ptr+4]<<(32-endbit)); + } + } + } + } +*/ + ret=((buffer[ptr])&0xff)>>>endbit; + if(bits>8){ + ret|=((buffer[ptr+1])&0xff)<<(8-endbit); +// ret|=((byte)(buffer[ptr+1]))<<(8-endbit); + if(bits>16){ + ret|=((buffer[ptr+2])&0xff)<<(16-endbit); +// ret|=((byte)(buffer[ptr+2]))<<(16-endbit); + if(bits>24){ + ret|=((buffer[ptr+3])&0xff)<<(24-endbit); +// ret|=((byte)(buffer[ptr+3]))<<(24-endbit); + if(bits>32 && endbit!=0){ + ret|=((buffer[ptr+4])&0xff)<<(32-endbit); +// ret|=((byte)(buffer[ptr+4]))<<(32-endbit); + } + } + } + } + + ret&=m; + + ptr+=bits/8; +// ptr=bits/8; + endbyte+=bits/8; +// endbyte=bits/8; + endbit=bits&7; + return(ret); + } + + public int readB(int bits){ + //System.err.println(this+" read: bits="+bits+", storage="+storage+", endbyte="+endbyte+ + // ", ptr="+ptr+", endbit="+endbit+", buf[ptr]="+buffer[ptr]); + int ret; + int m=32-bits; + + bits+=endbit; + + if(endbyte+4>=storage){ + /* not the main path */ + ret=-1; + if(endbyte*8+bits>storage*8) { + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return(ret); + } + } + + ret=(buffer[ptr]&0xff)<<(24+endbit); + if(bits>8){ + ret|=(buffer[ptr+1]&0xff)<<(16+endbit); + if(bits>16){ + ret|=(buffer[ptr+2]&0xff)<<(8+endbit); + if(bits>24){ + ret|=(buffer[ptr+3]&0xff)<<(endbit); + if(bits>32 && (endbit != 0)) + ret|=(buffer[ptr+4]&0xff)>>(8-endbit); + } + } + } + ret=(ret>>>(m>>1))>>>((m+1)>>1); + + ptr+=bits/8; + endbyte+=bits/8; + endbit=bits&7; + return(ret); + } + + public int read1(){ + int ret; + if(endbyte>=storage){ + ret=-1; + endbit++; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + return(ret); + } + + ret=(buffer[ptr]>>endbit)&1; + + endbit++; + if(endbit>7){ + endbit=0; + ptr++; + endbyte++; + } + return(ret); + } + + public int bytes(){ + return(endbyte+(endbit+7)/8); + } + + public int bits(){ + return(endbyte*8+endbit); + } + + public byte[] buffer(){ + return(buffer); + } + + public static int ilog(int v){ + int ret=0; + while(v>0){ + ret++; + v>>>=1; + } + return(ret); + } + + public static void report(String in){ + System.err.println(in); + System.exit(1); + } + + /* + static void cliptest(int[] b, int vals, int bits, int[] comp, int compsize){ + int bytes; + byte[] buffer; + + o.reset(); + for(int i=0;i<vals;i++){ + o.write(b[i],((bits!=0)?bits:ilog(b[i]))); + } + buffer=o.buffer(); + bytes=o.bytes(); +System.err.println("cliptest: bytes="+bytes); + if(bytes!=compsize)report("wrong number of bytes!\n"); + for(int i=0;i<bytes;i++){ + if(buffer[i]!=(byte)comp[i]){ + for(int j=0;j<bytes;j++){ + System.err.println(j+": "+Integer.toHexString(buffer[j])+" "+ + Integer.toHexString(comp[j])); + } + report("wrote incorrect value!\n"); + } + } +System.err.println("bits: "+bits); + r.readinit(buffer,bytes); + for(int i=0;i<vals;i++){ + int tbit=(bits!=0)?bits:ilog(b[i]); +System.err.println(Integer.toHexString(b[i])+" tbit: "+tbit); + if(r.look(tbit)==-1){ + report("out of data!\n"); + } + if(r.look(tbit)!=(b[i]&mask[tbit])){ + report(i+" looked at incorrect value! "+Integer.toHexString(r.look(tbit))+", "+Integer.toHexString(b[i]&mask[tbit])+":"+b[i]+" bit="+tbit); + } + if(tbit==1){ + if(r.look1()!=(b[i]&mask[tbit])){ + report("looked at single bit incorrect value!\n"); + } + } + if(tbit==1){ + if(r.read1()!=(b[i]&mask[tbit])){ + report("read incorrect single bit value!\n"); + } + } + else{ + if(r.read(tbit)!=(b[i]&mask[tbit])){ + report("read incorrect value!\n"); + } + } + } + if(r.bytes()!=bytes){ + report("leftover bytes after read!\n"); + } + } + + static int[] testbuffer1= + {18,12,103948,4325,543,76,432,52,3,65,4,56,32,42,34,21,1,23,32,546,456,7, + 567,56,8,8,55,3,52,342,341,4,265,7,67,86,2199,21,7,1,5,1,4}; + static int test1size=43; + + static int[] testbuffer2= + {216531625,1237861823,56732452,131,3212421,12325343,34547562,12313212, + 1233432,534,5,346435231,14436467,7869299,76326614,167548585, + 85525151,0,12321,1,349528352}; + static int test2size=21; + + static int[] large= + {2136531625,2137861823,56732452,131,3212421,12325343,34547562,12313212, + 1233432,534,5,2146435231,14436467,7869299,76326614,167548585, + 85525151,0,12321,1,2146528352}; + + static int[] testbuffer3= + {1,0,14,0,1,0,12,0,1,0,0,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,1,1,1,0,0,1, + 0,1,30,1,1,1,0,0,1,0,0,0,12,0,11,0,1,0,0,1}; + static int test3size=56; + + static int onesize=33; + static int[] one={146,25,44,151,195,15,153,176,233,131,196,65,85,172,47,40, + 34,242,223,136,35,222,211,86,171,50,225,135,214,75,172, + 223,4}; + + static int twosize=6; + static int[] two={61,255,255,251,231,29}; + + static int threesize=54; + static int[] three={169,2,232,252,91,132,156,36,89,13,123,176,144,32,254, + 142,224,85,59,121,144,79,124,23,67,90,90,216,79,23,83, + 58,135,196,61,55,129,183,54,101,100,170,37,127,126,10, + 100,52,4,14,18,86,77,1}; + + static int foursize=38; + static int[] four={18,6,163,252,97,194,104,131,32,1,7,82,137,42,129,11,72, + 132,60,220,112,8,196,109,64,179,86,9,137,195,208,122,169, + 28,2,133,0,1}; + + static int fivesize=45; + static int[] five={169,2,126,139,144,172,30,4,80,72,240,59,130,218,73,62, + 241,24,210,44,4,20,0,248,116,49,135,100,110,130,181,169, + 84,75,159,2,1,0,132,192,8,0,0,18,22}; + + static int sixsize=7; + static int[] six={17,177,170,242,169,19,148}; + + static Buffer o=new Buffer(); + static Buffer r=new Buffer(); + + public static void main(String[] arg){ + byte[] buffer; + int bytes; +// o=new Buffer(); +// r=new Buffer(); + + o.writeinit(); + + System.err.print("\nSmall preclipped packing: "); + cliptest(testbuffer1,test1size,0,one,onesize); + System.err.print("ok."); + + System.err.print("\nNull bit call: "); + cliptest(testbuffer3,test3size,0,two,twosize); + System.err.print("ok."); + + System.err.print("\nLarge preclipped packing: "); + cliptest(testbuffer2,test2size,0,three,threesize); + System.err.print("ok."); + + System.err.print("\n32 bit preclipped packing: "); + o.reset(); + for(int i=0;i<test2size;i++) + o.write(large[i],32); + buffer=o.buffer(); + bytes=o.bytes(); + + + r.readinit(buffer,bytes); + for(int i=0;i<test2size;i++){ + if(r.look(32)==-1){ + report("out of data. failed!"); + } + if(r.look(32)!=large[i]){ + System.err.print(r.look(32)+" != "+large[i]+" ("+ + Integer.toHexString(r.look(32))+"!="+ + Integer.toHexString(large[i])+")"); + report("read incorrect value!\n"); + } + r.adv(32); + } + if(r.bytes()!=bytes)report("leftover bytes after read!\n"); + System.err.print("ok."); + + System.err.print("\nSmall unclipped packing: "); + cliptest(testbuffer1,test1size,7,four,foursize); + System.err.print("ok."); + + System.err.print("\nLarge unclipped packing: "); + cliptest(testbuffer2,test2size,17,five,fivesize); + System.err.print("ok."); + + System.err.print("\nSingle bit unclicpped packing: "); + cliptest(testbuffer3,test3size,1,six,sixsize); + System.err.print("ok."); + + System.err.print("\nTesting read past end: "); + r.readinit("\0\0\0\0\0\0\0\0".getBytes(),8); + for(int i=0;i<64;i++){ + if(r.read(1)!=0){ + System.err.print("failed; got -1 prematurely.\n"); + System.exit(1); + } + } + + if(r.look(1)!=-1 || + r.read(1)!=-1){ + System.err.print("failed; read past end without -1.\n"); + System.exit(1); + } + + r.readinit("\0\0\0\0\0\0\0\0".getBytes(),8); + if(r.read(30)!=0 || r.read(16)!=0){ + System.err.print("failed 2; got -1 prematurely.\n"); + System.exit(1); + } + + if(r.look(18)!=0 || + r.look(18)!=0){ + System.err.print("failed 3; got -1 prematurely.\n"); + System.exit(1); + } + if(r.look(19)!=-1 || + r.look(19)!=-1){ + System.err.print("failed; read past end without -1.\n"); + System.exit(1); + } + if(r.look(32)!=-1 || + r.look(32)!=-1){ + System.err.print("failed; read past end without -1.\n"); + System.exit(1); + } + System.err.print("ok.\n\n"); + } + */ +} + + + + + diff --git a/songdbj/com/jcraft/jogg/Packet.java b/songdbj/com/jcraft/jogg/Packet.java new file mode 100644 index 0000000000..22a8a5439b --- /dev/null +++ b/songdbj/com/jcraft/jogg/Packet.java @@ -0,0 +1,82 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jogg; + +public class Packet{ + public byte[] packet_base; + public int packet; + public int bytes; + public int b_o_s; + public int e_o_s; + + public long granulepos; + + public long packetno; // sequence number for decode; the framing + // knows where there's a hole in the data, + // but we need coupling so that the codec + // (which is in a seperate abstraction + // layer) also knows about the gap + + /* + // TEST + static int sequence=0; + static int lastno=0; + void checkpacket(int len, int no, int pos){ + if(bytes!=len){ + System.err.println("incorrect packet length!"); + System.exit(1); + } + if(granulepos!=pos){ + System.err.println("incorrect packet position!"); + System.exit(1); + } + + // packet number just follows sequence/gap; adjust the input number + // for that + if(no==0){ + sequence=0; + } + else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(packetno!=sequence){ + System.err.println("incorrect packet sequence "+packetno+" != "+sequence); + System.exit(1); + } + + // Test data + for(int j=0;j<bytes;j++){ + if((packet_base[packet+j]&0xff)!=((j+no)&0xff)){ + System.err.println("body data mismatch at pos "+ j+": "+(packet_base[packet+j]&0xff)+"!="+((j+no)&0xff)+"!\n"); + System.exit(1); + } + } + } + */ +} diff --git a/songdbj/com/jcraft/jogg/Page.java b/songdbj/com/jcraft/jogg/Page.java new file mode 100644 index 0000000000..fc1add010e --- /dev/null +++ b/songdbj/com/jcraft/jogg/Page.java @@ -0,0 +1,973 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jogg; + +public class Page{ + private static int[] crc_lookup=new int[256]; + static { + for(int i=0; i<crc_lookup.length; i++){ + crc_lookup[i]=crc_entry(i); + } + } + + private static int crc_entry(int index){ + int r=index<<24; + for(int i=0; i<8; i++){ + if((r& 0x80000000)!=0){ + r=(r << 1)^0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + } + else{ + r<<=1; + } + } + return(r&0xffffffff); + } + + public byte[] header_base; + public int header; + public int header_len; + public byte[] body_base; + public int body; + public int body_len; + + int version(){ + return header_base[header+4]&0xff; + } + int continued(){ + return (header_base[header+5]&0x01); + } + public int bos(){ + return (header_base[header+5]&0x02); + } + public int eos(){ + return (header_base[header+5]&0x04); + } + public long granulepos(){ + long foo=header_base[header+13]&0xff; + foo=(foo<<8)|(header_base[header+12]&0xff); + foo=(foo<<8)|(header_base[header+11]&0xff); + foo=(foo<<8)|(header_base[header+10]&0xff); + foo=(foo<<8)|(header_base[header+9]&0xff); + foo=(foo<<8)|(header_base[header+8]&0xff); + foo=(foo<<8)|(header_base[header+7]&0xff); + foo=(foo<<8)|(header_base[header+6]&0xff); + return(foo); + } + public int serialno(){ + return (header_base[header+14]&0xff)| + ((header_base[header+15]&0xff)<<8)| + ((header_base[header+16]&0xff)<<16)| + ((header_base[header+17]&0xff)<<24); + } + int pageno(){ + return (header_base[header+18]&0xff)| + ((header_base[header+19]&0xff)<<8)| + ((header_base[header+20]&0xff)<<16)| + ((header_base[header+21]&0xff)<<24); + } + + void checksum(){ + int crc_reg=0; + +// for(int i=0;i<header_len;i++){ +// System.err.println("chksum: "+Integer.toHexString(header_base[header+i]&0xff)); +// } + + for(int i=0;i<header_len;i++){ + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg>>>24)&0xff)^(header_base[header+i]&0xff)]; + } + for(int i=0;i<body_len;i++){ + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg>>>24)&0xff)^(body_base[body+i]&0xff)]; + } + header_base[header+22]=(byte)crc_reg/*&0xff*/; + header_base[header+23]=(byte)(crc_reg>>>8)/*&0xff*/; + header_base[header+24]=(byte)(crc_reg>>>16)/*&0xff*/; + header_base[header+25]=(byte)(crc_reg>>>24)/*&0xff*/; + } + public Page copy(){ + return copy(new Page()); + } + public Page copy(Page p){ + byte[] tmp=new byte[header_len]; + System.arraycopy(header_base, header, tmp, 0, header_len); + p.header_len=header_len; + p.header_base=tmp; + p.header=0; + tmp=new byte[body_len]; + System.arraycopy(body_base, body, tmp, 0, body_len); + p.body_len=body_len; + p.body_base=tmp; + p.body=0; + return p; + } + /* + // TEST + static StreamState os_en, os_de; + static SyncState oy; + void check_page(byte[] data_base, int data, int[] _header){ + // Test data + for(int j=0;j<body_len;j++) + if(body_base[body+j]!=data_base[data+j]){ + System.err.println("body data mismatch at pos "+j+": "+data_base[data+j]+"!="+body_base[body+j]+"!\n"); + System.exit(1); + } + + // Test header + for(int j=0;j<header_len;j++){ + if((header_base[header+j]&0xff)!=_header[j]){ + System.err.println("header content mismatch at pos "+j); + for(int jj=0;jj<_header[26]+27;jj++) + System.err.print(" ("+jj+")"+Integer.toHexString(_header[jj])+":"+Integer.toHexString(header_base[header+jj])); + System.err.println(""); + System.exit(1); + } + } + if(header_len!=_header[26]+27){ + System.err.print("header length incorrect! ("+header_len+"!="+(_header[26]+27)+")"); + System.exit(1); + } + } + + void print_header(){ + System.err.println("\nHEADER:"); + System.err.println(" capture: "+ + (header_base[header+0]&0xff)+" "+ + (header_base[header+1]&0xff)+" "+ + (header_base[header+2]&0xff)+" "+ + (header_base[header+3]&0xff)+" "+ + " version: "+(header_base[header+4]&0xff)+" flags: "+ + (header_base[header+5]&0xff)); + System.err.println(" pcmpos: "+ + (((header_base[header+9]&0xff)<<24)| + ((header_base[header+8]&0xff)<<16)| + ((header_base[header+7]&0xff)<<8)| + ((header_base[header+6]&0xff)))+ + " serialno: "+ + (((header_base[header+17]&0xff)<<24)| + ((header_base[header+16]&0xff)<<16)| + ((header_base[header+15]&0xff)<<8)| + ((header_base[header+14]&0xff)))+ + " pageno: "+ + (((header_base[header+21]&0xff)<<24)| + ((header_base[header+20]&0xff)<<16)| + ((header_base[header+19]&0xff)<<8)| + ((header_base[header+18]&0xff)))); + + System.err.println(" checksum: "+ + (header_base[header+22]&0xff)+":"+ + (header_base[header+23]&0xff)+":"+ + (header_base[header+24]&0xff)+":"+ + (header_base[header+25]&0xff)+"\n segments: "+ + (header_base[header+26]&0xff)+" ("); + for(int j=27;j<header_len;j++){ + System.err.println((header_base[header+j]&0xff)+" "); + } + System.err.println(")\n"); + } + + void copy_page(){ + byte[] tmp=new byte[header_len]; + System.arraycopy(header_base, header, tmp, 0, header_len); + header_base=tmp; + header=0; + tmp=new byte[body_len]; + System.arraycopy(body_base, body, tmp, 0, body_len); + body_base=tmp; + body=0; + } + + static void test_pack(int[] pl, int[][] headers){ + byte[] data=new byte[1024*1024]; // for scripted test cases only + int inptr=0; + int outptr=0; + int deptr=0; + int depacket=0; + int pcm_pos=7; + int packets,pageno=0,pageout=0; + int eosflag=0; + int bosflag=0; + + os_en.reset(); + os_de.reset(); + oy.reset(); + + for(packets=0;;packets++){ + if(pl[packets]==-1)break; + } + + for(int i=0;i<packets;i++){ + // construct a test packet + Packet op=new Packet(); + int len=pl[i]; + op.packet_base=data; + op.packet=inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=pcm_pos; + + pcm_pos+=1024; + + for(int j=0;j<len;j++){ + data[inptr++]=(byte)(i+j); + } + + // submit the test packet + os_en.packetin(op); + + // retrieve any finished pages + { + Page og=new Page(); + + while(os_en.pageout(og)!=0){ + // We have a page. Check it carefully + //System.err.print(pageno+", "); + if(headers[pageno]==null){ + System.err.println("coded too many pages!"); + System.exit(1); + } + og.check_page(data, outptr, headers[pageno]); + + outptr+=og.body_len; + pageno++; + +//System.err.println("1# pageno="+pageno+", pageout="+pageout); + + // have a complete page; submit it to sync/decode + + { + Page og_de=new Page(); + Packet op_de=new Packet(); + int index=oy.buffer(og.header_len+og.body_len); + byte[] buf=oy.data; + System.arraycopy(og.header_base, og.header, buf, index, og.header_len); + System.arraycopy(og.body_base, og.body, buf, index+og.header_len, og.body_len); + oy.wrote(og.header_len+og.body_len); + +//System.err.println("2# pageno="+pageno+", pageout="+pageout); + + while(oy.pageout(og_de)>0){ + // got a page. Happy happy. Verify that it's good. + + og_de.check_page(data, deptr, headers[pageout]); + deptr+=og_de.body_len; + pageout++; + + // submit it to deconstitution + os_de.pagein(og_de); + + // packets out? + while(os_de.packetout(op_de)>0){ + + // verify the packet! + // check data + boolean check=false; + for(int ii=0; ii<op_de.bytes; ii++){ + if(data[depacket+ii]!=op_de.packet_base[op_de.packet+ii]){ + check=true; + break; + } + } + if(check){ + System.err.println("packet data mismatch in decode! pos="+ + depacket); + System.exit(1); + } + + // check bos flag + if(bosflag==0 && op_de.b_o_s==0){ + System.err.println("b_o_s flag not set on packet!"); + System.exit(1); + } + if(bosflag!=0 && op_de.b_o_s!=0){ + System.err.println("b_o_s flag incorrectly set on packet!"); + System.exit(1); + } + + bosflag=1; + depacket+=op_de.bytes; + + // check eos flag + if(eosflag!=0){ + System.err.println("Multiple decoded packets with eos flag!"); + System.exit(1); + } + + if(op_de.e_o_s!=0)eosflag=1; + + // check pcmpos flag + if(op_de.granulepos!=-1){ + System.err.print(" pcm:"+op_de.granulepos+" "); + } + } + } + } + } + } + } + //free(data); + if(headers[pageno]!=null){ + System.err.println("did not write last page!"); + System.exit(1); + } + if(headers[pageout]!=null){ + System.err.println("did not decode last page!"); + System.exit(1); + } + if(inptr!=outptr){ + System.err.println("encoded page data incomplete!"); + System.exit(1); + } + if(inptr!=deptr){ + System.err.println("decoded page data incomplete!"); + System.exit(1); + } + if(inptr!=depacket){ + System.err.println("decoded packet data incomplete!"); + System.exit(1); + } + if(eosflag==0){ + System.err.println("Never got a packet with EOS set!"); + } + System.err.println("ok."); + } + + static void error(){ + System.err.println("error!"); + System.exit(1); + } + public static void main(String[] arg){ + + os_en=new StreamState(0x04030201); + os_de=new StreamState(0x04030201); + + oy=new SyncState(); + + // Exercise each code path in the framing code. Also verify that + // the checksums are working. + + { + // 17 only + int[] packets={17, -1}; + int[] head1={0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + int[][] headret={head1, null}; + + System.err.print("testing single page encoding... "); + test_pack(packets,headret); + } + + { + // 17, 254, 255, 256, 500, 510, 600 byte, pad + int[] packets={17, 254, 255, 256, 500, 510, 600, -1}; + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; + int[] head2={0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + int[][] headret={head1,head2,null}; + + System.err.print("testing basic page encoding... "); + test_pack(packets,headret); + } + + { + // nil packets; beginning,middle,end + int[] packets={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + int[] head2={0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + int[][] headret={head1,head2,null}; + + System.err.print("testing basic nil packets... "); + test_pack(packets,headret); + } + + { + // large initial packet + int[] packets={4345,259,255,-1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + + int[] head2={0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + int[][] headret={head1,head2,null}; + + System.err.print("testing initial-packet lacing > 4k... "); + test_pack(packets,headret); + } + + { + // continuing packet test + int[] packets={0,4345,259,255,-1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + + int[] head2={0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x34,0x24,0xd5,0x29, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + + int[] head3={0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xc8,0xc3,0xcb,0xed, + 5, + 10,255,4,255,0}; + int[][] headret={head1,head2,head3,null}; + + System.err.print("testing single packet page span... "); + test_pack(packets,headret); + } + + // page with the 255 segment limit + { + + int[] packets={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + + int[] head2={0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + + int[] head3={0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + int[][] headret={head1,head2,head3,null}; + + System.err.print("testing max packet segments... "); + test_pack(packets,headret); + } + + { + // packet that overspans over an entire page + + int[] packets={0,100,9000,259,255,-1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + + int[] head2={0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + + int[] head3={0x4f,0x67,0x67,0x53,0,0x01, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xbd,0xd5,0xb5,0x8b, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + + int[] head4={0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xef,0xdd,0x88,0xde, + 7, + 255,255,75,255,4,255,0}; + int[][] headret={head1,head2,head3,head4,null}; + + System.err.print("testing very large packets... "); + test_pack(packets,headret); + } + + { + // term only page. why not? + + int[] packets={0,100,4080,-1}; + + int[] head1={0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + + int[] head2={0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + + int[] head3={0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1,0}; + + int[][] headret={head1,head2,head3,null}; + + System.err.print("testing zero data page (1 nil packet)... "); + test_pack(packets,headret); + } + + { + // build a bunch of pages for testing + byte[] data=new byte[1024*1024]; + int[] pl={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int inptr=0; + Page[] og=new Page[5]; + for(int i=0; i<5; i++){ + og[i]=new Page(); + } + + os_en.reset(); + + for(int i=0;pl[i]!=-1;i++){ + Packet op=new Packet(); + int len=pl[i]; + + op.packet_base=data; + op.packet=inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(int j=0;j<len;j++)data[inptr++]=(byte)(i+j); + os_en.packetin(op); + } + +// free(data); + + // retrieve finished pages + for(int i=0;i<5;i++){ + if(os_en.pageout(og[i])==0){ + System.err.print("Too few pages output building sync tests!\n"); + System.exit(1); + } + og[i].copy_page(); + } + + // Test lost pages on pagein/packetout: no rollback + { + Page temp=new Page(); + Packet test=new Packet(); + + System.err.print("Testing loss of pages... "); + + oy.reset(); + os_de.reset(); + for(int i=0;i<5;i++){ + int index=oy.buffer(og[i].header_len); + System.arraycopy(og[i].header_base, og[i].header, + oy.data, index, og[i].header_len); + oy.wrote(og[i].header_len); + index=oy.buffer(og[i].body_len); + System.arraycopy(og[i].body_base, og[i].body, + oy.data, index, og[i].body_len); + oy.wrote(og[i].body_len); + } + + oy.pageout(temp); + os_de.pagein(temp); + oy.pageout(temp); + os_de.pagein(temp); + oy.pageout(temp); + + // skip + oy.pageout(temp); + os_de.pagein(temp); + + // do we get the expected results/packets? + + if(os_de.packetout(test)!=1)error(); + test.checkpacket(0,0,0); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(100,1,-1); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(4079,2,3000); + if(os_de.packetout(test)!=-1){ + System.err.println("Error: loss of page did not return error"); + System.exit(1); + } + if(os_de.packetout(test)!=1)error(); + test.checkpacket(76,5,-1); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(34,6,-1); + System.err.println("ok."); + } + + // Test lost pages on pagein/packetout: rollback with continuation + { + Page temp=new Page(); + Packet test=new Packet(); + + System.err.print("Testing loss of pages (rollback required)... "); + + oy.reset(); + os_de.reset(); + for(int i=0;i<5;i++){ + int index=oy.buffer(og[i].header_len); + System.arraycopy(og[i].header_base, og[i].header, + oy.data, index, og[i].header_len); + oy.wrote(og[i].header_len); + index=oy.buffer(og[i].body_len); + System.arraycopy(og[i].body_base, og[i].body, + oy.data, index, og[i].body_len); + oy.wrote(og[i].body_len); + } + + oy.pageout(temp); + os_de.pagein(temp); + oy.pageout(temp); + os_de.pagein(temp); + oy.pageout(temp); + os_de.pagein(temp); + oy.pageout(temp); + // skip + oy.pageout(temp); + os_de.pagein(temp); + + // do we get the expected results/packets? + + if(os_de.packetout(test)!=1)error(); + test.checkpacket(0,0,0); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(100,1,-1); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(4079,2,3000); + if(os_de.packetout(test)!=1)error(); + test.checkpacket(2956,3,4000); + if(os_de.packetout(test)!=-1){ + System.err.println("Error: loss of page did not return error"); + System.exit(1); + } + if(os_de.packetout(test)!=1)error(); + test.checkpacket(300,13,14000); + System.err.println("ok."); + } + + // the rest only test sync + { + Page og_de=new Page(); + // Test fractional page inputs: incomplete capture + System.err.print("Testing sync on partial inputs... "); + oy.reset(); + int index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header, + oy.data, index, 3); + oy.wrote(3); + if(oy.pageout(og_de)>0)error(); + + // Test fractional page inputs: incomplete fixed header + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header+3, + oy.data, index, 20); + + oy.wrote(20); + if(oy.pageout(og_de)>0)error(); + + // Test fractional page inputs: incomplete header + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header+23, + oy.data, index, 5); + oy.wrote(5); + if(oy.pageout(og_de)>0)error(); + + // Test fractional page inputs: incomplete body + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header+28, + oy.data, index, og[1].header_len-28); + oy.wrote(og[1].header_len-28); + if(oy.pageout(og_de)>0)error(); + + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, 1000); + oy.wrote(1000); + if(oy.pageout(og_de)>0)error(); + + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body+1000, + oy.data, index, og[1].body_len-1000); + oy.wrote(og[1].body_len-1000); + if(oy.pageout(og_de)<=0)error(); + System.err.println("ok."); + } + + // Test fractional page inputs: page + incomplete capture + { + Page og_de=new Page(); + System.err.print("Testing sync on 1+partial inputs... "); + oy.reset(); + + int index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header, + oy.data, index, og[1].header_len); + oy.wrote(og[1].header_len); + + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, og[1].body_len); + oy.wrote(og[1].body_len); + + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header, + oy.data, index, 20); + oy.wrote(20); + if(oy.pageout(og_de)<=0)error(); + if(oy.pageout(og_de)>0)error(); + + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header+20, + oy.data, index, og[1].header_len-20); + oy.wrote(og[1].header_len-20); + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, og[1].body_len); + + oy.wrote(og[1].body_len); + if(oy.pageout(og_de)<=0)error(); + + System.err.println("ok."); + } + +// // // // // // // // // + // Test recapture: garbage + page + { + Page og_de=new Page(); + System.err.print("Testing search for capture... "); + oy.reset(); + + // 'garbage' + int index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, og[1].body_len); + oy.wrote(og[1].body_len); + + index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header, + oy.data, index, og[1].header_len); + oy.wrote(og[1].header_len); + + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, og[1].body_len); + oy.wrote(og[1].body_len); + + index=oy.buffer(og[2].header_len); + System.arraycopy(og[2].header_base, og[2].header, + oy.data, index, 20); + + oy.wrote(20); + if(oy.pageout(og_de)>0)error(); + if(oy.pageout(og_de)<=0)error(); + if(oy.pageout(og_de)>0)error(); + + index=oy.buffer(og[2].header_len); + System.arraycopy(og[2].header_base, og[2].header+20, + oy.data, index, og[2].header_len-20); + oy.wrote(og[2].header_len-20); + index=oy.buffer(og[2].body_len); + System.arraycopy(og[2].body_base, og[2].body, + oy.data, index, og[2].body_len); + oy.wrote(og[2].body_len); + if(oy.pageout(og_de)<=0)error(); + + System.err.println("ok."); + } + + // Test recapture: page + garbage + page + { + Page og_de=new Page(); + System.err.print("Testing recapture... "); + oy.reset(); + + int index=oy.buffer(og[1].header_len); + System.arraycopy(og[1].header_base, og[1].header, + oy.data, index, og[1].header_len); + oy.wrote(og[1].header_len); + + index=oy.buffer(og[1].body_len); + System.arraycopy(og[1].body_base, og[1].body, + oy.data, index, og[1].body_len); + oy.wrote(og[1].body_len); + + index=oy.buffer(og[2].header_len); + System.arraycopy(og[2].header_base, og[2].header, + oy.data, index, og[2].header_len); + oy.wrote(og[2].header_len); + + index=oy.buffer(og[2].header_len); + System.arraycopy(og[2].header_base, og[2].header, + oy.data, index, og[2].header_len); + oy.wrote(og[2].header_len); + + if(oy.pageout(og_de)<=0)error(); + + index=oy.buffer(og[2].body_len); + System.arraycopy(og[2].body_base, og[2].body, + oy.data, index, og[2].body_len-5); + oy.wrote(og[2].body_len-5); + + index=oy.buffer(og[3].header_len); + System.arraycopy(og[3].header_base, og[3].header, + oy.data, index, og[3].header_len); + oy.wrote(og[3].header_len); + + index=oy.buffer(og[3].body_len); + System.arraycopy(og[3].body_base, og[3].body, + oy.data, index, og[3].body_len); + oy.wrote(og[3].body_len); + + if(oy.pageout(og_de)>0)error(); + if(oy.pageout(og_de)<=0)error(); + + System.err.println("ok."); + } + } + //return(0); + } + */ +} diff --git a/songdbj/com/jcraft/jogg/StreamState.java b/songdbj/com/jcraft/jogg/StreamState.java new file mode 100644 index 0000000000..2f34b374f8 --- /dev/null +++ b/songdbj/com/jcraft/jogg/StreamState.java @@ -0,0 +1,657 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jogg; + +public class StreamState{ + byte[] body_data; /* bytes from packet bodies */ + int body_storage; /* storage elements allocated */ + int body_fill; /* elements stored; fill mark */ +private int body_returned; /* elements of fill returned */ + + + int[] lacing_vals; /* The values that will go to the segment table */ + long[] granule_vals; /* pcm_pos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + int lacing_storage; + int lacing_fill; + int lacing_packet; + int lacing_returned; + + byte[] header=new byte[282]; /* working space for header encode */ + int header_fill; + + public int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + int serialno; + int pageno; + long packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + long granulepos; + + public StreamState(){ + init(); + } + + StreamState(int serialno){ + this(); + init(serialno); + } + void init(){ + body_storage=16*1024; + body_data=new byte[body_storage]; + lacing_storage=1024; + lacing_vals=new int[lacing_storage]; + granule_vals=new long[lacing_storage]; + } + public void init(int serialno){ + if(body_data==null){ init(); } + else{ + for(int i=0; i<body_data.length; i++) body_data[i]=0; + for(int i=0; i<lacing_vals.length; i++) lacing_vals[i]=0; + for(int i=0; i<granule_vals.length; i++) granule_vals[i]=0; + } + this.serialno=serialno; + } + public void clear(){ + body_data=null; + lacing_vals=null; + granule_vals=null; + //memset(os,0,sizeof(ogg_stream_state)); + } + void destroy(){ + clear(); + } + void body_expand(int needed){ + if(body_storage<=body_fill+needed){ + body_storage+=(needed+1024); + byte[] foo=new byte[body_storage]; + System.arraycopy(body_data, 0, foo, 0, body_data.length); + body_data=foo; +//System.out.println("expand: body_fill="+body_fill+", body_storage="+body_data.length); + } + } + void lacing_expand(int needed){ + if(lacing_storage<=lacing_fill+needed){ + lacing_storage+=(needed+32); + int[] foo=new int[lacing_storage]; + System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length); + lacing_vals=foo; + + long[] bar=new long[lacing_storage]; + System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length); + granule_vals=bar; + } + } + + /* submit data to the internal buffer of the framing engine */ + public int packetin(Packet op){ + int lacing_val=op.bytes/255+1; + + if(body_returned!=0){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + body_fill-=body_returned; + if(body_fill!=0){ +// memmove(os->body_data,os->body_data+os->body_returned, +// os->body_fill*sizeof(char)); + System.arraycopy(body_data, body_returned, body_data, 0, body_fill); + } + body_returned=0; + } + + /* make sure we have the buffer storage */ + body_expand(op.bytes); + lacing_expand(lacing_val); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes); + body_fill+=op.bytes; +//System.out.println("add: "+body_fill); + + /* Store lacing vals for this packet */ + int j; + for(j=0;j<lacing_val-1;j++){ + lacing_vals[lacing_fill+j]=255; + granule_vals[lacing_fill+j]=granulepos; + } + lacing_vals[lacing_fill+j]=(op.bytes)%255; + granulepos=granule_vals[lacing_fill+j]=op.granulepos; + + /* flag the first segment as the beginning of the packet */ + lacing_vals[lacing_fill]|= 0x100; + + lacing_fill+=lacing_val; + + /* for the sake of completeness */ + packetno++; + + if(op.e_o_s!=0)e_o_s=1; + return(0); + } + + public int packetout(Packet op){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=lacing_returned; + + if(lacing_packet<=ptr){ + return(0); + } + + if((lacing_vals[ptr]&0x400)!=0){ + /* We lost sync here; let the app know */ + lacing_returned++; + + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + packetno++; + return(-1); + } + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=lacing_vals[ptr]&0xff; + int bytes=0; + + op.packet_base=body_data; + op.packet=body_returned; + op.e_o_s=lacing_vals[ptr]&0x200; /* last packet of the stream? */ + op.b_o_s=lacing_vals[ptr]&0x100; /* first packet of the stream? */ + bytes+=size; + + while(size==255){ + int val=lacing_vals[++ptr]; + size=val&0xff; + if((val&0x200)!=0)op.e_o_s=0x200; + bytes+=size; + } + + op.packetno=packetno; + op.granulepos=granule_vals[ptr]; + op.bytes=bytes; + +//System.out.println(this+" # body_returned="+body_returned); + body_returned+=bytes; +//System.out.println(this+"## body_returned="+body_returned); + + lacing_returned=ptr+1; + } + packetno++; + return(1); + } + + + // add the incoming page to the stream state; we decompose the page + // into packet segments here as well. + + public int pagein(Page og){ + byte[] header_base=og.header_base; + int header=og.header; + byte[] body_base=og.body_base; + int body=og.body; + int bodysize=og.body_len; + int segptr=0; + + int version=og.version(); + int continued=og.continued(); + int bos=og.bos(); + int eos=og.eos(); + long granulepos=og.granulepos(); + int _serialno=og.serialno(); + int _pageno=og.pageno(); + int segments=header_base[header+26]&0xff; + + // clean up 'returned data' + { + int lr=lacing_returned; + int br=body_returned; + + // body data + +//System.out.println("br="+br+", body_fill="+body_fill); + + if(br!=0){ + body_fill-=br; + if(body_fill!=0){ + System.arraycopy(body_data, br, body_data, 0, body_fill); + } + body_returned=0; + } + +//System.out.println("?? br="+br+", body_fill="+body_fill+" body_returned="+body_returned); + + if(lr!=0){ + // segment table + if((lacing_fill-lr)!=0){ + System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill-lr); + System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill-lr); + } + lacing_fill-=lr; + lacing_packet-=lr; + lacing_returned=0; + } + } + + // check the serial number + if(_serialno!=serialno)return(-1); + if(version>0)return(-1); + + lacing_expand(segments+1); + + // are we in sequence? + if(_pageno!=pageno){ + int i; + + // unroll previous partial packet (if any) + for(i=lacing_packet;i<lacing_fill;i++){ + body_fill-=lacing_vals[i]&0xff; +//System.out.println("??"); + } + lacing_fill=lacing_packet; + + // make a note of dropped data in segment table + if(pageno!=-1){ + lacing_vals[lacing_fill++]=0x400; + lacing_packet++; + } + + // are we a 'continued packet' page? If so, we'll need to skip + // some segments + if(continued!=0){ + bos=0; + for(;segptr<segments;segptr++){ + int val=(header_base[header+27+segptr]&0xff); + body+=val; + bodysize-=val; + if(val<255){ + segptr++; + break; + } + } + } + } + +//System.out.println("bodysize="+bodysize); + + if(bodysize!=0){ + body_expand(bodysize); + System.arraycopy(body_base, body, body_data, body_fill, bodysize); + body_fill+=bodysize; + } + +//System.out.println("bodyfill="+body_fill); + + { + int saved=-1; + while(segptr<segments){ + int val=(header_base[header+27+segptr]&0xff); + lacing_vals[lacing_fill]=val; + granule_vals[lacing_fill]=-1; + + if(bos!=0){ + lacing_vals[lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=lacing_fill; + + lacing_fill++; + segptr++; + + if(val<255)lacing_packet=lacing_fill; + } + + /* set the granulepos on the last pcmval of the last full packet */ + if(saved!=-1){ + granule_vals[saved]=granulepos; + } + } + + if(eos!=0){ + e_o_s=1; + if(lacing_fill>0) + lacing_vals[lacing_fill-1]|=0x200; + } + + pageno=_pageno+1; + return(0); + } + + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not gurantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + ogg_stream_page will flush the last page in a stream even if it's + undersized; you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you need to flush an undersized + page in the middle of a stream for some reason. */ + + public int flush(Page og){ + +//System.out.println(this+" ---body_returned: "+body_returned); + + int i; + int vals=0; + int maxvals=(lacing_fill>255?255:lacing_fill); + int bytes=0; + int acc=0; + long granule_pos=granule_vals[0]; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;vals<maxvals;vals++){ + if((lacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + } + else{ + for(vals=0;vals<maxvals;vals++){ + if(acc>4096)break; + acc+=(lacing_vals[vals]&0x0ff); + granule_pos=granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + System.arraycopy("OggS".getBytes(), 0, header, 0, 4); + + /* stream structure version */ + header[4]=0x00; + + /* continued packet flag? */ + header[5]=0x00; + if((lacing_vals[0]&0x100)==0)header[5]|=0x01; + /* first page flag? */ + if(b_o_s==0) header[5]|=0x02; + /* last page flag? */ + if(e_o_s!=0 && lacing_fill==vals) header[5]|=0x04; + b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + header[i]=(byte)granule_pos; + granule_pos>>>=8; + } + + /* 32 bits of stream serial number */ + { + int _serialno=serialno; + for(i=14;i<18;i++){ + header[i]=(byte)_serialno; + _serialno>>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(pageno==-1)pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + int _pageno=pageno++; + for(i=18;i<22;i++){ + header[i]=(byte)_pageno; + _pageno>>>=8; + } + } + + /* zero for computation; filled in later */ + header[22]=0; + header[23]=0; + header[24]=0; + header[25]=0; + + /* segment table */ + header[26]=(byte)vals; + for(i=0;i<vals;i++){ + header[i+27]=(byte)lacing_vals[i]; + bytes+=(header[i+27]&0xff); + } + + /* set pointers in the ogg_page struct */ + og.header_base=header; + og.header=0; + og.header_len=header_fill=vals+27; + og.body_base=body_data; + og.body=body_returned; + og.body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + +//System.out.println("###body_returned: "+body_returned); + + lacing_fill-=vals; + System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill*4); + System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill*8); + body_returned+=bytes; + +//System.out.println("####body_returned: "+body_returned); + + /* calculate the checksum */ + + og.checksum(); + + /* done */ + return(1); + } + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + public int pageout(Page og){ +// if(body_returned!=0){ +// /* advance packet data according to the body_returned pointer. We +// had to keep it around to return a pointer into the buffer last +// call */ +// +// body_fill-=body_returned; +// if(body_fill!=0){ // overlap? +// System.arraycopy(body_data, body_returned, body_data, 0, body_fill); +// } +// body_returned=0; +// } +// +//System.out.println("pageout: e_o_s="+e_o_s+" lacing_fill="+lacing_fill+" body_fill="+body_fill+", lacing_fill="+lacing_fill+" b_o_s="+b_o_s); +// +// if((e_o_s!=0&&lacing_fill!=0) || /* 'were done, now flush' case */ +// body_fill > 4096 || /* 'page nominal size' case */ +// lacing_fill>=255 || /* 'segment table full' case */ +// (lacing_fill!=0&&b_o_s==0)){ /* 'initial header page' case */ +// int vals=0,bytes=0; +// int maxvals=(lacing_fill>255?255:lacing_fill); +// long acc=0; +// long pcm_pos=granule_vals[0]; +// +// /* construct a page */ +// /* decide how many segments to include */ +// +// /* If this is the initial header case, the first page must only include +// the initial header packet */ +// if(b_o_s==0){ /* 'initial header page' case */ +// pcm_pos=0; +// for(vals=0;vals<maxvals;vals++){ +// if((lacing_vals[vals]&0x0ff)<255){ +// vals++; +// break; +// } +// } +// } +// else{ +// for(vals=0;vals<maxvals;vals++){ +// if(acc>4096)break; +// acc+=lacing_vals[vals]&0x0ff; +// pcm_pos=granule_vals[vals]; +// } +// } +// +// /* construct the header in temp storage */ +// System.arraycopy("OggS".getBytes(), 0, header, 0, 4); +// +// /* stream structure version */ +// header[4]=0x00; +// +// /* continued packet flag? */ +// header[5]=0x00; +// if((lacing_vals[0]&0x100)==0)header[5]|=0x01; +// /* first page flag? */ +// if(b_o_s==0)header[5]|=0x02; +// /* last page flag? */ +// if(e_o_s!=0 && lacing_fill==vals)header[5]|=0x04; +// b_o_s=1; +// +// /* 64 bits of PCM position */ +// for(int i=6;i<14;i++){ +// header[i]=(byte)pcm_pos; +// pcm_pos>>>=8; +// } +// +// /* 32 bits of stream serial number */ +// { +// int serialn=serialno; +// for(int i=14;i<18;i++){ +// header[i]=(byte)serialn; +// serialn>>>=8; +// } +// } +// +// +///* 32 bits of page counter (we have both counter and page header +// because this val can roll over) */ +// if(pageno==-1)pageno=0; /* because someone called +// stream_reset; this would be a +// strange thing to do in an +// encode stream, but it has +// plausible uses */ +// { +// int pagen=pageno++; +// for(int i=18;i<22;i++){ +// header[i]=(byte)pagen; +// pagen>>>=8; +// } +// } +// +// /* zero for computation; filled in later */ +// header[22]=0; +// header[23]=0; +// header[24]=0; +// header[25]=0; +// +// /* segment table */ +// header[26]=(byte)vals; +// for(int i=0;i<vals;i++){ +// header[i+27]=(byte)lacing_vals[i]; +// bytes+=header[i+27]&0xff; +//// bytes+=header[i+27]=(lacing_vals[i]&0xff); +// } +// +// /* advance the lacing data and set the body_returned pointer */ +// +// lacing_fill-=vals; +// System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill); +// System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill); +// body_returned=bytes; +// +// /* set pointers in the ogg_page struct */ +// og.header_base=header; +// og.header=0; +// og.header_len=header_fill=vals+27; +// +// og.body_base=body_data; +// og.body=0; +// og.body_len=bytes; +// +// /* calculate the checksum */ +// +// og.checksum(); +// return(1); +// } +// /* not enough data to construct a page and not end of stream */ +// return(0); +//System.out.println("pageout: "+body_returned); + if((e_o_s!=0&&lacing_fill!=0) || /* 'were done, now flush' case */ + body_fill-body_returned> 4096 || /* 'page nominal size' case */ + lacing_fill>=255 || /* 'segment table full' case */ + (lacing_fill!=0&&b_o_s==0)){ /* 'initial header page' case */ + return flush(og); + } + return 0; + } + + public int eof(){ + return e_o_s; + } + + public int reset(){ + body_fill=0; + body_returned=0; + + lacing_fill=0; + lacing_packet=0; + lacing_returned=0; + + header_fill=0; + + e_o_s=0; + b_o_s=0; + pageno=-1; + packetno=0; + granulepos=0; + return(0); + } +} diff --git a/songdbj/com/jcraft/jogg/SyncState.java b/songdbj/com/jcraft/jogg/SyncState.java new file mode 100644 index 0000000000..b3705e54dd --- /dev/null +++ b/songdbj/com/jcraft/jogg/SyncState.java @@ -0,0 +1,275 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jogg; + +// DECODING PRIMITIVES: packet streaming layer + +// This has two layers to place more of the multi-serialno and paging +// control in the application's hands. First, we expose a data buffer +// using ogg_decode_buffer(). The app either copies into the +// buffer, or passes it directly to read(), etc. We then call +// ogg_decode_wrote() to tell how many bytes we just added. +// +// Pages are returned (pointers into the buffer in ogg_sync_state) +// by ogg_decode_stream(). The page is then submitted to +// ogg_decode_page() along with the appropriate +// ogg_stream_state* (ie, matching serialno). We then get raw +// packets out calling ogg_stream_packet() with a +// ogg_stream_state. See the 'frame-prog.txt' docs for details and +// example code. + +public class SyncState{ + + public byte[] data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; + + public int clear(){ + data=null; + return(0); + } + +// !!!!!!!!!!!! +// byte[] buffer(int size){ + public int buffer(int size){ + // first, clear out any space that has been previously returned + if(returned!=0){ + fill-=returned; + if(fill>0){ + System.arraycopy(data, returned, data, 0, fill); + } + returned=0; + } + + if(size>storage-fill){ + // We need to extend the internal buffer + int newsize=size+fill+4096; // an extra page to be nice + if(data!=null){ + byte[] foo=new byte[newsize]; + System.arraycopy(data, 0, foo, 0, data.length); + data=foo; + } + else{ + data=new byte[newsize]; + } + storage=newsize; + } + + // expose a segment at least as large as requested at the fill mark +// return((char *)oy->data+oy->fill); +// return(data); + return(fill); + } + + public int wrote(int bytes){ + if(fill+bytes>storage)return(-1); + fill+=bytes; + return(0); + } + +// sync the stream. This is meant to be useful for finding page +// boundaries. +// +// return values for this: +// -n) skipped n bytes +// 0) page not ready; more data (no bytes skipped) +// n) page synced at current location; page length n bytes + private Page pageseek=new Page(); + private byte[] chksum=new byte[4]; + public int pageseek(Page og){ + int page=returned; + int next; + int bytes=fill-returned; + + if(headerbytes==0){ + int _headerbytes,i; + if(bytes<27)return(0); // not enough for a header + + /* verify capture pattern */ +//!!!!!!!!!!! + if(data[page]!='O' || + data[page+1]!='g' || + data[page+2]!='g' || + data[page+3]!='S'){ + headerbytes=0; + bodybytes=0; + + // search for possible capture + next=0; + for(int ii=0; ii<bytes-1; ii++){ + if(data[page+1+ii]=='O'){next=page+1+ii; break;} + } + //next=memchr(page+1,'O',bytes-1); + if(next==0) next=fill; + + returned=next; + return(-(next-page)); + } + _headerbytes=(data[page+26]&0xff)+27; + if(bytes<_headerbytes)return(0); // not enough for header + seg table + + // count up body length in the segment table + + for(i=0;i<(data[page+26]&0xff);i++){ + bodybytes+=(data[page+27+i]&0xff); + } + headerbytes=_headerbytes; + } + + if(bodybytes+headerbytes>bytes)return(0); + + // The whole test page is buffered. Verify the checksum + synchronized(chksum){ + // Grab the checksum bytes, set the header field to zero + + System.arraycopy(data, page+22, chksum, 0, 4); + data[page+22]=0; + data[page+23]=0; + data[page+24]=0; + data[page+25]=0; + + // set up a temp page struct and recompute the checksum + Page log=pageseek; + log.header_base=data; + log.header=page; + log.header_len=headerbytes; + + log.body_base=data; + log.body=page+headerbytes; + log.body_len=bodybytes; + log.checksum(); + + // Compare + if(chksum[0]!=data[page+22] || + chksum[1]!=data[page+23] || + chksum[2]!=data[page+24] || + chksum[3]!=data[page+25]){ + // D'oh. Mismatch! Corrupt page (or miscapture and not a page at all) + // replace the computed checksum with the one actually read in + System.arraycopy(chksum, 0, data, page+22, 4); + // Bad checksum. Lose sync */ + + headerbytes=0; + bodybytes=0; + // search for possible capture + next=0; + for(int ii=0; ii<bytes-1; ii++){ + if(data[page+1+ii]=='O'){next=page+1+ii; break;} + } + //next=memchr(page+1,'O',bytes-1); + if(next==0) next=fill; + returned=next; + return(-(next-page)); + } + } + + // yes, have a whole page all ready to go + { + page=returned; + + if(og!=null){ + og.header_base=data; + og.header=page; + og.header_len=headerbytes; + og.body_base=data; + og.body=page+headerbytes; + og.body_len=bodybytes; + } + + unsynced=0; + returned+=(bytes=headerbytes+bodybytes); + headerbytes=0; + bodybytes=0; + return(bytes); + } +// headerbytes=0; +// bodybytes=0; +// next=0; +// for(int ii=0; ii<bytes-1; ii++){ +// if(data[page+1+ii]=='O'){next=page+1+ii;} +// } +// //next=memchr(page+1,'O',bytes-1); +// if(next==0) next=fill; +// returned=next; +// return(-(next-page)); + } + + +// sync the stream and get a page. Keep trying until we find a page. +// Supress 'sync errors' after reporting the first. +// +// return values: +// -1) recapture (hole in data) +// 0) need more data +// 1) page returned +// +// Returns pointers into buffered data; invalidated by next call to +// _stream, _clear, _init, or _buffer + + public int pageout(Page og){ + // all we need to do is verify a page at the head of the stream + // buffer. If it doesn't verify, we look for the next potential + // frame + + while(true){ + int ret=pageseek(og); + if(ret>0){ + // have a page + return(1); + } + if(ret==0){ + // need more data + return(0); + } + + // head did not start a synced page... skipped some bytes + if(unsynced==0){ + unsynced=1; + return(-1); + } + // loop. keep looking + } + } + +// clear things to an initial state. Good to call, eg, before seeking + public int reset(){ + fill=0; + returned=0; + unsynced=0; + headerbytes=0; + bodybytes=0; + return(0); + } + public void init(){} + + public int getDataOffset(){ return returned; } + public int getBufferOffset(){ return fill; } +} diff --git a/songdbj/com/jcraft/jorbis/AllocChain.java b/songdbj/com/jcraft/jorbis/AllocChain.java new file mode 100644 index 0000000000..b3492d5c94 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/AllocChain.java @@ -0,0 +1,31 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class AllocChain{ + Object ptr; + AllocChain next; +}; diff --git a/songdbj/com/jcraft/jorbis/Block.java b/songdbj/com/jcraft/jorbis/Block.java new file mode 100644 index 0000000000..8fd15f76bf --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Block.java @@ -0,0 +1,188 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +public class Block{ + ///necessary stream state for linking to the framing abstraction + float[][] pcm=new float[0][]; // this is a pointer into local storage + Buffer opb=new Buffer(); + + int lW; + int W; + int nW; + int pcmend; + int mode; + + int eofflag; + long granulepos; + long sequence; + DspState vd; // For read-only access of configuration + + // local storage to avoid remallocing; it's up to the mapping to + // structure it +//byte[] localstore; +//int localtop; +//int localalloc; +//int totaluse; +//AllocChain reap; + + // bitmetrics for the frame + int glue_bits; + int time_bits; + int floor_bits; + int res_bits; + + public Block(DspState vd){ + this.vd=vd; +// localalloc=0; +// localstore=null; + if(vd.analysisp!=0){ + opb.writeinit(); + } + } + + public void init(DspState vd){ + this.vd=vd; + } + +// int alloc(int bytes){ +// bytes=(bytes+(8-1))&(~(8-1)); +// if(bytes+localtop>localalloc){ +// if(localstore!=null){ +// AllocChain link=new AllocChain(); +// totaluse+=localtop; +// link.next=reap; +// link.ptr=localstore; +// reap=link; +// } +// // highly conservative +// localalloc=bytes; +// localstore=new byte[localalloc]; +// localtop=0; +// } +// { +// int foo=localtop; +// //void *ret=(void *)(((char *)vb->localstore)+vb->localtop); +// localtop+=bytes; +// return foo; +// } +// } + + // reap the chain, pull the ripcord +// void ripcord(){ +// // reap the chain +// while(reap!=null){ +// AllocChain next=reap.next; +// //free(reap->ptr); +// reap.ptr=null; +// //memset(reap,0,sizeof(struct alloc_chain)); +// //free(reap); +// reap=next; +// } +// // consolidate storage +// if(totaluse!=0){ +// //vb->localstore=realloc(vb->localstore,vb->totaluse+vb->localalloc); +// byte[] foo=new byte[totaluse+localalloc]; +// System.arraycopy(localstore, 0, foo, 0, localstore.length); +// localstore=foo; +// localalloc+=totaluse; +// totaluse=0; +// } +// // pull the ripcord +// localtop=0; +// reap=null; +// } + + public int clear(){ + if(vd!=null){ + if(vd.analysisp!=0){ + opb.writeclear(); + } + } + //ripcord(); + //if(localstore!=null) + // localstore=null; + //memset(vb,0,sizeof(vorbis_block)); + return(0); + } + + public int synthesis(Packet op){ + Info vi=vd.vi; + + // first things first. Make sure decode is ready + // ripcord(); + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Check the packet type + if(opb.read(1)!=0){ + // Oops. This is not an audio data packet + return(-1); + } + + // read our mode and pre/post windowsize + int _mode=opb.read(vd.modebits); + if(_mode==-1)return(-1); + + mode=_mode; + W=vi.mode_param[mode].blockflag; + if(W!=0){ + lW=opb.read(1); + nW=opb.read(1); + if(nW==-1) return(-1); + } + else{ + lW=0; + nW=0; + } + + // more setup + granulepos=op.granulepos; + sequence=op.packetno-3; // first block is third packet + eofflag=op.e_o_s; + + // alloc pcm passback storage + pcmend=vi.blocksizes[W]; + //pcm=alloc(vi.channels); + if(pcm.length<vi.channels){ + pcm=new float[vi.channels][]; + } + for(int i=0;i<vi.channels;i++){ + if(pcm[i]==null || pcm[i].length<pcmend){ + pcm[i]=new float[pcmend]; + //pcm[i]=alloc(pcmend); + } + else{ + for(int j=0;j<pcmend;j++){ pcm[i][j]=0; } + } + } + + // unpack_header enforces range checking + int type=vi.map_type[vi.mode_param[mode].mapping]; + return(FuncMapping.mapping_P[type].inverse(this, vd.mode[mode])); + } +} diff --git a/songdbj/com/jcraft/jorbis/ChainingExample.java b/songdbj/com/jcraft/jorbis/ChainingExample.java new file mode 100644 index 0000000000..82592f29c4 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/ChainingExample.java @@ -0,0 +1,61 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class ChainingExample{ + public static void main(String[] arg){ + VorbisFile ov=null; + + try{ + ov=new VorbisFile(System.in, null, -1); + } + catch(Exception e){ + System.err.println(e); + return; + } + + if(ov.seekable()){ + System.out.println("Input bitstream contained "+ov.streams()+" logical bitstream section(s)."); + System.out.println("Total bitstream playing time: "+ov.time_total(-1)+" seconds\n"); + } + else{ + System.out.println("Standard input was not seekable."); + System.out.println("First logical bitstream information:\n"); + } + + for(int i=0;i<ov.streams();i++){ + Info vi=ov.getInfo(i); + System.out.println("\tlogical bitstream section "+(i+1)+" information:"); + System.out.println("\t\t"+vi.rate+"Hz "+vi.channels+" channels bitrate "+ + (ov.bitrate(i)/1000)+"kbps serial number="+ov.serialnumber(i)); + System.out.print("\t\tcompressed length: "+ov.raw_total(i)+" bytes "); + System.out.println(" play time: "+ov.time_total(i)+"s"); + Comment vc=ov.getComment(i); + System.out.println(vc); + } + //clear(&ov); + } +} diff --git a/songdbj/com/jcraft/jorbis/CodeBook.java b/songdbj/com/jcraft/jorbis/CodeBook.java new file mode 100644 index 0000000000..9708e066a4 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/CodeBook.java @@ -0,0 +1,742 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class CodeBook{ + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + StaticCodeBook c=new StaticCodeBook(); + + float[] valuelist; // list of dim*entries actual entry values + int[] codelist; // list of bitstream codewords for each entry + DecodeAux decode_tree; + + // returns the number of bits + int encode(int a, Buffer b){ + b.write(codelist[a], c.lengthlist[a]); + return(c.lengthlist[a]); + } + + // One the encode side, our vector writers are each designed for a + // specific purpose, and the encoder is not flexible without modification: + // + // The LSP vector coder uses a single stage nearest-match with no + // interleave, so no step and no error return. This is specced by floor0 + // and doesn't change. + // + // Residue0 encoding interleaves, uses multiple stages, and each stage + // peels of a specific amount of resolution from a lattice (thus we want + // to match by threshhold, not nearest match). Residue doesn't *have* to + // be encoded that way, but to change it, one will need to add more + // infrastructure on the encode side (decode side is specced and simpler) + + // floor0 LSP (single stage, non interleaved, nearest match) + // returns entry number and *modifies a* to the quantization value + int errorv(float[] a){ + int best=best(a,1); + for(int k=0;k<dim;k++){ + a[k]=valuelist[best*dim+k]; + } + return(best); + } + + // returns the number of bits and *modifies a* to the quantization value + int encodev(int best, float[] a, Buffer b){ + for(int k=0;k<dim;k++){ + a[k]=valuelist[best*dim+k]; + } + return(encode(best,b)); + } + + // res0 (multistage, interleave, lattice) + // returns the number of bits and *modifies a* to the remainder value + int encodevs(float[] a, Buffer b, int step,int addmul){ + int best=besterror(a,step,addmul); + return(encode(best,b)); + } + + private int[] t=new int[15]; // decodevs_add is synchronized for re-using t. + synchronized int decodevs_add(float[]a, int offset, Buffer b, int n){ + int step=n/dim; + int entry; + int i,j,o; + + if(t.length<step){ + t=new int[step]; + } + + for(i = 0; i < step; i++){ + entry=decode(b); + if(entry==-1)return(-1); + t[i]=entry*dim; + } + for(i=0,o=0;i<dim;i++,o+=step){ + for(j=0;j<step;j++){ + a[offset+o+j]+=valuelist[t[j]+i]; + } + } + + return(0); + } + + int decodev_add(float[]a, int offset, Buffer b,int n){ + int i,j,entry; + int t; + + if(dim>8){ + for(i=0;i<n;){ + entry = decode(b); + if(entry==-1)return(-1); + t=entry*dim; + for(j=0;j<dim;){ + a[offset+(i++)]+=valuelist[t+(j++)]; + } + } + } + else{ + for(i=0;i<n;){ + entry=decode(b); + if(entry==-1)return(-1); + t=entry*dim; + j=0; + switch(dim){ + case 8: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 7: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 6: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 5: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 4: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 3: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 2: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 1: + a[offset+(i++)]+=valuelist[t+(j++)]; + case 0: + break; + } + } + } + return(0); + } + + int decodev_set(float[] a,int offset, Buffer b, int n){ + int i,j,entry; + int t; + + for(i=0;i<n;){ + entry = decode(b); + if(entry==-1)return(-1); + t=entry*dim; + for(j=0;j<dim;){ + a[offset+i++]=valuelist[t+(j++)]; + } + } + return(0); + } + + int decodevv_add(float[][] a, int offset,int ch, Buffer b,int n){ + int i,j,k,entry; + int chptr=0; + //System.out.println("decodevv_add: a="+a+",b="+b+",valuelist="+valuelist); + + for(i=offset/ch;i<(offset+n)/ch;){ + entry = decode(b); + if(entry==-1)return(-1); + + int t = entry*dim; + for(j=0;j<dim;j++){ + a[chptr++][i]+=valuelist[t+j]; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + return(0); + } + + + // Decode side is specced and easier, because we don't need to find + // matches using different criteria; we simply read and map. There are + // two things we need to do 'depending': + // + // We may need to support interleave. We don't really, but it's + // convenient to do it here rather than rebuild the vector later. + // + // Cascades may be additive or multiplicitive; this is not inherent in + // the codebook, but set in the code using the codebook. Like + // interleaving, it's easiest to do it here. + // stage==0 -> declarative (set the value) + // stage==1 -> additive + // stage==2 -> multiplicitive + + // returns the entry number or -1 on eof + int decode(Buffer b){ + int ptr=0; + DecodeAux t=decode_tree; + int lok=b.look(t.tabn); + //System.err.println(this+" "+t+" lok="+lok+", tabn="+t.tabn); + + if(lok>=0){ + ptr=t.tab[lok]; + b.adv(t.tabl[lok]); + if(ptr<=0){ + return -ptr; + } + } + do{ + switch(b.read1()){ + case 0: + ptr=t.ptr0[ptr]; + break; + case 1: + ptr=t.ptr1[ptr]; + break; + case -1: + default: + return(-1); + } + } + while(ptr>0); + return(-ptr); + } + + // returns the entry number or -1 on eof + int decodevs(float[] a, int index, Buffer b, int step,int addmul){ + int entry=decode(b); + if(entry==-1)return(-1); + switch(addmul){ + case -1: + for(int i=0,o=0;i<dim;i++,o+=step) + a[index+o]=valuelist[entry*dim+i]; + break; + case 0: + for(int i=0,o=0;i<dim;i++,o+=step) + a[index+o]+=valuelist[entry*dim+i]; + break; + case 1: + for(int i=0,o=0;i<dim;i++,o+=step) + a[index+o]*=valuelist[entry*dim+i]; + break; + default: + //System.err.println("CodeBook.decodeves: addmul="+addmul); + } + return(entry); + } + + int best(float[] a, int step){ + EncodeAuxNearestMatch nt=c.nearest_tree; + EncodeAuxThreshMatch tt=c.thresh_tree; + int ptr=0; + + // we assume for now that a thresh tree is the only other possibility + if(tt!=null){ + int index=0; + // find the quant val of each scalar + for(int k=0,o=step*(dim-1);k<dim;k++,o-=step){ + int i; + // linear search the quant list for now; it's small and although + // with > 8 entries, it would be faster to bisect, this would be + // a misplaced optimization for now + for(i=0;i<tt.threshvals-1;i++){ + if(a[o]<tt.quantthresh[i]){ + break; + } + } + index=(index*tt.quantvals)+tt.quantmap[i]; + } + // regular lattices are easy :-) + if(c.lengthlist[index]>0){ + // is this unused? If so, we'll + // use a decision tree after all + // and fall through + return(index); + } + } + if(nt!=null){ + // optimized using the decision tree + while(true){ + float c=0.f; + int p=nt.p[ptr]; + int q=nt.q[ptr]; + for(int k=0,o=0;k<dim;k++,o+=step){ + c+=(valuelist[p+k]-valuelist[q+k])* + (a[o]-(valuelist[p+k]+valuelist[q+k])*.5); + } + if(c>0.){ // in A + ptr= -nt.ptr0[ptr]; + } + else{ // in B + ptr= -nt.ptr1[ptr]; + } + if(ptr<=0)break; + } + return(-ptr); + } + + // brute force it! + { + int besti=-1; + float best=0.f; + int e=0; + for(int i=0;i<entries;i++){ + if(c.lengthlist[i]>0){ + float _this=dist(dim, valuelist, e, a, step); + if(besti==-1 || _this<best){ + best=_this; + besti=i; + } + } + e+=dim; + } + return(besti); + } + } + + // returns the entry number and *modifies a* to the remainder value + int besterror(float[] a, int step, int addmul){ + int best=best(a,step); + switch(addmul){ + case 0: + for(int i=0,o=0;i<dim;i++,o+=step) + a[o]-=valuelist[best*dim+i]; + break; + case 1: + for(int i=0,o=0;i<dim;i++,o+=step){ + float val=valuelist[best*dim+i]; + if(val==0){ + a[o]=0; + }else{ + a[o]/=val; + } + } + break; + } + return(best); + } + + void clear(){ + // static book is not cleared; we're likely called on the lookup and + // the static codebook belongs to the info struct + //if(decode_tree!=null){ + // free(b->decode_tree->ptr0); + // free(b->decode_tree->ptr1); + // memset(b->decode_tree,0,sizeof(decode_aux)); + // free(b->decode_tree); + //} + //if(valuelist!=null)free(b->valuelist); + //if(codelist!=null)free(b->codelist); + //memset(b,0,sizeof(codebook)); + } + + private static float dist(int el, float[] ref, int index, float[] b, int step){ + float acc=(float)0.; + for(int i=0; i<el; i++){ + float val=(ref[index+i]-b[i*step]); + acc+=val*val; + } + return(acc); + } + +/* + int init_encode(StaticCodeBook s){ + //memset(c,0,sizeof(codebook)); + c=s; + entries=s.entries; + dim=s.dim; + codelist=make_words(s.lengthlist, s.entries); + valuelist=s.unquantize(); + return(0); + } +*/ + + int init_decode(StaticCodeBook s){ + //memset(c,0,sizeof(codebook)); + c=s; + entries=s.entries; + dim=s.dim; + valuelist=s.unquantize(); + + decode_tree=make_decode_tree(); + if(decode_tree==null){ + //goto err_out; + clear(); + return(-1); + } + return(0); +// err_out: +// vorbis_book_clear(c); +// return(-1); + } + + // given a list of word lengths, generate a list of codewords. Works + // for length ordered or unordered, always assigns the lowest valued + // codewords first. Extended to handle unused entries (length 0) + static int[] make_words(int[] l, int n){ + int[] marker=new int[33]; + int[] r=new int[n]; + //memset(marker,0,sizeof(marker)); + + for(int i=0;i<n;i++){ + int length=l[i]; + if(length>0){ + int entry=marker[length]; + + // when we claim a node for an entry, we also claim the nodes + // below it (pruning off the imagined tree that may have dangled + // from it) as well as blocking the use of any nodes directly + // above for leaves + + // update ourself + if(length<32 && (entry>>>length)!=0){ + // error condition; the lengths must specify an overpopulated tree + //free(r); + return(null); + } + r[i]=entry; + + // Look to see if the next shorter marker points to the node + // above. if so, update it and repeat. + { + for(int j=length;j>0;j--){ + if((marker[j]&1)!=0){ + // have to jump branches + if(j==1)marker[1]++; + else marker[j]=marker[j-1]<<1; + break; // invariant says next upper marker would already + // have been moved if it was on the same path + } + marker[j]++; + } + } + + // prune the tree; the implicit invariant says all the longer + // markers were dangling from our just-taken node. Dangle them + // from our *new* node. + for(int j=length+1;j<33;j++){ + if((marker[j]>>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + } + else{ + break; + } + } + } + } + + // bitreverse the words because our bitwise packer/unpacker is LSb + // endian + for(int i=0;i<n;i++){ + int temp=0; + for(int j=0;j<l[i];j++){ + temp<<=1; + temp|=(r[i]>>>j)&1; + } + r[i]=temp; + } + + return(r); + } + + // build the decode helper tree from the codewords + DecodeAux make_decode_tree(){ + int top=0; + DecodeAux t=new DecodeAux(); + int[] ptr0=t.ptr0=new int[entries*2]; + int[] ptr1=t.ptr1=new int[entries*2]; + int[] codelist=make_words(c.lengthlist, c.entries); + + if(codelist==null)return(null); + t.aux=entries*2; + + for(int i=0;i<entries;i++){ + if(c.lengthlist[i]>0){ + int ptr=0; + int j; + for(j=0;j<c.lengthlist[i]-1;j++){ + int bit=(codelist[i]>>>j)&1; + if(bit==0){ + if(ptr0[ptr]==0){ + ptr0[ptr]=++top; + } + ptr=ptr0[ptr]; + } + else{ + if(ptr1[ptr]==0){ + ptr1[ptr]= ++top; + } + ptr=ptr1[ptr]; + } + } + + if(((codelist[i]>>>j)&1)==0){ ptr0[ptr]=-i; } + else{ ptr1[ptr]=-i; } + + } + } + //free(codelist); + + t.tabn = ilog(entries)-4; + + if(t.tabn<5)t.tabn=5; + int n = 1<<t.tabn; + t.tab = new int[n]; + t.tabl = new int[n]; + for(int i = 0; i < n; i++){ + int p = 0; + int j=0; + for(j = 0; j < t.tabn && (p > 0 || j == 0); j++){ + if ((i&(1<<j))!=0){ + p = ptr1[p]; + } + else{ + p = ptr0[p]; + } + } + t.tab[i]=p; // -code + t.tabl[i]=j; // length + } + + return(t); + } + + private static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return(ret); + } + +/* + // TEST + // Simple enough; pack a few candidate codebooks, unpack them. Code a + // number of vectors through (keeping track of the quantized values), + // and decode using the unpacked book. quantized version of in should + // exactly equal out + + //#include "vorbis/book/lsp20_0.vqh" + //#include "vorbis/book/lsp32_0.vqh" + //#include "vorbis/book/res0_1a.vqh" + static final int TESTSIZE=40; + + static float[] test1={ + 0.105939, + 0.215373, + 0.429117, + 0.587974, + + 0.181173, + 0.296583, + 0.515707, + 0.715261, + + 0.162327, + 0.263834, + 0.342876, + 0.406025, + + 0.103571, + 0.223561, + 0.368513, + 0.540313, + + 0.136672, + 0.395882, + 0.587183, + 0.652476, + + 0.114338, + 0.417300, + 0.525486, + 0.698679, + + 0.147492, + 0.324481, + 0.643089, + 0.757582, + + 0.139556, + 0.215795, + 0.324559, + 0.399387, + + 0.120236, + 0.267420, + 0.446940, + 0.608760, + + 0.115587, + 0.287234, + 0.571081, + 0.708603, + }; + + static float[] test2={ + 0.088654, + 0.165742, + 0.279013, + 0.395894, + + 0.110812, + 0.218422, + 0.283423, + 0.371719, + + 0.136985, + 0.186066, + 0.309814, + 0.381521, + + 0.123925, + 0.211707, + 0.314771, + 0.433026, + + 0.088619, + 0.192276, + 0.277568, + 0.343509, + + 0.068400, + 0.132901, + 0.223999, + 0.302538, + + 0.202159, + 0.306131, + 0.360362, + 0.416066, + + 0.072591, + 0.178019, + 0.304315, + 0.376516, + + 0.094336, + 0.188401, + 0.325119, + 0.390264, + + 0.091636, + 0.223099, + 0.282899, + 0.375124, + }; + + static float[] test3={ + 0,1,-2,3,4,-5,6,7,8,9, + 8,-2,7,-1,4,6,8,3,1,-9, + 10,11,12,13,14,15,26,17,18,19, + 30,-25,-30,-1,-5,-32,4,3,-2,0}; + +// static_codebook *testlist[]={&_vq_book_lsp20_0, +// &_vq_book_lsp32_0, +// &_vq_book_res0_1a,NULL}; + static[][] float testvec={test1,test2,test3}; + + static void main(String[] arg){ + Buffer write=new Buffer(); + Buffer read=new Buffer(); + int ptr=0; + write.writeinit(); + + System.err.println("Testing codebook abstraction...:"); + + while(testlist[ptr]!=null){ + CodeBook c=new CodeBook(); + StaticCodeBook s=new StaticCodeBook();; + float *qv=alloca(sizeof(float)*TESTSIZE); + float *iv=alloca(sizeof(float)*TESTSIZE); + memcpy(qv,testvec[ptr],sizeof(float)*TESTSIZE); + memset(iv,0,sizeof(float)*TESTSIZE); + + System.err.print("\tpacking/coding "+ptr+"... "); + + // pack the codebook, write the testvector + write.reset(); + vorbis_book_init_encode(&c,testlist[ptr]); // get it into memory + // we can write + vorbis_staticbook_pack(testlist[ptr],&write); + System.err.print("Codebook size "+write.bytes()+" bytes... "); + for(int i=0;i<TESTSIZE;i+=c.dim){ + vorbis_book_encodev(&c,qv+i,&write); + } + c.clear(); + + System.err.print("OK.\n"); + System.err.print("\tunpacking/decoding "+ptr+"... "); + + // transfer the write data to a read buffer and unpack/read + _oggpack_readinit(&read,_oggpack_buffer(&write),_oggpack_bytes(&write)); + if(s.unpack(read)){ + System.err.print("Error unpacking codebook.\n"); + System.exit(1); + } + if(vorbis_book_init_decode(&c,&s)){ + System.err.print("Error initializing codebook.\n"); + System.exit(1); + } + for(int i=0;i<TESTSIZE;i+=c.dim){ + if(vorbis_book_decodevs(&c,iv+i,&read,1,-1)==-1){ + System.err.print("Error reading codebook test data (EOP).\n"); + System.exit(1); + } + } + for(int i=0;i<TESTSIZE;i++){ + if(fabs(qv[i]-iv[i])>.000001){ + System.err.print("read ("+iv[i]+") != written ("+qv[i]+") at position ("+i+")\n"); + System.exit(1); + } + } + + System.err.print("OK\n"); + ptr++; + } + // The above is the trivial stuff; + // now try unquantizing a log scale codebook + } +*/ +} + +class DecodeAux{ + int[] tab; + int[] tabl; + int tabn; + + int[] ptr0; + int[] ptr1; + int aux; // number of tree entries +} diff --git a/songdbj/com/jcraft/jorbis/Comment.java b/songdbj/com/jcraft/jorbis/Comment.java new file mode 100644 index 0000000000..f83b7cb985 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Comment.java @@ -0,0 +1,252 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +// the comments are not part of vorbis_info so that vorbis_info can be +// static storage +public class Comment{ + private static byte[] _vorbis="vorbis".getBytes(); + + private static final int OV_EFAULT=-129; + private static final int OV_EIMPL=-130; + + // unlimited user comment fields. libvorbis writes 'libvorbis' + // whatever vendor is set to in encode + public byte[][] user_comments; + public int[] comment_lengths; + public int comments; + public byte[] vendor; + + public void init(){ + user_comments=null; + comments=0; + vendor=null; + } + + public void add(String comment){ + add(comment.getBytes()); + } + + private void add(byte[] comment){ + byte[][] foo=new byte[comments+2][]; + if(user_comments!=null){ + System.arraycopy(user_comments, 0, foo, 0, comments); + } + user_comments=foo; + + int[] goo=new int[comments+2]; + if(comment_lengths!=null){ + System.arraycopy(comment_lengths, 0, goo, 0, comments); + } + comment_lengths=goo; + + byte[] bar=new byte[comment.length+1]; + System.arraycopy(comment, 0, bar, 0, comment.length); + user_comments[comments]=bar; + comment_lengths[comments]=comment.length; + comments++; + user_comments[comments]=null; + } + + public void add_tag(String tag, String contents){ + if(contents==null) contents=""; + add(tag+"="+contents); + } + +/* + private void add_tag(byte[] tag, byte[] contents){ + byte[] foo=new byte[tag.length+contents.length+1]; + int j=0; + for(int i=0; i<tag.length; i++){foo[j++]=tag[i];} + foo[j++]=(byte)'='; j++; + for(int i=0; i<contents.length; i++){foo[j++]=tag[i];} + add(foo); + } +*/ + + // This is more or less the same as strncasecmp - but that doesn't exist + // * everywhere, and this is a fairly trivial function, so we include it + static boolean tagcompare(byte[] s1, byte[] s2, int n){ + int c=0; + byte u1, u2; + while(c < n){ + u1=s1[c]; u2=s2[c]; + if('Z'>=u1 && u1>='A')u1=(byte)(u1-'A'+'a'); + if('Z'>=u2 && u2>='A')u2=(byte)(u2-'A'+'a'); + if(u1!=u2){ return false; } + c++; + } + return true; + } + + public String query(String tag){ + return query(tag, 0); + } + + public String query(String tag, int count){ + int foo=query(tag.getBytes(), count); + if(foo==-1)return null; + byte[] comment=user_comments[foo]; + for(int i=0; i<comment_lengths[foo]; i++){ + if(comment[i]=='='){ + return new String(comment, i+1, comment_lengths[foo]-(i+1)); + } + } + return null; + } + + private int query(byte[] tag, int count){ + int i=0; + int found = 0; + int fulltaglen = tag.length + 1; + byte[] fulltag = new byte[fulltaglen]; + System.arraycopy(tag, 0, fulltag, 0, tag.length); + fulltag[tag.length]=(byte)'='; + + for(i=0;i<comments;i++){ + if(tagcompare(user_comments[i], fulltag, fulltaglen)){ + if(count==found){ + // We return a pointer to the data, not a copy + //return user_comments[i] + taglen + 1; + return i; + } + else{ found++; } + } + } + return -1; + } + + int unpack(Buffer opb){ + int vendorlen=opb.read(32); + if(vendorlen<0){ + //goto err_out; + clear(); + return(-1); + } + vendor=new byte[vendorlen+1]; + opb.read(vendor,vendorlen); + comments=opb.read(32); + if(comments<0){ + //goto err_out; + clear(); + return(-1); + } + user_comments=new byte[comments+1][]; + comment_lengths=new int[comments+1]; + + for(int i=0;i<comments;i++){ + int len=opb.read(32); + if(len<0){ + //goto err_out; + clear(); + return(-1); + } + comment_lengths[i]=len; + user_comments[i]=new byte[len+1]; + opb.read(user_comments[i], len); + } + if(opb.read(1)!=1){ + //goto err_out; // EOP check + clear(); + return(-1); + + } + return(0); +// err_out: +// comment_clear(vc); +// return(-1); + } + + int pack(Buffer opb){ + byte[] temp="Xiphophorus libVorbis I 20000508".getBytes(); + + // preamble + opb.write(0x03,8); + opb.write(_vorbis); + + // vendor + opb.write(temp.length,32); + opb.write(temp); + + // comments + + opb.write(comments,32); + if(comments!=0){ + for(int i=0;i<comments;i++){ + if(user_comments[i]!=null){ + opb.write(comment_lengths[i],32); + opb.write(user_comments[i]); + } + else{ + opb.write(0,32); + } + } + } + opb.write(1,1); + return(0); + } + + public int header_out(Packet op){ + Buffer opb=new Buffer(); + opb.writeinit(); + + if(pack(opb)!=0) return OV_EIMPL; + + op.packet_base = new byte[opb.bytes()]; + op.packet=0; + op.bytes=opb.bytes(); + System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes); + op.b_o_s=0; + op.e_o_s=0; + op.granulepos=0; + return 0; + } + + void clear(){ + for(int i=0;i<comments;i++) + user_comments[i]=null; + user_comments=null; + vendor=null; + } + + public String getVendor(){ + return new String(vendor, 0, vendor.length-1); + } + public String getComment(int i){ + if(comments<=i)return null; + return new String(user_comments[i], 0, user_comments[i].length-1); + } + public String toString(){ + String foo="Vendor: "+new String(vendor, 0, vendor.length-1); + for(int i=0; i<comments; i++){ + foo=foo+"\nComment: "+new String(user_comments[i], 0, user_comments[i].length-1); + } + foo=foo+"\n"; + return foo; + } +} diff --git a/songdbj/com/jcraft/jorbis/DecodeExample.java b/songdbj/com/jcraft/jorbis/DecodeExample.java new file mode 100644 index 0000000000..f8768969a2 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/DecodeExample.java @@ -0,0 +1,316 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +// Takes a vorbis bitstream from stdin and writes raw stereo PCM to +// stdout. Decodes simple and chained OggVorbis files from beginning +// to end. Vorbisfile.a is somewhat more complex than the code below. + +class DecodeExample{ + static int convsize=4096*2; + static byte[] convbuffer=new byte[convsize]; // take 8k out of the data segment, not the stack + + public static void main(String[] arg){ + java.io.InputStream input=System.in; + if(arg.length>0){ + try{ + input=new java.io.FileInputStream(arg[0]); + } + catch(Exception e){ + System.err.println(e); + } + } + + SyncState oy=new SyncState(); // sync and verify incoming physical bitstream + StreamState os=new StreamState(); // take physical pages, weld into a logical stream of packets + Page og=new Page(); // one Ogg bitstream page. Vorbis packets are inside + Packet op=new Packet(); // one raw packet of data for decode + + Info vi=new Info(); // struct that stores all the static vorbis bitstream settings + Comment vc=new Comment(); // struct that stores all the bitstream user comments + DspState vd=new DspState(); // central working state for the packet->PCM decoder + Block vb=new Block(vd); // local working space for packet->PCM decode + + byte[] buffer; + int bytes=0; + + // Decode setup + + oy.init(); // Now we can read pages + + while(true){ // we repeat if the bitstream is chained + int eos=0; + + // grab some data at the head of the stream. We want the first page + // (which is guaranteed to be small and only contain the Vorbis + // stream initial header) We need the first page to get the stream + // serialno. + + // submit a 4k block to libvorbis' Ogg layer + int index=oy.buffer(4096); + buffer=oy.data; + try{ + bytes=input.read(buffer, index, 4096); + } + catch(Exception e){ + System.err.println(e); + System.exit(-1); + } + oy.wrote(bytes); + + // Get the first page. + if(oy.pageout(og)!=1){ + // have we simply run out of data? If so, we're done. + if(bytes<4096)break; + + // error case. Must not be Vorbis data + System.err.println("Input does not appear to be an Ogg bitstream."); + System.exit(1); + } + + // Get the serial number and set up the rest of decode. + // serialno first; use it to set up a logical stream + os.init(og.serialno()); + + // extract the initial header from the first page and verify that the + // Ogg bitstream is in fact Vorbis data + + // I handle the initial header first instead of just having the code + // read all three Vorbis headers at once because reading the initial + // header is an easy way to identify a Vorbis bitstream and it's + // useful to see that functionality seperated out. + + vi.init(); + vc.init(); + if(os.pagein(og)<0){ + // error; stream version mismatch perhaps + System.err.println("Error reading first page of Ogg bitstream data."); + System.exit(1); + } + + if(os.packetout(op)!=1){ + // no page? must not be vorbis + System.err.println("Error reading initial header packet."); + System.exit(1); + } + + if(vi.synthesis_headerin(vc,op)<0){ + // error case; not a vorbis header + System.err.println("This Ogg bitstream does not contain Vorbis audio data."); + System.exit(1); + } + + // At this point, we're sure we're Vorbis. We've set up the logical + // (Ogg) bitstream decoder. Get the comment and codebook headers and + // set up the Vorbis decoder + + // The next two packets in order are the comment and codebook headers. + // They're likely large and may span multiple pages. Thus we reead + // and submit data until we get our two pacakets, watching that no + // pages are missing. If a page is missing, error out; losing a + // header page is the only place where missing data is fatal. */ + + int i=0; + while(i<2){ + while(i<2){ + + int result=oy.pageout(og); + if(result==0) break; // Need more data + // Don't complain about missing or corrupt data yet. We'll + // catch it at the packet output phase + + if(result==1){ + os.pagein(og); // we can ignore any errors here + // as they'll also become apparent + // at packetout + while(i<2){ + result=os.packetout(op); + if(result==0)break; + if(result==-1){ + // Uh oh; data at some point was corrupted or missing! + // We can't tolerate that in a header. Die. + System.err.println("Corrupt secondary header. Exiting."); + System.exit(1); + } + vi.synthesis_headerin(vc,op); + i++; + } + } + } + // no harm in not checking before adding more + index=oy.buffer(4096); + buffer=oy.data; + try{ + bytes=input.read(buffer, index, 4096); + } + catch(Exception e){ + System.err.println(e); + System.exit(1); + } + if(bytes==0 && i<2){ + System.err.println("End of file before finding all Vorbis headers!"); + System.exit(1); + } + oy.wrote(bytes); + } + + // Throw the comments plus a few lines about the bitstream we're + // decoding + { + byte[][] ptr=vc.user_comments; + for(int j=0; j<ptr.length;j++){ + if(ptr[j]==null) break; + System.err.println(new String(ptr[j], 0, ptr[j].length-1)); + } + System.err.println("\nBitstream is "+vi.channels+" channel, "+vi.rate+"Hz"); + System.err.println("Encoded by: "+new String(vc.vendor, 0, vc.vendor.length-1)+"\n"); + } + + convsize=4096/vi.channels; + + // OK, got and parsed all three headers. Initialize the Vorbis + // packet->PCM decoder. + vd.synthesis_init(vi); // central decode state + vb.init(vd); // local state for most of the decode + // so multiple block decodes can + // proceed in parallel. We could init + // multiple vorbis_block structures + // for vd here + + float[][][] _pcm=new float[1][][]; + int[] _index=new int[vi.channels]; + // The rest is just a straight decode loop until end of stream + while(eos==0){ + while(eos==0){ + + int result=oy.pageout(og); + if(result==0)break; // need more data + if(result==-1){ // missing or corrupt data at this page position + System.err.println("Corrupt or missing data in bitstream; continuing..."); + } + else{ + os.pagein(og); // can safely ignore errors at + // this point + while(true){ + result=os.packetout(op); + + if(result==0)break; // need more data + if(result==-1){ // missing or corrupt data at this page position + // no reason to complain; already complained above + } + else{ + // we have a packet. Decode it + int samples; + if(vb.synthesis(op)==0){ // test for success! + vd.synthesis_blockin(vb); + } + + // **pcm is a multichannel float vector. In stereo, for + // example, pcm[0] is left, and pcm[1] is right. samples is + // the size of each channel. Convert the float values + // (-1.<=range<=1.) to whatever PCM format and write it out + + while((samples=vd.synthesis_pcmout(_pcm, _index))>0){ + float[][] pcm=_pcm[0]; + boolean clipflag=false; + int bout=(samples<convsize?samples:convsize); + + // convert floats to 16 bit signed ints (host order) and + // interleave + for(i=0;i<vi.channels;i++){ + int ptr=i*2; + //int ptr=i; + int mono=_index[i]; + for(int j=0;j<bout;j++){ + int val=(int)(pcm[i][mono+j]*32767.); +// short val=(short)(pcm[i][mono+j]*32767.); +// int val=(int)Math.round(pcm[i][mono+j]*32767.); + // might as well guard against clipping + if(val>32767){ + val=32767; + clipflag=true; + } + if(val<-32768){ + val=-32768; + clipflag=true; + } + if(val<0) val=val|0x8000; + convbuffer[ptr]=(byte)(val); + convbuffer[ptr+1]=(byte)(val>>>8); + ptr+=2*(vi.channels); + } + } + + //if(clipflag) + // System.err.println("Clipping in frame "+vd.sequence); + + System.out.write(convbuffer, 0, 2*vi.channels*bout); + + vd.synthesis_read(bout); // tell libvorbis how + // many samples we + // actually consumed + } + } + } + if(og.eos()!=0)eos=1; + } + } + if(eos==0){ + index=oy.buffer(4096); + buffer=oy.data; + try{ + bytes=input.read(buffer,index,4096); + } + catch(Exception e){ + System.err.println(e); + System.exit(1); + } + oy.wrote(bytes); + if(bytes==0)eos=1; + } + } + + // clean up this logical bitstream; before exit we see if we're + // followed by another [chained] + + os.clear(); + + // ogg_page and ogg_packet structs always point to storage in + // libvorbis. They're never freed or manipulated directly + + vb.clear(); + vd.clear(); + vi.clear(); // must be called last + } + + // OK, clean up the framer + oy.clear(); + System.err.println("Done."); + } +} + diff --git a/songdbj/com/jcraft/jorbis/Drft.java b/songdbj/com/jcraft/jorbis/Drft.java new file mode 100644 index 0000000000..c7ff2032e7 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Drft.java @@ -0,0 +1,1317 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class Drft{ + int n; + float[] trigcache; + int[] splitcache; + + void backward(float[] data){ + //System.err.println("Drft.backward"); + if(n==1)return; + drftb1(n,data,trigcache,trigcache,n,splitcache); + } + + void init(int n){ + //System.err.println("Drft.init"); + this.n=n; + trigcache=new float[3*n]; + splitcache=new int[32]; + fdrffti(n, trigcache, splitcache); + } + + void clear(){ + //System.err.println("Drft.clear"); + if(trigcache!=null)trigcache=null; + if(splitcache!=null)splitcache=null; +// memset(l,0,sizeof(drft_lookup)); + } + + static int[] ntryh = { 4,2,3,5 }; + static float tpi = 6.28318530717958647692528676655900577f; + static float hsqt2 = .70710678118654752440084436210485f; + static float taui = .86602540378443864676372317075293618f; + static float taur = -.5f; + static float sqrt2 = 1.4142135623730950488016887242097f; + + static void drfti1(int n, float[] wa, int index, int[] ifac){ + float arg,argh,argld,fi; + int ntry=0,i,j=-1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl=n; + int nf=0; + + int state=101; + + loop: while(true){ + switch(state){ + case 101: + j++; + if (j < 4) + ntry=ntryh[j]; + else + ntry+=2; + case 104: + nq=nl/ntry; + nr=nl-ntry*nq; + if(nr!=0){ + state=101; + break; + } + nf++; + ifac[nf+1]=ntry; + nl=nq; + if(ntry!=2){ + state=107; + break; + } + if(nf==1){ + state=107; + break; + } + + for(i=1;i<nf;i++){ + ib=nf-i+1; + ifac[ib+1]=ifac[ib]; + } + ifac[2] = 2; + case 107: + if(nl!=1){ + state=104; + break; + } + ifac[0]=n; + ifac[1]=nf; + argh=tpi/n; + is=0; + nfm1=nf-1; + l1=1; + + if(nfm1==0)return; + + for (k1=0;k1<nfm1;k1++){ + ip=ifac[k1+2]; + ld=0; + l2=l1*ip; + ido=n/l2; + ipm=ip-1; + + for (j=0;j<ipm;j++){ + ld+=l1; + i=is; + argld=(float)ld*argh; + fi=0.f; + for (ii=2;ii<ido;ii+=2){ + fi+=1.f; + arg=fi*argld; + wa[index+i++]=(float)Math.cos(arg); + wa[index+i++]=(float)Math.sin(arg); + } + is+=ido; + } + l1=l2; + } + break loop; + } + } + } + + static void fdrffti(int n, float[] wsave, int[] ifac){ +//System.err.println("fdrffti: n="+n); + if(n == 1) return; + drfti1(n, wsave, n, ifac); + } + + static void dradf2(int ido,int l1,float[] cc, float[] ch, float[] wa1, int index){ + int i,k; + float ti2,tr2; + int t0,t1,t2,t3,t4,t5,t6; + + t1=0; + t0=(t2=l1*ido); + t3=ido<<1; + for(k=0;k<l1;k++){ + ch[t1<<1]=cc[t1]+cc[t2]; + ch[(t1<<1)+t3-1]=cc[t1]-cc[t2]; + t1+=ido; + t2+=ido; + } + + if(ido<2)return; + + if(ido!=2){ + t1=0; + t2=t0; + for(k=0;k<l1;k++){ + t3=t2; + t4=(t1<<1)+(ido<<1); + t5=t1; + t6=t1+t1; + for(i=2;i<ido;i+=2){ + t3+=2; + t4-=2; + t5+=2; + t6+=2; + tr2=wa1[index+i-2]*cc[t3-1]+wa1[index+i-1]*cc[t3]; + ti2=wa1[index+i-2]*cc[t3]-wa1[index+i-1]*cc[t3-1]; + ch[t6]=cc[t5]+ti2; + ch[t4]=ti2-cc[t5]; + ch[t6-1]=cc[t5-1]+tr2; + ch[t4-1]=cc[t5-1]-tr2; + } + t1+=ido; + t2+=ido; + } + if(ido%2==1)return; + } + + t3=(t2=(t1=ido)-1); + t2+=t0; + for(k=0;k<l1;k++){ + ch[t1]=-cc[t2]; + ch[t1-1]=cc[t3]; + t1+=ido<<1; + t2+=ido; + t3+=ido; + } + } + + static void dradf4(int ido,int l1,float[] cc, float[] ch, + float[] wa1, int index1, + float[] wa2, int index2, + float[] wa3, int index3){ + int i,k,t0,t1,t2,t3,t4,t5,t6; + float ci2,ci3,ci4,cr2,cr3,cr4,ti1,ti2,ti3,ti4,tr1,tr2,tr3,tr4; + t0=l1*ido; + + t1=t0; + t4=t1<<1; + t2=t1+(t1<<1); + t3=0; + + for(k=0;k<l1;k++){ + tr1=cc[t1]+cc[t2]; + tr2=cc[t3]+cc[t4]; + + ch[t5=t3<<2]=tr1+tr2; + ch[(ido<<2)+t5-1]=tr2-tr1; + ch[(t5+=(ido<<1))-1]=cc[t3]-cc[t4]; + ch[t5]=cc[t2]-cc[t1]; + + t1+=ido; + t2+=ido; + t3+=ido; + t4+=ido; + } + if(ido<2)return; + + if(ido!=2){ + t1=0; + for(k=0;k<l1;k++){ + t2=t1; + t4=t1<<2; + t5=(t6=ido<<1)+t4; + for(i=2;i<ido;i+=2){ + t3=(t2+=2); + t4+=2; + t5-=2; + + t3+=t0; + cr2=wa1[index1+i-2]*cc[t3-1]+wa1[index1+i-1]*cc[t3]; + ci2=wa1[index1+i-2]*cc[t3]-wa1[index1+i-1]*cc[t3-1]; + t3+=t0; + cr3=wa2[index2+i-2]*cc[t3-1]+wa2[index2+i-1]*cc[t3]; + ci3=wa2[index2+i-2]*cc[t3]-wa2[index2+i-1]*cc[t3-1]; + t3+=t0; + cr4=wa3[index3+i-2]*cc[t3-1]+wa3[index3+i-1]*cc[t3]; + ci4=wa3[index3+i-2]*cc[t3]-wa3[index3+i-1]*cc[t3-1]; + + tr1=cr2+cr4; + tr4=cr4-cr2; + ti1=ci2+ci4; + ti4=ci2-ci4; + + ti2=cc[t2]+ci3; + ti3=cc[t2]-ci3; + tr2=cc[t2-1]+cr3; + tr3=cc[t2-1]-cr3; + + ch[t4-1]=tr1+tr2; + ch[t4]=ti1+ti2; + + ch[t5-1]=tr3-ti4; + ch[t5]=tr4-ti3; + + ch[t4+t6-1]=ti4+tr3; + ch[t4+t6]=tr4+ti3; + + ch[t5+t6-1]=tr2-tr1; + ch[t5+t6]=ti1-ti2; + } + t1+=ido; + } + if((ido&1)!=0)return; + } + + t2=(t1=t0+ido-1)+(t0<<1); + t3=ido<<2; + t4=ido; + t5=ido<<1; + t6=ido; + + for(k=0;k<l1;k++){ + ti1=-hsqt2*(cc[t1]+cc[t2]); + tr1=hsqt2*(cc[t1]-cc[t2]); + + ch[t4-1]=tr1+cc[t6-1]; + ch[t4+t5-1]=cc[t6-1]-tr1; + + ch[t4]=ti1-cc[t1+t0]; + ch[t4+t5]=ti1+cc[t1+t0]; + + t1+=ido; + t2+=ido; + t4+=t3; + t6+=ido; + } + } + + static void dradfg(int ido,int ip,int l1,int idl1,float[] cc,float[] c1, + float[] c2, float[] ch, float[] ch2, float[] wa, int index){ + int idij,ipph,i,j,k,l,ic,ik,is; + int t0,t1,t2=0,t3,t4,t5,t6,t7,t8,t9,t10; + float dc2,ai1,ai2,ar1,ar2,ds2; + int nbd; + float dcp=0,arg,dsp=0,ar1h,ar2h; + int idp2,ipp2; + + arg=tpi/(float)ip; + dcp=(float)Math.cos(arg); + dsp=(float)Math.sin(arg); + ipph=(ip+1)>>1; + ipp2=ip; + idp2=ido; + nbd=(ido-1)>>1; + t0=l1*ido; + t10=ip*ido; + + int state=100; + loop: while(true){ + switch(state){ + case 101: + if(ido==1){ + state=119; + break; + } + for(ik=0;ik<idl1;ik++)ch2[ik]=c2[ik]; + + t1=0; + for(j=1;j<ip;j++){ + t1+=t0; + t2=t1; + for(k=0;k<l1;k++){ + ch[t2]=c1[t2]; + t2+=ido; + } + } + + is=-ido; + t1=0; + if(nbd>l1){ + for(j=1;j<ip;j++){ + t1+=t0; + is+=ido; + t2= -ido+t1; + for(k=0;k<l1;k++){ + idij=is-1; + t2+=ido; + t3=t2; + for(i=2;i<ido;i+=2){ + idij+=2; + t3+=2; + ch[t3-1]=wa[index+idij-1]*c1[t3-1]+wa[index+idij]*c1[t3]; + ch[t3]=wa[index+idij-1]*c1[t3]-wa[index+idij]*c1[t3-1]; + } + } + } + } + else{ + + for(j=1;j<ip;j++){ + is+=ido; + idij=is-1; + t1+=t0; + t2=t1; + for(i=2;i<ido;i+=2){ + idij+=2; + t2+=2; + t3=t2; + for(k=0;k<l1;k++){ + ch[t3-1]=wa[index+idij-1]*c1[t3-1]+wa[index+idij]*c1[t3]; + ch[t3]=wa[index+idij-1]*c1[t3]-wa[index+idij]*c1[t3-1]; + t3+=ido; + } + } + } + } + + t1=0; + t2=ipp2*t0; + if(nbd<l1){ + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + for(i=2;i<ido;i+=2){ + t3+=2; + t4+=2; + t5=t3-ido; + t6=t4-ido; + for(k=0;k<l1;k++){ + t5+=ido; + t6+=ido; + c1[t5-1]=ch[t5-1]+ch[t6-1]; + c1[t6-1]=ch[t5]-ch[t6]; + c1[t5]=ch[t5]+ch[t6]; + c1[t6]=ch[t6-1]-ch[t5-1]; + } + } + } + } + else{ + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + for(k=0;k<l1;k++){ + t5=t3; + t6=t4; + for(i=2;i<ido;i+=2){ + t5+=2; + t6+=2; + c1[t5-1]=ch[t5-1]+ch[t6-1]; + c1[t6-1]=ch[t5]-ch[t6]; + c1[t5]=ch[t5]+ch[t6]; + c1[t6]=ch[t6-1]-ch[t5-1]; + } + t3+=ido; + t4+=ido; + } + } + } + case 119: + for(ik=0;ik<idl1;ik++)c2[ik]=ch2[ik]; + + t1=0; + t2=ipp2*idl1; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1-ido; + t4=t2-ido; + for(k=0;k<l1;k++){ + t3+=ido; + t4+=ido; + c1[t3]=ch[t3]+ch[t4]; + c1[t4]=ch[t4]-ch[t3]; + } + } + + ar1=1.f; + ai1=0.f; + t1=0; + t2=ipp2*idl1; + t3=(ip-1)*idl1; + for(l=1;l<ipph;l++){ + t1+=idl1; + t2-=idl1; + ar1h=dcp*ar1-dsp*ai1; + ai1=dcp*ai1+dsp*ar1; + ar1=ar1h; + t4=t1; + t5=t2; + t6=t3; + t7=idl1; + + for(ik=0;ik<idl1;ik++){ + ch2[t4++]=c2[ik]+ar1*c2[t7++]; + ch2[t5++]=ai1*c2[t6++]; + } + + dc2=ar1; + ds2=ai1; + ar2=ar1; + ai2=ai1; + + t4=idl1; + t5=(ipp2-1)*idl1; + for(j=2;j<ipph;j++){ + t4+=idl1; + t5-=idl1; + + ar2h=dc2*ar2-ds2*ai2; + ai2=dc2*ai2+ds2*ar2; + ar2=ar2h; + + t6=t1; + t7=t2; + t8=t4; + t9=t5; + for(ik=0;ik<idl1;ik++){ + ch2[t6++]+=ar2*c2[t8++]; + ch2[t7++]+=ai2*c2[t9++]; + } + } + } + t1=0; + for(j=1;j<ipph;j++){ + t1+=idl1; + t2=t1; + for(ik=0;ik<idl1;ik++)ch2[ik]+=c2[t2++]; + } + + if(ido<l1){ + state=132; + break; + } + + t1=0; + t2=0; + for(k=0;k<l1;k++){ + t3=t1; + t4=t2; + for(i=0;i<ido;i++)cc[t4++]=ch[t3++]; + t1+=ido; + t2+=t10; + } + state=135; + break; + + case 132: + for(i=0;i<ido;i++){ + t1=i; + t2=i; + for(k=0;k<l1;k++){ + cc[t2]=ch[t1]; + t1+=ido; + t2+=t10; + } + } + case 135: + t1=0; + t2=ido<<1; + t3=0; + t4=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t2; + t3+=t0; + t4-=t0; + + t5=t1; + t6=t3; + t7=t4; + + for(k=0;k<l1;k++){ + cc[t5-1]=ch[t6]; + cc[t5]=ch[t7]; + t5+=t10; + t6+=ido; + t7+=ido; + } + } + + if(ido==1)return; + if(nbd<l1){ + state=141; + break; + } + + t1=-ido; + t3=0; + t4=0; + t5=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t2; + t3+=t2; + t4+=t0; + t5-=t0; + t6=t1; + t7=t3; + t8=t4; + t9=t5; + for(k=0;k<l1;k++){ + for(i=2;i<ido;i+=2){ + ic=idp2-i; + cc[i+t7-1]=ch[i+t8-1]+ch[i+t9-1]; + cc[ic+t6-1]=ch[i+t8-1]-ch[i+t9-1]; + cc[i+t7]=ch[i+t8]+ch[i+t9]; + cc[ic+t6]=ch[i+t9]-ch[i+t8]; + } + t6+=t10; + t7+=t10; + t8+=ido; + t9+=ido; + } + } + return; + case 141: + t1=-ido; + t3=0; + t4=0; + t5=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t2; + t3+=t2; + t4+=t0; + t5-=t0; + for(i=2;i<ido;i+=2){ + t6=idp2+t1-i; + t7=i+t3; + t8=i+t4; + t9=i+t5; + for(k=0;k<l1;k++){ + cc[t7-1]=ch[t8-1]+ch[t9-1]; + cc[t6-1]=ch[t8-1]-ch[t9-1]; + cc[t7]=ch[t8]+ch[t9]; + cc[t6]=ch[t9]-ch[t8]; + t6+=t10; + t7+=t10; + t8+=ido; + t9+=ido; + } + } + } + break loop; + } + } + } + + static void drftf1(int n,float[] c, float[] ch, float[] wa, int[] ifac){ + int i,k1,l1,l2; + int na,kh,nf; + int ip,iw,ido,idl1,ix2,ix3; + + nf=ifac[1]; + na=1; + l2=n; + iw=n; + + for(k1=0;k1<nf;k1++){ + kh=nf-k1; + ip=ifac[kh+1]; + l1=l2/ip; + ido=n/l2; + idl1=ido*l1; + iw-=(ip-1)*ido; + na=1-na; + + int state=100; + loop: while(true){ + switch(state){ + case 100: + if(ip!=4){ + state=102; + break; + } + + ix2=iw+ido; + ix3=ix2+ido; + if(na!=0) + dradf4(ido,l1,ch,c,wa,iw-1,wa,ix2-1,wa,ix3-1); + else + dradf4(ido,l1,c,ch,wa,iw-1,wa,ix2-1,wa,ix3-1); + state=110; + break; + case 102: + if(ip!=2){ + state=104; + break; + } + if(na!=0){ + state=103; + break; + } + dradf2(ido,l1,c,ch,wa, iw-1); + state=110; + break; + case 103: + dradf2(ido,l1,ch,c,wa, iw-1); + case 104: + if(ido==1)na=1-na; + if(na!=0){ + state=109; + break; + } + dradfg(ido,ip,l1,idl1,c,c,c,ch,ch,wa,iw-1); + na=1; + state=110; + break; + case 109: + dradfg(ido,ip,l1,idl1,ch,ch,ch,c,c,wa,iw-1); + na=0; + case 110: + l2=l1; + break loop; + } + } + } + if(na==1)return; + for(i=0;i<n;i++)c[i]=ch[i]; + } + + static void dradb2(int ido,int l1,float[] cc,float[] ch,float[] wa1, int index){ + int i,k,t0,t1,t2,t3,t4,t5,t6; + float ti2,tr2; + + t0=l1*ido; + + t1=0; + t2=0; + t3=(ido<<1)-1; + for(k=0;k<l1;k++){ + ch[t1]=cc[t2]+cc[t3+t2]; + ch[t1+t0]=cc[t2]-cc[t3+t2]; + t2=(t1+=ido)<<1; + } + + if(ido<2)return; + if(ido!=2){ + t1=0; + t2=0; + for(k=0;k<l1;k++){ + t3=t1; + t5=(t4=t2)+(ido<<1); + t6=t0+t1; + for(i=2;i<ido;i+=2){ + t3+=2; + t4+=2; + t5-=2; + t6+=2; + ch[t3-1]=cc[t4-1]+cc[t5-1]; + tr2=cc[t4-1]-cc[t5-1]; + ch[t3]=cc[t4]-cc[t5]; + ti2=cc[t4]+cc[t5]; + ch[t6-1]=wa1[index+i-2]*tr2-wa1[index+i-1]*ti2; + ch[t6]=wa1[index+i-2]*ti2+wa1[index+i-1]*tr2; + } + t2=(t1+=ido)<<1; + } + if((ido%2)==1)return; + } + + t1=ido-1; + t2=ido-1; + for(k=0;k<l1;k++){ + ch[t1]=cc[t2]+cc[t2]; + ch[t1+t0]=-(cc[t2+1]+cc[t2+1]); + t1+=ido; + t2+=ido<<1; + } + } + + static void dradb3(int ido,int l1,float[] cc,float[] ch, + float[] wa1, int index1, + float[] wa2, int index2){ + int i,k,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10; + float ci2,ci3,di2,di3,cr2,cr3,dr2,dr3,ti2,tr2; + t0=l1*ido; + + t1=0; + t2=t0<<1; + t3=ido<<1; + t4=ido+(ido<<1); + t5=0; + for(k=0;k<l1;k++){ + tr2=cc[t3-1]+cc[t3-1]; + cr2=cc[t5]+(taur*tr2); + ch[t1]=cc[t5]+tr2; + ci3=taui*(cc[t3]+cc[t3]); + ch[t1+t0]=cr2-ci3; + ch[t1+t2]=cr2+ci3; + t1+=ido; + t3+=t4; + t5+=t4; + } + + if(ido==1)return; + + t1=0; + t3=ido<<1; + for(k=0;k<l1;k++){ + t7=t1+(t1<<1); + t6=(t5=t7+t3); + t8=t1; + t10=(t9=t1+t0)+t0; + + for(i=2;i<ido;i+=2){ + t5+=2; + t6-=2; + t7+=2; + t8+=2; + t9+=2; + t10+=2; + tr2=cc[t5-1]+cc[t6-1]; + cr2=cc[t7-1]+(taur*tr2); + ch[t8-1]=cc[t7-1]+tr2; + ti2=cc[t5]-cc[t6]; + ci2=cc[t7]+(taur*ti2); + ch[t8]=cc[t7]+ti2; + cr3=taui*(cc[t5-1]-cc[t6-1]); + ci3=taui*(cc[t5]+cc[t6]); + dr2=cr2-ci3; + dr3=cr2+ci3; + di2=ci2+cr3; + di3=ci2-cr3; + ch[t9-1]=wa1[index1+i-2]*dr2-wa1[index1+i-1]*di2; + ch[t9]=wa1[index1+i-2]*di2+wa1[index1+i-1]*dr2; + ch[t10-1]=wa2[index2+i-2]*dr3-wa2[index2+i-1]*di3; + ch[t10]=wa2[index2+i-2]*di3+wa2[index2+i-1]*dr3; + } + t1+=ido; + } + } + + static void dradb4(int ido,int l1,float[] cc,float[] ch, + float[] wa1, int index1, + float[] wa2, int index2, + float[] wa3, int index3){ + int i,k,t0,t1,t2,t3,t4,t5,t6,t7,t8; + float ci2,ci3,ci4,cr2,cr3,cr4,ti1,ti2,ti3,ti4,tr1,tr2,tr3,tr4; + t0=l1*ido; + + t1=0; + t2=ido<<2; + t3=0; + t6=ido<<1; + for(k=0;k<l1;k++){ + t4=t3+t6; + t5=t1; + tr3=cc[t4-1]+cc[t4-1]; + tr4=cc[t4]+cc[t4]; + tr1=cc[t3]-cc[(t4+=t6)-1]; + tr2=cc[t3]+cc[t4-1]; + ch[t5]=tr2+tr3; + ch[t5+=t0]=tr1-tr4; + ch[t5+=t0]=tr2-tr3; + ch[t5+=t0]=tr1+tr4; + t1+=ido; + t3+=t2; + } + + if(ido<2)return; + if(ido!=2){ + t1=0; + for(k=0;k<l1;k++){ + t5=(t4=(t3=(t2=t1<<2)+t6))+t6; + t7=t1; + for(i=2;i<ido;i+=2){ + t2+=2; + t3+=2; + t4-=2; + t5-=2; + t7+=2; + ti1=cc[t2]+cc[t5]; + ti2=cc[t2]-cc[t5]; + ti3=cc[t3]-cc[t4]; + tr4=cc[t3]+cc[t4]; + tr1=cc[t2-1]-cc[t5-1]; + tr2=cc[t2-1]+cc[t5-1]; + ti4=cc[t3-1]-cc[t4-1]; + tr3=cc[t3-1]+cc[t4-1]; + ch[t7-1]=tr2+tr3; + cr3=tr2-tr3; + ch[t7]=ti2+ti3; + ci3=ti2-ti3; + cr2=tr1-tr4; + cr4=tr1+tr4; + ci2=ti1+ti4; + ci4=ti1-ti4; + + ch[(t8=t7+t0)-1]=wa1[index1+i-2]*cr2-wa1[index1+i-1]*ci2; + ch[t8]=wa1[index1+i-2]*ci2+wa1[index1+i-1]*cr2; + ch[(t8+=t0)-1]=wa2[index2+i-2]*cr3-wa2[index2+i-1]*ci3; + ch[t8]=wa2[index2+i-2]*ci3+wa2[index2+i-1]*cr3; + ch[(t8+=t0)-1]=wa3[index3+i-2]*cr4-wa3[index3+i-1]*ci4; + ch[t8]=wa3[index3+i-2]*ci4+wa3[index3+i-1]*cr4; + } + t1+=ido; + } + if(ido%2 == 1)return; + } + + t1=ido; + t2=ido<<2; + t3=ido-1; + t4=ido+(ido<<1); + for(k=0;k<l1;k++){ + t5=t3; + ti1=cc[t1]+cc[t4]; + ti2=cc[t4]-cc[t1]; + tr1=cc[t1-1]-cc[t4-1]; + tr2=cc[t1-1]+cc[t4-1]; + ch[t5]=tr2+tr2; + ch[t5+=t0]=sqrt2*(tr1-ti1); + ch[t5+=t0]=ti2+ti2; + ch[t5+=t0]=-sqrt2*(tr1+ti1); + + t3+=ido; + t1+=t2; + t4+=t2; + } + } + + static void dradbg(int ido,int ip,int l1,int idl1,float[] cc,float[] c1, + float[] c2,float[] ch,float[] ch2,float[] wa, int index ){ + + int idij,ipph=0,i,j,k,l,ik,is,t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10=0, + t11,t12; + float dc2,ai1,ai2,ar1,ar2,ds2; + int nbd=0; + float dcp=0,arg,dsp=0,ar1h,ar2h; + int ipp2=0; + + int state=100; + + loop: while(true){ + switch(state){ + case 100: + t10=ip*ido; + t0=l1*ido; + arg=tpi/(float)ip; + dcp=(float)Math.cos(arg); + dsp=(float)Math.sin(arg); + nbd=(ido-1)>>>1; + ipp2=ip; + ipph=(ip+1)>>>1; + if(ido<l1){ + state=103; + break; + } + t1=0; + t2=0; + for(k=0;k<l1;k++){ + t3=t1; + t4=t2; + for(i=0;i<ido;i++){ + ch[t3]=cc[t4]; + t3++; + t4++; + } + t1+=ido; + t2+=t10; + } + state=106; + break; + case 103: + t1=0; + for(i=0;i<ido;i++){ + t2=t1; + t3=t1; + for(k=0;k<l1;k++){ + ch[t2]=cc[t3]; + t2+=ido; + t3+=t10; + } + t1++; + } + case 106: + t1=0; + t2=ipp2*t0; + t7=(t5=ido<<1); + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + t6=t5; + for(k=0;k<l1;k++){ + ch[t3]=cc[t6-1]+cc[t6-1]; + ch[t4]=cc[t6]+cc[t6]; + t3+=ido; + t4+=ido; + t6+=t10; + } + t5+=t7; + } + if (ido == 1){ + state=116; + break; + } + if(nbd<l1){ + state=112; + break; + } + + t1=0; + t2=ipp2*t0; + t7=0; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + + t7+=(ido<<1); + t8=t7; + for(k=0;k<l1;k++){ + t5=t3; + t6=t4; + t9=t8; + t11=t8; + for(i=2;i<ido;i+=2){ + t5+=2; + t6+=2; + t9+=2; + t11-=2; + ch[t5-1]=cc[t9-1]+cc[t11-1]; + ch[t6-1]=cc[t9-1]-cc[t11-1]; + ch[t5]=cc[t9]-cc[t11]; + ch[t6]=cc[t9]+cc[t11]; + } + t3+=ido; + t4+=ido; + t8+=t10; + } + } + state=116; + break; + case 112: + t1=0; + t2=ipp2*t0; + t7=0; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + t7+=(ido<<1); + t8=t7; + t9=t7; + for(i=2;i<ido;i+=2){ + t3+=2; + t4+=2; + t8+=2; + t9-=2; + t5=t3; + t6=t4; + t11=t8; + t12=t9; + for(k=0;k<l1;k++){ + ch[t5-1]=cc[t11-1]+cc[t12-1]; + ch[t6-1]=cc[t11-1]-cc[t12-1]; + ch[t5]=cc[t11]-cc[t12]; + ch[t6]=cc[t11]+cc[t12]; + t5+=ido; + t6+=ido; + t11+=t10; + t12+=t10; + } + } + } + case 116: + ar1=1.f; + ai1=0.f; + t1=0; + t9=(t2=ipp2*idl1); + t3=(ip-1)*idl1; + for(l=1;l<ipph;l++){ + t1+=idl1; + t2-=idl1; + + ar1h=dcp*ar1-dsp*ai1; + ai1=dcp*ai1+dsp*ar1; + ar1=ar1h; + t4=t1; + t5=t2; + t6=0; + t7=idl1; + t8=t3; + for(ik=0;ik<idl1;ik++){ + c2[t4++]=ch2[t6++]+ar1*ch2[t7++]; + c2[t5++]=ai1*ch2[t8++]; + } + dc2=ar1; + ds2=ai1; + ar2=ar1; + ai2=ai1; + + t6=idl1; + t7=t9-idl1; + for(j=2;j<ipph;j++){ + t6+=idl1; + t7-=idl1; + ar2h=dc2*ar2-ds2*ai2; + ai2=dc2*ai2+ds2*ar2; + ar2=ar2h; + t4=t1; + t5=t2; + t11=t6; + t12=t7; + for(ik=0;ik<idl1;ik++){ + c2[t4++]+=ar2*ch2[t11++]; + c2[t5++]+=ai2*ch2[t12++]; + } + } + } + + t1=0; + for(j=1;j<ipph;j++){ + t1+=idl1; + t2=t1; + for(ik=0;ik<idl1;ik++)ch2[ik]+=ch2[t2++]; + } + + t1=0; + t2=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + for(k=0;k<l1;k++){ + ch[t3]=c1[t3]-c1[t4]; + ch[t4]=c1[t3]+c1[t4]; + t3+=ido; + t4+=ido; + } + } + + if(ido==1){ + state=132; + break; + } + if(nbd<l1){ + state=128; + break; + } + + t1=0; + t2=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + for(k=0;k<l1;k++){ + t5=t3; + t6=t4; + for(i=2;i<ido;i+=2){ + t5+=2; + t6+=2; + ch[t5-1]=c1[t5-1]-c1[t6]; + ch[t6-1]=c1[t5-1]+c1[t6]; + ch[t5]=c1[t5]+c1[t6-1]; + ch[t6]=c1[t5]-c1[t6-1]; + } + t3+=ido; + t4+=ido; + } + } + state=132; + break; + case 128: + t1=0; + t2=ipp2*t0; + for(j=1;j<ipph;j++){ + t1+=t0; + t2-=t0; + t3=t1; + t4=t2; + for(i=2;i<ido;i+=2){ + t3+=2; + t4+=2; + t5=t3; + t6=t4; + for(k=0;k<l1;k++){ + ch[t5-1]=c1[t5-1]-c1[t6]; + ch[t6-1]=c1[t5-1]+c1[t6]; + ch[t5]=c1[t5]+c1[t6-1]; + ch[t6]=c1[t5]-c1[t6-1]; + t5+=ido; + t6+=ido; + } + } + } + case 132: + if(ido==1)return; + + for(ik=0;ik<idl1;ik++)c2[ik]=ch2[ik]; + + t1=0; + for(j=1;j<ip;j++){ + t2=(t1+=t0); + for(k=0;k<l1;k++){ + c1[t2]=ch[t2]; + t2+=ido; + } + } + + if(nbd>l1){ + state=139; + break; + } + + is= -ido-1; + t1=0; + for(j=1;j<ip;j++){ + is+=ido; + t1+=t0; + idij=is; + t2=t1; + for(i=2;i<ido;i+=2){ + t2+=2; + idij+=2; + t3=t2; + for(k=0;k<l1;k++){ + c1[t3-1]=wa[index+idij-1]*ch[t3-1]-wa[index+idij]*ch[t3]; + c1[t3]=wa[index+idij-1]*ch[t3]+wa[index+idij]*ch[t3-1]; + t3+=ido; + } + } + } + return; + + case 139: + is= -ido-1; + t1=0; + for(j=1;j<ip;j++){ + is+=ido; + t1+=t0; + t2=t1; + for(k=0;k<l1;k++){ + idij=is; + t3=t2; + for(i=2;i<ido;i+=2){ + idij+=2; + t3+=2; + c1[t3-1]=wa[index+idij-1]*ch[t3-1]-wa[index+idij]*ch[t3]; + c1[t3]=wa[index+idij-1]*ch[t3]+wa[index+idij]*ch[t3-1]; + } + t2+=ido; + } + } + break loop; + } + } + } + + static void drftb1(int n, float[] c, float[] ch, float[] wa, int index, int[] ifac){ + int i,k1,l1,l2=0; + int na; + int nf,ip=0,iw,ix2,ix3,ido=0,idl1=0; + + nf=ifac[1]; + na=0; + l1=1; + iw=1; + + for(k1=0;k1<nf;k1++){ + int state=100; + loop: while(true){ + switch(state){ + case 100: + ip=ifac[k1 + 2]; + l2=ip*l1; + ido=n/l2; + idl1=ido*l1; + if(ip!=4){ + state=103; + break; + } + ix2=iw+ido; + ix3=ix2+ido; + + if(na!=0) + dradb4(ido,l1,ch,c,wa,index+iw-1,wa,index+ix2-1,wa,index+ix3-1); + else + dradb4(ido,l1,c,ch,wa,index+iw-1,wa,index+ix2-1,wa,index+ix3-1); + na=1-na; + state=115; + break; + case 103: + if(ip!=2){ + state=106; + break; + } + + if(na!=0) + dradb2(ido,l1,ch,c,wa,index+iw-1); + else + dradb2(ido,l1,c,ch,wa,index+iw-1); + na=1-na; + state=115; + break; + + case 106: + if(ip!=3){ + state=109; + break; + } + + ix2=iw+ido; + if(na!=0) + dradb3(ido,l1,ch,c,wa,index+iw-1,wa,index+ix2-1); + else + dradb3(ido,l1,c,ch,wa,index+iw-1,wa,index+ix2-1); + na=1-na; + state=115; + break; + case 109: + // The radix five case can be translated later..... + // if(ip!=5)goto L112; + // + //ix2=iw+ido; + //ix3=ix2+ido; + //ix4=ix3+ido; + //if(na!=0) + // dradb5(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1); + //else + // dradb5(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1); + //na=1-na; + //state=115; + //break; + if(na!=0) + dradbg(ido,ip,l1,idl1,ch,ch,ch,c,c,wa,index+iw-1); + else + dradbg(ido,ip,l1,idl1,c,c,c,ch,ch,wa,index+iw-1); + if(ido==1)na=1-na; + + case 115: + l1=l2; + iw+=(ip-1)*ido; + break loop; + } + } + } + if(na==0)return; + for(i=0;i<n;i++)c[i]=ch[i]; + } +} diff --git a/songdbj/com/jcraft/jorbis/DspState.java b/songdbj/com/jcraft/jorbis/DspState.java new file mode 100644 index 0000000000..5676f640c1 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/DspState.java @@ -0,0 +1,459 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +public class DspState{ + static final float M_PI=3.1415926539f; + static final int VI_TRANSFORMB=1; + static final int VI_WINDOWB=1; + + int analysisp; + Info vi; + int modebits; + + float[][] pcm; + //float[][] pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + float[] multipliers; + int envelope_storage; + int envelope_current; + + int eofflag; + + int lW; + int W; + int nW; + int centerW; + + long granulepos; + long sequence; + + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + // local lookup storage +//!! Envelope ve=new Envelope(); // envelope +//float **window[2][2][2]; // block, leadin, leadout, type + float[][][][][] window; // block, leadin, leadout, type + //vorbis_look_transform **transform[2]; // block, type + Object[][] transform; + CodeBook[] fullbooks; + // backend lookups are tied to the mode, not the backend or naked mapping + Object[] mode; + + // local storage, only used on the encoding side. This way the + // application does not need to worry about freeing some packets' + // memory and not others'; packet storage is always tracked. + // Cleared next call to a _dsp_ function + byte[] header; + byte[] header1; + byte[] header2; + + public DspState(){ + transform=new Object[2][]; + window=new float[2][][][][]; + window[0]=new float[2][][][]; + window[0][0]=new float[2][][]; + window[0][1]=new float[2][][]; + window[0][0][0]=new float[2][]; + window[0][0][1]=new float[2][]; + window[0][1][0]=new float[2][]; + window[0][1][1]=new float[2][]; + window[1]=new float[2][][][]; + window[1][0]=new float[2][][]; + window[1][1]=new float[2][][]; + window[1][0][0]=new float[2][]; + window[1][0][1]=new float[2][]; + window[1][1][0]=new float[2][]; + window[1][1][1]=new float[2][]; + } + + private static int ilog2(int v){ + int ret=0; + while(v>1){ + ret++; + v>>>=1; + } + return(ret); + } + + static float[] window(int type, int window, int left, int right){ + float[] ret=new float[window]; + switch(type){ + case 0: + // The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi) + { + int leftbegin=window/4-left/2; + int rightbegin=window-window/4-right/2; + + for(int i=0;i<left;i++){ + float x=(float)((i+.5)/left*M_PI/2.); + x=(float)Math.sin(x); + x*=x; + x*=M_PI/2.; + x=(float)Math.sin(x); + ret[i+leftbegin]=x; + } + + for(int i=leftbegin+left;i<rightbegin;i++){ + ret[i]=1.f; + } + + for(int i=0;i<right;i++){ + float x=(float)((right-i-.5)/right*M_PI/2.); + x=(float)Math.sin(x); + x*=x; + x*=M_PI/2.; + x=(float)Math.sin(x); + ret[i+rightbegin]=x; + } + } + break; + default: + //free(ret); + return(null); + } + return(ret); + } + + // Analysis side code, but directly related to blocking. Thus it's + // here and not in analysis.c (which is for analysis transforms only). + // The init is here because some of it is shared + + int init(Info vi, boolean encp){ +//System.err.println("DspState.init: vi="+vi+", encp="+encp); + //memset(v,0,sizeof(vorbis_dsp_state)); + this.vi=vi; + modebits=ilog2(vi.modes); + + transform[0]=new Object[VI_TRANSFORMB]; + transform[1]=new Object[VI_TRANSFORMB]; + + // MDCT is tranform 0 + + transform[0][0]=new Mdct(); + transform[1][0]=new Mdct(); + ((Mdct)transform[0][0]).init(vi.blocksizes[0]); + ((Mdct)transform[1][0]).init(vi.blocksizes[1]); + + window[0][0][0]=new float[VI_WINDOWB][]; + window[0][0][1]=window[0][0][0]; + window[0][1][0]=window[0][0][0]; + window[0][1][1]=window[0][0][0]; + window[1][0][0]=new float[VI_WINDOWB][]; + window[1][0][1]=new float[VI_WINDOWB][]; + window[1][1][0]=new float[VI_WINDOWB][]; + window[1][1][1]=new float[VI_WINDOWB][]; + + for(int i=0;i<VI_WINDOWB;i++){ + window[0][0][0][i]= + window(i,vi.blocksizes[0],vi.blocksizes[0]/2,vi.blocksizes[0]/2); + window[1][0][0][i]= + window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[0]/2); + window[1][0][1][i]= + window(i,vi.blocksizes[1],vi.blocksizes[0]/2,vi.blocksizes[1]/2); + window[1][1][0][i]= + window(i,vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[0]/2); + window[1][1][1][i]= + window(i,vi.blocksizes[1],vi.blocksizes[1]/2,vi.blocksizes[1]/2); + } + +// if(encp){ // encode/decode differ here +// // finish the codebooks +// fullbooks=new CodeBook[vi.books]; +// for(int i=0;i<vi.books;i++){ +// fullbooks[i]=new CodeBook(); +// fullbooks[i].init_encode(vi.book_param[i]); +// } +// analysisp=1; +// } +// else{ + // finish the codebooks + fullbooks=new CodeBook[vi.books]; + for(int i=0;i<vi.books;i++){ + fullbooks[i]=new CodeBook(); + fullbooks[i].init_decode(vi.book_param[i]); + } +// } + + // initialize the storage vectors to a decent size greater than the + // minimum + + pcm_storage=8192; // we'll assume later that we have + // a minimum of twice the blocksize of + // accumulated samples in analysis + pcm=new float[vi.channels][]; + //pcmret=new float[vi.channels][]; + { + for(int i=0;i<vi.channels;i++){ + pcm[i]=new float[pcm_storage]; + } + } + + // all 1 (large block) or 0 (small block) + // explicitly set for the sake of clarity + lW=0; // previous window size + W=0; // current window size + + // all vector indexes; multiples of samples_per_envelope_step + centerW=vi.blocksizes[1]/2; + + pcm_current=centerW; + + // initialize all the mapping/backend lookups + mode=new Object[vi.modes]; + for(int i=0;i<vi.modes;i++){ + int mapnum=vi.mode_param[i].mapping; + int maptype=vi.map_type[mapnum]; + mode[i]=FuncMapping.mapping_P[maptype].look(this,vi.mode_param[i], + vi.map_param[mapnum]); + } + return(0); + } + + public int synthesis_init(Info vi){ + init(vi, false); + // Adjust centerW to allow an easier mechanism for determining output + pcm_returned=centerW; + centerW-= vi.blocksizes[W]/4+vi.blocksizes[lW]/4; + granulepos=-1; + sequence=-1; + return(0); + } + + DspState(Info vi){ + this(); + init(vi, false); + // Adjust centerW to allow an easier mechanism for determining output + pcm_returned=centerW; + centerW-= vi.blocksizes[W]/4+vi.blocksizes[lW]/4; + granulepos=-1; + sequence=-1; + } + + // Unike in analysis, the window is only partially applied for each + // block. The time domain envelope is not yet handled at the point of + // calling (as it relies on the previous block). + + public int synthesis_blockin(Block vb){ + // Shift out any PCM/multipliers that we returned previously + // centerW is currently the center of the last block added + if(centerW>vi.blocksizes[1]/2 && pcm_returned>8192){ + // don't shift too much; we need to have a minimum PCM buffer of + // 1/2 long block + + int shiftPCM=centerW-vi.blocksizes[1]/2; + shiftPCM=(pcm_returned<shiftPCM?pcm_returned:shiftPCM); + + pcm_current-=shiftPCM; + centerW-=shiftPCM; + pcm_returned-=shiftPCM; + if(shiftPCM!=0){ + for(int i=0;i<vi.channels;i++){ + System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current); + } + } + } + + lW=W; + W=vb.W; + nW=-1; + + glue_bits+=vb.glue_bits; + time_bits+=vb.time_bits; + floor_bits+=vb.floor_bits; + res_bits+=vb.res_bits; + + if(sequence+1 != vb.sequence)granulepos=-1; // out of sequence; lose count + + sequence=vb.sequence; + + { + int sizeW=vi.blocksizes[W]; + int _centerW=centerW+vi.blocksizes[lW]/4+sizeW/4; + int beginW=_centerW-sizeW/2; + int endW=beginW+sizeW; + int beginSl=0; + int endSl=0; + + // Do we have enough PCM/mult storage for the block? + if(endW>pcm_storage){ + // expand the storage + pcm_storage=endW+vi.blocksizes[1]; + for(int i=0;i<vi.channels;i++){ + float[] foo=new float[pcm_storage]; + System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length); + pcm[i]=foo; + } + } + + // overlap/add PCM + switch(W){ + case 0: + beginSl=0; + endSl=vi.blocksizes[0]/2; + break; + case 1: + beginSl=vi.blocksizes[1]/4-vi.blocksizes[lW]/4; + endSl=beginSl+vi.blocksizes[lW]/2; + break; + } + + for(int j=0;j<vi.channels;j++){ + int _pcm=beginW; + // the overlap/add section + int i=0; + for(i=beginSl;i<endSl;i++){ + pcm[j][_pcm+i]+=vb.pcm[j][i]; + } + // the remaining section + for(;i<sizeW;i++){ + pcm[j][_pcm+i]=vb.pcm[j][i]; + } + } + + // track the frame number... This is for convenience, but also + // making sure our last packet doesn't end with added padding. If + // the last packet is partial, the number of samples we'll have to + // return will be past the vb->granulepos. + // + // This is not foolproof! It will be confused if we begin + // decoding at the last page after a seek or hole. In that case, + // we don't have a starting point to judge where the last frame + // is. For this reason, vorbisfile will always try to make sure + // it reads the last two marked pages in proper sequence + + if(granulepos==-1){ + granulepos=vb.granulepos; + } + else{ + granulepos+=(_centerW-centerW); + if(vb.granulepos!=-1 && granulepos!=vb.granulepos){ + if(granulepos>vb.granulepos && vb.eofflag!=0){ + // partial last frame. Strip the padding off + _centerW-=(granulepos-vb.granulepos); + }// else{ Shouldn't happen *unless* the bitstream is out of + // spec. Either way, believe the bitstream } + granulepos=vb.granulepos; + } + } + + // Update, cleanup + + centerW=_centerW; + pcm_current=endW; + if(vb.eofflag!=0)eofflag=1; + } + return(0); + } + + // pcm==NULL indicates we just want the pending samples, no more + public int synthesis_pcmout(float[][][] _pcm, int[] index){ + if(pcm_returned<centerW){ + if(_pcm!=null){ + for(int i=0;i<vi.channels;i++){ +// pcmret[i]=pcm[i]+v.pcm_returned; +//!!!!!!!! + index[i]=pcm_returned; + } + _pcm[0]=pcm; + } + return(centerW-pcm_returned); + } + return(0); + } + + public int synthesis_read(int bytes){ + if(bytes!=0 && pcm_returned+bytes>centerW)return(-1); + pcm_returned+=bytes; + return(0); + } + + public void clear(){ +/* + if(window[0][0][0]!=0){ + for(i=0;i<VI_WINDOWB;i++) + if(v->window[0][0][0][i])free(v->window[0][0][0][i]); + free(v->window[0][0][0]); + + for(j=0;j<2;j++) + for(k=0;k<2;k++){ + for(i=0;i<VI_WINDOWB;i++) + if(v->window[1][j][k][i])free(v->window[1][j][k][i]); + free(v->window[1][j][k]); + } + } + + if(v->pcm){ + for(i=0;i<vi->channels;i++) + if(v->pcm[i])free(v->pcm[i]); + free(v->pcm); + if(v->pcmret)free(v->pcmret); + } + if(v->multipliers)free(v->multipliers); + + _ve_envelope_clear(&v->ve); + if(v->transform[0]){ + mdct_clear(v->transform[0][0]); + free(v->transform[0][0]); + free(v->transform[0]); + } + if(v->transform[1]){ + mdct_clear(v->transform[1][0]); + free(v->transform[1][0]); + free(v->transform[1]); + } + + // free mode lookups; these are actually vorbis_look_mapping structs + if(vi){ + for(i=0;i<vi->modes;i++){ + int mapnum=vi->mode_param[i]->mapping; + int maptype=vi->map_type[mapnum]; + _mapping_P[maptype]->free_look(v->mode[i]); + } + // free codebooks + for(i=0;i<vi->books;i++) + vorbis_book_clear(v->fullbooks+i); + } + + if(v->mode)free(v->mode); + if(v->fullbooks)free(v->fullbooks); + + // free header, header1, header2 + if(v->header)free(v->header); + if(v->header1)free(v->header1); + if(v->header2)free(v->header2); + + memset(v,0,sizeof(vorbis_dsp_state)); + } +*/ +} +} diff --git a/songdbj/com/jcraft/jorbis/EncodeAuxNearestMatch.java b/songdbj/com/jcraft/jorbis/EncodeAuxNearestMatch.java new file mode 100644 index 0000000000..c4b3b06c6e --- /dev/null +++ b/songdbj/com/jcraft/jorbis/EncodeAuxNearestMatch.java @@ -0,0 +1,36 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class EncodeAuxNearestMatch{ + int[] ptr0; + int[] ptr1; + + int[] p; // decision points (each is an entry) + int[] q; // decision points (each is an entry) + int aux; // number of tree entries + int alloc; +} diff --git a/songdbj/com/jcraft/jorbis/EncodeAuxThreshMatch.java b/songdbj/com/jcraft/jorbis/EncodeAuxThreshMatch.java new file mode 100644 index 0000000000..33cb58733c --- /dev/null +++ b/songdbj/com/jcraft/jorbis/EncodeAuxThreshMatch.java @@ -0,0 +1,33 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class EncodeAuxThreshMatch{ + float[] quantthresh; + int[] quantmap; + int quantvals; + int threshvals; +} diff --git a/songdbj/com/jcraft/jorbis/Floor0.java b/songdbj/com/jcraft/jorbis/Floor0.java new file mode 100644 index 0000000000..3f1d1c32d5 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Floor0.java @@ -0,0 +1,352 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Floor0 extends FuncFloor{ + + void pack(Object i, Buffer opb){ + InfoFloor0 info=(InfoFloor0)i; + opb.write(info.order,8); + opb.write(info.rate,16); + opb.write(info.barkmap,16); + opb.write(info.ampbits,6); + opb.write(info.ampdB,8); + opb.write(info.numbooks-1,4); + for(int j=0;j<info.numbooks;j++) + opb.write(info.books[j],8); + } + + Object unpack(Info vi , Buffer opb){ + InfoFloor0 info=new InfoFloor0(); + info.order=opb.read(8); + info.rate=opb.read(16); + info.barkmap=opb.read(16); + info.ampbits=opb.read(6); + info.ampdB=opb.read(8); + info.numbooks=opb.read(4)+1; + + if((info.order<1)|| + (info.rate<1)|| + (info.barkmap<1)|| + (info.numbooks<1)){ + //free_info(info); + return(null); + } + + for(int j=0;j<info.numbooks;j++){ + info.books[j]=opb.read(8); + if(info.books[j]<0 || info.books[j]>=vi.books){ + //free_info(info); + return(null); + } + } + return(info); +// err_out: +// free_info(info); +// return(NULL); + } + Object look(DspState vd, InfoMode mi, Object i){ + float scale; + Info vi=vd.vi; + InfoFloor0 info=(InfoFloor0)i; + LookFloor0 look=new LookFloor0(); + look.m=info.order; + look.n=vi.blocksizes[mi.blockflag]/2; + look.ln=info.barkmap; + look.vi=info; + look.lpclook.init(look.ln,look.m); + + // we choose a scaling constant so that: + // floor(bark(rate/2-1)*C)=mapped-1 + // floor(bark(rate/2)*C)=mapped + scale=look.ln/toBARK((float)(info.rate/2.)); + + // the mapping from a linear scale to a smaller bark scale is + // straightforward. We do *not* make sure that the linear mapping + // does not skip bark-scale bins; the decoder simply skips them and + // the encoder may do what it wishes in filling them. They're + // necessary in some mapping combinations to keep the scale spacing + // accurate + look.linearmap=new int[look.n]; + for(int j=0;j<look.n;j++){ + int val=(int)Math.floor(toBARK((float)((info.rate/2.)/look.n*j)) + *scale); // bark numbers represent band edges + if(val>=look.ln)val=look.ln; // guard against the approximation + look.linearmap[j]=val; + } + return look; + } + + static float toBARK(float f){ + return (float)(13.1*Math.atan(.00074*(f))+2.24*Math.atan((f)*(f)*1.85e-8)+1e-4*(f)); + } + + Object state(Object i){ + EchstateFloor0 state=new EchstateFloor0(); + InfoFloor0 info=(InfoFloor0)i; + + // a safe size if usually too big (dim==1) + state.codewords=new int[info.order]; + state.curve=new float[info.barkmap]; + state.frameno=-1; + return(state); + } + void free_info(Object i){} + void free_look(Object i){} + void free_state(Object vs){} + int forward(Block vb, Object i, float[] in, float[] out, Object vs){return 0;} + + float[] lsp=null; + int inverse(Block vb, Object i, float[] out){ + //System.err.println("Floor0.inverse "+i.getClass()+"]"); + LookFloor0 look=(LookFloor0)i; + InfoFloor0 info=look.vi; + int ampraw=vb.opb.read(info.ampbits); + if(ampraw>0){ // also handles the -1 out of data case + int maxval=(1<<info.ampbits)-1; + float amp=(float)ampraw/maxval*info.ampdB; + int booknum=vb.opb.read(ilog(info.numbooks)); + + if(booknum!=-1 && booknum<info.numbooks){ + + synchronized(this){ + if(lsp==null||lsp.length<look.m){ + lsp=new float[look.m]; + } + else{ + for(int j=0; j<look.m; j++)lsp[j]=0.f; + } + + CodeBook b=vb.vd.fullbooks[info.books[booknum]]; + float last=0.f; + + //memset(out,0,sizeof(float)*look->m); + for(int j=0; j<look.m; j++)out[j]=0.0f; + + for(int j=0;j<look.m;j+=b.dim){ + if(b.decodevs(lsp, j, vb.opb, 1, -1)==-1){ + //goto eop; + // memset(out,0,sizeof(float)*look->n); + for(int k=0; k<look.n; k++)out[k]=0.0f; + return(0); + } + } + for(int j=0;j<look.m;){ + for(int k=0;k<b.dim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + // take the coefficients back to a spectral envelope curve + /* + lsp_to_lpc(out,out,look.m); + lpc_to_curve(out,out,amp,look,"",0); + for(int j=0;j<look.n;j++){ + out[j]=fromdB(out[j]-info.ampdB); + } + */ + Lsp.lsp_to_curve(out,look.linearmap,look.n,look.ln, + lsp,look.m,amp,info.ampdB); + + return(1); + } + } + } +// eop: +// memset(out,0,sizeof(float)*look->n); + return(0); + } + + Object inverse1(Block vb, Object i, Object memo){ + //System.err.println("Floor0.inverse "+i.getClass()+"]"); + LookFloor0 look=(LookFloor0)i; + InfoFloor0 info=look.vi; + float[] lsp=null; + if(memo instanceof float[]){ + lsp=(float[])memo; + } + + int ampraw=vb.opb.read(info.ampbits); + if(ampraw>0){ // also handles the -1 out of data case + int maxval=(1<<info.ampbits)-1; + float amp=(float)ampraw/maxval*info.ampdB; + int booknum=vb.opb.read(ilog(info.numbooks)); + + if(booknum!=-1 && booknum<info.numbooks){ + CodeBook b=vb.vd.fullbooks[info.books[booknum]]; + float last=0.f; + + if(lsp==null||lsp.length<look.m+1){ + lsp=new float[look.m+1]; + } + else{ + for(int j=0; j<lsp.length; j++)lsp[j]=0.f; + } + + for(int j=0;j<look.m;j+=b.dim){ + if(b.decodev_set(lsp, j, vb.opb, b.dim)==-1){ + //goto eop; + return(null); + } + } + + for(int j=0;j<look.m;){ + for(int k=0;k<b.dim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + lsp[look.m]=amp; + return(lsp); + } + } +// eop: + return(null); + } + + int inverse2(Block vb, Object i, Object memo, float[] out){ + //System.err.println("Floor0.inverse "+i.getClass()+"]"); + LookFloor0 look=(LookFloor0)i; + InfoFloor0 info=look.vi; + + if(memo!=null){ + float[] lsp=(float[])memo; + float amp=lsp[look.m]; + + Lsp.lsp_to_curve(out,look.linearmap,look.n,look.ln, + lsp,look.m,amp,info.ampdB); + return(1); + } +// eop: +// memset(out,0,sizeof(float)*look->n); + for(int j=0; j<look.n; j++){ + out[j]=0.f; + } + return(0); + } + + static float fromdB(float x){ + return (float)(Math.exp((x)*.11512925)); + } + private static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return(ret); + } + + static void lsp_to_lpc(float[] lsp, float[] lpc, int m){ + int i,j,m2=m/2; + float[] O=new float[m2]; + float[] E=new float[m2]; + float A; + float[] Ae=new float[m2+1]; + float[] Ao=new float[m2+1]; + float B; + float[] Be=new float[m2]; + float[] Bo=new float[m2]; + float temp; + + // even/odd roots setup + for(i=0;i<m2;i++){ + O[i]=(float)(-2.*Math.cos(lsp[i*2])); + E[i]=(float)(-2.*Math.cos(lsp[i*2+1])); + } + + // set up impulse response + for(j=0;j<m2;j++){ + Ae[j]=0.f; + Ao[j]=1.f; + Be[j]=0.f; + Bo[j]=1.f; + } + Ao[j]=1.f; + Ae[j]=1.f; + + // run impulse response + for(i=1;i<m+1;i++){ + A=B=0.f; + for(j=0;j<m2;j++){ + temp=O[j]*Ao[j]+Ae[j]; + Ae[j]=Ao[j]; + Ao[j]=A; + A+=temp; + + temp=E[j]*Bo[j]+Be[j]; + Be[j]=Bo[j]; + Bo[j]=B; + B+=temp; + } + lpc[i-1]=(A+Ao[j]+B-Ae[j])/2; + Ao[j]=A; + Ae[j]=B; + } + } + + static void lpc_to_curve(float[] curve, float[] lpc,float amp, + LookFloor0 l, String name, int frameno){ + // l->m+1 must be less than l->ln, but guard in case we get a bad stream + float[] lcurve=new float[Math.max(l.ln*2,l.m*2+2)]; + + if(amp==0){ + //memset(curve,0,sizeof(float)*l->n); + for(int j=0; j<l.n; j++)curve[j]=0.0f; + return; + } + l.lpclook.lpc_to_curve(lcurve,lpc,amp); + + for(int i=0;i<l.n;i++)curve[i]=lcurve[l.linearmap[i]]; + } +} + +class InfoFloor0{ + int order; + int rate; + int barkmap; + + int ampbits; + int ampdB; + + int numbooks; // <= 16 + int[] books=new int[16]; +} + +class LookFloor0{ + int n; + int ln; + int m; + int[] linearmap; + + InfoFloor0 vi; + Lpc lpclook=new Lpc(); +} + +class EchstateFloor0{ + int[] codewords; + float[] curve; + long frameno; + long codes; +} diff --git a/songdbj/com/jcraft/jorbis/Floor1.java b/songdbj/com/jcraft/jorbis/Floor1.java new file mode 100644 index 0000000000..1e52c3e537 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Floor1.java @@ -0,0 +1,653 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Floor1 extends FuncFloor{ + static final int floor1_rangedb=140; + static final int VIF_POSIT=63; + + void pack(Object i, Buffer opb){ + InfoFloor1 info=(InfoFloor1)i; + + int count=0; + int rangebits; + int maxposit=info.postlist[1]; + int maxclass=-1; + + /* save out partitions */ + opb.write(info.partitions,5); /* only 0 to 31 legal */ + for(int j=0;j<info.partitions;j++){ + opb.write(info.partitionclass[j],4); /* only 0 to 15 legal */ + if(maxclass<info.partitionclass[j]) + maxclass=info.partitionclass[j]; + } + + /* save out partition classes */ + for(int j=0;j<maxclass+1;j++){ + opb.write(info.class_dim[j]-1,3); /* 1 to 8 */ + opb.write(info.class_subs[j],2); /* 0 to 3 */ + if(info.class_subs[j]!=0){ + opb.write(info.class_book[j],8); + } + for(int k=0;k<(1<<info.class_subs[j]);k++){ + opb.write(info.class_subbook[j][k]+1,8); + } + } + + /* save out the post list */ + opb.write(info.mult-1,2); /* only 1,2,3,4 legal now */ + opb.write(ilog2(maxposit),4); + rangebits=ilog2(maxposit); + + for(int j=0,k=0;j<info.partitions;j++){ + count+=info.class_dim[info.partitionclass[j]]; + for(;k<count;k++){ + opb.write(info.postlist[k+2],rangebits); + } + } + } + + Object unpack(Info vi , Buffer opb){ + int count=0,maxclass=-1,rangebits; + InfoFloor1 info=new InfoFloor1(); + + /* read partitions */ + info.partitions=opb.read(5); /* only 0 to 31 legal */ + for(int j=0;j<info.partitions;j++){ + info.partitionclass[j]=opb.read(4); /* only 0 to 15 legal */ + if(maxclass<info.partitionclass[j]) + maxclass=info.partitionclass[j]; + } + + /* read partition classes */ + for(int j=0;j<maxclass+1;j++){ + info.class_dim[j]=opb.read(3)+1; /* 1 to 8 */ + info.class_subs[j]=opb.read(2); /* 0,1,2,3 bits */ + if(info.class_subs[j]<0){ + //goto err_out; + info.free(); + return(null); + } + if(info.class_subs[j]!=0){ + info.class_book[j]=opb.read(8); + } + if(info.class_book[j]<0 || info.class_book[j]>=vi.books){ + //goto err_out; + info.free(); + return(null); + } + for(int k=0;k<(1<<info.class_subs[j]);k++){ + info.class_subbook[j][k]=opb.read(8)-1; + if(info.class_subbook[j][k]<-1 || info.class_subbook[j][k]>=vi.books){ + //goto err_out; + info.free(); + return(null); + } + } + } + + /* read the post list */ + info.mult=opb.read(2)+1; /* only 1,2,3,4 legal now */ + rangebits=opb.read(4); + + for(int j=0,k=0;j<info.partitions;j++){ + count+=info.class_dim[info.partitionclass[j]]; + for(;k<count;k++){ + int t=info.postlist[k+2]=opb.read(rangebits); + if(t<0 || t>=(1<<rangebits)){ + //goto err_out; + info.free(); + return(null); + } + } + } + info.postlist[0]=0; + info.postlist[1]=1<<rangebits; + + return(info); +// err_out: +// info.free(); +// return(null); + } + + Object look(DspState vd, InfoMode mi, Object i){ + int _n=0; + + int[] sortpointer=new int[VIF_POSIT+2]; + +// Info vi=vd.vi; + + InfoFloor1 info=(InfoFloor1)i; + LookFloor1 look=new LookFloor1(); + look.vi=info; + look.n=info.postlist[1]; + + /* we drop each position value in-between already decoded values, + and use linear interpolation to predict each new value past the + edges. The positions are read in the order of the position + list... we precompute the bounding positions in the lookup. Of + course, the neighbors can change (if a position is declined), but + this is an initial mapping */ + + for(int j=0;j<info.partitions;j++){ + _n+=info.class_dim[info.partitionclass[j]]; + } + _n+=2; + look.posts=_n; + + /* also store a sorted position index */ + for(int j=0;j<_n;j++){ + sortpointer[j]=j; + } +// qsort(sortpointer,n,sizeof(int),icomp); // !! + + int foo; + for(int j=0; j<_n-1; j++){ + for(int k=j; k<_n; k++){ + if(info.postlist[sortpointer[j]]>info.postlist[sortpointer[k]]){ + foo=sortpointer[k]; + sortpointer[k]=sortpointer[j]; + sortpointer[j]=foo; + } + } + } + + /* points from sort order back to range number */ + for(int j=0;j<_n;j++){ + look.forward_index[j]=sortpointer[j]; + } + /* points from range order to sorted position */ + for(int j=0;j<_n;j++){ + look.reverse_index[look.forward_index[j]]=j; + } + /* we actually need the post values too */ + for(int j=0;j<_n;j++){ + look.sorted_index[j]=info.postlist[look.forward_index[j]]; + } + + + /* quantize values to multiplier spec */ + switch(info.mult){ + case 1: /* 1024 -> 256 */ + look.quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look.quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look.quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look.quant_q=64; + break; + default: + look.quant_q=-1; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(int j=0;j<_n-2;j++){ + int lo=0; + int hi=1; + int lx=0; + int hx=look.n; + int currentx=info.postlist[j+2]; + for(int k=0;k<j+2;k++){ + int x=info.postlist[k]; + if(x>lx && x<currentx){ + lo=k; + lx=x; + } + if(x<hx && x>currentx){ + hi=k; + hx=x; + } + } + look.loneighbor[j]=lo; + look.hineighbor[j]=hi; + } + + return look; + } + + void free_info(Object i){} + void free_look(Object i){} + void free_state(Object vs){} + + int forward(Block vb, Object i, float[] in, float[] out, Object vs){return 0;} + + Object inverse1(Block vb, Object ii, Object memo){ + //System.err.println("Floor1.inverse "+i.getClass()+"]"); + LookFloor1 look=(LookFloor1)ii; + InfoFloor1 info=look.vi; + CodeBook[] books=vb.vd.fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(vb.opb.read(1)==1){ + int[] fit_value=null; + if(memo instanceof int[]){ + fit_value=(int[])memo; + } + if(fit_value==null || fit_value.length<look.posts){ + fit_value=new int[look.posts]; + } + else{ + for(int i=0; i<fit_value.length; i++) fit_value[i]=0; + } + + fit_value[0]=vb.opb.read(ilog(look.quant_q-1)); + fit_value[1]=vb.opb.read(ilog(look.quant_q-1)); + + /* partition by partition */ + for(int i=0,j=2;i<info.partitions;i++){ + int clss=info.partitionclass[i]; + int cdim=info.class_dim[clss]; + int csubbits=info.class_subs[clss]; + int csub=1<<csubbits; + int cval=0; + + /* decode the partition's first stage cascade value */ + if(csubbits!=0){ + cval=books[info.class_book[clss]].decode(vb.opb); + + if(cval==-1){ + //goto eop; + return(null); + } + } + + for(int k=0;k<cdim;k++){ + int book=info.class_subbook[clss][cval&(csub-1)]; + cval>>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=books[book].decode(vb.opb))==-1){ + //goto eop; + return(null); + } + } + else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(int i=2;i<look.posts;i++){ + int predicted=render_point(info.postlist[look.loneighbor[i-2]], + info.postlist[look.hineighbor[i-2]], + fit_value[look.loneighbor[i-2]], + fit_value[look.hineighbor[i-2]], + info.postlist[i]); + int hiroom=look.quant_q-predicted; + int loroom=predicted; + int room=(hiroom<loroom?hiroom:loroom)<<1; + int val=fit_value[i]; + + if(val!=0){ + if(val>=room){ + if(hiroom>loroom){ + val = val-loroom; + } + else{ + val = -1-(val-hiroom); + } + } + else{ + if((val&1)!=0){ + val= -((val+1)>>>1); + } + else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[look.loneighbor[i-2]]&=0x7fff; + fit_value[look.hineighbor[i-2]]&=0x7fff; + } + else{ + fit_value[i]=predicted|0x8000; + } + } + return(fit_value); + } + +// eop: +// return(NULL); + return(null); + } + + private static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=Math.abs(dy); + int err=ady*(x-x0); + + int off=(int)(err/adx); + if(dy<0)return(y0-off); + return(y0+off); + } + } + + int inverse2(Block vb, Object i, Object memo, float[] out){ + LookFloor1 look=(LookFloor1)i; + InfoFloor1 info=look.vi; + int n=vb.vd.vi.blocksizes[vb.mode]/2; + + if(memo!=null){ + /* render the lines */ + int[] fit_value=(int[] )memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info.mult; + for(int j=1;j<look.posts;j++){ + int current=look.forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + hy*=info.mult; + hx=info.postlist[current]; + + render_line(lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(int j=hx;j<n;j++){ + out[j]*=out[j-1]; /* be certain */ + } + return(1); + } + for(int j=0; j<n; j++){ + out[j]=0.f; + } + return(0); + } + + + private static float[] FLOOR_fromdB_LOOKUP={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F + }; + + private static void render_line(int x0, int x1,int y0,int y1,float[] d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=Math.abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=Math.abs(base*adx); + + d[x]*=FLOOR_fromdB_LOOKUP[y]; + while(++x<x1){ + err=err+ady; + if(err>=adx){ + err-=adx; + y+=sy; + } + else{ + y+=base; + } + d[x]*=FLOOR_fromdB_LOOKUP[y]; + } + } + + static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return(ret); + } + + private static int ilog2(int v){ + int ret=0; + while(v>1){ + ret++; + v>>>=1; + } + return(ret); + } +} + +class InfoFloor1{ + static final int VIF_POSIT=63; + static final int VIF_CLASS=16; + static final int VIF_PARTS=31; + + int partitions; /* 0 to 31 */ + int[] partitionclass=new int[VIF_PARTS]; /* 0 to 15 */ + + int[] class_dim=new int[VIF_CLASS]; /* 1 to 8 */ + int[] class_subs=new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1<<n poss) */ + int[] class_book=new int[VIF_CLASS]; /* subs ^ dim entries */ + int[][] class_subbook=new int[VIF_CLASS][]; /* [VIF_CLASS][subs] */ + + + int mult; /* 1 2 3 or 4 */ + int[] postlist=new int[VIF_POSIT+2]; /* first two implicit */ + + + /* encode side analysis parameters */ + float maxover; + float maxunder; + float maxerr; + + int twofitminsize; + int twofitminused; + int twofitweight; + float twofitatten; + int unusedminsize; + int unusedmin_n; + + int n; + + InfoFloor1(){ + for(int i=0; i<class_subbook.length; i++){ + class_subbook[i]=new int[8]; + } + } + + void free(){ + partitionclass=null; + class_dim=null; + class_subs=null; + class_book=null; + class_subbook=null; + postlist=null; + } + + Object copy_info(){ + InfoFloor1 info=this; + InfoFloor1 ret=new InfoFloor1(); + + ret.partitions=info.partitions; + System.arraycopy(info.partitionclass, 0, ret.partitionclass, 0, VIF_PARTS); + System.arraycopy(info.class_dim, 0, ret.class_dim, 0, VIF_CLASS); + System.arraycopy(info.class_subs, 0, ret.class_subs, 0, VIF_CLASS); + System.arraycopy(info.class_book, 0, ret.class_book, 0, VIF_CLASS); + + for(int j=0; j<VIF_CLASS; j++){ + System.arraycopy(info.class_subbook[j], 0, + ret.class_subbook[j], 0, 8); + } + + ret.mult=info.mult; + System.arraycopy(info.postlist, 0, ret.postlist, 0, VIF_POSIT+2); + + ret.maxover=info.maxover; + ret.maxunder=info.maxunder; + ret.maxerr=info.maxerr; + + ret.twofitminsize=info.twofitminsize; + ret.twofitminused=info.twofitminused; + ret.twofitweight=info.twofitweight; + ret.twofitatten=info.twofitatten; + ret.unusedminsize=info.unusedminsize; + ret.unusedmin_n=info.unusedmin_n; + + ret.n=info.n; + + return(ret); + } + +} + +class LookFloor1{ + static final int VIF_POSIT=63; + + int[] sorted_index=new int[VIF_POSIT+2]; + int[] forward_index=new int[VIF_POSIT+2]; + int[] reverse_index=new int[VIF_POSIT+2]; + int[] hineighbor=new int[VIF_POSIT]; + int[] loneighbor=new int[VIF_POSIT]; + int posts; + + int n; + int quant_q; + InfoFloor1 vi; + + int phrasebits; + int postbits; + int frames; + + void free(){ + +/* + System.out.println("floor 1 bit usage "+ + (float)(phrasebits/frames) + +":"+ + (float)(postbits/frames) + +"("+ + (float)((postbits+phrasebits)/frames) + +" total)" + +*/ + + sorted_index=null; + forward_index=null; + reverse_index=null; + hineighbor=null; + loneighbor=null; + } +} + +class Lsfit_acc{ + long x0; + long x1; + + long xa; + long ya; + long x2a; + long y2a; + long xya; + long n; + long an; + long un; + long edgey0; + long edgey1; +} + +class EchstateFloor1{ + int[] codewords; + float[] curve; + long frameno; + long codes; +} diff --git a/songdbj/com/jcraft/jorbis/FuncFloor.java b/songdbj/com/jcraft/jorbis/FuncFloor.java new file mode 100644 index 0000000000..f438d0f260 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/FuncFloor.java @@ -0,0 +1,45 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncFloor{ +// public static FuncFloor[] floor_P={new Floor0()}; + public static FuncFloor[] floor_P={new Floor0(),new Floor1()}; + + abstract void pack(Object i, Buffer opb); + abstract Object unpack(Info vi, Buffer opb); + abstract Object look(DspState vd, InfoMode mi, Object i); +// abstract Object state(Object i); + abstract void free_info(Object i); + abstract void free_look(Object i); + abstract void free_state(Object vs); + abstract int forward(Block vb, Object i, float[] in, float[] out, Object vs); +// abstract int inverse(Block vb, Object i, float[] out); + abstract Object inverse1(Block vb, Object i, Object memo); + abstract int inverse2(Block vb, Object i, Object memo, float[] out); +} diff --git a/songdbj/com/jcraft/jorbis/FuncMapping.java b/songdbj/com/jcraft/jorbis/FuncMapping.java new file mode 100644 index 0000000000..c8ecf75fe7 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/FuncMapping.java @@ -0,0 +1,40 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncMapping{ + public static FuncMapping[] mapping_P={new Mapping0()}; + + abstract void pack(Info info , Object imap, Buffer buffer); + abstract Object unpack(Info info , Buffer buffer); + abstract Object look(DspState vd, InfoMode vm, Object m); + abstract void free_info(Object imap); + abstract void free_look(Object imap); +// abstract int forward(Block vd, Object lm); + abstract int inverse(Block vd, Object lm); +} diff --git a/songdbj/com/jcraft/jorbis/FuncResidue.java b/songdbj/com/jcraft/jorbis/FuncResidue.java new file mode 100644 index 0000000000..4cbf6a1d53 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/FuncResidue.java @@ -0,0 +1,43 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncResidue{ + public static FuncResidue[] residue_P={new Residue0(), + new Residue1(), + new Residue2()}; + + abstract void pack(Object vr, Buffer opb); + abstract Object unpack(Info vi, Buffer opb); + abstract Object look(DspState vd, InfoMode vm, Object vr); + abstract void free_info(Object i); + abstract void free_look(Object i); + abstract int forward(Block vb,Object vl, float[][] in, int ch); +// abstract int inverse(Block vb, Object vl, float[][] in, int ch); +abstract int inverse(Block vb, Object vl, float[][] in, int[] nonzero,int ch); +} diff --git a/songdbj/com/jcraft/jorbis/FuncTime.java b/songdbj/com/jcraft/jorbis/FuncTime.java new file mode 100644 index 0000000000..b3cd080461 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/FuncTime.java @@ -0,0 +1,40 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +abstract class FuncTime{ + public static FuncTime[] time_P={new Time0()}; + + abstract void pack(Object i, Buffer opb); + abstract Object unpack(Info vi , Buffer opb); + abstract Object look(DspState vd, InfoMode vm, Object i); + abstract void free_info(Object i); + abstract void free_look(Object i); + abstract int forward(Block vb, Object i); + abstract int inverse(Block vb, Object i, float[] in, float[] out); +} diff --git a/songdbj/com/jcraft/jorbis/Info.java b/songdbj/com/jcraft/jorbis/Info.java new file mode 100644 index 0000000000..dffd4d9de2 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Info.java @@ -0,0 +1,516 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +public class Info{ + private static final int OV_EBADPACKET=-136; + private static final int OV_ENOTAUDIO=-135; + + private static byte[] _vorbis="vorbis".getBytes(); + private static final int VI_TIMEB=1; +// private static final int VI_FLOORB=1; + private static final int VI_FLOORB=2; +// private static final int VI_RESB=1; + private static final int VI_RESB=3; + private static final int VI_MAPB=1; + private static final int VI_WINDOWB=1; + + public int version; + public int channels; + public int rate; + + // The below bitrate declarations are *hints*. + // Combinations of the three values carry the following implications: + // + // all three set to the same value: + // implies a fixed rate bitstream + // only nominal set: + // implies a VBR stream that averages the nominal bitrate. No hard + // upper/lower limit + // upper and or lower set: + // implies a VBR bitstream that obeys the bitrate limits. nominal + // may also be set to give a nominal rate. + // none set: + // the coder does not care to speculate. + + int bitrate_upper; + int bitrate_nominal; + int bitrate_lower; + + // Vorbis supports only short and long blocks, but allows the + // encoder to choose the sizes + + int[] blocksizes=new int[2]; + + // modes are the primary means of supporting on-the-fly different + // blocksizes, different channel mappings (LR or mid-side), + // different residue backends, etc. Each mode consists of a + // blocksize flag and a mapping (along with the mapping setup + + int modes; + int maps; + int times; + int floors; + int residues; + int books; + int psys; // encode only + + InfoMode[] mode_param=null; + + int[] map_type=null; + Object[] map_param=null; + + int[] time_type=null; + Object[] time_param=null; + + int[] floor_type=null; + Object[] floor_param=null; + + int[] residue_type=null; + Object[] residue_param=null; + + StaticCodeBook[] book_param=null; + + PsyInfo[] psy_param=new PsyInfo[64]; // encode only + + // for block long/sort tuning; encode only + int envelopesa; + float preecho_thresh; + float preecho_clamp; + + // used by synthesis, which has a full, alloced vi + public void init(){ + rate=0; + //memset(vi,0,sizeof(vorbis_info)); + } + + public void clear(){ + for(int i=0;i<modes;i++){ mode_param[i]=null; } + mode_param=null; + + for(int i=0;i<maps;i++){ // unpack does the range checking + FuncMapping.mapping_P[map_type[i]].free_info(map_param[i]); + } + map_param=null; + + for(int i=0;i<times;i++){ // unpack does the range checking + FuncTime.time_P[time_type[i]].free_info(time_param[i]); + } + time_param=null; + + for(int i=0;i<floors;i++){ // unpack does the range checking + FuncFloor.floor_P[floor_type[i]].free_info(floor_param[i]); + } + floor_param=null; + + for(int i=0;i<residues;i++){ // unpack does the range checking + FuncResidue.residue_P[residue_type[i]].free_info(residue_param[i]); + } + residue_param=null; + + // the static codebooks *are* freed if you call info_clear, because + // decode side does alloc a 'static' codebook. Calling clear on the + // full codebook does not clear the static codebook (that's our + // responsibility) + for(int i=0;i<books;i++){ + // just in case the decoder pre-cleared to save space + if(book_param[i]!=null){ + book_param[i].clear(); + book_param[i]=null; + } + } + //if(vi->book_param)free(vi->book_param); + book_param=null; + + for(int i=0;i<psys;i++){ + psy_param[i].free(); + } + //if(vi->psy_param)free(vi->psy_param); + //memset(vi,0,sizeof(vorbis_info)); + } + + // Header packing/unpacking + int unpack_info(Buffer opb){ + version=opb.read(32); + if(version!=0)return(-1); + + channels=opb.read(8); + rate=opb.read(32); + + bitrate_upper=opb.read(32); + bitrate_nominal=opb.read(32); + bitrate_lower=opb.read(32); + + blocksizes[0]=1<<opb.read(4); + blocksizes[1]=1<<opb.read(4); + + if((rate<1) || + (channels<1)|| + (blocksizes[0]<8)|| + (blocksizes[1]<blocksizes[0]) || + (opb.read(1)!=1)){ + //goto err_out; // EOP check + clear(); + return(-1); + } + return(0); + // err_out: + // vorbis_info_clear(vi); + // return(-1); + } + + // all of the real encoding details are here. The modes, books, + // everything + int unpack_books(Buffer opb){ + + //d* codebooks + books=opb.read(8)+1; + + if(book_param==null || book_param.length!=books) + book_param=new StaticCodeBook[books]; + for(int i=0;i<books;i++){ + book_param[i]=new StaticCodeBook(); + if(book_param[i].unpack(opb)!=0){ + //goto err_out; + clear(); + return(-1); + } + } + + // time backend settings + times=opb.read(6)+1; + if(time_type==null || time_type.length!=times) time_type=new int[times]; + if(time_param==null || time_param.length!=times) + time_param=new Object[times]; + for(int i=0;i<times;i++){ + time_type[i]=opb.read(16); + if(time_type[i]<0 || time_type[i]>=VI_TIMEB){ + //goto err_out; + clear(); + return(-1); + } + time_param[i]=FuncTime.time_P[time_type[i]].unpack(this, opb); + if(time_param[i]==null){ + //goto err_out; + clear(); + return(-1); + } + } + + // floor backend settings + floors=opb.read(6)+1; + if(floor_type==null || floor_type.length!=floors) + floor_type=new int[floors]; + if(floor_param==null || floor_param.length!=floors) + floor_param=new Object[floors]; + + for(int i=0;i<floors;i++){ + floor_type[i]=opb.read(16); + if(floor_type[i]<0 || floor_type[i]>=VI_FLOORB){ + //goto err_out; + clear(); + return(-1); + } + + floor_param[i]=FuncFloor.floor_P[floor_type[i]].unpack(this,opb); + if(floor_param[i]==null){ + //goto err_out; + clear(); + return(-1); + } + } + + // residue backend settings + residues=opb.read(6)+1; + + if(residue_type==null || residue_type.length!=residues) + residue_type=new int[residues]; + + if(residue_param==null || residue_param.length!=residues) + residue_param=new Object[residues]; + + for(int i=0;i<residues;i++){ + residue_type[i]=opb.read(16); + if(residue_type[i]<0 || residue_type[i]>=VI_RESB){ +// goto err_out; + clear(); + return(-1); + } + residue_param[i]=FuncResidue.residue_P[residue_type[i]].unpack(this,opb); + if(residue_param[i]==null){ +// goto err_out; + clear(); + return(-1); + } + } + + // map backend settings + maps=opb.read(6)+1; + if(map_type==null || map_type.length!=maps) map_type=new int[maps]; + if(map_param==null || map_param.length!=maps) map_param=new Object[maps]; + for(int i=0;i<maps;i++){ + map_type[i]=opb.read(16); + if(map_type[i]<0 || map_type[i]>=VI_MAPB){ +// goto err_out; + clear(); + return(-1); + } + map_param[i]=FuncMapping.mapping_P[map_type[i]].unpack(this,opb); + if(map_param[i]==null){ +// goto err_out; + clear(); + return(-1); + } + } + + // mode settings + modes=opb.read(6)+1; + if(mode_param==null || mode_param.length!=modes) + mode_param=new InfoMode[modes]; + for(int i=0;i<modes;i++){ + mode_param[i]=new InfoMode(); + mode_param[i].blockflag=opb.read(1); + mode_param[i].windowtype=opb.read(16); + mode_param[i].transformtype=opb.read(16); + mode_param[i].mapping=opb.read(8); + + if((mode_param[i].windowtype>=VI_WINDOWB)|| + (mode_param[i].transformtype>=VI_WINDOWB)|| + (mode_param[i].mapping>=maps)){ +// goto err_out; + clear(); + return(-1); + } + } + + if(opb.read(1)!=1){ + //goto err_out; // top level EOP check + clear(); + return(-1); + } + + return(0); +// err_out: +// vorbis_info_clear(vi); +// return(-1); + } + + // The Vorbis header is in three packets; the initial small packet in + // the first page that identifies basic parameters, a second packet + // with bitstream comments and a third packet that holds the + // codebook. + + public int synthesis_headerin(Comment vc, Packet op){ + Buffer opb=new Buffer(); + + if(op!=null){ + opb.readinit(op.packet_base, op.packet, op.bytes); + + // Which of the three types of header is this? + // Also verify header-ness, vorbis + { + byte[] buffer=new byte[6]; + int packtype=opb.read(8); + //memset(buffer,0,6); + opb.read(buffer,6); + if(buffer[0]!='v' || buffer[1]!='o' || buffer[2]!='r' || + buffer[3]!='b' || buffer[4]!='i' || buffer[5]!='s'){ + // not a vorbis header + return(-1); + } + switch(packtype){ + case 0x01: // least significant *bit* is read first + if(op.b_o_s==0){ + // Not the initial packet + return(-1); + } + if(rate!=0){ + // previously initialized info header + return(-1); + } + return(unpack_info(opb)); + case 0x03: // least significant *bit* is read first + if(rate==0){ + // um... we didn't get the initial header + return(-1); + } + return(vc.unpack(opb)); + case 0x05: // least significant *bit* is read first + if(rate==0 || vc.vendor==null){ + // um... we didn;t get the initial header or comments yet + return(-1); + } + return(unpack_books(opb)); + default: + // Not a valid vorbis header type + //return(-1); + break; + } + } + } + return(-1); + } + + // pack side + int pack_info(Buffer opb){ + // preamble + opb.write(0x01,8); + opb.write(_vorbis); + + // basic information about the stream + opb.write(0x00,32); + opb.write(channels,8); + opb.write(rate,32); + + opb.write(bitrate_upper,32); + opb.write(bitrate_nominal,32); + opb.write(bitrate_lower,32); + + opb.write(ilog2(blocksizes[0]),4); + opb.write(ilog2(blocksizes[1]),4); + opb.write(1,1); + return(0); + } + + int pack_books(Buffer opb){ + opb.write(0x05,8); + opb.write(_vorbis); + + // books + opb.write(books-1,8); + for(int i=0;i<books;i++){ + if(book_param[i].pack(opb)!=0){ + //goto err_out; + return(-1); + } + } + + // times + opb.write(times-1,6); + for(int i=0;i<times;i++){ + opb.write(time_type[i],16); + FuncTime.time_P[time_type[i]].pack(this.time_param[i],opb); + } + + // floors + opb.write(floors-1,6); + for(int i=0;i<floors;i++){ + opb.write(floor_type[i],16); + FuncFloor.floor_P[floor_type[i]].pack(floor_param[i],opb); + } + + // residues + opb.write(residues-1,6); + for(int i=0;i<residues;i++){ + opb.write(residue_type[i],16); + FuncResidue.residue_P[residue_type[i]].pack(residue_param[i],opb); + } + + // maps + opb.write(maps-1,6); + for(int i=0;i<maps;i++){ + opb.write(map_type[i],16); + FuncMapping.mapping_P[map_type[i]].pack(this,map_param[i],opb); + } + + // modes + opb.write(modes-1,6); + for(int i=0;i<modes;i++){ + opb.write(mode_param[i].blockflag,1); + opb.write(mode_param[i].windowtype,16); + opb.write(mode_param[i].transformtype,16); + opb.write(mode_param[i].mapping,8); + } + opb.write(1,1); + return(0); + //err_out: + //return(-1); + } + +// static void v_writestring(Buffer o, byte[] s){ +// int i=0; +// while(s[i]!=0){ +// o.write(s[i++],8); +// } +// } + +// static void v_readstring(Buffer o, byte[] buf, int bytes){ +// int i=0 +// while(bytes--!=0){ +// buf[i++]=o.read(8); +// } +// } + +// private Buffer opb_blocksize=new Buffer(); + public int blocksize(Packet op){ + //codec_setup_info *ci=vi->codec_setup; + Buffer opb=new Buffer(); +// synchronized(opb_blocksize){ + int mode; + + opb.readinit(op.packet_base, op.packet, op.bytes); + + /* Check the packet type */ + if(opb.read(1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + { + int modebits=0; + int v=modes; + while(v>1){ + modebits++; + v>>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=opb.read(modebits); + } + if(mode==-1)return(OV_EBADPACKET); + return(blocksizes[mode_param[mode].blockflag]); +// } + } + + private static int ilog2(int v){ + int ret=0; + while(v>1){ + ret++; + v>>>=1; + } + return(ret); + } + + public String toString(){ + return "version:"+new Integer(version)+ + ", channels:"+new Integer(channels)+ + ", rate:"+new Integer(rate)+ + ", bitrate:"+new Integer(bitrate_upper)+","+ + new Integer(bitrate_nominal)+","+ + new Integer(bitrate_lower); + } +} diff --git a/songdbj/com/jcraft/jorbis/InfoMode.java b/songdbj/com/jcraft/jorbis/InfoMode.java new file mode 100644 index 0000000000..b570aa5c21 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/InfoMode.java @@ -0,0 +1,33 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class InfoMode{ + int blockflag; + int windowtype; + int transformtype; + int mapping; +} diff --git a/songdbj/com/jcraft/jorbis/JOrbisException.java b/songdbj/com/jcraft/jorbis/JOrbisException.java new file mode 100644 index 0000000000..ce09d4f9fc --- /dev/null +++ b/songdbj/com/jcraft/jorbis/JOrbisException.java @@ -0,0 +1,35 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +public class JOrbisException extends Exception { + public JOrbisException () { + super(); + } + public JOrbisException (String s) { + super ("JOrbis: "+s); + } +} diff --git a/songdbj/com/jcraft/jorbis/Lookup.java b/songdbj/com/jcraft/jorbis/Lookup.java new file mode 100644 index 0000000000..fb7651a19f --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Lookup.java @@ -0,0 +1,154 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class Lookup{ + static final int COS_LOOKUP_SZ=128; + static final float[] COS_LOOKUP={ + +1.0000000000000f,+0.9996988186962f,+0.9987954562052f,+0.9972904566787f, + +0.9951847266722f,+0.9924795345987f,+0.9891765099648f,+0.9852776423889f, + +0.9807852804032f,+0.9757021300385f,+0.9700312531945f,+0.9637760657954f, + +0.9569403357322f,+0.9495281805930f,+0.9415440651830f,+0.9329927988347f, + +0.9238795325113f,+0.9142097557035f,+0.9039892931234f,+0.8932243011955f, + +0.8819212643484f,+0.8700869911087f,+0.8577286100003f,+0.8448535652497f, + +0.8314696123025f,+0.8175848131516f,+0.8032075314806f,+0.7883464276266f, + +0.7730104533627f,+0.7572088465065f,+0.7409511253550f,+0.7242470829515f, + +0.7071067811865f,+0.6895405447371f,+0.6715589548470f,+0.6531728429538f, + +0.6343932841636f,+0.6152315905806f,+0.5956993044924f,+0.5758081914178f, + +0.5555702330196f,+0.5349976198871f,+0.5141027441932f,+0.4928981922298f, + +0.4713967368260f,+0.4496113296546f,+0.4275550934303f,+0.4052413140050f, + +0.3826834323651f,+0.3598950365350f,+0.3368898533922f,+0.3136817403989f, + +0.2902846772545f,+0.2667127574749f,+0.2429801799033f,+0.2191012401569f, + +0.1950903220161f,+0.1709618887603f,+0.1467304744554f,+0.1224106751992f, + +0.0980171403296f,+0.0735645635997f,+0.0490676743274f,+0.0245412285229f, + +0.0000000000000f,-0.0245412285229f,-0.0490676743274f,-0.0735645635997f, + -0.0980171403296f,-0.1224106751992f,-0.1467304744554f,-0.1709618887603f, + -0.1950903220161f,-0.2191012401569f,-0.2429801799033f,-0.2667127574749f, + -0.2902846772545f,-0.3136817403989f,-0.3368898533922f,-0.3598950365350f, + -0.3826834323651f,-0.4052413140050f,-0.4275550934303f,-0.4496113296546f, + -0.4713967368260f,-0.4928981922298f,-0.5141027441932f,-0.5349976198871f, + -0.5555702330196f,-0.5758081914178f,-0.5956993044924f,-0.6152315905806f, + -0.6343932841636f,-0.6531728429538f,-0.6715589548470f,-0.6895405447371f, + -0.7071067811865f,-0.7242470829515f,-0.7409511253550f,-0.7572088465065f, + -0.7730104533627f,-0.7883464276266f,-0.8032075314806f,-0.8175848131516f, + -0.8314696123025f,-0.8448535652497f,-0.8577286100003f,-0.8700869911087f, + -0.8819212643484f,-0.8932243011955f,-0.9039892931234f,-0.9142097557035f, + -0.9238795325113f,-0.9329927988347f,-0.9415440651830f,-0.9495281805930f, + -0.9569403357322f,-0.9637760657954f,-0.9700312531945f,-0.9757021300385f, + -0.9807852804032f,-0.9852776423889f,-0.9891765099648f,-0.9924795345987f, + -0.9951847266722f,-0.9972904566787f,-0.9987954562052f,-0.9996988186962f, + -1.0000000000000f, + }; + /* interpolated lookup based cos function, domain 0 to PI only */ + static float coslook(float a){ + double d=a*(.31830989*(float)COS_LOOKUP_SZ); + int i=(int)d; + return COS_LOOKUP[i]+ ((float)(d-i))*(COS_LOOKUP[i+1]-COS_LOOKUP[i]); + } + + static final int INVSQ_LOOKUP_SZ=32; + static final float[] INVSQ_LOOKUP={ + 1.414213562373f,1.392621247646f,1.371988681140f,1.352246807566f, + 1.333333333333f,1.315191898443f,1.297771369046f,1.281025230441f, + 1.264911064067f,1.249390095109f,1.234426799697f,1.219988562661f, + 1.206045378311f,1.192569588000f,1.179535649239f,1.166919931983f, + 1.154700538379f,1.142857142857f,1.131370849898f,1.120224067222f, + 1.109400392450f,1.098884511590f,1.088662107904f,1.078719779941f, + 1.069044967650f,1.059625885652f,1.050451462878f,1.041511287847f, + 1.032795558989f,1.024295039463f,1.016001016002f,1.007905261358f, + 1.000000000000f, + }; + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsqlook(float a){ +// System.out.println(a); + double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ; + int i=(int)d; + return INVSQ_LOOKUP[i]+ ((float)(d-i))*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]); + } + + static final int INVSQ2EXP_LOOKUP_MIN=-32; + static final int INVSQ2EXP_LOOKUP_MAX=32; + static final float[] INVSQ2EXP_LOOKUP={ + 65536.f, 46340.95001f, 32768.f, 23170.47501f, + 16384.f, 11585.2375f, 8192.f, 5792.618751f, + 4096.f, 2896.309376f, 2048.f, 1448.154688f, + 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, + 64.f, 45.254834f, 32.f, 22.627417f, + 16.f, 11.3137085f, 8.f, 5.656854249f, + 4.f, 2.828427125f, 2.f, 1.414213562f, + 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, + 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, + 0.015625f, 0.01104854346f, 0.0078125f, 0.005524271728f, + 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, + 0.000244140625f,0.0001726334915f,0.0001220703125f,8.631674575e-05f, + 6.103515625e-05f,4.315837288e-05f,3.051757812e-05f,2.157918644e-05f, + 1.525878906e-05f, + }; + /* interpolated 1./sqrt(p) where .5 <= p < 1. */ + static float invsq2explook(int a){ + return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN]; + } + + static final int FROMdB_LOOKUP_SZ=35; + static final int FROMdB2_LOOKUP_SZ=32; + static final int FROMdB_SHIFT=5; + static final int FROMdB2_SHIFT=3; + static final int FROMdB2_MASK=31; + static final float[] FROMdB_LOOKUP={ + 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, + 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, + 0.003981071706f, 0.002511886432f, 0.001584893192f, 0.001f, + 0.0006309573445f,0.0003981071706f,0.0002511886432f,0.0001584893192f, + 0.0001f,6.309573445e-05f,3.981071706e-05f,2.511886432e-05f, + 1.584893192e-05f, 1e-05f,6.309573445e-06f,3.981071706e-06f, + 2.511886432e-06f,1.584893192e-06f, 1e-06f,6.309573445e-07f, + 3.981071706e-07f,2.511886432e-07f,1.584893192e-07f, + }; + static final float[] FROMdB2_LOOKUP={ + 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, + 0.9372921937f, 0.92390007f, 0.9106992942f, 0.8976871324f, + 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, + 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, + 0.7028699885f, 0.6928273125f, 0.6829281272f, 0.6731703824f, + 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, + }; + /* interpolated lookup based fromdB function, domain -140dB to 0dB only */ + static float fromdBlook(float a){ + int i=(int)(a*((float)(-(1<<FROMdB2_SHIFT)))); + return (i<0)?1.f: + ((i>=(FROMdB_LOOKUP_SZ<<FROMdB_SHIFT))?0.f: + FROMdB_LOOKUP[i>>>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); + } + +} + + diff --git a/songdbj/com/jcraft/jorbis/Lpc.java b/songdbj/com/jcraft/jorbis/Lpc.java new file mode 100644 index 0000000000..452ed86d91 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Lpc.java @@ -0,0 +1,254 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class Lpc{ + // en/decode lookups + Drft fft=new Drft();; + + int ln; + int m; + + // Autocorrelation LPC coeff generation algorithm invented by + // N. Levinson in 1947, modified by J. Durbin in 1959. + + // Input : n elements of time doamin data + // Output: m lpc coefficients, excitation energy + + static float lpc_from_data(float[] data, float[] lpc,int n,int m){ + float[] aut=new float[m+1]; + float error; + int i,j; + + // autocorrelation, p+1 lag coefficients + + j=m+1; + while(j--!=0){ + float d=0; + for(i=j;i<n;i++)d+=data[i]*data[i-j]; + aut[j]=d; + } + + // Generate lpc coefficients from autocorr values + + error=aut[0]; + /* + if(error==0){ + for(int k=0; k<m; k++) lpc[k]=0.0f; + return 0; + } + */ + + for(i=0;i<m;i++){ + float r=-aut[i+1]; + + if(error==0){ + for(int k=0; k<m; k++) lpc[k]=0.0f; + return 0; + } + + // Sum up this iteration's reflection coefficient; note that in + // Vorbis we don't save it. If anyone wants to recycle this code + // and needs reflection coefficients, save the results of 'r' from + // each iteration. + + for(j=0;j<i;j++)r-=lpc[j]*aut[i-j]; + r/=error; + + // Update LPC coefficients and total error + + lpc[i]=r; + for(j=0;j<i/2;j++){ + float tmp=lpc[j]; + lpc[j]+=r*lpc[i-1-j]; + lpc[i-1-j]+=r*tmp; + } + if(i%2!=0)lpc[j]+=lpc[j]*r; + + error*=1.0-r*r; + } + + // we need the error value to know how big an impulse to hit the + // filter with later + + return error; + } + + // Input : n element envelope spectral curve + // Output: m lpc coefficients, excitation energy + + float lpc_from_curve(float[] curve, float[] lpc){ + int n=ln; + float[] work=new float[n+n]; + float fscale=(float)(.5/n); + int i,j; + + // input is a real curve. make it complex-real + // This mixes phase, but the LPC generation doesn't care. + for(i=0;i<n;i++){ + work[i*2]=curve[i]*fscale; + work[i*2+1]=0; + } + work[n*2-1]=curve[n-1]*fscale; + + n*=2; + fft.backward(work); + + // The autocorrelation will not be circular. Shift, else we lose + // most of the power in the edges. + + for(i=0,j=n/2;i<n/2;){ + float temp=work[i]; + work[i++]=work[j]; + work[j++]=temp; + } + + return(lpc_from_data(work,lpc,n,m)); + } + + void init(int mapped, int m){ + //memset(l,0,sizeof(lpc_lookup)); + + ln=mapped; + this.m=m; + + // we cheat decoding the LPC spectrum via FFTs + fft.init(mapped*2); + } + + void clear(){ + fft.clear(); + } + + static float FAST_HYPOT(float a, float b){ + return (float)Math.sqrt((a)*(a) + (b)*(b)); + } + + // One can do this the long way by generating the transfer function in + // the time domain and taking the forward FFT of the result. The + // results from direct calculation are cleaner and faster. + // + // This version does a linear curve generation and then later + // interpolates the log curve from the linear curve. + + void lpc_to_curve(float[] curve, float[] lpc, float amp){ + + //memset(curve,0,sizeof(float)*l->ln*2); + for(int i=0; i<ln*2; i++)curve[i]=0.0f; + + if(amp==0)return; + + for(int i=0;i<m;i++){ + curve[i*2+1]=lpc[i]/(4*amp); + curve[i*2+2]=-lpc[i]/(4*amp); + } + + fft.backward(curve); // reappropriated ;-) + + { + int l2=ln*2; + float unit=(float)(1./amp); + curve[0]=(float)(1./(curve[0]*2+unit)); + for(int i=1;i<ln;i++){ + float real=(curve[i]+curve[l2-i]); + float imag=(curve[i]-curve[l2-i]); + + float a = real + unit; + curve[i] = (float)(1.0 / FAST_HYPOT(a, imag)); + } + } + } + +/* + // subtract or add an lpc filter to data. Vorbis doesn't actually use this. + + static void lpc_residue(float[] coeff, float[] prime,int m, + float[] data, int n){ + + // in: coeff[0...m-1] LPC coefficients + // prime[0...m-1] initial values + // data[0...n-1] data samples + // out: data[0...n-1] residuals from LPC prediction + + float[] work=new float[m+n]; + float y; + + if(prime==null){ + for(int i=0;i<m;i++){ + work[i]=0; + } + } + else{ + for(int i=0;i<m;i++){ + work[i]=prime[i]; + } + } + + for(int i=0;i<n;i++){ + y=0; + for(int j=0;j<m;j++){ + y-=work[i+j]*coeff[m-j-1]; + } + work[i+m]=data[i]; + data[i]-=y; + } + } + + static void lpc_predict(float[] coeff, float[] prime,int m, + float[] data, int n){ + + // in: coeff[0...m-1] LPC coefficients + // prime[0...m-1] initial values (allocated size of n+m-1) + // data[0...n-1] residuals from LPC prediction + // out: data[0...n-1] data samples + + int o,p; + float y; + float[] work=new float[m+n]; + + if(prime==null){ + for(int i=0;i<m;i++){ + work[i]=0.f; + } + } + else{ + for(int i=0;i<m;i++){ + work[i]=prime[i]; + } + } + + for(int i=0;i<n;i++){ + y=data[i]; + o=i; + p=m; + for(int j=0;j<m;j++){ + y-=work[o++]*coeff[--p]; + } + data[i]=work[o]=y; + } + } +*/ +} diff --git a/songdbj/com/jcraft/jorbis/Lsp.java b/songdbj/com/jcraft/jorbis/Lsp.java new file mode 100644 index 0000000000..550c7d62ee --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Lsp.java @@ -0,0 +1,111 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +/* + function: LSP (also called LSF) conversion routines + + The LSP generation code is taken (with minimal modification) from + "On the Computation of the LSP Frequencies" by Joseph Rothweiler + <rothwlr@altavista.net>, available at: + + http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html + ********************************************************************/ + +class Lsp{ + + static final float M_PI=(float)(3.1415926539); + + static void lsp_to_curve(float[] curve, + int[] map, int n, int ln, + float[] lsp, int m, + float amp, float ampoffset){ + int i; + float wdel=M_PI/ln; + for(i=0;i<m;i++)lsp[i]=Lookup.coslook(lsp[i]); + int m2=(m/2)*2; + + i=0; + while(i<n){ + int k=map[i]; + float p=.7071067812f; + float q=.7071067812f; + float w=Lookup.coslook(wdel*k); + int ftmp=0; + int c=m>>>1; + + for(int j=0;j<m2;j+=2){ + q*=lsp[j]-w; + p*=lsp[j+1]-w; + } + + if((m&1)!=0){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + q*=lsp[m-1]-w; + q*=q; + p*=p*(1.f-w*w); + } + else{ + /* even order filter; still symmetric */ + q*=q*(1.f+w); + p*=p*(1.f-w); + } + + // q=frexp(p+q,&qexp); + q=p+q; + int hx=Float.floatToIntBits(q); + int ix=0x7fffffff&hx; + int qexp=0; + + if(ix>=0x7f800000||(ix==0)){ + // 0,inf,nan + } + else{ + if(ix<0x00800000){ // subnormal + q*=3.3554432000e+07; // 0x4c000000 + hx=Float.floatToIntBits(q); + ix=0x7fffffff&hx; + qexp=-25; + } + qexp += ((ix>>>23)-126); + hx=(hx&0x807fffff)|0x3f000000; + q=Float.intBitsToFloat(hx); + } + + q=Lookup.fromdBlook(amp* + Lookup.invsqlook(q)* + Lookup.invsq2explook(qexp+m)-ampoffset); + + do{curve[i++]*=q;} +// do{curve[i++]=q;} + while(i<n&&map[i]==k); + + } + } +} + + diff --git a/songdbj/com/jcraft/jorbis/Mapping0.java b/songdbj/com/jcraft/jorbis/Mapping0.java new file mode 100644 index 0000000000..a2c3d06b5e --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Mapping0.java @@ -0,0 +1,566 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Mapping0 extends FuncMapping{ + static int seq=0; + void free_info(Object imap){}; + void free_look(Object imap){ +/* + LookMapping0 l=(LookMapping0)imap; + InfoMapping0 info=l.map; + if(l!=null){ + for(int i=0;i<l.map.submaps;i++){ + l.time_func[i].free_look(l.time_look[i]); + l.floor_func[i].free_look(l.floor_look[i]); + l.residue_func[i].free_look(l.residue_look[i]); + if(l.psy_look!=null)l.psy_look[i].clear(); + } + } + + if(l.floor_state!=null){ + for(int i=0;i<l.ch;i++) + l.floor_func[info.chmuxlist[i]].free_state(l.floor_state[i]); + //free(l.floor_state); + } + + if(l.decay!=null){ + for(int i=0;i<l.ch;i++){ + //if(l.decay[i])free(l->decay[i]); + l.decay[i]=null; + } + //free(l->decay); + l.decay=null; + } + //free(l->time_func); + //free(l->floor_func); + //free(l->residue_func); + //free(l->time_look); + //free(l->floor_look); + //free(l->residue_look); + //f(l->psy_look)free(l->psy_look); + l.time_func=null; + l.floor_func=null; + l.residue_func=null; + l.time_look=null; + l.floor_look=null; + l.residue_look=null; + //memset(l,0,sizeof(vorbis_look_mapping0)); + //free(l); +*/ + } + + Object look(DspState vd, InfoMode vm, Object m){ +//System.err.println("Mapping0.look"); + Info vi=vd.vi; + LookMapping0 look=new LookMapping0(); + InfoMapping0 info=look.map=(InfoMapping0)m; + look.mode=vm; + + look.time_look=new Object[info.submaps]; + look.floor_look=new Object[info.submaps]; + look.residue_look=new Object[info.submaps]; + +/* + if(vd.analysisp!=0){ + look.floor_state=new Object[vi.channels]; + } + if(vi.psys!=0){ + look.psy_look=new PsyLook[info.submaps]; + for(int i=0; i<info.submaps; i++){ look.psy_look[i]=new PsyLook(); } + } +*/ + + look.time_func=new FuncTime[info.submaps]; + look.floor_func=new FuncFloor[info.submaps]; + look.residue_func=new FuncResidue[info.submaps]; + + for(int i=0;i<info.submaps;i++){ + int timenum=info.timesubmap[i]; + int floornum=info.floorsubmap[i]; + int resnum=info.residuesubmap[i]; + + look.time_func[i]=FuncTime.time_P[vi.time_type[timenum]]; + look.time_look[i]=look.time_func[i].look(vd,vm,vi.time_param[timenum]); + look.floor_func[i]=FuncFloor.floor_P[vi.floor_type[floornum]]; + look.floor_look[i]=look.floor_func[i]. + look(vd,vm,vi.floor_param[floornum]); + look.residue_func[i]=FuncResidue.residue_P[vi.residue_type[resnum]]; + look.residue_look[i]=look.residue_func[i]. + look(vd,vm,vi.residue_param[resnum]); + +/* + if(vi.psys!=0 && vd.analysisp!=0){ + int psynum=info.psysubmap[i]; + look.psy_look[i].init(vi.psy_param[psynum], + vi.blocksizes[vm.blockflag]/2,vi.rate); + } +*/ + } + + if(vi.psys!=0 && vd.analysisp!=0){ + /* + if(info->psy[0] != info->psy[1]){ + + int psynum=info->psy[0]; + look->psy_look[0]=_ogg_calloc(1,sizeof(vorbis_look_psy)); + _vp_psy_init(look->psy_look[0],ci->psy_param[psynum], + ci->psy_g_param, + ci->blocksizes[vm->blockflag]/2,vi->rate); + + psynum=info->psy[1]; + look->psy_look[1]=_ogg_calloc(1,sizeof(vorbis_look_psy)); + _vp_psy_init(look->psy_look[1],ci->psy_param[psynum], + ci->psy_g_param, + ci->blocksizes[vm->blockflag]/2,vi->rate); + }else{ + + int psynum=info->psy[0]; + look->psy_look[0]=_ogg_calloc(1,sizeof(vorbis_look_psy)); + look->psy_look[1]=look->psy_look[0]; + _vp_psy_init(look->psy_look[0],ci->psy_param[psynum], + ci->psy_g_param, + ci->blocksizes[vm->blockflag]/2,vi->rate); + + } + */ + } + + look.ch=vi.channels; +// if(vd->analysisp)drft_init(&look->fft_look,ci->blocksizes[vm->blockflag]); + + return(look); +//return null; + } + + void pack(Info vi, Object imap, Buffer opb){ + InfoMapping0 info=(InfoMapping0)imap; + + /* another 'we meant to do it this way' hack... up to beta 4, we + packed 4 binary zeros here to signify one submapping in use. We + now redefine that to mean four bitflags that indicate use of + deeper features; bit0:submappings, bit1:coupling, + bit2,3:reserved. This is backward compatable with all actual uses + of the beta code. */ + + if(info.submaps>1){ + opb.write(1,1); + opb.write(info.submaps-1,4); + } + else{ + opb.write(0,1); + } + + if(info.coupling_steps>0){ + opb.write(1,1); + opb.write(info.coupling_steps-1,8); + for(int i=0;i<info.coupling_steps;i++){ + opb.write(info.coupling_mag[i],ilog2(vi.channels)); + opb.write(info.coupling_ang[i],ilog2(vi.channels)); + } + } + else{ + opb.write(0,1); + } + + opb.write(0,2); /* 2,3:reserved */ + + /* we don't write the channel submappings if we only have one... */ + if(info.submaps>1){ + for(int i=0;i<vi.channels;i++) + opb.write(info.chmuxlist[i],4); + } + for(int i=0;i<info.submaps;i++){ + opb.write(info.timesubmap[i],8); + opb.write(info.floorsubmap[i],8); + opb.write(info.residuesubmap[i],8); + } + } + + // also responsible for range checking + Object unpack(Info vi, Buffer opb){ + InfoMapping0 info=new InfoMapping0(); + + // !!!! + if(opb.read(1)!=0){ + info.submaps=opb.read(4)+1; + } + else{ + info.submaps=1; + } + + if(opb.read(1)!=0){ + info.coupling_steps=opb.read(8)+1; + + for(int i=0;i<info.coupling_steps;i++){ + int testM=info.coupling_mag[i]=opb.read(ilog2(vi.channels)); + int testA=info.coupling_ang[i]=opb.read(ilog2(vi.channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi.channels || + testA>=vi.channels){ + //goto err_out; + info.free(); + return(null); + } + } + } + + if(opb.read(2)>0){ /* 2,3:reserved */ + //goto err_out; + info.free(); + return(null); + } + + if(info.submaps>1){ + for(int i=0;i<vi.channels;i++){ + info.chmuxlist[i]=opb.read(4); + if(info.chmuxlist[i]>=info.submaps){ + //goto err_out; + info.free(); + return(null); + } + } + } + + for(int i=0;i<info.submaps;i++){ + info.timesubmap[i]=opb.read(8); + if(info.timesubmap[i]>=vi.times){ + //goto err_out; + info.free(); + return(null); + } + info.floorsubmap[i]=opb.read(8); + if(info.floorsubmap[i]>=vi.floors){ + //goto err_out; + info.free(); + return(null); + } + info.residuesubmap[i]=opb.read(8); + if(info.residuesubmap[i]>=vi.residues){ + //goto err_out; + info.free(); + return(null); + } + } + return info; + //err_out: + //free_info(info); + //return(NULL); + } + +/* + // no time mapping implementation for now + static int seq=0; + int forward(Block vb, Object l){ + DspState vd=vb.vd; + Info vi=vd.vi; + LookMapping0 look=(LookMapping0)l; + InfoMapping0 info=look.map; + InfoMode mode=look.mode; + int n=vb.pcmend; + float[] window=vd.window[vb.W][vb.lW][vb.nW][mode.windowtype]; + + float[][] pcmbundle=new float[vi.channles][]; + int[] nonzero=new int[vi.channels]; + + // time domain pre-window: NONE IMPLEMENTED + + // window the PCM data: takes PCM vector, vb; modifies PCM vector + + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + for(int j=0;j<n;j++) + pcm[j]*=window[j]; + } + + // time-domain post-window: NONE IMPLEMENTED + + // transform the PCM data; takes PCM vector, vb; modifies PCM vector + // only MDCT right now.... + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + mdct_forward(vd.transform[vb.W][0],pcm,pcm); + } + + { + float[] floor=_vorbis_block_alloc(vb,n*sizeof(float)/2); + + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + float[] decay=look.decay[i]; + int submap=info.chmuxlist[i]; + + // if some other mode/mapping was called last frame, our decay + // accumulator is out of date. Clear it. + //if(look.lastframe+1 != vb->sequence) + // memset(decay,0,n*sizeof(float)/2); + + // perform psychoacoustics; do masking + _vp_compute_mask(look.psy_look[submap],pcm,floor,decay); + + _analysis_output("mdct",seq,pcm,n/2,0,1); + _analysis_output("lmdct",seq,pcm,n/2,0,0); + _analysis_output("prefloor",seq,floor,n/2,0,1); + + // perform floor encoding + nonzero[i]=look.floor_func[submap]. + forward(vb,look.floor_look[submap],floor,floor,look.floor_state[i]); + + _analysis_output("floor",seq,floor,n/2,0,1); + + // apply the floor, do optional noise levelling + _vp_apply_floor(look->psy_look+submap,pcm,floor); + + _analysis_output("res",seq++,pcm,n/2,0,0); + } + + // perform residue encoding with residue mapping; this is + // multiplexed. All the channels belonging to one submap are + // encoded (values interleaved), then the next submap, etc + + for(int i=0;i<info.submaps;i++){ + int ch_in_bundle=0; + for(int j=0;j<vi.channels;j++){ + if(info.chmuxlist[j]==i && nonzero[j]==1){ + pcmbundle[ch_in_bundle++]=vb.pcm[j]; + } + } + look.residue_func[i].forward(vb,look.residue_look[i], pcmbundle,ch_in_bundle); + } + } + look.lastframe=vb.sequence; + return(0); + } +*/ + + float[][] pcmbundle=null; + int[] zerobundle=null; + int[] nonzero=null; + Object[] floormemo=null; + + synchronized int inverse(Block vb, Object l){ + //System.err.println("Mapping0.inverse"); + DspState vd=vb.vd; + Info vi=vd.vi; + LookMapping0 look=(LookMapping0)l; + InfoMapping0 info=look.map; + InfoMode mode=look.mode; + int n=vb.pcmend=vi.blocksizes[vb.W]; + + float[] window=vd.window[vb.W][vb.lW][vb.nW][mode.windowtype]; + // float[][] pcmbundle=new float[vi.channels][]; + // int[] nonzero=new int[vi.channels]; + if(pcmbundle==null || pcmbundle.length<vi.channels){ + pcmbundle=new float[vi.channels][]; + nonzero=new int[vi.channels]; + zerobundle=new int[vi.channels]; + floormemo=new Object[vi.channels]; + } + + // time domain information decode (note that applying the + // information would have to happen later; we'll probably add a + // function entry to the harness for that later + // NOT IMPLEMENTED + + // recover the spectral envelope; store it in the PCM vector for now + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + int submap=info.chmuxlist[i]; + + floormemo[i]=look.floor_func[submap].inverse1(vb,look. + floor_look[submap], + floormemo[i] + ); + if(floormemo[i]!=null){ nonzero[i]=1; } + else{ nonzero[i]=0; } + for(int j=0; j<n/2; j++){ + pcm[j]=0; + } + + //_analysis_output("ifloor",seq+i,pcm,n/2,0,1); + } + + for(int i=0; i<info.coupling_steps; i++){ + if(nonzero[info.coupling_mag[i]]!=0 || + nonzero[info.coupling_ang[i]]!=0){ + nonzero[info.coupling_mag[i]]=1; + nonzero[info.coupling_ang[i]]=1; + } + } + + // recover the residue, apply directly to the spectral envelope + + for(int i=0;i<info.submaps;i++){ + int ch_in_bundle=0; + for(int j=0;j<vi.channels;j++){ + if(info.chmuxlist[j]==i){ + if(nonzero[j]!=0){ + zerobundle[ch_in_bundle]=1; + } + else{ + zerobundle[ch_in_bundle]=0; + } + pcmbundle[ch_in_bundle++]=vb.pcm[j]; + } + } + + look.residue_func[i].inverse(vb,look.residue_look[i], + pcmbundle,zerobundle,ch_in_bundle); + } + + + for(int i=info.coupling_steps-1;i>=0;i--){ + float[] pcmM=vb.pcm[info.coupling_mag[i]]; + float[] pcmA=vb.pcm[info.coupling_ang[i]]; + + for(int j=0;j<n/2;j++){ + float mag=pcmM[j]; + float ang=pcmA[j]; + + if(mag>0){ + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + } + else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + } + else{ + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + } + else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + } + +// /* compute and apply spectral envelope */ + + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + int submap=info.chmuxlist[i]; + look.floor_func[submap].inverse2(vb,look.floor_look[submap],floormemo[i],pcm); + } + + // transform the PCM data; takes PCM vector, vb; modifies PCM vector + // only MDCT right now.... + + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + //_analysis_output("out",seq+i,pcm,n/2,0,0); + ((Mdct)vd.transform[vb.W][0]).backward(pcm,pcm); + } + + // now apply the decoded pre-window time information + // NOT IMPLEMENTED + + // window the data + for(int i=0;i<vi.channels;i++){ + float[] pcm=vb.pcm[i]; + if(nonzero[i]!=0){ + for(int j=0;j<n;j++){ + pcm[j]*=window[j]; + } + } + else{ + for(int j=0;j<n;j++){ + pcm[j]=0.f; + } + } + //_analysis_output("final",seq++,pcm,n,0,0); + } + + // now apply the decoded post-window time information + // NOT IMPLEMENTED + // all done! + return(0); + } + + + private static int ilog2(int v){ + int ret=0; + while(v>1){ + ret++; + v>>>=1; + } + return(ret); + } +} + +class InfoMapping0{ + int submaps; // <= 16 + int[] chmuxlist=new int[256]; // up to 256 channels in a Vorbis stream + + int[] timesubmap=new int[16]; // [mux] + int[] floorsubmap=new int[16]; // [mux] submap to floors + int[] residuesubmap=new int[16];// [mux] submap to residue + int[] psysubmap=new int[16]; // [mux]; encode only + + int coupling_steps; + int[] coupling_mag=new int[256]; + int[] coupling_ang=new int[256]; + + void free(){ + chmuxlist=null; + timesubmap=null; + floorsubmap=null; + residuesubmap=null; + psysubmap=null; + + coupling_mag=null; + coupling_ang=null; + } +} + +class LookMapping0{ + InfoMode mode; + InfoMapping0 map; + Object[] time_look; + Object[] floor_look; + Object[] floor_state; + Object[] residue_look; + PsyLook[] psy_look; + + FuncTime[] time_func; + FuncFloor[] floor_func; + FuncResidue[] residue_func; + + int ch; + float[][] decay; + int lastframe; // if a different mode is called, we need to + // invalidate decay and floor state +} diff --git a/songdbj/com/jcraft/jorbis/Mdct.java b/songdbj/com/jcraft/jorbis/Mdct.java new file mode 100644 index 0000000000..bd5cc38fb7 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Mdct.java @@ -0,0 +1,249 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class Mdct{ + + static private final float cPI3_8=0.38268343236508977175f; + static private final float cPI2_8=0.70710678118654752441f; + static private final float cPI1_8=0.92387953251128675613f; + + int n; + int log2n; + + float[] trig; + int[] bitrev; + + float scale; + + void init(int n){ + bitrev=new int[n/4]; + trig=new float[n+n/4]; + + int n2=n>>>1; + log2n=(int)Math.rint(Math.log(n)/Math.log(2)); + this.n=n; + + + int AE=0; + int AO=1; + int BE=AE+n/2; + int BO=BE+1; + int CE=BE+n/2; + int CO=CE+1; + // trig lookups... + for(int i=0;i<n/4;i++){ + trig[AE+i*2]=(float)Math.cos((Math.PI/n)*(4*i)); + trig[AO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i)); + trig[BE+i*2]=(float)Math.cos((Math.PI/(2*n))*(2*i+1)); + trig[BO+i*2]=(float)Math.sin((Math.PI/(2*n))*(2*i+1)); + } + for(int i=0;i<n/8;i++){ + trig[CE+i*2]=(float)Math.cos((Math.PI/n)*(4*i+2)); + trig[CO+i*2]=(float)-Math.sin((Math.PI/n)*(4*i+2)); + } + + { + int mask=(1<<(log2n-1))-1; + int msb=1<<(log2n-2); + for(int i=0;i<n/8;i++){ + int acc=0; + for(int j=0;msb>>>j!=0;j++) + if(((msb>>>j)&i)!=0)acc|=1<<j; + bitrev[i*2]=((~acc)&mask); +// bitrev[i*2]=((~acc)&mask)-1; + bitrev[i*2+1]=acc; + } + } + scale=4.f/n; + } + + void clear(){ + } + + void forward(float[] in, float[] out){ + } + + float[] _x=new float[1024]; + float[] _w=new float[1024]; + + synchronized void backward(float[] in, float[] out){ + if(_x.length<n/2){_x=new float[n/2];} + if(_w.length<n/2){_w=new float[n/2];} + float[] x=_x; + float[] w=_w; + int n2=n>>>1; + int n4=n>>>2; + int n8=n>>>3; + + // rotate + step 1 + { + int inO=1; + int xO=0; + int A=n2; + + int i; + for(i=0;i<n8;i++){ + A-=2; + x[xO++]=-in[inO+2]*trig[A+1] - in[inO]*trig[A]; + x[xO++]= in[inO]*trig[A+1] - in[inO+2]*trig[A]; + inO+=4; + } + + inO=n2-4; + + for(i=0;i<n8;i++){ + A-=2; + x[xO++]=in[inO]*trig[A+1] + in[inO+2]*trig[A]; + x[xO++]=in[inO]*trig[A] - in[inO+2]*trig[A+1]; + inO-=4; + } + } + + float[] xxx=mdct_kernel(x,w,n,n2,n4,n8); + int xx=0; + + // step 8 + + { + int B=n2; + int o1=n4,o2=o1-1; + int o3=n4+n2,o4=o3-1; + + for(int i=0;i<n4;i++){ + float temp1= (xxx[xx] * trig[B+1] - xxx[xx+1] * trig[B]); + float temp2=-(xxx[xx] * trig[B] + xxx[xx+1] * trig[B+1]); + + out[o1]=-temp1; + out[o2]= temp1; + out[o3]= temp2; + out[o4]= temp2; + + o1++; + o2--; + o3++; + o4--; + xx+=2; + B+=2; + } + } + } + private float[] mdct_kernel(float[] x, float[] w, + int n, int n2, int n4, int n8){ + // step 2 + + int xA=n4; + int xB=0; + int w2=n4; + int A=n2; + + for(int i=0;i<n4;){ + float x0=x[xA] - x[xB]; + float x1; + w[w2+i]=x[xA++]+x[xB++]; + + x1=x[xA]-x[xB]; + A-=4; + + w[i++]= x0 * trig[A] + x1 * trig[A+1]; + w[i]= x1 * trig[A] - x0 * trig[A+1]; + + w[w2+i]=x[xA++]+x[xB++]; + i++; + } + + // step 3 + + { + for(int i=0;i<log2n-3;i++){ + int k0=n>>>(i+2); + int k1=1<<(i+3); + int wbase=n2-2; + + A=0; + float[] temp; + + for(int r=0;r<(k0>>>2);r++){ + int w1=wbase; + w2=w1-(k0>>1); + float AEv= trig[A],wA; + float AOv= trig[A+1],wB; + wbase-=2; + + k0++; + for(int s=0;s<(2<<i);s++){ + wB =w[w1] -w[w2]; + x[w1] =w[w1] +w[w2]; + + wA =w[++w1] -w[++w2]; + x[w1] =w[w1] +w[w2]; + + x[w2] =wA*AEv - wB*AOv; + x[w2-1]=wB*AEv + wA*AOv; + + w1-=k0; + w2-=k0; + } + k0--; + A+=k1; + } + + temp=w; + w=x; + x=temp; + } + } + + // step 4, 5, 6, 7 + { + int C=n; + int bit=0; + int x1=0; + int x2=n2-1; + + for(int i=0;i<n8;i++){ + int t1=bitrev[bit++]; + int t2=bitrev[bit++]; + + float wA=w[t1]-w[t2+1]; + float wB=w[t1-1]+w[t2]; + float wC=w[t1]+w[t2+1]; + float wD=w[t1-1]-w[t2]; + + float wACE=wA* trig[C]; + float wBCE=wB* trig[C++]; + float wACO=wA* trig[C]; + float wBCO=wB* trig[C++]; + + x[x1++]=( wC+wACO+wBCE)*.5f; + x[x2--]=(-wD+wBCO-wACE)*.5f; + x[x1++]=( wD+wBCO-wACE)*.5f; + x[x2--]=( wC-wACO-wBCE)*.5f; + } + } + return(x); + } +} diff --git a/songdbj/com/jcraft/jorbis/PsyInfo.java b/songdbj/com/jcraft/jorbis/PsyInfo.java new file mode 100644 index 0000000000..599c41e52d --- /dev/null +++ b/songdbj/com/jcraft/jorbis/PsyInfo.java @@ -0,0 +1,72 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +// psychoacoustic setup +class PsyInfo{ + int athp; + int decayp; + int smoothp; + int noisefitp; + int noisefit_subblock; + float noisefit_threshdB; + + float ath_att; + + int tonemaskp; + float[] toneatt_125Hz=new float[5]; + float[] toneatt_250Hz=new float[5]; + float[] toneatt_500Hz=new float[5]; + float[] toneatt_1000Hz=new float[5]; + float[] toneatt_2000Hz=new float[5]; + float[] toneatt_4000Hz=new float[5]; + float[] toneatt_8000Hz=new float[5]; + + int peakattp; + float[] peakatt_125Hz=new float[5]; + float[] peakatt_250Hz=new float[5]; + float[] peakatt_500Hz=new float[5]; + float[] peakatt_1000Hz=new float[5]; + float[] peakatt_2000Hz=new float[5]; + float[] peakatt_4000Hz=new float[5]; + float[] peakatt_8000Hz=new float[5]; + + int noisemaskp; + float[] noiseatt_125Hz=new float[5]; + float[] noiseatt_250Hz=new float[5]; + float[] noiseatt_500Hz=new float[5]; + float[] noiseatt_1000Hz=new float[5]; + float[] noiseatt_2000Hz=new float[5]; + float[] noiseatt_4000Hz=new float[5]; + float[] noiseatt_8000Hz=new float[5]; + + float max_curve_dB; + + float attack_coeff; + float decay_coeff; + + void free(){} +} diff --git a/songdbj/com/jcraft/jorbis/PsyLook.java b/songdbj/com/jcraft/jorbis/PsyLook.java new file mode 100644 index 0000000000..9da85edfb1 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/PsyLook.java @@ -0,0 +1,187 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +class PsyLook { + int n; + PsyInfo vi; + + float[][][] tonecurves; + float[][] peakatt; + float[][][] noisecurves; + + float[] ath; + int[] octave; + + void init(PsyInfo vi, int n, int rate){ + /* + float rate2=rate/2.; + //memset(p,0,sizeof(vorbis_look_psy)); + ath=new float[n]; + octave=new int[n]; + this.vi=vi; + this.n=n; + + // set up the lookups for a given blocksize and sample rate + // Vorbis max sample rate is limited by 26 Bark (54kHz) + set_curve(ATH_Bark_dB, ath,n,rate); + for(int i=0;i<n;i++) + ath[i]=fromdB(ath[i]+vi.ath_att); + + for(int i=0;i<n;i++){ + int oc=rint(toOC((i+.5)*rate2/n)*2.); + if(oc<0)oc=0; + if(oc>12)oc=12; + octave[i]=oc; + } + + tonecurves=malloc(13*sizeof(float **)); + noisecurves=malloc(13*sizeof(float **)); + peakatt=malloc(7*sizeof(float *)); + for(int i=0;i<13;i++){ + tonecurves[i]=malloc(9*sizeof(float *)); + noisecurves[i]=malloc(9*sizeof(float *)); + } + for(i=0;i<7;i++) + peakatt[i]=malloc(5*sizeof(float)); + + for(i=0;i<13;i++){ + for(j=0;j<9;j++){ + tonecurves[i][j]=malloc(EHMER_MAX*sizeof(float)); + noisecurves[i][j]=malloc(EHMER_MAX*sizeof(float)); + } + } + + // OK, yeah, this was a silly way to do it + memcpy(tonecurves[0][2],tone_125_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[0][4],tone_125_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[0][6],tone_125_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[0][8],tone_125_100dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[2][2],tone_250_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[2][4],tone_250_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[2][6],tone_250_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[2][8],tone_250_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[4][2],tone_500_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[4][4],tone_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[4][6],tone_500_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[4][8],tone_500_100dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[6][2],tone_1000_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[6][4],tone_1000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[6][6],tone_1000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[6][8],tone_1000_100dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[8][2],tone_2000_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[8][4],tone_2000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[8][6],tone_2000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[8][8],tone_2000_100dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[10][2],tone_4000_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[10][4],tone_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[10][6],tone_4000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[10][8],tone_4000_100dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(tonecurves[12][2],tone_4000_40dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[12][4],tone_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[12][6],tone_8000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(tonecurves[12][8],tone_8000_100dB_SL,sizeof(float)*EHMER_MAX); + + + memcpy(noisecurves[0][2],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[0][4],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[0][6],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[0][8],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[2][2],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[2][4],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[2][6],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[2][8],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[4][2],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[4][4],noise_500_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[4][6],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[4][8],noise_500_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[6][2],noise_1000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[6][4],noise_1000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[6][6],noise_1000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[6][8],noise_1000_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[8][2],noise_2000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[8][4],noise_2000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[8][6],noise_2000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[8][8],noise_2000_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[10][2],noise_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[10][4],noise_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[10][6],noise_4000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[10][8],noise_4000_80dB_SL,sizeof(float)*EHMER_MAX); + + memcpy(noisecurves[12][2],noise_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[12][4],noise_4000_60dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[12][6],noise_4000_80dB_SL,sizeof(float)*EHMER_MAX); + memcpy(noisecurves[12][8],noise_4000_80dB_SL,sizeof(float)*EHMER_MAX); + + setup_curve(tonecurves[0],0,vi.toneatt_125Hz); + setup_curve(tonecurves[2],2,vi.toneatt_250Hz); + setup_curve(tonecurves[4],4,vi.toneatt_500Hz); + setup_curve(tonecurves[6],6,vi.toneatt_1000Hz); + setup_curve(tonecurves[8],8,vi.toneatt_2000Hz); + setup_curve(tonecurves[10],10,vi.toneatt_4000Hz); + setup_curve(tonecurves[12],12,vi.toneatt_8000Hz); + + setup_curve(noisecurves[0],0,vi.noiseatt_125Hz); + setup_curve(noisecurves[2],2,vi.noiseatt_250Hz); + setup_curve(noisecurves[4],4,vi.noiseatt_500Hz); + setup_curve(noisecurves[6],6,vi.noiseatt_1000Hz); + setup_curve(noisecurves[8],8,vi.noiseatt_2000Hz); + setup_curve(noisecurves[10],10,vi.noiseatt_4000Hz); + setup_curve(noisecurves[12],12,vi.noiseatt_8000Hz); + + for(i=1;i<13;i+=2){ + for(j=0;j<9;j++){ + interp_curve_dB(tonecurves[i][j], + tonecurves[i-1][j], + tonecurves[i+1][j],.5); + interp_curve_dB(noisecurves[i][j], + noisecurves[i-1][j], + noisecurves[i+1][j],.5); + } + } + for(i=0;i<5;i++){ + peakatt[0][i]=fromdB(vi.peakatt_125Hz[i]); + peakatt[1][i]=fromdB(vi.peakatt_250Hz[i]); + peakatt[2][i]=fromdB(vi.peakatt_500Hz[i]); + peakatt[3][i]=fromdB(vi.peakatt_1000Hz[i]); + peakatt[4][i]=fromdB(vi.peakatt_2000Hz[i]); + peakatt[5][i]=fromdB(vi.peakatt_4000Hz[i]); + peakatt[6][i]=fromdB(vi.peakatt_8000Hz[i]); + } + */ + } +} diff --git a/songdbj/com/jcraft/jorbis/Residue0.java b/songdbj/com/jcraft/jorbis/Residue0.java new file mode 100644 index 0000000000..be42518f1c --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Residue0.java @@ -0,0 +1,454 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Residue0 extends FuncResidue{ + void pack(Object vr, Buffer opb){ + InfoResidue0 info=(InfoResidue0)vr; + int acc=0; + opb.write(info.begin,24); + opb.write(info.end,24); + + opb.write(info.grouping-1,24); /* residue vectors to group and + code with a partitioned book */ + opb.write(info.partitions-1,6); /* possible partition choices */ + opb.write(info.groupbook,8); /* group huffman book */ + + /* secondstages is a bitmask; as encoding progresses pass by pass, a + bitmask of one indicates this partition class has bits to write + this pass */ + for(int j=0;j<info.partitions;j++){ + if(ilog(info.secondstages[j])>3){ + /* yes, this is a minor hack due to not thinking ahead */ + opb.write(info.secondstages[j],3); + opb.write(1,1); + opb.write(info.secondstages[j]>>>3,5); + } + else{ + opb.write(info.secondstages[j],4); /* trailing zero */ + } + acc+=icount(info.secondstages[j]); + } + for(int j=0;j<acc;j++){ + opb.write(info.booklist[j],8); + } + } + + Object unpack(Info vi, Buffer opb){ + int acc=0; + InfoResidue0 info=new InfoResidue0(); + + info.begin=opb.read(24); + info.end=opb.read(24); + info.grouping=opb.read(24)+1; + info.partitions=opb.read(6)+1; + info.groupbook=opb.read(8); + + for(int j=0;j<info.partitions;j++){ + int cascade=opb.read(3); + if(opb.read(1)!=0){ + cascade|=(opb.read(5)<<3); + } + info.secondstages[j]=cascade; + acc+=icount(cascade); + } + + for(int j=0;j<acc;j++){ + info.booklist[j]=opb.read(8); +// if(info.booklist[j]==255)info.booklist[j]=-1; + } + + if(info.groupbook>=vi.books){ + free_info(info); + return(null); + } + + for(int j=0;j<acc;j++){ + if(info.booklist[j]>=vi.books){ + free_info(info); + return(null); + } + } + return(info); +// errout: +// free_info(info); +// return(NULL); + } + + Object look(DspState vd, InfoMode vm, Object vr){ + InfoResidue0 info=(InfoResidue0)vr; + LookResidue0 look=new LookResidue0(); + int acc=0; + int dim; + int maxstage=0; + look.info=info; + look.map=vm.mapping; + + look.parts=info.partitions; + look.fullbooks=vd.fullbooks; + look.phrasebook=vd.fullbooks[info.groupbook]; + + dim=look.phrasebook.dim; + + look.partbooks=new int[look.parts][]; + + for(int j=0;j<look.parts;j++){ + int stages=ilog(info.secondstages[j]); + if(stages!=0){ + if(stages>maxstage)maxstage=stages; + look.partbooks[j]=new int[stages]; + for(int k=0; k<stages; k++){ + if((info.secondstages[j]&(1<<k))!=0){ + look.partbooks[j][k]=info.booklist[acc++]; + } + } + } + } + + look.partvals=(int)Math.rint(Math.pow(look.parts,dim)); + look.stages=maxstage; + look.decodemap=new int[look.partvals][]; + for(int j=0;j<look.partvals;j++){ + int val=j; + int mult=look.partvals/look.parts; + look.decodemap[j]=new int[dim]; + + for(int k=0;k<dim;k++){ + int deco=val/mult; + val-=deco*mult; + mult/=look.parts; + look.decodemap[j][k]=deco; + } + } + return(look); + } + void free_info(Object i){} + void free_look(Object i){} + int forward(Block vb,Object vl, float[][] in, int ch){ + System.err.println("Residue0.forward: not implemented"); + return 0; + } + + static int[][][] partword=new int[2][][]; // _01inverse is synchronized for + // re-using partword + synchronized static int _01inverse(Block vb, Object vl, + float[][] in,int ch,int decodepart){ + int i,j,k,l,s; + LookResidue0 look=(LookResidue0 )vl; + InfoResidue0 info=look.info; + + // move all this setup out later + int samples_per_partition=info.grouping; + int partitions_per_word=look.phrasebook.dim; + int n=info.end-info.begin; + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + + if(partword.length<ch){ + partword=new int[ch][][]; + for(j=0;j<ch;j++){ + partword[j]=new int[partwords][]; + } + } + else{ + for(j=0;j<ch;j++){ + if(partword[j]==null || partword[j].length<partwords) + partword[j]=new int[partwords][]; + } + } + + for(s=0;s<look.stages;s++){ + // each loop decodes on partition codeword containing + // partitions_pre_word partitions + for(i=0,l=0;i<partvals;l++){ + if(s==0){ + // fetch the partition word for each channel + for(j=0;j<ch;j++){ + int temp=look.phrasebook.decode(vb.opb); + if(temp==-1){ + //goto eopbreak; + return(0); + } + partword[j][l]=look.decodemap[temp]; + if(partword[j][l]==null){ +// goto errout; + return(0); + } + } + } + + // now we decode residual values for the partitions + for(k=0;k<partitions_per_word && i<partvals;k++,i++) + for(j=0;j<ch;j++){ + int offset=info.begin+i*samples_per_partition; + if((info.secondstages[partword[j][l][k]]&(1<<s))!=0){ + CodeBook stagebook=look.fullbooks[look.partbooks[partword[j][l][k]][s]]; +// CodeBook stagebook=look.partbooks[partword[j][l][k]][s]; + if(stagebook!=null){ + if(decodepart==0){ + if(stagebook.decodevs_add(in[j],offset,vb.opb,samples_per_partition)==-1){ + // goto errout; + return(0); + } + } + else if(decodepart==1){ + if(stagebook.decodev_add(in[j], offset, vb.opb,samples_per_partition)==-1){ + // goto errout; + return(0); + } + } + } + } + } + } + } +// errout: +// eopbreak: + return(0); + } + + static int _2inverse(Block vb, Object vl, float[][] in, int ch){ + int i,j,k,l,s; + LookResidue0 look=(LookResidue0 )vl; + InfoResidue0 info=look.info; + + // move all this setup out later + int samples_per_partition=info.grouping; + int partitions_per_word=look.phrasebook.dim; + int n=info.end-info.begin; + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + + int[][] partword=new int[partwords][]; + for(s=0;s<look.stages;s++){ + for(i=0,l=0;i<partvals;l++){ + if(s==0){ + // fetch the partition word for each channel + int temp=look.phrasebook.decode(vb.opb); + if(temp==-1){ + // goto eopbreak; + return(0); + } + partword[l]=look.decodemap[temp]; + if(partword[l]==null){ + // goto errout; + return(0); + } + } + + // now we decode residual values for the partitions + for(k=0;k<partitions_per_word && i<partvals;k++,i++){ + int offset=info.begin+i*samples_per_partition; + if((info.secondstages[partword[l][k]]&(1<<s))!=0){ + CodeBook stagebook=look.fullbooks[look.partbooks[partword[l][k]][s]]; + if(stagebook!=null){ + if(stagebook.decodevv_add(in, offset, ch, vb.opb,samples_per_partition)==-1){ + // goto errout; + return(0); + } + } + } + } + } + } +// errout: +// eopbreak: + return(0); + } + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){ + //System.err.println("Residue0.inverse"); + int used=0; + for(int i=0;i<ch;i++){ + if(nonzero[i]!=0){ + in[used++]=in[i]; + } + } + if(used!=0) + return(_01inverse(vb,vl,in,used,0)); + else + return(0); + } + +/* + int inverse(Block vb, Object vl, float[][] in, int ch){ +//System.err.println("Residue0.inverse"); + int i,j,k,l,transend=vb.pcmend/2; + LookResidue0 look=(LookResidue0 )vl; + InfoResidue0 info=look.info; + + // move all this setup out later + int samples_per_partition=info.grouping; + int partitions_per_word=look.phrasebook.dim; + int n=info.end-info.begin; + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int[][] partword=new int[ch][]; + float[] work=new float[samples_per_partition]; + partvals=partwords*partitions_per_word; + + // make sure we're zeroed up to the start + for(j=0;j<ch;j++){ + for(k=0; k<info.begin; k++)in[j][k]=0.0f; + } + + for(i=info.begin,l=0;i<info.end;){ + // fetch the partition word for each channel + for(j=0;j<ch;j++){ + int temp=look.phrasebook.decode(vb.opb); + if(temp==-1){ + //goto eopbreak; + if(i<transend){ + for(j=0;j<ch;j++){ + for(k=0;k<transend-i;k++)in[j][i+k]=0.0f; + } + } + return(0); + } + partword[j]=look.decodemap[temp]; + if(partword[j]==null){ + //goto errout; + for(j=0;j<ch;j++){ + for(k=0;k<transend;k++)in[j][k]=0.0f; + } + return(0); + } + } + + // now we decode interleaved residual values for the partitions + for(k=0;k<partitions_per_word;k++,l++,i+=samples_per_partition){ + for(j=0;j<ch;j++){ + int part=partword[j][k]; + if(decodepart(vb.opb,work, in[j], i,samples_per_partition, + info.secondstages[part], + look.partbooks[part])==-1){ + //goto eopbreak; + if(i<transend){ + for(j=0;j<ch;j++){ + for(k=0;k<transend-i;k++)in[j][i+k]=0.0f; + } + } + return(0); + } + } + } + } + +// eopbreak: + if(i<transend){ + for(j=0;j<ch;j++){ + for(k=0;k<transend-i;k++)in[j][i+k]=0.0f; + } + } + return(0); + +// errout: +// for(j=0;j<ch;j++) +// for(k=0;k<transend;k++)in[j][k]=0.0f; +// return(0); + } + int decodepart(Buffer opb, float[] work, float[] vec, int veci, + int n, int stages, CodeBook[] books){ + int i,j; + for(i=0;i<n;i++)work[i]=0.0f; + + for(j=0;j<stages;j++){ + int dim=books[j].dim; + int step=n/dim; + for(i=0;i<step;i++){ + if(books[j].decodevs(work, i, opb, step, 0)==-1){ + return(-1); + } + } + } + for(i=0;i<n;i++){ + vec[veci+i]*=work[i]; + } + return(0); + } +*/ + + private static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return(ret); + } + private static int icount(int v){ + int ret=0; + while(v!=0){ + ret+=(v&1); + v>>>=1; + } + return(ret); + } +} + +class LookResidue0 { + InfoResidue0 info; + int map; + + int parts; + int stages; + CodeBook[] fullbooks; + CodeBook phrasebook; + int[][] partbooks; +// CodeBook[][] partbooks; + + int partvals; + int[][] decodemap; + + int postbits; + int phrasebits; +// int[][] frames; + int frames; +} + +class InfoResidue0{ + // block-partitioned VQ coded straight residue + int begin; + int end; + + // first stage (lossless partitioning) + int grouping; // group n vectors per partition + int partitions; // possible codebooks for a partition + int groupbook; // huffbook for partitioning + int[] secondstages=new int[64]; // expanded out to pointers in lookup + int[] booklist=new int[256]; // list of second stage books + + // encode-only heuristic settings + float[] entmax=new float[64]; // book entropy threshholds + float[] ampmax=new float[64]; // book amp threshholds + int[] subgrp=new int[64]; // book heuristic subgroup size + int[] blimit=new int[64]; // subgroup position limits +} diff --git a/songdbj/com/jcraft/jorbis/Residue1.java b/songdbj/com/jcraft/jorbis/Residue1.java new file mode 100644 index 0000000000..c29ed8d671 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Residue1.java @@ -0,0 +1,51 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Residue1 extends Residue0{ + int forward(Block vb,Object vl, float[][] in, int ch){ + System.err.println("Residue0.forward: not implemented"); + return 0; + } + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){ +//System.err.println("Residue0.inverse"); + int used=0; + for(int i=0; i<ch; i++){ + if(nonzero[i]!=0){ + in[used++]=in[i]; + } + } + if(used!=0){ + return(_01inverse(vb,vl,in,used,1)); + } + else{ + return 0; + } + } +} diff --git a/songdbj/com/jcraft/jorbis/Residue2.java b/songdbj/com/jcraft/jorbis/Residue2.java new file mode 100644 index 0000000000..146a8341e5 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Residue2.java @@ -0,0 +1,44 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Residue2 extends Residue0{ + int forward(Block vb,Object vl, float[][] in, int ch){ + System.err.println("Residue0.forward: not implemented"); + return 0; + } + + int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch){ +//System.err.println("Residue0.inverse"); + int i=0; + for(i=0;i<ch;i++)if(nonzero[i]!=0)break; + if(i==ch)return(0); /* no nonzero vectors */ + + return(_2inverse(vb,vl,in, ch)); + } +} diff --git a/songdbj/com/jcraft/jorbis/StaticCodeBook.java b/songdbj/com/jcraft/jorbis/StaticCodeBook.java new file mode 100644 index 0000000000..7d9d6dc232 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/StaticCodeBook.java @@ -0,0 +1,588 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class StaticCodeBook{ + int dim; // codebook dimensions (elements per vector) + int entries; // codebook entries + int[] lengthlist; // codeword lengths in bits + + // mapping + int maptype; // 0=none + // 1=implicitly populated values from map column + // 2=listed arbitrary values + + // The below does a linear, single monotonic sequence mapping. + int q_min; // packed 32 bit float; quant value 0 maps to minval + int q_delta; // packed 32 bit float; val 1 - val 0 == delta + int q_quant; // bits: 0 < quant <= 16 + int q_sequencep; // bitflag + + // additional information for log (dB) mapping; the linear mapping + // is assumed to actually be values in dB. encodebias is used to + // assign an error weight to 0 dB. We have two additional flags: + // zeroflag indicates if entry zero is to represent -Inf dB; negflag + // indicates if we're to represent negative linear values in a + // mirror of the positive mapping. + + int[] quantlist; // map == 1: (int)(entries/dim) element column map + // map == 2: list of dim*entries quantized entry vals + + // encode helpers + EncodeAuxNearestMatch nearest_tree; + EncodeAuxThreshMatch thresh_tree; + + StaticCodeBook(){} + StaticCodeBook(int dim, int entries, int[] lengthlist, + int maptype, int q_min, int q_delta, + int q_quant, int q_sequencep, int[] quantlist, + //EncodeAuxNearestmatch nearest_tree, + Object nearest_tree, + // EncodeAuxThreshmatch thresh_tree, + Object thresh_tree + ){ + this(); + this.dim=dim; this.entries=entries; this.lengthlist=lengthlist; + this.maptype=maptype; this.q_min=q_min; this.q_delta=q_delta; + this.q_quant=q_quant; this.q_sequencep=q_sequencep; + this.quantlist=quantlist; + } + + int pack(Buffer opb){ + int i; + boolean ordered=false; + + opb.write(0x564342,24); + opb.write(dim, 16); + opb.write(entries, 24); + + // pack the codewords. There are two packings; length ordered and + // length random. Decide between the two now. + + for(i=1;i<entries;i++){ + if(lengthlist[i]<lengthlist[i-1])break; + } + if(i==entries)ordered=true; + + if(ordered){ + // length ordered. We only need to say how many codewords of + // each length. The actual codewords are generated + // deterministically + + int count=0; + opb.write(1,1); // ordered + opb.write(lengthlist[0]-1,5); // 1 to 32 + + for(i=1;i<entries;i++){ + int _this=lengthlist[i]; + int _last=lengthlist[i-1]; + if(_this>_last){ + for(int j=_last;j<_this;j++){ + opb.write(i-count,ilog(entries-count)); + count=i; + } + } + } + opb.write(i-count,ilog(entries-count)); + } + else{ + // length random. Again, we don't code the codeword itself, just + // the length. This time, though, we have to encode each length + opb.write(0,1); // unordered + + // algortihmic mapping has use for 'unused entries', which we tag + // here. The algorithmic mapping happens as usual, but the unused + // entry has no codeword. + for(i=0;i<entries;i++){ + if(lengthlist[i]==0)break; + } + + if(i==entries){ + opb.write(0,1); // no unused entries + for(i=0;i<entries;i++){ + opb.write(lengthlist[i]-1,5); + } + } + else{ + opb.write(1,1); // we have unused entries; thus we tag + for(i=0;i<entries;i++){ + if(lengthlist[i]==0){ + opb.write(0,1); + } + else{ + opb.write(1,1); + opb.write(lengthlist[i]-1,5); + } + } + } + } + + // is the entry number the desired return value, or do we have a + // mapping? If we have a mapping, what type? + opb.write(maptype,4); + switch(maptype){ + case 0: + // no mapping + break; + case 1: + case 2: + // implicitly populated value mapping + // explicitly populated value mapping + if(quantlist==null){ + // no quantlist? error + return(-1); + } + + // values that define the dequantization + opb.write(q_min,32); + opb.write(q_delta,32); + opb.write(q_quant-1,4); + opb.write(q_sequencep,1); + + { + int quantvals=0; + switch(maptype){ + case 1: + // a single column of (c->entries/c->dim) quantized values for + // building a full value list algorithmically (square lattice) + quantvals=maptype1_quantvals(); + break; + case 2: + // every value (c->entries*c->dim total) specified explicitly + quantvals=entries*dim; + break; + } + + // quantized values + for(i=0;i<quantvals;i++){ + opb.write(Math.abs(quantlist[i]),q_quant); + } + } + break; + default: + // error case; we don't have any other map types now + return(-1); + } + return(0); + } +/* +*/ + + // unpacks a codebook from the packet buffer into the codebook struct, + // readies the codebook auxiliary structures for decode + int unpack(Buffer opb){ + int i; + //memset(s,0,sizeof(static_codebook)); + + // make sure alignment is correct + if(opb.read(24)!=0x564342){ +// goto _eofout; + clear(); + return(-1); + } + + // first the basic parameters + dim=opb.read(16); + entries=opb.read(24); + if(entries==-1){ +// goto _eofout; + clear(); + return(-1); + } + + // codeword ordering.... length ordered or unordered? + switch(opb.read(1)){ + case 0: + // unordered + lengthlist=new int[entries]; + + // allocated but unused entries? + if(opb.read(1)!=0){ + // yes, unused entries + + for(i=0;i<entries;i++){ + if(opb.read(1)!=0){ + int num=opb.read(5); + if(num==-1){ +// goto _eofout; + clear(); + return(-1); + } + lengthlist[i]=num+1; + } + else{ + lengthlist[i]=0; + } + } + } + else{ + // all entries used; no tagging + for(i=0;i<entries;i++){ + int num=opb.read(5); + if(num==-1){ +// goto _eofout; + clear(); + return(-1); + } + lengthlist[i]=num+1; + } + } + break; + case 1: + // ordered + { + int length=opb.read(5)+1; + lengthlist=new int[entries]; + + for(i=0;i<entries;){ + int num=opb.read(ilog(entries-i)); + if(num==-1){ +// goto _eofout; + clear(); + return(-1); + } + for(int j=0;j<num;j++,i++){ + lengthlist[i]=length; + } + length++; + } + } + break; + default: + // EOF + return(-1); + } + + // Do we have a mapping to unpack? + switch((maptype=opb.read(4))){ + case 0: + // no mapping + break; + case 1: + case 2: + // implicitly populated value mapping + // explicitly populated value mapping + q_min=opb.read(32); + q_delta=opb.read(32); + q_quant=opb.read(4)+1; + q_sequencep=opb.read(1); + + { + int quantvals=0; + switch(maptype){ + case 1: + quantvals=maptype1_quantvals(); + break; + case 2: + quantvals=entries*dim; + break; + } + + // quantized values + quantlist=new int[quantvals]; + for(i=0;i<quantvals;i++){ + quantlist[i]=opb.read(q_quant); + } + if(quantlist[quantvals-1]==-1){ +// goto _eofout; + clear(); + return(-1); + } + } + break; + default: +// goto _eofout; + clear(); + return(-1); + } + // all set + return(0); +// _errout: +// _eofout: +// vorbis_staticbook_clear(s); +// return(-1); + } + + // there might be a straightforward one-line way to do the below + // that's portable and totally safe against roundoff, but I haven't + // thought of it. Therefore, we opt on the side of caution + private int maptype1_quantvals(){ + int vals=(int)(Math.floor(Math.pow(entries,1./dim))); + + // the above *should* be reliable, but we'll not assume that FP is + // ever reliable when bitstream sync is at stake; verify via integer + // means that vals really is the greatest value of dim for which + // vals^b->bim <= b->entries + // treat the above as an initial guess + while(true){ + int acc=1; + int acc1=1; + for(int i=0;i<dim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=entries && acc1>entries){ return(vals); } + else{ + if(acc>entries){ vals--; } + else{ vals++; } + } + } + } + + void clear(){ +// if(quantlist!=null)free(b->quantlist); +// if(lengthlist!=null)free(b->lengthlist); +// if(nearest_tree!=null){ +// free(b->nearest_tree->ptr0); +// free(b->nearest_tree->ptr1); +// free(b->nearest_tree->p); +// free(b->nearest_tree->q); +// memset(b->nearest_tree,0,sizeof(encode_aux_nearestmatch)); +// free(b->nearest_tree); +// } +// if(thresh_tree!=null){ +// free(b->thresh_tree->quantthresh); +// free(b->thresh_tree->quantmap); +// memset(b->thresh_tree,0,sizeof(encode_aux_threshmatch)); +// free(b->thresh_tree); +// } +// memset(b,0,sizeof(static_codebook)); + } + + // unpack the quantized list of values for encode/decode + // we need to deal with two map types: in map type 1, the values are + // generated algorithmically (each column of the vector counts through + // the values in the quant vector). in map type 2, all the values came + // in in an explicit list. Both value lists must be unpacked + float[] unquantize(){ + + if(maptype==1 || maptype==2){ + int quantvals; + float mindel=float32_unpack(q_min); + float delta=float32_unpack(q_delta); + float[] r=new float[entries*dim]; + + //System.err.println("q_min="+q_min+", mindel="+mindel); + + // maptype 1 and 2 both use a quantized value vector, but + // different sizes + switch(maptype){ + case 1: + // most of the time, entries%dimensions == 0, but we need to be + // well defined. We define that the possible vales at each + // scalar is values == entries/dim. If entries%dim != 0, we'll + // have 'too few' values (values*dim<entries), which means that + // we'll have 'left over' entries; left over entries use zeroed + // values (and are wasted). So don't generate codebooks like that + quantvals=maptype1_quantvals(); + for(int j=0;j<entries;j++){ + float last=0.f; + int indexdiv=1; + for(int k=0;k<dim;k++){ + int index=(j/indexdiv)%quantvals; + float val=quantlist[index]; + val=Math.abs(val)*delta+mindel+last; + if(q_sequencep!=0)last=val; + r[j*dim+k]=val; + indexdiv*=quantvals; + } + } + break; + case 2: + for(int j=0;j<entries;j++){ + float last=0.f; + for(int k=0;k<dim;k++){ + float val=quantlist[j*dim+k]; +//if((j*dim+k)==0){System.err.println(" | 0 -> "+val+" | ");} + val=Math.abs(val)*delta+mindel+last; + if(q_sequencep!=0)last=val; + r[j*dim+k]=val; +//if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");} + } + } +//System.err.println("\nr[0]="+r[0]); + } + return(r); + } + return(null); + } + + private static int ilog(int v){ + int ret=0; + while(v!=0){ + ret++; + v>>>=1; + } + return(ret); + } + + // 32 bit float (not IEEE; nonnormalized mantissa + + // biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + // Why not IEEE? It's just not that important here. + + static final int VQ_FEXP=10; + static final int VQ_FMAN=21; + static final int VQ_FEXP_BIAS=768; // bias toward values smaller than 1. + + // doesn't currently guard under/overflow + static long float32_pack(float val){ + int sign=0; + int exp; + int mant; + if(val<0){ + sign=0x80000000; + val= -val; + } + exp=(int)Math.floor(Math.log(val)/Math.log(2)); + mant=(int)Math.rint(Math.pow(val,(VQ_FMAN-1)-exp)); + exp=(exp+VQ_FEXP_BIAS)<<VQ_FMAN; + return(sign|exp|mant); + } + + static float float32_unpack(int val){ + float mant=val&0x1fffff; + float sign=val&0x80000000; + float exp =(val&0x7fe00000)>>>VQ_FMAN; +//System.err.println("mant="+mant+", sign="+sign+", exp="+exp); + //if(sign!=0.0)mant= -mant; + if((val&0x80000000)!=0)mant= -mant; +//System.err.println("mant="+mant); + return(ldexp(mant,((int)exp)-(VQ_FMAN-1)-VQ_FEXP_BIAS)); + } + + static float ldexp(float foo, int e){ + return (float)(foo*Math.pow(2, e)); + } + +/* + // TEST + // Unit tests of the dequantizer; this stuff will be OK + // cross-platform, I simply want to be sure that special mapping cases + // actually work properly; a bug could go unnoticed for a while + + // cases: + // + // no mapping + // full, explicit mapping + // algorithmic mapping + // + // nonsequential + // sequential + + static int[] full_quantlist1={0,1,2,3, 4,5,6,7, 8,3,6,1}; + static int[] partial_quantlist1={0,7,2}; + + // no mapping + static StaticCodeBook test1=new StaticCodeBook(4,16,null, + 0,0,0,0,0, + null,null,null); + static float[] test1_result=null; + + // linear, full mapping, nonsequential + static StaticCodeBook test2=new StaticCodeBook(4,3,null, + 2,-533200896,1611661312,4,0, + full_quantlist1, null, null); + static float[] test2_result={-3,-2,-1,0, 1,2,3,4, 5,0,3,-2}; + + // linear, full mapping, sequential + static StaticCodeBook test3=new StaticCodeBook(4,3,null, + 2, -533200896,1611661312,4,1, + full_quantlist1,null, null); + static float[] test3_result={-3,-5,-6,-6, 1,3,6,10, 5,5,8,6}; + + // linear, algorithmic mapping, nonsequential + static StaticCodeBook test4=new StaticCodeBook(3,27,null, + 1,-533200896,1611661312,4,0, + partial_quantlist1,null,null); + static float[] test4_result={-3,-3,-3, 4,-3,-3, -1,-3,-3, + -3, 4,-3, 4, 4,-3, -1, 4,-3, + -3,-1,-3, 4,-1,-3, -1,-1,-3, + -3,-3, 4, 4,-3, 4, -1,-3, 4, + -3, 4, 4, 4, 4, 4, -1, 4, 4, + -3,-1, 4, 4,-1, 4, -1,-1, 4, + -3,-3,-1, 4,-3,-1, -1,-3,-1, + -3, 4,-1, 4, 4,-1, -1, 4,-1, + -3,-1,-1, 4,-1,-1, -1,-1,-1}; + + // linear, algorithmic mapping, sequential + static StaticCodeBook test5=new StaticCodeBook(3,27,null, + 1,-533200896,1611661312,4,1, + partial_quantlist1,null,null); + static float[] test5_result={-3,-6,-9, 4, 1,-2, -1,-4,-7, + -3, 1,-2, 4, 8, 5, -1, 3, 0, + -3,-4,-7, 4, 3, 0, -1,-2,-5, + -3,-6,-2, 4, 1, 5, -1,-4, 0, + -3, 1, 5, 4, 8,12, -1, 3, 7, + -3,-4, 0, 4, 3, 7, -1,-2, 2, + -3,-6,-7, 4, 1, 0, -1,-4,-5, + -3, 1, 0, 4, 8, 7, -1, 3, 2, + -3,-4,-5, 4, 3, 2, -1,-2,-3}; + + void run_test(float[] comp){ + float[] out=unquantize(); + if(comp!=null){ + if(out==null){ + System.err.println("_book_unquantize incorrectly returned NULL"); + System.exit(1); + } + for(int i=0;i<entries*dim;i++){ + if(Math.abs(out[i]-comp[i])>.0001){ + System.err.println("disagreement in unquantized and reference data:\nposition "+i+": "+out[i]+" != "+comp[i]); + System.exit(1); + } + } + } + else{ + if(out!=null){ + System.err.println("_book_unquantize returned a value array:\n correct result should have been NULL"); + System.exit(1); + } + } + } + + public static void main(String[] arg){ + // run the nine dequant tests, and compare to the hand-rolled results + System.err.print("Dequant test 1... "); + test1.run_test(test1_result); + System.err.print("OK\nDequant test 2... "); + test2.run_test(test2_result); + System.err.print("OK\nDequant test 3... "); + test3.run_test(test3_result); + System.err.print("OK\nDequant test 4... "); + test4.run_test(test4_result); + System.err.print("OK\nDequant test 5... "); + test5.run_test(test5_result); + System.err.print("OK\n\n"); + } +*/ +} + + + + + diff --git a/songdbj/com/jcraft/jorbis/Time0.java b/songdbj/com/jcraft/jorbis/Time0.java new file mode 100644 index 0000000000..f6a9fcb077 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/Time0.java @@ -0,0 +1,38 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +class Time0 extends FuncTime{ + void pack(Object i, Buffer opb){} + Object unpack(Info vi , Buffer opb){return "";} + Object look(DspState vd, InfoMode mi, Object i){return "";} + void free_info(Object i){} + void free_look(Object i){} + int forward(Block vb, Object i){return 0;} + int inverse(Block vb, Object i, float[] in, float[] out){return 0;} +} diff --git a/songdbj/com/jcraft/jorbis/VorbisFile.java b/songdbj/com/jcraft/jorbis/VorbisFile.java new file mode 100644 index 0000000000..64edff006e --- /dev/null +++ b/songdbj/com/jcraft/jorbis/VorbisFile.java @@ -0,0 +1,1361 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; + +import java.io.InputStream; +import java.io.IOException; + +public class VorbisFile{ + static final int CHUNKSIZE=8500; + static final int SEEK_SET=0; + static final int SEEK_CUR=1; + static final int SEEK_END=2; + + static final int OV_FALSE=-1; + static final int OV_EOF=-2; + static final int OV_HOLE=-3; + + static final int OV_EREAD=-128; + static final int OV_EFAULT=-129; + static final int OV_EIMPL=-130; + static final int OV_EINVAL=-131; + static final int OV_ENOTVORBIS=-132; + static final int OV_EBADHEADER=-133; + static final int OV_EVERSION=-134; + static final int OV_ENOTAUDIO=-135; + static final int OV_EBADPACKET=-136; + static final int OV_EBADLINK=-137; + static final int OV_ENOSEEK=-138; + + InputStream datasource; + boolean seekable=false; + long offset; + long end; + + SyncState oy=new SyncState(); + + int links; + long[] offsets; + long[] dataoffsets; + int[] serialnos; + long[] pcmlengths; + Info[] vi; + Comment[] vc; + + // Decoding working state local storage + long pcm_offset; + boolean decode_ready=false; + int current_serialno; + int current_link; + + float bittrack; + float samptrack; + + StreamState os=new StreamState(); // take physical pages, weld into a logical + // stream of packets + DspState vd=new DspState(); // central working state for + // the packet->PCM decoder + Block vb=new Block(vd); // local working space for packet->PCM decode + + //ov_callbacks callbacks; + + public VorbisFile(String file) throws JOrbisException { + super(); + InputStream is=null; + try{ + is=new SeekableInputStream(file); + int ret=open(is, null, 0); + if(ret==-1){ + throw new JOrbisException("VorbisFile: open return -1"); + } + } + catch(Exception e){ + throw new JOrbisException("VorbisFile: "+e.toString()); + } + finally{ + if(is != null){ + try { + is.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public VorbisFile(InputStream is, byte[] initial, int ibytes) + throws JOrbisException { + super(); + int ret=open(is, initial, ibytes); + if(ret==-1){ + } + } + + private int get_data(){ + int index=oy.buffer(CHUNKSIZE); + byte[] buffer=oy.data; +// int bytes=callbacks.read_func(buffer, index, 1, CHUNKSIZE, datasource); + int bytes=0; + try{ + bytes=datasource.read(buffer, index, CHUNKSIZE); + } + catch(Exception e){ + //System.err.println(e); + return OV_EREAD; + } + oy.wrote(bytes); + if(bytes==-1){ +// System.out.println("bytes="+bytes); + bytes=0; + } + return bytes; + } + + private void seek_helper(long offst){ + //callbacks.seek_func(datasource, offst, SEEK_SET); + fseek(datasource, offst, SEEK_SET); + this.offset=offst; + oy.reset(); + } + + private int get_next_page(Page page, long boundary){ + if(boundary>0) boundary+=offset; + while(true){ + int more; + if(boundary>0 && offset>=boundary)return OV_FALSE; + more=oy.pageseek(page); + if(more<0){offset-=more;} + else{ + if(more==0){ + if(boundary==0)return OV_FALSE; +// if(get_data()<=0)return -1; + int ret=get_data(); + if(ret==0) return OV_EOF; + if(ret<0) return OV_EREAD; + } + else{ + int ret=(int)offset; //!!! + offset+=more; + return ret; + } + } + } + } + + private int get_prev_page(Page page) throws JOrbisException { + long begin=offset; //!!! + int ret; + int offst=-1; + while(offst==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + seek_helper(begin); + while(offset<begin+CHUNKSIZE){ + ret=get_next_page(page, begin+CHUNKSIZE-offset); + if(ret==OV_EREAD){ return OV_EREAD; } + if(ret<0){ + if(offst == -1) + throw new JOrbisException(); + break; + } + else{ offst=ret; } + } + } + seek_helper(offst); //!!! + ret=get_next_page(page, CHUNKSIZE); + if(ret<0){ + //System.err.println("Missed page fencepost at end of logical bitstream Exiting"); + //System.exit(1); + return OV_EFAULT; + } + return offst; + } + + int bisect_forward_serialno(long begin, long searched, long end, int currentno, int m){ + long endsearched=end; + long next=end; + Page page=new Page(); + int ret; + + while(searched<endsearched){ + long bisect; + if(endsearched-searched<CHUNKSIZE){ + bisect=searched; + } + else{ + bisect=(searched+endsearched)/2; + } + + seek_helper(bisect); + ret=get_next_page(page, -1); + if(ret==OV_EREAD) return OV_EREAD; + if(ret<0 || page.serialno()!=currentno){ + endsearched=bisect; + if(ret>=0)next=ret; + } + else{ + searched=ret+page.header_len+page.body_len; + } + } + seek_helper(next); + ret=get_next_page(page, -1); + if(ret==OV_EREAD) return OV_EREAD; + + if(searched>=end || ret==-1){ + links=m+1; + offsets=new long[m+2]; + offsets[m+1]=searched; + } + else{ + ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1); + if(ret==OV_EREAD)return OV_EREAD; + } + offsets[m]=begin; + return 0; + } + + // uses the local ogg_stream storage in vf; this is important for + // non-streaming input sources + int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr){ + //System.err.println("fetch_headers"); + Page og=new Page(); + Packet op=new Packet(); + int ret; + + if(og_ptr==null){ + ret=get_next_page(og, CHUNKSIZE); + if(ret==OV_EREAD)return OV_EREAD; + if(ret<0) return OV_ENOTVORBIS; + og_ptr=og; + } + + if(serialno!=null)serialno[0]=og_ptr.serialno(); + + os.init(og_ptr.serialno()); + + // extract the initial header from the first page and verify that the + // Ogg bitstream is in fact Vorbis data + + vi.init(); + vc.init(); + + int i=0; + while(i<3){ + os.pagein(og_ptr); + while(i<3){ + int result=os.packetout(op); + if(result==0)break; + if(result==-1){ + //System.err.println("Corrupt header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + if(vi.synthesis_headerin(vc, op)!=0){ + //System.err.println("Illegal header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + i++; + } + if(i<3) + if(get_next_page(og_ptr, 1)<0){ + //System.err.println("Missing header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + } + return 0; + +// bail_header: +// vorbis_info_clear(vi); +// vorbis_comment_clear(vc); +// ogg_stream_clear(&vf->os); +// return -1; + } + + // last step of the OggVorbis_File initialization; get all the + // vorbis_info structs and PCM positions. Only called by the seekable + // initialization (local stream storage is hacked slightly; pay + // attention to how that's done) + void prefetch_all_headers(Info first_i,Comment first_c, + int dataoffset) throws JOrbisException { + Page og=new Page(); + int ret; + + vi=new Info[links]; + vc=new Comment[links]; + dataoffsets=new long[links]; + pcmlengths=new long[links]; + serialnos=new int[links]; + + for(int i=0;i<links;i++){ + if(first_i!=null && first_c!=null && i==0){ + // we already grabbed the initial header earlier. This just + // saves the waste of grabbing it again + // !!!!!!!!!!!!! + vi[i]=first_i; + //memcpy(vf->vi+i,first_i,sizeof(vorbis_info)); + vc[i]=first_c; + //memcpy(vf->vc+i,first_c,sizeof(vorbis_comment)); + dataoffsets[i]=dataoffset; + } + else{ + // seek to the location of the initial header + seek_helper(offsets[i]); //!!! + vi[i]=new Info(); + vc[i]=new Comment(); + if(fetch_headers(vi[i], vc[i], null, null)==-1){ + //System.err.println("Error opening logical bitstream #"+(i+1)+"\n"); + dataoffsets[i]=-1; + } + else{ + dataoffsets[i]=offset; + os.clear(); + } + } + + // get the serial number and PCM length of this link. To do this, + // get the last page of the stream + { + long end=offsets[i+1]; //!!! + seek_helper(end); + + while(true){ + ret=get_prev_page(og); + if(ret==-1){ + // this should not be possible + //System.err.println("Could not find last page of logical "+ + // "bitstream #"+(i)+"\n"); + vi[i].clear(); + vc[i].clear(); + break; + } + if(og.granulepos()!=-1){ + serialnos[i]=og.serialno(); + pcmlengths[i]=og.granulepos(); + break; + } + } + } + } + } + + int make_decode_ready(){ + if(decode_ready)System.exit(1); + vd.synthesis_init(vi[0]); + vb.init(vd); + decode_ready=true; + return(0); + } + + int open_seekable() throws JOrbisException { + Info initial_i=new Info(); + Comment initial_c=new Comment(); + int serialno; + long end; + int ret; + int dataoffset; + Page og=new Page(); + // is this even vorbis...? + int[] foo=new int[1]; + ret=fetch_headers(initial_i, initial_c, foo, null); + serialno=foo[0]; + dataoffset=(int)offset; //!! + os.clear(); + if(ret==-1)return(-1); + // we can seek, so set out learning all about this file + seekable=true; + //(callbacks.seek_func)(datasource, 0, SEEK_END); + fseek(datasource, 0, SEEK_END); + //offset=end=(callbacks.tell_func)(datasource); + offset=ftell(datasource); + end=offset; + // We get the offset for the last page of the physical bitstream. + // Most OggVorbis files will contain a single logical bitstream + end=get_prev_page(og); + // moer than one logical bitstream? + if(og.serialno()!=serialno){ + // Chained bitstream. Bisect-search each logical bitstream + // section. Do so based on serial number only + if(bisect_forward_serialno(0,0,end+1,serialno,0)<0){ + clear(); + return OV_EREAD; + } + } + else{ + // Only one logical bitstream + if(bisect_forward_serialno(0,end,end+1,serialno,0)<0){ + clear(); + return OV_EREAD; + } + } + prefetch_all_headers(initial_i, initial_c, dataoffset); + return(raw_seek(0)); + } + + int open_nonseekable(){ + //System.err.println("open_nonseekable"); + // we cannot seek. Set up a 'single' (current) logical bitstream entry + links=1; + vi=new Info[links]; vi[0]=new Info(); // ?? + vc=new Comment[links]; vc[0]=new Comment(); // ?? bug? + + // Try to fetch the headers, maintaining all the storage + int[]foo=new int[1]; + if(fetch_headers(vi[0], vc[0], foo, null)==-1)return(-1); + current_serialno=foo[0]; + make_decode_ready(); + return 0; + } + + // clear out the current logical bitstream decoder + void decode_clear(){ + os.clear(); + vd.clear(); + vb.clear(); + decode_ready=false; + bittrack=0.f; + samptrack=0.f; + } + + // fetch and process a packet. Handles the case where we're at a + // bitstream boundary and dumps the decoding machine. If the decoding + // machine is unloaded, it loads it. It also keeps pcm_offset up to + // date (seek and read both use this. seek uses a special hack with + // readp). + // + // return: -1) hole in the data (lost packet) + // 0) need more date (only if readp==0)/eof + // 1) got a packet + + int process_packet(int readp){ + Page og=new Page(); + + // handle one packet. Try to fetch it from current stream state + // extract packets from page + while(true){ + // process a packet if we can. If the machine isn't loaded, + // neither is a page + if(decode_ready){ + Packet op=new Packet(); + int result=os.packetout(op); + long granulepos; + // if(result==-1)return(-1); // hole in the data. For now, swallow + // and go. We'll need to add a real + // error code in a bit. + if(result>0){ + // got a packet. process it + granulepos=op.granulepos; + if(vb.synthesis(op)==0){ // lazy check for lazy + // header handling. The + // header packets aren't + // audio, so if/when we + // submit them, + // vorbis_synthesis will + // reject them + // suck in the synthesis data and track bitrate + { + int oldsamples=vd.synthesis_pcmout(null, null); + vd.synthesis_blockin(vb); + samptrack+=vd.synthesis_pcmout(null, null)-oldsamples; + bittrack+=op.bytes*8; + } + + // update the pcm offset. + if(granulepos!=-1 && op.e_o_s==0){ + int link=(seekable?current_link:0); + int samples; + // this packet has a pcm_offset on it (the last packet + // completed on a page carries the offset) After processing + // (above), we know the pcm position of the *last* sample + // ready to be returned. Find the offset of the *first* + // + // As an aside, this trick is inaccurate if we begin + // reading anew right at the last page; the end-of-stream + // granulepos declares the last frame in the stream, and the + // last packet of the last page may be a partial frame. + // So, we need a previous granulepos from an in-sequence page + // to have a reference point. Thus the !op.e_o_s clause above + + samples=vd.synthesis_pcmout(null, null); + granulepos-=samples; + for(int i=0;i<link;i++){ + granulepos+=pcmlengths[i]; + } + pcm_offset=granulepos; + } + return(1); + } + } + } + + if(readp==0)return(0); + if(get_next_page(og,-1)<0)return(0); // eof. leave unitialized + + // bitrate tracking; add the header's bytes here, the body bytes + // are done by packet above + bittrack+=og.header_len*8; + + // has our decoding just traversed a bitstream boundary? + if(decode_ready){ + if(current_serialno!=og.serialno()){ + decode_clear(); + } + } + + // Do we need to load a new machine before submitting the page? + // This is different in the seekable and non-seekable cases. + // + // In the seekable case, we already have all the header + // information loaded and cached; we just initialize the machine + // with it and continue on our merry way. + // + // In the non-seekable (streaming) case, we'll only be at a + // boundary if we just left the previous logical bitstream and + // we're now nominally at the header of the next bitstream + + if(!decode_ready){ + int i; + if(seekable){ + current_serialno=og.serialno(); + + // match the serialno to bitstream section. We use this rather than + // offset positions to avoid problems near logical bitstream + // boundaries + for(i=0;i<links;i++){ + if(serialnos[i]==current_serialno)break; + } + if(i==links)return(-1); // sign of a bogus stream. error out, + // leave machine uninitialized + current_link=i; + + os.init(current_serialno); + os.reset(); + + } + else{ + // we're streaming + // fetch the three header packets, build the info struct + int foo[]=new int[1]; + int ret=fetch_headers(vi[0], vc[0], foo, og); + current_serialno=foo[0]; + if(ret!=0)return ret; + current_link++; + i=0; + } + make_decode_ready(); + } + os.pagein(og); + } + } + + //The helpers are over; it's all toplevel interface from here on out + // clear out the OggVorbis_File struct + int clear(){ + vb.clear(); + vd.clear(); + os.clear(); + + if(vi!=null && links!=0){ + for(int i=0;i<links;i++){ + vi[i].clear(); + vc[i].clear(); + } + vi=null; + vc=null; + } + if(dataoffsets!=null)dataoffsets=null; + if(pcmlengths!=null)pcmlengths=null; + if(serialnos!=null)serialnos=null; + if(offsets!=null)offsets=null; + oy.clear(); + //if(datasource!=null)(vf->callbacks.close_func)(vf->datasource); + //memset(vf,0,sizeof(OggVorbis_File)); + return(0); + } + + static int fseek(InputStream fis, + //int64_t off, + long off, + int whence){ + if(fis instanceof SeekableInputStream){ + SeekableInputStream sis=(SeekableInputStream)fis; + try{ + if(whence==SEEK_SET){ + sis.seek(off); + } + else if(whence==SEEK_END){ + sis.seek(sis.getLength()-off); + } + else{ + //System.out.println("seek: "+whence+" is not supported"); + } + } + catch(Exception e){ + } + return 0; + } + try{ + if(whence==0){ fis.reset(); } + fis.skip(off); + } + catch(Exception e){return -1;} + return 0; + } + + static long ftell(InputStream fis){ + try{ + if(fis instanceof SeekableInputStream){ + SeekableInputStream sis=(SeekableInputStream)fis; + return (sis.tell()); + } + } + catch(Exception e){ + } + return 0; + } + + // inspects the OggVorbis file and finds/documents all the logical + // bitstreams contained in it. Tries to be tolerant of logical + // bitstream sections that are truncated/woogie. + // + // return: -1) error + // 0) OK + + int open(InputStream is, byte[] initial, int ibytes) throws JOrbisException { + //callbacks callbacks = { + // (size_t (*)(void *, size_t, size_t, void *)) fread, + // (int (*)(void *, int64_t, int)) _fseek, + // (int (*)(void *)) fclose, + // (long (*)(void *)) ftell + // }; + return open_callbacks(is, initial, ibytes//, callbacks + ); + } + + int open_callbacks(InputStream is, byte[] initial, + int ibytes//, callbacks callbacks + ) throws JOrbisException { + int ret; + datasource=is; + //callbacks = _callbacks; + // init the framing state + oy.init(); + + // perhaps some data was previously read into a buffer for testing + // against other stream types. Allow initialization from this + // previously read data (as we may be reading from a non-seekable + // stream) + if(initial!=null){ + int index=oy.buffer(ibytes); + System.arraycopy(initial, 0, oy.data, index, ibytes); + oy.wrote(ibytes); + } + // can we seek? Stevens suggests the seek test was portable + if(is instanceof SeekableInputStream){ ret=open_seekable(); } + else{ ret=open_nonseekable(); } + if(ret!=0){ + datasource=null; + clear(); + } + return ret; + } + + // How many logical bitstreams in this physical bitstream? + public int streams(){ + return links; + } + + // Is the FILE * associated with vf seekable? + public boolean seekable(){ + return seekable; + } + + // returns the bitrate for a given logical bitstream or the entire + // physical bitstream. If the file is open for random access, it will + // find the *actual* average bitrate. If the file is streaming, it + // returns the nominal bitrate (if set) else the average of the + // upper/lower bounds (if set) else -1 (unset). + // + // If you want the actual bitrate field settings, get them from the + // vorbis_info structs + + public int bitrate(int i){ + if(i>=links)return(-1); + if(!seekable && i!=0)return(bitrate(0)); + if(i<0){ + long bits=0; + for(int j=0;j<links;j++){ + bits+=(offsets[j+1]-dataoffsets[j])*8; + } + return((int)Math.rint(bits/time_total(-1))); + } + else{ + if(seekable){ + // return the actual bitrate + return((int)Math.rint((offsets[i+1]-dataoffsets[i])*8/time_total(i))); + } + else{ + // return nominal if set + if(vi[i].bitrate_nominal>0){ + return vi[i].bitrate_nominal; + } + else{ + if(vi[i].bitrate_upper>0){ + if(vi[i].bitrate_lower>0){ + return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2; + }else{ + return vi[i].bitrate_upper; + } + } + return(-1); + } + } + } + } + + // returns the actual bitrate since last call. returns -1 if no + // additional data to offer since last call (or at beginning of stream) + public int bitrate_instant(){ + int _link=(seekable?current_link:0); + if(samptrack==0)return(-1); + int ret=(int)(bittrack/samptrack*vi[_link].rate+.5); + bittrack=0.f; + samptrack=0.f; + return(ret); + } + + public int serialnumber(int i){ + if(i>=links)return(-1); + if(!seekable && i>=0)return(serialnumber(-1)); + if(i<0){ + return(current_serialno); + } + else{ + return(serialnos[i]); + } + } + + // returns: total raw (compressed) length of content if i==-1 + // raw (compressed) length of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + + public long raw_total(int i){ + if(!seekable || i>=links)return(-1); + if(i<0){ + long acc=0; // bug? + for(int j=0;j<links;j++){ + acc+=raw_total(j); + } + return(acc); + } + else{ + return(offsets[i+1]-offsets[i]); + } + } + + // returns: total PCM length (samples) of content if i==-1 + // PCM length (samples) of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public long pcm_total(int i){ + if(!seekable || i>=links)return(-1); + if(i<0){ + long acc=0; + for(int j=0;j<links;j++){ + acc+=pcm_total(j); + } + return(acc); + } + else{ + return(pcmlengths[i]); + } + } + + // returns: total seconds of content if i==-1 + // seconds in that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public float time_total(int i){ + if(!seekable || i>=links)return(-1); + if(i<0){ + float acc=0; + for(int j=0;j<links;j++){ + acc+=time_total(j); + } + return(acc); + } + else{ + return((float)(pcmlengths[i])/vi[i].rate); + } + } + + // seek to an offset relative to the *compressed* data. This also + // immediately sucks in and decodes pages to update the PCM cursor. It + // will cross a logical bitstream boundary, but only if it can't get + // any packets out of the tail of the bitstream we seek to (so no + // surprises). + // + // returns zero on success, nonzero on failure + + public int raw_seek(int pos){ + if(!seekable)return(-1); // don't dump machine if we can't seek + if(pos<0 || pos>offsets[links]){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // clear out decoding machine state + pcm_offset=-1; + decode_clear(); + + // seek + seek_helper(pos); + + // we need to make sure the pcm_offset is set. We use the + // _fetch_packet helper to process one packet with readp set, then + // call it until it returns '0' with readp not set (the last packet + // from a page has the 'granulepos' field set, and that's how the + // helper updates the offset + + switch(process_packet(1)){ + case 0: + // oh, eof. There are no packets remaining. Set the pcm offset to + // the end of file + pcm_offset=pcm_total(-1); + return(0); + case -1: + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: + // all OK + break; + } + while(true){ + switch(process_packet(0)){ + case 0: + // the offset is set. If it's a bogus bitstream with no offset + // information, it's not but that's not our fault. We still run + // gracefully, we're just missing the offset + return(0); + case -1: + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: + // continue processing packets + break; + } + } + + // seek_error: + // dump the machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // seek to a sample offset relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + + public int pcm_seek(long pos){ + int link=-1; + long total=pcm_total(-1); + + if(!seekable)return(-1); // don't dump machine if we can't seek + if(pos<0 || pos>total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this pcm offset occur in? + for(link=links-1;link>=0;link--){ + total-=pcmlengths[link]; + if(pos>=total)break; + } + + // search within the logical bitstream for the page with the highest + // pcm_pos preceeding (or equal to) pos. There is a danger here; + // missing pages or incorrect frame number information in the + // bitstream could make our task impossible. Account for that (it + // would be an error condition) + { + long target=pos-total; + long end=offsets[link+1]; + long begin=offsets[link]; + int best=(int)begin; + + Page og=new Page(); + while(begin<end){ + long bisect; + int ret; + + if(end-begin<CHUNKSIZE){ + bisect=begin; + } + else{ + bisect=(end+begin)/2; + } + + seek_helper(bisect); + ret=get_next_page(og,end-bisect); + + if(ret==-1){ + end=bisect; + } + else{ + long granulepos=og.granulepos(); + if(granulepos<target){ + best=ret; // raw offset of packet with granulepos + begin=offset; // raw offset of next packet + } + else{ + end=bisect; + } + } + } + // found our page. seek to it (call raw_seek). + if(raw_seek(best)!=0){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + } + + // verify result + if(pcm_offset>=pos){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + if(pos>pcm_total(-1)){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // discard samples until we reach the desired position. Crossing a + // logical bitstream boundary with abandon is OK. + while(pcm_offset<pos){ + float[][] pcm; + int target=(int)(pos-pcm_offset); + float[][][] _pcm=new float[1][][]; + int[] _index=new int[getInfo(-1).channels]; + int samples=vd.synthesis_pcmout(_pcm, _index); + pcm=_pcm[0]; + + if(samples>target)samples=target; + vd.synthesis_read(samples); + pcm_offset+=samples; + + if(samples<target) + if(process_packet(1)==0){ + pcm_offset=pcm_total(-1); // eof + } + } + return 0; + + // seek_error: + // dump machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // seek to a playback time relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + int time_seek(float seconds){ + // translate time to PCM position and call pcm_seek + + int link=-1; + long pcm_total=pcm_total(-1); + float time_total=time_total(-1); + + if(!seekable)return(-1); // don't dump machine if we can't seek + if(seconds<0 || seconds>time_total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this time offset occur in? + for(link=links-1;link>=0;link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(seconds>=time_total)break; + } + + // enough information to convert time offset to pcm offset + { + long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate); + return(pcm_seek(target)); + } + + //seek_error: + // dump machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // tell the current stream offset cursor. Note that seek followed by + // tell will likely not give the set offset due to caching + public long raw_tell(){ + return(offset); + } + + // return PCM offset (sample) of next PCM sample to be read + public long pcm_tell(){ + return(pcm_offset); + } + + // return time offset (seconds) of next PCM sample to be read + public float time_tell(){ + // translate time to PCM position and call pcm_seek + + int link=-1; + long pcm_total=0; + float time_total=0.f; + + if(seekable){ + pcm_total=pcm_total(-1); + time_total=time_total(-1); + + // which bitstream section does this time offset occur in? + for(link=links-1;link>=0;link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(pcm_offset>=pcm_total)break; + } + } + + return((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate); + } + + // link: -1) return the vorbis_info struct for the bitstream section + // currently being decoded + // 0-n) to request information for a specific bitstream section + // + // In the case of a non-seekable bitstream, any call returns the + // current bitstream. NULL in the case that the machine is not + // initialized + + public Info getInfo(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ + return vi[current_link]; + } + else{ + return null; + } + } + else{ + if(link>=links){ + return null; + } + else{ + return vi[link]; + } + } + } + else{ + if(decode_ready){ + return vi[0]; + } + else{ + return null; + } + } + } + + public Comment getComment(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ return vc[current_link]; } + else{ return null; } + } + else{ + if(link>=links){ return null;} + else{ return vc[link]; } + } + } + else{ + if(decode_ready){ return vc[0]; } + else{ return null; } + } + } + + int host_is_big_endian() { + return 1; +// short pattern = 0xbabe; +// unsigned char *bytewise = (unsigned char *)&pattern; +// if (bytewise[0] == 0xba) return 1; +// assert(bytewise[0] == 0xbe); +// return 0; + } + + // up to this point, everything could more or less hide the multiple + // logical bitstream nature of chaining from the toplevel application + // if the toplevel application didn't particularly care. However, at + // the point that we actually read audio back, the multiple-section + // nature must surface: Multiple bitstream sections do not necessarily + // have to have the same number of channels or sampling rate. + // + // read returns the sequential logical bitstream number currently + // being decoded along with the PCM data in order that the toplevel + // application can take action on channel/sample rate changes. This + // number will be incremented even for streamed (non-seekable) streams + // (for seekable streams, it represents the actual logical bitstream + // index within the physical bitstream. Note that the accessor + // functions above are aware of this dichotomy). + // + // input values: buffer) a buffer to hold packed PCM data for return + // length) the byte length requested to be placed into buffer + // bigendianp) should the data be packed LSB first (0) or + // MSB first (1) + // word) word size for output. currently 1 (byte) or + // 2 (16 bit short) + // + // return values: -1) error/hole in data + // 0) EOF + // n) number of bytes of PCM actually returned. The + // below works on a packet-by-packet basis, so the + // return length is not related to the 'length' passed + // in, just guaranteed to fit. + // + // *section) set to the logical bitstream number + + int read(byte[] buffer,int length, + int bigendianp, int word, int sgned, int[] bitstream){ + int host_endian = host_is_big_endian(); + int index=0; + + while(true){ + if(decode_ready){ + float[][] pcm; + float[][][] _pcm=new float[1][][]; + int[] _index=new int[getInfo(-1).channels]; + int samples=vd.synthesis_pcmout(_pcm, _index); + pcm=_pcm[0]; + if(samples!=0){ + // yay! proceed to pack data into the byte buffer + int channels=getInfo(-1).channels; + int bytespersample=word * channels; + if(samples>length/bytespersample)samples=length/bytespersample; + + // a tight loop to pack each size + { + int val; + if(word==1){ + int off=(sgned!=0?0:128); + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][_index[i]+j]*128. + 0.5); + if(val>127)val=127; + else if(val<-128)val=-128; + buffer[index++]=(byte)(val+off); + } + } + } + else{ + int off=(sgned!=0?0:32768); + + if(host_endian==bigendianp){ + if(sgned!=0){ + for(int i=0;i<channels;i++) { // It's faster in this order + int src=_index[i]; + int dest=i; + for(int j=0;j<samples;j++) { + val=(int)(pcm[i][src+j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + buffer[dest]=(byte)(val>>>8); + buffer[dest+1]=(byte)(val); + dest+=channels*2; + } + } + } + else{ + for(int i=0;i<channels;i++) { + float[] src=pcm[i]; + int dest=i; + for(int j=0;j<samples;j++) { + val=(int)(src[j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + buffer[dest]=(byte)((val+off)>>>8); + buffer[dest+1]=(byte)(val+off); + dest+=channels*2; + } + } + } + } + else if(bigendianp!=0){ + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + buffer[index++]=(byte)(val>>>8); + buffer[index++]=(byte)val; + } + } + } + else{ + //int val; + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + buffer[index++]=(byte)val; + buffer[index++]=(byte)(val>>>8); + } + } + } + } + } + + vd.synthesis_read(samples); + pcm_offset+=samples; + if(bitstream!=null)bitstream[0]=current_link; + return(samples*bytespersample); + } + } + + // suck in another packet + switch(process_packet(1)){ + case 0: + return(0); + case -1: + return -1; + default: + break; + } + } + } + + public Info[] getInfo(){return vi;} + public Comment[] getComment(){return vc;} + +/* + public static void main(String[] arg){ + try{ + VorbisFile foo=new VorbisFile(arg[0]); + int links=foo.streams(); + System.out.println("links="+links); + Comment[] comment=foo.getComment(); + Info[] info=foo.getInfo(); + for(int i=0; i<links; i++){ + System.out.println(info[i]); + System.out.println(comment[i]); + } + System.out.println("raw_total: "+foo.raw_total(-1)); + System.out.println("pcm_total: "+foo.pcm_total(-1)); + System.out.println("time_total: "+foo.time_total(-1)); + } + catch(Exception e){ + System.err.println(e); + } + } +*/ + + public void close() throws java.io.IOException { + datasource.close(); + } + + class SeekableInputStream extends InputStream { + java.io.RandomAccessFile raf=null; + final String mode="r"; + private SeekableInputStream(){ + } + SeekableInputStream(String file) throws java.io.IOException{ + raf=new java.io.RandomAccessFile(file, mode); + } + public int read() throws java.io.IOException{ + return raf.read(); + } + public int read(byte[] buf) throws java.io.IOException{ + return raf.read(buf); + } + public int read(byte[] buf , int s, int len) throws java.io.IOException{ + return raf.read(buf, s, len); + } + public long skip(long n) throws java.io.IOException{ + return (long)(raf.skipBytes((int)n)); + } + public long getLength() throws java.io.IOException{ + return raf.length(); + } + public long tell() throws java.io.IOException{ + return raf.getFilePointer(); + } + public int available() throws java.io.IOException{ + return (raf.length()==raf.getFilePointer())? 0 : 1; + } + public void close() throws java.io.IOException{ + raf.close(); + } + public synchronized void mark(int m){ + } + public synchronized void reset() throws java.io.IOException{ + } + public boolean markSupported(){ + return false; + } + public void seek(long pos) throws java.io.IOException{ + raf.seek(pos); + } + } + +} diff --git a/songdbj/com/jcraft/jorbis/VorbisFile.java.new b/songdbj/com/jcraft/jorbis/VorbisFile.java.new new file mode 100644 index 0000000000..1f822b0991 --- /dev/null +++ b/songdbj/com/jcraft/jorbis/VorbisFile.java.new @@ -0,0 +1,1240 @@ +/* JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk<ymnk@jcraft.com> + * + * Many thanks to + * Monty <monty@xiph.org> and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * 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. + */ + +package com.jcraft.jorbis; + +import com.jcraft.jogg.*; +import java.io.InputStream; + +public class VorbisFile{ + static final int CHUNKSIZE=4096; + static final int SEEK_SET=0; + + InputStream datasource; + boolean seekable=false; + long offset; + long end; + + SyncState oy=new SyncState(); + + int links; + Comment[] vc; + Info[] vi; + + long[] offsets; + long[] dataoffsets; + int[] serialnos; + long[] pcmlengths; + + + + // Decoding working state local storage + long pcm_offset; + boolean decode_ready=false; + int current_serialno; + int current_link; + + float bittrack; + float samptrack; + + StreamState os=new StreamState(); // take physical pages, weld into a logical + // stream of packets + DspState vd=new DspState(); // central working state for + // the packet->PCM decoder + Block vb=new Block(vd); // local working space for packet->PCM decode + + //ov_callbacks callbacks; + + public VorbisFile(String file) throws JOrbisException { + super(); + InputStream is=null; + try{ is=new java.io.BufferedInputStream(new java.io.FileInputStream(file));} + catch(Exception e){ + throw new JOrbisException("VorbisFile: "+e.toString()); + } + int ret=open(is, null, 0); + if(ret==-1){ + throw new JOrbisException("VorbisFile: open return -1"); + } + } + + public VorbisFile(InputStream is, byte[] initial, int ibytes) + throws JOrbisException { + super(); + int ret=open(is, initial, ibytes); + if(ret==-1){ + } + } + + private int get_data(){ + int index=oy.buffer(CHUNKSIZE); + byte[] buffer=oy.data; +// int bytes=callbacks.read_func(buffer, index, 1, CHUNKSIZE, datasource); + int bytes=0; + try{ + bytes=datasource.read(buffer, index, CHUNKSIZE); + } + catch(Exception e){System.err.println(e);} + oy.wrote(bytes); + return bytes; + } + + private void seek_helper(int offst){ + //callbacks.seek_func(datasource, offst, SEEK_SET); + fseek64_wrap(datasource, offst, SEEK_SET); + this.offset=offst; + oy.reset(); + } + + private int get_next_page(Page page, int boundary){ + if(boundary>0) boundary+=offset; + while(true){ + int more; + if(boundary>0 && offset>=boundary)return -1; + more=oy.pageseek(page); + if(more<0){offset-=more;} + else{ + if(more==0){ + if(boundary==0)return -1; + if(get_data()<=0)return -1; + } + else{ + int ret=(int)offset; //!!! + offset+=more; + return ret; + } + } + } + } + + private int get_prev_page(Page page){ + int begin=(int)offset; //!!! + int ret; + int offst=-1; + while(offst==-1){ + begin-=CHUNKSIZE; + seek_helper(begin); + while(offset<begin+CHUNKSIZE){ + ret=get_next_page(page, begin+CHUNKSIZE-((int)offset)); + if(ret==-1){ break; } + else{ offst=ret; } + } + } + seek_helper((int)offset); //!!! + ret=get_next_page(page, CHUNKSIZE); + if(ret==-1){ + System.err.println("Missed page fencepost at end of logical bitstream Exiting"); + System.exit(1); + } + return offst; + } + + void bisect_forward_serialno(int begin, int searched, int end, int currentno, int m){ + int endsearched=end; + int next=end; + Page page=new Page(); + int ret; + while(searched<endsearched){ + int bisect; + if(endsearched-searched<CHUNKSIZE){ + bisect=searched; + } + else{ + bisect=(searched+endsearched)/2; + } + + seek_helper(bisect); + ret=get_next_page(page, -1); + if(ret<0 || page.serialno()!=currentno){ + endsearched=bisect; + if(ret>=0)next=ret; + } + else{ + searched=ret+page.header_len+page.body_len; + } + } + seek_helper(next); + ret=get_next_page(page, -1); + + if(searched>=end || ret==-1){ + links=m+1; + offsets=new long[m+2]; + offsets[m+1]=searched; + } + else{ + bisect_forward_serialno(next, (int)offset, end, page.serialno(), m+1); + } + offsets[m]=begin; + } + + // uses the local ogg_stream storage in vf; this is important for + // non-streaming input sources + int fetch_headers(Info vi, Comment vc, int[] serialno){ + //System.err.println("fetch_headers"); + Page og=new Page(); + Packet op=new Packet(); + int ret; + + ret=get_next_page(og, CHUNKSIZE); + if(ret==-1){ + System.err.println("Did not find initial header for bitstream."); + return -1; + } + + if(serialno!=null)serialno[0]=og.serialno(); + + os.init(og.serialno()); + + // extract the initial header from the first page and verify that the + // Ogg bitstream is in fact Vorbis data + + vi.init(); + vc.init(); + + int i=0; + while(i<3){ + os.pagein(og); + while(i<3){ + int result=os.packetout(op); + if(result==0)break; + if(result==-1){ + System.err.println("Corrupt header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + if(vi.synthesis_headerin(vc, op)!=0){ + System.err.println("Illegal header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + i++; + } + if(i<3) + if(get_next_page(og, 1)<0){ + System.err.println("Missing header in logical bitstream."); + //goto bail_header; + vi.clear(); + vc.clear(); + os.clear(); + return -1; + } + } + return 0; + +// bail_header: +// vorbis_info_clear(vi); +// vorbis_comment_clear(vc); +// ogg_stream_clear(&vf->os); +// return -1; + } + + // last step of the OggVorbis_File initialization; get all the + // vorbis_info structs and PCM positions. Only called by the seekable + // initialization (local stream storage is hacked slightly; pay + // attention to how that's done) + void prefetch_all_headers(Info first_i,Comment first_c, int dataoffset){ + Page og=new Page(); + int ret; + + vi=new Info[links]; + vc=new Comment[links]; + dataoffsets=new long[links]; + pcmlengths=new long[links]; + serialnos=new int[links]; + + for(int i=0;i<links;i++){ + if(first_i!=null && first_c!=null && i==0){ + // we already grabbed the initial header earlier. This just + // saves the waste of grabbing it again + // !!!!!!!!!!!!! + vi[i]=first_i; + //memcpy(vf->vi+i,first_i,sizeof(vorbis_info)); + vc[i]=first_c; + //memcpy(vf->vc+i,first_c,sizeof(vorbis_comment)); + dataoffsets[i]=dataoffset; + } + else{ + // seek to the location of the initial header + seek_helper((int)offsets[i]); //!!! + if(fetch_headers(vi[i], vc[i], null)==-1){ + System.err.println("Error opening logical bitstream #"+(i+1)+"\n"); + dataoffsets[i]=-1; + } + else{ + dataoffsets[i]=offset; + os.clear(); + } + } + + // get the serial number and PCM length of this link. To do this, + // get the last page of the stream + { + int end=(int)offsets[i+1]; //!!! + seek_helper(end); + + while(true){ + ret=get_prev_page(og); + if(ret==-1){ + // this should not be possible + System.err.println("Could not find last page of logical "+ + "bitstream #"+(i)+"\n"); + vi[i].clear(); + vc[i].clear(); + break; + } + if(og.granulepos()!=-1){ + serialnos[i]=og.serialno(); + pcmlengths[i]=og.granulepos(); + break; + } + } + } + } + } + + int make_decode_ready(){ + if(decode_ready)System.exit(1); + vd.synthesis_init(vi[0]); + vb.init(vd); + decode_ready=true; + return(0); + } + + int open_seekable(){ + Info initial_i=new Info(); + Comment initial_c=new Comment(); + int serialno,end; + int ret; + int dataoffset; + Page og=new Page(); +System.out.println("open_seekable"); + // is this even vorbis...? + int[] foo=new int[1]; + ret=fetch_headers(initial_i, initial_c, foo); + serialno=foo[0]; + dataoffset=(int)offset; //!! + os.clear(); + if(ret==-1)return(-1); + + // we can seek, so set out learning all about this file + seekable=true; + //(callbacks.seek_func)(datasource, 0, SEEK_END); + fseek64_wrap(datasource, (int)offset, SEEK_SET); + //offset=end=(callbacks.tell_func)(datasource); + end=(int)offset; + + // We get the offset for the last page of the physical bitstream. + // Most OggVorbis files will contain a single logical bitstream + end=get_prev_page(og); + + // moer than one logical bitstream? + if(og.serialno()!=serialno){ + // Chained bitstream. Bisect-search each logical bitstream + // section. Do so based on serial number only + bisect_forward_serialno(0,0,end+1,serialno,0); + } + else{ + // Only one logical bitstream + bisect_forward_serialno(0,end,end+1,serialno,0); + } + prefetch_all_headers(initial_i, initial_c, dataoffset); + +System.out.println("?"); + return(raw_seek(0)); + } + + int open_nonseekable(){ + //System.err.println("open_nonseekable"); + // we cannot seek. Set up a 'single' (current) logical bitstream entry + links=1; + vi=new Info[links]; vi[0]=new Info(); // ?? + vc=new Comment[links]; vc[0]=new Comment(); // ?? bug? + + // Try to fetch the headers, maintaining all the storage + int[]foo=new int[1]; + if(fetch_headers(vi[0], vc[0], foo)==-1)return(-1); + current_serialno=foo[0]; + make_decode_ready(); + return 0; + } + + // clear out the current logical bitstream decoder + void decode_clear(){ + os.clear(); + vd.clear(); + vb.clear(); + decode_ready=false; + bittrack=0.f; + samptrack=0.f; + } + + // fetch and process a packet. Handles the case where we're at a + // bitstream boundary and dumps the decoding machine. If the decoding + // machine is unloaded, it loads it. It also keeps pcm_offset up to + // date (seek and read both use this. seek uses a special hack with + // readp). + // + // return: -1) hole in the data (lost packet) + // 0) need more date (only if readp==0)/eof + // 1) got a packet + + int process_packet(int readp){ +System.out.println("porcess_packet:"+ readp+" , decode_ready="+decode_ready); + Page og=new Page(); + + // handle one packet. Try to fetch it from current stream state + // extract packets from page + while(true){ + // process a packet if we can. If the machine isn't loaded, + // neither is a page + if(decode_ready){ + Packet op=new Packet(); + int result=os.packetout(op); + long granulepos; + // if(result==-1)return(-1); // hole in the data. For now, swallow + // and go. We'll need to add a real + // error code in a bit. + if(result>0){ + // got a packet. process it + granulepos=op.granulepos; + if(vb.synthesis(op)==0){ // lazy check for lazy + // header handling. The + // header packets aren't + // audio, so if/when we + // submit them, + // vorbis_synthesis will + // reject them + // suck in the synthesis data and track bitrate + { + int oldsamples=vd.synthesis_pcmout(null, null); + vd.synthesis_blockin(vb); + samptrack+=vd.synthesis_pcmout(null, null)-oldsamples; + bittrack+=op.bytes*8; + } + + // update the pcm offset. + if(granulepos!=-1 && op.e_o_s==0){ + int link=(seekable?current_link:0); + int samples; + // this packet has a pcm_offset on it (the last packet + // completed on a page carries the offset) After processing + // (above), we know the pcm position of the *last* sample + // ready to be returned. Find the offset of the *first* + // + // As an aside, this trick is inaccurate if we begin + // reading anew right at the last page; the end-of-stream + // granulepos declares the last frame in the stream, and the + // last packet of the last page may be a partial frame. + // So, we need a previous granulepos from an in-sequence page + // to have a reference point. Thus the !op.e_o_s clause above + + samples=vd.synthesis_pcmout(null, null); + granulepos-=samples; + for(int i=0;i<link;i++){ + granulepos+=pcmlengths[i]; + } + pcm_offset=granulepos; + } + return(1); + } + } + } + + if(readp==0)return(0); + if(get_next_page(og,-1)<0)return(0); // eof. leave unitialized + + // bitrate tracking; add the header's bytes here, the body bytes + // are done by packet above + bittrack+=og.header_len*8; + + // has our decoding just traversed a bitstream boundary? + if(decode_ready){ + if(current_serialno!=og.serialno()){ + decode_clear(); + } + } + + // Do we need to load a new machine before submitting the page? + // This is different in the seekable and non-seekable cases. + // + // In the seekable case, we already have all the header + // information loaded and cached; we just initialize the machine + // with it and continue on our merry way. + // + // In the non-seekable (streaming) case, we'll only be at a + // boundary if we just left the previous logical bitstream and + // we're now nominally at the header of the next bitstream + + if(!decode_ready){ + int i; + if(seekable){ + current_serialno=og.serialno(); + + // match the serialno to bitstream section. We use this rather than + // offset positions to avoid problems near logical bitstream + // boundaries + for(i=0;i<links;i++){ + if(serialnos[i]==current_serialno)break; + } + if(i==links)return(-1); // sign of a bogus stream. error out, + // leave machine uninitialized + current_link=i; + + os.init(current_serialno); + os.reset(); + + } + else{ + // we're streaming + // fetch the three header packets, build the info struct + int foo[]=new int[1]; + fetch_headers(vi[0], vc[0], foo); + current_serialno=foo[0]; + current_link++; + i=0; + } + make_decode_ready(); + } + os.pagein(og); + } + } + + //The helpers are over; it's all toplevel interface from here on out + // clear out the OggVorbis_File struct + int clear(){ + vb.clear(); + vd.clear(); + os.clear(); + + if(vi!=null && links!=0){ + for(int i=0;i<links;i++){ + vi[i].clear(); + vc[i].clear(); + } + vi=null; + vc=null; + } + if(dataoffsets!=null)dataoffsets=null; + if(pcmlengths!=null)pcmlengths=null; + if(serialnos!=null)serialnos=null; + if(offsets!=null)offsets=null; + oy.clear(); + //if(datasource!=null)(vf->callbacks.close_func)(vf->datasource); + //memset(vf,0,sizeof(OggVorbis_File)); + return(0); + } + + static int fseek64_wrap(InputStream fis, + //int64_t off, + int off, + int whence){ + + if(!fis.markSupported()){ return -1; } + try{ + try{if(whence==0){ fis.reset(); }} + catch(Exception ee){System.out.println(ee);} + fis.skip(off); + } + catch(Exception e){ System.out.println(e); + //return -1; + } + return 0; + } + + // inspects the OggVorbis file and finds/documents all the logical + // bitstreams contained in it. Tries to be tolerant of logical + // bitstream sections that are truncated/woogie. + // + // return: -1) error + // 0) OK + + int open(InputStream is, byte[] initial, int ibytes){ + return open_callbacks(is, initial, ibytes//, callbacks + ); + } + + int open_callbacks(InputStream is, byte[] initial, + int ibytes//, callbacks callbacks + ){ +// int offset=callbacks.seek_func(f,0,SEEK_CUR); + int _offset=fseek64_wrap(is, (int)offset, SEEK_SET); + int ret; + // memset(vf,0,sizeof(OggVorbis_File)); + datasource=is; + //callbacks = _callbacks; + + // init the framing state + oy.init(); + + // perhaps some data was previously read into a buffer for testing + // against other stream types. Allow initialization from this + // previously read data (as we may be reading from a non-seekable + // stream) + if(initial!=null){ + int index=oy.buffer(ibytes); + System.arraycopy(initial, 0, oy.data, index, ibytes); + oy.wrote(ibytes); + } + +System.out.println("open_callbacks="+_offset); + // can we seek? Stevens suggests the seek test was portable + if(_offset!=-1){ ret=open_seekable(); } + else{ ret=open_nonseekable(); } + +System.out.println("ret="+ret); + + if(ret!=0){ + datasource=null; + clear(); + } + + return(ret); + } + + // How many logical bitstreams in this physical bitstream? + public int streams(){ + return links; + } + + // Is the FILE * associated with vf seekable? + public boolean seekable(){ + return seekable; + } + + // returns the bitrate for a given logical bitstream or the entire + // physical bitstream. If the file is open for random access, it will + // find the *actual* average bitrate. If the file is streaming, it + // returns the nominal bitrate (if set) else the average of the + // upper/lower bounds (if set) else -1 (unset). + // + // If you want the actual bitrate field settings, get them from the + // vorbis_info structs + + public int bitrate(int i){ + if(i>=links)return(-1); + if(!seekable && i!=0)return(bitrate(0)); + if(i<0){ + long bits=0; + for(int j=0;j<links;j++){ + bits+=(offsets[j+1]-dataoffsets[j])*8; + } + return((int)Math.rint(bits/time_total(-1))); + } + else{ + if(seekable){ + // return the actual bitrate + return((int)Math.rint((offsets[i+1]-dataoffsets[i])*8/time_total(i))); + } + else{ + // return nominal if set + if(vi[i].bitrate_nominal>0){ + return vi[i].bitrate_nominal; + } + else{ + if(vi[i].bitrate_upper>0){ + if(vi[i].bitrate_lower>0){ + return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2; + }else{ + return vi[i].bitrate_upper; + } + } + return(-1); + } + } + } + } + + // returns the actual bitrate since last call. returns -1 if no + // additional data to offer since last call (or at beginning of stream) + public int bitrate_instant(){ + int _link=(seekable?current_link:0); + if(samptrack==0)return(-1); + int ret=(int)(bittrack/samptrack*vi[_link].rate+.5); + bittrack=0.f; + samptrack=0.f; + return(ret); + } + + public int serialnumber(int i){ + if(i>=links)return(-1); + if(!seekable && i>=0)return(serialnumber(-1)); + if(i<0){ + return(current_serialno); + } + else{ + return(serialnos[i]); + } + } + + // returns: total raw (compressed) length of content if i==-1 + // raw (compressed) length of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + + public long raw_total(int i){ +System.out.println("raw_total: "+seekable); + if(!seekable || i>=links)return(-1); + if(i<0){ + long acc=0; // bug? + for(int j=0;j<links;j++){ + acc+=raw_total(j); + } + return(acc); + } + else{ + return(offsets[i+1]-offsets[i]); + } + } + + // returns: total PCM length (samples) of content if i==-1 + // PCM length (samples) of that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public long pcm_total(int i){ + if(!seekable || i>=links)return(-1); + if(i<0){ + long acc=0; + for(int j=0;j<links;j++){ + acc+=pcm_total(j); + } + return(acc); + } + else{ + return(pcmlengths[i]); + } + } + + // returns: total seconds of content if i==-1 + // seconds in that logical bitstream for i==0 to n + // -1 if the stream is not seekable (we can't know the length) + public float time_total(int i){ + if(!seekable || i>=links)return(-1); + if(i<0){ + float acc=0; + for(int j=0;j<links;j++){ + acc+=time_total(j); + } + return(acc); + } + else{ + return((float)(pcmlengths[i])/vi[i].rate); + } + } + + // seek to an offset relative to the *compressed* data. This also + // immediately sucks in and decodes pages to update the PCM cursor. It + // will cross a logical bitstream boundary, but only if it can't get + // any packets out of the tail of the bitstream we seek to (so no + // surprises). + // + // returns zero on success, nonzero on failure + + public int raw_seek(int pos){ +System.out.println("raw_seek: "+pos); + if(!seekable)return(-1); // don't dump machine if we can't seek + if(pos<0 || pos>offsets[links]){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } +System.out.println("#1"); + // clear out decoding machine state + pcm_offset=-1; +System.out.println("#2"); + decode_clear(); +System.out.println("#3"); + // seek + seek_helper(pos); + + // we need to make sure the pcm_offset is set. We use the + // _fetch_packet helper to process one packet with readp set, then + // call it until it returns '0' with readp not set (the last packet + // from a page has the 'granulepos' field set, and that's how the + // helper updates the offset +System.out.println("#4"); + switch(process_packet(1)){ + case 0: +System.out.println("?0"); + // oh, eof. There are no packets remaining. Set the pcm offset to + // the end of file + pcm_offset=pcm_total(-1); + return(0); + case -1: +System.out.println("?-1"); + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: +System.out.println("?break"); + // all OK + break; + } +System.out.println("pcm_offset="+pcm_offset); + while(true){ + switch(process_packet(0)){ + case 0: + // the offset is set. If it's a bogus bitstream with no offset + // information, it's not but that's not our fault. We still run + // gracefully, we're just missing the offset + return(0); + case -1: + // error! missing data or invalid bitstream structure + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + default: + // continue processing packets + break; + } + } + + // seek_error: + // dump the machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // seek to a sample offset relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + + public int pcm_seek(long pos){ + int link=-1; + long total=pcm_total(-1); + + if(!seekable)return(-1); // don't dump machine if we can't seek + if(pos<0 || pos>total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this pcm offset occur in? + for(link=links-1;link>=0;link--){ + total-=pcmlengths[link]; + if(pos>=total)break; + } + + // search within the logical bitstream for the page with the highest + // pcm_pos preceeding (or equal to) pos. There is a danger here; + // missing pages or incorrect frame number information in the + // bitstream could make our task impossible. Account for that (it + // would be an error condition) + { + long target=pos-total; + int end=(int)offsets[link+1]; + int begin=(int)offsets[link]; + int best=begin; + + Page og=new Page(); + while(begin<end){ + int bisect; + int ret; + + if(end-begin<CHUNKSIZE){ + bisect=begin; + } + else{ + bisect=(end+begin)/2; + } + + seek_helper(bisect); + ret=get_next_page(og,end-bisect); + + if(ret==-1){ + end=bisect; + } + else{ + long granulepos=og.granulepos(); + if(granulepos<target){ + best=ret; // raw offset of packet with granulepos + begin=(int)offset; // raw offset of next packet + } + else{ + end=bisect; + } + } + } + // found our page. seek to it (call raw_seek). + if(raw_seek(best)!=0){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + } + + // verify result + if(pcm_offset>=pos){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + if(pos>pcm_total(-1)){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // discard samples until we reach the desired position. Crossing a + // logical bitstream boundary with abandon is OK. + while(pcm_offset<pos){ + float[][] pcm; + int target=(int)(pos-pcm_offset); + float[][][] _pcm=new float[1][][]; + int[] _index=new int[info(-1).channels]; + int samples=vd.synthesis_pcmout(_pcm, _index); + pcm=_pcm[0]; + + if(samples>target)samples=target; + vd.synthesis_read(samples); + pcm_offset+=samples; + + if(samples<target) + if(process_packet(1)==0){ + pcm_offset=pcm_total(-1); // eof + } + } + return 0; + + // seek_error: + // dump machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // seek to a playback time relative to the decompressed pcm stream + // returns zero on success, nonzero on failure + public int time_seek(float seconds){ + // translate time to PCM position and call pcm_seek + + int link=-1; + long pcm_total=pcm_total(-1); + float time_total=time_total(-1); + + if(!seekable)return(-1); // don't dump machine if we can't seek + if(seconds<0 || seconds>time_total){ + //goto seek_error; + pcm_offset=-1; + decode_clear(); + return -1; + } + + // which bitstream section does this time offset occur in? + for(link=links-1;link>=0;link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(seconds>=time_total)break; + } + + // enough information to convert time offset to pcm offset + { + long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate); + return(pcm_seek(target)); + } + + //seek_error: + // dump machine so we're in a known state + //pcm_offset=-1; + //decode_clear(); + //return -1; + } + + // tell the current stream offset cursor. Note that seek followed by + // tell will likely not give the set offset due to caching + public long raw_tell(){ + return(offset); + } + + // return PCM offset (sample) of next PCM sample to be read + public long pcm_tell(){ + return(pcm_offset); + } + + // return time offset (seconds) of next PCM sample to be read + public float time_tell(){ + // translate time to PCM position and call pcm_seek + + int link=-1; + long pcm_total=0; + float time_total=0.f; + + if(seekable){ + pcm_total=pcm_total(-1); + time_total=time_total(-1); + + // which bitstream section does this time offset occur in? + for(link=links-1;link>=0;link--){ + pcm_total-=pcmlengths[link]; + time_total-=time_total(link); + if(pcm_offset>=pcm_total)break; + } + } + + return((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate); + } + + // link: -1) return the vorbis_info struct for the bitstream section + // currently being decoded + // 0-n) to request information for a specific bitstream section + // + // In the case of a non-seekable bitstream, any call returns the + // current bitstream. NULL in the case that the machine is not + // initialized + + public Info info(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ + return vi[current_link]; + } + else{ + return null; + } + } + else{ + if(link>=links){ + return null; + } + else{ + return vi[link]; + } + } + } + else{ + if(decode_ready){ + return vi[0]; + } + else{ + return null; + } + } + } + + public Comment comment(int link){ + if(seekable){ + if(link<0){ + if(decode_ready){ return vc[current_link]; } + else{ return null; } + } + else{ + if(link>=links){ return null;} + else{ return vc[link]; } + } + } + else{ + if(decode_ready){ return vc[0]; } + else{ return null; } + } + } + + int host_is_big_endian() { + return 1; +// short pattern = 0xbabe; +// unsigned char *bytewise = (unsigned char *)&pattern; +// if (bytewise[0] == 0xba) return 1; +// assert(bytewise[0] == 0xbe); +// return 0; + } + + // up to this point, everything could more or less hide the multiple + // logical bitstream nature of chaining from the toplevel application + // if the toplevel application didn't particularly care. However, at + // the point that we actually read audio back, the multiple-section + // nature must surface: Multiple bitstream sections do not necessarily + // have to have the same number of channels or sampling rate. + // + // read returns the sequential logical bitstream number currently + // being decoded along with the PCM data in order that the toplevel + // application can take action on channel/sample rate changes. This + // number will be incremented even for streamed (non-seekable) streams + // (for seekable streams, it represents the actual logical bitstream + // index within the physical bitstream. Note that the accessor + // functions above are aware of this dichotomy). + // + // input values: buffer) a buffer to hold packed PCM data for return + // length) the byte length requested to be placed into buffer + // bigendianp) should the data be packed LSB first (0) or + // MSB first (1) + // word) word size for output. currently 1 (byte) or + // 2 (16 bit short) + // + // return values: -1) error/hole in data + // 0) EOF + // n) number of bytes of PCM actually returned. The + // below works on a packet-by-packet basis, so the + // return length is not related to the 'length' passed + // in, just guaranteed to fit. + // + // *section) set to the logical bitstream number + + int read(byte[] buffer,int length, + int bigendianp, int word, int sgned, int[] bitstream){ + int host_endian = host_is_big_endian(); + int index=0; + + while(true){ + if(decode_ready){ + float[][] pcm; + float[][][] _pcm=new float[1][][]; + int[] _index=new int[info(-1).channels]; + int samples=vd.synthesis_pcmout(_pcm, _index); + pcm=_pcm[0]; + if(samples!=0){ + // yay! proceed to pack data into the byte buffer + int channels=info(-1).channels; + int bytespersample=word * channels; + if(samples>length/bytespersample)samples=length/bytespersample; + + // a tight loop to pack each size + { + int val; + if(word==1){ + int off=(sgned!=0?0:128); + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][_index[i]+j]*128. + 0.5); + if(val>127)val=127; + else if(val<-128)val=-128; + buffer[index++]=(byte)(val+off); + } + } + } + else{ + int off=(sgned!=0?0:32768); + + if(host_endian==bigendianp){ + if(sgned!=0){ + for(int i=0;i<channels;i++) { // It's faster in this order + int src=_index[i]; + int dest=i; + for(int j=0;j<samples;j++) { + val=(int)(pcm[i][src+j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + buffer[dest]=(byte)(val>>>8); + buffer[dest+1]=(byte)(val); + dest+=channels*2; + } + } + } + else{ + for(int i=0;i<channels;i++) { + float[] src=pcm[i]; + int dest=i; + for(int j=0;j<samples;j++) { + val=(int)(src[j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + buffer[dest]=(byte)((val+off)>>>8); + buffer[dest+1]=(byte)(val+off); + dest+=channels*2; + } + } + } + } + else if(bigendianp!=0){ + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + buffer[index++]=(byte)(val>>>8); + buffer[index++]=(byte)val; + } + } + } + else{ + //int val; + for(int j=0;j<samples;j++){ + for(int i=0;i<channels;i++){ + val=(int)(pcm[i][j]*32768. + 0.5); + if(val>32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + buffer[index++]=(byte)val; + buffer[index++]=(byte)(val>>>8); + } + } + } + } + } + + vd.synthesis_read(samples); + pcm_offset+=samples; + if(bitstream!=null)bitstream[0]=current_link; + return(samples*bytespersample); + } + } + + // suck in another packet + switch(process_packet(1)){ + case 0: + return(0); + case -1: + return -1; + default: + break; + } + } + } + + public int getLinks(){return links;} + public Info[] getInfo(){return vi;} + public Comment[] getComment(){return vc;} + + public static void main(String[] arg){ + try{ + VorbisFile foo=new VorbisFile(arg[0]); + int links=foo.getLinks(); + System.out.println("links="+links); + Comment[] comment=foo.getComment(); + Info[] info=foo.getInfo(); + for(int i=0; i<links; i++){ + System.out.println(info[i]); + System.out.println(comment[i]); + } + System.out.println("raw_total: "+foo.raw_total(-1)); + System.out.println("pcm_total: "+foo.pcm_total(-1)); + System.out.println("time_total: "+foo.time_total(-1)); + } + catch(Exception e){ + System.err.println(e); + } + } +} diff --git a/songdbj/de/jarnbjo/ogg/BasicStream.java b/songdbj/de/jarnbjo/ogg/BasicStream.java new file mode 100644 index 0000000000..9939524d6c --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/BasicStream.java @@ -0,0 +1,121 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.3 2004/09/21 12:09:45 shred + * *** empty log message *** + * + * Revision 1.2 2004/09/21 06:38:45 shred + * Importe reorganisiert, damit Eclipse Ruhe gibt. ;-) + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * + */ + +package de.jarnbjo.ogg; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Implementation of the <code>PhysicalOggStream</code> interface for reading + * an Ogg stream from a URL. This class performs + * no internal caching, and will not read data from the network before + * requested to do so. It is intended to be used in non-realtime applications + * like file download managers or similar. + */ + +public class BasicStream implements PhysicalOggStream { + + private boolean closed=false; + private InputStream sourceStream; + private Object drainLock=new Object(); + private LinkedList pageCache=new LinkedList(); + private long numberOfSamples=-1; + private int position=0; + + private HashMap logicalStreams=new HashMap(); + private OggPage firstPage; + + public BasicStream(InputStream sourceStream) throws OggFormatException, IOException { + firstPage=OggPage.create(sourceStream); + position+=firstPage.getTotalLength(); + LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber()); + logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los); + los.checkFormat(firstPage); + } + + public Collection getLogicalStreams() { + return logicalStreams.values(); + } + + public boolean isOpen() { + return !closed; + } + + public void close() throws IOException { + closed=true; + sourceStream.close(); + } + + public int getContentLength() { + return -1; + } + + public int getPosition() { + return position; + } + + int pageNumber=2; + + public OggPage getOggPage(int index) throws IOException { + if(firstPage!=null) { + OggPage tmp=firstPage; + firstPage=null; + return tmp; + } + else { + OggPage page=OggPage.create(sourceStream); + position+=page.getTotalLength(); + return page; + } + } + + private LogicalOggStream getLogicalStream(int serialNumber) { + return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); + } + + public void setTime(long granulePosition) throws IOException { + throw new UnsupportedOperationException("Method not supported by this class"); + } + + /** + * @return always <code>false</code> + */ + + public boolean isSeekable() { + return false; + } + +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/CachedUrlStream.java b/songdbj/de/jarnbjo/ogg/CachedUrlStream.java new file mode 100644 index 0000000000..86f792e272 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/CachedUrlStream.java @@ -0,0 +1,252 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/04/10 19:48:22 jarnbjo + * no message + * + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Implementation of the <code>PhysicalOggStream</code> interface for reading + * and caching an Ogg stream from a URL. This class reads the data as fast as + * possible from the URL, caches it locally either in memory or on disk, and + * supports seeking within the available data. + */ + +public class CachedUrlStream implements PhysicalOggStream { + + private boolean closed=false; + private URLConnection source; + private InputStream sourceStream; + private Object drainLock=new Object(); + private RandomAccessFile drain; + private byte[] memoryCache; + private ArrayList pageOffsets=new ArrayList(); + private ArrayList pageLengths=new ArrayList(); + private long numberOfSamples=-1; + private long cacheLength; + + private HashMap logicalStreams=new HashMap(); + + private LoaderThread loaderThread; + + /** + * Creates an instance of this class, using a memory cache. + */ + + public CachedUrlStream(URL source) throws OggFormatException, IOException { + this(source, null); + } + + /** + * Creates an instance of this class, using the specified file as cache. The + * file is not automatically deleted when this class is disposed. + */ + + public CachedUrlStream(URL source, RandomAccessFile drain) throws OggFormatException, IOException { + + this.source=source.openConnection(); + + if(drain==null) { + int contentLength=this.source.getContentLength(); + if(contentLength==-1) { + throw new IOException("The URLConncetion's content length must be set when operating with a in-memory cache."); + } + memoryCache=new byte[contentLength]; + } + + this.drain=drain; + this.sourceStream=this.source.getInputStream(); + + loaderThread=new LoaderThread(sourceStream, drain, memoryCache); + new Thread(loaderThread).start(); + + while(!loaderThread.isBosDone() || pageOffsets.size()<20) { + System.out.print("pageOffsets.size(): "+pageOffsets.size()+"\r"); + try { + Thread.sleep(200); + } + catch (InterruptedException ex) { + } + } + System.out.println(); + System.out.println("caching "+pageOffsets.size()+"/20 pages\r"); + } + + public Collection getLogicalStreams() { + return logicalStreams.values(); + } + + public boolean isOpen() { + return !closed; + } + + public void close() throws IOException { + closed=true; + sourceStream.close(); + } + + public long getCacheLength() { + return cacheLength; + } + + /* + private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { + return getNextPage(false); + } + + private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { + return OggPage.create(sourceStream, skipData); + } + */ + + public OggPage getOggPage(int index) throws IOException { + synchronized(drainLock) { + Long offset=(Long)pageOffsets.get(index); + Long length=(Long)pageLengths.get(index); + if(offset!=null) { + if(drain!=null) { + drain.seek(offset.longValue()); + return OggPage.create(drain); + } + else { + byte[] tmpArray=new byte[length.intValue()]; + System.arraycopy(memoryCache, offset.intValue(), tmpArray, 0, length.intValue()); + return OggPage.create(tmpArray); + } + } + else { + return null; + } + } + } + + private LogicalOggStream getLogicalStream(int serialNumber) { + return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); + } + + public void setTime(long granulePosition) throws IOException { + for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) { + LogicalOggStream los=(LogicalOggStream)iter.next(); + los.setTime(granulePosition); + } + } + + public class LoaderThread implements Runnable { + + private InputStream source; + private RandomAccessFile drain; + private byte[] memoryCache; + + private boolean bosDone=false; + + private int pageNumber; + + public LoaderThread(InputStream source, RandomAccessFile drain, byte[] memoryCache) { + this.source=source; + this.drain=drain; + this.memoryCache=memoryCache; + } + + public void run() { + try { + boolean eos=false; + byte[] buffer=new byte[8192]; + while(!eos) { + OggPage op=OggPage.create(source); + synchronized (drainLock) { + int listSize=pageOffsets.size(); + + long pos= + listSize>0? + ((Long)pageOffsets.get(listSize-1)).longValue()+ + ((Long)pageLengths.get(listSize-1)).longValue(): + 0; + + byte[] arr1=op.getHeader(); + byte[] arr2=op.getSegmentTable(); + byte[] arr3=op.getData(); + + if(drain!=null) { + drain.seek(pos); + drain.write(arr1); + drain.write(arr2); + drain.write(arr3); + } + else { + System.arraycopy(arr1, 0, memoryCache, (int)pos, arr1.length); + System.arraycopy(arr2, 0, memoryCache, (int)pos+arr1.length, arr2.length); + System.arraycopy(arr3, 0, memoryCache, (int)pos+arr1.length+arr2.length, arr3.length); + } + + pageOffsets.add(new Long(pos)); + pageLengths.add(new Long(arr1.length+arr2.length+arr3.length)); + } + + if(!op.isBos()) { + bosDone=true; + //System.out.println("bosDone=true;"); + } + if(op.isEos()) { + eos=true; + } + + LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); + if(los==null) { + los=new LogicalOggStreamImpl(CachedUrlStream.this, op.getStreamSerialNumber()); + logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); + los.checkFormat(op); + } + + los.addPageNumberMapping(pageNumber); + los.addGranulePosition(op.getAbsoluteGranulePosition()); + + pageNumber++; + cacheLength=op.getAbsoluteGranulePosition(); + //System.out.println("read page: "+pageNumber); + } + } + catch(EndOfOggStreamException e) { + // ok + } + catch(IOException e) { + e.printStackTrace(); + } + } + + public boolean isBosDone() { + return bosDone; + } + } + + public boolean isSeekable() { + return true; + } +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java b/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java new file mode 100644 index 0000000000..4a0c3200f4 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/EndOfOggStreamException.java @@ -0,0 +1,45 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.2 2005/02/09 23:10:47 shred + * Serial UID für jarnbjo + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + + package de.jarnbjo.ogg; + +import java.io.IOException; + +/** + * Exception thrown when reaching the end of an Ogg stream + */ + +public class EndOfOggStreamException extends IOException { + private static final long serialVersionUID = 3907210438109444408L; + + public EndOfOggStreamException() { + } +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/FileStream.java b/songdbj/de/jarnbjo/ogg/FileStream.java new file mode 100644 index 0000000000..5a526300bf --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/FileStream.java @@ -0,0 +1,154 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/04/10 19:48:22 jarnbjo + * no message + * + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; +import java.util.*; + +/** + * Implementation of the <code>PhysicalOggStream</code> interface for accessing + * normal disk files. + */ + +public class FileStream implements PhysicalOggStream { + + private boolean closed=false; + private RandomAccessFile source; + private long[] pageOffsets; + private long numberOfSamples=-1; + + private HashMap logicalStreams=new HashMap(); + + /** + * Creates access to the specified file through the <code>PhysicalOggStream</code> interface. + * The specified source file must have been opened for reading. + * + * @param source the file to read from + * + * @throws OggFormatException if the stream format is incorrect + * @throws IOException if some other IO error occurs when reading the file + */ + + public FileStream(RandomAccessFile source) throws OggFormatException, IOException { + this.source=source; + + ArrayList po=new ArrayList(); + int pageNumber=0; + try { + while(true) { + po.add(new Long(this.source.getFilePointer())); + + // skip data if pageNumber>0 + OggPage op=getNextPage(pageNumber>0); + if(op==null) { + break; + } + + LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); + if(los==null) { + los=new LogicalOggStreamImpl(this, op.getStreamSerialNumber()); + logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); + } + + if(pageNumber==0) { + los.checkFormat(op); + } + + los.addPageNumberMapping(pageNumber); + los.addGranulePosition(op.getAbsoluteGranulePosition()); + + if(pageNumber>0) { + this.source.seek(this.source.getFilePointer()+op.getTotalLength()); + } + + pageNumber++; + } + } + catch(EndOfOggStreamException e) { + // ok + } + catch(IOException e) { + throw e; + } + //System.out.println("pageNumber: "+pageNumber); + this.source.seek(0L); + pageOffsets=new long[po.size()]; + int i=0; + Iterator iter=po.iterator(); + while(iter.hasNext()) { + pageOffsets[i++]=((Long)iter.next()).longValue(); + } + } + + public Collection getLogicalStreams() { + return logicalStreams.values(); + } + + public boolean isOpen() { + return !closed; + } + + public void close() throws IOException { + closed=true; + source.close(); + } + + private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { + return getNextPage(false); + } + + private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { + return OggPage.create(source, skipData); + } + + public OggPage getOggPage(int index) throws IOException { + source.seek(pageOffsets[index]); + return OggPage.create(source); + } + + private LogicalOggStream getLogicalStream(int serialNumber) { + return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); + } + + public void setTime(long granulePosition) throws IOException { + for(Iterator iter=logicalStreams.values().iterator(); iter.hasNext(); ) { + LogicalOggStream los=(LogicalOggStream)iter.next(); + los.setTime(granulePosition); + } + } + + /** + * @return always <code>true</code> + */ + + public boolean isSeekable() { + return true; + } +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/LogicalOggStream.java b/songdbj/de/jarnbjo/ogg/LogicalOggStream.java new file mode 100644 index 0000000000..2f97b2a728 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/LogicalOggStream.java @@ -0,0 +1,151 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.2 2003/04/10 19:48:22 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.IOException; + +/** + * Interface providing access to a logical Ogg stream as part of a + * physical Ogg stream. + */ + + +public interface LogicalOggStream { + + public static final String FORMAT_UNKNOWN = "application/octet-stream"; + + public static final String FORMAT_VORBIS = "audio/x-vorbis"; + public static final String FORMAT_FLAC = "audio/x-flac"; + public static final String FORMAT_THEORA = "video/x-theora"; + + /** + * <i>Note:</i> To read from the stream, you must use either + * this method or the method <code>getNextOggPacket</code>. + * Mixing calls to the two methods will cause data corruption. + * + * @return the next Ogg page + * + * @see #getNextOggPacket() + * + * @throws OggFormatException if the ogg stream is corrupted + * @throws IOException if some other IO error occurs + */ + + public OggPage getNextOggPage() throws OggFormatException, IOException; + + /** + * <i>Note:</i> To read from the stream, you must use either + * this method or the method <code>getNextOggPage</code>. + * Mixing calls to the two methods will cause data corruption. + * + * @return the next packet as a byte array + * + * @see #getNextOggPage() + * + * @throws OggFormatException if the ogg stream is corrupted + * @throws IOException if some other IO error occurs + */ + + public byte[] getNextOggPacket() throws OggFormatException, IOException; + + /** + * Checks if this stream is open for reading. + * + * @return <code>true</code> if this stream is open for reading, + * <code>false</code> otherwise + */ + + public boolean isOpen(); + + /** + * Closes this stream. After invoking this method, no further access + * to the streams data is possible. + * + * @throws IOException if an IO error occurs + */ + + public void close() throws IOException; + + /** + * Sets the stream's position to the beginning of the stream. + * This method does not work if the physical Ogg stream is not + * seekable. + * + * @throws OggFormatException if the ogg stream is corrupted + * @throws IOException if some other IO error occurs + */ + + public void reset() throws OggFormatException, IOException; + + /** + * This method does not work if the physical Ogg stream is not + * seekable. + * + * @return the granule position of the last page within + * this stream + */ + + public long getMaximumGranulePosition(); + + /** + * This method is invoked on all logical streams when + * calling the same method on the physical stream. The + * same restrictions as mentioned there apply. + * This method does not work if the physical Ogg stream is not + * seekable. + * + * @param granulePosition + * + * @see PhysicalOggStream#setTime(long) + * + * @throws IOException if an IO error occurs + */ + + public void setTime(long granulePosition) throws IOException; + + /** + * @return the last parsed granule position of this stream + */ + + public long getTime(); + + /** + * @return the content type of this stream + * + * @see #FORMAT_UNKNOWN + * @see #FORMAT_VORBIS + * @see #FORMAT_FLAC + * @see #FORMAT_THEORA + */ + + public String getFormat(); +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java b/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java new file mode 100644 index 0000000000..1a503e91ca --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/LogicalOggStreamImpl.java @@ -0,0 +1,213 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.3 2003/03/31 00:23:04 jarnbjo + * no message + * + * Revision 1.2 2003/03/16 01:11:26 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; +import java.util.*; + +public class LogicalOggStreamImpl implements LogicalOggStream { + + private PhysicalOggStream source; + private int serialNumber; + + private ArrayList pageNumberMapping=new ArrayList(); + private ArrayList granulePositions=new ArrayList(); + + private int pageIndex=0; + private OggPage currentPage; + private int currentSegmentIndex; + + private boolean open=true; + + private String format=FORMAT_UNKNOWN; + + public LogicalOggStreamImpl(PhysicalOggStream source, int serialNumber) { + this.source=source; + this.serialNumber=serialNumber; + } + + public void addPageNumberMapping(int physicalPageNumber) { + pageNumberMapping.add(new Integer(physicalPageNumber)); + } + + public void addGranulePosition(long granulePosition) { + granulePositions.add(new Long(granulePosition)); + } + + public synchronized void reset() throws OggFormatException, IOException { + currentPage=null; + currentSegmentIndex=0; + pageIndex=0; + } + + public synchronized OggPage getNextOggPage() throws EndOfOggStreamException, OggFormatException, IOException { + if(source.isSeekable()) { + currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); + } + else { + currentPage=source.getOggPage(-1); + } + return currentPage; + } + + public synchronized byte[] getNextOggPacket() throws EndOfOggStreamException, OggFormatException, IOException { + ByteArrayOutputStream res=new ByteArrayOutputStream(); + int segmentLength=0; + + if(currentPage==null) { + currentPage=getNextOggPage(); + } + + do { + if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) { + currentSegmentIndex=0; + + if(!currentPage.isEos()) { + if(source.isSeekable() && pageNumberMapping.size()<=pageIndex) { + while(pageNumberMapping.size()<=pageIndex+10) { + try { + Thread.sleep(1000); + } + catch (InterruptedException ex) { + } + } + } + currentPage=getNextOggPage(); + + if(res.size()==0 && currentPage.isContinued()) { + boolean done=false; + while(!done) { + if(currentPage.getSegmentLengths()[currentSegmentIndex++]!=255) { + done=true; + } + if(currentSegmentIndex>currentPage.getSegmentTable().length) { + currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); + } + } + } + } + else { + throw new EndOfOggStreamException(); + } + } + segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex]; + res.write(currentPage.getData(), currentPage.getSegmentOffsets()[currentSegmentIndex], segmentLength); + currentSegmentIndex++; + } while(segmentLength==255); + + return res.toByteArray(); + } + + public boolean isOpen() { + return open; + } + + public void close() throws IOException { + open=false; + } + + public long getMaximumGranulePosition() { + Long mgp=(Long)granulePositions.get(granulePositions.size()-1); + return mgp.longValue(); + } + + public synchronized long getTime() { + return currentPage!=null?currentPage.getAbsoluteGranulePosition():-1; + } + + public synchronized void setTime(long granulePosition) throws IOException { + + int page=0; + for(page=0; page<granulePositions.size(); page++) { + Long gp=(Long)granulePositions.get(page); + if(gp.longValue()>granulePosition) { + break; + } + } + + pageIndex=page; + currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); + currentSegmentIndex=0; + int segmentLength=0; + do { + if(currentSegmentIndex>=currentPage.getSegmentOffsets().length) { + currentSegmentIndex=0; + if(pageIndex>=pageNumberMapping.size()) { + throw new EndOfOggStreamException(); + } + currentPage=source.getOggPage(((Integer)pageNumberMapping.get(pageIndex++)).intValue()); + } + segmentLength=currentPage.getSegmentLengths()[currentSegmentIndex]; + currentSegmentIndex++; + } while(segmentLength==255); + } + + public void checkFormat(OggPage page) { + byte[] data=page.getData(); + + if(data.length>=7 && + data[1]==0x76 && + data[2]==0x6f && + data[3]==0x72 && + data[4]==0x62 && + data[5]==0x69 && + data[6]==0x73) { + + format=FORMAT_VORBIS; + } + else if(data.length>=7 && + data[1]==0x74 && + data[2]==0x68 && + data[3]==0x65 && + data[4]==0x6f && + data[5]==0x72 && + data[6]==0x61) { + + format=FORMAT_THEORA; + } + else if (data.length==4 && + data[0]==0x66 && + data[1]==0x4c && + data[2]==0x61 && + data[3]==0x43) { + + format=FORMAT_FLAC; + } + } + + public String getFormat() { + return format; + } +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/OggFormatException.java b/songdbj/de/jarnbjo/ogg/OggFormatException.java new file mode 100644 index 0000000000..a6b2466b92 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/OggFormatException.java @@ -0,0 +1,50 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.2 2005/02/09 23:10:47 shred + * Serial UID für jarnbjo + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.IOException; + +/** + * Exception thrown when trying to read a corrupted Ogg stream. + */ + +public class OggFormatException extends IOException { + private static final long serialVersionUID = 3544953238333175349L; + + public OggFormatException() { + super(); + } + + public OggFormatException(String message) { + super(message); + } +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/OggPage.java b/songdbj/de/jarnbjo/ogg/OggPage.java new file mode 100644 index 0000000000..cc965cc7a9 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/OggPage.java @@ -0,0 +1,431 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.3 2003/04/10 19:48:22 jarnbjo + * no message + * + * Revision 1.2 2003/03/31 00:23:04 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; + +import de.jarnbjo.util.io.*; + +/** + * <p>An instance of this class represents an ogg page read from an ogg file + * or network stream. It has no public constructor, but instances can be + * created by the <code>create</code> methods, supplying a JMF stream or + * a <code>RandomAccessFile</code> + * which is positioned at the beginning of an Ogg page.</p> + * + * <p>Furtheron, the class provides methods for accessing the raw page data, + * as well as data attributes like segmenting information, sequence number, + * stream serial number, chechsum and wether this page is the beginning or + * end of a logical bitstream (BOS, EOS) and if the page data starts with a + * continued packet or a fresh data packet.</p> + */ + +public class OggPage { + + private int version; + private boolean continued, bos, eos; + private long absoluteGranulePosition; + private int streamSerialNumber, pageSequenceNumber, pageCheckSum; + private int[] segmentOffsets; + private int[] segmentLengths; + private int totalLength; + private byte[] header, segmentTable, data; + + protected OggPage() { + } + + private OggPage( + int version, + boolean continued, + boolean bos, + boolean eos, + long absoluteGranulePosition, + int streamSerialNumber, + int pageSequenceNumber, + int pageCheckSum, + int[] segmentOffsets, + int[] segmentLengths, + int totalLength, + byte[] header, + byte[] segmentTable, + byte[] data) { + + this.version=version; + this.continued=continued; + this.bos=bos; + this.eos=eos; + this.absoluteGranulePosition=absoluteGranulePosition; + this.streamSerialNumber=streamSerialNumber; + this.pageSequenceNumber=pageSequenceNumber; + this.pageCheckSum=pageCheckSum; + this.segmentOffsets=segmentOffsets; + this.segmentLengths=segmentLengths; + this.totalLength=totalLength; + this.header=header; + this.segmentTable=segmentTable; + this.data=data; + } + + /** + * this method equals to create(RandomAccessFile source, false) + * + * @see #create(RandomAccessFile, boolean) + */ + + public static OggPage create(RandomAccessFile source) throws IOException, EndOfOggStreamException, OggFormatException { + return create(source, false); + } + + /** + * This method is called to read data from the current position in the + * specified RandomAccessFile and create a new OggPage instance based on the data + * read. If the parameter <code>skipData</code> is set to <code>true</code>, + * the actual page segments (page data) is skipped and not read into + * memory. This mode is useful when scanning through an ogg file to build + * a seek table. + * + * @param source the source from which the ogg page is generated + * @param skipData if set to <code>true</code>, the actual page data is not read into memory + * @return an ogg page created by reading data from the specified source, starting at the current position + * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page + * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source + * @throws IOException if some other I/O error is detected when reading from the source + * + * @see #create(RandomAccessFile) + */ + + public static OggPage create(RandomAccessFile source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { + return create((Object)source, skipData); + } + + /** + * this method equals to create(InputStream source, false) + * + * @see #create(InputStream, boolean) + */ + + public static OggPage create(InputStream source) throws IOException, EndOfOggStreamException, OggFormatException { + return create(source, false); + } + + /** + * This method is called to read data from the current position in the + * specified InpuStream and create a new OggPage instance based on the data + * read. If the parameter <code>skipData</code> is set to <code>true</code>, + * the actual page segments (page data) is skipped and not read into + * memory. This mode is useful when scanning through an ogg file to build + * a seek table. + * + * @param source the source from which the ogg page is generated + * @param skipData if set to <code>true</code>, the actual page data is not read into memory + * @return an ogg page created by reading data from the specified source, starting at the current position + * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page + * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source + * @throws IOException if some other I/O error is detected when reading from the source + * + * @see #create(InputStream) + */ + + public static OggPage create(InputStream source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { + return create((Object)source, skipData); + } + + /** + * this method equals to create(byte[] source, false) + * + * @see #create(byte[], boolean) + */ + + public static OggPage create(byte[] source) throws IOException, EndOfOggStreamException, OggFormatException { + return create(source, false); + } + + /** + * This method is called to + * create a new OggPage instance based on the specified byte array. + * + * @param source the source from which the ogg page is generated + * @param skipData if set to <code>true</code>, the actual page data is not read into memory + * @return an ogg page created by reading data from the specified source, starting at the current position + * @throws FormatException if the data read from the specified source is not matching the specification for an ogg page + * @throws EndOfStreamException if it is not possible to read an entire ogg page from the specified source + * @throws IOException if some other I/O error is detected when reading from the source + * + * @see #create(byte[]) + */ + + public static OggPage create(byte[] source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { + return create((Object)source, skipData); + } + + private static OggPage create(Object source, boolean skipData) throws IOException, EndOfOggStreamException, OggFormatException { + + try { + int sourceOffset=27; + + byte[] header=new byte[27]; + if(source instanceof RandomAccessFile) { + RandomAccessFile raf=(RandomAccessFile)source; + if(raf.getFilePointer()==raf.length()) { + return null; + } + raf.readFully(header); + } + else if(source instanceof InputStream) { + readFully((InputStream)source, header); + } + else if(source instanceof byte[]) { + System.arraycopy((byte[])source, 0, header, 0, 27); + } + + BitInputStream bdSource=new ByteArrayBitInputStream(header); + + int capture=bdSource.getInt(32); + + if(capture!=0x5367674f) { + //throw new FormatException("Ogg page does not start with 'OggS' (0x4f676753)"); + + /* + ** This condition is IMHO an error, but older Ogg files often contain + ** pages with a different capture than OggS. I am not sure how to + ** manage these pages, but the decoder seems to work properly, if + ** the incorrect capture is simply ignored. + */ + + String cs=Integer.toHexString(capture); + while(cs.length()<8) { + cs="0"+cs; + } + cs=cs.substring(6, 8)+cs.substring(4, 6)+cs.substring(2, 4)+cs.substring(0, 2); + char c1=(char)(Integer.valueOf(cs.substring(0, 2), 16).intValue()); + char c2=(char)(Integer.valueOf(cs.substring(2, 4), 16).intValue()); + char c3=(char)(Integer.valueOf(cs.substring(4, 6), 16).intValue()); + char c4=(char)(Integer.valueOf(cs.substring(6, 8), 16).intValue()); + System.out.println("Ogg packet header is 0x"+cs+" ("+c1+c2+c3+c4+"), should be 0x4f676753 (OggS)"); + } + + int version=bdSource.getInt(8); + byte tmp=(byte)bdSource.getInt(8); + boolean bf1=(tmp&1)!=0; + boolean bos=(tmp&2)!=0; + boolean eos=(tmp&4)!=0; + long absoluteGranulePosition=bdSource.getLong(64); + int streamSerialNumber=bdSource.getInt(32); + int pageSequenceNumber=bdSource.getInt(32); + int pageCheckSum=bdSource.getInt(32); + int pageSegments=bdSource.getInt(8); + + //System.out.println("OggPage: "+streamSerialNumber+" / "+absoluteGranulePosition+" / "+pageSequenceNumber); + + int[] segmentOffsets=new int[pageSegments]; + int[] segmentLengths=new int[pageSegments]; + int totalLength=0; + + byte[] segmentTable=new byte[pageSegments]; + byte[] tmpBuf=new byte[1]; + + for(int i=0; i<pageSegments; i++) { + int l=0; + if(source instanceof RandomAccessFile) { + l=((int)((RandomAccessFile)source).readByte()&0xff); + } + else if(source instanceof InputStream) { + l=(int)((InputStream)source).read(); + } + else if(source instanceof byte[]) { + l=(int)((byte[])source)[sourceOffset++]; + l&=255; + } + segmentTable[i]=(byte)l; + segmentLengths[i]=l; + segmentOffsets[i]=totalLength; + totalLength+=l; + } + + byte[] data=null; + + if(!skipData) { + + //System.out.println("createPage: "+absoluteGranulePosition*1000/44100); + + data=new byte[totalLength]; + //source.read(data, 0, totalLength); + if(source instanceof RandomAccessFile) { + ((RandomAccessFile)source).readFully(data); + } + else if(source instanceof InputStream) { + readFully((InputStream)source, data); + } + else if(source instanceof byte[]) { + System.arraycopy(source, sourceOffset, data, 0, totalLength); + } + } + + return new OggPage(version, bf1, bos, eos, absoluteGranulePosition, streamSerialNumber, pageSequenceNumber, pageCheckSum, segmentOffsets, segmentLengths, totalLength, header, segmentTable, data); + } + catch(EOFException e) { + throw new EndOfOggStreamException(); + } + } + + private static void readFully(InputStream source, byte[] buffer) throws IOException { + int total=0; + while(total<buffer.length) { + int read=source.read(buffer, total, buffer.length-total); + if(read==-1) { + throw new EndOfOggStreamException(); + } + total+=read; + } + } + + /** + * Returns the absolute granule position of the last complete + * packet contained in this Ogg page, or -1 if the page contains a single + * packet, which is not completed on this page. For pages containing Vorbis + * data, this value is the sample index within the Vorbis stream. The Vorbis + * stream does not necessarily start with sample index 0. + * + * @return the absolute granule position of the last packet completed on + * this page + */ + + + public long getAbsoluteGranulePosition() { + return absoluteGranulePosition; + } + + /** + * Returns the stream serial number of this ogg page. + * + * @return this page's serial number + */ + + public int getStreamSerialNumber() { + return streamSerialNumber; + } + + /** + * Return the sequnce number of this ogg page. + * + * @return this page's sequence number + */ + + public int getPageSequenceNumber() { + return pageSequenceNumber; + } + + /** + * Return the check sum of this ogg page. + * + * @return this page's check sum + */ + + public int getPageCheckSum() { + return pageCheckSum; + } + + /** + * @return the total number of bytes in the page data + */ + + + public int getTotalLength() { + if(data!=null) { + return 27+segmentTable.length+data.length; + } + else { + return totalLength; + } + } + + /** + * @return a ByteBuffer containing the page data + */ + + + public byte[] getData() { + return data; + } + + public byte[] getHeader() { + return header; + } + + public byte[] getSegmentTable() { + return segmentTable; + } + + public int[] getSegmentOffsets() { + return segmentOffsets; + } + + public int[] getSegmentLengths() { + return segmentLengths; + } + + /** + * @return <code>true</code> if this page begins with a continued packet + */ + + public boolean isContinued() { + return continued; + } + + /** + * @return <code>true</code> if this page begins with a fresh packet + */ + + public boolean isFresh() { + return !continued; + } + + /** + * @return <code>true</code> if this page is the beginning of a logical stream + */ + + public boolean isBos() { + return bos; + } + + /** + * @return <code>true</code> if this page is the end of a logical stream + */ + + public boolean isEos() { + return eos; + } + +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java b/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java new file mode 100644 index 0000000000..98159c4e7c --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/OnDemandUrlStream.java @@ -0,0 +1,127 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/04/10 19:48:22 jarnbjo + * no message + * + * Revision 1.1 2003/03/31 00:23:04 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Implementation of the <code>PhysicalOggStream</code> interface for reading + * an Ogg stream from a URL. This class performs + * no internal caching, and will not read data from the network before + * requested to do so. It is intended to be used in non-realtime applications + * like file download managers or similar. + */ + +public class OnDemandUrlStream implements PhysicalOggStream { + + private boolean closed=false; + private URLConnection source; + private InputStream sourceStream; + private Object drainLock=new Object(); + private LinkedList pageCache=new LinkedList(); + private long numberOfSamples=-1; + private int contentLength=0; + private int position=0; + + private HashMap logicalStreams=new HashMap(); + private OggPage firstPage; + + private static final int PAGECACHE_SIZE = 20; + + public OnDemandUrlStream(URL source) throws OggFormatException, IOException { + this.source=source.openConnection(); + this.sourceStream=this.source.getInputStream(); + + contentLength=this.source.getContentLength(); + + firstPage=OggPage.create(sourceStream); + position+=firstPage.getTotalLength(); + LogicalOggStreamImpl los=new LogicalOggStreamImpl(this, firstPage.getStreamSerialNumber()); + logicalStreams.put(new Integer(firstPage.getStreamSerialNumber()), los); + los.checkFormat(firstPage); + } + + public Collection getLogicalStreams() { + return logicalStreams.values(); + } + + public boolean isOpen() { + return !closed; + } + + public void close() throws IOException { + closed=true; + sourceStream.close(); + } + + public int getContentLength() { + return contentLength; + } + + public int getPosition() { + return position; + } + + int pageNumber=2; + + public OggPage getOggPage(int index) throws IOException { + if(firstPage!=null) { + OggPage tmp=firstPage; + firstPage=null; + return tmp; + } + else { + OggPage page=OggPage.create(sourceStream); + position+=page.getTotalLength(); + return page; + } + } + + private LogicalOggStream getLogicalStream(int serialNumber) { + return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); + } + + public void setTime(long granulePosition) throws IOException { + throw new UnsupportedOperationException("Method not supported by this class"); + } + + /** + * @return always <code>false</code> + */ + + public boolean isSeekable() { + return false; + } + +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java b/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java new file mode 100644 index 0000000000..5f342a38b7 --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/PhysicalOggStream.java @@ -0,0 +1,124 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.3 2003/04/10 19:48:22 jarnbjo + * no message + * + * Revision 1.2 2003/03/31 00:23:04 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.IOException; +import java.util.Collection; + +/** + * Interface providing access to a physical Ogg stream. Typically this is + * a file. + */ + +public interface PhysicalOggStream { + + /** + * Returns a collection of objects implementing <code>LogicalOggStream</code> + * for accessing the separate logical streams within this physical Ogg stream. + * + * @return a collection of objects implementing <code>LogicalOggStream</code> + * which are representing the logical streams contained within this + * physical stream + * + * @see LogicalOggStream + */ + + public Collection getLogicalStreams(); + + /** + * Return the Ogg page with the absolute index <code>index</code>, + * independent from the logical structure of this stream or if the + * index parameter is -1, the next Ogg page is returned. + * This method should only be used by implementations of <code>LogicalOggStream</code> + * to access the raw pages. + * + * @param index the absolute index starting from 0 at the beginning of + * the file or stream or -1 to get the next page in a non-seekable + * stream + * + * @return the Ogg page with the physical absolute index <code>index</code> + * + * @throws OggFormatException if the ogg stream is corrupted + * @throws IOException if some other IO error occurs + */ + + public OggPage getOggPage(int index) throws OggFormatException, IOException; + + /** + * Checks if this stream is open for reading. + * + * @return <code>true</code> if this stream is open for reading, + * <code>false</code> otherwise + */ + + public boolean isOpen(); + + /** + * Closes this stream. After invoking this method, no further access + * to the streams data is possible. + * + * @throws IOException + */ + + public void close() throws IOException; + + /** + * Sets this stream's (and its logical stream's) position to the granule + * position. The next packet read from any logical stream will be the + * first packet beginning on the first page with a granule position higher + * than the argument.<br><br> + * + * At the moment, this method only works correctly for Ogg files with + * a single logical Vorbis stream, and due to the different interpretations + * of the granule position, depending on mixed content, this method will + * never be able to work for mixed streams. Chained and interleaved streams are + * also not yet supported. Actually, this method is only a hack to support + * seeking from JMF, but may of course be abused otherwise too :) + * + * @param granulePosition + * + * @throws OggFormatException if the ogg stream is corrupted + * @throws IOException if some other IO error occurs + */ + + public void setTime(long granulePosition) throws OggFormatException, IOException; + + /** + * @return <code>true</code> if the stream is seekable, <code>false</code> + * otherwise + */ + + public boolean isSeekable(); +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java b/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java new file mode 100644 index 0000000000..a07f0ac00e --- /dev/null +++ b/songdbj/de/jarnbjo/ogg/UncachedUrlStream.java @@ -0,0 +1,207 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.1 2003/04/10 19:48:22 jarnbjo + * no message + * + */ + +package de.jarnbjo.ogg; + +import java.io.*; +import java.net.*; +import java.util.*; + +/** + * Implementation of the <code>PhysicalOggStream</code> interface for reading + * an Ogg stream from a URL. This class performs only the necessary caching + * to provide continous playback. Seeking within the stream is not supported. + */ + +public class UncachedUrlStream implements PhysicalOggStream { + + private boolean closed=false; + private URLConnection source; + private InputStream sourceStream; + private Object drainLock=new Object(); + private LinkedList pageCache=new LinkedList(); + private long numberOfSamples=-1; + + private HashMap logicalStreams=new HashMap(); + + private LoaderThread loaderThread; + + private static final int PAGECACHE_SIZE = 10; + + /** Creates an instance of the <code>PhysicalOggStream</code> interface + * suitable for reading an Ogg stream from a URL. + */ + + public UncachedUrlStream(URL source) throws OggFormatException, IOException { + + this.source=source.openConnection(); + this.sourceStream=this.source.getInputStream(); + + loaderThread=new LoaderThread(sourceStream, pageCache); + new Thread(loaderThread).start(); + + while(!loaderThread.isBosDone() || pageCache.size()<PAGECACHE_SIZE) { + try { + Thread.sleep(200); + } + catch (InterruptedException ex) { + } + //System.out.print("caching "+pageCache.size()+"/"+PAGECACHE_SIZE+" pages\r"); + } + //System.out.println(); + } + + public Collection getLogicalStreams() { + return logicalStreams.values(); + } + + public boolean isOpen() { + return !closed; + } + + public void close() throws IOException { + closed=true; + sourceStream.close(); + } + + /* + public long getCacheLength() { + return cacheLength; + } + */ + + /* + private OggPage getNextPage() throws EndOfOggStreamException, IOException, OggFormatException { + return getNextPage(false); + } + + private OggPage getNextPage(boolean skipData) throws EndOfOggStreamException, IOException, OggFormatException { + return OggPage.create(sourceStream, skipData); + } + */ + + public OggPage getOggPage(int index) throws IOException { + while(pageCache.size()==0) { + try { + Thread.sleep(100); + } + catch (InterruptedException ex) { + } + } + synchronized(drainLock) { + //OggPage page=(OggPage)pageCache.getFirst(); + //pageCache.removeFirst(); + //return page; + return (OggPage)pageCache.removeFirst(); + } + } + + private LogicalOggStream getLogicalStream(int serialNumber) { + return (LogicalOggStream)logicalStreams.get(new Integer(serialNumber)); + } + + public void setTime(long granulePosition) throws IOException { + throw new UnsupportedOperationException("Method not supported by this class"); + } + + public class LoaderThread implements Runnable { + + private InputStream source; + private LinkedList pageCache; + private RandomAccessFile drain; + private byte[] memoryCache; + + private boolean bosDone=false; + + private int pageNumber; + + public LoaderThread(InputStream source, LinkedList pageCache) { + this.source=source; + this.pageCache=pageCache; + } + + public void run() { + try { + boolean eos=false; + byte[] buffer=new byte[8192]; + while(!eos) { + OggPage op=OggPage.create(source); + synchronized (drainLock) { + pageCache.add(op); + } + + if(!op.isBos()) { + bosDone=true; + } + if(op.isEos()) { + eos=true; + } + + LogicalOggStreamImpl los=(LogicalOggStreamImpl)getLogicalStream(op.getStreamSerialNumber()); + if(los==null) { + los=new LogicalOggStreamImpl(UncachedUrlStream.this, op.getStreamSerialNumber()); + logicalStreams.put(new Integer(op.getStreamSerialNumber()), los); + los.checkFormat(op); + } + + //los.addPageNumberMapping(pageNumber); + //los.addGranulePosition(op.getAbsoluteGranulePosition()); + + pageNumber++; + + while(pageCache.size()>PAGECACHE_SIZE) { + try { + Thread.sleep(200); + } + catch (InterruptedException ex) { + } + } + } + } + catch(EndOfOggStreamException e) { + // ok + } + catch(IOException e) { + e.printStackTrace(); + } + } + + public boolean isBosDone() { + return bosDone; + } + } + + /** + * @return always <code>false</code> + */ + + public boolean isSeekable() { + return false; + } + +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java b/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java new file mode 100644 index 0000000000..4916102d4b --- /dev/null +++ b/songdbj/de/jarnbjo/util/audio/FadeableAudioInputStream.java @@ -0,0 +1,62 @@ +package de.jarnbjo.util.audio; + +import java.io.*; +import javax.sound.sampled.*; + +public class FadeableAudioInputStream extends AudioInputStream { + + private AudioInputStream stream; + private boolean fading=false; + private double phi=0.0; + + public FadeableAudioInputStream(AudioInputStream stream) throws IOException { + super(stream, stream.getFormat(), -1L); + } + + public void fadeOut() { + fading=true; + phi=0.0; + } + + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte[] b, int offset, int length) throws IOException { + int read=super.read(b, offset, length); + + //System.out.println("read "+read); + + if(fading) { + int j=0, l=0, r=0; + double gain=0.0; + + for(int i=offset; i<offset+read; i+=4) { + j=i; + l=((int)b[j++])&0xff; + l|=((int)b[j++])<<8; + r=((int)b[j++])&0xff; + r|=((int)b[j])<<8; + + if(phi<Math.PI/2) { + phi+=0.000015; + } + + gain=Math.cos(phi); + //System.out.println("gain "+gain); + + l=(int)(l*gain); + r=(int)(r*gain); + + j=i; + b[j++]=(byte)(l&0xff); + b[j++]=(byte)((l>>8)&0xff); + b[j++]=(byte)(r&0xff); + b[j++]=(byte)((r>>8)&0xff); + } + } + + return read; + } + +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/util/io/BitInputStream.java b/songdbj/de/jarnbjo/util/io/BitInputStream.java new file mode 100644 index 0000000000..89cadb8380 --- /dev/null +++ b/songdbj/de/jarnbjo/util/io/BitInputStream.java @@ -0,0 +1,185 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.5 2003/04/10 19:48:31 jarnbjo + * no message + * + * Revision 1.4 2003/03/16 20:57:06 jarnbjo + * no message + * + * Revision 1.3 2003/03/16 20:56:56 jarnbjo + * no message + * + * Revision 1.2 2003/03/16 01:11:39 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.util.io; + +import java.io.IOException; + +/** + * An interface with methods allowing bit-wise reading from + * an input stream. All methods in this interface are optional + * and an implementation not support a method or a specific state + * (e.g. endian) will throw an UnspportedOperationException if + * such a method is being called. This should be speicified in + * the implementation documentation. + */ + +public interface BitInputStream { + + /** + * constant for setting this stream's mode to little endian + * + * @see #setEndian(int) + */ + + public static final int LITTLE_ENDIAN = 0; + + /** + * constant for setting this stream's mode to big endian + * + * @see #setEndian(int) + */ + + public static final int BIG_ENDIAN = 1; + + /** + * reads one bit (as a boolean) from the input stream + * + * @return <code>true</code> if the next bit is 1, + * <code>false</code> otherwise + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public boolean getBit() throws IOException; + + /** + * reads <code>bits</code> number of bits from the input + * stream + * + * @return the unsigned integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public int getInt(int bits) throws IOException; + + /** + * reads <code>bits</code> number of bits from the input + * stream + * + * @return the signed integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public int getSignedInt(int bits) throws IOException; + + /** + * reads a huffman codeword based on the <code>root</code> + * parameter and returns the decoded value + * + * @param root the root of the Huffman tree used to decode the codeword + * @return the decoded unsigned integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public int getInt(HuffmanNode root) throws IOException; + + /** + * reads an integer encoded as "signed rice" as described in + * the FLAC audio format specification + * + * @param order + * @return the decoded integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public int readSignedRice(int order) throws IOException; + + /** + * fills the array from <code>offset</code> with <code>len</code> + * integers encoded as "signed rice" as described in + * the FLAC audio format specification + * + * @param order + * @param buffer + * @param offset + * @param len + * @return the decoded integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public void readSignedRice(int order, int[] buffer, int offset, int len) throws IOException; + + /** + * reads <code>bits</code> number of bits from the input + * stream + * + * @return the unsigned long value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public long getLong(int bits) throws IOException; + + /** + * causes the read pointer to be moved to the beginning + * of the next byte, remaining bits in the current byte + * are discarded + * + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public void align(); + + /** + * changes the endian mode used when reading bit-wise from + * the stream, changing the mode mid-stream will cause the + * read cursor to move to the beginning of the next byte + * (as if calling the <code>allign</code> method + * + * @see #align() + * + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public void setEndian(int endian); +}
\ No newline at end of file diff --git a/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java b/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java new file mode 100644 index 0000000000..9c84c7daca --- /dev/null +++ b/songdbj/de/jarnbjo/util/io/ByteArrayBitInputStream.java @@ -0,0 +1,352 @@ +/* + * $ProjectName$ + * $ProjectRevision$ + * ----------------------------------------------------------- + * $Id$ + * ----------------------------------------------------------- + * + * $Author$ + * + * Description: + * + * Copyright 2002-2003 Tor-Einar Jarnbjo + * ----------------------------------------------------------- + * + * Change History + * ----------------------------------------------------------- + * $Log$ + * Revision 1.1 2005/07/11 15:42:36 hcl + * Songdb java version, source. only 1.5 compatible + * + * Revision 1.1.1.1 2004/04/04 22:09:12 shred + * First Import + * + * Revision 1.3 2003/04/10 19:48:31 jarnbjo + * no message + * + * Revision 1.2 2003/03/16 01:11:39 jarnbjo + * no message + * + * Revision 1.1 2003/03/03 21:02:20 jarnbjo + * no message + * + */ + +package de.jarnbjo.util.io; + +import java.io.IOException; + +/** + * Implementation of the <code>BitInputStream</code> interface, + * using a byte array as data source. +*/ + +public class ByteArrayBitInputStream implements BitInputStream { + + private byte[] source; + private byte currentByte; + + private int endian; + + private int byteIndex=0; + private int bitIndex=0; + + public ByteArrayBitInputStream(byte[] source) { + this(source, LITTLE_ENDIAN); + } + + public ByteArrayBitInputStream(byte[] source, int endian) { + this.endian=endian; + this.source=source; + currentByte=source[0]; + bitIndex=(endian==LITTLE_ENDIAN)?0:7; + } + + public boolean getBit() throws IOException { + if(endian==LITTLE_ENDIAN) { + if(bitIndex>7) { + bitIndex=0; + currentByte=source[++byteIndex]; + } + return (currentByte&(1<<(bitIndex++)))!=0; + } + else { + if(bitIndex<0) { + bitIndex=7; + currentByte=source[++byteIndex]; + } + return (currentByte&(1<<(bitIndex--)))!=0; + } + } + + public int getInt(int bits) throws IOException { + if(bits>32) { + throw new IllegalArgumentException("Argument \"bits\" must be <= 32"); + } + int res=0; + if(endian==LITTLE_ENDIAN) { + for(int i=0; i<bits; i++) { + if(getBit()) { + res|=(1<<i); + } + } + } + else { + if(bitIndex<0) { + bitIndex=7; + currentByte=source[++byteIndex]; + } + if(bits<=bitIndex+1) { + int ci=((int)currentByte)&0xff; + int offset=1+bitIndex-bits; + int mask=((1<<bits)-1)<<offset; + res=(ci&mask)>>offset; + bitIndex-=bits; + } + else { + res=(((int)currentByte)&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1); + bits-=bitIndex+1; + currentByte=source[++byteIndex]; + while(bits>=8) { + bits-=8; + res|=(((int)source[byteIndex])&0xff)<<bits; + currentByte=source[++byteIndex]; + } + if(bits>0) { + int ci=((int)source[byteIndex])&0xff; + res|=(ci>>(8-bits))&((1<<bits)-1); + bitIndex=7-bits; + } + else { + currentByte=source[--byteIndex]; + bitIndex=-1; + } + } + } + + return res; + } + + public int getSignedInt(int bits) throws IOException { + int raw=getInt(bits); + if(raw>=1<<(bits-1)) { + raw-=1<<bits; + } + return raw; + } + + public int getInt(HuffmanNode root) throws IOException { + while(root.value==null) { + if(bitIndex>7) { + bitIndex=0; + currentByte=source[++byteIndex]; + } + root=(currentByte&(1<<(bitIndex++)))!=0?root.o1:root.o0; + } + return root.value.intValue(); + } + + public long getLong(int bits) throws IOException { + if(bits>64) { + throw new IllegalArgumentException("Argument \"bits\" must be <= 64"); + } + long res=0; + if(endian==LITTLE_ENDIAN) { + for(int i=0; i<bits; i++) { + if(getBit()) { + res|=(1L<<i); + } + } + } + else { + for(int i=bits-1; i>=0; i--) { + if(getBit()) { + res|=(1L<<i); + } + } + } + return res; + } + + /** + * <p>reads an integer encoded as "signed rice" as described in + * the FLAC audio format specification</p> + * + * <p><b>not supported for little endian</b></p> + * + * @param order + * @return the decoded integer value read from the stream + * + * @throws IOException if an I/O error occurs + * @throws UnsupportedOperationException if the method is not supported by the implementation + */ + + public int readSignedRice(int order) throws IOException { + + int msbs=-1, lsbs=0, res=0; + + if(endian==LITTLE_ENDIAN) { + // little endian + throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode"); + } + else { + // big endian + + byte cb=source[byteIndex]; + do { + msbs++; + if(bitIndex<0) { + bitIndex=7; + byteIndex++; + cb=source[byteIndex]; + } + } while((cb&(1<<bitIndex--))==0); + + int bits=order; + + if(bitIndex<0) { + bitIndex=7; + byteIndex++; + } + if(bits<=bitIndex+1) { + int ci=((int)source[byteIndex])&0xff; + int offset=1+bitIndex-bits; + int mask=((1<<bits)-1)<<offset; + lsbs=(ci&mask)>>offset; + bitIndex-=bits; + } + else { + lsbs=(((int)source[byteIndex])&0xff&((1<<(bitIndex+1))-1))<<(bits-bitIndex-1); + bits-=bitIndex+1; + byteIndex++; + while(bits>=8) { + bits-=8; + lsbs|=(((int)source[byteIndex])&0xff)<<bits; + byteIndex++; + } + if(bits>0) { + int ci=((int)source[byteIndex])&0xff; + lsbs|=(ci>>(8-bits))&((1<<bits)-1); + bitIndex=7-bits; + } + else { + byteIndex--; + bitIndex=-1; + } + } + + res=(msbs<<order)|lsbs; + } + + return (res&1)==1?-(res>>1)-1:(res>>1); + } + + /** + * <p>fills the array from <code>offset</code> with <code |