********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c,v 1.66 2003/03/02 21:32:00 xiphmont Exp $
+ last mod: $Id: vorbisfile.c,v 1.73 2003/09/02 04:39:26 xiphmont Exp $
********************************************************************/
}
}
-static void _make_decode_ready(OggVorbis_File *vf){
- if(vf->ready_state!=STREAMSET)return;
+static int _make_decode_ready(OggVorbis_File *vf){
+ if(vf->ready_state!=STREAMSET)return OV_EFAULT;
if(vf->seekable){
- vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link);
+ if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link))
+ return OV_EBADLINK;
}else{
- vorbis_synthesis_init(&vf->vd,vf->vi);
+ if(vorbis_synthesis_init(&vf->vd,vf->vi))
+ return OV_EBADLINK;
}
vorbis_block_init(&vf->vd,&vf->vb);
vf->ready_state=INITSET;
- return;
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
+ return 0;
}
static int _open_seekable2(OggVorbis_File *vf){
vorbis_dsp_clear(&vf->vd);
vorbis_block_clear(&vf->vb);
vf->ready_state=OPENED;
-
- vf->bittrack=0.f;
- vf->samptrack=0.f;
}
/* fetch and process a packet. Handles the case where we're at a
}
if(vf->ready_state>=OPENED){
+ int ret;
if(!readp)return(0);
- if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* eof.
- leave unitialized */
- /* bitrate tracking; add the header's bytes here, the body bytes
- are done by packet above */
+ if((ret=_get_next_page(vf,&og,-1))<0){
+ return(OV_EOF); /* eof.
+ leave unitialized */
+ }
+
+ /* bitrate tracking; add the header's bytes here, the body bytes
+ are done by packet above */
vf->bittrack+=og.header_len*8;
/* has our decoding just traversed a bitstream boundary? */
if(vf->ready_state==INITSET){
if(vf->current_serialno!=ogg_page_serialno(&og)){
- if(!spanp)return(OV_EOF);
+ if(!spanp)
+ return(OV_EOF);
_decode_clear(vf);
}
}
- _make_decode_ready(vf);
+ {
+ int ret=_make_decode_ready(vf);
+ if(ret<0)return ret;
+ }
}
ogg_stream_pagein(&vf->os,&og);
}
if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){
vf->datasource=NULL;
ov_clear(vf);
- }else if(vf->ready_state < PARTOPEN)
+ }else
vf->ready_state=PARTOPEN;
return(ret);
}
return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
}
+
+/* cheap hack for game usage where downsampling is desirable; there's
+ no need for SRC as we can just do it cheaply in libvorbis. */
+
+int ov_halfrate(OggVorbis_File *vf,int flag){
+ int i;
+ if(vf->vi==NULL)return OV_EINVAL;
+ if(!vf->seekable)return OV_EINVAL;
+ if(vf->ready_state>=STREAMSET)
+ _decode_clear(vf); /* clear out stream state; later on libvorbis
+ will be able to swap this on the fly, but
+ for now dumping the decode machine is needed
+ to reinit the MDCT lookups. 1.1 libvorbis
+ is planned to be able to switch on the fly */
+ for(i=0;i<vf->links;i++){
+ if(vorbis_synthesis_halfrate(vf->vi+i,flag)){
+ ov_halfrate(vf,0);
+ return OV_EINVAL;
+ }
+ }
+ return 0;
+}
+
+int ov_halfrate_p(OggVorbis_File *vf){
+ if(vf->vi==NULL)return OV_EINVAL;
+ return vorbis_synthesis_halfrate_p(vf->vi);
+}
+
/* Only partially open the vorbis file; test for Vorbisness, and load
the headers for the first chain. Do not seek (although test for
seekability). Use ov_test_open to finish opening the file, else
if(pos<0 || pos>vf->end)return(OV_EINVAL);
- /* clear out decoding machine state */
+ /* don't yet clear out decoding machine (if it's initialized), in
+ the case we're in the same link. Restart the decode lapping, and
+ let _fetch_and_process_packet deal with a potential bitstream
+ boundary */
vf->pcm_offset=-1;
- _decode_clear(vf);
-
+ ogg_stream_reset_serialno(&vf->os,
+ vf->current_serialno); /* must set serialno */
+ vorbis_synthesis_restart(&vf->vd);
+
_seek_helper(vf,pos);
/* we need to make sure the pcm_offset is set, but we don't want to
int thisblock;
int eosflag;
- ogg_stream_init(&work_os,-1); /* get the memory ready */
+ ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */
+ ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE
+ return from not necessarily
+ starting from the beginning */
while(1){
- if(vf->ready_state==STREAMSET){
+ if(vf->ready_state>=STREAMSET){
/* snarf/scan a packet if we can */
int result=ogg_stream_packetout(&work_os,&op);
if(result>0){
- if(vf->vi[vf->current_link].codec_setup)
+ if(vf->vi[vf->current_link].codec_setup){
thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
- if(eosflag)
+ if(thisblock<0){
+ ogg_stream_packetout(&vf->os,NULL);
+ thisblock=0;
+ }else{
+
+ if(eosflag)
+ ogg_stream_packetout(&vf->os,NULL);
+ else
+ if(lastblock)accblock+=(lastblock+thisblock)>>2;
+ }
+
+ if(op.granulepos!=-1){
+ int i,link=vf->current_link;
+ ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
+ if(granulepos<0)granulepos=0;
+
+ for(i=0;i<link;i++)
+ granulepos+=vf->pcmlengths[i*2+1];
+ vf->pcm_offset=granulepos-accblock;
+ break;
+ }
+ lastblock=thisblock;
+ continue;
+ }else
ogg_stream_packetout(&vf->os,NULL);
- else
- if(lastblock)accblock+=(lastblock+thisblock)>>2;
-
- if(op.granulepos!=-1){
- int i,link=vf->current_link;
- ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
- if(granulepos<0)granulepos=0;
-
- for(i=0;i<link;i++)
- granulepos+=vf->pcmlengths[i*2+1];
- vf->pcm_offset=granulepos-accblock;
- break;
- }
- lastblock=thisblock;
- continue;
}
}
}
/* has our decoding just traversed a bitstream boundary? */
- if(vf->ready_state==STREAMSET)
+ if(vf->ready_state>=STREAMSET)
if(vf->current_serialno!=ogg_page_serialno(&og)){
- _decode_clear(vf); /* clear out stream state */
- ogg_stream_clear(&work_os);
- }
+ _decode_clear(vf); /* clear out stream state */
+ ogg_stream_clear(&work_os);
+ }
if(vf->ready_state<STREAMSET){
int link;
}
ogg_stream_clear(&work_os);
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
return(0);
seek_error:
{
ogg_page og;
ogg_packet op;
- /* clear out decoding machine state */
- _decode_clear(vf);
+
/* seek */
_seek_helper(vf,best);
+ vf->pcm_offset=-1;
if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* shouldn't happen */
- vf->current_serialno=ogg_page_serialno(&og);
- vf->current_link=link;
+ if(link!=vf->current_link){
+ /* Different link; dump entire decode machine */
+ _decode_clear(vf);
+
+ vf->current_link=link;
+ vf->current_serialno=ogg_page_serialno(&og);
+ vf->ready_state=STREAMSET;
+
+ }else{
+ vorbis_synthesis_restart(&vf->vd);
+ }
+
ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
- vf->ready_state=STREAMSET;
ogg_stream_pagein(&vf->os,&og);
/* pull out all but last packet; the one with granulepos */
preceeding page. Keep fetching previous pages until we
get one with a granulepos or without the 'continued' flag
set. Then just use raw_seek for simplicity. */
-
- _decode_clear(vf);
+
_seek_helper(vf,best);
-
+
while(1){
result=_get_prev_page(vf,&og);
if(result<0) goto seek_error;
}
}
if(result<0){
- result = OV_EBADPACKET;
- goto seek_error;
- }
+ result = OV_EBADPACKET;
+ goto seek_error;
+ }
if(op.granulepos!=-1){
vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
if(vf->pcm_offset<0)vf->pcm_offset=0;
result=OV_EFAULT;
goto seek_error;
}
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
return(0);
seek_error:
int thisblock,lastblock=0;
int ret=ov_pcm_seek_page(vf,pos);
if(ret<0)return(ret);
- _make_decode_ready(vf);
+ if((ret=_make_decode_ready(vf)))return ret;
/* discard leading packets we don't need for the lapping of the
position we want; don't decode them */
int ret=ogg_stream_packetpeek(&vf->os,&op);
if(ret>0){
thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
- if(thisblock<0)thisblock=0; /* non audio packet */
+ if(thisblock<0){
+ ogg_stream_packetout(&vf->os,NULL);
+ continue; /* non audio packet */
+ }
if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
if(vf->pcm_offset+((thisblock+
ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
vf->ready_state=STREAMSET;
- _make_decode_ready(vf);
+ ret=_make_decode_ready(vf);
+ if(ret)return ret;
lastblock=0;
}
}
}
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
/* discard samples until we reach the desired position. Crossing a
logical bitstream boundary with abandon is OK. */
while(vf->pcm_offset<pos){
/* suck in another packet */
{
int ret=_fetch_and_process_packet(vf,NULL,1,1);
- if(ret==OV_EOF)return(0);
- if(ret<=0)return(ret);
+ if(ret==OV_EOF)
+ return(0);
+ if(ret<=0)
+ return(ret);
}
}
}
extern float *vorbis_window(vorbis_dsp_state *v,int W);
+extern void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB,
+ ogg_int64_t off);
-static void _vorbis_splice(float *d,float *s,
- vorbis_dsp_state *v,int W){
-
- vorbis_info *vi=v->vi;
- int n=vorbis_info_blocksize(vi,0)/2;
- float *w=vorbis_window(v,W);
- int i;
+static void _ov_splice(float **pcm,float **lappcm,
+ int n1, int n2,
+ int ch1, int ch2,
+ float *w1, float *w2){
+ int i,j;
+ float *w=w1;
+ int n=n1;
- for(i=0;i<n;i++){
- float wd=w[i]*w[i];
- float ws=1.-wd;
- d[i]=d[i]*wd + s[i]*ws;
+ if(n1>n2){
+ n=n2;
+ w=w2;
}
-}
-
-
-/* this sets up crosslapping of a sample by using trailing data from
- sample 1 and lapping it into the windowing buffer of the second */
-
-int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
- vorbis_info *vi1,*vi2;
- vorbis_dsp_state *vd1=&vf1->vd;
- vorbis_dsp_state *vd2=&vf2->vd;
- float **lappcm;
- float **pcm;
- vorbis_dsp_state *winstate;
- int lapsize,lapcount=0,i,j;
-
- /* first enforce some level of sanity... */
- /* we need to know that sample rate & channels match. To do that,
- we need to verify the streams are actually initialized; the call
- is not just to be used for end-to-beginning lapping */
+ /* splice */
+ for(j=0;j<ch1 && j<ch2;j++){
+ float *s=lappcm[j];
+ float *d=pcm[j];
- if(vf1->ready_state<OPENED)return(OV_EINVAL);
- if(vf2->ready_state<OPENED)return(OV_EINVAL);
+ for(i=0;i<n;i++){
+ float wd=w[i]*w[i];
+ float ws=1.-wd;
+ d[i]=d[i]*wd + s[i]*ws;
+ }
+ }
+ /* window from zero */
+ for(;j<ch2;j++){
+ float *d=pcm[j];
+ for(i=0;i<n;i++){
+ float wd=w[i]*w[i];
+ d[i]=d[i]*wd;
+ }
+ }
- /* make sure vf1 is INITSET */
+}
+
+/* make sure vf is INITSET */
+static int _ov_initset(OggVorbis_File *vf){
while(1){
- if(vf1->ready_state==INITSET)break;
+ if(vf->ready_state==INITSET)break;
/* suck in another packet */
{
- int ret=_fetch_and_process_packet(vf1,NULL,1,0);
- if(ret<0)return(ret);
+ int ret=_fetch_and_process_packet(vf,NULL,1,0);
+ if(ret<0 && ret!=OV_HOLE)return(ret);
}
}
+ return 0;
+}
- /* make sure vf2 is INITSET and that we have a primed buffer; if
- we're crosslapping at a stream section boundary, this also makes
- sure we're sanity checking against the right stream information */
-
+/* make sure vf is INITSET and that we have a primed buffer; if
+ we're crosslapping at a stream section boundary, this also makes
+ sure we're sanity checking against the right stream information */
+static int _ov_initprime(OggVorbis_File *vf){
+ vorbis_dsp_state *vd=&vf->vd;
while(1){
- if(vf2->ready_state==INITSET)
- if(vorbis_synthesis_pcmout(vd2,NULL))break;
-
+ if(vf->ready_state==INITSET)
+ if(vorbis_synthesis_pcmout(vd,NULL))break;
+
/* suck in another packet */
{
- int ret=_fetch_and_process_packet(vf2,NULL,1,0);
- if(ret<0)return(ret);
+ int ret=_fetch_and_process_packet(vf,NULL,1,0);
+ if(ret<0 && ret!=OV_HOLE)return(ret);
}
- }
-
- /* sanity-check settings */
- vi1=ov_info(vf1,-1);
- vi2=ov_info(vf2,-1);
- if(vi1->channels != vi2->channels ||
- vi1->rate != vi2->rate) return OV_EINVAL;
-
- /* begin by grabbing enough data for lapping from vf1; this may be
- in the form of unreturned, already-decoded pcm, remaining PCM we
- will need to decode, or synthetic postextrapolation from last
- packets. */
-
- lappcm=alloca(sizeof(*lappcm)*vi1->channels);
- lapsize=vorbis_info_blocksize(vi1,0);
- if(vorbis_info_blocksize(vi2,0)<lapsize){
- lapsize=vorbis_info_blocksize(vi2,0)/2;
- winstate=vd2;
- }else{
- lapsize/=2;
- winstate=vd1;
- }
+ }
+ return 0;
+}
- for(i=0;i<vi1->channels;i++)
- lappcm[i]=alloca(sizeof(**lappcm)*lapsize);
+/* grab enough data for lapping from vf; this may be in the form of
+ unreturned, already-decoded pcm, remaining PCM we will need to
+ decode, or synthetic postextrapolation from last packets. */
+static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd,
+ float **lappcm,int lapsize){
+ int lapcount=0,i;
+ float **pcm;
/* try first to decode the lapping data */
while(lapcount<lapsize){
- int samples=vorbis_synthesis_pcmout(vd1,&pcm);
+ int samples=vorbis_synthesis_pcmout(vd,&pcm);
if(samples){
if(samples>lapsize-lapcount)samples=lapsize-lapcount;
- for(i=0;i<vi1->channels;i++)
+ for(i=0;i<vi->channels;i++)
memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
lapcount+=samples;
- vorbis_synthesis_read(vd1,samples);
+ vorbis_synthesis_read(vd,samples);
}else{
/* suck in another packet */
- int ret=_fetch_and_process_packet(vf1,NULL,1,0); /* do *not* span */
+ int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */
if(ret==OV_EOF)break;
}
}
/* failed to get lapping data from normal decode; pry it from the
postextrapolation buffering, or the second half of the MDCT
from the last packet */
- int samples=vorbis_synthesis_lapout(&vf1->vd,&pcm);
- if(samples>lapsize-lapcount)samples=lapsize-lapcount;
- for(i=0;i<vi1->channels;i++)
- memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
- lapcount+=samples;
-
- if(lapcount<lapsize)return OV_EFAULT;
+ int samples=vorbis_synthesis_lapout(&vf->vd,&pcm);
+ if(samples==0){
+ for(i=0;i<vi->channels;i++)
+ memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount);
+ lapcount=lapsize;
+ }else{
+ if(samples>lapsize-lapcount)samples=lapsize-lapcount;
+ for(i=0;i<vi->channels;i++)
+ memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
+ lapcount+=samples;
+ }
}
+}
+
+/* this sets up crosslapping of a sample by using trailing data from
+ sample 1 and lapping it into the windowing buffer of sample 2 */
+int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
+ vorbis_info *vi1,*vi2;
+ float **lappcm;
+ float **pcm;
+ float *w1,*w2;
+ int n1,n2,i,ret,hs1,hs2;
+
+ if(vf1==vf2)return(0); /* degenerate case */
+ if(vf1->ready_state<OPENED)return(OV_EINVAL);
+ if(vf2->ready_state<OPENED)return(OV_EINVAL);
+
+ /* the relevant overlap buffers must be pre-checked and pre-primed
+ before looking at settings in the event that priming would cross
+ a bitstream boundary. So, do it now */
+
+ ret=_ov_initset(vf1);
+ if(ret)return(ret);
+ ret=_ov_initprime(vf2);
+ if(ret)return(ret);
+
+ vi1=ov_info(vf1,-1);
+ vi2=ov_info(vf2,-1);
+ hs1=ov_halfrate_p(vf1);
+ hs2=ov_halfrate_p(vf2);
+
+ lappcm=alloca(sizeof(*lappcm)*vi1->channels);
+ n1=vorbis_info_blocksize(vi1,0)>>(1+hs1);
+ n2=vorbis_info_blocksize(vi2,0)>>(1+hs2);
+ w1=vorbis_window(&vf1->vd,0);
+ w2=vorbis_window(&vf2->vd,0);
+
+ for(i=0;i<vi1->channels;i++)
+ lappcm[i]=alloca(sizeof(**lappcm)*n1);
+
+ _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1);
/* have a lapping buffer from vf1; now to splice it into the lapping
buffer of vf2 */
+ /* consolidate and expose the buffer. */
+ vorbis_synthesis_lapout(&vf2->vd,&pcm);
+ _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0);
+ _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0);
+
+ /* splice */
+ _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2);
+
+ /* done */
+ return(0);
+}
+
+static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos,
+ int (*localseek)(OggVorbis_File *,ogg_int64_t)){
+ vorbis_info *vi;
+ float **lappcm;
+ float **pcm;
+ float *w1,*w2;
+ int n1,n2,ch1,ch2,hs;
+ int i,ret;
+
+ if(vf->ready_state<OPENED)return(OV_EINVAL);
+ ret=_ov_initset(vf);
+ if(ret)return(ret);
+ vi=ov_info(vf,-1);
+ hs=ov_halfrate_p(vf);
+
+ ch1=vi->channels;
+ n1=vorbis_info_blocksize(vi,0)>>(1+hs);
+ w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
+ persistent; even if the decode state
+ from this link gets dumped, this
+ window array continues to exist */
+
+ lappcm=alloca(sizeof(*lappcm)*ch1);
+ for(i=0;i<ch1;i++)
+ lappcm[i]=alloca(sizeof(**lappcm)*n1);
+ _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
+
+ /* have lapping data; seek and prime the buffer */
+ ret=localseek(vf,pos);
+ if(ret)return ret;
+ ret=_ov_initprime(vf);
+ if(ret)return(ret);
+
+ /* Guard against cross-link changes; they're perfectly legal */
+ vi=ov_info(vf,-1);
+ ch2=vi->channels;
+ n2=vorbis_info_blocksize(vi,0)>>(1+hs);
+ w2=vorbis_window(&vf->vd,0);
/* consolidate and expose the buffer. */
- if(vorbis_synthesis_lapout(vd2,&pcm)<lapsize)return OV_EFAULT;
+ vorbis_synthesis_lapout(&vf->vd,&pcm);
/* splice */
- for(j=0;j<vi1->channels;j++){
- float *s=lappcm[j];
- float *d=pcm[j];
- _vorbis_splice(d,s,winstate,0);
- }
+ _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
+
+ /* done */
+ return(0);
+}
+
+int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
+ return _ov_64_seek_lap(vf,pos,ov_raw_seek);
+}
+
+int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
+ return _ov_64_seek_lap(vf,pos,ov_pcm_seek);
+}
+
+int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){
+ return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page);
+}
+
+static int _ov_d_seek_lap(OggVorbis_File *vf,double pos,
+ int (*localseek)(OggVorbis_File *,double)){
+ vorbis_info *vi;
+ float **lappcm;
+ float **pcm;
+ float *w1,*w2;
+ int n1,n2,ch1,ch2,hs;
+ int i,ret;
+
+ if(vf->ready_state<OPENED)return(OV_EINVAL);
+ ret=_ov_initset(vf);
+ if(ret)return(ret);
+ vi=ov_info(vf,-1);
+ hs=ov_halfrate_p(vf);
+
+ ch1=vi->channels;
+ n1=vorbis_info_blocksize(vi,0)>>(1+hs);
+ w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
+ persistent; even if the decode state
+ from this link gets dumped, this
+ window array continues to exist */
+
+ lappcm=alloca(sizeof(*lappcm)*ch1);
+ for(i=0;i<ch1;i++)
+ lappcm[i]=alloca(sizeof(**lappcm)*n1);
+ _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
+
+ /* have lapping data; seek and prime the buffer */
+ ret=localseek(vf,pos);
+ if(ret)return ret;
+ ret=_ov_initprime(vf);
+ if(ret)return(ret);
+
+ /* Guard against cross-link changes; they're perfectly legal */
+ vi=ov_info(vf,-1);
+ ch2=vi->channels;
+ n2=vorbis_info_blocksize(vi,0)>>(1+hs);
+ w2=vorbis_window(&vf->vd,0);
+
+ /* consolidate and expose the buffer. */
+ vorbis_synthesis_lapout(&vf->vd,&pcm);
+
+ /* splice */
+ _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
/* done */
return(0);
}
+int ov_time_seek_lap(OggVorbis_File *vf,double pos){
+ return _ov_d_seek_lap(vf,pos,ov_time_seek);
+}
+int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){
+ return _ov_d_seek_lap(vf,pos,ov_time_seek_page);
+}