Commit 2d7db0df authored by Monty's avatar Monty
Browse files

Full 'vorbisfile.a' library commit.  The whole convenience API is
there; the vast majority of the code is untested.  chaining_example.c
does work, however.

Monty

svn path=/trunk/vorbis/; revision=160
parent 8e60b86f
......@@ -32,25 +32,24 @@ int main(){
}
/* print details about each logical bitstream in the input */
if(ov.seekable){
if(ov_seekable(&ov)){
printf("Input bitstream contained %d logical bitstream section(s).\n",
ov.links);
ov_streams(&ov));
printf("Total bitstream playing time: %ld seconds\n\n",
(long)ov_totaltime(&ov));
(long)ov_time_total(&ov,-1));
}else{
printf("Standard input was not seekable.\n"
"First logical bitstream information:\n\n");
}
for(i=0;i<ov.links;i++){
for(i=0;i<ov_streams(&ov);i++){
vorbis_info *vi=ov_info(&ov,i);
printf("\tlogical bitstream section %d information:\n",i+1);
printf("\t\t%ldHz %d channels serial number=%ld\n",
ov.vi[i].rate,ov.vi[i].channels,ov.serialnos[i]);
printf("\t\tcompressed length: %ldbytes ",(ov.offsets?
ov.offsets[i+1]-ov.offsets[i]:
-1));
printf(" play time: %lds\n",(long)ov_lbtime(&ov,i));
vi->rate,vi->channels,ov.serialnos[i]);
printf("\t\tcompressed length: %ldbytes ",ov_raw_total(&ov,i));
printf(" play time: %lds\n",(long)ov_time_total(&ov,i));
}
ov_clear(&ov);
......
......@@ -172,7 +172,7 @@ typedef struct {
int *lacing_vals; /* The values that will go to the segment table */
int64_t *pcm_vals; /* pcm_pos values for headers. Not compact
size64 *pcm_vals; /* pcm_pos values for headers. Not compact
this way, but it is simple coupled to the
lacing fifo */
long lacing_storage;
......@@ -194,7 +194,7 @@ typedef struct {
but we need coupling so that the codec
(which is in a seperate abstraction
layer) also knows about the gap */
int64_t pcmpos;
size64 pcmpos;
} ogg_stream_state;
......@@ -207,7 +207,7 @@ typedef struct {
long b_o_s;
long e_o_s;
int64_t frameno;
size64 frameno;
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
......@@ -346,7 +346,7 @@ extern int ogg_page_version(ogg_page *og);
extern int ogg_page_continued(ogg_page *og);
extern int ogg_page_bos(ogg_page *og);
extern int ogg_page_eos(ogg_page *og);
extern int64_t ogg_page_frameno(ogg_page *og);
extern size64 ogg_page_frameno(ogg_page *og);
extern int ogg_page_serialno(ogg_page *og);
extern int ogg_page_pageno(ogg_page *og);
......
......@@ -14,7 +14,7 @@
function: stdio-based convenience library for opening/seeking/decoding
author: Monty <xiphmont@mit.edu>
modifications by: Monty
last modification date: Nov 02 1999
last modification date: Nov 04 1999
********************************************************************/
......@@ -344,22 +344,134 @@ static int _open_seekable(OggVorbis_File *vf){
}
static int _open_nonseekable(OggVorbis_File *vf){
vorbis_info vi;
long serialno;
/* Try to fetch the headers, maintaining all the storage */
if(_fetch_headers(vf,&vi,&serialno)==-1)return(-1);
/* we cannot seek. Set up a 'single' (current) logical bitstream entry */
vf->links=1;
vf->vi=malloc(sizeof(vorbis_info));
vf->serialnos=malloc(sizeof(long));
memcpy(vf->vi,&vi,sizeof(vorbis_info));
vf->serialnos[0]=serialno;
/* Try to fetch the headers, maintaining all the storage */
if(_fetch_headers(vf,vf->vi,&vf->current_serialno)==-1)return(-1);
return 0;
}
/* clear out the current logical bitstream decoder */
static void _decode_clear(OggVorbis_File *vf){
ogg_stream_clear(&vf->os);
vorbis_dsp_clear(&vf->vd);
vorbis_block_clear(&vf->vb);
vf->pcm_offset=-1;
vf->decode_ready=0;
}
/* fetch and process a packet. Handles the case where we're at a
bitstream boundary and dumps the decoding machine. If the decoding
machine is unloaded, it loads it. It also keeps pcm_offset up to
date (seek and read both use this. seek uses a special hack with
readp).
return: -1) hole in the data (lost packet)
0) need more date (only if readp==0)/eof
1) got a packet
*/
static int _process_packet(OggVorbis_File *vf,int readp){
ogg_page og;
/* handle one packet. Try to fetch it from current stream state */
/* extract packets from page */
while(1){
/* process a packet if we can. If the machine isn't loaded,
neither is a page */
if(vf->decode_ready){
ogg_packet op;
int result=ogg_stream_packetout(&vf->os,&op);
size64 frameno;
if(result==-1)return(-1); /* hole in the data. alert the toplevel */
if(result>0){
/* got a packet. process it */
frameno=op.frameno;
vorbis_synthesis(&vf->vb,&op);
vorbis_synthesis_blockin(&vf->vd,&vf->vb);
/* update the pcm offset. */
if(frameno!=-1){
int link=(vf->seekable?vf->current_link:0);
double **dummy;
int i,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* */
samples=vorbis_synthesis_pcmout(&vf->vd,&dummy);
frameno-=samples;
for(i=0;i<link;i++)
frameno+=vf->pcmlengths[i];
vf->pcm_offset=frameno;
}
return(1);
}
}
if(!readp)return(0);
if(_get_next_page(vf,&og,-1)<0)return(0); /* eof. leave unitialized */
/* has our decoding just traversed a bitstream boundary? */
if(vf->decode_ready){
if(vf->current_serialno!=ogg_page_serialno(&og)){
_decode_clear(vf);
}
}
/* Do we need to load a new machine before submitting the page? */
/* This is different in the seekable and non-seekable cases.
In the seekable case, we already have all the header
information loaded and cached; we just initialize the machine
with it and continue on our merry way.
In the non-seekable (streaming) case, we'll only be at a
boundary if we just left the previous logical bitstream and
we're now nominally at the header of the next bitstream
*/
if(!vf->decode_ready){
int link;
if(vf->seekable){
vf->current_serialno=ogg_page_serialno(&og);
/* match the serialno to bitstream section. We use this rather than
offset positions to avoid problems near logical bitstream
boundaries */
for(link=0;link<vf->links;link++)
if(vf->serialnos[link]==vf->current_serialno)break;
if(link==vf->links)return(-1); /* sign of a bogus stream. error out,
leave machine uninitialized */
vf->current_link=link;
}else{
/* we're streaming */
/* fetch the three header packets, build the info struct */
_fetch_headers(vf,vf->vi,&vf->current_serialno);
vf->current_link++;
link=0;
}
/* reload */
ogg_stream_init(&vf->os,vf->current_serialno);
vorbis_synthesis_init(&vf->vd,vf->vi+link);
vorbis_block_init(&vf->vd,&vf->vb);
vf->decode_ready=1;
}
ogg_stream_pagein(&vf->os,&og);
}
}
/**********************************************************************
* The helpers are over; it's all toplevel interface from here on out */
......@@ -418,55 +530,407 @@ int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
if(ret){
vf->f=NULL;
ov_clear(vf);
}else{
ogg_stream_init(&vf->os,vf->current_serialno);
vorbis_synthesis_init(&vf->vd,vf->vi);
vorbis_block_init(&vf->vd,&vf->vb);
vf->decode_ready=1;
}
return(ret);
}
double ov_lbtime(OggVorbis_File *vf,int i){
if(i>=0 && i<vf->links)
return((float)(vf->pcmlengths[i])/vf->vi[i].rate);
return(0);
long ov_streams(OggVorbis_File *vf){
return vf->links;
}
double ov_totaltime(OggVorbis_File *vf){
double acc=0;
int i;
for(i=0;i<vf->links;i++)
acc+=ov_lbtime(vf,i);
return(acc);
long ov_seekable(OggVorbis_File *vf){
return vf->seekable;
}
/* seek to an offset relative to the *compressed* data */
int ov_seek_stream(OggVorbis_File *vf,long pos){
long ov_raw_total(OggVorbis_File *vf,int i){
if(!vf->seekable)return(-1);
if(i<0 || i>=vf->links){
long acc=0;
int i;
for(i=0;i<vf->links;i++)
acc+=ov_raw_total(vf,i);
return(acc);
}else{
return(vf->offsets[i+1]-vf->offsets[i]);
}
}
size64 ov_pcm_total(OggVorbis_File *vf,int i){
if(!vf->seekable)return(-1);
if(i<0 || i>=vf->links){
size64 acc=0;
int i;
for(i=0;i<vf->links;i++)
acc+=ov_pcm_total(vf,i);
return(acc);
}else{
return(vf->pcmlengths[i]);
}
}
double ov_time_total(OggVorbis_File *vf,int i){
if(!vf->seekable)return(-1);
if(i<0 || i>=vf->links){
double acc=0;
int i;
for(i=0;i<vf->links;i++)
acc+=ov_time_total(vf,i);
return(acc);
}else{
return((float)(vf->pcmlengths[i])/vf->vi[i].rate);
}
}
/* seek to an offset relative to the *compressed* data. This also
immediately sucks in and decodes pages to update the PCM cursor. It
will cross a logical bitstream boundary, but only if it can't get
any packets out of the tail of the bitstream we seek to (so no
surprises). */
int ov_raw_seek(OggVorbis_File *vf,long pos){
int link;
if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
if(pos<0 || pos>vf->offsets[vf->links])goto seek_error;
/* clear out decoding machine state */
_decode_clear(vf);
/* seek */
_seek_helper(vf,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 'frameno' field set, and that's how the
helper updates the offset */
switch(_process_packet(vf,1)){
case 0:
/* oh, eof. There are no packets remaining. Set the pcm offset to
the end of file */
vf->pcm_offset=ov_pcm_total(vf,-1);
return(0);
case -1:
/* error! missing data or invalid bitstream structure */
goto seek_error;
default:
/* all OK */
break;
}
while(1){
switch(_process_packet(vf,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;
default:
/* continue processing packets */
break;
}
}
seek_error:
/* dump the machine so we're in a known state */
_decode_clear(vf);
return -1;
}
/* seek to the beginning of the next logical bitstream within the
physical bitstream */
int ov_seek_bitstream(OggVorbis_File *vf,long pos){
/* seek to a sample offset relative to the decompressed pcm stream */
int ov_pcm_seek(OggVorbis_File *vf,size64 pos){
int i,link=-1;
size64 total=ov_pcm_total(vf,-1);
if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
if(pos<0 || pos>total)goto seek_error;
/* which bitstream section does this pcm offset occur in? */
for(link=vf->links-1;link>=0;link--){
total-=vf->pcmlengths[link];
if(pos>=total)break;
}
/* seach 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) */
{
size64 target=pos-total;
long end=vf->offsets[link+1];
long begin=vf->offsets[link];
long best=begin;
ogg_page og;
while(begin<end){
long bisect;
long ret,acc;
if(end-begin<CHUNKSIZE){
bisect=begin;
}else{
bisect=(end+begin)/2;
}
_seek_helper(vf,bisect);
acc=0;
while(1){
ret=_get_next_page(vf,&og,-1);
if(ret==-1){
end=bisect;
}else{
size64 frameno=ogg_page_frameno(&og);
acc+=ret;
if(frameno==-1)continue;
if(frameno<target){
best=bisect+acc; /* raw offset of packet with frameno */
begin=vf->offset; /* raw offset of next packet */
}else{
end=bisect;
}
}
break;
}
}
/* found our page. seek to it (call raw_seek). */
if(ov_raw_seek(vf,best))goto seek_error;
}
/* verify result */
if(vf->pcm_offset>=pos)goto seek_error;
if(pos>ov_pcm_total(vf,-1))goto seek_error;
/* discard samples until we reach the desired position. Crossing a
logical bitstream boundary with abandon is OK. */
while(vf->pcm_offset<pos){
double **pcm;
long target=pos-vf->pcm_offset;
long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
if(samples>target)samples=target;
vorbis_synthesis_read(&vf->vd,samples);
vf->pcm_offset+=samples;
if(samples<target)
if(_process_packet(vf,1)==0)
vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
}
return 0;
seek_error:
/* dump machine so we're in a known state */
_decode_clear(vf);
return -1;
}
/* seek to an offset relative to the decompressed *output* stream */
int ov_seek_pcm(OggVorbis_File *vf,long pos){
/* seek to a playback time relative to the decompressed pcm stream */
int ov_time_seek(OggVorbis_File *vf,double seconds){
/* translate time to PCM position and call ov_pcm_seek */
int i,link=-1;
size64 pcm_total=ov_pcm_total(vf,-1);
double time_total=ov_time_total(vf,-1);
if(!vf->seekable)return(-1); /* don't dump machine if we can't seek */
if(seconds<0 || seconds>time_total)goto seek_error;
/* which bitstream section does this time offset occur in? */
for(link=vf->links-1;link>=0;link--){
pcm_total-=vf->pcmlengths[link];
time_total-=ov_time_total(vf,link);
if(seconds>=time_total)break;
}
/* enough information to convert time offset to pcm offset */
{
size64 target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
return(ov_pcm_seek(vf,target));
}
seek_error:
/* dump machine so we're in a known state */
_decode_clear(vf);
return -1;
}
/* tell the current stream offset cursor. Note that seek followed by
tell will likely not give the set offset due to caching */
long ov_raw_tell(OggVorbis_File *vf){
return(vf->offset);
}
int ov_seek_time(OggVorbis_File *vf,double seconds){
size64 ov_pcm_tell(OggVorbis_File *vf){
return(vf->pcm_offset);
}
double ov_time_tell(OggVorbis_File *vf){
/* translate time to PCM position and call ov_pcm_seek */
int link=-1;
size64 pcm_total=0;
double time_total=0.;
if(vf->seekable){
pcm_total=ov_pcm_total(vf,-1);
time_total=ov_time_total(vf,-1);
/* which bitstream section does this time offset occur in? */
for(link=vf->links-1;link>=0;link--){
pcm_total-=vf->pcmlengths[link];
time_total-=ov_time_total(vf,link);
if(vf->pcm_offset>pcm_total)break;
}
}
return(time_total+(vf->pcm_offset-pcm_total)/vf->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 */
vorbis_info *ov_info(OggVorbis_File *vf,int link){
if(vf->seekable){
if(link<0)
if(vf->decode_ready)
return vf->vi+vf->current_link;
else
return NULL;
else
if(link>=vf->links)
return NULL;
else
return vf->vi+link;
}else{
if(vf->decode_ready)
return vf->vi;
else
return NULL;
}
}
/* 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.
ov_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 */
long ov_read(OggVorbis_File *vf,char *buffer,int length,
int bigendianp,int word,int sgned,int *bitstream){
int i,j;
while(1){
if(vf->decode_ready){
double **pcm;
long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
if(samples){
/* yay! proceed to pack data into the byte buffer */
long channels=ov_info(vf,-1)->channels;
long bytespersample=word * channels;
if(samples>length/bytespersample)samples=length/bytespersample;
/* a tight loop to pack each size */
{
if(word==1){
int off=(sgned?0:128);
for(j=0;j<samples;j++)
for(i=0;i<channels;i++){
int val=rint(pcm[i][j]*128.);
if(val>127)val=127;
if(val<-128)val=-128;
*buffer++=val+off;
}
}else{
int off=(sgned?0:32768);
if(bigendianp){
for(j=0;j<samples;j++)
for(i=0;i<channels;i++){
int val=rint(pcm[i][j]*32768.);
if(val>32767)val=32767;
if(val<-32768)val=-32768;
val+=off;
*buffer++=(val>>8);
*buffer++=(val&0xff);
}
}else{
for(j=0;j<samples;j++)
for(i=0;i<channels;i++){
int val=rint(pcm[i][j]*32768.);
if(val>32767)val=32767;
if(val<-32768)val=-32768;
val+=off;
*buffer++=(val&0xff);