
com.jcraft.jorbis.VorbisFile Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sound Show documentation
Show all versions of sound Show documentation
Etyl's default sound module
The newest version!
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk
*
* Many thanks to
* Monty 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 java.io.IOException;
import java.io.InputStream;
import com.jcraft.jogg.Packet;
import com.jcraft.jogg.Page;
import com.jcraft.jogg.StreamState;
import com.jcraft.jogg.SyncState;
public class VorbisFile{
static final int CHUNKSIZE=8500;
static final int SEEK_SET=0;
static final int SEEK_CUR=1;
static final int SEEK_END=2;
static final int OV_FALSE=-1;
static final int OV_EOF=-2;
static final int OV_HOLE=-3;
static final int OV_EREAD=-128;
static final int OV_EFAULT=-129;
static final int OV_EIMPL=-130;
static final int OV_EINVAL=-131;
static final int OV_ENOTVORBIS=-132;
static final int OV_EBADHEADER=-133;
static final int OV_EVERSION=-134;
static final int OV_ENOTAUDIO=-135;
static final int OV_EBADPACKET=-136;
static final int OV_EBADLINK=-137;
static final int OV_ENOSEEK=-138;
InputStream datasource;
boolean seekable=false;
long offset;
long end;
SyncState oy=new SyncState();
int links;
long[] offsets;
long[] dataoffsets;
int[] serialnos;
long[] pcmlengths;
Info[] vi;
Comment[] vc;
// Decoding working state local storage
long pcm_offset;
boolean decode_ready=false;
int current_serialno;
int current_link;
float bittrack;
float samptrack;
StreamState os=new StreamState(); // take physical pages, weld into a logical
// stream of packets
DspState vd=new DspState(); // central working state for
// the packet->PCM decoder
Block vb=new Block(vd); // local working space for packet->PCM decode
//ov_callbacks callbacks;
public VorbisFile(String file) throws JOrbisException{
super();
InputStream is=null;
try{
is=new SeekableInputStream(file);
int ret=open(is, null, 0);
if(ret==-1){
throw new JOrbisException("VorbisFile: open return -1");
}
}
catch(Exception e){
throw new JOrbisException("VorbisFile: "+e.toString());
}
finally{
if(is!=null){
try{
is.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
}
public VorbisFile(InputStream is, byte[] initial, int ibytes)
throws JOrbisException{
super();
int ret=open(is, initial, ibytes);
if(ret==-1){
}
}
private int get_data(){
int index=oy.buffer(CHUNKSIZE);
byte[] buffer=oy.data;
int bytes=0;
try{
bytes=datasource.read(buffer, index, CHUNKSIZE);
}
catch(Exception e){
return OV_EREAD;
}
oy.wrote(bytes);
if(bytes==-1){
bytes=0;
}
return bytes;
}
private void seek_helper(long offst){
fseek(datasource, offst, SEEK_SET);
this.offset=offst;
oy.reset();
}
private int get_next_page(Page page, long boundary){
if(boundary>0)
boundary+=offset;
while(true){
int more;
if(boundary>0&&offset>=boundary)
return OV_FALSE;
more=oy.pageseek(page);
if(more<0){
offset-=more;
}
else{
if(more==0){
if(boundary==0)
return OV_FALSE;
int ret=get_data();
if(ret==0)
return OV_EOF;
if(ret<0)
return OV_EREAD;
}
else{
int ret=(int)offset; //!!!
offset+=more;
return ret;
}
}
}
}
private int get_prev_page(Page page) throws JOrbisException{
long begin=offset; //!!!
int ret;
int offst=-1;
while(offst==-1){
begin-=CHUNKSIZE;
if(begin<0)
begin=0;
seek_helper(begin);
while(offset=0)
next=ret;
}
else{
searched=ret+page.header_len+page.body_len;
}
}
seek_helper(next);
ret=get_next_page(page, -1);
if(ret==OV_EREAD)
return OV_EREAD;
if(searched>=end||ret==-1){
links=m+1;
offsets=new long[m+2];
offsets[m+1]=searched;
}
else{
ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1);
if(ret==OV_EREAD)
return OV_EREAD;
}
offsets[m]=begin;
return 0;
}
// uses the local ogg_stream storage in vf; this is important for
// non-streaming input sources
int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr){
Page og=new Page();
Packet op=new Packet();
int ret;
if(og_ptr==null){
ret=get_next_page(og, CHUNKSIZE);
if(ret==OV_EREAD)
return OV_EREAD;
if(ret<0)
return OV_ENOTVORBIS;
og_ptr=og;
}
if(serialno!=null)
serialno[0]=og_ptr.serialno();
os.init(og_ptr.serialno());
// extract the initial header from the first page and verify that the
// Ogg bitstream is in fact Vorbis data
vi.init();
vc.init();
int i=0;
while(i<3){
os.pagein(og_ptr);
while(i<3){
int result=os.packetout(op);
if(result==0)
break;
if(result==-1){
vi.clear();
vc.clear();
os.clear();
return -1;
}
if(vi.synthesis_headerin(vc, op)!=0){
vi.clear();
vc.clear();
os.clear();
return -1;
}
i++;
}
if(i<3)
if(get_next_page(og_ptr, 1)<0){
vi.clear();
vc.clear();
os.clear();
return -1;
}
}
return 0;
}
// last step of the OggVorbis_File initialization; get all the
// vorbis_info structs and PCM positions. Only called by the seekable
// initialization (local stream storage is hacked slightly; pay
// attention to how that's done)
void prefetch_all_headers(Info first_i, Comment first_c, int dataoffset)
throws JOrbisException{
Page og=new Page();
int ret;
vi=new Info[links];
vc=new Comment[links];
dataoffsets=new long[links];
pcmlengths=new long[links];
serialnos=new int[links];
for(int i=0; i0){
// got a packet. process it
granulepos=op.granulepos;
if(vb.synthesis(op)==0){ // lazy check for lazy
// header handling. The
// header packets aren't
// audio, so if/when we
// submit them,
// vorbis_synthesis will
// reject them
// suck in the synthesis data and track bitrate
{
int oldsamples=vd.synthesis_pcmout(null, null);
vd.synthesis_blockin(vb);
samptrack+=vd.synthesis_pcmout(null, null)-oldsamples;
bittrack+=op.bytes*8;
}
// update the pcm offset.
if(granulepos!=-1&&op.e_o_s==0){
int link=(seekable ? current_link : 0);
int samples;
// this packet has a pcm_offset on it (the last packet
// completed on a page carries the offset) After processing
// (above), we know the pcm position of the *last* sample
// ready to be returned. Find the offset of the *first*
//
// As an aside, this trick is inaccurate if we begin
// reading anew right at the last page; the end-of-stream
// granulepos declares the last frame in the stream, and the
// last packet of the last page may be a partial frame.
// So, we need a previous granulepos from an in-sequence page
// to have a reference point. Thus the !op.e_o_s clause above
samples=vd.synthesis_pcmout(null, null);
granulepos-=samples;
for(int i=0; i=links)
return (-1);
if(!seekable&&i!=0)
return (bitrate(0));
if(i<0){
long bits=0;
for(int j=0; j0){
return vi[i].bitrate_nominal;
}
else{
if(vi[i].bitrate_upper>0){
if(vi[i].bitrate_lower>0){
return (vi[i].bitrate_upper+vi[i].bitrate_lower)/2;
}
else{
return vi[i].bitrate_upper;
}
}
return (-1);
}
}
}
}
// returns the actual bitrate since last call. returns -1 if no
// additional data to offer since last call (or at beginning of stream)
public int bitrate_instant(){
int _link=(seekable ? current_link : 0);
if(samptrack==0)
return (-1);
int ret=(int)(bittrack/samptrack*vi[_link].rate+.5);
bittrack=0.f;
samptrack=0.f;
return (ret);
}
public int serialnumber(int i){
if(i>=links)
return (-1);
if(!seekable&&i>=0)
return (serialnumber(-1));
if(i<0){
return (current_serialno);
}
else{
return (serialnos[i]);
}
}
// returns: total raw (compressed) length of content if i==-1
// raw (compressed) length of that logical bitstream for i==0 to n
// -1 if the stream is not seekable (we can't know the length)
public long raw_total(int i){
if(!seekable||i>=links)
return (-1);
if(i<0){
long acc=0; // bug?
for(int j=0; j=links)
return (-1);
if(i<0){
long acc=0;
for(int j=0; j=links)
return (-1);
if(i<0){
float acc=0;
for(int j=0; joffsets[links]){
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
}
// clear out decoding machine state
pcm_offset=-1;
decode_clear();
// seek
seek_helper(pos);
// we need to make sure the pcm_offset is set. We use the
// _fetch_packet helper to process one packet with readp set, then
// call it until it returns '0' with readp not set (the last packet
// from a page has the 'granulepos' field set, and that's how the
// helper updates the offset
switch(process_packet(1)){
case 0:
// oh, eof. There are no packets remaining. Set the pcm offset to
// the end of file
pcm_offset=pcm_total(-1);
return (0);
case -1:
// error! missing data or invalid bitstream structure
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
default:
// all OK
break;
}
while(true){
switch(process_packet(0)){
case 0:
// the offset is set. If it's a bogus bitstream with no offset
// information, it's not but that's not our fault. We still run
// gracefully, we're just missing the offset
return (0);
case -1:
// error! missing data or invalid bitstream structure
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
default:
// continue processing packets
break;
}
}
// seek_error:
// dump the machine so we're in a known state
//pcm_offset=-1;
//decode_clear();
//return -1;
}
// seek to a sample offset relative to the decompressed pcm stream
// returns zero on success, nonzero on failure
public int pcm_seek(long pos){
int link=-1;
long total=pcm_total(-1);
if(!seekable)
return (-1); // don't dump machine if we can't seek
if(pos<0||pos>total){
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
}
// which bitstream section does this pcm offset occur in?
for(link=links-1; link>=0; link--){
total-=pcmlengths[link];
if(pos>=total)
break;
}
// search within the logical bitstream for the page with the highest
// pcm_pos preceeding (or equal to) pos. There is a danger here;
// missing pages or incorrect frame number information in the
// bitstream could make our task impossible. Account for that (it
// would be an error condition)
{
long target=pos-total;
long end=offsets[link+1];
long begin=offsets[link];
int best=(int)begin;
Page og=new Page();
while(begin=pos){
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
}
if(pos>pcm_total(-1)){
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
}
// discard samples until we reach the desired position. Crossing a
// logical bitstream boundary with abandon is OK.
while(pcm_offsettarget)
samples=target;
vd.synthesis_read(samples);
pcm_offset+=samples;
if(samplestime_total){
//goto seek_error;
pcm_offset=-1;
decode_clear();
return -1;
}
// which bitstream section does this time offset occur in?
for(link=links-1; link>=0; link--){
pcm_total-=pcmlengths[link];
time_total-=time_total(link);
if(seconds>=time_total)
break;
}
// enough information to convert time offset to pcm offset
{
long target=(long)(pcm_total+(seconds-time_total)*vi[link].rate);
return (pcm_seek(target));
}
//seek_error:
// dump machine so we're in a known state
//pcm_offset=-1;
//decode_clear();
//return -1;
}
// tell the current stream offset cursor. Note that seek followed by
// tell will likely not give the set offset due to caching
public long raw_tell(){
return (offset);
}
// return PCM offset (sample) of next PCM sample to be read
public long pcm_tell(){
return (pcm_offset);
}
// return time offset (seconds) of next PCM sample to be read
public float time_tell(){
// translate time to PCM position and call pcm_seek
int link=-1;
long pcm_total=0;
float time_total=0.f;
if(seekable){
pcm_total=pcm_total(-1);
time_total=time_total(-1);
// which bitstream section does this time offset occur in?
for(link=links-1; link>=0; link--){
pcm_total-=pcmlengths[link];
time_total-=time_total(link);
if(pcm_offset>=pcm_total)
break;
}
}
return ((float)time_total+(float)(pcm_offset-pcm_total)/vi[link].rate);
}
// link: -1) return the vorbis_info struct for the bitstream section
// currently being decoded
// 0-n) to request information for a specific bitstream section
//
// In the case of a non-seekable bitstream, any call returns the
// current bitstream. NULL in the case that the machine is not
// initialized
public Info getInfo(int link){
if(seekable){
if(link<0){
if(decode_ready){
return vi[current_link];
}
else{
return null;
}
}
else{
if(link>=links){
return null;
}
else{
return vi[link];
}
}
}
else{
if(decode_ready){
return vi[0];
}
else{
return null;
}
}
}
public Comment getComment(int link){
if(seekable){
if(link<0){
if(decode_ready){
return vc[current_link];
}
else{
return null;
}
}
else{
if(link>=links){
return null;
}
else{
return vc[link];
}
}
}
else{
if(decode_ready){
return vc[0];
}
else{
return null;
}
}
}
int host_is_big_endian(){
return 1;
// short pattern = 0xbabe;
// unsigned char *bytewise = (unsigned char *)&pattern;
// if (bytewise[0] == 0xba) return 1;
// assert(bytewise[0] == 0xbe);
// return 0;
}
// up to this point, everything could more or less hide the multiple
// logical bitstream nature of chaining from the toplevel application
// if the toplevel application didn't particularly care. However, at
// the point that we actually read audio back, the multiple-section
// nature must surface: Multiple bitstream sections do not necessarily
// have to have the same number of channels or sampling rate.
//
// read returns the sequential logical bitstream number currently
// being decoded along with the PCM data in order that the toplevel
// application can take action on channel/sample rate changes. This
// number will be incremented even for streamed (non-seekable) streams
// (for seekable streams, it represents the actual logical bitstream
// index within the physical bitstream. Note that the accessor
// functions above are aware of this dichotomy).
//
// input values: buffer) a buffer to hold packed PCM data for return
// length) the byte length requested to be placed into buffer
// bigendianp) should the data be packed LSB first (0) or
// MSB first (1)
// word) word size for output. currently 1 (byte) or
// 2 (16 bit short)
//
// return values: -1) error/hole in data
// 0) EOF
// n) number of bytes of PCM actually returned. The
// below works on a packet-by-packet basis, so the
// return length is not related to the 'length' passed
// in, just guaranteed to fit.
//
// *section) set to the logical bitstream number
int read(byte[] buffer, int length, int bigendianp, int word, int sgned,
int[] bitstream){
int host_endian=host_is_big_endian();
int index=0;
while(true){
if(decode_ready){
float[][] pcm;
float[][][] _pcm=new float[1][][];
int[] _index=new int[getInfo(-1).channels];
int samples=vd.synthesis_pcmout(_pcm, _index);
pcm=_pcm[0];
if(samples!=0){
// yay! proceed to pack data into the byte buffer
int channels=getInfo(-1).channels;
int bytespersample=word*channels;
if(samples>length/bytespersample)
samples=length/bytespersample;
// a tight loop to pack each size
{
int val;
if(word==1){
int off=(sgned!=0 ? 0 : 128);
for(int j=0; j127)
val=127;
else if(val<-128)
val=-128;
buffer[index++]=(byte)(val+off);
}
}
}
else{
int off=(sgned!=0 ? 0 : 32768);
if(host_endian==bigendianp){
if(sgned!=0){
for(int i=0; i32767)
val=32767;
else if(val<-32768)
val=-32768;
buffer[dest]=(byte)(val>>>8);
buffer[dest+1]=(byte)(val);
dest+=channels*2;
}
}
}
else{
for(int i=0; i32767)
val=32767;
else if(val<-32768)
val=-32768;
buffer[dest]=(byte)((val+off)>>>8);
buffer[dest+1]=(byte)(val+off);
dest+=channels*2;
}
}
}
}
else if(bigendianp!=0){
for(int j=0; j32767)
val=32767;
else if(val<-32768)
val=-32768;
val+=off;
buffer[index++]=(byte)(val>>>8);
buffer[index++]=(byte)val;
}
}
}
else{
//int val;
for(int j=0; j32767)
val=32767;
else if(val<-32768)
val=-32768;
val+=off;
buffer[index++]=(byte)val;
buffer[index++]=(byte)(val>>>8);
}
}
}
}
}
vd.synthesis_read(samples);
pcm_offset+=samples;
if(bitstream!=null)
bitstream[0]=current_link;
return (samples*bytespersample);
}
}
// suck in another packet
switch(process_packet(1)){
case 0:
return (0);
case -1:
return -1;
default:
break;
}
}
}
public Info[] getInfo(){
return vi;
}
public Comment[] getComment(){
return vc;
}
public void close() throws java.io.IOException{
datasource.close();
}
class SeekableInputStream extends InputStream{
java.io.RandomAccessFile raf=null;
final String mode="r";
SeekableInputStream(String file) throws java.io.IOException{
raf=new java.io.RandomAccessFile(file, mode);
}
public int read() throws java.io.IOException{
return raf.read();
}
public int read(byte[] buf) throws java.io.IOException{
return raf.read(buf);
}
public int read(byte[] buf, int s, int len) throws java.io.IOException{
return raf.read(buf, s, len);
}
public long skip(long n) throws java.io.IOException{
return (long)(raf.skipBytes((int)n));
}
public long getLength() throws java.io.IOException{
return raf.length();
}
public long tell() throws java.io.IOException{
return raf.getFilePointer();
}
public int available() throws java.io.IOException{
return (raf.length()==raf.getFilePointer()) ? 0 : 1;
}
public void close() throws java.io.IOException{
raf.close();
}
public synchronized void mark(int m){
}
public synchronized void reset() throws java.io.IOException{
}
public boolean markSupported(){
return false;
}
public void seek(long pos) throws java.io.IOException{
raf.seek(pos);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy