Blame lib/vorbisfile.c

Packit 06404a
/********************************************************************
Packit 06404a
 *                                                                  *
Packit 06404a
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
Packit 06404a
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
Packit 06404a
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
Packit 06404a
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
Packit 06404a
 *                                                                  *
Packit 06404a
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015             *
Packit 06404a
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
Packit 06404a
 *                                                                  *
Packit 06404a
 ********************************************************************
Packit 06404a
Packit 06404a
 function: stdio-based convenience library for opening/seeking/decoding
Packit 06404a
 last mod: $Id: vorbisfile.c 19457 2015-03-03 00:15:29Z giles $
Packit 06404a
Packit 06404a
 ********************************************************************/
Packit 06404a
Packit 06404a
#include <stdlib.h>
Packit 06404a
#include <stdio.h>
Packit 06404a
#include <errno.h>
Packit 06404a
#include <string.h>
Packit 06404a
#include <math.h>
Packit 06404a
Packit 06404a
#include "vorbis/codec.h"
Packit 06404a
Packit 06404a
/* we don't need or want the static callback symbols here */
Packit 06404a
#define OV_EXCLUDE_STATIC_CALLBACKS
Packit 06404a
#include "vorbis/vorbisfile.h"
Packit 06404a
Packit 06404a
#include "os.h"
Packit 06404a
#include "misc.h"
Packit 06404a
Packit 06404a
/* A 'chained bitstream' is a Vorbis bitstream that contains more than
Packit 06404a
   one logical bitstream arranged end to end (the only form of Ogg
Packit 06404a
   multiplexing allowed in a Vorbis bitstream; grouping [parallel
Packit 06404a
   multiplexing] is not allowed in Vorbis) */
Packit 06404a
Packit 06404a
/* A Vorbis file can be played beginning to end (streamed) without
Packit 06404a
   worrying ahead of time about chaining (see decoder_example.c).  If
Packit 06404a
   we have the whole file, however, and want random access
Packit 06404a
   (seeking/scrubbing) or desire to know the total length/time of a
Packit 06404a
   file, we need to account for the possibility of chaining. */
Packit 06404a
Packit 06404a
/* We can handle things a number of ways; we can determine the entire
Packit 06404a
   bitstream structure right off the bat, or find pieces on demand.
Packit 06404a
   This example determines and caches structure for the entire
Packit 06404a
   bitstream, but builds a virtual decoder on the fly when moving
Packit 06404a
   between links in the chain. */
Packit 06404a
Packit 06404a
/* There are also different ways to implement seeking.  Enough
Packit 06404a
   information exists in an Ogg bitstream to seek to
Packit 06404a
   sample-granularity positions in the output.  Or, one can seek by
Packit 06404a
   picking some portion of the stream roughly in the desired area if
Packit 06404a
   we only want coarse navigation through the stream. */
Packit 06404a
Packit 06404a
/*************************************************************************
Packit 06404a
 * Many, many internal helpers.  The intention is not to be confusing;
Packit 06404a
 * rampant duplication and monolithic function implementation would be
Packit 06404a
 * harder to understand anyway.  The high level functions are last.  Begin
Packit 06404a
 * grokking near the end of the file */
Packit 06404a
Packit 06404a
/* read a little more data from the file/pipe into the ogg_sync framer
Packit 06404a
*/
Packit 06404a
#define CHUNKSIZE 65536 /* greater-than-page-size granularity seeking */
Packit 06404a
#define READSIZE 2048 /* a smaller read size is needed for low-rate streaming. */
Packit 06404a
Packit 06404a
static long _get_data(OggVorbis_File *vf){
Packit 06404a
  errno=0;
Packit 06404a
  if(!(vf->callbacks.read_func))return(-1);
Packit 06404a
  if(vf->datasource){
Packit 06404a
    char *buffer=ogg_sync_buffer(&vf->oy,READSIZE);
Packit 06404a
    long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource);
Packit 06404a
    if(bytes>0)ogg_sync_wrote(&vf->oy,bytes);
Packit 06404a
    if(bytes==0 && errno)return(-1);
Packit 06404a
    return(bytes);
Packit 06404a
  }else
Packit 06404a
    return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* save a tiny smidge of verbosity to make the code more readable */
