1
0
Fork 0
forked from len0rd/rockbox

Songdb java version, source. only 1.5 compatible

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michiel Van Der Kolk 2005-07-11 15:42:37 +00:00
parent dfa8ecbe60
commit 9fee0ec4ca
306 changed files with 63685 additions and 0 deletions

76
songdbj/AlbumEntry.java Normal file
View file

@ -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;
}
}

56
songdbj/ArtistEntry.java Normal file
View file

@ -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;
}
}

14
songdbj/Entry.java Normal file
View file

@ -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;
}

155
songdbj/FileEntry.java Normal file
View file

@ -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;
}
}

367
songdbj/MpegInfo.java Normal file
View file

@ -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;
}
}

311
songdbj/OggVorbisInfo.java Normal file
View file

@ -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;
}
}

28
songdbj/RundbEntry.java Normal file
View file

@ -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;
}
}

View file

@ -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();
}
}

74
songdbj/SongDB.java Normal file
View file

@ -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);
}
}
}

167
songdbj/SongEntry.java Normal file
View file

@ -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;
}
}

377
songdbj/TagDatabase.java Normal file
View file

@ -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();
}
}

112
songdbj/TagInfo.java Normal file
View file

@ -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();
}

2
songdbj/build.sh Executable file
View file

@ -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/ .

View file

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Created-By: Apache Ant 1.5.1
Main-Class: SongDB

View file

@ -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

View file

@ -0,0 +1,3 @@
# for the javalayer mp3 decoder
javazoom.spi.mpeg.sampled.convert.MpegFormatConversionProvider
org.tritonus.sampled.convert.vorbis.VorbisFormatConversionProvider

View file

@ -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");
}
*/
}

View file

@ -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);
}
}
}
*/
}

View file

@ -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);
}
*/
}

View file

@ -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);
}
}

View file

@ -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; }
}

View file

@ -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;
};

View file

@ -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]));
}
}

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -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.");
}
}

File diff suppressed because it is too large Load diff

View file

@ -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));
}
*/
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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]);
}
}

View file

@ -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;
}
}
*/
}

View file

@ -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);
}
}
}

View file

@ -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
}

View file

@ -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);
}
}

View file

@ -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(){}
}

View file

@ -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]);
}
*/
}
}

View file

@ -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
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}

View file

@ -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");
}
*/
}

View file

@ -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;}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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() {
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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>len</code>
* integers encoded as "signed rice" as described in
* the FLAC audio format specification</p>
*
* <p><b>not supported for little endian</b></p>
*
* @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 off, int len) throws IOException {
if(endian==LITTLE_ENDIAN) {
// little endian
throw new UnsupportedOperationException("ByteArrayBitInputStream.readSignedRice() is only supported in big endian mode");
}
else {
// big endian
for(int i=off; i<off+len; i++) {
int msbs=-1, lsbs=0;
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;
}
}
int res=(msbs<<order)|lsbs;
buffer[i]=(res&1)==1?-(res>>1)-1:(res>>1);
}
}
}
public void align() {
if(endian==BIG_ENDIAN && bitIndex>=0) {
bitIndex=7;
byteIndex++;
}
else if(endian==LITTLE_ENDIAN && bitIndex<=7) {
bitIndex=0;
byteIndex++;
}
}
public void setEndian(int endian) {
if(this.endian==BIG_ENDIAN && endian==LITTLE_ENDIAN) {
bitIndex=0;
byteIndex++;
}
else if(this.endian==LITTLE_ENDIAN && endian==BIG_ENDIAN) {
bitIndex=7;
byteIndex++;
}
this.endian=endian;
}
/**
* @return the byte array used as a source for this instance
*/
public byte[] getSource() {
return source;
}
}

View file

@ -0,0 +1,144 @@
/*
* $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:31 jarnbjo
* no message
*
*/
package de.jarnbjo.util.io;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
/**
* Representation of a node in a Huffman tree, used to read
* Huffman compressed codewords from e.g. a Vorbis stream.
*/
final public class HuffmanNode {
private HuffmanNode parent;
private int depth=0;
protected HuffmanNode o0, o1;
protected Integer value;
private boolean full=false;
/**
* creates a new Huffman tree root node
*/
public HuffmanNode() {
this(null);
}
protected HuffmanNode(HuffmanNode parent) {
this.parent=parent;
if(parent!=null) {
depth=parent.getDepth()+1;
}
}
protected HuffmanNode(HuffmanNode parent, int value) {
this(parent);
this.value=new Integer(value);
full=true;
}
protected int read(BitInputStream bis) throws IOException {
HuffmanNode iter=this;
while(iter.value==null) {
iter=bis.getBit()?iter.o1:iter.o0;
}
return iter.value.intValue();
}
protected HuffmanNode get0() {
return o0==null?set0(new HuffmanNode(this)):o0;
}
protected HuffmanNode get1() {
return o1==null?set1(new HuffmanNode(this)):o1;
}
protected Integer getValue() {
return value;
}
private HuffmanNode getParent() {
return parent;
}
protected int getDepth() {
return depth;
}
private boolean isFull() {
return full?true:(full=o0!=null&&o0.isFull()&&o1!=null&&o1.isFull());
}
private HuffmanNode set0(HuffmanNode value) {
return o0=value;
}
private HuffmanNode set1(HuffmanNode value) {
return o1=value;
}
private void setValue(Integer value) {
full=true;
this.value=value;
}
/**
* creates a new tree node at the first free location at the given
* depth, and assigns the value to it
*
* @param depth the tree depth of the new node (codeword length in bits)
* @param value the node's new value
*/
public boolean setNewValue(int depth, int value) {
if(isFull()) {
return false;
}
if(depth==1) {
if(o0==null) {
set0(new HuffmanNode(this, value));
return true;
}
else if(o1==null) {
set1(new HuffmanNode(this, value));
return true;
}
else {
return false;
}
}
else {
return get0().setNewValue(depth-1, value)?
true:
get1().setNewValue(depth-1, value);
}
}
}

View file

@ -0,0 +1,328 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class AudioPacket {
private int modeNumber;
private Mode mode;
private Mapping mapping;
private int n; // block size
private boolean blockFlag, previousWindowFlag, nextWindowFlag;
private int windowCenter, leftWindowStart, leftWindowEnd, leftN, rightWindowStart, rightWindowEnd, rightN;
private float[] window;
private float[][] pcm;
private int[][] pcmInt;
private Floor[] channelFloors;
private boolean[] noResidues;
private final static float[][] windows=new float[8][];
protected AudioPacket(final VorbisStream vorbis, final BitInputStream source) throws VorbisFormatException, IOException {
final SetupHeader sHeader=vorbis.getSetupHeader();
final IdentificationHeader iHeader=vorbis.getIdentificationHeader();
final Mode[] modes=sHeader.getModes();
final Mapping[] mappings=sHeader.getMappings();
final Residue[] residues=sHeader.getResidues();
final int channels=iHeader.getChannels();
if(source.getInt(1)!=0) {
throw new VorbisFormatException("Packet type mismatch when trying to create an audio packet.");
}
modeNumber=source.getInt(Util.ilog(modes.length-1));
try {
mode=modes[modeNumber];
}
catch(ArrayIndexOutOfBoundsException e) {
throw new VorbisFormatException("Reference to invalid mode in audio packet.");
}
mapping=mappings[mode.getMapping()];
final int[] magnitudes=mapping.getMagnitudes();
final int[] angles=mapping.getAngles();
blockFlag=mode.getBlockFlag();
final int blockSize0=iHeader.getBlockSize0();
final int blockSize1=iHeader.getBlockSize1();
n=blockFlag?blockSize1:blockSize0;
if(blockFlag) {
previousWindowFlag=source.getBit();
nextWindowFlag=source.getBit();
}
windowCenter=n/2;
if(blockFlag && !previousWindowFlag) {
leftWindowStart=n/4-blockSize0/4;
leftWindowEnd=n/4+blockSize0/4;
leftN=blockSize0/2;
}
else {
leftWindowStart=0;
leftWindowEnd=n/2;
leftN=windowCenter;
}
if(blockFlag && !nextWindowFlag) {
rightWindowStart=n*3/4-blockSize0/4;
rightWindowEnd=n*3/4+blockSize0/4;
rightN=blockSize0/2;
}
else {
rightWindowStart=windowCenter;
rightWindowEnd=n;
rightN=n/2;
}
window=getComputedWindow();//new double[n];
channelFloors=new Floor[channels];
noResidues=new boolean[channels];
pcm=new float[channels][n];
pcmInt=new int[channels][n];
boolean allFloorsEmpty=true;
for(int i=0; i<channels; i++) {
int submapNumber=mapping.getMux()[i];
int floorNumber=mapping.getSubmapFloors()[submapNumber];
Floor decodedFloor=sHeader.getFloors()[floorNumber].decodeFloor(vorbis, source);
channelFloors[i]=decodedFloor;
noResidues[i]=decodedFloor==null;
if(decodedFloor!=null) {
allFloorsEmpty=false;
}
}
if(allFloorsEmpty) {
return;
}
for(int i=0; i<magnitudes.length; i++) {
if(!noResidues[magnitudes[i]] ||
!noResidues[angles[i]]) {
noResidues[magnitudes[i]]=false;
noResidues[angles[i]]=false;
}
}
Residue[] decodedResidues=new Residue[mapping.getSubmaps()];
for(int i=0; i<mapping.getSubmaps(); i++) {
int ch=0;
boolean[] doNotDecodeFlags=new boolean[channels];
for(int j=0; j<channels; j++) {
if(mapping.getMux()[j]==i) {
doNotDecodeFlags[ch++]=noResidues[j];
}
}
int residueNumber=mapping.getSubmapResidues()[i];
Residue residue=residues[residueNumber];
residue.decodeResidue(vorbis, source, mode, ch, doNotDecodeFlags, pcm);
}
for(int i=mapping.getCouplingSteps()-1; i>=0; i--) {
double newA=0, newM=0;
final float[] magnitudeVector=pcm[magnitudes[i]];
final float[] angleVector=pcm[angles[i]];
for(int j=0; j<magnitudeVector.length; j++) {
float a=angleVector[j];
float m=magnitudeVector[j];
if(a>0) {
//magnitudeVector[j]=m;
angleVector[j]=m>0?m-a:m+a;
}
else {
magnitudeVector[j]=m>0?m+a:m-a;
angleVector[j]=m;
}
}
}
for(int i=0; i<channels; i++) {
if(channelFloors[i]!=null) {
channelFloors[i].computeFloor(pcm[i]);
}
}
// perform an inverse mdct to all channels
for(int i=0; i<channels; i++) {
MdctFloat mdct=blockFlag?iHeader.getMdct1():iHeader.getMdct0();
mdct.imdct(pcm[i], window, pcmInt[i]);
}
}
private float[] getComputedWindow() {
int ix=(blockFlag?4:0)+(previousWindowFlag?2:0)+(nextWindowFlag?1:0);
float[] w=windows[ix];
if(w==null) {
w=new float[n];
for(int i=0;i<leftN;i++){
float x=(float)((i+.5)/leftN*Math.PI/2.);
x=(float)Math.sin(x);
x*=x;
x*=(float)Math.PI/2.;
x=(float)Math.sin(x);
w[i+leftWindowStart]=x;
}
for(int i=leftWindowEnd; i<rightWindowStart; w[i++]=1.0f);
for(int i=0;i<rightN;i++){
float x=(float)((rightN-i-.5)/rightN*Math.PI/2.);
x=(float)Math.sin(x);
x*=x;
x*=(float)Math.PI/2.;
x=(float)Math.sin(x);
w[i+rightWindowStart]=x;
}
windows[ix]=w;
}
return w;
}
protected int getNumberOfSamples() {
return rightWindowStart-leftWindowStart;
}
protected int getPcm(final AudioPacket previousPacket, final int[][] buffer) {
int channels=pcm.length;
int val;
// copy left window flank and mix with right window flank from
// the previous audio packet
for(int i=0; i<channels; i++) {
int j1=0, j2=previousPacket.rightWindowStart;
final int[] ppcm=previousPacket.pcmInt[i];
final int[] tpcm=pcmInt[i];
final int[] target=buffer[i];
for(int j=leftWindowStart; j<leftWindowEnd; j++) {
val=ppcm[j2++]+tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
target[j1++]=val;
}
}
// use System.arraycopy to copy the middle part (if any)
// of the window
if(leftWindowEnd+1<rightWindowStart) {
for(int i=0; i<channels; i++) {
System.arraycopy(pcmInt[i], leftWindowEnd, buffer[i], leftWindowEnd-leftWindowStart, rightWindowStart-leftWindowEnd);
}
}
return rightWindowStart-leftWindowStart;
}
protected void getPcm(final AudioPacket previousPacket, final byte[] buffer) {
int channels=pcm.length;
int val;
// copy left window flank and mix with right window flank from
// the previous audio packet
for(int i=0; i<channels; i++) {
int ix=0, j2=previousPacket.rightWindowStart;
final int[] ppcm=previousPacket.pcmInt[i];
final int[] tpcm=pcmInt[i];
for(int j=leftWindowStart; j<leftWindowEnd; j++) {
val=ppcm[j2++]+tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
buffer[ix+(i*2)+1]=(byte)(val&0xff);
buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
ix+=channels*2;
}
ix=(leftWindowEnd-leftWindowStart)*channels*2;
for(int j=leftWindowEnd; j<rightWindowStart; j++) {
val=tpcm[j];
if(val>32767) val=32767;
if(val<-32768) val=-32768;
buffer[ix+(i*2)+1]=(byte)(val&0xff);
buffer[ix+(i*2)]=(byte)((val>>8)&0xff);
ix+=channels*2;
}
}
}
protected float[] getWindow() {
return window;
}
protected int getLeftWindowStart() {
return leftWindowStart;
}
protected int getLeftWindowEnd() {
return leftWindowEnd;
}
protected int getRightWindowStart() {
return rightWindowStart;
}
protected int getRightWindowEnd() {
return rightWindowEnd;
}
public int[][] getPcm() {
return pcmInt;
}
public float[][] getFreqencyDomain() {
return pcm;
}
}

View file