Packit 06404a
static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
Packit 06404a
  if(vf->datasource){
Packit 06404a
    /* only seek if the file position isn't already there */
Packit 06404a
    if(vf->offset != offset){
Packit 06404a
      if(!(vf->callbacks.seek_func)||
Packit 06404a
         (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1)
Packit 06404a
        return OV_EREAD;
Packit 06404a
      vf->offset=offset;
Packit 06404a
      ogg_sync_reset(&vf->oy);
Packit 06404a
    }
Packit 06404a
  }else{
Packit 06404a
    /* shouldn't happen unless someone writes a broken callback */
Packit 06404a
    return OV_EFAULT;
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* The read/seek functions track absolute position within the stream */
Packit 06404a
Packit 06404a
/* from the head of the stream, get the next page.  boundary specifies
Packit 06404a
   if the function is allowed to fetch more data from the stream (and
Packit 06404a
   how much) or only use internally buffered data.
Packit 06404a
Packit 06404a
   boundary: -1) unbounded search
Packit 06404a
              0) read no additional data; use cached only
Packit 06404a
              n) search for a new page beginning for n bytes
Packit 06404a
Packit 06404a
   return:   <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
Packit 06404a
              n) found a page at absolute offset n */
Packit 06404a
Packit 06404a
static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og,
Packit 06404a
                                  ogg_int64_t boundary){
Packit 06404a
  if(boundary>0)boundary+=vf->offset;
Packit 06404a
  while(1){
Packit 06404a
    long more;
Packit 06404a
Packit 06404a
    if(boundary>0 && vf->offset>=boundary)return(OV_FALSE);
Packit 06404a
    more=ogg_sync_pageseek(&vf->oy,og);
Packit 06404a
Packit 06404a
    if(more<0){
Packit 06404a
      /* skipped n bytes */
Packit 06404a
      vf->offset-=more;
Packit 06404a
    }else{
Packit 06404a
      if(more==0){
Packit 06404a
        /* send more paramedics */
Packit 06404a
        if(!boundary)return(OV_FALSE);
Packit 06404a
        {
Packit 06404a
          long ret=_get_data(vf);
Packit 06404a
          if(ret==0)return(OV_EOF);
Packit 06404a
          if(ret<0)return(OV_EREAD);
Packit 06404a
        }
Packit 06404a
      }else{
Packit 06404a
        /* got a page.  Return the offset at the page beginning,
Packit 06404a
           advance the internal offset past the page end */
Packit 06404a
        ogg_int64_t ret=vf->offset;
Packit 06404a
        vf->offset+=more;
Packit 06404a
        return(ret);
Packit 06404a
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* find the latest page beginning before the passed in position. Much
Packit 06404a
   dirtier than the above as Ogg doesn't have any backward search
Packit 06404a
   linkage.  no 'readp' as it will certainly have to read. */
Packit 06404a
/* returns offset or OV_EREAD, OV_FAULT */
Packit 06404a
static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_int64_t begin,ogg_page *og){
Packit 06404a
  ogg_int64_t end = begin;
Packit 06404a
  ogg_int64_t ret;
Packit 06404a
  ogg_int64_t offset=-1;
Packit 06404a
Packit 06404a
  while(offset==-1){
Packit 06404a
    begin-=CHUNKSIZE;
Packit 06404a
    if(begin<0)
Packit 06404a
      begin=0;
Packit 06404a
Packit 06404a
    ret=_seek_helper(vf,begin);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
Packit 06404a
    while(vf->offset
Packit 06404a
      memset(og,0,sizeof(*og));
Packit 06404a
      ret=_get_next_page(vf,og,end-vf->offset);
Packit 06404a
      if(ret==OV_EREAD)return(OV_EREAD);
Packit 06404a
      if(ret<0){
Packit 06404a
        break;
Packit 06404a
      }else{
Packit 06404a
        offset=ret;
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* In a fully compliant, non-multiplexed stream, we'll still be
Packit 06404a
     holding the last page.  In multiplexed (or noncompliant streams),
Packit 06404a
     we will probably have to re-read the last page we saw */
Packit 06404a
  if(og->header_len==0){
Packit 06404a
    ret=_seek_helper(vf,offset);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
Packit 06404a
    ret=_get_next_page(vf,og,CHUNKSIZE);
Packit 06404a
    if(ret<0)
Packit 06404a
      /* this shouldn't be possible */
Packit 06404a
      return(OV_EFAULT);
Packit 06404a
  }
Packit 06404a
Packit 06404a
  return(offset);
Packit 06404a
}
Packit 06404a
Packit 06404a
static void _add_serialno(ogg_page *og,long **serialno_list, int *n){
Packit 06404a
  long s = ogg_page_serialno(og);
Packit 06404a
  (*n)++;
Packit 06404a
Packit 06404a
  if(*serialno_list){
Packit 06404a
    *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n));
Packit 06404a
  }else{
Packit 06404a
    *serialno_list = _ogg_malloc(sizeof(**serialno_list));
Packit 06404a
  }
Packit 06404a
Packit 06404a
  (*serialno_list)[(*n)-1] = s;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns nonzero if found */
Packit 06404a
static int _lookup_serialno(long s, long *serialno_list, int n){
Packit 06404a
  if(serialno_list){
Packit 06404a
    while(n--){
Packit 06404a
      if(*serialno_list == s) return 1;
Packit 06404a
      serialno_list++;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _lookup_page_serialno(ogg_page *og, long *serialno_list, int n){
Packit 06404a
  long s = ogg_page_serialno(og);
Packit 06404a
  return _lookup_serialno(s,serialno_list,n);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* performs the same search as _get_prev_page, but prefers pages of
Packit 06404a
   the specified serial number. If a page of the specified serialno is
Packit 06404a
   spotted during the seek-back-and-read-forward, it will return the
Packit 06404a
   info of last page of the matching serial number instead of the very
Packit 06404a
   last page.  If no page of the specified serialno is seen, it will
Packit 06404a
   return the info of last page and alter *serialno.  */
Packit 06404a
static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_int64_t begin,
Packit 06404a
                                         long *serial_list, int serial_n,
Packit 06404a
                                         int *serialno, ogg_int64_t *granpos){
Packit 06404a
  ogg_page og;
Packit 06404a
  ogg_int64_t end=begin;
Packit 06404a
  ogg_int64_t ret;
Packit 06404a
Packit 06404a
  ogg_int64_t prefoffset=-1;
Packit 06404a
  ogg_int64_t offset=-1;
Packit 06404a
  ogg_int64_t ret_serialno=-1;
Packit 06404a
  ogg_int64_t ret_gran=-1;
Packit 06404a
Packit 06404a
  while(offset==-1){
Packit 06404a
    begin-=CHUNKSIZE;
Packit 06404a
    if(begin<0)
Packit 06404a
      begin=0;
Packit 06404a
Packit 06404a
    ret=_seek_helper(vf,begin);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
Packit 06404a
    while(vf->offset
Packit 06404a
      ret=_get_next_page(vf,&og,end-vf->offset);
Packit 06404a
      if(ret==OV_EREAD)return(OV_EREAD);
Packit 06404a
      if(ret<0){
Packit 06404a
        break;
Packit 06404a
      }else{
Packit 06404a
        ret_serialno=ogg_page_serialno(&og);
Packit 06404a
        ret_gran=ogg_page_granulepos(&og);
Packit 06404a
        offset=ret;
Packit 06404a
Packit 06404a
        if(ret_serialno == *serialno){
Packit 06404a
          prefoffset=ret;
Packit 06404a
          *granpos=ret_gran;
Packit 06404a
        }
Packit 06404a
Packit 06404a
        if(!_lookup_serialno(ret_serialno,serial_list,serial_n)){
Packit 06404a
          /* we fell off the end of the link, which means we seeked
Packit 06404a
             back too far and shouldn't have been looking in that link
Packit 06404a
             to begin with.  If we found the preferred serial number,
Packit 06404a
             forget that we saw it. */
Packit 06404a
          prefoffset=-1;
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* we're not interested in the page... just the serialno and granpos. */
Packit 06404a
  if(prefoffset>=0)return(prefoffset);
Packit 06404a
Packit 06404a
  *serialno = ret_serialno;
Packit 06404a
  *granpos = ret_gran;
Packit 06404a
  return(offset);
Packit 06404a
Packit 06404a
}
Packit 06404a
Packit 06404a
/* uses the local ogg_stream storage in vf; this is important for
Packit 06404a
   non-streaming input sources */
Packit 06404a
static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
Packit 06404a
                          long **serialno_list, int *serialno_n,
Packit 06404a
                          ogg_page *og_ptr){
Packit 06404a
  ogg_page og;
Packit 06404a
  ogg_packet op;
Packit 06404a
  int i,ret;
Packit 06404a
  int allbos=0;
Packit 06404a
Packit 06404a
  if(!og_ptr){
Packit 06404a
    ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
Packit 06404a
    if(llret==OV_EREAD)return(OV_EREAD);
Packit 06404a
    if(llret<0)return(OV_ENOTVORBIS);
Packit 06404a
    og_ptr=&og;
Packit 06404a
  }
Packit 06404a
Packit 06404a
  vorbis_info_init(vi);
Packit 06404a
  vorbis_comment_init(vc);
Packit 06404a
  vf->ready_state=OPENED;
Packit 06404a
Packit 06404a
  /* extract the serialnos of all BOS pages + the first set of vorbis
Packit 06404a
     headers we see in the link */
Packit 06404a
Packit 06404a
  while(ogg_page_bos(og_ptr)){
Packit 06404a
    if(serialno_list){
Packit 06404a
      if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){
Packit 06404a
        /* a dupe serialnumber in an initial header packet set == invalid stream */
Packit 06404a
        if(*serialno_list)_ogg_free(*serialno_list);
Packit 06404a
        *serialno_list=0;
Packit 06404a
        *serialno_n=0;
Packit 06404a
        ret=OV_EBADHEADER;
Packit 06404a
        goto bail_header;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      _add_serialno(og_ptr,serialno_list,serialno_n);
Packit 06404a
    }
Packit 06404a
Packit 06404a
    if(vf->ready_state
Packit 06404a
      /* we don't have a vorbis stream in this link yet, so begin
Packit 06404a
         prospective stream setup. We need a stream to get packets */
Packit 06404a
      ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr));
Packit 06404a
      ogg_stream_pagein(&vf->os,og_ptr);
Packit 06404a
Packit 06404a
      if(ogg_stream_packetout(&vf->os,&op) > 0 &&
Packit 06404a
         vorbis_synthesis_idheader(&op)){
Packit 06404a
        /* vorbis header; continue setup */
Packit 06404a
        vf->ready_state=STREAMSET;
Packit 06404a
        if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
Packit 06404a
          ret=OV_EBADHEADER;
Packit 06404a
          goto bail_header;
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* get next page */
Packit 06404a
    {
Packit 06404a
      ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE);
Packit 06404a
      if(llret==OV_EREAD){
Packit 06404a
        ret=OV_EREAD;
Packit 06404a
        goto bail_header;
Packit 06404a
      }
Packit 06404a
      if(llret<0){
Packit 06404a
        ret=OV_ENOTVORBIS;
Packit 06404a
        goto bail_header;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      /* if this page also belongs to our vorbis stream, submit it and break */
Packit 06404a
      if(vf->ready_state==STREAMSET &&
Packit 06404a
         vf->os.serialno == ogg_page_serialno(og_ptr)){
Packit 06404a
        ogg_stream_pagein(&vf->os,og_ptr);
Packit 06404a
        break;
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  if(vf->ready_state!=STREAMSET){
Packit 06404a
    ret = OV_ENOTVORBIS;
Packit 06404a
    goto bail_header;
Packit 06404a
  }
Packit 06404a
Packit 06404a
  while(1){
Packit 06404a
Packit 06404a
    i=0;
Packit 06404a
    while(i<2){ /* get a page loop */
Packit 06404a
Packit 06404a
      while(i<2){ /* get a packet loop */
Packit 06404a
Packit 06404a
        int result=ogg_stream_packetout(&vf->os,&op);
Packit 06404a
        if(result==0)break;
Packit 06404a
        if(result==-1){
Packit 06404a
          ret=OV_EBADHEADER;
Packit 06404a
          goto bail_header;
Packit 06404a
        }
Packit 06404a
Packit 06404a
        if((ret=vorbis_synthesis_headerin(vi,vc,&op)))
Packit 06404a
          goto bail_header;
Packit 06404a
Packit 06404a
        i++;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      while(i<2){
Packit 06404a
        if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){
Packit 06404a
          ret=OV_EBADHEADER;
Packit 06404a
          goto bail_header;
Packit 06404a
        }
Packit 06404a
Packit 06404a
        /* if this page belongs to the correct stream, go parse it */
Packit 06404a
        if(vf->os.serialno == ogg_page_serialno(og_ptr)){
Packit 06404a
          ogg_stream_pagein(&vf->os,og_ptr);
Packit 06404a
          break;
Packit 06404a
        }
Packit 06404a
Packit 06404a
        /* if we never see the final vorbis headers before the link
Packit 06404a
           ends, abort */
Packit 06404a
        if(ogg_page_bos(og_ptr)){
Packit 06404a
          if(allbos){
Packit 06404a
            ret = OV_EBADHEADER;
Packit 06404a
            goto bail_header;
Packit 06404a
          }else
Packit 06404a
            allbos=1;
Packit 06404a
        }
Packit 06404a
Packit 06404a
        /* otherwise, keep looking */
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    return 0;
Packit 06404a
  }
Packit 06404a
Packit 06404a
 bail_header:
Packit 06404a
  vorbis_info_clear(vi);
Packit 06404a
  vorbis_comment_clear(vc);
Packit 06404a
  vf->ready_state=OPENED;
Packit 06404a
Packit 06404a
  return ret;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* Starting from current cursor position, get initial PCM offset of
Packit 06404a
   next page.  Consumes the page in the process without decoding
Packit 06404a
   audio, however this is only called during stream parsing upon
Packit 06404a
   seekable open. */
Packit 06404a
static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){
Packit 06404a
  ogg_page    og;
Packit 06404a
  ogg_int64_t accumulated=0;
Packit 06404a
  long        lastblock=-1;
Packit 06404a
  int         result;
Packit 06404a
  int         serialno = vf->os.serialno;
Packit 06404a
Packit 06404a
  while(1){
Packit 06404a
    ogg_packet op;
Packit 06404a
    if(_get_next_page(vf,&og,-1)<0)
Packit 06404a
      break; /* should not be possible unless the file is truncated/mangled */
Packit 06404a
Packit 06404a
    if(ogg_page_bos(&og)) break;
Packit 06404a
    if(ogg_page_serialno(&og)!=serialno) continue;
Packit 06404a
Packit 06404a
    /* count blocksizes of all frames in the page */
Packit 06404a
    ogg_stream_pagein(&vf->os,&og);
Packit 06404a
    while((result=ogg_stream_packetout(&vf->os,&op))){
Packit 06404a
      if(result>0){ /* ignore holes */
Packit 06404a
        long thisblock=vorbis_packet_blocksize(vi,&op);
Packit 06404a
        if(thisblock>=0){
Packit 06404a
          if(lastblock!=-1)
Packit 06404a
            accumulated+=(lastblock+thisblock)>>2;
Packit 06404a
          lastblock=thisblock;
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    if(ogg_page_granulepos(&og)!=-1){
Packit 06404a
      /* pcm offset of last packet on the first audio page */
Packit 06404a
      accumulated= ogg_page_granulepos(&og)-accumulated;
Packit 06404a
      break;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* less than zero?  Either a corrupt file or a stream with samples
Packit 06404a
     trimmed off the beginning, a normal occurrence; in both cases set
Packit 06404a
     the offset to zero */
Packit 06404a
  if(accumulated<0)accumulated=0;
Packit 06404a
Packit 06404a
  return accumulated;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* finds each bitstream link one at a time using a bisection search
Packit 06404a
   (has to begin by knowing the offset of the lb's initial page).
Packit 06404a
   Recurses for each link so it can alloc the link storage after
Packit 06404a
   finding them all, then unroll and fill the cache at the same time */
Packit 06404a
static int _bisect_forward_serialno(OggVorbis_File *vf,
Packit 06404a
                                    ogg_int64_t begin,
Packit 06404a
                                    ogg_int64_t searched,
Packit 06404a
                                    ogg_int64_t end,
Packit 06404a
                                    ogg_int64_t endgran,
Packit 06404a
                                    int endserial,
Packit 06404a
                                    long *currentno_list,
Packit 06404a
                                    int  currentnos,
Packit 06404a
                                    long m){
Packit 06404a
  ogg_int64_t pcmoffset;
Packit 06404a
  ogg_int64_t dataoffset=searched;
Packit 06404a
  ogg_int64_t endsearched=end;
Packit 06404a
  ogg_int64_t next=end;
Packit 06404a
  ogg_int64_t searchgran=-1;
Packit 06404a
  ogg_page og;
Packit 06404a
  ogg_int64_t ret,last;
Packit 06404a
  int serialno = vf->os.serialno;
Packit 06404a
Packit 06404a
  /* invariants:
Packit 06404a
     we have the headers and serialnos for the link beginning at 'begin'
Packit 06404a
     we have the offset and granpos of the last page in the file (potentially
Packit 06404a
       not a page we care about)
Packit 06404a
  */
Packit 06404a
Packit 06404a
  /* Is the last page in our list of current serialnumbers? */
Packit 06404a
  if(_lookup_serialno(endserial,currentno_list,currentnos)){
Packit 06404a
Packit 06404a
    /* last page is in the starting serialno list, so we've bisected
Packit 06404a
       down to (or just started with) a single link.  Now we need to
Packit 06404a
       find the last vorbis page belonging to the first vorbis stream
Packit 06404a
       for this link. */
Packit 06404a
    searched = end;
Packit 06404a
    while(endserial != serialno){
Packit 06404a
      endserial = serialno;
Packit 06404a
      searched=_get_prev_page_serial(vf,searched,currentno_list,currentnos,&endserial,&endgran);
Packit 06404a
    }
Packit 06404a
Packit 06404a
    vf->links=m+1;
Packit 06404a
    if(vf->offsets)_ogg_free(vf->offsets);
Packit 06404a
    if(vf->serialnos)_ogg_free(vf->serialnos);
Packit 06404a
    if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
Packit 06404a
Packit 06404a
    vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
Packit 06404a
    vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
Packit 06404a
    vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
Packit 06404a
    vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
Packit 06404a
    vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
Packit 06404a
    vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
Packit 06404a
Packit 06404a
    vf->offsets[m+1]=end;
Packit 06404a
    vf->offsets[m]=begin;
Packit 06404a
    vf->pcmlengths[m*2+1]=(endgran<0?0:endgran);
Packit 06404a
Packit 06404a
  }else{
Packit 06404a
Packit 06404a
    /* last page is not in the starting stream's serial number list,
Packit 06404a
       so we have multiple links.  Find where the stream that begins
Packit 06404a
       our bisection ends. */
Packit 06404a
Packit 06404a
    long *next_serialno_list=NULL;
Packit 06404a
    int next_serialnos=0;
Packit 06404a
    vorbis_info vi;
Packit 06404a
    vorbis_comment vc;
Packit 06404a
    int testserial = serialno+1;
Packit 06404a
Packit 06404a
    /* the below guards against garbage seperating the last and
Packit 06404a
       first pages of two links. */
Packit 06404a
    while(searched
Packit 06404a
      ogg_int64_t bisect;
Packit 06404a
Packit 06404a
      if(endsearched-searched
Packit 06404a
        bisect=searched;
Packit 06404a
      }else{
Packit 06404a
        bisect=(searched+endsearched)/2;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      ret=_seek_helper(vf,bisect);
Packit 06404a
      if(ret)return(ret);
Packit 06404a
Packit 06404a
      last=_get_next_page(vf,&og,-1);
Packit 06404a
      if(last==OV_EREAD)return(OV_EREAD);
Packit 06404a
      if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){
Packit 06404a
        endsearched=bisect;
Packit 06404a
        if(last>=0)next=last;
Packit 06404a
      }else{
Packit 06404a
        searched=vf->offset;
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* Bisection point found */
Packit 06404a
    /* for the time being, fetch end PCM offset the simple way */
Packit 06404a
    searched = next;
Packit 06404a
    while(testserial != serialno){
Packit 06404a
      testserial = serialno;
Packit 06404a
      searched = _get_prev_page_serial(vf,searched,currentno_list,currentnos,&testserial,&searchgran);
Packit 06404a
    }
Packit 06404a
Packit 06404a
    ret=_seek_helper(vf,next);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
Packit 06404a
    ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
    serialno = vf->os.serialno;
Packit 06404a
    dataoffset = vf->offset;
Packit 06404a
Packit 06404a
    /* this will consume a page, however the next bisection always
Packit 06404a
       starts with a raw seek */
Packit 06404a
    pcmoffset = _initial_pcmoffset(vf,&vi;;
Packit 06404a
Packit 06404a
    ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial,
Packit 06404a
                                 next_serialno_list,next_serialnos,m+1);
Packit 06404a
    if(ret)return(ret);
Packit 06404a
Packit 06404a
    if(next_serialno_list)_ogg_free(next_serialno_list);
Packit 06404a
Packit 06404a
    vf->offsets[m+1]=next;
Packit 06404a
    vf->serialnos[m+1]=serialno;
Packit 06404a
    vf->dataoffsets[m+1]=dataoffset;
Packit 06404a
Packit 06404a
    vf->vi[m+1]=vi;
Packit 06404a
    vf->vc[m+1]=vc;
Packit 06404a
Packit 06404a
    vf->pcmlengths[m*2+1]=searchgran;
Packit 06404a
    vf->pcmlengths[m*2+2]=pcmoffset;
Packit 06404a
    vf->pcmlengths[m*2+3]-=pcmoffset;
Packit 06404a
    if(vf->pcmlengths[m*2+3]<0)vf->pcmlengths[m*2+3]=0;
Packit 06404a
  }
Packit 06404a
  return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _make_decode_ready(OggVorbis_File *vf){
Packit 06404a
  if(vf->ready_state>STREAMSET)return 0;
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(vf->seekable){
Packit 06404a
    if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link))
Packit 06404a
      return OV_EBADLINK;
Packit 06404a
  }else{
Packit 06404a
    if(vorbis_synthesis_init(&vf->vd,vf->vi))
Packit 06404a
      return OV_EBADLINK;
Packit 06404a
  }
Packit 06404a
  vorbis_block_init(&vf->vd,&vf->vb);
Packit 06404a
  vf->ready_state=INITSET;
Packit 06404a
  vf->bittrack=0.f;
Packit 06404a
  vf->samptrack=0.f;
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _open_seekable2(OggVorbis_File *vf){
Packit 06404a
  ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1;
Packit 06404a
  int endserial=vf->os.serialno;
Packit 06404a
  int serialno=vf->os.serialno;
Packit 06404a
Packit 06404a
  /* we're partially open and have a first link header state in
Packit 06404a
     storage in vf */
Packit 06404a
Packit 06404a
  /* fetch initial PCM offset */
Packit 06404a
  ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi);
Packit 06404a
Packit 06404a
  /* we can seek, so set out learning all about this file */
Packit 06404a
  if(vf->callbacks.seek_func && vf->callbacks.tell_func){
Packit 06404a
    (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
Packit 06404a
    vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
Packit 06404a
  }else{
Packit 06404a
    vf->offset=vf->end=-1;
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* If seek_func is implemented, tell_func must also be implemented */
Packit 06404a
  if(vf->end==-1) return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* Get the offset of the last page of the physical bitstream, or, if
Packit 06404a
     we're lucky the last vorbis page of this link as most OggVorbis
Packit 06404a
     files will contain a single logical bitstream */
Packit 06404a
  end=_get_prev_page_serial(vf,vf->end,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran);
Packit 06404a
  if(end<0)return(end);
Packit 06404a
Packit 06404a
  /* now determine bitstream structure recursively */
Packit 06404a
  if(_bisect_forward_serialno(vf,0,dataoffset,end,endgran,endserial,
Packit 06404a
                              vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD);
Packit 06404a
Packit 06404a
  vf->offsets[0]=0;
Packit 06404a
  vf->serialnos[0]=serialno;
Packit 06404a
  vf->dataoffsets[0]=dataoffset;
Packit 06404a
  vf->pcmlengths[0]=pcmoffset;
Packit 06404a
  vf->pcmlengths[1]-=pcmoffset;
Packit 06404a
  if(vf->pcmlengths[1]<0)vf->pcmlengths[1]=0;
Packit 06404a
Packit 06404a
  return(ov_raw_seek(vf,dataoffset));
Packit 06404a
}
Packit 06404a
Packit 06404a
/* clear out the current logical bitstream decoder */
Packit 06404a
static void _decode_clear(OggVorbis_File *vf){
Packit 06404a
  vorbis_dsp_clear(&vf->vd);
Packit 06404a
  vorbis_block_clear(&vf->vb);
Packit 06404a
  vf->ready_state=OPENED;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* fetch and process a packet.  Handles the case where we're at a
Packit 06404a
   bitstream boundary and dumps the decoding machine.  If the decoding
Packit 06404a
   machine is unloaded, it loads it.  It also keeps pcm_offset up to
Packit 06404a
   date (seek and read both use this.  seek uses a special hack with
Packit 06404a
   readp).
Packit 06404a
Packit 06404a
   return: <0) error, OV_HOLE (lost packet) or OV_EOF
Packit 06404a
            0) need more data (only if readp==0)
Packit 06404a
            1) got a packet
Packit 06404a
*/
Packit 06404a
Packit 06404a
static int _fetch_and_process_packet(OggVorbis_File *vf,
Packit 06404a
                                     ogg_packet *op_in,
Packit 06404a
                                     int readp,
Packit 06404a
                                     int spanp){
Packit 06404a
  ogg_page og;
Packit 06404a
Packit 06404a
  /* handle one packet.  Try to fetch it from current stream state */
Packit 06404a
  /* extract packets from page */
Packit 06404a
  while(1){
Packit 06404a
Packit 06404a
    if(vf->ready_state==STREAMSET){
Packit 06404a
      int ret=_make_decode_ready(vf);
Packit 06404a
      if(ret<0)return ret;
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* process a packet if we can. */
Packit 06404a
Packit 06404a
    if(vf->ready_state==INITSET){
Packit 06404a
      int hs=vorbis_synthesis_halfrate_p(vf->vi);
Packit 06404a
Packit 06404a
      while(1) {
Packit 06404a
              ogg_packet op;
Packit 06404a
              ogg_packet *op_ptr=(op_in?op_in:&op);
Packit 06404a
        int result=ogg_stream_packetout(&vf->os,op_ptr);
Packit 06404a
        ogg_int64_t granulepos;
Packit 06404a
Packit 06404a
        op_in=NULL;
Packit 06404a
        if(result==-1)return(OV_HOLE); /* hole in the data. */
Packit 06404a
        if(result>0){
Packit 06404a
          /* got a packet.  process it */
Packit 06404a
          granulepos=op_ptr->granulepos;
Packit 06404a
          if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy
Packit 06404a
                                                    header handling.  The
Packit 06404a
                                                    header packets aren't
Packit 06404a
                                                    audio, so if/when we
Packit 06404a
                                                    submit them,
Packit 06404a
                                                    vorbis_synthesis will
Packit 06404a
                                                    reject them */
Packit 06404a
Packit 06404a
            /* suck in the synthesis data and track bitrate */
Packit 06404a
            {
Packit 06404a
              int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL);
Packit 06404a
              /* for proper use of libvorbis within libvorbisfile,
Packit 06404a
                 oldsamples will always be zero. */
Packit 06404a
              if(oldsamples)return(OV_EFAULT);
Packit 06404a
Packit 06404a
              vorbis_synthesis_blockin(&vf->vd,&vf->vb);
Packit 06404a
              vf->samptrack+=(vorbis_synthesis_pcmout(&vf->vd,NULL)<
Packit 06404a
              vf->bittrack+=op_ptr->bytes*8;
Packit 06404a
            }
Packit 06404a
Packit 06404a
            /* update the pcm offset. */
Packit 06404a
            if(granulepos!=-1 && !op_ptr->e_o_s){
Packit 06404a
              int link=(vf->seekable?vf->current_link:0);
Packit 06404a
              int i,samples;
Packit 06404a
Packit 06404a
              /* this packet has a pcm_offset on it (the last packet
Packit 06404a
                 completed on a page carries the offset) After processing
Packit 06404a
                 (above), we know the pcm position of the *last* sample
Packit 06404a
                 ready to be returned. Find the offset of the *first*
Packit 06404a
Packit 06404a
                 As an aside, this trick is inaccurate if we begin
Packit 06404a
                 reading anew right at the last page; the end-of-stream
Packit 06404a
                 granulepos declares the last frame in the stream, and the
Packit 06404a
                 last packet of the last page may be a partial frame.
Packit 06404a
                 So, we need a previous granulepos from an in-sequence page
Packit 06404a
                 to have a reference point.  Thus the !op_ptr->e_o_s clause
Packit 06404a
                 above */
Packit 06404a
Packit 06404a
              if(vf->seekable && link>0)
Packit 06404a
                granulepos-=vf->pcmlengths[link*2];
Packit 06404a
              if(granulepos<0)granulepos=0; /* actually, this
Packit 06404a
                                               shouldn't be possible
Packit 06404a
                                               here unless the stream
Packit 06404a
                                               is very broken */
Packit 06404a
Packit 06404a
              samples=(vorbis_synthesis_pcmout(&vf->vd,NULL)<
Packit 06404a
Packit 06404a
              granulepos-=samples;
Packit 06404a
              for(i=0;i
Packit 06404a
                granulepos+=vf->pcmlengths[i*2+1];
Packit 06404a
              vf->pcm_offset=granulepos;
Packit 06404a
            }
Packit 06404a
            return(1);
Packit 06404a
          }
Packit 06404a
        }
Packit 06404a
        else
Packit 06404a
          break;
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    if(vf->ready_state>=OPENED){
Packit 06404a
      ogg_int64_t ret;
Packit 06404a
Packit 06404a
      while(1){
Packit 06404a
        /* the loop is not strictly necessary, but there's no sense in
Packit 06404a
           doing the extra checks of the larger loop for the common
Packit 06404a
           case in a multiplexed bistream where the page is simply
Packit 06404a
           part of a different logical bitstream; keep reading until
Packit 06404a
           we get one with the correct serialno */
Packit 06404a
Packit 06404a
        if(!readp)return(0);
Packit 06404a
        if((ret=_get_next_page(vf,&og,-1))<0){
Packit 06404a
          return(OV_EOF); /* eof. leave unitialized */
Packit 06404a
        }
Packit 06404a
Packit 06404a
        /* bitrate tracking; add the header's bytes here, the body bytes
Packit 06404a
           are done by packet above */
Packit 06404a
        vf->bittrack+=og.header_len*8;
Packit 06404a
Packit 06404a
        if(vf->ready_state==INITSET){
Packit 06404a
          if(vf->current_serialno!=ogg_page_serialno(&og)){
Packit 06404a
Packit 06404a
            /* two possibilities:
Packit 06404a
               1) our decoding just traversed a bitstream boundary
Packit 06404a
               2) another stream is multiplexed into this logical section */
Packit 06404a
Packit 06404a
            if(ogg_page_bos(&og)){
Packit 06404a
              /* boundary case */
Packit 06404a
              if(!spanp)
Packit 06404a
                return(OV_EOF);
Packit 06404a
Packit 06404a
              _decode_clear(vf);
Packit 06404a
Packit 06404a
              if(!vf->seekable){
Packit 06404a
                vorbis_info_clear(vf->vi);
Packit 06404a
                vorbis_comment_clear(vf->vc);
Packit 06404a
              }
Packit 06404a
              break;
Packit 06404a
Packit 06404a
            }else
Packit 06404a
              continue; /* possibility #2 */
Packit 06404a
          }
Packit 06404a
        }
Packit 06404a
Packit 06404a
        break;
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* Do we need to load a new machine before submitting the page? */
Packit 06404a
    /* This is different in the seekable and non-seekable cases.
Packit 06404a
Packit 06404a
       In the seekable case, we already have all the header
Packit 06404a
       information loaded and cached; we just initialize the machine
Packit 06404a
       with it and continue on our merry way.
Packit 06404a
Packit 06404a
       In the non-seekable (streaming) case, we'll only be at a
Packit 06404a
       boundary if we just left the previous logical bitstream and
Packit 06404a
       we're now nominally at the header of the next bitstream
Packit 06404a
    */
Packit 06404a
Packit 06404a
    if(vf->ready_state!=INITSET){
Packit 06404a
      int link;
Packit 06404a
Packit 06404a
      if(vf->ready_state
Packit 06404a
        if(vf->seekable){
Packit 06404a
          long serialno = ogg_page_serialno(&og);
Packit 06404a
Packit 06404a
          /* match the serialno to bitstream section.  We use this rather than
Packit 06404a
             offset positions to avoid problems near logical bitstream
Packit 06404a
             boundaries */
Packit 06404a
Packit 06404a
          for(link=0;link<vf->links;link++)
Packit 06404a
            if(vf->serialnos[link]==serialno)break;
Packit 06404a
Packit 06404a
          if(link==vf->links) continue; /* not the desired Vorbis
Packit 06404a
                                           bitstream section; keep
Packit 06404a
                                           trying */
Packit 06404a
Packit 06404a
          vf->current_serialno=serialno;
Packit 06404a
          vf->current_link=link;
Packit 06404a
Packit 06404a
          ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
Packit 06404a
          vf->ready_state=STREAMSET;
Packit 06404a
Packit 06404a
        }else{
Packit 06404a
          /* we're streaming */
Packit 06404a
          /* fetch the three header packets, build the info struct */
Packit 06404a
Packit 06404a
          int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og);
Packit 06404a
          if(ret)return(ret);
Packit 06404a
          vf->current_serialno=vf->os.serialno;
Packit 06404a
          vf->current_link++;
Packit 06404a
          link=0;
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* the buffered page is the data we want, and we're ready for it;
Packit 06404a
       add it to the stream state */
Packit 06404a
    ogg_stream_pagein(&vf->os,&og);
Packit 06404a
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* if, eg, 64 bit stdio is configured by default, this will build with
Packit 06404a
   fseek64 */
Packit 06404a
static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
Packit 06404a
  if(f==NULL)return(-1);
Packit 06404a
  return fseek(f,off,whence);
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial,
Packit 06404a
                     long ibytes, ov_callbacks callbacks){
Packit 06404a
  int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1);
Packit 06404a
  long *serialno_list=NULL;
Packit 06404a
  int serialno_list_size=0;
Packit 06404a
  int ret;
Packit 06404a
Packit 06404a
  memset(vf,0,sizeof(*vf));
Packit 06404a
  vf->datasource=f;
Packit 06404a
  vf->callbacks = callbacks;
Packit 06404a
Packit 06404a
  /* init the framing state */
Packit 06404a
  ogg_sync_init(&vf->oy);
Packit 06404a
Packit 06404a
  /* perhaps some data was previously read into a buffer for testing
Packit 06404a
     against other stream types.  Allow initialization from this
Packit 06404a
     previously read data (especially as we may be reading from a
Packit 06404a
     non-seekable stream) */
Packit 06404a
  if(initial){
Packit 06404a
    char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
Packit 06404a
    memcpy(buffer,initial,ibytes);
Packit 06404a
    ogg_sync_wrote(&vf->oy,ibytes);
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* can we seek? Stevens suggests the seek test was portable */
Packit 06404a
  if(offsettest!=-1)vf->seekable=1;
Packit 06404a
Packit 06404a
  /* No seeking yet; Set up a 'single' (current) logical bitstream
Packit 06404a
     entry for partial open */
Packit 06404a
  vf->links=1;
Packit 06404a
  vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi));
Packit 06404a
  vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc));
Packit 06404a
  ogg_stream_init(&vf->os,-1); /* fill in the serialno later */
Packit 06404a
Packit 06404a
  /* Fetch all BOS pages, store the vorbis header and all seen serial
Packit 06404a
     numbers, load subsequent vorbis setup headers */
Packit 06404a
  if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){
Packit 06404a
    vf->datasource=NULL;
Packit 06404a
    ov_clear(vf);
Packit 06404a
  }else{
Packit 06404a
    /* serial number list for first link needs to be held somewhere
Packit 06404a
       for second stage of seekable stream open; this saves having to
Packit 06404a
       seek/reread first link's serialnumber data then. */
Packit 06404a
    vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos));
Packit 06404a
    vf->serialnos[0]=vf->current_serialno=vf->os.serialno;
Packit 06404a
    vf->serialnos[1]=serialno_list_size;
Packit 06404a
    memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos));
Packit 06404a
Packit 06404a
    vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets));
Packit 06404a
    vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets));
Packit 06404a
    vf->offsets[0]=0;
Packit 06404a
    vf->dataoffsets[0]=vf->offset;
Packit 06404a
Packit 06404a
    vf->ready_state=PARTOPEN;
Packit 06404a
  }
Packit 06404a
  if(serialno_list)_ogg_free(serialno_list);
Packit 06404a
  return(ret);
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _ov_open2(OggVorbis_File *vf){
Packit 06404a
  if(vf->ready_state != PARTOPEN) return OV_EINVAL;
Packit 06404a
  vf->ready_state=OPENED;
Packit 06404a
  if(vf->seekable){
Packit 06404a
    int ret=_open_seekable2(vf);
Packit 06404a
    if(ret){
Packit 06404a
      vf->datasource=NULL;
Packit 06404a
      ov_clear(vf);
Packit 06404a
    }
Packit 06404a
    return(ret);
Packit 06404a
  }else
Packit 06404a
    vf->ready_state=STREAMSET;
Packit 06404a
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
Packit 06404a
/* clear out the OggVorbis_File struct */
Packit 06404a
int ov_clear(OggVorbis_File *vf){
Packit 06404a
  if(vf){
Packit 06404a
    vorbis_block_clear(&vf->vb);
Packit 06404a
    vorbis_dsp_clear(&vf->vd);
Packit 06404a
    ogg_stream_clear(&vf->os);
Packit 06404a
Packit 06404a
    if(vf->vi && vf->links){
Packit 06404a
      int i;
Packit 06404a
      for(i=0;i<vf->links;i++){
Packit 06404a
        vorbis_info_clear(vf->vi+i);
Packit 06404a
        vorbis_comment_clear(vf->vc+i);
Packit 06404a
      }
Packit 06404a
      _ogg_free(vf->vi);
Packit 06404a
      _ogg_free(vf->vc);
Packit 06404a
    }
Packit 06404a
    if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
Packit 06404a
    if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
Packit 06404a
    if(vf->serialnos)_ogg_free(vf->serialnos);
Packit 06404a
    if(vf->offsets)_ogg_free(vf->offsets);
Packit 06404a
    ogg_sync_clear(&vf->oy);
Packit 06404a
    if(vf->datasource && vf->callbacks.close_func)
Packit 06404a
      (vf->callbacks.close_func)(vf->datasource);
Packit 06404a
    memset(vf,0,sizeof(*vf));
Packit 06404a
  }
Packit 06404a
#ifdef DEBUG_LEAKS
Packit 06404a
  _VDBG_dump();
Packit 06404a
#endif
Packit 06404a
  return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* inspects the OggVorbis file and finds/documents all the logical
Packit 06404a
   bitstreams contained in it.  Tries to be tolerant of logical
Packit 06404a
   bitstream sections that are truncated/woogie.
Packit 06404a
Packit 06404a
   return: -1) error
Packit 06404a
            0) OK
Packit 06404a
*/
Packit 06404a
Packit 06404a
int ov_open_callbacks(void *f,OggVorbis_File *vf,
Packit 06404a
    const char *initial,long ibytes,ov_callbacks callbacks){
Packit 06404a
  int ret=_ov_open1(f,vf,initial,ibytes,callbacks);
Packit 06404a
  if(ret)return ret;
Packit 06404a
  return _ov_open2(vf);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){
Packit 06404a
  ov_callbacks callbacks = {
Packit 06404a
    (size_t (*)(void *, size_t, size_t, void *))  fread,
Packit 06404a
    (int (*)(void *, ogg_int64_t, int))              _fseek64_wrap,
Packit 06404a
    (int (*)(void *))                             fclose,
Packit 06404a
    (long (*)(void *))                            ftell
Packit 06404a
  };
Packit 06404a
Packit 06404a
  return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_fopen(const char *path,OggVorbis_File *vf){
Packit 06404a
  int ret;
Packit 06404a
  FILE *f = fopen(path,"rb");
Packit 06404a
  if(!f) return -1;
Packit 06404a
Packit 06404a
  ret = ov_open(f,vf,NULL,0);
Packit 06404a
  if(ret) fclose(f);
Packit 06404a
  return ret;
Packit 06404a
}
Packit 06404a
Packit 06404a
Packit 06404a
/* cheap hack for game usage where downsampling is desirable; there's
Packit 06404a
   no need for SRC as we can just do it cheaply in libvorbis. */
Packit 06404a
Packit 06404a
int ov_halfrate(OggVorbis_File *vf,int flag){
Packit 06404a
  int i;
Packit 06404a
  if(vf->vi==NULL)return OV_EINVAL;
Packit 06404a
  if(vf->ready_state>STREAMSET){
Packit 06404a
    /* clear out stream state; dumping the decode machine is needed to
Packit 06404a
       reinit the MDCT lookups. */
Packit 06404a
    vorbis_dsp_clear(&vf->vd);
Packit 06404a
    vorbis_block_clear(&vf->vb);
Packit 06404a
    vf->ready_state=STREAMSET;
Packit 06404a
    if(vf->pcm_offset>=0){
Packit 06404a
      ogg_int64_t pos=vf->pcm_offset;
Packit 06404a
      vf->pcm_offset=-1; /* make sure the pos is dumped if unseekable */
Packit 06404a
      ov_pcm_seek(vf,pos);
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  for(i=0;i<vf->links;i++){
Packit 06404a
    if(vorbis_synthesis_halfrate(vf->vi+i,flag)){
Packit 06404a
      if(flag) ov_halfrate(vf,0);
Packit 06404a
      return OV_EINVAL;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_halfrate_p(OggVorbis_File *vf){
Packit 06404a
  if(vf->vi==NULL)return OV_EINVAL;
Packit 06404a
  return vorbis_synthesis_halfrate_p(vf->vi);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* Only partially open the vorbis file; test for Vorbisness, and load
Packit 06404a
   the headers for the first chain.  Do not seek (although test for
Packit 06404a
   seekability).  Use ov_test_open to finish opening the file, else
Packit 06404a
   ov_clear to close/free it. Same return codes as open.
Packit 06404a
Packit 06404a
   Note that vorbisfile does _not_ take ownership of the file if the
Packit 06404a
   call fails; the calling applicaiton is responsible for closing the file
Packit 06404a
   if this call returns an error. */
Packit 06404a
Packit 06404a
int ov_test_callbacks(void *f,OggVorbis_File *vf,
Packit 06404a
    const char *initial,long ibytes,ov_callbacks callbacks)
Packit 06404a
{
Packit 06404a
  return _ov_open1(f,vf,initial,ibytes,callbacks);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){
Packit 06404a
  ov_callbacks callbacks = {
Packit 06404a
    (size_t (*)(void *, size_t, size_t, void *))  fread,
Packit 06404a
    (int (*)(void *, ogg_int64_t, int))              _fseek64_wrap,
Packit 06404a
    (int (*)(void *))                             fclose,
Packit 06404a
    (long (*)(void *))                            ftell
Packit 06404a
  };
Packit 06404a
Packit 06404a
  return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_test_open(OggVorbis_File *vf){
Packit 06404a
  if(vf->ready_state!=PARTOPEN)return(OV_EINVAL);
Packit 06404a
  return _ov_open2(vf);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* How many logical bitstreams in this physical bitstream? */
Packit 06404a
long ov_streams(OggVorbis_File *vf){
Packit 06404a
  return vf->links;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* Is the FILE * associated with vf seekable? */
Packit 06404a
long ov_seekable(OggVorbis_File *vf){
Packit 06404a
  return vf->seekable;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns the bitrate for a given logical bitstream or the entire
Packit 06404a
   physical bitstream.  If the file is open for random access, it will
Packit 06404a
   find the *actual* average bitrate.  If the file is streaming, it
Packit 06404a
   returns the nominal bitrate (if set) else the average of the
Packit 06404a
   upper/lower bounds (if set) else -1 (unset).
Packit 06404a
Packit 06404a
   If you want the actual bitrate field settings, get them from the
Packit 06404a
   vorbis_info structs */
Packit 06404a
Packit 06404a
long ov_bitrate(OggVorbis_File *vf,int i){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(i>=vf->links)return(OV_EINVAL);
Packit 06404a
  if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
Packit 06404a
  if(i<0){
Packit 06404a
    ogg_int64_t bits=0;
Packit 06404a
    int i;
Packit 06404a
    float br;
Packit 06404a
    for(i=0;i<vf->links;i++)
Packit 06404a
      bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
Packit 06404a
    /* This once read: return(rint(bits/ov_time_total(vf,-1)));
Packit 06404a
     * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
Packit 06404a
     * so this is slightly transformed to make it work.
Packit 06404a
     */
Packit 06404a
    br = bits/ov_time_total(vf,-1);
Packit 06404a
    return(rint(br));
Packit 06404a
  }else{
Packit 06404a
    if(vf->seekable){
Packit 06404a
      /* return the actual bitrate */
Packit 06404a
      return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i)));
Packit 06404a
    }else{
Packit 06404a
      /* return nominal if set */
Packit 06404a
      if(vf->vi[i].bitrate_nominal>0){
Packit 06404a
        return vf->vi[i].bitrate_nominal;
Packit 06404a
      }else{
Packit 06404a
        if(vf->vi[i].bitrate_upper>0){
Packit 06404a
          if(vf->vi[i].bitrate_lower>0){
Packit 06404a
            return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
Packit 06404a
          }else{
Packit 06404a
            return vf->vi[i].bitrate_upper;
Packit 06404a
          }
Packit 06404a
        }
Packit 06404a
        return(OV_FALSE);
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns the actual bitrate since last call.  returns -1 if no
Packit 06404a
   additional data to offer since last call (or at beginning of stream),
Packit 06404a
   EINVAL if stream is only partially open
Packit 06404a
*/
Packit 06404a
long ov_bitrate_instant(OggVorbis_File *vf){
Packit 06404a
  int link=(vf->seekable?vf->current_link:0);
Packit 06404a
  long ret;
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(vf->samptrack==0)return(OV_FALSE);
Packit 06404a
  ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
Packit 06404a
  vf->bittrack=0.f;
Packit 06404a
  vf->samptrack=0.f;
Packit 06404a
  return(ret);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* Guess */
Packit 06404a
long ov_serialnumber(OggVorbis_File *vf,int i){
Packit 06404a
  if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1));
Packit 06404a
  if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1));
Packit 06404a
  if(i<0){
Packit 06404a
    return(vf->current_serialno);
Packit 06404a
  }else{
Packit 06404a
    return(vf->serialnos[i]);
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns: total raw (compressed) length of content if i==-1
Packit 06404a
            raw (compressed) length of that logical bitstream for i==0 to n
Packit 06404a
            OV_EINVAL if the stream is not seekable (we can't know the length)
Packit 06404a
            or if stream is only partially open
Packit 06404a
*/
Packit 06404a
ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
Packit 06404a
  if(i<0){
Packit 06404a
    ogg_int64_t acc=0;
Packit 06404a
    int i;
Packit 06404a
    for(i=0;i<vf->links;i++)
Packit 06404a
      acc+=ov_raw_total(vf,i);
Packit 06404a
    return(acc);
Packit 06404a
  }else{
Packit 06404a
    return(vf->offsets[i+1]-vf->offsets[i]);
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns: total PCM length (samples) of content if i==-1 PCM length
Packit 06404a
            (samples) of that logical bitstream for i==0 to n
Packit 06404a
            OV_EINVAL if the stream is not seekable (we can't know the
Packit 06404a
            length) or only partially open
Packit 06404a
*/
Packit 06404a
ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
Packit 06404a
  if(i<0){
Packit 06404a
    ogg_int64_t acc=0;
Packit 06404a
    int i;
Packit 06404a
    for(i=0;i<vf->links;i++)
Packit 06404a
      acc+=ov_pcm_total(vf,i);
Packit 06404a
    return(acc);
Packit 06404a
  }else{
Packit 06404a
    return(vf->pcmlengths[i*2+1]);
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* returns: total seconds of content if i==-1
Packit 06404a
            seconds in that logical bitstream for i==0 to n
Packit 06404a
            OV_EINVAL if the stream is not seekable (we can't know the
Packit 06404a
            length) or only partially open
Packit 06404a
*/
Packit 06404a
double ov_time_total(OggVorbis_File *vf,int i){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
Packit 06404a
  if(i<0){
Packit 06404a
    double acc=0;
Packit 06404a
    int i;
Packit 06404a
    for(i=0;i<vf->links;i++)
Packit 06404a
      acc+=ov_time_total(vf,i);
Packit 06404a
    return(acc);
Packit 06404a
  }else{
Packit 06404a
    return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate);
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* seek to an offset relative to the *compressed* data. This also
Packit 06404a
   scans packets to update the PCM cursor. It will cross a logical
Packit 06404a
   bitstream boundary, but only if it can't get any packets out of the
Packit 06404a
   tail of the bitstream we seek to (so no surprises).
Packit 06404a
Packit 06404a
   returns zero on success, nonzero on failure */
Packit 06404a
Packit 06404a
int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  ogg_stream_state work_os;
Packit 06404a
  int ret;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable)
Packit 06404a
    return(OV_ENOSEEK); /* don't dump machine if we can't seek */
Packit 06404a
Packit 06404a
  if(pos<0 || pos>vf->end)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* is the seek position outside our current link [if any]? */
Packit 06404a
  if(vf->ready_state>=STREAMSET){
Packit 06404a
    if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1])
Packit 06404a
      _decode_clear(vf); /* clear out stream state */
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* don't yet clear out decoding machine (if it's initialized), in
Packit 06404a
     the case we're in the same link.  Restart the decode lapping, and
Packit 06404a
     let _fetch_and_process_packet deal with a potential bitstream
Packit 06404a
     boundary */
Packit 06404a
  vf->pcm_offset=-1;
Packit 06404a
  ogg_stream_reset_serialno(&vf->os,
Packit 06404a
                            vf->current_serialno); /* must set serialno */
Packit 06404a
  vorbis_synthesis_restart(&vf->vd);
Packit 06404a
Packit 06404a
  ret=_seek_helper(vf,pos);
Packit 06404a
  if(ret)goto seek_error;
Packit 06404a
Packit 06404a
  /* we need to make sure the pcm_offset is set, but we don't want to
Packit 06404a
     advance the raw cursor past good packets just to get to the first
Packit 06404a
     with a granulepos.  That's not equivalent behavior to beginning
Packit 06404a
     decoding as immediately after the seek position as possible.
Packit 06404a
Packit 06404a
     So, a hack.  We use two stream states; a local scratch state and
Packit 06404a
     the shared vf->os stream state.  We use the local state to
Packit 06404a
     scan, and the shared state as a buffer for later decode.
Packit 06404a
Packit 06404a
     Unfortuantely, on the last page we still advance to last packet
Packit 06404a
     because the granulepos on the last page is not necessarily on a
Packit 06404a
     packet boundary, and we need to make sure the granpos is
Packit 06404a
     correct.
Packit 06404a
  */
Packit 06404a
Packit 06404a
  {
Packit 06404a
    ogg_page og;
Packit 06404a
    ogg_packet op;
Packit 06404a
    int lastblock=0;
Packit 06404a
    int accblock=0;
Packit 06404a
    int thisblock=0;
Packit 06404a
    int lastflag=0;
Packit 06404a
    int firstflag=0;
Packit 06404a
    ogg_int64_t pagepos=-1;
Packit 06404a
Packit 06404a
    ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */
Packit 06404a
    ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE
Packit 06404a
                                   return from not necessarily
Packit 06404a
                                   starting from the beginning */
Packit 06404a
Packit 06404a
    while(1){
Packit 06404a
      if(vf->ready_state>=STREAMSET){
Packit 06404a
        /* snarf/scan a packet if we can */
Packit 06404a
        int result=ogg_stream_packetout(&work_os,&op);
Packit 06404a
Packit 06404a
        if(result>0){
Packit 06404a
Packit 06404a
          if(vf->vi[vf->current_link].codec_setup){
Packit 06404a
            thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
Packit 06404a
            if(thisblock<0){
Packit 06404a
              ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
              thisblock=0;
Packit 06404a
            }else{
Packit 06404a
Packit 06404a
              /* We can't get a guaranteed correct pcm position out of the
Packit 06404a
                 last page in a stream because it might have a 'short'
Packit 06404a
                 granpos, which can only be detected in the presence of a
Packit 06404a
                 preceding page.  However, if the last page is also the first
Packit 06404a
                 page, the granpos rules of a first page take precedence.  Not
Packit 06404a
                 only that, but for first==last, the EOS page must be treated
Packit 06404a
                 as if its a normal first page for the stream to open/play. */
Packit 06404a
              if(lastflag && !firstflag)
Packit 06404a
                ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
              else
Packit 06404a
                if(lastblock)accblock+=(lastblock+thisblock)>>2;
Packit 06404a
            }
Packit 06404a
Packit 06404a
            if(op.granulepos!=-1){
Packit 06404a
              int i,link=vf->current_link;
Packit 06404a
              ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
Packit 06404a
              if(granulepos<0)granulepos=0;
Packit 06404a
Packit 06404a
              for(i=0;i
Packit 06404a
                granulepos+=vf->pcmlengths[i*2+1];
Packit 06404a
              vf->pcm_offset=granulepos-accblock;
Packit 06404a
              if(vf->pcm_offset<0)vf->pcm_offset=0;
Packit 06404a
              break;
Packit 06404a
            }
Packit 06404a
            lastblock=thisblock;
Packit 06404a
            continue;
Packit 06404a
          }else
Packit 06404a
            ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
Packit 06404a
      if(!lastblock){
Packit 06404a
        pagepos=_get_next_page(vf,&og,-1);
Packit 06404a
        if(pagepos<0){
Packit 06404a
          vf->pcm_offset=ov_pcm_total(vf,-1);
Packit 06404a
          break;
Packit 06404a
        }
Packit 06404a
      }else{
Packit 06404a
        /* huh?  Bogus stream with packets but no granulepos */
Packit 06404a
        vf->pcm_offset=-1;
Packit 06404a
        break;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      /* has our decoding just traversed a bitstream boundary? */
Packit 06404a
      if(vf->ready_state>=STREAMSET){
Packit 06404a
        if(vf->current_serialno!=ogg_page_serialno(&og)){
Packit 06404a
Packit 06404a
          /* two possibilities:
Packit 06404a
             1) our decoding just traversed a bitstream boundary
Packit 06404a
             2) another stream is multiplexed into this logical section? */
Packit 06404a
Packit 06404a
          if(ogg_page_bos(&og)){
Packit 06404a
            /* we traversed */
Packit 06404a
            _decode_clear(vf); /* clear out stream state */
Packit 06404a
            ogg_stream_clear(&work_os);
Packit 06404a
          } /* else, do nothing; next loop will scoop another page */
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
Packit 06404a
      if(vf->ready_state
Packit 06404a
        int link;
Packit 06404a
        long serialno = ogg_page_serialno(&og);
Packit 06404a
Packit 06404a
        for(link=0;link<vf->links;link++)
Packit 06404a
          if(vf->serialnos[link]==serialno)break;
Packit 06404a
Packit 06404a
        if(link==vf->links) continue; /* not the desired Vorbis
Packit 06404a
                                         bitstream section; keep
Packit 06404a
                                         trying */
Packit 06404a
        vf->current_link=link;
Packit 06404a
        vf->current_serialno=serialno;
Packit 06404a
        ogg_stream_reset_serialno(&vf->os,serialno);
Packit 06404a
        ogg_stream_reset_serialno(&work_os,serialno);
Packit 06404a
        vf->ready_state=STREAMSET;
Packit 06404a
        firstflag=(pagepos<=vf->dataoffsets[link]);
Packit 06404a
      }
Packit 06404a
Packit 06404a
      ogg_stream_pagein(&vf->os,&og);
Packit 06404a
      ogg_stream_pagein(&work_os,&og);
Packit 06404a
      lastflag=ogg_page_eos(&og);
Packit 06404a
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  ogg_stream_clear(&work_os);
Packit 06404a
  vf->bittrack=0.f;
Packit 06404a
  vf->samptrack=0.f;
Packit 06404a
  return(0);
Packit 06404a
Packit 06404a
 seek_error:
Packit 06404a
  /* dump the machine so we're in a known state */
Packit 06404a
  vf->pcm_offset=-1;
Packit 06404a
  ogg_stream_clear(&work_os);
Packit 06404a
  _decode_clear(vf);
Packit 06404a
  return OV_EBADLINK;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* Page granularity seek (faster than sample granularity because we
Packit 06404a
   don't do the last bit of decode to find a specific sample).
Packit 06404a
Packit 06404a
   Seek to the last [granule marked] page preceding the specified pos
Packit 06404a
   location, such that decoding past the returned point will quickly
Packit 06404a
   arrive at the requested position. */
Packit 06404a
int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  int link=-1;
Packit 06404a
  ogg_int64_t result=0;
Packit 06404a
  ogg_int64_t total=ov_pcm_total(vf,-1);
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable)return(OV_ENOSEEK);
Packit 06404a
Packit 06404a
  if(pos<0 || pos>total)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* which bitstream section does this pcm offset occur in? */
Packit 06404a
  for(link=vf->links-1;link>=0;link--){
Packit 06404a
    total-=vf->pcmlengths[link*2+1];
Packit 06404a
    if(pos>=total)break;
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* Search within the logical bitstream for the page with the highest
Packit 06404a
     pcm_pos preceding pos.  If we're looking for a position on the
Packit 06404a
     first page, bisection will halt without finding our position as
Packit 06404a
     it's before the first explicit granulepos fencepost. That case is
Packit 06404a
     handled separately below.
Packit 06404a
Packit 06404a
     There is a danger here; missing pages or incorrect frame number
Packit 06404a
     information in the bitstream could make our task impossible.
Packit 06404a
     Account for that (it would be an error condition) */
Packit 06404a
Packit 06404a
  /* new search algorithm originally by HB (Nicholas Vinen) */
Packit 06404a
Packit 06404a
  {
Packit 06404a
    ogg_int64_t end=vf->offsets[link+1];
Packit 06404a
    ogg_int64_t begin=vf->dataoffsets[link];
Packit 06404a
    ogg_int64_t begintime = vf->pcmlengths[link*2];
Packit 06404a
    ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime;
Packit 06404a
    ogg_int64_t target=pos-total+begintime;
Packit 06404a
    ogg_int64_t best=-1;
Packit 06404a
    int         got_page=0;
Packit 06404a
Packit 06404a
    ogg_page og;
Packit 06404a
Packit 06404a
    /* if we have only one page, there will be no bisection.  Grab the page here */
Packit 06404a
    if(begin==end){
Packit 06404a
      result=_seek_helper(vf,begin);
Packit 06404a
      if(result) goto seek_error;
Packit 06404a
Packit 06404a
      result=_get_next_page(vf,&og,1);
Packit 06404a
      if(result<0) goto seek_error;
Packit 06404a
Packit 06404a
      got_page=1;
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* bisection loop */
Packit 06404a
    while(begin
Packit 06404a
      ogg_int64_t bisect;
Packit 06404a
Packit 06404a
      if(end-begin
Packit 06404a
        bisect=begin;
Packit 06404a
      }else{
Packit 06404a
        /* take a (pretty decent) guess. */
Packit 06404a
        bisect=begin +
Packit 06404a
          (ogg_int64_t)((double)(target-begintime)*(end-begin)/(endtime-begintime))
Packit 06404a
          - CHUNKSIZE;
Packit 06404a
        if(bisect
Packit 06404a
          bisect=begin;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      result=_seek_helper(vf,bisect);
Packit 06404a
      if(result) goto seek_error;
Packit 06404a
Packit 06404a
      /* read loop within the bisection loop */
Packit 06404a
      while(begin
Packit 06404a
        result=_get_next_page(vf,&og,end-vf->offset);
Packit 06404a
        if(result==OV_EREAD) goto seek_error;
Packit 06404a
        if(result<0){
Packit 06404a
          /* there is no next page! */
Packit 06404a
          if(bisect<=begin+1)
Packit 06404a
              /* No bisection left to perform.  We've either found the
Packit 06404a
                 best candidate already or failed. Exit loop. */
Packit 06404a
            end=begin;
Packit 06404a
          else{
Packit 06404a
            /* We tried to load a fraction of the last page; back up a
Packit 06404a
               bit and try to get the whole last page */
Packit 06404a
            if(bisect==0) goto seek_error;
Packit 06404a
            bisect-=CHUNKSIZE;
Packit 06404a
Packit 06404a
            /* don't repeat/loop on a read we've already performed */
Packit 06404a
            if(bisect<=begin)bisect=begin+1;
Packit 06404a
Packit 06404a
            /* seek and cntinue bisection */
Packit 06404a
            result=_seek_helper(vf,bisect);
Packit 06404a
            if(result) goto seek_error;
Packit 06404a
          }
Packit 06404a
        }else{
Packit 06404a
          ogg_int64_t granulepos;
Packit 06404a
          got_page=1;
Packit 06404a
Packit 06404a
          /* got a page. analyze it */
Packit 06404a
          /* only consider pages from primary vorbis stream */
Packit 06404a
          if(ogg_page_serialno(&og)!=vf->serialnos[link])
Packit 06404a
            continue;
Packit 06404a
Packit 06404a
          /* only consider pages with the granulepos set */
Packit 06404a
          granulepos=ogg_page_granulepos(&og);
Packit 06404a
          if(granulepos==-1)continue;
Packit 06404a
Packit 06404a
          if(granulepos
Packit 06404a
            /* this page is a successful candidate! Set state */
Packit 06404a
Packit 06404a
            best=result;  /* raw offset of packet with granulepos */
Packit 06404a
            begin=vf->offset; /* raw offset of next page */
Packit 06404a
            begintime=granulepos;
Packit 06404a
Packit 06404a
            /* if we're before our target but within a short distance,
Packit 06404a
               don't bisect; read forward */
Packit 06404a
            if(target-begintime>44100)break;
Packit 06404a
Packit 06404a
            bisect=begin; /* *not* begin + 1 as above */
Packit 06404a
          }else{
Packit 06404a
Packit 06404a
            /* This is one of our pages, but the granpos is
Packit 06404a
               post-target; it is not a bisection return
Packit 06404a
               candidate. (The only way we'd use it is if it's the
Packit 06404a
               first page in the stream; we handle that case later
Packit 06404a
               outside the bisection) */
Packit 06404a
            if(bisect<=begin+1){
Packit 06404a
              /* No bisection left to perform.  We've either found the
Packit 06404a
                 best candidate already or failed. Exit loop. */
Packit 06404a
              end=begin;
Packit 06404a
            }else{
Packit 06404a
              if(end==vf->offset){
Packit 06404a
                /* bisection read to the end; use the known page
Packit 06404a
                   boundary (result) to update bisection, back up a
Packit 06404a
                   little bit, and try again */
Packit 06404a
                end=result;
Packit 06404a
                bisect-=CHUNKSIZE;
Packit 06404a
                if(bisect<=begin)bisect=begin+1;
Packit 06404a
                result=_seek_helper(vf,bisect);
Packit 06404a
                if(result) goto seek_error;
Packit 06404a
              }else{
Packit 06404a
                /* Normal bisection */
Packit 06404a
                end=bisect;
Packit 06404a
                endtime=granulepos;
Packit 06404a
                break;
Packit 06404a
              }
Packit 06404a
            }
Packit 06404a
          }
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* Out of bisection: did it 'fail?' */
Packit 06404a
    if(best == -1){
Packit 06404a
Packit 06404a
      /* Check the 'looking for data in first page' special case;
Packit 06404a
         bisection would 'fail' because our search target was before the
Packit 06404a
         first PCM granule position fencepost. */
Packit 06404a
Packit 06404a
      if(got_page &&
Packit 06404a
         begin == vf->dataoffsets[link] &&
Packit 06404a
         ogg_page_serialno(&og)==vf->serialnos[link]){
Packit 06404a
Packit 06404a
        /* Yes, this is the beginning-of-stream case. We already have
Packit 06404a
           our page, right at the beginning of PCM data.  Set state
Packit 06404a
           and return. */
Packit 06404a
Packit 06404a
        vf->pcm_offset=total;
Packit 06404a
Packit 06404a
        if(link!=vf->current_link){
Packit 06404a
          /* Different link; dump entire decode machine */
Packit 06404a
          _decode_clear(vf);
Packit 06404a
Packit 06404a
          vf->current_link=link;
Packit 06404a
          vf->current_serialno=vf->serialnos[link];
Packit 06404a
          vf->ready_state=STREAMSET;
Packit 06404a
Packit 06404a
        }else{
Packit 06404a
          vorbis_synthesis_restart(&vf->vd);
Packit 06404a
        }
Packit 06404a
Packit 06404a
        ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
Packit 06404a
        ogg_stream_pagein(&vf->os,&og);
Packit 06404a
Packit 06404a
      }else
Packit 06404a
        goto seek_error;
Packit 06404a
Packit 06404a
    }else{
Packit 06404a
Packit 06404a
      /* Bisection found our page. seek to it, update pcm offset. Easier case than
Packit 06404a
         raw_seek, don't keep packets preceding granulepos. */
Packit 06404a
Packit 06404a
      ogg_page og;
Packit 06404a
      ogg_packet op;
Packit 06404a
Packit 06404a
      /* seek */
Packit 06404a
      result=_seek_helper(vf,best);
Packit 06404a
      vf->pcm_offset=-1;
Packit 06404a
      if(result) goto seek_error;
Packit 06404a
      result=_get_next_page(vf,&og,-1);
Packit 06404a
      if(result<0) goto seek_error;
Packit 06404a
Packit 06404a
      if(link!=vf->current_link){
Packit 06404a
        /* Different link; dump entire decode machine */
Packit 06404a
        _decode_clear(vf);
Packit 06404a
Packit 06404a
        vf->current_link=link;
Packit 06404a
        vf->current_serialno=vf->serialnos[link];
Packit 06404a
        vf->ready_state=STREAMSET;
Packit 06404a
Packit 06404a
      }else{
Packit 06404a
        vorbis_synthesis_restart(&vf->vd);
Packit 06404a
      }
Packit 06404a
Packit 06404a
      ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
Packit 06404a
      ogg_stream_pagein(&vf->os,&og);
Packit 06404a
Packit 06404a
      /* pull out all but last packet; the one with granulepos */
Packit 06404a
      while(1){
Packit 06404a
        result=ogg_stream_packetpeek(&vf->os,&op);
Packit 06404a
        if(result==0){
Packit 06404a
          /* No packet returned; we exited the bisection with 'best'
Packit 06404a
             pointing to a page with a granule position, so the packet
Packit 06404a
             finishing this page ('best') originated on a preceding
Packit 06404a
             page. Keep fetching previous pages until we get one with
Packit 06404a
             a granulepos or without the 'continued' flag set.  Then
Packit 06404a
             just use raw_seek for simplicity. */
Packit 06404a
          /* Do not rewind past the beginning of link data; if we do,
Packit 06404a
             it's either a bug or a broken stream */
Packit 06404a
          result=best;
Packit 06404a
          while(result>vf->dataoffsets[link]){
Packit 06404a
            result=_get_prev_page(vf,result,&og);
Packit 06404a
            if(result<0) goto seek_error;
Packit 06404a
            if(ogg_page_serialno(&og)==vf->current_serialno &&
Packit 06404a
               (ogg_page_granulepos(&og)>-1 ||
Packit 06404a
                !ogg_page_continued(&og))){
Packit 06404a
              return ov_raw_seek(vf,result);
Packit 06404a
            }
Packit 06404a
          }
Packit 06404a
        }
Packit 06404a
        if(result<0){
Packit 06404a
          result = OV_EBADPACKET;
Packit 06404a
          goto seek_error;
Packit 06404a
        }
Packit 06404a
        if(op.granulepos!=-1){
Packit 06404a
          vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
Packit 06404a
          if(vf->pcm_offset<0)vf->pcm_offset=0;
Packit 06404a
          vf->pcm_offset+=total;
Packit 06404a
          break;
Packit 06404a
        }else
Packit 06404a
          result=ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* verify result */
Packit 06404a
  if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){
Packit 06404a
    result=OV_EFAULT;
Packit 06404a
    goto seek_error;
Packit 06404a
  }
Packit 06404a
  vf->bittrack=0.f;
Packit 06404a
  vf->samptrack=0.f;
Packit 06404a
  return(0);
Packit 06404a
Packit 06404a
 seek_error:
Packit 06404a
  /* dump machine so we're in a known state */
Packit 06404a
  vf->pcm_offset=-1;
Packit 06404a
  _decode_clear(vf);
Packit 06404a
  return (int)result;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* seek to a sample offset relative to the decompressed pcm stream
Packit 06404a
   returns zero on success, nonzero on failure */
Packit 06404a
Packit 06404a
int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  int thisblock,lastblock=0;
Packit 06404a
  int ret=ov_pcm_seek_page(vf,pos);
Packit 06404a
  if(ret<0)return(ret);
Packit 06404a
  if((ret=_make_decode_ready(vf)))return ret;
Packit 06404a
Packit 06404a
  /* discard leading packets we don't need for the lapping of the
Packit 06404a
     position we want; don't decode them */
Packit 06404a
Packit 06404a
  while(1){
Packit 06404a
    ogg_packet op;
Packit 06404a
    ogg_page og;
Packit 06404a
Packit 06404a
    int ret=ogg_stream_packetpeek(&vf->os,&op);
Packit 06404a
    if(ret>0){
Packit 06404a
      thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
Packit 06404a
      if(thisblock<0){
Packit 06404a
        ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
        continue; /* non audio packet */
Packit 06404a
      }
Packit 06404a
      if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
Packit 06404a
Packit 06404a
      if(vf->pcm_offset+((thisblock+
Packit 06404a
                          vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
Packit 06404a
Packit 06404a
      /* remove the packet from packet queue and track its granulepos */
Packit 06404a
      ogg_stream_packetout(&vf->os,NULL);
Packit 06404a
      vorbis_synthesis_trackonly(&vf->vb,&op);  /* set up a vb with
Packit 06404a
                                                   only tracking, no
Packit 06404a
                                                   pcm_decode */
Packit 06404a
      vorbis_synthesis_blockin(&vf->vd,&vf->vb);
Packit 06404a
Packit 06404a
      /* end of logical stream case is hard, especially with exact
Packit 06404a
         length positioning. */
Packit 06404a
Packit 06404a
      if(op.granulepos>-1){
Packit 06404a
        int i;
Packit 06404a
        /* always believe the stream markers */
Packit 06404a
        vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
Packit 06404a
        if(vf->pcm_offset<0)vf->pcm_offset=0;
Packit 06404a
        for(i=0;i<vf->current_link;i++)
Packit 06404a
          vf->pcm_offset+=vf->pcmlengths[i*2+1];
Packit 06404a
      }
Packit 06404a
Packit 06404a
      lastblock=thisblock;
Packit 06404a
Packit 06404a
    }else{
Packit 06404a
      if(ret<0 && ret!=OV_HOLE)break;
Packit 06404a
Packit 06404a
      /* suck in a new page */
Packit 06404a
      if(_get_next_page(vf,&og,-1)<0)break;
Packit 06404a
      if(ogg_page_bos(&og))_decode_clear(vf);
Packit 06404a
Packit 06404a
      if(vf->ready_state
Packit 06404a
        long serialno=ogg_page_serialno(&og);
Packit 06404a
        int link;
Packit 06404a
Packit 06404a
        for(link=0;link<vf->links;link++)
Packit 06404a
          if(vf->serialnos[link]==serialno)break;
Packit 06404a
        if(link==vf->links) continue;
Packit 06404a
        vf->current_link=link;
Packit 06404a
Packit 06404a
        vf->ready_state=STREAMSET;
Packit 06404a
        vf->current_serialno=ogg_page_serialno(&og);
Packit 06404a
        ogg_stream_reset_serialno(&vf->os,serialno);
Packit 06404a
        ret=_make_decode_ready(vf);
Packit 06404a
        if(ret)return ret;
Packit 06404a
        lastblock=0;
Packit 06404a
      }
Packit 06404a
Packit 06404a
      ogg_stream_pagein(&vf->os,&og);
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  vf->bittrack=0.f;
Packit 06404a
  vf->samptrack=0.f;
Packit 06404a
  /* discard samples until we reach the desired position. Crossing a
Packit 06404a
     logical bitstream boundary with abandon is OK. */
Packit 06404a
  {
Packit 06404a
    /* note that halfrate could be set differently in each link, but
Packit 06404a
       vorbisfile encoforces all links are set or unset */
Packit 06404a
    int hs=vorbis_synthesis_halfrate_p(vf->vi);
Packit 06404a
    while(vf->pcm_offset<((pos>>hs)<
Packit 06404a
      ogg_int64_t target=(pos-vf->pcm_offset)>>hs;
Packit 06404a
      long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
Packit 06404a
Packit 06404a
      if(samples>target)samples=target;
Packit 06404a
      vorbis_synthesis_read(&vf->vd,samples);
Packit 06404a
      vf->pcm_offset+=samples<
Packit 06404a
Packit 06404a
      if(samples
Packit 06404a
        if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
Packit 06404a
          vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* seek to a playback time relative to the decompressed pcm stream
Packit 06404a
   returns zero on success, nonzero on failure */
Packit 06404a
int ov_time_seek(OggVorbis_File *vf,double seconds){
Packit 06404a
  /* translate time to PCM position and call ov_pcm_seek */
Packit 06404a
Packit 06404a
  int link=-1;
Packit 06404a
  ogg_int64_t pcm_total=0;
Packit 06404a
  double time_total=0.;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable)return(OV_ENOSEEK);
Packit 06404a
  if(seconds<0)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* which bitstream section does this time offset occur in? */
Packit 06404a
  for(link=0;link<vf->links;link++){
Packit 06404a
    double addsec = ov_time_total(vf,link);
Packit 06404a
    if(seconds
Packit 06404a
    time_total+=addsec;
Packit 06404a
    pcm_total+=vf->pcmlengths[link*2+1];
Packit 06404a
  }
Packit 06404a
Packit 06404a
  if(link==vf->links)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* enough information to convert time offset to pcm offset */
Packit 06404a
  {
Packit 06404a
    ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
Packit 06404a
    return(ov_pcm_seek(vf,target));
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* page-granularity version of ov_time_seek
Packit 06404a
   returns zero on success, nonzero on failure */
Packit 06404a
int ov_time_seek_page(OggVorbis_File *vf,double seconds){
Packit 06404a
  /* translate time to PCM position and call ov_pcm_seek */
Packit 06404a
Packit 06404a
  int link=-1;
Packit 06404a
  ogg_int64_t pcm_total=0;
Packit 06404a
  double time_total=0.;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(!vf->seekable)return(OV_ENOSEEK);
Packit 06404a
  if(seconds<0)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* which bitstream section does this time offset occur in? */
Packit 06404a
  for(link=0;link<vf->links;link++){
Packit 06404a
    double addsec = ov_time_total(vf,link);
Packit 06404a
    if(seconds
Packit 06404a
    time_total+=addsec;
Packit 06404a
    pcm_total+=vf->pcmlengths[link*2+1];
Packit 06404a
  }
Packit 06404a
Packit 06404a
  if(link==vf->links)return(OV_EINVAL);
Packit 06404a
Packit 06404a
  /* enough information to convert time offset to pcm offset */
Packit 06404a
  {
Packit 06404a
    ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
Packit 06404a
    return(ov_pcm_seek_page(vf,target));
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* tell the current stream offset cursor.  Note that seek followed by
Packit 06404a
   tell will likely not give the set offset due to caching */
Packit 06404a
ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  return(vf->offset);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* return PCM offset (sample) of next PCM sample to be read */
Packit 06404a
ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
Packit 06404a
  if(vf->ready_state
Packit 06404a
  return(vf->pcm_offset);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* return time offset (seconds) of next PCM sample to be read */
Packit 06404a
double ov_time_tell(OggVorbis_File *vf){
Packit 06404a
  int link=0;
Packit 06404a
  ogg_int64_t pcm_total=0;
Packit 06404a
  double time_total=0.f;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  if(vf->seekable){
Packit 06404a
    pcm_total=ov_pcm_total(vf,-1);
Packit 06404a
    time_total=ov_time_total(vf,-1);
Packit 06404a
Packit 06404a
    /* which bitstream section does this time offset occur in? */
Packit 06404a
    for(link=vf->links-1;link>=0;link--){
Packit 06404a
      pcm_total-=vf->pcmlengths[link*2+1];
Packit 06404a
      time_total-=ov_time_total(vf,link);
Packit 06404a
      if(vf->pcm_offset>=pcm_total)break;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
  return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate);
Packit 06404a
}
Packit 06404a
Packit 06404a
/*  link:   -1) return the vorbis_info struct for the bitstream section
Packit 06404a
                currently being decoded
Packit 06404a
           0-n) to request information for a specific bitstream section
Packit 06404a
Packit 06404a
    In the case of a non-seekable bitstream, any call returns the
Packit 06404a
    current bitstream.  NULL in the case that the machine is not
Packit 06404a
    initialized */
Packit 06404a
Packit 06404a
vorbis_info *ov_info(OggVorbis_File *vf,int link){
Packit 06404a
  if(vf->seekable){
Packit 06404a
    if(link<0)
Packit 06404a
      if(vf->ready_state>=STREAMSET)
Packit 06404a
        return vf->vi+vf->current_link;
Packit 06404a
      else
Packit 06404a
      return vf->vi;
Packit 06404a
    else
Packit 06404a
      if(link>=vf->links)
Packit 06404a
        return NULL;
Packit 06404a
      else
Packit 06404a
        return vf->vi+link;
Packit 06404a
  }else{
Packit 06404a
    return vf->vi;
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* grr, strong typing, grr, no templates/inheritence, grr */
Packit 06404a
vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
Packit 06404a
  if(vf->seekable){
Packit 06404a
    if(link<0)
Packit 06404a
      if(vf->ready_state>=STREAMSET)
Packit 06404a
        return vf->vc+vf->current_link;
Packit 06404a
      else
Packit 06404a
        return vf->vc;
Packit 06404a
    else
Packit 06404a
      if(link>=vf->links)
Packit 06404a
        return NULL;
Packit 06404a
      else
Packit 06404a
        return vf->vc+link;
Packit 06404a
  }else{
Packit 06404a
    return vf->vc;
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
static int host_is_big_endian() {
Packit 06404a
  ogg_int32_t pattern = 0xfeedface; /* deadbeef */
Packit 06404a
  unsigned char *bytewise = (unsigned char *)&pattern;
Packit 06404a
  if (bytewise[0] == 0xfe) return 1;
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* up to this point, everything could more or less hide the multiple
Packit 06404a
   logical bitstream nature of chaining from the toplevel application
Packit 06404a
   if the toplevel application didn't particularly care.  However, at
Packit 06404a
   the point that we actually read audio back, the multiple-section
Packit 06404a
   nature must surface: Multiple bitstream sections do not necessarily
Packit 06404a
   have to have the same number of channels or sampling rate.
Packit 06404a
Packit 06404a
   ov_read returns the sequential logical bitstream number currently
Packit 06404a
   being decoded along with the PCM data in order that the toplevel
Packit 06404a
   application can take action on channel/sample rate changes.  This
Packit 06404a
   number will be incremented even for streamed (non-seekable) streams
Packit 06404a
   (for seekable streams, it represents the actual logical bitstream
Packit 06404a
   index within the physical bitstream.  Note that the accessor
Packit 06404a
   functions above are aware of this dichotomy).
Packit 06404a
Packit 06404a
   ov_read_filter is exactly the same as ov_read except that it processes
Packit 06404a
   the decoded audio data through a filter before packing it into the
Packit 06404a
   requested format. This gives greater accuracy than applying a filter
Packit 06404a
   after the audio has been converted into integral PCM.
Packit 06404a
Packit 06404a
   input values: buffer) a buffer to hold packed PCM data for return
Packit 06404a
                 length) the byte length requested to be placed into buffer
Packit 06404a
                 bigendianp) should the data be packed LSB first (0) or
Packit 06404a
                             MSB first (1)
Packit 06404a
                 word) word size for output.  currently 1 (byte) or
Packit 06404a
                       2 (16 bit short)
Packit 06404a
Packit 06404a
   return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
Packit 06404a
                   0) EOF
Packit 06404a
                   n) number of bytes of PCM actually returned.  The
Packit 06404a
                   below works on a packet-by-packet basis, so the
Packit 06404a
                   return length is not related to the 'length' passed
Packit 06404a
                   in, just guaranteed to fit.
Packit 06404a
Packit 06404a
            *section) set to the logical bitstream number */
Packit 06404a
Packit 06404a
long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
Packit 06404a
                    int bigendianp,int word,int sgned,int *bitstream,
Packit 06404a
                    void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param){
Packit 06404a
  int i,j;
Packit 06404a
  int host_endian = host_is_big_endian();
Packit 06404a
  int hs;
Packit 06404a
Packit 06404a
  float **pcm;
Packit 06404a
  long samples;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
Packit 06404a
  while(1){
Packit 06404a
    if(vf->ready_state==INITSET){
Packit 06404a
      samples=vorbis_synthesis_pcmout(&vf->vd,&pcm;;
Packit 06404a
      if(samples)break;
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* suck in another packet */
Packit 06404a
    {
Packit 06404a
      int ret=_fetch_and_process_packet(vf,NULL,1,1);
Packit 06404a
      if(ret==OV_EOF)
Packit 06404a
        return(0);
Packit 06404a
      if(ret<=0)
Packit 06404a
        return(ret);
Packit 06404a
    }
Packit 06404a
Packit 06404a
  }
Packit 06404a
Packit 06404a
  if(samples>0){
Packit 06404a
Packit 06404a
    /* yay! proceed to pack data into the byte buffer */
Packit 06404a
Packit 06404a
    long channels=ov_info(vf,-1)->channels;
Packit 06404a
    long bytespersample=word * channels;
Packit 06404a
    vorbis_fpu_control fpu;
Packit 06404a
    if(samples>length/bytespersample)samples=length/bytespersample;
Packit 06404a
Packit 06404a
    if(samples <= 0)
Packit 06404a
      return OV_EINVAL;
Packit 06404a
Packit 06404a
    /* Here. */
Packit 06404a
    if(filter)
Packit 06404a
      filter(pcm,channels,samples,filter_param);
Packit 06404a
Packit 06404a
    /* a tight loop to pack each size */
Packit 06404a
    {
Packit 06404a
      int val;
Packit 06404a
      if(word==1){
Packit 06404a
        int off=(sgned?0:128);
Packit 06404a
        vorbis_fpu_setround(&fpu;;
Packit 06404a
        for(j=0;j
Packit 06404a
          for(i=0;i
Packit 06404a
            val=vorbis_ftoi(pcm[i][j]*128.f);
Packit 06404a
            if(val>127)val=127;
Packit 06404a
            else if(val<-128)val=-128;
Packit 06404a
            *buffer++=val+off;
Packit 06404a
          }
Packit 06404a
        vorbis_fpu_restore(fpu);
Packit 06404a
      }else{
Packit 06404a
        int off=(sgned?0:32768);
Packit 06404a
Packit 06404a
        if(host_endian==bigendianp){
Packit 06404a
          if(sgned){
Packit 06404a
Packit 06404a
            vorbis_fpu_setround(&fpu;;
Packit 06404a
            for(i=0;i
Packit 06404a
              float *src=pcm[i];
Packit 06404a
              short *dest=((short *)buffer)+i;
Packit 06404a
              for(j=0;j
Packit 06404a
                val=vorbis_ftoi(src[j]*32768.f);
Packit 06404a
                if(val>32767)val=32767;
Packit 06404a
                else if(val<-32768)val=-32768;
Packit 06404a
                *dest=val;
Packit 06404a
                dest+=channels;
Packit 06404a
              }
Packit 06404a
            }
Packit 06404a
            vorbis_fpu_restore(fpu);
Packit 06404a
Packit 06404a
          }else{
Packit 06404a
Packit 06404a
            vorbis_fpu_setround(&fpu;;
Packit 06404a
            for(i=0;i
Packit 06404a
              float *src=pcm[i];
Packit 06404a
              short *dest=((short *)buffer)+i;
Packit 06404a
              for(j=0;j
Packit 06404a
                val=vorbis_ftoi(src[j]*32768.f);
Packit 06404a
                if(val>32767)val=32767;
Packit 06404a
                else if(val<-32768)val=-32768;
Packit 06404a
                *dest=val+off;
Packit 06404a
                dest+=channels;
Packit 06404a
              }
Packit 06404a
            }
Packit 06404a
            vorbis_fpu_restore(fpu);
Packit 06404a
Packit 06404a
          }
Packit 06404a
        }else if(bigendianp){
Packit 06404a
Packit 06404a
          vorbis_fpu_setround(&fpu;;
Packit 06404a
          for(j=0;j
Packit 06404a
            for(i=0;i
Packit 06404a
              val=vorbis_ftoi(pcm[i][j]*32768.f);
Packit 06404a
              if(val>32767)val=32767;
Packit 06404a
              else if(val<-32768)val=-32768;
Packit 06404a
              val+=off;
Packit 06404a
              *buffer++=(val>>8);
Packit 06404a
              *buffer++=(val&0xff);
Packit 06404a
            }
Packit 06404a
          vorbis_fpu_restore(fpu);
Packit 06404a
Packit 06404a
        }else{
Packit 06404a
          int val;
Packit 06404a
          vorbis_fpu_setround(&fpu;;
Packit 06404a
          for(j=0;j
Packit 06404a
            for(i=0;i
Packit 06404a
              val=vorbis_ftoi(pcm[i][j]*32768.f);
Packit 06404a
              if(val>32767)val=32767;
Packit 06404a
              else if(val<-32768)val=-32768;
Packit 06404a
              val+=off;
Packit 06404a
              *buffer++=(val&0xff);
Packit 06404a
              *buffer++=(val>>8);
Packit 06404a
                  }
Packit 06404a
          vorbis_fpu_restore(fpu);
Packit 06404a
Packit 06404a
        }
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    vorbis_synthesis_read(&vf->vd,samples);
Packit 06404a
    hs=vorbis_synthesis_halfrate_p(vf->vi);
Packit 06404a
    vf->pcm_offset+=(samples<
Packit 06404a
    if(bitstream)*bitstream=vf->current_link;
Packit 06404a
    return(samples*bytespersample);
Packit 06404a
  }else{
Packit 06404a
    return(samples);
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
long ov_read(OggVorbis_File *vf,char *buffer,int length,
Packit 06404a
             int bigendianp,int word,int sgned,int *bitstream){
Packit 06404a
  return ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, NULL, NULL);
Packit 06404a
}
Packit 06404a
Packit 06404a
/* input values: pcm_channels) a float vector per channel of output
Packit 06404a
                 length) the sample length being read by the app
Packit 06404a
Packit 06404a
   return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
Packit 06404a
                   0) EOF
Packit 06404a
                   n) number of samples of PCM actually returned.  The
Packit 06404a
                   below works on a packet-by-packet basis, so the
Packit 06404a
                   return length is not related to the 'length' passed
Packit 06404a
                   in, just guaranteed to fit.
Packit 06404a
Packit 06404a
            *section) set to the logical bitstream number */
Packit 06404a
Packit 06404a
Packit 06404a
Packit 06404a
long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
Packit 06404a
                   int *bitstream){
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
Packit 06404a
  while(1){
Packit 06404a
    if(vf->ready_state==INITSET){
Packit 06404a
      float **pcm;
Packit 06404a
      long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm;;
Packit 06404a
      if(samples){
Packit 06404a
        int hs=vorbis_synthesis_halfrate_p(vf->vi);
Packit 06404a
        if(pcm_channels)*pcm_channels=pcm;
Packit 06404a
        if(samples>length)samples=length;
Packit 06404a
        vorbis_synthesis_read(&vf->vd,samples);
Packit 06404a
        vf->pcm_offset+=samples<
Packit 06404a
        if(bitstream)*bitstream=vf->current_link;
Packit 06404a
        return samples;
Packit 06404a
Packit 06404a
      }
Packit 06404a
    }
Packit 06404a
Packit 06404a
    /* suck in another packet */
Packit 06404a
    {
Packit 06404a
      int ret=_fetch_and_process_packet(vf,NULL,1,1);
Packit 06404a
      if(ret==OV_EOF)return(0);
Packit 06404a
      if(ret<=0)return(ret);
Packit 06404a
    }
Packit 06404a
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
extern const float *vorbis_window(vorbis_dsp_state *v,int W);
Packit 06404a
Packit 06404a
static void _ov_splice(float **pcm,float **lappcm,
Packit 06404a
                       int n1, int n2,
Packit 06404a
                       int ch1, int ch2,
Packit 06404a
                       const float *w1, const float *w2){
Packit 06404a
  int i,j;
Packit 06404a
  const float *w=w1;
Packit 06404a
  int n=n1;
Packit 06404a
Packit 06404a
  if(n1>n2){
Packit 06404a
    n=n2;
Packit 06404a
    w=w2;
Packit 06404a
  }
Packit 06404a
Packit 06404a
  /* splice */
Packit 06404a
  for(j=0;j
Packit 06404a
    float *s=lappcm[j];
Packit 06404a
    float *d=pcm[j];
Packit 06404a
Packit 06404a
    for(i=0;i
Packit 06404a
      float wd=w[i]*w[i];
Packit 06404a
      float ws=1.-wd;
Packit 06404a
      d[i]=d[i]*wd + s[i]*ws;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  /* window from zero */
Packit 06404a
  for(;j
Packit 06404a
    float *d=pcm[j];
Packit 06404a
    for(i=0;i
Packit 06404a
      float wd=w[i]*w[i];
Packit 06404a
      d[i]=d[i]*wd;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
Packit 06404a
}
Packit 06404a
Packit 06404a
/* make sure vf is INITSET */
Packit 06404a
static int _ov_initset(OggVorbis_File *vf){
Packit 06404a
  while(1){
Packit 06404a
    if(vf->ready_state==INITSET)break;
Packit 06404a
    /* suck in another packet */
Packit 06404a
    {
Packit 06404a
      int ret=_fetch_and_process_packet(vf,NULL,1,0);
Packit 06404a
      if(ret<0 && ret!=OV_HOLE)return(ret);
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* make sure vf is INITSET and that we have a primed buffer; if
Packit 06404a
   we're crosslapping at a stream section boundary, this also makes
Packit 06404a
   sure we're sanity checking against the right stream information */
Packit 06404a
static int _ov_initprime(OggVorbis_File *vf){
Packit 06404a
  vorbis_dsp_state *vd=&vf->vd;
Packit 06404a
  while(1){
Packit 06404a
    if(vf->ready_state==INITSET)
Packit 06404a
      if(vorbis_synthesis_pcmout(vd,NULL))break;
Packit 06404a
Packit 06404a
    /* suck in another packet */
Packit 06404a
    {
Packit 06404a
      int ret=_fetch_and_process_packet(vf,NULL,1,0);
Packit 06404a
      if(ret<0 && ret!=OV_HOLE)return(ret);
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  return 0;
Packit 06404a
}
Packit 06404a
Packit 06404a
/* grab enough data for lapping from vf; this may be in the form of
Packit 06404a
   unreturned, already-decoded pcm, remaining PCM we will need to
Packit 06404a
   decode, or synthetic postextrapolation from last packets. */
Packit 06404a
static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd,
Packit 06404a
                       float **lappcm,int lapsize){
Packit 06404a
  int lapcount=0,i;
Packit 06404a
  float **pcm;
Packit 06404a
Packit 06404a
  /* try first to decode the lapping data */
Packit 06404a
  while(lapcount
Packit 06404a
    int samples=vorbis_synthesis_pcmout(vd,&pcm;;
Packit 06404a
    if(samples){
Packit 06404a
      if(samples>lapsize-lapcount)samples=lapsize-lapcount;
Packit 06404a
      for(i=0;i<vi->channels;i++)
Packit 06404a
        memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
Packit 06404a
      lapcount+=samples;
Packit 06404a
      vorbis_synthesis_read(vd,samples);
Packit 06404a
    }else{
Packit 06404a
    /* suck in another packet */
Packit 06404a
      int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */
Packit 06404a
      if(ret==OV_EOF)break;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
  if(lapcount
Packit 06404a
    /* failed to get lapping data from normal decode; pry it from the
Packit 06404a
       postextrapolation buffering, or the second half of the MDCT
Packit 06404a
       from the last packet */
Packit 06404a
    int samples=vorbis_synthesis_lapout(&vf->vd,&pcm;;
Packit 06404a
    if(samples==0){
Packit 06404a
      for(i=0;i<vi->channels;i++)
Packit 06404a
        memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount);
Packit 06404a
      lapcount=lapsize;
Packit 06404a
    }else{
Packit 06404a
      if(samples>lapsize-lapcount)samples=lapsize-lapcount;
Packit 06404a
      for(i=0;i<vi->channels;i++)
Packit 06404a
        memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
Packit 06404a
      lapcount+=samples;
Packit 06404a
    }
Packit 06404a
  }
Packit 06404a
}
Packit 06404a
Packit 06404a
/* this sets up crosslapping of a sample by using trailing data from
Packit 06404a
   sample 1 and lapping it into the windowing buffer of sample 2 */
Packit 06404a
int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
Packit 06404a
  vorbis_info *vi1,*vi2;
Packit 06404a
  float **lappcm;
Packit 06404a
  float **pcm;
Packit 06404a
  const float *w1,*w2;
Packit 06404a
  int n1,n2,i,ret,hs1,hs2;
Packit 06404a
Packit 06404a
  if(vf1==vf2)return(0); /* degenerate case */
Packit 06404a
  if(vf1->ready_state
Packit 06404a
  if(vf2->ready_state
Packit 06404a
Packit 06404a
  /* the relevant overlap buffers must be pre-checked and pre-primed
Packit 06404a
     before looking at settings in the event that priming would cross
Packit 06404a
     a bitstream boundary.  So, do it now */
Packit 06404a
Packit 06404a
  ret=_ov_initset(vf1);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
  ret=_ov_initprime(vf2);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
Packit 06404a
  vi1=ov_info(vf1,-1);
Packit 06404a
  vi2=ov_info(vf2,-1);
Packit 06404a
  hs1=ov_halfrate_p(vf1);
Packit 06404a
  hs2=ov_halfrate_p(vf2);
Packit 06404a
Packit 06404a
  lappcm=alloca(sizeof(*lappcm)*vi1->channels);
Packit 06404a
  n1=vorbis_info_blocksize(vi1,0)>>(1+hs1);
Packit 06404a
  n2=vorbis_info_blocksize(vi2,0)>>(1+hs2);
Packit 06404a
  w1=vorbis_window(&vf1->vd,0);
Packit 06404a
  w2=vorbis_window(&vf2->vd,0);
Packit 06404a
Packit 06404a
  for(i=0;i<vi1->channels;i++)
Packit 06404a
    lappcm[i]=alloca(sizeof(**lappcm)*n1);
Packit 06404a
Packit 06404a
  _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1);
Packit 06404a
Packit 06404a
  /* have a lapping buffer from vf1; now to splice it into the lapping
Packit 06404a
     buffer of vf2 */
Packit 06404a
  /* consolidate and expose the buffer. */
Packit 06404a
  vorbis_synthesis_lapout(&vf2->vd,&pcm;;
Packit 06404a
Packit 06404a
#if 0
Packit 06404a
  _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0);
Packit 06404a
  _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0);
Packit 06404a
#endif
Packit 06404a
Packit 06404a
  /* splice */
Packit 06404a
  _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2);
Packit 06404a
Packit 06404a
  /* done */
Packit 06404a
  return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos,
Packit 06404a
                           int (*localseek)(OggVorbis_File *,ogg_int64_t)){
Packit 06404a
  vorbis_info *vi;
Packit 06404a
  float **lappcm;
Packit 06404a
  float **pcm;
Packit 06404a
  const float *w1,*w2;
Packit 06404a
  int n1,n2,ch1,ch2,hs;
Packit 06404a
  int i,ret;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  ret=_ov_initset(vf);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
  vi=ov_info(vf,-1);
Packit 06404a
  hs=ov_halfrate_p(vf);
Packit 06404a
Packit 06404a
  ch1=vi->channels;
Packit 06404a
  n1=vorbis_info_blocksize(vi,0)>>(1+hs);
Packit 06404a
  w1=vorbis_window(&vf->vd,0);  /* window arrays from libvorbis are
Packit 06404a
                                   persistent; even if the decode state
Packit 06404a
                                   from this link gets dumped, this
Packit 06404a
                                   window array continues to exist */
Packit 06404a
Packit 06404a
  lappcm=alloca(sizeof(*lappcm)*ch1);
Packit 06404a
  for(i=0;i
Packit 06404a
    lappcm[i]=alloca(sizeof(**lappcm)*n1);
Packit 06404a
  _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
Packit 06404a
Packit 06404a
  /* have lapping data; seek and prime the buffer */
Packit 06404a
  ret=localseek(vf,pos);
Packit 06404a
  if(ret)return ret;
Packit 06404a
  ret=_ov_initprime(vf);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
Packit 06404a
 /* Guard against cross-link changes; they're perfectly legal */
Packit 06404a
  vi=ov_info(vf,-1);
Packit 06404a
  ch2=vi->channels;
Packit 06404a
  n2=vorbis_info_blocksize(vi,0)>>(1+hs);
Packit 06404a
  w2=vorbis_window(&vf->vd,0);
Packit 06404a
Packit 06404a
  /* consolidate and expose the buffer. */
Packit 06404a
  vorbis_synthesis_lapout(&vf->vd,&pcm;;
Packit 06404a
Packit 06404a
  /* splice */
Packit 06404a
  _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
Packit 06404a
Packit 06404a
  /* done */
Packit 06404a
  return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  return _ov_64_seek_lap(vf,pos,ov_raw_seek);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  return _ov_64_seek_lap(vf,pos,ov_pcm_seek);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){
Packit 06404a
  return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page);
Packit 06404a
}
Packit 06404a
Packit 06404a
static int _ov_d_seek_lap(OggVorbis_File *vf,double pos,
Packit 06404a
                           int (*localseek)(OggVorbis_File *,double)){
Packit 06404a
  vorbis_info *vi;
Packit 06404a
  float **lappcm;
Packit 06404a
  float **pcm;
Packit 06404a
  const float *w1,*w2;
Packit 06404a
  int n1,n2,ch1,ch2,hs;
Packit 06404a
  int i,ret;
Packit 06404a
Packit 06404a
  if(vf->ready_state
Packit 06404a
  ret=_ov_initset(vf);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
  vi=ov_info(vf,-1);
Packit 06404a
  hs=ov_halfrate_p(vf);
Packit 06404a
Packit 06404a
  ch1=vi->channels;
Packit 06404a
  n1=vorbis_info_blocksize(vi,0)>>(1+hs);
Packit 06404a
  w1=vorbis_window(&vf->vd,0);  /* window arrays from libvorbis are
Packit 06404a
                                   persistent; even if the decode state
Packit 06404a
                                   from this link gets dumped, this
Packit 06404a
                                   window array continues to exist */
Packit 06404a
Packit 06404a
  lappcm=alloca(sizeof(*lappcm)*ch1);
Packit 06404a
  for(i=0;i
Packit 06404a
    lappcm[i]=alloca(sizeof(**lappcm)*n1);
Packit 06404a
  _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
Packit 06404a
Packit 06404a
  /* have lapping data; seek and prime the buffer */
Packit 06404a
  ret=localseek(vf,pos);
Packit 06404a
  if(ret)return ret;
Packit 06404a
  ret=_ov_initprime(vf);
Packit 06404a
  if(ret)return(ret);
Packit 06404a
Packit 06404a
 /* Guard against cross-link changes; they're perfectly legal */
Packit 06404a
  vi=ov_info(vf,-1);
Packit 06404a
  ch2=vi->channels;
Packit 06404a
  n2=vorbis_info_blocksize(vi,0)>>(1+hs);
Packit 06404a
  w2=vorbis_window(&vf->vd,0);
Packit 06404a
Packit 06404a
  /* consolidate and expose the buffer. */
Packit 06404a
  vorbis_synthesis_lapout(&vf->vd,&pcm;;
Packit 06404a
Packit 06404a
  /* splice */
Packit 06404a
  _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
Packit 06404a
Packit 06404a
  /* done */
Packit 06404a
  return(0);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_time_seek_lap(OggVorbis_File *vf,double pos){
Packit 06404a
  return _ov_d_seek_lap(vf,pos,ov_time_seek);
Packit 06404a
}
Packit 06404a
Packit 06404a
int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){
Packit 06404a
  return _ov_d_seek_lap(vf,pos,ov_time_seek_page);
Packit 06404a
}