@ -0,0 +1,275 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.3 2003/04/10 19:49:04 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import java.util.Arrays;
import de.jarnbjo.util.io.BitInputStream;
import de.jarnbjo.util.io.HuffmanNode;
class CodeBook {
private HuffmanNode huffmanRoot;
private int dimensions, entries;
private int[] entryLengths;
private float[][] valueVector;
protected CodeBook(BitInputStream source) throws VorbisFormatException, IOException {
// check sync
if(source.getInt(24)!=0x564342) {
throw new VorbisFormatException("The code book sync pattern is not correct.");
}
dimensions=source.getInt(16);
entries=source.getInt(24);
entryLengths=new int[entries];
boolean ordered=source.getBit();
if(ordered) {
int cl=source.getInt(5)+1;
for(int i=0; i<entryLengths.length; ) {
int num=source.getInt(Util.ilog(entryLengths.length-i));
if(i+num>entryLengths.length) {
throw new VorbisFormatException("The codebook entry length list is longer than the actual number of entry lengths.");
}
Arrays.fill(entryLengths, i, i+num, cl);
cl++;
i+=num;
}
}
else {
// !ordered
boolean sparse=source.getBit();
if(sparse) {
for(int i=0; i<entryLengths.length; i++) {
if(source.getBit()) {
entryLengths[i]=source.getInt(5)+1;
}
else {
entryLengths[i]=-1;
}
}
}
else {
// !sparse
for(int i=0; i<entryLengths.length; i++) {
entryLengths[i]=source.getInt(5)+1;
}
}
}
if (!createHuffmanTree(entryLengths)) {
throw new VorbisFormatException("An exception was thrown when building the codebook Huffman tree.");
}
int codeBookLookupType=source.getInt(4);
switch(codeBookLookupType) {
case 0:
// codebook has no scalar vectors to be calculated
break;
case 1:
case 2:
float codeBookMinimumValue=Util.float32unpack(source.getInt(32));
float codeBookDeltaValue=Util.float32unpack(source.getInt(32));
int codeBookValueBits=source.getInt(4)+1;
boolean codeBookSequenceP=source.getBit();
int codeBookLookupValues=0;
if(codeBookLookupType==1) {
codeBookLookupValues=Util.lookup1Values(entries, dimensions);
}
else {
codeBookLookupValues=entries*dimensions;
}
int codeBookMultiplicands[]=new int[codeBookLookupValues];
for(int i=0; i<codeBookMultiplicands.length; i++) {
codeBookMultiplicands[i]=source.getInt(codeBookValueBits);
}
valueVector=new float[entries][dimensions];
if(codeBookLookupType==1) {
for(int i=0; i<entries; i++) {
float last=0;
int indexDivisor=1;
for(int j=0; j<dimensions; j++) {
int multiplicandOffset=
(i/indexDivisor)%codeBookLookupValues;
valueVector[i][j]=
codeBookMultiplicands[multiplicandOffset]*codeBookDeltaValue+codeBookMinimumValue+last;
if(codeBookSequenceP) {
last=valueVector[i][j];
}
indexDivisor*=codeBookLookupValues;
}
}
}
else {
throw new UnsupportedOperationException();
/** @todo implement */
}
break;
default:
throw new VorbisFormatException("Unsupported codebook lookup type: "+codeBookLookupType);
}
}
private static long totalTime=0;
private boolean createHuffmanTree(int[] entryLengths) {
huffmanRoot=new HuffmanNode();
for(int i=0; i<entryLengths.length; i++) {
int el=entryLengths[i];
if(el>0) {
if(!huffmanRoot.setNewValue(el, i)) {
return false;
}
}
}
return true;
}
protected int getDimensions() {
return dimensions;
}
protected int getEntries() {
return entries;
}
protected HuffmanNode getHuffmanRoot() {
return huffmanRoot;
}
//public float[] readVQ(ReadableBitChannel source) throws IOException {
// return valueVector[readInt(source)];
//}
protected int readInt(final BitInputStream source) throws IOException {
return source.getInt(huffmanRoot);
/*
HuffmanNode node;
for(node=huffmanRoot; node.value==null; node=source.getBit()?node.o1:node.o0);
return node.value.intValue();
*/
}
protected void readVvAdd(float[][] a, BitInputStream source, int offset, int length)
throws VorbisFormatException, IOException {
int i,j;//k;//entry;
int chptr=0;
int ch=a.length;
if(ch==0) {
return;
}
int lim=(offset+length)/ch;
for(i=offset/ch;i<lim;){
final float[] ve=valueVector[source.getInt(huffmanRoot)];
for(j=0;j<dimensions;j++){
a[chptr++][i]+=ve[j];
if(chptr==ch){
chptr=0;
i++;
}
}
}
}
/*
public void readVAdd(double[] a, ReadableBitChannel source, int offset, int length)
throws FormatException, IOException {
int i,j,entry;
int t;
if(dimensions>8){
for(i=0;i<length;){
entry = readInt(source);
//if(entry==-1)return(-1);
//t=entry*dimensions;
for(j=0;j<dimensions;){
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
}
}
}
else{
for(i=0;i<length;){
entry=readInt(source);
//if(entry==-1)return(-1);
//t=entry*dim;
j=0;
switch(dimensions){
case 8:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 7:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 6:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 5:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 4:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 3:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 2:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 1:
a[offset+(i++)]+=valueVector[entry][j++];//valuelist[t+(j++)];
case 0:
break;
}
}
}
}
*/
}

View file

@ -0,0 +1,244 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.*;
import java.util.*;
import de.jarnbjo.util.io.BitInputStream;
/**
*/
public class CommentHeader {
public static final String TITLE = "TITLE";
public static final String ARTIST = "ARTIST";
public static final String ALBUM = "ALBUM";
public static final String TRACKNUMBER = "TRACKNUMBER";
public static final String VERSION = "VERSION";
public static final String PERFORMER = "PERFORMER";
public static final String COPYRIGHT = "COPYRIGHT";
public static final String LICENSE = "LICENSE";
public static final String ORGANIZATION = "ORGANIZATION";
public static final String DESCRIPTION = "DESCRIPTION";
public static final String GENRE = "GENRE";
public static final String DATE = "DATE";
public static final String LOCATION = "LOCATION";
public static final String CONTACT = "CONTACT";
public static final String ISRC = "ISRC";
private String vendor;
private HashMap comments=new HashMap();
private boolean framingBit;
private static final long HEADER = 0x736962726f76L; // 'vorbis'
public CommentHeader(BitInputStream source) throws VorbisFormatException, IOException {
if(source.getLong(48)!=HEADER) {
throw new VorbisFormatException("The identification header has an illegal leading.");
}
vendor=getString(source);
int ucLength=source.getInt(32);
for(int i=0; i<ucLength; i++) {
String comment=getString(source);
int ix=comment.indexOf('=');
String key=comment.substring(0, ix);
String value=comment.substring(ix+1);
//comments.put(key, value);
addComment(key, value);
}
framingBit=source.getInt(8)!=0;
}
private void addComment(String key, String value) {
key = key.toUpperCase(); // Comment keys are case insensitive
ArrayList al=(ArrayList)comments.get(key);
if(al==null) {
al=new ArrayList();
comments.put(key, al);
}
al.add(value);
}
public String getVendor() {
return vendor;
}
public String getComment(String key) {
ArrayList al=(ArrayList)comments.get(key);
return al==null?(String)null:(String)al.get(0);
}
public String[] getComments(String key) {
ArrayList al=(ArrayList)comments.get(key);
return al==null?new String[0]:(String[])al.toArray(new String[al.size()]);
}
public String getTitle() {
return getComment(TITLE);
}
public String[] getTitles() {
return getComments(TITLE);
}
public String getVersion() {
return getComment(VERSION);
}
public String[] getVersions() {
return getComments(VERSION);
}
public String getAlbum() {
return getComment(ALBUM);
}
public String[] getAlbums() {
return getComments(ALBUM);
}
public String getTrackNumber() {
return getComment(TRACKNUMBER);
}
public String[] getTrackNumbers() {
return getComments(TRACKNUMBER);
}
public String getArtist() {
return getComment(ARTIST);
}
public String[] getArtists() {
return getComments(ARTIST);
}
public String getPerformer() {
return getComment(PERFORMER);
}
public String[] getPerformers() {
return getComments(PERFORMER);
}
public String getCopyright() {
return getComment(COPYRIGHT);
}
public String[] getCopyrights() {
return getComments(COPYRIGHT);
}
public String getLicense() {
return getComment(LICENSE);
}
public String[] getLicenses() {
return getComments(LICENSE);
}
public String getOrganization() {
return getComment(ORGANIZATION);
}
public String[] getOrganizations() {
return getComments(ORGANIZATION);
}
public String getDescription() {
return getComment(DESCRIPTION);
}
public String[] getDescriptions() {
return getComments(DESCRIPTION);
}
public String getGenre() {
return getComment(GENRE);
}
public String[] getGenres() {
return getComments(GENRE);
}
public String getDate() {
return getComment(DATE);
}
public String[] getDates() {
return getComments(DATE);
}
public String getLocation() {
return getComment(LOCATION);
}
public String[] getLocations() {
return getComments(LOCATION);
}
public String getContact() {
return getComment(CONTACT);
}
public String[] getContacts() {
return getComments(CONTACT);
}
public String getIsrc() {
return getComment(ISRC);
}
public String[] getIsrcs() {
return getComments(ISRC);
}
private String getString(BitInputStream source) throws IOException, VorbisFormatException {
int length=source.getInt(32);
byte[] strArray=new byte[length];
for(int i=0; i<length; i++) {
strArray[i]=(byte)source.getInt(8);
}
return new String(strArray, "UTF-8");
}
}

View file

@ -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:49:04 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
public abstract class Floor {
public final static float[] DB_STATIC_TABLE={
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.0f};
static Floor createInstance(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
int type=source.getInt(16);
switch(type) {
case 0:
return new Floor0(source, header);
case 1:
return new Floor1(source, header);
default:
throw new VorbisFormatException("Floor type "+type+" is not supported.");
}
}
abstract int getType();
abstract Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException;
abstract void computeFloor(float[] vector);
}

View file

@ -0,0 +1,74 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class Floor0 extends Floor {
private int order, rate, barkMapSize, amplitudeBits, amplitudeOffset;
private int bookList[];
protected Floor0(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
order=source.getInt(8);
rate=source.getInt(16);
barkMapSize=source.getInt(16);
amplitudeBits=source.getInt(6);
amplitudeOffset=source.getInt(8);
int bookCount=source.getInt(4)+1;
bookList=new int[bookCount];
for(int i=0; i<bookList.length; i++) {
bookList[i]=source.getInt(8);
if(bookList[i]>header.getCodeBooks().length) {
throw new VorbisFormatException("A floor0_book_list entry is higher than the code book count.");
}
}
}
protected int getType() {
return 0;
}
protected Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException {
/** @todo implement */
throw new UnsupportedOperationException();
}
protected void computeFloor(float[] vector) {
/** @todo implement */
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,324 @@
/*
* $ProjectName$
* $ProjectRevision$
* -----------------------------------------------------------
* $Id$multip
* -----------------------------------------------------------
*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import java.util.*;
import de.jarnbjo.util.io.BitInputStream;
class Floor1 extends Floor implements Cloneable {
private int[] partitionClassList;
private int maximumClass, multiplier, rangeBits;
private int[] classDimensions;
private int[] classSubclasses;
private int[] classMasterbooks;
private int[][] subclassBooks;
private int[] xList;
private int[] yList;
private int[] lowNeighbours, highNeighbours;
//private boolean[] step2Flags;
private static final int[] RANGES = {256, 128, 86, 64};
private Floor1() {
}
protected Floor1(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
maximumClass=-1;
int partitions=source.getInt(5);
partitionClassList=new int[partitions];
for(int i=0; i<partitionClassList.length; i++) {
partitionClassList[i]=source.getInt(4);
if(partitionClassList[i]>maximumClass) {
maximumClass=partitionClassList[i];
}
}
classDimensions=new int[maximumClass+1];
classSubclasses=new int[maximumClass+1];
classMasterbooks=new int[maximumClass+1];
subclassBooks=new int[maximumClass+1][];
int xListLength=2;
for(int i=0; i<=maximumClass; i++) {
classDimensions[i]=source.getInt(3)+1;
xListLength+=classDimensions[i];
classSubclasses[i]=source.getInt(2);
if(classDimensions[i] > header.getCodeBooks().length ||
classSubclasses[i] > header.getCodeBooks().length) {
throw new VorbisFormatException("There is a class dimension or class subclasses entry higher than the number of codebooks in the setup header.");
}
if(classSubclasses[i]!=0) {
classMasterbooks[i]=source.getInt(8);
}
subclassBooks[i]=new int[1<<classSubclasses[i]];
for(int j=0; j<subclassBooks[i].length; j++) {
subclassBooks[i][j]=source.getInt(8)-1;
}
}
multiplier=source.getInt(2)+1;
rangeBits=source.getInt(4);
//System.out.println("multiplier: "+multiplier);
//System.out.println("rangeBits: "+rangeBits);
//System.out.println("xListLength: "+xListLength);
int floorValues=0;
ArrayList alXList=new ArrayList();
alXList.add(new Integer(0));
alXList.add(new Integer(1<<rangeBits));
//System.out.println("partitions: "+partitions);
//System.out.println("classDimensions.length: "+classDimensions.length);
for(int i=0; i<partitions; i++) {
for(int j=0; j<classDimensions[partitionClassList[i]]; j++) {
alXList.add(new Integer(source.getInt(rangeBits)));
}
}
xList=new int[alXList.size()];
lowNeighbours=new int[xList.length];
highNeighbours=new int[xList.length];
Iterator iter=alXList.iterator();
for(int i=0; i<xList.length; i++) {
xList[i]=((Integer)iter.next()).intValue();
}
for(int i=0; i<xList.length; i++) {
lowNeighbours[i]=Util.lowNeighbour(xList, i);
highNeighbours[i]=Util.highNeighbour(xList, i);
}
}
protected int getType() {
return 1;
}
protected Floor decodeFloor(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException {
//System.out.println("decodeFloor");
if(!source.getBit()) {
//System.out.println("null");
return null;
}
Floor1 clone=(Floor1)clone();
clone.yList=new int[xList.length];
int range=RANGES[multiplier-1];
clone.yList[0]=source.getInt(Util.ilog(range-1));
clone.yList[1]=source.getInt(Util.ilog(range-1));
int offset=2;
for(int i=0; i<partitionClassList.length; i++) {
int cls=partitionClassList[i];
int cdim=classDimensions[cls];
int cbits=classSubclasses[cls];
int csub=(1<<cbits)-1;
int cval=0;
if(cbits>0) {
cval=source.getInt(vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].getHuffmanRoot());
//cval=vorbis.getSetupHeader().getCodeBooks()[classMasterbooks[cls]].readInt(source);
//System.out.println("cval: "+cval);
}
//System.out.println("0: "+cls+" "+cdim+" "+cbits+" "+csub+" "+cval);
for(int j=0; j<cdim; j++) {
//System.out.println("a: "+cls+" "+cval+" "+csub);
int book=subclassBooks[cls][cval&csub];
cval>>>=cbits;
if(book>=0) {
clone.yList[j+offset]=source.getInt(vorbis.getSetupHeader().getCodeBooks()[book].getHuffmanRoot());
//clone.yList[j+offset]=vorbis.getSetupHeader().getCodeBooks()[book].readInt(source);
//System.out.println("b: "+(j+offset)+" "+book+" "+clone.yList[j+offset]);
//System.out.println("");
}
else {
clone.yList[j+offset]=0;
}
}
offset+=cdim;
}
//System.out.println("");
//for(int i=0; i<clone.xList.length; i++) {
// System.out.println(i+" = "+clone.xList[i]);
//}
//System.out.println("");
//for(int i=0; i<clone.yList.length; i++) {
// System.out.println(i+" = "+clone.yList[i]);
//}
//System.out.println("offset: "+offset);
//System.out.println("yList.length: "+clone.yList.length);
//System.exit(0);
return clone;
}
protected void computeFloor(final float[] vector) {
int n=vector.length;
final int values=xList.length;
final boolean[] step2Flags=new boolean[values];
final int range=RANGES[multiplier-1];
for(int i=2; i<values; i++) {
final int lowNeighbourOffset=lowNeighbours[i];//Util.lowNeighbour(xList, i);
final int highNeighbourOffset=highNeighbours[i];//Util.highNeighbour(xList, i);
final int predicted=Util.renderPoint(
xList[lowNeighbourOffset], xList[highNeighbourOffset],
yList[lowNeighbourOffset], yList[highNeighbourOffset],
xList[i]);
final int val=yList[i];
final int highRoom=range-predicted;
final int lowRoom=predicted;
final int room=highRoom<lowRoom?highRoom*2:lowRoom*2;
if(val!=0) {
step2Flags[lowNeighbourOffset]=true;
step2Flags[highNeighbourOffset]=true;
step2Flags[i]=true;
if(val>=room) {
yList[i]=highRoom>lowRoom?
val-lowRoom+predicted:
-val+highRoom+predicted-1;
}
else {
yList[i]=(val&1)==1?
predicted-((val+1)>>1):
predicted+(val>>1);
}
}
else {
step2Flags[i]=false;
yList[i]=predicted;
}
}
final int[] xList2=new int[values];
System.arraycopy(xList, 0, xList2, 0, values);
sort(xList2, yList, step2Flags);
int hx=0, hy=0, lx=0, ly=yList[0]*multiplier;
float[] vector2=new float[vector.length];
float[] vector3=new float[vector.length];
Arrays.fill(vector2, 1.0f);
System.arraycopy(vector, 0, vector3, 0, vector.length);
for(int i=1; i<values; i++) {
if(step2Flags[i]) {
hy=yList[i]*multiplier;
hx=xList2[i];
Util.renderLine(lx, ly, hx, hy, vector);
Util.renderLine(lx, ly, hx, hy, vector2);
lx=hx;
ly=hy;
}
}
final float r=DB_STATIC_TABLE[hy];
for(; hx<n/2; vector[hx++]=r);
}
public Object clone() {
Floor1 clone=new Floor1();
clone.classDimensions=classDimensions;
clone.classMasterbooks=classMasterbooks;
clone.classSubclasses=classSubclasses;
clone.maximumClass=maximumClass;
clone.multiplier=multiplier;
clone.partitionClassList=partitionClassList;
clone.rangeBits=rangeBits;
clone.subclassBooks=subclassBooks;
clone.xList=xList;
clone.yList=yList;
clone.lowNeighbours=lowNeighbours;
clone.highNeighbours=highNeighbours;
return clone;
}
private final static void sort(int x[], int y[], boolean b[]) {
int off=0;
int len=x.length;
int lim=len+off;
int itmp;
boolean btmp;
// Insertion sort on smallest arrays
for (int i=off; i<lim; i++) {
for (int j=i; j>off && x[j-1]>x[j]; j--) {
itmp=x[j];
x[j]=x[j-1];
x[j-1]=itmp;
itmp=y[j];
y[j]=y[j-1];
y[j-1]=itmp;
btmp=b[j];
b[j]=b[j-1];
b[j-1]=btmp;
//swap(x, j, j-1);
//swap(y, j, j-1);
//swap(b, j, j-1);
}
}
}
private final static void swap(int x[], int a, int b) {
int t = x[a];
x[a] = x[b];
x[b] = t;
}
private final static void swap(boolean x[], int a, int b) {
boolean t = x[a];
x[a] = x[b];
x[b] = t;
}
}

View file

@ -0,0 +1,120 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.3 2003/03/31 00:20:16 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
/**
*/
public class IdentificationHeader {
private int version, channels, sampleRate, bitrateMaximum, bitrateNominal, bitrateMinimum, blockSize0, blockSize1;
private boolean framingFlag;
private MdctFloat[] mdct=new MdctFloat[2];
//private MdctLong[] mdctInt=new MdctLong[2];
private static final long HEADER = 0x736962726f76L; // 'vorbis'
public IdentificationHeader(BitInputStream source) throws VorbisFormatException, IOException {
//equalizer=new Equalizer();
//equalizer.pack();
//equalizer.show();
long leading=source.getLong(48);
if(leading!=HEADER) {
throw new VorbisFormatException("The identification header has an illegal leading.");
}
version=source.getInt(32);
channels=source.getInt(8);
sampleRate=source.getInt(32);
bitrateMaximum=source.getInt(32);
bitrateNominal=source.getInt(32);
bitrateMinimum=source.getInt(32);
int bs=source.getInt(8);
blockSize0=1<<(bs&0xf);
blockSize1=1<<(bs>>4);
mdct[0]=new MdctFloat(blockSize0);
mdct[1]=new MdctFloat(blockSize1);
//mdctInt[0]=new MdctLong(blockSize0);
//mdctInt[1]=new MdctLong(blockSize1);
framingFlag=source.getInt(8)!=0;
}
public int getSampleRate() {
return sampleRate;
}
public int getMaximumBitrate() {
return bitrateMaximum;
}
public int getNominalBitrate() {
return bitrateNominal;
}
public int getMinimumBitrate() {
return bitrateMinimum;
}
public int getChannels() {
return channels;
}
public int getBlockSize0() {
return blockSize0;
}
public int getBlockSize1() {
return blockSize1;
}
protected MdctFloat getMdct0() {
return mdct[0];
}
protected MdctFloat getMdct1() {
return mdct[1];
}
public int getVersion() {
return version;
}
}

View file

@ -0,0 +1,59 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
abstract class Mapping {
protected static Mapping createInstance(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
int type=source.getInt(16);
switch(type) {
case 0:
//System.out.println("mapping type 0");
return new Mapping0(vorbis, source, header);
default:
throw new VorbisFormatException("Mapping type "+type+" is not supported.");
}
}
protected abstract int getType();
protected abstract int[] getAngles();
protected abstract int[] getMagnitudes() ;
protected abstract int[] getMux();
protected abstract int[] getSubmapFloors();
protected abstract int[] getSubmapResidues();
protected abstract int getCouplingSteps();
protected abstract int getSubmaps();
}

View file

@ -0,0 +1,146 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class Mapping0 extends Mapping {
private int[] magnitudes, angles, mux, submapFloors, submapResidues;
protected Mapping0(VorbisStream vorbis, BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
int submaps=1;
if(source.getBit()) {
submaps=source.getInt(4)+1;
}
//System.out.println("submaps: "+submaps);
int channels=vorbis.getIdentificationHeader().getChannels();
int ilogChannels=Util.ilog(channels-1);
//System.out.println("ilogChannels: "+ilogChannels);
if(source.getBit()) {
int couplingSteps=source.getInt(8)+1;
magnitudes=new int[couplingSteps];
angles=new int[couplingSteps];
for(int i=0; i<couplingSteps; i++) {
magnitudes[i]=source.getInt(ilogChannels);
angles[i]=source.getInt(ilogChannels);
if(magnitudes[i]==angles[i] || magnitudes[i]>=channels || angles[i]>=channels) {
System.err.println(magnitudes[i]);
System.err.println(angles[i]);
throw new VorbisFormatException("The channel magnitude and/or angle mismatch.");
}
}
}
else {
magnitudes=new int[0];
angles=new int[0];
}
if(source.getInt(2)!=0) {
throw new VorbisFormatException("A reserved mapping field has an invalid value.");
}
mux=new int[channels];
if(submaps>1) {
for(int i=0; i<channels; i++) {
mux[i]=source.getInt(4);
if(mux[i]>submaps) {
throw new VorbisFormatException("A mapping mux value is higher than the number of submaps");
}
}
}
else {
for(int i=0; i<channels; i++) {
mux[i]=0;
}
}
submapFloors=new int[submaps];
submapResidues=new int[submaps];
int floorCount=header.getFloors().length;
int residueCount=header.getResidues().length;
for(int i=0; i<submaps; i++) {
source.getInt(8); // discard time placeholder
submapFloors[i]=source.getInt(8);
submapResidues[i]=source.getInt(8);
if(submapFloors[i]>floorCount) {
throw new VorbisFormatException("A mapping floor value is higher than the number of floors.");
}
if(submapResidues[i]>residueCount) {
throw new VorbisFormatException("A mapping residue value is higher than the number of residues.");
}
}
}
protected int getType() {
return 0;
}
protected int[] getAngles() {
return angles;
}
protected int[] getMagnitudes() {
return magnitudes;
}
protected int[] getMux() {
return mux;
}
protected int[] getSubmapFloors() {
return submapFloors;
}
protected int[] getSubmapResidues() {
return submapResidues;
}
protected int getCouplingSteps() {
return angles.length;
}
protected int getSubmaps() {
return submapFloors.length;
}
}

View file

@ -0,0 +1,321 @@
/*
* $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 2004/09/21 12:09:45 shred
* *** empty log message ***
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.3 2003/04/10 19:49:04 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
class MdctFloat {
static private final float cPI3_8=0.38268343236508977175f;
static private final float cPI2_8=0.70710678118654752441f;
static private final float cPI1_8=0.92387953251128675613f;
private int n;
private int log2n;
private float[] trig;
private int[] bitrev;
private float[] equalizer;
private float scale;
private int itmp1, itmp2, itmp3, itmp4, itmp5, itmp6, itmp7, itmp8, itmp9;
private float dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, dtmp6, dtmp7, dtmp8, dtmp9;
protected MdctFloat(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){
//}
private float[] _x=new float[1024];
private float[] _w=new float[1024];
protected void setEqualizer(float[] equalizer) {
this.equalizer=equalizer;
}
protected float[] getEqualizer() {
return equalizer;
}
protected synchronized void imdct(final float[] frq, final float[] window, final int[] pcm) {//, float[] out){
float[] in=frq;//, out=buf;
if(_x.length<n/2){_x=new float[n/2];}
if(_w.length<n/2){_w=new float[n/2];}
final float[] x=_x;
final float[] w=_w;
int n2=n>>1;
int n4=n>>2;
int n8=n>>3;
if(equalizer!=null) {
for(int i=0; i<n; i++) {
frq[i]*=equalizer[i];
}
}
// rotate + step 1
{
int inO=-1;
int xO=0;
int A=n2;
int i;
for(i=0;i<n8;i++) {
dtmp1=in[inO+=2];
dtmp2=in[inO+=2];
dtmp3=trig[--A];
dtmp4=trig[--A];
x[xO++]=-dtmp2*dtmp3 - dtmp1*dtmp4;
x[xO++]= dtmp1*dtmp3 - dtmp2*dtmp4;
//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++) {
dtmp1=in[inO-=2];
dtmp2=in[inO-=2];
dtmp3=trig[--A];
dtmp4=trig[--A];
x[xO++]=dtmp2*dtmp3 + dtmp1*dtmp4;
x[xO++]=dtmp2*dtmp4 - dtmp1*dtmp3;
//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=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++){
dtmp1=xxx[xx++];
dtmp2=xxx[xx++];
dtmp3=trig[B++];
dtmp4=trig[B++];
float temp1= (dtmp1* dtmp4 - dtmp2 * dtmp3);
float temp2=-(dtmp1 * dtmp3 + dtmp2 * dtmp4);
/*
float temp1= (xxx[xx] * trig[B+1] - xxx[xx+1] * trig[B]);//*32767.0f;
float temp2=-(xxx[xx] * trig[B] + xxx[xx+1] * trig[B+1]);//*32767.0f;
*/
//if(temp1>32767.0f) temp1=32767.0f;
//if(temp1<-32768.0f) temp1=-32768.0f;
//if(temp2>32767.0f) temp2=32767.0f;
//if(temp2<-32768.0f) temp2=-32768.0f;
pcm[o1]=(int)(-temp1*window[o1]);
pcm[o2]=(int)( temp1*window[o2]);
pcm[o3]=(int)( temp2*window[o3]);
pcm[o4]=(int)( temp2*window[o4]);
o1++;
o2--;
o3++;
o4--;
//xx+=2;
//B+=2;
}
}
}
private float[] 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++){
dtmp1=w[w1];
dtmp2=w[w2];
wB=dtmp1-dtmp2;
x[w1]=dtmp1+dtmp2;
dtmp1=w[++w1];
dtmp2=w[++w2];
wA=dtmp1-dtmp2;
x[w1]=dtmp1+dtmp2;
x[w2] =wA*AEv - wB*AOv;
x[w2-1]=wB*AEv + wA*AOv;
/*
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)*16383.0f;
x[x2--]=(-wD+wBCO-wACE)*16383.0f;
x[x1++]=( wD+wBCO-wACE)*16383.0f;
x[x2--]=( wC-wACO-wBCE)*16383.0f;
}
}
return x;
}
}

View file

@ -0,0 +1,75 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.*;
import de.jarnbjo.util.io.*;
class Mode {
private boolean blockFlag;
private int windowType, transformType, mapping;
protected Mode(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
blockFlag=source.getBit();
windowType=source.getInt(16);
transformType=source.getInt(16);
mapping=source.getInt(8);
if(windowType!=0) {
throw new VorbisFormatException("Window type = "+windowType+", != 0");
}
if(transformType!=0) {
throw new VorbisFormatException("Transform type = "+transformType+", != 0");
}
if(mapping>header.getMappings().length) {
throw new VorbisFormatException("Mode mapping number is higher than total number of mappings.");
}
}
protected boolean getBlockFlag() {
return blockFlag;
}
protected int getWindowType() {
return windowType;
}
protected int getTransformType() {
return transformType;
}
protected int getMapping() {
return mapping;
}
}

View file

@ -0,0 +1,260 @@
/*
* $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/04 08:33:02 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import java.util.HashMap;
import de.jarnbjo.util.io.*;
abstract class Residue {
protected int begin, end;
protected int partitionSize; // grouping
protected int classifications; // partitions
protected int classBook; // groupbook
protected int[] cascade; // secondstages
protected int[][] books;
protected HashMap looks=new HashMap();
protected Residue() {
}
protected Residue(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
begin=source.getInt(24);
end=source.getInt(24);
partitionSize=source.getInt(24)+1;
classifications=source.getInt(6)+1;
classBook=source.getInt(8);
cascade=new int[classifications];
int acc=0;
for(int i=0; i<classifications; i++) {
int highBits=0, lowBits=0;
lowBits=source.getInt(3);
if(source.getBit()) {
highBits=source.getInt(5);
}
cascade[i]=(highBits<<3)|lowBits;
acc+=Util.icount(cascade[i]);
}
books=new int[classifications][8];
for(int i=0; i<classifications; i++) {
for(int j=0; j<8; j++) {
if((cascade[i]&(1<<j))!=0) {
books[i][j]=source.getInt(8);
if(books[i][j]>header.getCodeBooks().length) {
throw new VorbisFormatException("Reference to invalid codebook entry in residue header.");
}
}
}
}
}
protected static Residue createInstance(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
int type=source.getInt(16);
switch(type) {
case 0:
//System.out.println("residue type 0");
return new Residue0(source, header);
case 1:
//System.out.println("residue type 1");
return new Residue2(source, header);
case 2:
//System.out.println("residue type 2");
return new Residue2(source, header);
default:
throw new VorbisFormatException("Residue type "+type+" is not supported.");
}
}
protected abstract int getType();
protected abstract void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException;
//public abstract double[][] getDecodedVectors();
protected int getBegin() {
return begin;
}
protected int getEnd() {
return end;
}
protected int getPartitionSize() {
return partitionSize;
}
protected int getClassifications() {
return classifications;
}
protected int getClassBook() {
return classBook;
}
protected int[] getCascade() {
return cascade;
}
protected int[][] getBooks() {
return books;
}
protected final void fill(Residue clone) {
clone.begin=begin;
clone.books=books;
clone.cascade=cascade;
clone.classBook=classBook;
clone.classifications=classifications;
clone.end=end;
clone.partitionSize=partitionSize;
}
protected Look getLook(VorbisStream source, Mode key) {
//return new Look(source, key);
Look look=(Look)looks.get(key);
if(look==null) {
look=new Look(source, key);
looks.put(key, look);
}
return look;
}
class Look {
int map;
int parts;
int stages;
CodeBook[] fullbooks;
CodeBook phrasebook;
int[][] partbooks;
int partvals;
int[][] decodemap;
int postbits;
int phrasebits;
int frames;
protected Look (VorbisStream source, Mode mode) {
int dim=0, acc=0, maxstage=0;
map=mode.getMapping();
parts=Residue.this.getClassifications();
fullbooks=source.getSetupHeader().getCodeBooks();
phrasebook=fullbooks[Residue.this.getClassBook()];
dim=phrasebook.getDimensions();
partbooks=new int[parts][];
for(int j=0;j<parts;j++) {
int stages=Util.ilog(Residue.this.getCascade()[j]);
if(stages!=0) {
if(stages>maxstage) {
maxstage=stages;
}
partbooks[j]=new int[stages];
for(int k=0; k<stages; k++){
if((Residue.this.getCascade()[j]&(1<<k))!=0){
partbooks[j][k]=Residue.this.getBooks()[j][k];
}
}
}
}
partvals=(int)Math.rint(Math.pow(parts, dim));
stages=maxstage;
decodemap=new int[partvals][];
for(int j=0;j<partvals;j++){
int val=j;
int mult=partvals/parts;
decodemap[j]=new int[dim];
for(int k=0;k<dim;k++){
int deco=val/mult;
val-=deco*mult;
mult/=parts;
decodemap[j][k]=deco;
}
}
}
protected int[][] getDecodeMap() {
return decodemap;
}
protected int getFrames() {
return frames;
}
protected int getMap() {
return map;
}
protected int[][] getPartBooks() {
return partbooks;
}
protected int getParts() {
return parts;
}
protected int getPartVals() {
return partvals;
}
protected int getPhraseBits() {
return phrasebits;
}
protected CodeBook getPhraseBook() {
return phrasebook;
}
protected int getPostBits() {
return postbits;
}
protected int getStages() {
return stages;
}
}
}

View file

@ -0,0 +1,53 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class Residue0 extends Residue {
protected Residue0(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
super(source, header);
}
protected int getType() {
return 0;
}
protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException {
/** @todo implement */
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,55 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class Residue1 extends Residue {
protected Residue1(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
super(source, header);
}
protected int getType() {
return 1;
}
protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException {
/** @todo implement */
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,123 @@
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
import de.jarnbjo.util.io.BitInputStream;
class Residue2 extends Residue {
private double[][] decodedVectors;
private Residue2() {
}
protected Residue2(BitInputStream source, SetupHeader header) throws VorbisFormatException, IOException {
super(source, header);
}
protected int getType() {
return 2;
}
protected void decodeResidue(VorbisStream vorbis, BitInputStream source, Mode mode, int ch, boolean[] doNotDecodeFlags, float[][] vectors) throws VorbisFormatException, IOException {
Look look=getLook(vorbis, mode);
CodeBook codeBook=vorbis.getSetupHeader().getCodeBooks()[getClassBook()];
int classvalsPerCodeword=codeBook.getDimensions();
int nToRead=getEnd()-getBegin();
int partitionsToRead=nToRead/getPartitionSize(); // partvals
int samplesPerPartition=getPartitionSize();
int partitionsPerWord=look.getPhraseBook().getDimensions();
int partWords=(partitionsToRead+partitionsPerWord-1)/partitionsPerWord;
int realCh=0;
for(int i=0; i<doNotDecodeFlags.length; i++) {
if(!doNotDecodeFlags[i]) {
realCh++;
}
}
float[][] realVectors=new float[realCh][];
realCh=0;
for(int i=0; i<doNotDecodeFlags.length; i++) {
if(!doNotDecodeFlags[i]) {
realVectors[realCh++]=vectors[i];
}
}
int[][] partword=new int[partWords][];
for(int s=0;s<look.getStages();s++){
for(int i=0,l=0;i<partitionsToRead;l++){
if(s==0){
//int temp=look.getPhraseBook().readInt(source);
int temp=source.getInt(look.getPhraseBook().getHuffmanRoot());
if(temp==-1){
throw new VorbisFormatException("");
}
partword[l]=look.getDecodeMap()[temp];
if(partword[l]==null){
throw new VorbisFormatException("");
}
}
for(int k=0;k<partitionsPerWord && i<partitionsToRead;k++,i++){
int offset=begin+i*samplesPerPartition;
if((cascade[partword[l][k]]&(1<<s))!=0){
CodeBook stagebook=vorbis.getSetupHeader().getCodeBooks()[look.getPartBooks()[partword[l][k]][s]];
if(stagebook!=null){
stagebook.readVvAdd(realVectors, source, offset, samplesPerPartition);
}
}
}
}
}
}
public Object clone() {
Residue2 clone=new Residue2();
fill(clone);
return clone;
}
protected double[][] getDecodedVectors() {
return decodedVectors;
}
}

View file

@ -0,0 +1,131 @@
/*
* $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/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.*;
import de.jarnbjo.util.io.*;
class SetupHeader {
private static final long HEADER = 0x736962726f76L; // 'vorbis'
private CodeBook[] codeBooks;
private Floor[] floors;
private Residue[] residues;
private Mapping[] mappings;
private Mode[] modes;
public SetupHeader(VorbisStream vorbis, BitInputStream source) throws VorbisFormatException, IOException {
if(source.getLong(48)!=HEADER) {
throw new VorbisFormatException("The setup header has an illegal leading.");
}
// read code books
int codeBookCount=source.getInt(8)+1;
codeBooks=new CodeBook[codeBookCount];
for(int i=0; i<codeBooks.length; i++) {
codeBooks[i]=new CodeBook(source);
}
// read the time domain transformations,
// these should all be 0
int timeCount=source.getInt(6)+1;
for(int i=0; i<timeCount; i++) {
if(source.getInt(16)!=0) {
throw new VorbisFormatException("Time domain transformation != 0");
}
}
// read floor entries
int floorCount=source.getInt(6)+1;
floors=new Floor[floorCount];
for(int i=0; i<floorCount; i++) {
floors[i]=Floor.createInstance(source, this);
}
// read residue entries
int residueCount=source.getInt(6)+1;
residues=new Residue[residueCount];
for(int i=0; i<residueCount; i++) {
residues[i]=Residue.createInstance(source, this);
}
// read mapping entries
int mappingCount=source.getInt(6)+1;
mappings=new Mapping[mappingCount];
for(int i=0; i<mappingCount; i++) {
mappings[i]=Mapping.createInstance(vorbis, source, this);
}
// read mode entries
int modeCount=source.getInt(6)+1;
modes=new Mode[modeCount];
for(int i=0; i<modeCount; i++) {
modes[i]=new Mode(source, this);
}
if(!source.getBit()) {
throw new VorbisFormatException("The setup header framing bit is incorrect.");
}
}
public CodeBook[] getCodeBooks() {
return codeBooks;
}
public Floor[] getFloors() {
return floors;
}
public Residue[] getResidues() {
return residues;
}
public Mapping[] getMappings() {
return mappings;
}
public Mode[] getModes() {
return modes;
}
}

View file

@ -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.3 2003/04/10 19:49:04 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
final public class Util {
public static final int ilog(int x) {
int res=0;
for(; x>0; x>>=1, res++);
return res;
}
public static final float float32unpack(int x) {
float mantissa=x&0x1fffff;
float e=(x&0x7fe00000)>>21;
if((x&0x80000000)!=0) {
mantissa=-mantissa;
}
return mantissa*(float)Math.pow(2.0, e-788.0);
}
public static final int lookup1Values(int a, int b) {
int res=(int)Math.pow(Math.E, Math.log(a)/b);
return intPow(res+1, b)<=a?res+1:res;
}
public static final int intPow(int base, int e) {
int res=1;
for(; e>0; e--, res*=base);
return res;
}
public static final boolean isBitSet(int value, int bit) {
return (value&(1<<bit))!=0;
}
public static final int icount(int value) {
int res=0;
while(value>0) {
res+=value&1;
value>>=1;
}
return res;
}
public static final int lowNeighbour(int[] v, int x) {
int max=-1, n=0;
for(int i=0; i<v.length && i<x; i++) {
if(v[i]>max && v[i]<v[x]) {
max=v[i];
n=i;
}
}
return n;
}
public static final int highNeighbour(int[] v, int x) {
int min=Integer.MAX_VALUE, n=0;
for(int i=0; i<v.length && i<x; i++) {
if(v[i]<min && v[i]>v[x]) {
min=v[i];
n=i;
}
}
return n;
}
public static final int renderPoint(int x0, int x1, int y0, int y1, int x) {
int dy=y1-y0;
int ady=dy<0?-dy:dy;
int off=(ady*(x-x0))/(x1-x0);
return dy<0?y0-off:y0+off;
}
public static final void renderLine(final int x0, final int y0, final int x1, final int y1, final float[] v) {
final int dy=y1-y0;
final int adx=x1-x0;
final int base=dy/adx;
final int sy=dy<0?base-1:base+1;
int x=x0;
int y=y0;
int err=0;
final int ady=(dy<0?-dy:dy)-(base>0?base*adx:-base*adx);
v[x]*=Floor.DB_STATIC_TABLE[y];
for(x=x0+1; x<x1; x++) {
err+=ady;
if(err>=adx) {
err-=adx;
v[x]*=Floor.DB_STATIC_TABLE[y+=sy];
}
else {
v[x]*=Floor.DB_STATIC_TABLE[y+=base];
}
}
}
}

View file

@ -0,0 +1,217 @@
package de.jarnbjo.vorbis;
/*
* $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 2004/09/21 06:39:06 shred
* Importe reorganisiert, damit Eclipse Ruhe gibt. ;-)
*
* Revision 1.1.1.1 2004/04/04 22:09:12 shred
* First Import
*
*
*/
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.Collection;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.spi.AudioFileReader;
import de.jarnbjo.ogg.BasicStream;
import de.jarnbjo.ogg.EndOfOggStreamException;
import de.jarnbjo.ogg.FileStream;
import de.jarnbjo.ogg.LogicalOggStream;
import de.jarnbjo.ogg.OggFormatException;
import de.jarnbjo.ogg.PhysicalOggStream;
import de.jarnbjo.ogg.UncachedUrlStream;
public class VorbisAudioFileReader extends AudioFileReader {
public VorbisAudioFileReader() {
}
public AudioFileFormat getAudioFileFormat(File file) throws IOException, UnsupportedAudioFileException {
try {
return getAudioFileFormat(new FileStream(new RandomAccessFile(file, "r")));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public AudioFileFormat getAudioFileFormat(InputStream stream) throws IOException, UnsupportedAudioFileException {
try {
return getAudioFileFormat(new BasicStream(stream));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public AudioFileFormat getAudioFileFormat(URL url) throws IOException, UnsupportedAudioFileException {
try {
return getAudioFileFormat(new UncachedUrlStream(url));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
private AudioFileFormat getAudioFileFormat(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException {
try {
Collection streams=oggStream.getLogicalStreams();
if(streams.size()!=1) {
throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
}
LogicalOggStream los=(LogicalOggStream)streams.iterator().next();
if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) {
throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
}
VorbisStream vs=new VorbisStream(los);
AudioFormat audioFormat=new AudioFormat(
(float)vs.getIdentificationHeader().getSampleRate(),
16,
vs.getIdentificationHeader().getChannels(),
true, true);
return new AudioFileFormat(VorbisFormatType.getInstance(), audioFormat, AudioSystem.NOT_SPECIFIED);
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
catch(VorbisFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public AudioInputStream getAudioInputStream(File file) throws IOException, UnsupportedAudioFileException {
try {
return getAudioInputStream(new FileStream(new RandomAccessFile(file, "r")));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public AudioInputStream getAudioInputStream(InputStream stream) throws IOException, UnsupportedAudioFileException {
try {
return getAudioInputStream(new BasicStream(stream));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public AudioInputStream getAudioInputStream(URL url) throws IOException, UnsupportedAudioFileException {
try {
return getAudioInputStream(new UncachedUrlStream(url));
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
private AudioInputStream getAudioInputStream(PhysicalOggStream oggStream) throws IOException, UnsupportedAudioFileException {
try {
Collection streams=oggStream.getLogicalStreams();
if(streams.size()!=1) {
throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
}
LogicalOggStream los=(LogicalOggStream)streams.iterator().next();
if(los.getFormat()!=LogicalOggStream.FORMAT_VORBIS) {
throw new UnsupportedAudioFileException("Only Ogg files with one logical Vorbis stream are supported.");
}
VorbisStream vs=new VorbisStream(los);
AudioFormat audioFormat=new AudioFormat(
(float)vs.getIdentificationHeader().getSampleRate(),
16,
vs.getIdentificationHeader().getChannels(),
true, true);
return new AudioInputStream(new VorbisInputStream(vs), audioFormat, -1);
}
catch(OggFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
catch(VorbisFormatException e) {
throw new UnsupportedAudioFileException(e.getMessage());
}
}
public static class VorbisFormatType extends AudioFileFormat.Type {
private static final VorbisFormatType instance=new VorbisFormatType();
private VorbisFormatType() {
super("VORBIS", "ogg");
}
public static AudioFileFormat.Type getInstance() {
return instance;
}
}
public static class VorbisInputStream extends InputStream {
private VorbisStream source;
private byte[] buffer=new byte[8192];
public VorbisInputStream(VorbisStream source) {
this.source=source;
}
public int read() throws IOException {
return 0;
}
public int read(byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
public int read(byte[] buffer, int offset, int length) throws IOException {
try {
return source.readPcm(buffer, offset, length);
}
catch(EndOfOggStreamException e) {
return -1;
}
}
}
}

View file

@ -0,0 +1,51 @@
/*
* $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.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.IOException;
/**
* Exception thrown when trying to read a corrupted Vorbis stream.
*/
public class VorbisFormatException extends IOException {
private static final long serialVersionUID = 3616453405694834743L;
public VorbisFormatException() {
super();
}
public VorbisFormatException(String message) {
super(message);
}
}

View file

@ -0,0 +1,247 @@
/*
* $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.4 2003/04/10 19:49:04 jarnbjo
* no message
*
* Revision 1.3 2003/03/31 00:20:16 jarnbjo
* no message
*
* Revision 1.2 2003/03/16 01:11:12 jarnbjo
* no message
*
*
*/
package de.jarnbjo.vorbis;
import java.io.*;
import java.util.*;
import de.jarnbjo.ogg.*;
import de.jarnbjo.util.io.*;
/**
*/
public class VorbisStream {
private LogicalOggStream oggStream;
private IdentificationHeader identificationHeader;
private CommentHeader commentHeader;
private SetupHeader setupHeader;
private AudioPacket lastAudioPacket, nextAudioPacket;
private LinkedList audioPackets=new LinkedList();
private byte[] currentPcm;
private int currentPcmIndex;
private int currentPcmLimit;
private static final int IDENTIFICATION_HEADER = 1;
private static final int COMMENT_HEADER = 3;
private static final int SETUP_HEADER = 5;
private int bitIndex=0;
private byte lastByte=(byte)0;
private boolean initialized=false;
private Object streamLock=new Object();
private int pageCounter=0;
private int currentBitRate=0;
private long currentGranulePosition;
public static final int BIG_ENDIAN = 0;
public static final int LITTLE_ENDIAN = 1;
public VorbisStream() {
}
public VorbisStream(LogicalOggStream oggStream) throws VorbisFormatException, IOException {
this.oggStream=oggStream;
for(int i=0; i<3; i++) {
BitInputStream source=new ByteArrayBitInputStream(oggStream.getNextOggPacket());
int headerType=source.getInt(8);
switch(headerType) {
case IDENTIFICATION_HEADER:
identificationHeader=new IdentificationHeader(source);
break;
case COMMENT_HEADER:
commentHeader=new CommentHeader(source);
break;
case SETUP_HEADER:
setupHeader=new SetupHeader(this, source);
break;
}
}
if(identificationHeader==null) {
throw new VorbisFormatException("The file has no identification header.");
}
if(commentHeader==null) {
throw new VorbisFormatException("The file has no commentHeader.");
}
if(setupHeader==null) {
throw new VorbisFormatException("The file has no setup header.");
}
//currentPcm=new int[identificationHeader.getChannels()][16384];
currentPcm=new byte[identificationHeader.getChannels()*identificationHeader.getBlockSize1()*2];
//new BufferThread().start();
}
public IdentificationHeader getIdentificationHeader() {
return identificationHeader;
}
public CommentHeader getCommentHeader() {
return commentHeader;
}
protected SetupHeader getSetupHeader() {
return setupHeader;
}
public boolean isOpen() {
return oggStream.isOpen();
}
public void close() throws IOException {
oggStream.close();
}
public int readPcm(byte[] buffer, int offset, int length) throws IOException {
synchronized (streamLock) {
final int channels=identificationHeader.getChannels();
if(lastAudioPacket==null) {
lastAudioPacket=getNextAudioPacket();
}
if(currentPcm==null || currentPcmIndex>=currentPcmLimit) {
AudioPacket ap=getNextAudioPacket();
try {
ap.getPcm(lastAudioPacket, currentPcm);
currentPcmLimit=ap.getNumberOfSamples()*identificationHeader.getChannels()*2;
}
catch(ArrayIndexOutOfBoundsException e) {
return 0;
}
currentPcmIndex=0;
lastAudioPacket=ap;
}
int written=0;
int i=0;
int arrIx=0;
for(i=currentPcmIndex; i<currentPcmLimit && arrIx<length; i++) {
buffer[offset+arrIx++]=currentPcm[i];
written++;
}
currentPcmIndex=i;
return written;
}
}
private AudioPacket getNextAudioPacket() throws VorbisFormatException, IOException {
pageCounter++;
byte[] data=oggStream.getNextOggPacket();
AudioPacket res=null;
while(res==null) {
try {
res=new AudioPacket(this, new ByteArrayBitInputStream(data));
}
catch(ArrayIndexOutOfBoundsException e) {
// ignore and continue with next packet
}
}
currentGranulePosition+=res.getNumberOfSamples();
currentBitRate=data.length*8*identificationHeader.getSampleRate()/res.getNumberOfSamples();
return res;
}
public long getCurrentGranulePosition() {
return currentGranulePosition;
}
public int getCurrentBitRate() {
return currentBitRate;
}
public byte[] processPacket(byte[] packet) throws VorbisFormatException, IOException {
if(packet.length==0) {
throw new VorbisFormatException("Cannot decode a vorbis packet with length = 0");
}
if(((int)packet[0]&1)==1) {
// header packet
BitInputStream source=new ByteArrayBitInputStream(packet);
switch(source.getInt(8)) {
case IDENTIFICATION_HEADER:
identificationHeader=new IdentificationHeader(source);
break;
case COMMENT_HEADER:
commentHeader=new CommentHeader(source);
break;
case SETUP_HEADER:
setupHeader=new SetupHeader(this, source);
break;
}
return null;
}
else {
// audio packet
if(identificationHeader==null ||
commentHeader==null ||
setupHeader==null) {
throw new VorbisFormatException("Cannot decode audio packet before all three header packets have been decoded.");
}
AudioPacket ap=new AudioPacket(this, new ByteArrayBitInputStream(packet));
currentGranulePosition+=ap.getNumberOfSamples();
if(lastAudioPacket==null) {
lastAudioPacket=ap;
return null;
}
byte[] res=new byte[identificationHeader.getChannels()*ap.getNumberOfSamples()*2];
try {
ap.getPcm(lastAudioPacket, res);
}
catch(IndexOutOfBoundsException e) {
java.util.Arrays.fill(res, (byte)0);
}
lastAudioPacket=ap;
return res;
}
}
}

View file

@ -0,0 +1,186 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats;
import entagged.audioformats.exceptions.*;
import entagged.audioformats.generic.GenericTag;
import java.io.*;
/**
* <p>This is the main object manipulated by the user representing an audiofile, its properties and its tag.</p>
* <p>The prefered way to obtain an <code>AudioFile</code> is to use the <code>AudioFileIO.read(File)</code> method.</p>
* <p>The <code>AudioFile</code> contains every properties associated with the file itself (no meta-data), like the bitrate, the sampling rate, the encoding infos, etc.</p>
* <p>To get the meta-data contained in this file you have to get the <code>Tag</code> of this <code>AudioFile</code></p>
*
*@author Raphael Slinckx
*@version $Id$
*@since v0.01
*@see AudioFileIO
*@see Tag
*/
public class AudioFile extends File {
private static final long serialVersionUID = 3257289136422728502L;
private EncodingInfo info;
private Tag tag;
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file denoted by pathname s, the encodinginfos and containing the tag</p>
*
*@param s The pathname of the audiofile
*@param info the encoding infos over this file
*@param tag the tag contained in this file
*/
public AudioFile(String s, EncodingInfo info, Tag tag) {
super(s);
this.info = info;
this.tag = tag;
}
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file denoted by pathname s, the encodinginfos and containing an empty tag</p>
*
*@param s The pathname of the audiofile
*@param info the encoding infos over this file
*/
public AudioFile(String s, EncodingInfo info) {
super(s);
this.info = info;
this.tag = new GenericTag();
}
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file f, the encodinginfos and containing the tag</p>
*
*@param f The file of the audiofile
*@param info the encoding infos over this file
*@param tag the tag contained in this file
*/
public AudioFile(File f, EncodingInfo info, Tag tag) {
super(f.getAbsolutePath());
this.info = info;
this.tag = tag;
}
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file f, the encodinginfos and containing an empty tag</p>
*
*@param f The file of the audiofile
*@param info the encoding infos over this file
*/
public AudioFile(File f, EncodingInfo info) {
super(f.getAbsolutePath());
this.info = info;
this.tag = new GenericTag();
}
/**
* <p>Returns the bitrate of this AufioFile in kilobytes per second (KB/s). Example: 192 KB/s</p>
*
*@return Returns the bitrate of this AufioFile
*/
public int getBitrate() {
return info.getBitrate();
}
/**
* <p>Returns the number of audio channels contained in this AudioFile, 2 for example means stereo</p>
*
*@return Returns the number of audio channels contained in this AudioFile
*/
public int getChannelNumber() {
return info.getChannelNumber();
}
/**
* <p>Returns the encoding type of this AudioFile, this needs to be precisely specified in the future</p>
*
*@return Returns the encoding type of this AudioFile
*@todo This method needs to be fully specified
*/
public String getEncodingType() {
return info.getEncodingType();
}
/**
* <p>Returns the extra encoding infos of this AudioFile, this needs to be precisely specified in the future</p>
*
*@return Returns the extra encoding infos of this AudioFile
*@todo This method needs to be fully specified
*/
public String getExtraEncodingInfos() {
return info.getExtraEncodingInfos();
}
/**
* <p>Returns the sampling rate of this AudioFile in Hertz (Hz). Example: 44100 Hz for most of the audio files</p>
*
*@return Returns the sampling rate of this AudioFile
*/
public int getSamplingRate() {
return info.getSamplingRate();
}
/**
* <p>Returns the length (duration) in seconds (s) of this AudioFile.Example: 241 seconds</p>
*
*@return Returns the length (duration) of this AudioFile
*/
public int getLength() {
return info.getLength();
}
/**
* <p>Returns the tag contained in this AudioFile, the <code>Tag</code> contains any useful meta-data, like artist, album, title, etc.</p>
* <p>If the file does not contain any tag, a new empty tag is returned</p>
*
*@return Returns the tag contained in this AudioFile, or a new one if file hasn't any tag.
*/
public Tag getTag() {
return (tag == null) ? new GenericTag() : tag;
}
/*
* <p>Checks if this file is a VBR (variable bitrate) or a Constant Bitrate one</p>
* <p>True means VBR, false means CBR</p>
* <p>This has only meaning with MP3 and MPC files, other formats are always VBR
* since it offers a better compression ratio (and lossless compression is by nature VBR</p>
*/
public boolean isVbr() {
return info.isVbr();
}
/**
* <p>Returns a multi-line string with the file path, the encoding informations, and the tag contents.</p>
*
*@return A multi-line string with the file path, the encoding informations, and the tag contents.
*@todo Maybe this can be changed ?
*/
public String toString() {
return "AudioFile "+getAbsolutePath()+" --------\n"+info.toString()+"\n"+ ( (tag == null) ? "" : tag.toString())+"\n-------------------";
}
}

View file

@ -0,0 +1,116 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats;
import java.util.*;
public class EncodingInfo {
private Hashtable content;
public EncodingInfo() {
content = new Hashtable(6);
content.put("BITRATE", new Integer(-1) );
content.put("CHANNB", new Integer(-1) );
content.put("TYPE", "");
content.put("INFOS", "");
content.put("SAMPLING", new Integer(-1) );
content.put("LENGTH", new Integer(-1) );
content.put("VBR", new Boolean(true) );
}
//Sets the bitrate in KByte/s
public void setBitrate( int bitrate ) {
content.put("BITRATE", new Integer(bitrate) );
}
//Sets the number of channels
public void setChannelNumber( int chanNb ) {
content.put("CHANNB", new Integer(chanNb) );
}
//Sets the type of the encoding, this is a bit format specific. eg:Layer I/II/II
public void setEncodingType( String encodingType ) {
content.put("TYPE", encodingType );
}
//A string contianing anything else that might be interesting
public void setExtraEncodingInfos( String infos ) {
content.put("INFOS", infos );
}
//Sets the Sampling rate in Hz
public void setSamplingRate( int samplingRate ) {
content.put("SAMPLING", new Integer(samplingRate) );
}
//Sets the length of the song in seconds
public void setLength( int length ) {
content.put("LENGTH", new Integer(length) );
}
//Is the song vbr or not ?
public void setVbr( boolean b ) {
content.put("VBR", new Boolean(b) );
}
//returns the bitrate in KByte/s
public int getBitrate() {
return ((Integer) content.get("BITRATE")).intValue();
}
//Returns the number of channels
public int getChannelNumber() {
return ((Integer) content.get("CHANNB")).intValue();
}
//returns the encoding type.
public String getEncodingType() {
return (String) content.get("TYPE");
}
//returns a string with misc. information about the encoding
public String getExtraEncodingInfos() {
return (String) content.get("INFOS");
}
//returns the sample rate in Hz
public int getSamplingRate() {
return ((Integer) content.get("SAMPLING")).intValue();
}
//Returns the length of the song in seconds
public int getLength() {
return ((Integer) content.get("LENGTH")).intValue();
}
//Is the song vbr ?
public boolean isVbr() {
return ((Boolean) content.get("VBR")).booleanValue();
}
//Pretty prints this encoding info
public String toString() {
StringBuffer out = new StringBuffer(50);
out.append("Encoding infos content:\n");
Enumeration en = content.keys();
while(en.hasMoreElements()) {
Object key = en.nextElement();
Object val = content.get(key);
out.append("\t");
out.append(key);
out.append(" : ");
out.append(val);
out.append("\n");
}
return out.toString().substring(0,out.length()-1);
}
}

View file

@ -0,0 +1,116 @@
package entagged.audioformats;
import java.util.Iterator;
import java.util.List;
import entagged.audioformats.generic.TagField;
public interface Tag {
/**
* This final field contains all the tags that id3v1 supports. The list has
* the same order as the id3v1 genres. To be perfectly compatible (with
* id3v1) the genre field should match one of these genre (case ignored).
* You can also use this list to present a list of basic (modifiable)
* possible choices for the genre field.
*/
public static final String[] DEFAULT_GENRES = { "Blues", "Classic Rock",
"Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
"Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap",
"Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska",
"Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
"Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
"Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel",
"Noise", "AlternRock", "Bass", "Soul", "Punk", "Space",
"Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic",
"Gothic", "Darkwave", "Techno-Industrial", "Electronic",
"Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
"Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
"Native American", "Cabaret", "New Wave", "Psychadelic", "Rave",
"Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk",
"Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll",
"Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing",
"Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
"Avantgarde", "Gothic Rock", "Progressive Rock",
"Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
"Chorus", "Easy Listening", "Acoustic", "Humour", "Speech",
"Chanson", "Opera", "Chamber Music", "Sonata", "Symphony",
"Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam",
"Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad",
"Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo",
"A capella", "Euro-House", "Dance Hall" };
public void add(TagField field);
public void addAlbum(String s);
public void addArtist(String s);
public void addComment(String s);
public void addGenre(String s);
public void addTitle(String s);
public void addTrack(String s);
public void addYear(String s);
public List get(String id);
public Iterator getFields();
public List getGenre();
public List getTitle();
public List getTrack();
public List getYear();
public List getAlbum();
public List getArtist();
public List getComment();
public String getFirstGenre();
public String getFirstTitle();
public String getFirstTrack();
public String getFirstYear();
public String getFirstAlbum();
public String getFirstArtist();
public String getFirstComment();
public boolean hasCommonFields();
public boolean hasField(String id);
public boolean isEmpty();
//public Iterator getCommonFields();
//public Iterator getSpecificFields();
public void merge(Tag tag);
public void set(TagField field);
public void setAlbum(String s);
public void setArtist(String s);
public void setComment(String s);
public void setGenre(String s);
public void setTitle(String s);
public void setTrack(String s);
public void setYear(String s);
public String toString();
}

View file

@ -0,0 +1,112 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf;
import java.io.IOException;
import java.io.RandomAccessFile;
import entagged.audioformats.EncodingInfo;
import entagged.audioformats.Tag;
import entagged.audioformats.asf.data.AsfHeader;
import entagged.audioformats.asf.io.AsfHeaderReader;
import entagged.audioformats.asf.util.TagConverter;
import entagged.audioformats.exceptions.CannotReadException;
import entagged.audioformats.generic.AudioFileReader;
/**
* This reader can read asf files containing any content (stream type). <br>
*
* @author Christian Laireiter
*/
public class AsfFileReader extends AudioFileReader {
/**
* (overridden)
*
* @see entagged.audioformats.generic.AudioFileReader#getEncodingInfo(java.io.RandomAccessFile)
*/
protected EncodingInfo getEncodingInfo(RandomAccessFile raf)
throws CannotReadException, IOException {
raf.seek(0);
EncodingInfo info = new EncodingInfo();
try {
AsfHeader header = AsfHeaderReader.readHeader(raf);
if (header == null) {
throw new CannotReadException(
"Some values must have been "
+ "incorrect for interpretation as asf with wma content.");
}
info.setBitrate(header.getAudioStreamChunk().getKbps());
info.setChannelNumber((int) header.getAudioStreamChunk()
.getChannelCount());
info.setEncodingType("ASF (audio): "+header.getAudioStreamChunk()
.getCodecDescription());
info.setLength(header.getFileHeader().getDurationInSeconds());
info.setSamplingRate((int) header.getAudioStreamChunk()
.getSamplingRate());
} catch (Exception e) {
if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof CannotReadException)
throw (CannotReadException) e;
else {
throw new CannotReadException("Failed to read. Cause: "
+ e.getMessage());
}
}
return info;
}
/**
* (overridden)
*
* @see entagged.audioformats.generic.AudioFileReader#getTag(java.io.RandomAccessFile)
*/
protected Tag getTag(RandomAccessFile raf) throws CannotReadException,
IOException {
raf.seek(0);
Tag tag = null;
try {
AsfHeader header = AsfHeaderReader.readHeader(raf);
if (header == null) {
throw new CannotReadException(
"Some values must have been "
+ "incorrect for interpretation as asf with wma content.");
}
tag = TagConverter.createTagOf(header);
} catch (Exception e) {
if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof CannotReadException)
throw (CannotReadException) e;
else {
throw new CannotReadException("Failed to read. Cause: "
+ e.getMessage());
}
}
return tag;
}
}

View file

@ -0,0 +1,279 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.math.BigInteger;
import java.util.Arrays;
/**
* Each asf file starts with a so called header. <br>
* This header contains other chunks. Each chunk starts with a 16 byte GUID
* followed by the length (in bytes) of the chunk (including GUID). The length
* number takes 8 bytes and is unsigned. Finally the chunk's data appears. <br>
*
* @author Christian Laireiter
*/
public class AsfHeader extends Chunk {
/**
* An asf header contains multiple chunks. <br>
* The count of those is stored here.
*/
private final long chunkCount;
/**
* The content description of the entire file.
*/
private ContentDescription contentDescription;
/**
* Stores the encoding chunk.
*/
private EncodingChunk encodingChunk;
/**
* Stores the tag header.
*/
private ExtendedContentDescription extendedContentDescription;
/**
* Stores the file header.
*/
private FileHeader fileHeader;
/**
* This array stores all found stream chunks.
*/
private StreamChunk[] streamChunks;
/**
* This field stores all chunks which aren't specified and not represented
* by a wrapper. <br>
* However during write operations this position and size of those chunks is
* useful.
*/
private Chunk[] unspecifiedChunks;
/**
* Creates an instance.
*
* @param pos
* see {@link Chunk#position}
* @param chunkLen
* see {@link Chunk#chunkLength}
* @param chunkCnt
*/
public AsfHeader(long pos, BigInteger chunkLen, long chunkCnt) {
super(GUID.GUID_HEADER, pos, chunkLen);
this.chunkCount = chunkCnt;
this.streamChunks = new StreamChunk[0];
this.unspecifiedChunks = new Chunk[0];
}
/**
* This method appends a StreamChunk to the header. <br>
*
* @param toAdd
* Chunk to add.
*/
public void addStreamChunk(StreamChunk toAdd) {
if (toAdd == null) {
throw new IllegalArgumentException("Argument must not be null.");
}
if (!Arrays.asList(this.streamChunks).contains(toAdd)) {
StreamChunk[] tmp = new StreamChunk[this.streamChunks.length + 1];
System.arraycopy(this.streamChunks, 0, tmp, 0,
this.streamChunks.length);
tmp[tmp.length - 1] = toAdd;
this.streamChunks = tmp;
}
}
/**
* This method appends the given chunk to the
* {@linkplain #unspecifiedChunks unspecified}list. <br>
*
* @param toAppend
* The chunk whose use is unknown or of no interest.
*/
public void addUnspecifiedChunk(Chunk toAppend) {
if (toAppend == null) {
throw new IllegalArgumentException("Argument must not be null.");
}
if (!Arrays.asList(unspecifiedChunks).contains(toAppend)) {
Chunk[] tmp = new Chunk[unspecifiedChunks.length + 1];
System.arraycopy(unspecifiedChunks, 0, tmp, 0,
unspecifiedChunks.length);
tmp[tmp.length - 1] = toAppend;
unspecifiedChunks = tmp;
}
}
/**
* This method returns the first audio stream chunk found in the asf file or
* stream.
*
* @return Returns the audioStreamChunk.
*/
public AudioStreamChunk getAudioStreamChunk() {
AudioStreamChunk result = null;
for (int i = 0; i < getStreamChunkCount() && result == null; i++) {
StreamChunk tmp = getStreamChunk(i);
if (tmp instanceof AudioStreamChunk) {
result = (AudioStreamChunk) tmp;
}
}
return result;
}
/**
* @return Returns the chunkCount.
*/
public long getChunkCount() {
return chunkCount;
}
/**
* @return Returns the contentDescription.
*/
public ContentDescription getContentDescription() {
return contentDescription;
}
/**
* @return Returns the encodingChunk.
*/
public EncodingChunk getEncodingChunk() {
return encodingChunk;
}
/**
* @return Returns the tagHeader.
*/
public ExtendedContentDescription getExtendedContentDescription() {
return extendedContentDescription;
}
/**
* @return Returns the fileHeader.
*/
public FileHeader getFileHeader() {
return fileHeader;
}
/**
* This method returns the StreamChunk at given index. <br>
*
* @param index
* index of the wanted chunk
* @return StreamChunk at given index.
*/
public StreamChunk getStreamChunk(int index) {
return this.streamChunks[index];
}
/**
* This method returns the amount of StreamChunks in this header. <br>
*
* @return Number of inserted StreamChunks.
*/
public int getStreamChunkCount() {
return this.streamChunks.length;
}
/**
* This method returns the unspecified chunk at given position. <br>
*
* @param index
* Index of the wanted chunk
* @return The chunk at given index.
*/
public Chunk getUnspecifiedChunk(int index) {
return this.unspecifiedChunks[index];
}
/**
* This method returns the number of {@link Chunk}objects which where
* inserted using {@link #addUnspecifiedChunk(Chunk)}.
*
* @return Number of unspecified chunks.
*/
public int getUnspecifiedChunkCount() {
return this.unspecifiedChunks.length;
}
/**
* (overridden)
*
* @see entagged.audioformats.asf.data.Chunk#prettyPrint()
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer(super.prettyPrint());
result.insert(0, "\nASF Chunk\n");
result.append(" Contains: \"" + getChunkCount() + "\" chunks\n");
result.append(getFileHeader());
result.append(getExtendedContentDescription());
result.append(getEncodingChunk());
result.append(getContentDescription());
for (int i = 0; i < getStreamChunkCount(); i++) {
result.append(getStreamChunk(i));
}
return result.toString();
}
/**
* @param contentDesc
* sets the contentDescription. <code>null</code> deletes the
* chunk.
*/
public void setContentDescription(ContentDescription contentDesc) {
this.contentDescription = contentDesc;
}
/**
* @param encChunk
* The encodingChunk to set.
*/
public void setEncodingChunk(EncodingChunk encChunk) {
if (encChunk == null)
throw new IllegalArgumentException("Argument must not be null.");
this.encodingChunk = encChunk;
}
/**
* @param th
* sets the extendedContentDescription. <code>null</code>
* delete the chunk.
*/
public void setExtendedContentDescription(ExtendedContentDescription th) {
this.extendedContentDescription = th;
}
/**
* @param fh
*/
public void setFileHeader(FileHeader fh) {
if (fh == null)
throw new IllegalArgumentException("Argument must not be null.");
this.fileHeader = fh;
}
}

View file

@ -0,0 +1,292 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.math.BigInteger;
import entagged.audioformats.asf.util.Utils;
/**
* This class represents the streamchunk describing an audio stream. <br>
*
* @author Christian Laireiter
*/
public class AudioStreamChunk extends StreamChunk {
/**
* Stores the hex values of codec identifiers to their descriptions. <br>
*/
public final static String[][] CODEC_DESCRIPTIONS = {
{ "161", " (Windows Media Audio (ver 7,8,9))" },
{ "162", " (Windows Media Audio 9 series (Professional))" },
{ "163", "(Windows Media Audio 9 series (Lossless))" },
{ "7A21", " (GSM-AMR (CBR))" }, { "7A22", " (GSM-AMR (VBR))" } };
/**
* Stores the average amount of bytes used by audio stream. <br>
* This value is a field within type specific data of audio stream. Maybe it
* could be used to calculate the kbps.
*/
private long averageBytesPerSec;
/**
* Amount of bits used per sample. <br>
*/
private int bitsPerSample;
/**
* The block alignment of the audio data.
*/
private long blockAlignment;
/**
* Number of channels.
*/
private long channelCount;
/**
* Some data which needs to be interpreted if the codec is handled.
*/
private byte[] codecData;
/**
* The audio compression format code.
*/
private long compressionFormat;
/**
* this field stores the error concealment type.
*/
private GUID errorConcealment;
/**
* Sampling rate of audio stream.
*/
private long samplingRate;
/**
* Creates an instance.
*
* @param pos
* Position of current chunk within asf file or stream.
* @param chunkLen
* Length of the entire chunk (including guid and size)
*/
public AudioStreamChunk(long pos, BigInteger chunkLen) {
super(pos, chunkLen);
}
/**
* @return Returns the averageBytesPerSec.
*/
public long getAverageBytesPerSec() {
return averageBytesPerSec;
}
/**
* @return Returns the bitsPerSample.
*/
public int getBitsPerSample() {
return bitsPerSample;
}
/**
* @return Returns the blockAlignment.
*/
public long getBlockAlignment() {
return blockAlignment;
}
/**
* @return Returns the channelCount.
*/
public long getChannelCount() {
return channelCount;
}
/**
* @return Returns the codecData.
*/
public byte[] getCodecData() {
return codecData;
}
/**
* This method will take a look at {@link #compressionFormat}and returns a
* String with its hex value and if known a textual note on what coded it
* represents. <br>
*
* @return A description for the used codec.
*/
public String getCodecDescription() {
StringBuffer result = new StringBuffer(Long
.toHexString(getCompressionFormat()));
String furtherDesc = " (Unknown)";
for (int i = 0; i < CODEC_DESCRIPTIONS.length; i++) {
if (CODEC_DESCRIPTIONS[i][0].equalsIgnoreCase(result.toString())) {
furtherDesc = CODEC_DESCRIPTIONS[i][1];
break;
}
}
if (result.length() % 2 != 0) {
result.insert(0, "0x0");
} else {
result.insert(0, "0x");
}
result.append(furtherDesc);
return result.toString();
}
/**
* @return Returns the compressionFormat.
*/
public long getCompressionFormat() {
return compressionFormat;
}
/**
* @return Returns the errorConcealment.
*/
public GUID getErrorConcealment() {
return errorConcealment;
}
/**
* This method takes the value of {@link #getAverageBytesPerSec()}and
* calculates the kbps out of it, by simply multiplying by 8 and dividing by
* 1000. <br>
*
* @return amount of bits per second in kilo bits.
*/
public int getKbps() {
return (int) getAverageBytesPerSec() * 8 / 1000;
}
/**
* @return Returns the samplingRate.
*/
public long getSamplingRate() {
return samplingRate;
}
/**
* This mehtod returns whether the audio stream data is error concealed.
* <br>
* For now only interleaved concealment is known. <br>
*
* @return <code>true</code> if error concealment is used.
*/
public boolean isErrorConcealed() {
return getErrorConcealment().equals(
GUID.GUID_AUDIO_ERROR_CONCEALEMENT_INTERLEAVED);
}
/**
* (overridden)
*
* @see entagged.audioformats.asf.data.StreamChunk#prettyPrint()
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer(super.prettyPrint().replaceAll(
Utils.LINE_SEPARATOR, Utils.LINE_SEPARATOR + " "));
result.insert(0, Utils.LINE_SEPARATOR + "AudioStream");
result.append("Audio info:" + Utils.LINE_SEPARATOR);
result.append(" Bitrate : " + getKbps() + Utils.LINE_SEPARATOR);
result.append(" Channels : " + getChannelCount() + " at "
+ getSamplingRate() + " Hz" + Utils.LINE_SEPARATOR);
result.append(" Bits per Sample: " + getBitsPerSample()
+ Utils.LINE_SEPARATOR);
result.append(" Formatcode: " + getCodecDescription()
+ Utils.LINE_SEPARATOR);
return result.toString();
}
/**
* @param avgeBytesPerSec
* The averageBytesPerSec to set.
*/
public void setAverageBytesPerSec(long avgeBytesPerSec) {
this.averageBytesPerSec = avgeBytesPerSec;
}
/**
* Sets the bitsPerSample
*
* @param bps
*/
public void setBitsPerSample(int bps) {
this.bitsPerSample = bps;
}
/**
* Sets the blockAlignment.
*
* @param align
*/
public void setBlockAlignment(long align) {
this.blockAlignment = align;
}
/**
* @param channels
* The channelCount to set.
*/
public void setChannelCount(long channels) {
this.channelCount = channels;
}
/**
* Sets the codecData
*
* @param codecSpecificData
*/
public void setCodecData(byte[] codecSpecificData) {
this.codecData = codecSpecificData;
}
/**
* @param cFormatCode
* The compressionFormat to set.
*/
public void setCompressionFormat(long cFormatCode) {
this.compressionFormat = cFormatCode;
}
/**
* This method sets the error concealment type which is given by two GUIDs.
* <br>
*
* @param errConc
* the type of error concealment the audio stream is stored as.
*/
public void setErrorConcealment(GUID errConc) {
this.errorConcealment = errConc;
}
/**
* @param sampRate
* The samplingRate to set.
*/
public void setSamplingRate(long sampRate) {
this.samplingRate = sampRate;
}
}

View file

@ -0,0 +1,135 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.math.BigInteger;
/**
* This class represents a chunk within asf streams. <br>
* Each chunk starts with a 16byte guid identifying the type. After that a
* number (represented by 8 bytes) follows which shows the size in bytes of the
* chunk. Finally there is the data of the chunk.
*
* @author Christian Laireiter
*/
public class Chunk {
/**
* The length of current chunk. <br>
*/
protected final BigInteger chunkLength;
/**
* The guid of represented chunk header.
*/
protected final GUID guid;
/**
* The position of current header object within file or stream.
*/
protected final long position;
/**
* Creates an instance
*
* @param headerGuid
* The GUID of header object.
* @param pos
* Position of header object within stream or file.
* @param chunkLen
* Length of current chunk.
*/
public Chunk(GUID headerGuid, long pos, BigInteger chunkLen) {
if (headerGuid == null) {
throw new IllegalArgumentException(
"GUID must not be null nor anything else than "
+ GUID.GUID_LENGTH + " entries long.");
}
if (pos < 0) {
throw new IllegalArgumentException(
"Position of header can't be negative.");
}
if (chunkLen == null || chunkLen.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException(
"chunkLen must not be null nor negative.");
}
this.guid = headerGuid;
this.position = pos;
this.chunkLength = chunkLen;
}
/**
* This method returns the End of the current chunk introduced by current
* header object.
*
* @return Position after current chunk.
*/
public long getChunckEnd() {
return position + chunkLength.longValue();
}
/**
* @return Returns the chunkLength.
*/
public BigInteger getChunkLength() {
return chunkLength;
}
/**
* @return Returns the guid.
*/
public GUID getGuid() {
return guid;
}
/**
* @return Returns the position.
*/
public long getPosition() {
return position;
}
/**
* This method creates a String containing usefull information prepared to
* be printed on stdout. <br>
* This method is intended to be overwritten by inheriting classes.
*
* @return Information of current Chunk Object.
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer();
result.append("GUID: " + GUID.getGuidDescription(guid));
result.append("\n Starts at position: " + getPosition() + "\n");
result.append(" Last byte at: " + (getChunckEnd() - 1) + "\n\n");
return result.toString();
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
public String toString() {
return prettyPrint();
}
}

View file

@ -0,0 +1,251 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import entagged.audioformats.asf.util.Utils;
/**
* This class represents the data of a chunk which contains title, author,
* copyright, description and the rating of the file. <br>
* It is optional whithin asf files. But if exists only once.
*
* @author Christian Laireiter
*/
public class ContentDescription extends Chunk {
/**
* File artist.
*/
private String author = null;
/**
* File copyright.
*/
private String copyRight = null;
/**
* File comment.
*/
private String description = null;
/**
* File rating.
*/
private String rating = null;
/**
* File title.
*/
private String title = null;
/**
* Creates an instance. <br>
*/
public ContentDescription() {
this(0, BigInteger.valueOf(0));
}
/**
* Creates an instance.
*
* @param pos
* Position of content description within file or stream
* @param chunkLen
* Length of content description.
*/
public ContentDescription(long pos, BigInteger chunkLen) {
super(GUID.GUID_CONTENTDESCRIPTION, pos, chunkLen);
}
/**
* @return Returns the author.
*/
public String getAuthor() {
if (author == null)
return "";
return author;
}
/**
* This method creates a byte array that could directly be written to an asf
* file. <br>
*
* @return The asf chunk representation of a content description with the
* values of the current object.
*/
public byte[] getBytes() {
ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
ByteArrayOutputStream tags = new ByteArrayOutputStream();
String[] toWrite = new String[] { getTitle(), getAuthor(),
getCopyRight(), getComment(), getRating() };
byte[][] stringRepresentations = new byte[toWrite.length][];
// Create byte[] of UTF-16LE encodings
for (int i = 0; i < toWrite.length; i++) {
stringRepresentations[i] = toWrite[i].getBytes("UTF-16LE");
}
// Write the amount of bytes needed to store the values.
for (int i = 0; i < stringRepresentations.length; i++) {
tags.write(Utils.getBytes(stringRepresentations[i].length + 2,
2));
}
// Write the values themselves.
for (int i = 0; i < toWrite.length; i++) {
tags.write(stringRepresentations[i]);
// Zero term character.
tags.write(Utils.getBytes(0, 2));
}
// Now tags has got the values. The result just needs
// The GUID, length of the chunk and the tags.
byte[] tagContent = tags.toByteArray();
// The guid of the chunk
result.write(GUID.GUID_CONTENTDESCRIPTION.getBytes());
/*
* The length of the chunk. 16 Bytes guid 8 Bytes the length
* tagContent.length bytes.
*/
result.write(Utils.getBytes(tagContent.length + 24, 8));
// The tags.
result.write(tagContent);
} catch (Exception e) {
e.printStackTrace();
}
return result.toByteArray();
}
/**
* @return Returns the comment.
*/
public String getComment() {
if (description == null)
return "";
return description;
}
/**
* @return Returns the copyRight.
*/
public String getCopyRight() {
if (copyRight == null)
return "";
return copyRight;
}
/**
* @return returns the rating.
*/
public String getRating() {
if (rating == null)
return "";
return rating;
}
/**
* @return Returns the title.
*/
public String getTitle() {
if (title == null)
return "";
return title;
}
/**
* (overridden)
*
* @see entagged.audioformats.asf.data.Chunk#prettyPrint()
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer(super.prettyPrint());
result.insert(0, Utils.LINE_SEPARATOR + "Content Description:"
+ Utils.LINE_SEPARATOR);
result.append(" Title : " + getTitle() + Utils.LINE_SEPARATOR);
result.append(" Author : " + getAuthor() + Utils.LINE_SEPARATOR);
result.append(" Copyright : " + getCopyRight()
+ Utils.LINE_SEPARATOR);
result.append(" Description: " + getComment() + Utils.LINE_SEPARATOR);
result.append(" Rating :" + getRating() + Utils.LINE_SEPARATOR);
return result.toString();
}
/**
* @param fileAuthor
* The author to set.
* @throws IllegalArgumentException
* If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setAuthor(String fileAuthor) throws IllegalArgumentException {
Utils.checkStringLengthNullSafe(fileAuthor);
this.author = fileAuthor;
}
/**
* @param tagComment
* The comment to set.
* @throws IllegalArgumentException
* If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setComment(String tagComment) throws IllegalArgumentException {
Utils.checkStringLengthNullSafe(tagComment);
this.description = tagComment;
}
/**
* @param cpright
* The copyRight to set.
* @throws IllegalArgumentException
* If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setCopyRight(String cpright) throws IllegalArgumentException {
Utils.checkStringLengthNullSafe(cpright);
this.copyRight = cpright;
}
/**
* @param ratingText
* The rating to be set.
* @throws IllegalArgumentException
* If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setRating(String ratingText) throws IllegalArgumentException {
Utils.checkStringLengthNullSafe(ratingText);
this.rating = ratingText;
}
/**
* @param songTitle
* The title to set.
* @throws IllegalArgumentException
* If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setTitle(String songTitle) throws IllegalArgumentException {
Utils.checkStringLengthNullSafe(songTitle);
this.title = songTitle;
}
}

View file

@ -0,0 +1,517 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashSet;
import entagged.audioformats.asf.util.Utils;
/**
* This class is a wrapper for properties within a
* {@link entagged.audioformats.asf.data.ExtendedContentDescription}.<br>
*
* @author Christian Laireiter
*/
public final class ContentDescriptor implements Comparable {
/**
* This field stores all values of the "ID_"-constants.
*/
public final static HashSet COMMON_FIELD_IDS;
/**
* This constant gives the common id (name) for the "album" field in an asf
* extended content description.
*/
public final static String ID_ALBUM = "WM/AlbumTitle";
/**
* This constant gives the common id (name) for the "artist" field in an asf
* extended content description.
*/
public final static String ID_ARTIST ="WM/AlbumArtist";
/**
* This constant gives the common id (name) for the "genre" field in an asf
* extended content description.
*/
public final static String ID_GENRE = "WM/Genre";
/**
* This constant gives the common id (name) for the "genre Id" field in an
* asf extended content description.
*/
public final static String ID_GENREID = "WM/GenreID";
/**
* This constant gives the common id (name) for the "track number" field in
* an asf extended content description.
*/
public final static String ID_TRACKNUMBER = "WM/TrackNumber";
/**
* This constant gives the common id (name) for the "year" field in an asf
* extended content description.
*/
public final static String ID_YEAR = "WM/Year";
/**
* Constant for the content descriptor-type for binary data.
*/
public final static int TYPE_BINARY = 1;
/**
* Constant for the content descriptor-type for booleans.
*/
public final static int TYPE_BOOLEAN = 2;
/**
* Constant for the content descriptor-type for integers (32-bit). <br>
*/
public final static int TYPE_DWORD = 3;
/**
* Constant for the content descriptor-type for integers (64-bit). <br>
*/
public final static int TYPE_QWORD = 4;
/**
* Constant for the content descriptor-type for Strings.
*/
public final static int TYPE_STRING = 0;
/**
* Constant for the content descriptor-type for integers (16-bit). <br>
*/
public final static int TYPE_WORD = 5;
static {
COMMON_FIELD_IDS = new HashSet();
COMMON_FIELD_IDS.add(ID_ALBUM);
COMMON_FIELD_IDS.add(ID_ARTIST);
COMMON_FIELD_IDS.add(ID_GENRE);
COMMON_FIELD_IDS.add(ID_GENREID);
COMMON_FIELD_IDS.add(ID_TRACKNUMBER);
COMMON_FIELD_IDS.add(ID_YEAR);
}
/**
* The binary representation of the value.
*/
protected byte[] content = new byte[0];
/**
* This field shows the type of the content descriptor. <br>
*
* @see #TYPE_BINARY
* @see #TYPE_BOOLEAN
* @see #TYPE_DWORD
* @see #TYPE_QWORD
* @see #TYPE_STRING
* @see #TYPE_WORD
*/
private int descriptorType;
/**
* The name of the content descriptor.
*/
private final String name;
/**
* Creates an Instance.
*
* @param propName
* Name of the ContentDescriptor.
* @param propType
* Type of the content descriptor. See {@link #descriptorType}
*
*/
public ContentDescriptor(String propName, int propType) {
if (propName == null) {
throw new IllegalArgumentException("Arguments must not be null.");
}
Utils.checkStringLengthNullSafe(propName);
this.name = propName;
this.descriptorType = propType;
}
/**
* (overridden)
*
* @see java.lang.Object#clone()
*/
public Object clone() throws CloneNotSupportedException {
return createCopy();
}
/**
* (overridden)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object o) {
int result = 0;
if (o instanceof ContentDescriptor) {
ContentDescriptor other = (ContentDescriptor) o;
result = getName().compareTo(other.getName());
}
return result;
}
/**
* This mehtod creates a copy of the current object. <br>
* All data will be copied, too. <br>
*
* @return A new Contentdescriptor containing the same values as the current
* one.
*/
public ContentDescriptor createCopy() {
ContentDescriptor result = new ContentDescriptor(getName(), getType());
result.content = getRawData();
return result;
}
/**
* (overridden)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
boolean result = false;
if (obj instanceof ContentDescriptor) {
if (obj == this) {
result = true;
} else {
ContentDescriptor other = (ContentDescriptor) obj;
result = other.getName().equals(getName())
&& other.descriptorType == this.descriptorType
&& Arrays.equals(this.content, other.content);
}
}
return result;
}
/**
* Returns the value of the ContentDescriptor as a Boolean. <br>
* If no Conversion is Possible false is returned. <br>
* <code>true</code> if first byte of {@link #content}is not zero.
*
* @return boolean representation of the current value.
*/
public boolean getBoolean() {
return content.length > 0 && content[0] != 0;
}
/**
* This method will return a byte array, which can directly be written into
* an "Extended Content Description"-chunk. <br>
*
* @return byte[] with the data, that occurs in asf files.
*/
public byte[] getBytes() {
ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
byte[] nameBytes = getName().getBytes("UTF-16LE");
// Write the number of bytes the name needs. +2 because of the
// Zero term character.
result.write(Utils.getBytes(nameBytes.length + 2, 2));
// Write the name itself
result.write(nameBytes);
// Write zero term character
result.write(Utils.getBytes(0, 2));
// Write the type of the current descriptor
result.write(Utils.getBytes(getType(), 2));
/*
* Now the content.
*/
if (this.getType() == TYPE_STRING) {
// String length +2 for zero termination
result.write(Utils.getBytes(content.length + 2, 2));
// Value
result.write(content);
// Zero term
result.write(Utils.getBytes(0, 2));
} else {
result.write(Utils.getBytes(content.length, 2));
result.write(content);
}
} catch (Exception e) {
e.printStackTrace();
}
return result.toByteArray();
}
/**
* This method returns the name of the content descriptor.
*
* @return Name.
*/
public String getName() {
return this.name;
}
/**
* This method returns the value of the content descriptor as an integer.
* <br>
* Converts the needed amount of byte out of {@link #content}to a number.
* <br>
* Only possible if {@link #getType()}equals on of the following: <br>
* <li>
*
* @see #TYPE_BOOLEAN</li>
* <li>
* @see #TYPE_DWORD</li>
* <li>
* @see #TYPE_QWORD</li>
* <li>
* @see #TYPE_WORD</li>
*
* @return integer value.
*/
public long getNumber() {
long result = 0;
int bytesNeeded = -1;
switch (getType()) {
case TYPE_BOOLEAN:
bytesNeeded = 1;
break;
case TYPE_DWORD:
bytesNeeded = 4;
break;
case TYPE_QWORD:
bytesNeeded = 8;
break;
case TYPE_WORD:
bytesNeeded = 2;
break;
default:
throw new UnsupportedOperationException(
"The current type doesn't allow an interpretation as a number.");
}
if (bytesNeeded > content.length) {
throw new IllegalStateException(
"The stored data cannot represent the type of current object.");
}
for (int i = 0; i < bytesNeeded; i++) {
result |= (content[i] << (i * 8));
}
return result;
}
/**
* This method returns a copy of the content of the descriptor. <br>
*
* @return The content in binary representation, as it would be written to
* asf file. <br>
*/
public byte[] getRawData() {
byte[] copy = new byte[this.content.length];
System.arraycopy(copy, 0, this.content, 0, this.content.length);
return copy;
}
/**
* Returns the value of the ContentDescriptor as a String. <br>
*
* @return String - Representation Value
*/
public String getString() {
String result = "";
switch (getType()) {
case TYPE_BINARY:
result = "binary data";
break;
case TYPE_BOOLEAN:
result = String.valueOf(getBoolean());
break;
case TYPE_QWORD:
case TYPE_DWORD:
case TYPE_WORD:
result = String.valueOf(getNumber());
break;
case TYPE_STRING:
try {
result = new String(content, "UTF-16LE");
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
throw new IllegalStateException("Current type is not known.");
}
return result;
}
/**
* Returns the type of the content descriptor. <br>
*
* @see #TYPE_BINARY
* @see #TYPE_BOOLEAN
* @see #TYPE_DWORD
* @see #TYPE_QWORD
* @see #TYPE_STRING
* @see #TYPE_WORD
*
* @return the value of {@link #descriptorType}
*/
public int getType() {
return this.descriptorType;
}
/**
* This method checks whether the name of the current field is one of the
* commonly specified fields. <br>
*
* @see #ID_ALBUM
* @see #ID_GENRE
* @see #ID_GENREID
* @see #ID_TRACKNUMBER
* @see #ID_YEAR
* @return <code>true</code> if a common field.
*/
public boolean isCommon() {
return COMMON_FIELD_IDS.contains(this.getName());
}
/**
* This method checks if the binary data is empty. <br>
* Disregarding the type of the descriptor its content is stored as a byte
* array.
*
* @return <code>true</code> if no value is set.
*/
public boolean isEmpty() {
return this.content.length == 0;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_BINARY}.<br>
*
* @param data
* Value to set.
* @throws IllegalArgumentException
* If the byte array is greater that 65535 bytes.
*/
public void setBinaryValue(byte[] data) throws IllegalArgumentException {
if (data.length > 65535) {
throw new IllegalArgumentException(
"Too many bytes. 65535 is maximum.");
}
this.content = data;
this.descriptorType = TYPE_BINARY;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_BOOLEAN}.<br>
*
* @param value
* Value to set.
*/
public void setBooleanValue(boolean value) {
this.content = new byte[] { value ? (byte) 1 : 0, 0, 0, 0 };
this.descriptorType = TYPE_BOOLEAN;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_DWORD}.
*
* @param value
* Value to set.
*/
public void setDWordValue(long value) {
this.content = Utils.getBytes(value, 4);
this.descriptorType = TYPE_DWORD;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_QWORD}
*
* @param value
* Value to set.
*/
public void setQWordValue(long value) {
this.content = Utils.getBytes(value, 8);
this.descriptorType = TYPE_QWORD;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_STRING}.
*
* @param value
* Value to set.
* @throws IllegalArgumentException
* If byte representation would take more than 65535 Bytes.
*/
public void setStringValue(String value) throws IllegalArgumentException {
try {
byte[] tmp = value.getBytes("UTF-16LE");
if (tmp.length > 65535) {
throw new IllegalArgumentException(
"Byte representation of String in "
+ "\"UTF-16LE\" is to great. (Maximum is 65535 Bytes)");
}
this.content = tmp;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
this.content = new byte[0];
}
this.descriptorType = TYPE_STRING;
}
/**
* Sets the Value of the current content descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_WORD}
*
* @param value
* Value to set.
*/
public void setWordValue(int value) {
this.content = Utils.getBytes(value, 2);
this.descriptorType = TYPE_WORD;
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
public String toString() {
return getName()
+ " : "
+ new String[] { "String: ", "Binary: ", "Boolean: ",
"DWORD: ", "QWORD:", "WORD:" }[this.descriptorType]
+ getString();
}
}

View file

@ -0,0 +1,95 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import entagged.audioformats.asf.util.Utils;
/**
* This class was intended to store the data of a chunk which contained the
* encoding parameters in textual form. <br>
* Since the needed parameters were found in other chunks the implementation of
* this class was paused. <br>
* TODO complete analysis.
*
* @author Christian Laireiter
*/
public class EncodingChunk extends Chunk {
/**
* The read strings.
*/
private final ArrayList strings;
/**
* Creates an instance.
*
* @param pos
* Position of the chunk within file or stream
* @param chunkLen
* Length of current chunk.
*/
public EncodingChunk(long pos, BigInteger chunkLen) {
super(GUID.GUID_ENCODING, pos, chunkLen);
this.strings = new ArrayList();
}
/**
* This method appends a String.
*
* @param toAdd
* String to add.
*/
public void addString(String toAdd) {
strings.add(toAdd);
}
/**
* This method returns a collection of all {@link String}s which were addid
* due {@link #addString(String)}.
*
* @return Inserted Strings.
*/
public Collection getStrings() {
return new ArrayList(strings);
}
/**
* (overridden)
*
* @see entagged.audioformats.asf.data.Chunk#prettyPrint()
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer(super.prettyPrint());
result.insert(0, Utils.LINE_SEPARATOR + "Encoding:"
+ Utils.LINE_SEPARATOR);
Iterator iterator = this.strings.iterator();
while (iterator.hasNext()) {
result.append(" " + iterator.next() + Utils.LINE_SEPARATOR);
}
return result.toString();
}
}

View file

@ -0,0 +1,299 @@
/*
* ******************************************************************** **
* Copyright notice **
* ** **
* (c) 2003 Entagged Developpement Team **
* http://www.sourceforge.net/projects/entagged **
* ** **
* All rights reserved **
* ** **
* This script is part of the Entagged project. The Entagged **
* project is free software; you can redistribute it and/or modify **
* it under the terms of the GNU General Public License as published by **
* the Free Software Foundation; either version 2 of the License, or **
* (at your option) any later version. **
* ** **
* The GNU General Public License can be found at **
* http://www.gnu.org/copyleft/gpl.html. **
* ** **
* This copyright notice MUST APPEAR in all copies of the file! **
* ********************************************************************
*/
package entagged.audioformats.asf.data;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import entagged.audioformats.Tag;
import entagged.audioformats.asf.util.Utils;
/**
* This structure represents the data of a chunk, wich contains extended content
* description. <br>
* These properties are simply represented by
* {@link entagged.audioformats.asf.data.ContentDescriptor}
*
* @author Christian Laireiter
*/
public class ExtendedContentDescription extends Chunk {
/**
* Contains the properties. <br>
*/
private final ArrayList descriptors;
/**
* This map stores the ids (names) of inserted content descriptors. <br>
* If {@link #getDescriptor(String)}is called this field will be filled if
* <code>null</code>. Any modification of the contents of this object
* will set this field to <code>null</code>.
*/
private HashMap indexMap = null;
/**
* Creates an instance.
*
*/
public ExtendedContentDescription() {
this(0, BigInteger.valueOf(0));
}
/**
* Creates an instance.
*
* @param pos
* Position of header object within file or stream.
* @param chunkLen
* Length of the represented chunck.
*/
public ExtendedContentDescription(long pos, BigInteger chunkLen) {
super(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, pos, chunkLen);
this.descriptors = new ArrayList();
}
/**
* This method inserts the given ContentDescriptor.
*
* @param toAdd
* ContentDescriptor to insert.
*/
public void addDescriptor(ContentDescriptor toAdd) {
assert toAdd != null : "Argument must not be null.";
if (getDescriptor(toAdd.getName()) != null) {
throw new RuntimeException(toAdd.getName() + " is already present");
}
this.descriptors.add(toAdd);
this.indexMap.put(toAdd.getName(), new Integer(descriptors.size() - 1));
}
/**
* This method adds or replaces an existing content descriptor.
*
* @param descriptor
* Descriptor to be added or replaced.
*/
public void addOrReplace(ContentDescriptor descriptor) {
assert descriptor != null : "Argument must not be null";
if (getDescriptor(descriptor.getName()) != null) {
/*
* Just remove if exists. Will prevent the indexmap being rebuild.
*/
remove(descriptor.getName());
}
addDescriptor(descriptor);
}
/**
* Returns the album entered in the content descriptor chunk.
*
* @return Album, <code>""</code> if not defined.
*/
public String getAlbum() {
ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ALBUM);
if (result == null)
return "";
return result.getString();
}
/**
* Returns the "WM/AlbumArtist" entered in the extended content description.
*
* @return Title, <code>""</code> if not defined.
*/
public String getArtist() {
ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ARTIST);
if (result == null)
return "";
return result.getString();
}
/**
* This method creates a byte array which can be written to asf files.
*
* @return asf file representation of the current object.
*/
public byte[] getBytes() {
ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
ByteArrayOutputStream content = new ByteArrayOutputStream();
// Write the number of descriptors.
content.write(Utils.getBytes(this.descriptors.size(), 2));
Iterator it = this.descriptors.iterator();
while (it.hasNext()) {
ContentDescriptor current = (ContentDescriptor) it.next();
content.write(current.getBytes());
}
byte[] contentBytes = content.toByteArray();
// Write the guid
result.write(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION.getBytes());
// Write the length + 24.
result.write(Utils.getBytes(contentBytes.length + 24, 8));
// Write the content
result.write(contentBytes);
} catch (Exception e) {
e.printStackTrace();
}
return result.toByteArray();
}
/**
* Returns a previously inserted content descriptor.
*
* @param name
* name of the content descriptor.
* @return <code>null</code> if not present.
*/
public ContentDescriptor getDescriptor(String name) {
if (this.indexMap == null) {
this.indexMap = new HashMap();
for (int i = 0; i < descriptors.size(); i++) {
ContentDescriptor current = (ContentDescriptor) descriptors
.get(i);
indexMap.put(current.getName(), new Integer(i));
}
}
Integer pos = (Integer) indexMap.get(name);
if (pos != null) {
return (ContentDescriptor) descriptors.get(pos.intValue());
}
return null;
}
/**
* @return Returns the descriptorCount.
*/
public long getDescriptorCount() {
return descriptors.size();
}
/**
* Returns a collection of all {@link ContentDescriptor}objects stored in
* this extended content description.
*
* @return An enumeration of {@link ContentDescriptor}objects.
*/
public Collection getDescriptors() {
return new ArrayList(this.descriptors);
}
/**
* Returns the Genre entered in the content descriptor chunk.
*
* @return Genre, <code>""</code> if not defined.
*/
public String getGenre() {
String result = null;
ContentDescriptor prop = getDescriptor(ContentDescriptor.ID_GENRE);
if (prop == null) {
prop = getDescriptor(ContentDescriptor.ID_GENREID);
if (prop == null)
result = "";
else {
result = prop.getString();
if (result.startsWith("(") && result.endsWith(")")) {
result = result.substring(1, result.length() - 1);
try {
int genreNum = Integer.parseInt(result);
if (genreNum >= 0
&& genreNum < Tag.DEFAULT_GENRES.length) {
result = Tag.DEFAULT_GENRES[genreNum];
}
} catch (NumberFormatException e) {
// Do nothing
}
}
}
} else {
result = prop.getString();
}
return result;
}
/**
* Returns the Track entered in the content descriptor chunk.
*
* @return Track, <code>""</code> if not defined.
*/
public String getTrack() {
ContentDescriptor result = getDescriptor(ContentDescriptor.ID_TRACKNUMBER);
if (result == null)
return "";
return result.getString();
}
/**
* Returns the Year entered in the extended content descripion.
*
* @return Year, <code>""</code> if not defined.
*/
public String getYear() {
ContentDescriptor result = getDescriptor(ContentDescriptor.ID_YEAR);
if (result == null)
return "";
return result.getString();
}
/**
* This method creates a String containing the tag elements an their values
* for printing. <br>
*
* @return nice string.
*/
public String prettyPrint() {
StringBuffer result = new StringBuffer(super.prettyPrint());
result.insert(0, "\nExtended Content Description:\n");
ContentDescriptor[] list = (ContentDescriptor[]) descriptors
.toArray(new ContentDescriptor[descriptors.size()]);
Arrays.sort(list);
for (int i = 0; i < list.length; i++) {
result.append(" ");
result.append(list[i]);
result.append(Utils.LINE_SEPARATOR);
}
return result.toString();
}
/**
* This method removes the content descriptor with the given name. <br>
*
* @param id
* The id (name) of the descriptor which should be removed.
* @return The descriptor which is removed. If not present <code>null</code>.
*/
public ContentDescriptor remove(String id) {
ContentDescriptor result = getDescriptor(id);
if (result != null) {
descriptors.remove(result);
}
this.indexMap = null;
return result;
}
}

Some files were not shown because too many files have changed in this diff Show more