vrshoot

annotate libs/vorbis/vorbisfile.c @ 1:e7ca128b8713

looks nice :)
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 02 Feb 2014 00:35:22 +0200
parents
children
rev   line source
nuclear@0 1 /********************************************************************
nuclear@0 2 * *
nuclear@0 3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
nuclear@0 4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
nuclear@0 5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
nuclear@0 6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
nuclear@0 7 * *
nuclear@0 8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
nuclear@0 9 * by the Xiph.Org Foundation http://www.xiph.org/ *
nuclear@0 10 * *
nuclear@0 11 ********************************************************************
nuclear@0 12
nuclear@0 13 function: stdio-based convenience library for opening/seeking/decoding
nuclear@0 14 last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
nuclear@0 15
nuclear@0 16 ********************************************************************/
nuclear@0 17
nuclear@0 18 #include <stdlib.h>
nuclear@0 19 #include <stdio.h>
nuclear@0 20 #include <errno.h>
nuclear@0 21 #include <string.h>
nuclear@0 22 #include <math.h>
nuclear@0 23
nuclear@0 24 #include "vorbis/codec.h"
nuclear@0 25
nuclear@0 26 /* we don't need or want the static callback symbols here */
nuclear@0 27 #define OV_EXCLUDE_STATIC_CALLBACKS
nuclear@0 28 #include "vorbis/vorbisfile.h"
nuclear@0 29
nuclear@0 30 #include "os.h"
nuclear@0 31 #include "misc.h"
nuclear@0 32
nuclear@0 33 /* A 'chained bitstream' is a Vorbis bitstream that contains more than
nuclear@0 34 one logical bitstream arranged end to end (the only form of Ogg
nuclear@0 35 multiplexing allowed in a Vorbis bitstream; grouping [parallel
nuclear@0 36 multiplexing] is not allowed in Vorbis) */
nuclear@0 37
nuclear@0 38 /* A Vorbis file can be played beginning to end (streamed) without
nuclear@0 39 worrying ahead of time about chaining (see decoder_example.c). If
nuclear@0 40 we have the whole file, however, and want random access
nuclear@0 41 (seeking/scrubbing) or desire to know the total length/time of a
nuclear@0 42 file, we need to account for the possibility of chaining. */
nuclear@0 43
nuclear@0 44 /* We can handle things a number of ways; we can determine the entire
nuclear@0 45 bitstream structure right off the bat, or find pieces on demand.
nuclear@0 46 This example determines and caches structure for the entire
nuclear@0 47 bitstream, but builds a virtual decoder on the fly when moving
nuclear@0 48 between links in the chain. */
nuclear@0 49
nuclear@0 50 /* There are also different ways to implement seeking. Enough
nuclear@0 51 information exists in an Ogg bitstream to seek to
nuclear@0 52 sample-granularity positions in the output. Or, one can seek by
nuclear@0 53 picking some portion of the stream roughly in the desired area if
nuclear@0 54 we only want coarse navigation through the stream. */
nuclear@0 55
nuclear@0 56 /*************************************************************************
nuclear@0 57 * Many, many internal helpers. The intention is not to be confusing;
nuclear@0 58 * rampant duplication and monolithic function implementation would be
nuclear@0 59 * harder to understand anyway. The high level functions are last. Begin
nuclear@0 60 * grokking near the end of the file */
nuclear@0 61
nuclear@0 62 /* read a little more data from the file/pipe into the ogg_sync framer
nuclear@0 63 */
nuclear@0 64 #define CHUNKSIZE 65536 /* greater-than-page-size granularity seeking */
nuclear@0 65 #define READSIZE 2048 /* a smaller read size is needed for low-rate streaming. */
nuclear@0 66
nuclear@0 67 static long _get_data(OggVorbis_File *vf){
nuclear@0 68 errno=0;
nuclear@0 69 if(!(vf->callbacks.read_func))return(-1);
nuclear@0 70 if(vf->datasource){
nuclear@0 71 char *buffer=ogg_sync_buffer(&vf->oy,READSIZE);
nuclear@0 72 long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource);
nuclear@0 73 if(bytes>0)ogg_sync_wrote(&vf->oy,bytes);
nuclear@0 74 if(bytes==0 && errno)return(-1);
nuclear@0 75 return(bytes);
nuclear@0 76 }else
nuclear@0 77 return(0);
nuclear@0 78 }
nuclear@0 79
nuclear@0 80 /* save a tiny smidge of verbosity to make the code more readable */
nuclear@0 81 static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
nuclear@0 82 if(vf->datasource){
nuclear@0 83 if(!(vf->callbacks.seek_func)||
nuclear@0 84 (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1)
nuclear@0 85 return OV_EREAD;
nuclear@0 86 vf->offset=offset;
nuclear@0 87 ogg_sync_reset(&vf->oy);
nuclear@0 88 }else{
nuclear@0 89 /* shouldn't happen unless someone writes a broken callback */
nuclear@0 90 return OV_EFAULT;
nuclear@0 91 }
nuclear@0 92 return 0;
nuclear@0 93 }
nuclear@0 94
nuclear@0 95 /* The read/seek functions track absolute position within the stream */
nuclear@0 96
nuclear@0 97 /* from the head of the stream, get the next page. boundary specifies
nuclear@0 98 if the function is allowed to fetch more data from the stream (and
nuclear@0 99 how much) or only use internally buffered data.
nuclear@0 100
nuclear@0 101 boundary: -1) unbounded search
nuclear@0 102 0) read no additional data; use cached only
nuclear@0 103 n) search for a new page beginning for n bytes
nuclear@0 104
nuclear@0 105 return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
nuclear@0 106 n) found a page at absolute offset n */
nuclear@0 107
nuclear@0 108 static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og,
nuclear@0 109 ogg_int64_t boundary){
nuclear@0 110 if(boundary>0)boundary+=vf->offset;
nuclear@0 111 while(1){
nuclear@0 112 long more;
nuclear@0 113
nuclear@0 114 if(boundary>0 && vf->offset>=boundary)return(OV_FALSE);
nuclear@0 115 more=ogg_sync_pageseek(&vf->oy,og);
nuclear@0 116
nuclear@0 117 if(more<0){
nuclear@0 118 /* skipped n bytes */
nuclear@0 119 vf->offset-=more;
nuclear@0 120 }else{
nuclear@0 121 if(more==0){
nuclear@0 122 /* send more paramedics */
nuclear@0 123 if(!boundary)return(OV_FALSE);
nuclear@0 124 {
nuclear@0 125 long ret=_get_data(vf);
nuclear@0 126 if(ret==0)return(OV_EOF);
nuclear@0 127 if(ret<0)return(OV_EREAD);
nuclear@0 128 }
nuclear@0 129 }else{
nuclear@0 130 /* got a page. Return the offset at the page beginning,
nuclear@0 131 advance the internal offset past the page end */
nuclear@0 132 ogg_int64_t ret=vf->offset;
nuclear@0 133 vf->offset+=more;
nuclear@0 134 return(ret);
nuclear@0 135
nuclear@0 136 }
nuclear@0 137 }
nuclear@0 138 }
nuclear@0 139 }
nuclear@0 140
nuclear@0 141 /* find the latest page beginning before the current stream cursor
nuclear@0 142 position. Much dirtier than the above as Ogg doesn't have any
nuclear@0 143 backward search linkage. no 'readp' as it will certainly have to
nuclear@0 144 read. */
nuclear@0 145 /* returns offset or OV_EREAD, OV_FAULT */
nuclear@0 146 static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
nuclear@0 147 ogg_int64_t begin=vf->offset;
nuclear@0 148 ogg_int64_t end=begin;
nuclear@0 149 ogg_int64_t ret;
nuclear@0 150 ogg_int64_t offset=-1;
nuclear@0 151
nuclear@0 152 while(offset==-1){
nuclear@0 153 begin-=CHUNKSIZE;
nuclear@0 154 if(begin<0)
nuclear@0 155 begin=0;
nuclear@0 156
nuclear@0 157 ret=_seek_helper(vf,begin);
nuclear@0 158 if(ret)return(ret);
nuclear@0 159
nuclear@0 160 while(vf->offset<end){
nuclear@0 161 memset(og,0,sizeof(*og));
nuclear@0 162 ret=_get_next_page(vf,og,end-vf->offset);
nuclear@0 163 if(ret==OV_EREAD)return(OV_EREAD);
nuclear@0 164 if(ret<0){
nuclear@0 165 break;
nuclear@0 166 }else{
nuclear@0 167 offset=ret;
nuclear@0 168 }
nuclear@0 169 }
nuclear@0 170 }
nuclear@0 171
nuclear@0 172 /* In a fully compliant, non-multiplexed stream, we'll still be
nuclear@0 173 holding the last page. In multiplexed (or noncompliant streams),
nuclear@0 174 we will probably have to re-read the last page we saw */
nuclear@0 175 if(og->header_len==0){
nuclear@0 176 ret=_seek_helper(vf,offset);
nuclear@0 177 if(ret)return(ret);
nuclear@0 178
nuclear@0 179 ret=_get_next_page(vf,og,CHUNKSIZE);
nuclear@0 180 if(ret<0)
nuclear@0 181 /* this shouldn't be possible */
nuclear@0 182 return(OV_EFAULT);
nuclear@0 183 }
nuclear@0 184
nuclear@0 185 return(offset);
nuclear@0 186 }
nuclear@0 187
nuclear@0 188 static void _add_serialno(ogg_page *og,long **serialno_list, int *n){
nuclear@0 189 long s = ogg_page_serialno(og);
nuclear@0 190 (*n)++;
nuclear@0 191
nuclear@0 192 if(*serialno_list){
nuclear@0 193 *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n));
nuclear@0 194 }else{
nuclear@0 195 *serialno_list = _ogg_malloc(sizeof(**serialno_list));
nuclear@0 196 }
nuclear@0 197
nuclear@0 198 (*serialno_list)[(*n)-1] = s;
nuclear@0 199 }
nuclear@0 200
nuclear@0 201 /* returns nonzero if found */
nuclear@0 202 static int _lookup_serialno(long s, long *serialno_list, int n){
nuclear@0 203 if(serialno_list){
nuclear@0 204 while(n--){
nuclear@0 205 if(*serialno_list == s) return 1;
nuclear@0 206 serialno_list++;
nuclear@0 207 }
nuclear@0 208 }
nuclear@0 209 return 0;
nuclear@0 210 }
nuclear@0 211
nuclear@0 212 static int _lookup_page_serialno(ogg_page *og, long *serialno_list, int n){
nuclear@0 213 long s = ogg_page_serialno(og);
nuclear@0 214 return _lookup_serialno(s,serialno_list,n);
nuclear@0 215 }
nuclear@0 216
nuclear@0 217 /* performs the same search as _get_prev_page, but prefers pages of
nuclear@0 218 the specified serial number. If a page of the specified serialno is
nuclear@0 219 spotted during the seek-back-and-read-forward, it will return the
nuclear@0 220 info of last page of the matching serial number instead of the very
nuclear@0 221 last page. If no page of the specified serialno is seen, it will
nuclear@0 222 return the info of last page and alter *serialno. */
nuclear@0 223 static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf,
nuclear@0 224 long *serial_list, int serial_n,
nuclear@0 225 int *serialno, ogg_int64_t *granpos){
nuclear@0 226 ogg_page og;
nuclear@0 227 ogg_int64_t begin=vf->offset;
nuclear@0 228 ogg_int64_t end=begin;
nuclear@0 229 ogg_int64_t ret;
nuclear@0 230
nuclear@0 231 ogg_int64_t prefoffset=-1;
nuclear@0 232 ogg_int64_t offset=-1;
nuclear@0 233 ogg_int64_t ret_serialno=-1;
nuclear@0 234 ogg_int64_t ret_gran=-1;
nuclear@0 235
nuclear@0 236 while(offset==-1){
nuclear@0 237 begin-=CHUNKSIZE;
nuclear@0 238 if(begin<0)
nuclear@0 239 begin=0;
nuclear@0 240
nuclear@0 241 ret=_seek_helper(vf,begin);
nuclear@0 242 if(ret)return(ret);
nuclear@0 243
nuclear@0 244 while(vf->offset<end){
nuclear@0 245 ret=_get_next_page(vf,&og,end-vf->offset);
nuclear@0 246 if(ret==OV_EREAD)return(OV_EREAD);
nuclear@0 247 if(ret<0){
nuclear@0 248 break;
nuclear@0 249 }else{
nuclear@0 250 ret_serialno=ogg_page_serialno(&og);
nuclear@0 251 ret_gran=ogg_page_granulepos(&og);
nuclear@0 252 offset=ret;
nuclear@0 253
nuclear@0 254 if(ret_serialno == *serialno){
nuclear@0 255 prefoffset=ret;
nuclear@0 256 *granpos=ret_gran;
nuclear@0 257 }
nuclear@0 258
nuclear@0 259 if(!_lookup_serialno(ret_serialno,serial_list,serial_n)){
nuclear@0 260 /* we fell off the end of the link, which means we seeked
nuclear@0 261 back too far and shouldn't have been looking in that link
nuclear@0 262 to begin with. If we found the preferred serial number,
nuclear@0 263 forget that we saw it. */
nuclear@0 264 prefoffset=-1;
nuclear@0 265 }
nuclear@0 266 }
nuclear@0 267 }
nuclear@0 268 }
nuclear@0 269
nuclear@0 270 /* we're not interested in the page... just the serialno and granpos. */
nuclear@0 271 if(prefoffset>=0)return(prefoffset);
nuclear@0 272
nuclear@0 273 *serialno = ret_serialno;
nuclear@0 274 *granpos = ret_gran;
nuclear@0 275 return(offset);
nuclear@0 276
nuclear@0 277 }
nuclear@0 278
nuclear@0 279 /* uses the local ogg_stream storage in vf; this is important for
nuclear@0 280 non-streaming input sources */
nuclear@0 281 static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
nuclear@0 282 long **serialno_list, int *serialno_n,
nuclear@0 283 ogg_page *og_ptr){
nuclear@0 284 ogg_page og;
nuclear@0 285 ogg_packet op;
nuclear@0 286 int i,ret;
nuclear@0 287 int allbos=0;
nuclear@0 288
nuclear@0 289 if(!og_ptr){
nuclear@0 290 ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
nuclear@0 291 if(llret==OV_EREAD)return(OV_EREAD);
nuclear@0 292 if(llret<0)return(OV_ENOTVORBIS);
nuclear@0 293 og_ptr=&og;
nuclear@0 294 }
nuclear@0 295
nuclear@0 296 vorbis_info_init(vi);
nuclear@0 297 vorbis_comment_init(vc);
nuclear@0 298 vf->ready_state=OPENED;
nuclear@0 299
nuclear@0 300 /* extract the serialnos of all BOS pages + the first set of vorbis
nuclear@0 301 headers we see in the link */
nuclear@0 302
nuclear@0 303 while(ogg_page_bos(og_ptr)){
nuclear@0 304 if(serialno_list){
nuclear@0 305 if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){
nuclear@0 306 /* a dupe serialnumber in an initial header packet set == invalid stream */
nuclear@0 307 if(*serialno_list)_ogg_free(*serialno_list);
nuclear@0 308 *serialno_list=0;
nuclear@0 309 *serialno_n=0;
nuclear@0 310 ret=OV_EBADHEADER;
nuclear@0 311 goto bail_header;
nuclear@0 312 }
nuclear@0 313
nuclear@0 314 _add_serialno(og_ptr,serialno_list,serialno_n);
nuclear@0 315 }
nuclear@0 316
nuclear@0 317 if(vf->ready_state<STREAMSET){
nuclear@0 318 /* we don't have a vorbis stream in this link yet, so begin
nuclear@0 319 prospective stream setup. We need a stream to get packets */
nuclear@0 320 ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr));
nuclear@0 321 ogg_stream_pagein(&vf->os,og_ptr);
nuclear@0 322
nuclear@0 323 if(ogg_stream_packetout(&vf->os,&op) > 0 &&
nuclear@0 324 vorbis_synthesis_idheader(&op)){
nuclear@0 325 /* vorbis header; continue setup */
nuclear@0 326 vf->ready_state=STREAMSET;
nuclear@0 327 if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
nuclear@0 328 ret=OV_EBADHEADER;
nuclear@0 329 goto bail_header;
nuclear@0 330 }
nuclear@0 331 }
nuclear@0 332 }
nuclear@0 333
nuclear@0 334 /* get next page */
nuclear@0 335 {
nuclear@0 336 ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE);
nuclear@0 337 if(llret==OV_EREAD){
nuclear@0 338 ret=OV_EREAD;
nuclear@0 339 goto bail_header;
nuclear@0 340 }
nuclear@0 341 if(llret<0){
nuclear@0 342 ret=OV_ENOTVORBIS;
nuclear@0 343 goto bail_header;
nuclear@0 344 }
nuclear@0 345
nuclear@0 346 /* if this page also belongs to our vorbis stream, submit it and break */
nuclear@0 347 if(vf->ready_state==STREAMSET &&
nuclear@0 348 vf->os.serialno == ogg_page_serialno(og_ptr)){
nuclear@0 349 ogg_stream_pagein(&vf->os,og_ptr);
nuclear@0 350 break;
nuclear@0 351 }
nuclear@0 352 }
nuclear@0 353 }
nuclear@0 354
nuclear@0 355 if(vf->ready_state!=STREAMSET){
nuclear@0 356 ret = OV_ENOTVORBIS;
nuclear@0 357 goto bail_header;
nuclear@0 358 }
nuclear@0 359
nuclear@0 360 while(1){
nuclear@0 361
nuclear@0 362 i=0;
nuclear@0 363 while(i<2){ /* get a page loop */
nuclear@0 364
nuclear@0 365 while(i<2){ /* get a packet loop */
nuclear@0 366
nuclear@0 367 int result=ogg_stream_packetout(&vf->os,&op);
nuclear@0 368 if(result==0)break;
nuclear@0 369 if(result==-1){
nuclear@0 370 ret=OV_EBADHEADER;
nuclear@0 371 goto bail_header;
nuclear@0 372 }
nuclear@0 373
nuclear@0 374 if((ret=vorbis_synthesis_headerin(vi,vc,&op)))
nuclear@0 375 goto bail_header;
nuclear@0 376
nuclear@0 377 i++;
nuclear@0 378 }
nuclear@0 379
nuclear@0 380 while(i<2){
nuclear@0 381 if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){
nuclear@0 382 ret=OV_EBADHEADER;
nuclear@0 383 goto bail_header;
nuclear@0 384 }
nuclear@0 385
nuclear@0 386 /* if this page belongs to the correct stream, go parse it */
nuclear@0 387 if(vf->os.serialno == ogg_page_serialno(og_ptr)){
nuclear@0 388 ogg_stream_pagein(&vf->os,og_ptr);
nuclear@0 389 break;
nuclear@0 390 }
nuclear@0 391
nuclear@0 392 /* if we never see the final vorbis headers before the link
nuclear@0 393 ends, abort */
nuclear@0 394 if(ogg_page_bos(og_ptr)){
nuclear@0 395 if(allbos){
nuclear@0 396 ret = OV_EBADHEADER;
nuclear@0 397 goto bail_header;
nuclear@0 398 }else
nuclear@0 399 allbos=1;
nuclear@0 400 }
nuclear@0 401
nuclear@0 402 /* otherwise, keep looking */
nuclear@0 403 }
nuclear@0 404 }
nuclear@0 405
nuclear@0 406 return 0;
nuclear@0 407 }
nuclear@0 408
nuclear@0 409 bail_header:
nuclear@0 410 vorbis_info_clear(vi);
nuclear@0 411 vorbis_comment_clear(vc);
nuclear@0 412 vf->ready_state=OPENED;
nuclear@0 413
nuclear@0 414 return ret;
nuclear@0 415 }
nuclear@0 416
nuclear@0 417 /* Starting from current cursor position, get initial PCM offset of
nuclear@0 418 next page. Consumes the page in the process without decoding
nuclear@0 419 audio, however this is only called during stream parsing upon
nuclear@0 420 seekable open. */
nuclear@0 421 static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){
nuclear@0 422 ogg_page og;
nuclear@0 423 ogg_int64_t accumulated=0;
nuclear@0 424 long lastblock=-1;
nuclear@0 425 int result;
nuclear@0 426 int serialno = vf->os.serialno;
nuclear@0 427
nuclear@0 428 while(1){
nuclear@0 429 ogg_packet op;
nuclear@0 430 if(_get_next_page(vf,&og,-1)<0)
nuclear@0 431 break; /* should not be possible unless the file is truncated/mangled */
nuclear@0 432
nuclear@0 433 if(ogg_page_bos(&og)) break;
nuclear@0 434 if(ogg_page_serialno(&og)!=serialno) continue;
nuclear@0 435
nuclear@0 436 /* count blocksizes of all frames in the page */
nuclear@0 437 ogg_stream_pagein(&vf->os,&og);
nuclear@0 438 while((result=ogg_stream_packetout(&vf->os,&op))){
nuclear@0 439 if(result>0){ /* ignore holes */
nuclear@0 440 long thisblock=vorbis_packet_blocksize(vi,&op);
nuclear@0 441 if(lastblock!=-1)
nuclear@0 442 accumulated+=(lastblock+thisblock)>>2;
nuclear@0 443 lastblock=thisblock;
nuclear@0 444 }
nuclear@0 445 }
nuclear@0 446
nuclear@0 447 if(ogg_page_granulepos(&og)!=-1){
nuclear@0 448 /* pcm offset of last packet on the first audio page */
nuclear@0 449 accumulated= ogg_page_granulepos(&og)-accumulated;
nuclear@0 450 break;
nuclear@0 451 }
nuclear@0 452 }
nuclear@0 453
nuclear@0 454 /* less than zero? Either a corrupt file or a stream with samples
nuclear@0 455 trimmed off the beginning, a normal occurrence; in both cases set
nuclear@0 456 the offset to zero */
nuclear@0 457 if(accumulated<0)accumulated=0;
nuclear@0 458
nuclear@0 459 return accumulated;
nuclear@0 460 }
nuclear@0 461
nuclear@0 462 /* finds each bitstream link one at a time using a bisection search
nuclear@0 463 (has to begin by knowing the offset of the lb's initial page).
nuclear@0 464 Recurses for each link so it can alloc the link storage after
nuclear@0 465 finding them all, then unroll and fill the cache at the same time */
nuclear@0 466 static int _bisect_forward_serialno(OggVorbis_File *vf,
nuclear@0 467 ogg_int64_t begin,
nuclear@0 468 ogg_int64_t searched,
nuclear@0 469 ogg_int64_t end,
nuclear@0 470 ogg_int64_t endgran,
nuclear@0 471 int endserial,
nuclear@0 472 long *currentno_list,
nuclear@0 473 int currentnos,
nuclear@0 474 long m){
nuclear@0 475 ogg_int64_t pcmoffset;
nuclear@0 476 ogg_int64_t dataoffset=searched;
nuclear@0 477 ogg_int64_t endsearched=end;
nuclear@0 478 ogg_int64_t next=end;
nuclear@0 479 ogg_int64_t searchgran=-1;
nuclear@0 480 ogg_page og;
nuclear@0 481 ogg_int64_t ret,last;
nuclear@0 482 int serialno = vf->os.serialno;
nuclear@0 483
nuclear@0 484 /* invariants:
nuclear@0 485 we have the headers and serialnos for the link beginning at 'begin'
nuclear@0 486 we have the offset and granpos of the last page in the file (potentially
nuclear@0 487 not a page we care about)
nuclear@0 488 */
nuclear@0 489
nuclear@0 490 /* Is the last page in our list of current serialnumbers? */
nuclear@0 491 if(_lookup_serialno(endserial,currentno_list,currentnos)){
nuclear@0 492
nuclear@0 493 /* last page is in the starting serialno list, so we've bisected
nuclear@0 494 down to (or just started with) a single link. Now we need to
nuclear@0 495 find the last vorbis page belonging to the first vorbis stream
nuclear@0 496 for this link. */
nuclear@0 497
nuclear@0 498 while(endserial != serialno){
nuclear@0 499 endserial = serialno;
nuclear@0 500 vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&endserial,&endgran);
nuclear@0 501 }
nuclear@0 502
nuclear@0 503 vf->links=m+1;
nuclear@0 504 if(vf->offsets)_ogg_free(vf->offsets);
nuclear@0 505 if(vf->serialnos)_ogg_free(vf->serialnos);
nuclear@0 506 if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
nuclear@0 507
nuclear@0 508 vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
nuclear@0 509 vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
nuclear@0 510 vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
nuclear@0 511 vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
nuclear@0 512 vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
nuclear@0 513 vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
nuclear@0 514
nuclear@0 515 vf->offsets[m+1]=end;
nuclear@0 516 vf->offsets[m]=begin;
nuclear@0 517 vf->pcmlengths[m*2+1]=(endgran<0?0:endgran);
nuclear@0 518
nuclear@0 519 }else{
nuclear@0 520
nuclear@0 521 long *next_serialno_list=NULL;
nuclear@0 522 int next_serialnos=0;
nuclear@0 523 vorbis_info vi;
nuclear@0 524 vorbis_comment vc;
nuclear@0 525
nuclear@0 526 /* the below guards against garbage seperating the last and
nuclear@0 527 first pages of two links. */
nuclear@0 528 while(searched<endsearched){
nuclear@0 529 ogg_int64_t bisect;
nuclear@0 530
nuclear@0 531 if(endsearched-searched<CHUNKSIZE){
nuclear@0 532 bisect=searched;
nuclear@0 533 }else{
nuclear@0 534 bisect=(searched+endsearched)/2;
nuclear@0 535 }
nuclear@0 536
nuclear@0 537 if(bisect != vf->offset){
nuclear@0 538 ret=_seek_helper(vf,bisect);
nuclear@0 539 if(ret)return(ret);
nuclear@0 540 }
nuclear@0 541
nuclear@0 542 last=_get_next_page(vf,&og,-1);
nuclear@0 543 if(last==OV_EREAD)return(OV_EREAD);
nuclear@0 544 if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){
nuclear@0 545 endsearched=bisect;
nuclear@0 546 if(last>=0)next=last;
nuclear@0 547 }else{
nuclear@0 548 searched=vf->offset;
nuclear@0 549 }
nuclear@0 550 }
nuclear@0 551
nuclear@0 552 /* Bisection point found */
nuclear@0 553
nuclear@0 554 /* for the time being, fetch end PCM offset the simple way */
nuclear@0 555 {
nuclear@0 556 int testserial = serialno+1;
nuclear@0 557 vf->offset = next;
nuclear@0 558 while(testserial != serialno){
nuclear@0 559 testserial = serialno;
nuclear@0 560 vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&testserial,&searchgran);
nuclear@0 561 }
nuclear@0 562 }
nuclear@0 563
nuclear@0 564 if(vf->offset!=next){
nuclear@0 565 ret=_seek_helper(vf,next);
nuclear@0 566 if(ret)return(ret);
nuclear@0 567 }
nuclear@0 568
nuclear@0 569 ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL);
nuclear@0 570 if(ret)return(ret);
nuclear@0 571 serialno = vf->os.serialno;
nuclear@0 572 dataoffset = vf->offset;
nuclear@0 573
nuclear@0 574 /* this will consume a page, however the next bistection always
nuclear@0 575 starts with a raw seek */
nuclear@0 576 pcmoffset = _initial_pcmoffset(vf,&vi);
nuclear@0 577
nuclear@0 578 ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial,
nuclear@0 579 next_serialno_list,next_serialnos,m+1);
nuclear@0 580 if(ret)return(ret);
nuclear@0 581
nuclear@0 582 if(next_serialno_list)_ogg_free(next_serialno_list);
nuclear@0 583
nuclear@0 584 vf->offsets[m+1]=next;
nuclear@0 585 vf->serialnos[m+1]=serialno;
nuclear@0 586 vf->dataoffsets[m+1]=dataoffset;
nuclear@0 587
nuclear@0 588 vf->vi[m+1]=vi;
nuclear@0 589 vf->vc[m+1]=vc;
nuclear@0 590
nuclear@0 591 vf->pcmlengths[m*2+1]=searchgran;
nuclear@0 592 vf->pcmlengths[m*2+2]=pcmoffset;
nuclear@0 593 vf->pcmlengths[m*2+3]-=pcmoffset;
nuclear@0 594 if(vf->pcmlengths[m*2+3]<0)vf->pcmlengths[m*2+3]=0;
nuclear@0 595 }
nuclear@0 596 return(0);
nuclear@0 597 }
nuclear@0 598
nuclear@0 599 static int _make_decode_ready(OggVorbis_File *vf){
nuclear@0 600 if(vf->ready_state>STREAMSET)return 0;
nuclear@0 601 if(vf->ready_state<STREAMSET)return OV_EFAULT;
nuclear@0 602 if(vf->seekable){
nuclear@0 603 if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link))
nuclear@0 604 return OV_EBADLINK;
nuclear@0 605 }else{
nuclear@0 606 if(vorbis_synthesis_init(&vf->vd,vf->vi))
nuclear@0 607 return OV_EBADLINK;
nuclear@0 608 }
nuclear@0 609 vorbis_block_init(&vf->vd,&vf->vb);
nuclear@0 610 vf->ready_state=INITSET;
nuclear@0 611 vf->bittrack=0.f;
nuclear@0 612 vf->samptrack=0.f;
nuclear@0 613 return 0;
nuclear@0 614 }
nuclear@0 615
nuclear@0 616 static int _open_seekable2(OggVorbis_File *vf){
nuclear@0 617 ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1;
nuclear@0 618 int endserial=vf->os.serialno;
nuclear@0 619 int serialno=vf->os.serialno;
nuclear@0 620
nuclear@0 621 /* we're partially open and have a first link header state in
nuclear@0 622 storage in vf */
nuclear@0 623
nuclear@0 624 /* fetch initial PCM offset */
nuclear@0 625 ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi);
nuclear@0 626
nuclear@0 627 /* we can seek, so set out learning all about this file */
nuclear@0 628 if(vf->callbacks.seek_func && vf->callbacks.tell_func){
nuclear@0 629 (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
nuclear@0 630 vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
nuclear@0 631 }else{
nuclear@0 632 vf->offset=vf->end=-1;
nuclear@0 633 }
nuclear@0 634
nuclear@0 635 /* If seek_func is implemented, tell_func must also be implemented */
nuclear@0 636 if(vf->end==-1) return(OV_EINVAL);
nuclear@0 637
nuclear@0 638 /* Get the offset of the last page of the physical bitstream, or, if
nuclear@0 639 we're lucky the last vorbis page of this link as most OggVorbis
nuclear@0 640 files will contain a single logical bitstream */
nuclear@0 641 end=_get_prev_page_serial(vf,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran);
nuclear@0 642 if(end<0)return(end);
nuclear@0 643
nuclear@0 644 /* now determine bitstream structure recursively */
nuclear@0 645 if(_bisect_forward_serialno(vf,0,dataoffset,vf->offset,endgran,endserial,
nuclear@0 646 vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD);
nuclear@0 647
nuclear@0 648 vf->offsets[0]=0;
nuclear@0 649 vf->serialnos[0]=serialno;
nuclear@0 650 vf->dataoffsets[0]=dataoffset;
nuclear@0 651 vf->pcmlengths[0]=pcmoffset;
nuclear@0 652 vf->pcmlengths[1]-=pcmoffset;
nuclear@0 653 if(vf->pcmlengths[1]<0)vf->pcmlengths[1]=0;
nuclear@0 654
nuclear@0 655 return(ov_raw_seek(vf,dataoffset));
nuclear@0 656 }
nuclear@0 657
nuclear@0 658 /* clear out the current logical bitstream decoder */
nuclear@0 659 static void _decode_clear(OggVorbis_File *vf){
nuclear@0 660 vorbis_dsp_clear(&vf->vd);
nuclear@0 661 vorbis_block_clear(&vf->vb);
nuclear@0 662 vf->ready_state=OPENED;
nuclear@0 663 }
nuclear@0 664
nuclear@0 665 /* fetch and process a packet. Handles the case where we're at a
nuclear@0 666 bitstream boundary and dumps the decoding machine. If the decoding
nuclear@0 667 machine is unloaded, it loads it. It also keeps pcm_offset up to
nuclear@0 668 date (seek and read both use this. seek uses a special hack with
nuclear@0 669 readp).
nuclear@0 670
nuclear@0 671 return: <0) error, OV_HOLE (lost packet) or OV_EOF
nuclear@0 672 0) need more data (only if readp==0)
nuclear@0 673 1) got a packet
nuclear@0 674 */
nuclear@0 675
nuclear@0 676 static int _fetch_and_process_packet(OggVorbis_File *vf,
nuclear@0 677 ogg_packet *op_in,
nuclear@0 678 int readp,
nuclear@0 679 int spanp){
nuclear@0 680 ogg_page og;
nuclear@0 681
nuclear@0 682 /* handle one packet. Try to fetch it from current stream state */
nuclear@0 683 /* extract packets from page */
nuclear@0 684 while(1){
nuclear@0 685
nuclear@0 686 if(vf->ready_state==STREAMSET){
nuclear@0 687 int ret=_make_decode_ready(vf);
nuclear@0 688 if(ret<0)return ret;
nuclear@0 689 }
nuclear@0 690
nuclear@0 691 /* process a packet if we can. */
nuclear@0 692
nuclear@0 693 if(vf->ready_state==INITSET){
nuclear@0 694 int hs=vorbis_synthesis_halfrate_p(vf->vi);
nuclear@0 695
nuclear@0 696 while(1) {
nuclear@0 697 ogg_packet op;
nuclear@0 698 ogg_packet *op_ptr=(op_in?op_in:&op);
nuclear@0 699 int result=ogg_stream_packetout(&vf->os,op_ptr);
nuclear@0 700 ogg_int64_t granulepos;
nuclear@0 701
nuclear@0 702 op_in=NULL;
nuclear@0 703 if(result==-1)return(OV_HOLE); /* hole in the data. */
nuclear@0 704 if(result>0){
nuclear@0 705 /* got a packet. process it */
nuclear@0 706 granulepos=op_ptr->granulepos;
nuclear@0 707 if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy
nuclear@0 708 header handling. The
nuclear@0 709 header packets aren't
nuclear@0 710 audio, so if/when we
nuclear@0 711 submit them,
nuclear@0 712 vorbis_synthesis will
nuclear@0 713 reject them */
nuclear@0 714
nuclear@0 715 /* suck in the synthesis data and track bitrate */
nuclear@0 716 {
nuclear@0 717 int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL);
nuclear@0 718 /* for proper use of libvorbis within libvorbisfile,
nuclear@0 719 oldsamples will always be zero. */
nuclear@0 720 if(oldsamples)return(OV_EFAULT);
nuclear@0 721
nuclear@0 722 vorbis_synthesis_blockin(&vf->vd,&vf->vb);
nuclear@0 723 vf->samptrack+=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs);
nuclear@0 724 vf->bittrack+=op_ptr->bytes*8;
nuclear@0 725 }
nuclear@0 726
nuclear@0 727 /* update the pcm offset. */
nuclear@0 728 if(granulepos!=-1 && !op_ptr->e_o_s){
nuclear@0 729 int link=(vf->seekable?vf->current_link:0);
nuclear@0 730 int i,samples;
nuclear@0 731
nuclear@0 732 /* this packet has a pcm_offset on it (the last packet
nuclear@0 733 completed on a page carries the offset) After processing
nuclear@0 734 (above), we know the pcm position of the *last* sample
nuclear@0 735 ready to be returned. Find the offset of the *first*
nuclear@0 736
nuclear@0 737 As an aside, this trick is inaccurate if we begin
nuclear@0 738 reading anew right at the last page; the end-of-stream
nuclear@0 739 granulepos declares the last frame in the stream, and the
nuclear@0 740 last packet of the last page may be a partial frame.
nuclear@0 741 So, we need a previous granulepos from an in-sequence page
nuclear@0 742 to have a reference point. Thus the !op_ptr->e_o_s clause
nuclear@0 743 above */
nuclear@0 744
nuclear@0 745 if(vf->seekable && link>0)
nuclear@0 746 granulepos-=vf->pcmlengths[link*2];
nuclear@0 747 if(granulepos<0)granulepos=0; /* actually, this
nuclear@0 748 shouldn't be possible
nuclear@0 749 here unless the stream
nuclear@0 750 is very broken */
nuclear@0 751
nuclear@0 752 samples=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs);
nuclear@0 753
nuclear@0 754 granulepos-=samples;
nuclear@0 755 for(i=0;i<link;i++)
nuclear@0 756 granulepos+=vf->pcmlengths[i*2+1];
nuclear@0 757 vf->pcm_offset=granulepos;
nuclear@0 758 }
nuclear@0 759 return(1);
nuclear@0 760 }
nuclear@0 761 }
nuclear@0 762 else
nuclear@0 763 break;
nuclear@0 764 }
nuclear@0 765 }
nuclear@0 766
nuclear@0 767 if(vf->ready_state>=OPENED){
nuclear@0 768 ogg_int64_t ret;
nuclear@0 769
nuclear@0 770 while(1){
nuclear@0 771 /* the loop is not strictly necessary, but there's no sense in
nuclear@0 772 doing the extra checks of the larger loop for the common
nuclear@0 773 case in a multiplexed bistream where the page is simply
nuclear@0 774 part of a different logical bitstream; keep reading until
nuclear@0 775 we get one with the correct serialno */
nuclear@0 776
nuclear@0 777 if(!readp)return(0);
nuclear@0 778 if((ret=_get_next_page(vf,&og,-1))<0){
nuclear@0 779 return(OV_EOF); /* eof. leave unitialized */
nuclear@0 780 }
nuclear@0 781
nuclear@0 782 /* bitrate tracking; add the header's bytes here, the body bytes
nuclear@0 783 are done by packet above */
nuclear@0 784 vf->bittrack+=og.header_len*8;
nuclear@0 785
nuclear@0 786 if(vf->ready_state==INITSET){
nuclear@0 787 if(vf->current_serialno!=ogg_page_serialno(&og)){
nuclear@0 788
nuclear@0 789 /* two possibilities:
nuclear@0 790 1) our decoding just traversed a bitstream boundary
nuclear@0 791 2) another stream is multiplexed into this logical section */
nuclear@0 792
nuclear@0 793 if(ogg_page_bos(&og)){
nuclear@0 794 /* boundary case */
nuclear@0 795 if(!spanp)
nuclear@0 796 return(OV_EOF);
nuclear@0 797
nuclear@0 798 _decode_clear(vf);
nuclear@0 799
nuclear@0 800 if(!vf->seekable){
nuclear@0 801 vorbis_info_clear(vf->vi);
nuclear@0 802 vorbis_comment_clear(vf->vc);
nuclear@0 803 }
nuclear@0 804 break;
nuclear@0 805
nuclear@0 806 }else
nuclear@0 807 continue; /* possibility #2 */
nuclear@0 808 }
nuclear@0 809 }
nuclear@0 810
nuclear@0 811 break;
nuclear@0 812 }
nuclear@0 813 }
nuclear@0 814
nuclear@0 815 /* Do we need to load a new machine before submitting the page? */
nuclear@0 816 /* This is different in the seekable and non-seekable cases.
nuclear@0 817
nuclear@0 818 In the seekable case, we already have all the header
nuclear@0 819 information loaded and cached; we just initialize the machine
nuclear@0 820 with it and continue on our merry way.
nuclear@0 821
nuclear@0 822 In the non-seekable (streaming) case, we'll only be at a
nuclear@0 823 boundary if we just left the previous logical bitstream and
nuclear@0 824 we're now nominally at the header of the next bitstream
nuclear@0 825 */
nuclear@0 826
nuclear@0 827 if(vf->ready_state!=INITSET){
nuclear@0 828 int link;
nuclear@0 829
nuclear@0 830 if(vf->ready_state<STREAMSET){
nuclear@0 831 if(vf->seekable){
nuclear@0 832 long serialno = ogg_page_serialno(&og);
nuclear@0 833
nuclear@0 834 /* match the serialno to bitstream section. We use this rather than
nuclear@0 835 offset positions to avoid problems near logical bitstream
nuclear@0 836 boundaries */
nuclear@0 837
nuclear@0 838 for(link=0;link<vf->links;link++)
nuclear@0 839 if(vf->serialnos[link]==serialno)break;
nuclear@0 840
nuclear@0 841 if(link==vf->links) continue; /* not the desired Vorbis
nuclear@0 842 bitstream section; keep
nuclear@0 843 trying */
nuclear@0 844
nuclear@0 845 vf->current_serialno=serialno;
nuclear@0 846 vf->current_link=link;
nuclear@0 847
nuclear@0 848 ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
nuclear@0 849 vf->ready_state=STREAMSET;
nuclear@0 850
nuclear@0 851 }else{
nuclear@0 852 /* we're streaming */
nuclear@0 853 /* fetch the three header packets, build the info struct */
nuclear@0 854
nuclear@0 855 int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og);
nuclear@0 856 if(ret)return(ret);
nuclear@0 857 vf->current_serialno=vf->os.serialno;
nuclear@0 858 vf->current_link++;
nuclear@0 859 link=0;
nuclear@0 860 }
nuclear@0 861 }
nuclear@0 862 }
nuclear@0 863
nuclear@0 864 /* the buffered page is the data we want, and we're ready for it;
nuclear@0 865 add it to the stream state */
nuclear@0 866 ogg_stream_pagein(&vf->os,&og);
nuclear@0 867
nuclear@0 868 }
nuclear@0 869 }
nuclear@0 870
nuclear@0 871 /* if, eg, 64 bit stdio is configured by default, this will build with
nuclear@0 872 fseek64 */
nuclear@0 873 static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
nuclear@0 874 if(f==NULL)return(-1);
nuclear@0 875 return fseek(f,off,whence);
nuclear@0 876 }
nuclear@0 877
nuclear@0 878 static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial,
nuclear@0 879 long ibytes, ov_callbacks callbacks){
nuclear@0 880 int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1);
nuclear@0 881 long *serialno_list=NULL;
nuclear@0 882 int serialno_list_size=0;
nuclear@0 883 int ret;
nuclear@0 884
nuclear@0 885 memset(vf,0,sizeof(*vf));
nuclear@0 886 vf->datasource=f;
nuclear@0 887 vf->callbacks = callbacks;
nuclear@0 888
nuclear@0 889 /* init the framing state */
nuclear@0 890 ogg_sync_init(&vf->oy);
nuclear@0 891
nuclear@0 892 /* perhaps some data was previously read into a buffer for testing
nuclear@0 893 against other stream types. Allow initialization from this
nuclear@0 894 previously read data (especially as we may be reading from a
nuclear@0 895 non-seekable stream) */
nuclear@0 896 if(initial){
nuclear@0 897 char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
nuclear@0 898 memcpy(buffer,initial,ibytes);
nuclear@0 899 ogg_sync_wrote(&vf->oy,ibytes);
nuclear@0 900 }
nuclear@0 901
nuclear@0 902 /* can we seek? Stevens suggests the seek test was portable */
nuclear@0 903 if(offsettest!=-1)vf->seekable=1;
nuclear@0 904
nuclear@0 905 /* No seeking yet; Set up a 'single' (current) logical bitstream
nuclear@0 906 entry for partial open */
nuclear@0 907 vf->links=1;
nuclear@0 908 vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi));
nuclear@0 909 vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc));
nuclear@0 910 ogg_stream_init(&vf->os,-1); /* fill in the serialno later */
nuclear@0 911
nuclear@0 912 /* Fetch all BOS pages, store the vorbis header and all seen serial
nuclear@0 913 numbers, load subsequent vorbis setup headers */
nuclear@0 914 if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){
nuclear@0 915 vf->datasource=NULL;
nuclear@0 916 ov_clear(vf);
nuclear@0 917 }else{
nuclear@0 918 /* serial number list for first link needs to be held somewhere
nuclear@0 919 for second stage of seekable stream open; this saves having to
nuclear@0 920 seek/reread first link's serialnumber data then. */
nuclear@0 921 vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos));
nuclear@0 922 vf->serialnos[0]=vf->current_serialno=vf->os.serialno;
nuclear@0 923 vf->serialnos[1]=serialno_list_size;
nuclear@0 924 memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos));
nuclear@0 925
nuclear@0 926 vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets));
nuclear@0 927 vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets));
nuclear@0 928 vf->offsets[0]=0;
nuclear@0 929 vf->dataoffsets[0]=vf->offset;
nuclear@0 930
nuclear@0 931 vf->ready_state=PARTOPEN;
nuclear@0 932 }
nuclear@0 933 if(serialno_list)_ogg_free(serialno_list);
nuclear@0 934 return(ret);
nuclear@0 935 }
nuclear@0 936
nuclear@0 937 static int _ov_open2(OggVorbis_File *vf){
nuclear@0 938 if(vf->ready_state != PARTOPEN) return OV_EINVAL;
nuclear@0 939 vf->ready_state=OPENED;
nuclear@0 940 if(vf->seekable){
nuclear@0 941 int ret=_open_seekable2(vf);
nuclear@0 942 if(ret){
nuclear@0 943 vf->datasource=NULL;
nuclear@0 944 ov_clear(vf);
nuclear@0 945 }
nuclear@0 946 return(ret);
nuclear@0 947 }else
nuclear@0 948 vf->ready_state=STREAMSET;
nuclear@0 949
nuclear@0 950 return 0;
nuclear@0 951 }
nuclear@0 952
nuclear@0 953
nuclear@0 954 /* clear out the OggVorbis_File struct */
nuclear@0 955 int ov_clear(OggVorbis_File *vf){
nuclear@0 956 if(vf){
nuclear@0 957 vorbis_block_clear(&vf->vb);
nuclear@0 958 vorbis_dsp_clear(&vf->vd);
nuclear@0 959 ogg_stream_clear(&vf->os);
nuclear@0 960
nuclear@0 961 if(vf->vi && vf->links){
nuclear@0 962 int i;
nuclear@0 963 for(i=0;i<vf->links;i++){
nuclear@0 964 vorbis_info_clear(vf->vi+i);
nuclear@0 965 vorbis_comment_clear(vf->vc+i);
nuclear@0 966 }
nuclear@0 967 _ogg_free(vf->vi);
nuclear@0 968 _ogg_free(vf->vc);
nuclear@0 969 }
nuclear@0 970 if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
nuclear@0 971 if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
nuclear@0 972 if(vf->serialnos)_ogg_free(vf->serialnos);
nuclear@0 973 if(vf->offsets)_ogg_free(vf->offsets);
nuclear@0 974 ogg_sync_clear(&vf->oy);
nuclear@0 975 if(vf->datasource && vf->callbacks.close_func)
nuclear@0 976 (vf->callbacks.close_func)(vf->datasource);
nuclear@0 977 memset(vf,0,sizeof(*vf));
nuclear@0 978 }
nuclear@0 979 #ifdef DEBUG_LEAKS
nuclear@0 980 _VDBG_dump();
nuclear@0 981 #endif
nuclear@0 982 return(0);
nuclear@0 983 }
nuclear@0 984
nuclear@0 985 /* inspects the OggVorbis file and finds/documents all the logical
nuclear@0 986 bitstreams contained in it. Tries to be tolerant of logical
nuclear@0 987 bitstream sections that are truncated/woogie.
nuclear@0 988
nuclear@0 989 return: -1) error
nuclear@0 990 0) OK
nuclear@0 991 */
nuclear@0 992
nuclear@0 993 int ov_open_callbacks(void *f,OggVorbis_File *vf,
nuclear@0 994 const char *initial,long ibytes,ov_callbacks callbacks){
nuclear@0 995 int ret=_ov_open1(f,vf,initial,ibytes,callbacks);
nuclear@0 996 if(ret)return ret;
nuclear@0 997 return _ov_open2(vf);
nuclear@0 998 }
nuclear@0 999
nuclear@0 1000 int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){
nuclear@0 1001 ov_callbacks callbacks = {
nuclear@0 1002 (size_t (*)(void *, size_t, size_t, void *)) fread,
nuclear@0 1003 (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap,
nuclear@0 1004 (int (*)(void *)) fclose,
nuclear@0 1005 (long (*)(void *)) ftell
nuclear@0 1006 };
nuclear@0 1007
nuclear@0 1008 return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
nuclear@0 1009 }
nuclear@0 1010
nuclear@0 1011 int ov_fopen(const char *path,OggVorbis_File *vf){
nuclear@0 1012 int ret;
nuclear@0 1013 FILE *f = fopen(path,"rb");
nuclear@0 1014 if(!f) return -1;
nuclear@0 1015
nuclear@0 1016 ret = ov_open(f,vf,NULL,0);
nuclear@0 1017 if(ret) fclose(f);
nuclear@0 1018 return ret;
nuclear@0 1019 }
nuclear@0 1020
nuclear@0 1021
nuclear@0 1022 /* cheap hack for game usage where downsampling is desirable; there's
nuclear@0 1023 no need for SRC as we can just do it cheaply in libvorbis. */
nuclear@0 1024
nuclear@0 1025 int ov_halfrate(OggVorbis_File *vf,int flag){
nuclear@0 1026 int i;
nuclear@0 1027 if(vf->vi==NULL)return OV_EINVAL;
nuclear@0 1028 if(vf->ready_state>STREAMSET){
nuclear@0 1029 /* clear out stream state; dumping the decode machine is needed to
nuclear@0 1030 reinit the MDCT lookups. */
nuclear@0 1031 vorbis_dsp_clear(&vf->vd);
nuclear@0 1032 vorbis_block_clear(&vf->vb);
nuclear@0 1033 vf->ready_state=STREAMSET;
nuclear@0 1034 if(vf->pcm_offset>=0){
nuclear@0 1035 ogg_int64_t pos=vf->pcm_offset;
nuclear@0 1036 vf->pcm_offset=-1; /* make sure the pos is dumped if unseekable */
nuclear@0 1037 ov_pcm_seek(vf,pos);
nuclear@0 1038 }
nuclear@0 1039 }
nuclear@0 1040
nuclear@0 1041 for(i=0;i<vf->links;i++){
nuclear@0 1042 if(vorbis_synthesis_halfrate(vf->vi+i,flag)){
nuclear@0 1043 if(flag) ov_halfrate(vf,0);
nuclear@0 1044 return OV_EINVAL;
nuclear@0 1045 }
nuclear@0 1046 }
nuclear@0 1047 return 0;
nuclear@0 1048 }
nuclear@0 1049
nuclear@0 1050 int ov_halfrate_p(OggVorbis_File *vf){
nuclear@0 1051 if(vf->vi==NULL)return OV_EINVAL;
nuclear@0 1052 return vorbis_synthesis_halfrate_p(vf->vi);
nuclear@0 1053 }
nuclear@0 1054
nuclear@0 1055 /* Only partially open the vorbis file; test for Vorbisness, and load
nuclear@0 1056 the headers for the first chain. Do not seek (although test for
nuclear@0 1057 seekability). Use ov_test_open to finish opening the file, else
nuclear@0 1058 ov_clear to close/free it. Same return codes as open. */
nuclear@0 1059
nuclear@0 1060 int ov_test_callbacks(void *f,OggVorbis_File *vf,
nuclear@0 1061 const char *initial,long ibytes,ov_callbacks callbacks)
nuclear@0 1062 {
nuclear@0 1063 return _ov_open1(f,vf,initial,ibytes,callbacks);
nuclear@0 1064 }
nuclear@0 1065
nuclear@0 1066 int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){
nuclear@0 1067 ov_callbacks callbacks = {
nuclear@0 1068 (size_t (*)(void *, size_t, size_t, void *)) fread,
nuclear@0 1069 (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap,
nuclear@0 1070 (int (*)(void *)) fclose,
nuclear@0 1071 (long (*)(void *)) ftell
nuclear@0 1072 };
nuclear@0 1073
nuclear@0 1074 return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks);
nuclear@0 1075 }
nuclear@0 1076
nuclear@0 1077 int ov_test_open(OggVorbis_File *vf){
nuclear@0 1078 if(vf->ready_state!=PARTOPEN)return(OV_EINVAL);
nuclear@0 1079 return _ov_open2(vf);
nuclear@0 1080 }
nuclear@0 1081
nuclear@0 1082 /* How many logical bitstreams in this physical bitstream? */
nuclear@0 1083 long ov_streams(OggVorbis_File *vf){
nuclear@0 1084 return vf->links;
nuclear@0 1085 }
nuclear@0 1086
nuclear@0 1087 /* Is the FILE * associated with vf seekable? */
nuclear@0 1088 long ov_seekable(OggVorbis_File *vf){
nuclear@0 1089 return vf->seekable;
nuclear@0 1090 }
nuclear@0 1091
nuclear@0 1092 /* returns the bitrate for a given logical bitstream or the entire
nuclear@0 1093 physical bitstream. If the file is open for random access, it will
nuclear@0 1094 find the *actual* average bitrate. If the file is streaming, it
nuclear@0 1095 returns the nominal bitrate (if set) else the average of the
nuclear@0 1096 upper/lower bounds (if set) else -1 (unset).
nuclear@0 1097
nuclear@0 1098 If you want the actual bitrate field settings, get them from the
nuclear@0 1099 vorbis_info structs */
nuclear@0 1100
nuclear@0 1101 long ov_bitrate(OggVorbis_File *vf,int i){
nuclear@0 1102 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1103 if(i>=vf->links)return(OV_EINVAL);
nuclear@0 1104 if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
nuclear@0 1105 if(i<0){
nuclear@0 1106 ogg_int64_t bits=0;
nuclear@0 1107 int i;
nuclear@0 1108 float br;
nuclear@0 1109 for(i=0;i<vf->links;i++)
nuclear@0 1110 bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
nuclear@0 1111 /* This once read: return(rint(bits/ov_time_total(vf,-1)));
nuclear@0 1112 * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
nuclear@0 1113 * so this is slightly transformed to make it work.
nuclear@0 1114 */
nuclear@0 1115 br = bits/ov_time_total(vf,-1);
nuclear@0 1116 return(rint(br));
nuclear@0 1117 }else{
nuclear@0 1118 if(vf->seekable){
nuclear@0 1119 /* return the actual bitrate */
nuclear@0 1120 return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i)));
nuclear@0 1121 }else{
nuclear@0 1122 /* return nominal if set */
nuclear@0 1123 if(vf->vi[i].bitrate_nominal>0){
nuclear@0 1124 return vf->vi[i].bitrate_nominal;
nuclear@0 1125 }else{
nuclear@0 1126 if(vf->vi[i].bitrate_upper>0){
nuclear@0 1127 if(vf->vi[i].bitrate_lower>0){
nuclear@0 1128 return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
nuclear@0 1129 }else{
nuclear@0 1130 return vf->vi[i].bitrate_upper;
nuclear@0 1131 }
nuclear@0 1132 }
nuclear@0 1133 return(OV_FALSE);
nuclear@0 1134 }
nuclear@0 1135 }
nuclear@0 1136 }
nuclear@0 1137 }
nuclear@0 1138
nuclear@0 1139 /* returns the actual bitrate since last call. returns -1 if no
nuclear@0 1140 additional data to offer since last call (or at beginning of stream),
nuclear@0 1141 EINVAL if stream is only partially open
nuclear@0 1142 */
nuclear@0 1143 long ov_bitrate_instant(OggVorbis_File *vf){
nuclear@0 1144 int link=(vf->seekable?vf->current_link:0);
nuclear@0 1145 long ret;
nuclear@0 1146 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1147 if(vf->samptrack==0)return(OV_FALSE);
nuclear@0 1148 ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
nuclear@0 1149 vf->bittrack=0.f;
nuclear@0 1150 vf->samptrack=0.f;
nuclear@0 1151 return(ret);
nuclear@0 1152 }
nuclear@0 1153
nuclear@0 1154 /* Guess */
nuclear@0 1155 long ov_serialnumber(OggVorbis_File *vf,int i){
nuclear@0 1156 if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1));
nuclear@0 1157 if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1));
nuclear@0 1158 if(i<0){
nuclear@0 1159 return(vf->current_serialno);
nuclear@0 1160 }else{
nuclear@0 1161 return(vf->serialnos[i]);
nuclear@0 1162 }
nuclear@0 1163 }
nuclear@0 1164
nuclear@0 1165 /* returns: total raw (compressed) length of content if i==-1
nuclear@0 1166 raw (compressed) length of that logical bitstream for i==0 to n
nuclear@0 1167 OV_EINVAL if the stream is not seekable (we can't know the length)
nuclear@0 1168 or if stream is only partially open
nuclear@0 1169 */
nuclear@0 1170 ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
nuclear@0 1171 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1172 if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
nuclear@0 1173 if(i<0){
nuclear@0 1174 ogg_int64_t acc=0;
nuclear@0 1175 int i;
nuclear@0 1176 for(i=0;i<vf->links;i++)
nuclear@0 1177 acc+=ov_raw_total(vf,i);
nuclear@0 1178 return(acc);
nuclear@0 1179 }else{
nuclear@0 1180 return(vf->offsets[i+1]-vf->offsets[i]);
nuclear@0 1181 }
nuclear@0 1182 }
nuclear@0 1183
nuclear@0 1184 /* returns: total PCM length (samples) of content if i==-1 PCM length
nuclear@0 1185 (samples) of that logical bitstream for i==0 to n
nuclear@0 1186 OV_EINVAL if the stream is not seekable (we can't know the
nuclear@0 1187 length) or only partially open
nuclear@0 1188 */
nuclear@0 1189 ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
nuclear@0 1190 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1191 if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
nuclear@0 1192 if(i<0){
nuclear@0 1193 ogg_int64_t acc=0;
nuclear@0 1194 int i;
nuclear@0 1195 for(i=0;i<vf->links;i++)
nuclear@0 1196 acc+=ov_pcm_total(vf,i);
nuclear@0 1197 return(acc);
nuclear@0 1198 }else{
nuclear@0 1199 return(vf->pcmlengths[i*2+1]);
nuclear@0 1200 }
nuclear@0 1201 }
nuclear@0 1202
nuclear@0 1203 /* returns: total seconds of content if i==-1
nuclear@0 1204 seconds in that logical bitstream for i==0 to n
nuclear@0 1205 OV_EINVAL if the stream is not seekable (we can't know the
nuclear@0 1206 length) or only partially open
nuclear@0 1207 */
nuclear@0 1208 double ov_time_total(OggVorbis_File *vf,int i){
nuclear@0 1209 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1210 if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
nuclear@0 1211 if(i<0){
nuclear@0 1212 double acc=0;
nuclear@0 1213 int i;
nuclear@0 1214 for(i=0;i<vf->links;i++)
nuclear@0 1215 acc+=ov_time_total(vf,i);
nuclear@0 1216 return(acc);
nuclear@0 1217 }else{
nuclear@0 1218 return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate);
nuclear@0 1219 }
nuclear@0 1220 }
nuclear@0 1221
nuclear@0 1222 /* seek to an offset relative to the *compressed* data. This also
nuclear@0 1223 scans packets to update the PCM cursor. It will cross a logical
nuclear@0 1224 bitstream boundary, but only if it can't get any packets out of the
nuclear@0 1225 tail of the bitstream we seek to (so no surprises).
nuclear@0 1226
nuclear@0 1227 returns zero on success, nonzero on failure */
nuclear@0 1228
nuclear@0 1229 int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 1230 ogg_stream_state work_os;
nuclear@0 1231 int ret;
nuclear@0 1232
nuclear@0 1233 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1234 if(!vf->seekable)
nuclear@0 1235 return(OV_ENOSEEK); /* don't dump machine if we can't seek */
nuclear@0 1236
nuclear@0 1237 if(pos<0 || pos>vf->end)return(OV_EINVAL);
nuclear@0 1238
nuclear@0 1239 /* is the seek position outside our current link [if any]? */
nuclear@0 1240 if(vf->ready_state>=STREAMSET){
nuclear@0 1241 if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1])
nuclear@0 1242 _decode_clear(vf); /* clear out stream state */
nuclear@0 1243 }
nuclear@0 1244
nuclear@0 1245 /* don't yet clear out decoding machine (if it's initialized), in
nuclear@0 1246 the case we're in the same link. Restart the decode lapping, and
nuclear@0 1247 let _fetch_and_process_packet deal with a potential bitstream
nuclear@0 1248 boundary */
nuclear@0 1249 vf->pcm_offset=-1;
nuclear@0 1250 ogg_stream_reset_serialno(&vf->os,
nuclear@0 1251 vf->current_serialno); /* must set serialno */
nuclear@0 1252 vorbis_synthesis_restart(&vf->vd);
nuclear@0 1253
nuclear@0 1254 ret=_seek_helper(vf,pos);
nuclear@0 1255 if(ret)goto seek_error;
nuclear@0 1256
nuclear@0 1257 /* we need to make sure the pcm_offset is set, but we don't want to
nuclear@0 1258 advance the raw cursor past good packets just to get to the first
nuclear@0 1259 with a granulepos. That's not equivalent behavior to beginning
nuclear@0 1260 decoding as immediately after the seek position as possible.
nuclear@0 1261
nuclear@0 1262 So, a hack. We use two stream states; a local scratch state and
nuclear@0 1263 the shared vf->os stream state. We use the local state to
nuclear@0 1264 scan, and the shared state as a buffer for later decode.
nuclear@0 1265
nuclear@0 1266 Unfortuantely, on the last page we still advance to last packet
nuclear@0 1267 because the granulepos on the last page is not necessarily on a
nuclear@0 1268 packet boundary, and we need to make sure the granpos is
nuclear@0 1269 correct.
nuclear@0 1270 */
nuclear@0 1271
nuclear@0 1272 {
nuclear@0 1273 ogg_page og;
nuclear@0 1274 ogg_packet op;
nuclear@0 1275 int lastblock=0;
nuclear@0 1276 int accblock=0;
nuclear@0 1277 int thisblock=0;
nuclear@0 1278 int lastflag=0;
nuclear@0 1279 int firstflag=0;
nuclear@0 1280 ogg_int64_t pagepos=-1;
nuclear@0 1281
nuclear@0 1282 ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */
nuclear@0 1283 ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE
nuclear@0 1284 return from not necessarily
nuclear@0 1285 starting from the beginning */
nuclear@0 1286
nuclear@0 1287 while(1){
nuclear@0 1288 if(vf->ready_state>=STREAMSET){
nuclear@0 1289 /* snarf/scan a packet if we can */
nuclear@0 1290 int result=ogg_stream_packetout(&work_os,&op);
nuclear@0 1291
nuclear@0 1292 if(result>0){
nuclear@0 1293
nuclear@0 1294 if(vf->vi[vf->current_link].codec_setup){
nuclear@0 1295 thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
nuclear@0 1296 if(thisblock<0){
nuclear@0 1297 ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1298 thisblock=0;
nuclear@0 1299 }else{
nuclear@0 1300
nuclear@0 1301 /* We can't get a guaranteed correct pcm position out of the
nuclear@0 1302 last page in a stream because it might have a 'short'
nuclear@0 1303 granpos, which can only be detected in the presence of a
nuclear@0 1304 preceding page. However, if the last page is also the first
nuclear@0 1305 page, the granpos rules of a first page take precedence. Not
nuclear@0 1306 only that, but for first==last, the EOS page must be treated
nuclear@0 1307 as if its a normal first page for the stream to open/play. */
nuclear@0 1308 if(lastflag && !firstflag)
nuclear@0 1309 ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1310 else
nuclear@0 1311 if(lastblock)accblock+=(lastblock+thisblock)>>2;
nuclear@0 1312 }
nuclear@0 1313
nuclear@0 1314 if(op.granulepos!=-1){
nuclear@0 1315 int i,link=vf->current_link;
nuclear@0 1316 ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
nuclear@0 1317 if(granulepos<0)granulepos=0;
nuclear@0 1318
nuclear@0 1319 for(i=0;i<link;i++)
nuclear@0 1320 granulepos+=vf->pcmlengths[i*2+1];
nuclear@0 1321 vf->pcm_offset=granulepos-accblock;
nuclear@0 1322 if(vf->pcm_offset<0)vf->pcm_offset=0;
nuclear@0 1323 break;
nuclear@0 1324 }
nuclear@0 1325 lastblock=thisblock;
nuclear@0 1326 continue;
nuclear@0 1327 }else
nuclear@0 1328 ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1329 }
nuclear@0 1330 }
nuclear@0 1331
nuclear@0 1332 if(!lastblock){
nuclear@0 1333 pagepos=_get_next_page(vf,&og,-1);
nuclear@0 1334 if(pagepos<0){
nuclear@0 1335 vf->pcm_offset=ov_pcm_total(vf,-1);
nuclear@0 1336 break;
nuclear@0 1337 }
nuclear@0 1338 }else{
nuclear@0 1339 /* huh? Bogus stream with packets but no granulepos */
nuclear@0 1340 vf->pcm_offset=-1;
nuclear@0 1341 break;
nuclear@0 1342 }
nuclear@0 1343
nuclear@0 1344 /* has our decoding just traversed a bitstream boundary? */
nuclear@0 1345 if(vf->ready_state>=STREAMSET){
nuclear@0 1346 if(vf->current_serialno!=ogg_page_serialno(&og)){
nuclear@0 1347
nuclear@0 1348 /* two possibilities:
nuclear@0 1349 1) our decoding just traversed a bitstream boundary
nuclear@0 1350 2) another stream is multiplexed into this logical section? */
nuclear@0 1351
nuclear@0 1352 if(ogg_page_bos(&og)){
nuclear@0 1353 /* we traversed */
nuclear@0 1354 _decode_clear(vf); /* clear out stream state */
nuclear@0 1355 ogg_stream_clear(&work_os);
nuclear@0 1356 } /* else, do nothing; next loop will scoop another page */
nuclear@0 1357 }
nuclear@0 1358 }
nuclear@0 1359
nuclear@0 1360 if(vf->ready_state<STREAMSET){
nuclear@0 1361 int link;
nuclear@0 1362 long serialno = ogg_page_serialno(&og);
nuclear@0 1363
nuclear@0 1364 for(link=0;link<vf->links;link++)
nuclear@0 1365 if(vf->serialnos[link]==serialno)break;
nuclear@0 1366
nuclear@0 1367 if(link==vf->links) continue; /* not the desired Vorbis
nuclear@0 1368 bitstream section; keep
nuclear@0 1369 trying */
nuclear@0 1370 vf->current_link=link;
nuclear@0 1371 vf->current_serialno=serialno;
nuclear@0 1372 ogg_stream_reset_serialno(&vf->os,serialno);
nuclear@0 1373 ogg_stream_reset_serialno(&work_os,serialno);
nuclear@0 1374 vf->ready_state=STREAMSET;
nuclear@0 1375 firstflag=(pagepos<=vf->dataoffsets[link]);
nuclear@0 1376 }
nuclear@0 1377
nuclear@0 1378 ogg_stream_pagein(&vf->os,&og);
nuclear@0 1379 ogg_stream_pagein(&work_os,&og);
nuclear@0 1380 lastflag=ogg_page_eos(&og);
nuclear@0 1381
nuclear@0 1382 }
nuclear@0 1383 }
nuclear@0 1384
nuclear@0 1385 ogg_stream_clear(&work_os);
nuclear@0 1386 vf->bittrack=0.f;
nuclear@0 1387 vf->samptrack=0.f;
nuclear@0 1388 return(0);
nuclear@0 1389
nuclear@0 1390 seek_error:
nuclear@0 1391 /* dump the machine so we're in a known state */
nuclear@0 1392 vf->pcm_offset=-1;
nuclear@0 1393 ogg_stream_clear(&work_os);
nuclear@0 1394 _decode_clear(vf);
nuclear@0 1395 return OV_EBADLINK;
nuclear@0 1396 }
nuclear@0 1397
nuclear@0 1398 /* Page granularity seek (faster than sample granularity because we
nuclear@0 1399 don't do the last bit of decode to find a specific sample).
nuclear@0 1400
nuclear@0 1401 Seek to the last [granule marked] page preceding the specified pos
nuclear@0 1402 location, such that decoding past the returned point will quickly
nuclear@0 1403 arrive at the requested position. */
nuclear@0 1404 int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 1405 int link=-1;
nuclear@0 1406 ogg_int64_t result=0;
nuclear@0 1407 ogg_int64_t total=ov_pcm_total(vf,-1);
nuclear@0 1408
nuclear@0 1409 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1410 if(!vf->seekable)return(OV_ENOSEEK);
nuclear@0 1411
nuclear@0 1412 if(pos<0 || pos>total)return(OV_EINVAL);
nuclear@0 1413
nuclear@0 1414 /* which bitstream section does this pcm offset occur in? */
nuclear@0 1415 for(link=vf->links-1;link>=0;link--){
nuclear@0 1416 total-=vf->pcmlengths[link*2+1];
nuclear@0 1417 if(pos>=total)break;
nuclear@0 1418 }
nuclear@0 1419
nuclear@0 1420 /* search within the logical bitstream for the page with the highest
nuclear@0 1421 pcm_pos preceding (or equal to) pos. There is a danger here;
nuclear@0 1422 missing pages or incorrect frame number information in the
nuclear@0 1423 bitstream could make our task impossible. Account for that (it
nuclear@0 1424 would be an error condition) */
nuclear@0 1425
nuclear@0 1426 /* new search algorithm by HB (Nicholas Vinen) */
nuclear@0 1427 {
nuclear@0 1428 ogg_int64_t end=vf->offsets[link+1];
nuclear@0 1429 ogg_int64_t begin=vf->offsets[link];
nuclear@0 1430 ogg_int64_t begintime = vf->pcmlengths[link*2];
nuclear@0 1431 ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime;
nuclear@0 1432 ogg_int64_t target=pos-total+begintime;
nuclear@0 1433 ogg_int64_t best=begin;
nuclear@0 1434
nuclear@0 1435 ogg_page og;
nuclear@0 1436 while(begin<end){
nuclear@0 1437 ogg_int64_t bisect;
nuclear@0 1438
nuclear@0 1439 if(end-begin<CHUNKSIZE){
nuclear@0 1440 bisect=begin;
nuclear@0 1441 }else{
nuclear@0 1442 /* take a (pretty decent) guess. */
nuclear@0 1443 bisect=begin +
nuclear@0 1444 (ogg_int64_t)((double)(target-begintime)*(end-begin)/(endtime-begintime))
nuclear@0 1445 - CHUNKSIZE;
nuclear@0 1446 if(bisect<begin+CHUNKSIZE)
nuclear@0 1447 bisect=begin;
nuclear@0 1448 }
nuclear@0 1449
nuclear@0 1450 if(bisect!=vf->offset){
nuclear@0 1451 result=_seek_helper(vf,bisect);
nuclear@0 1452 if(result) goto seek_error;
nuclear@0 1453 }
nuclear@0 1454
nuclear@0 1455 while(begin<end){
nuclear@0 1456 result=_get_next_page(vf,&og,end-vf->offset);
nuclear@0 1457 if(result==OV_EREAD) goto seek_error;
nuclear@0 1458 if(result<0){
nuclear@0 1459 if(bisect<=begin+1)
nuclear@0 1460 end=begin; /* found it */
nuclear@0 1461 else{
nuclear@0 1462 if(bisect==0) goto seek_error;
nuclear@0 1463 bisect-=CHUNKSIZE;
nuclear@0 1464 if(bisect<=begin)bisect=begin+1;
nuclear@0 1465 result=_seek_helper(vf,bisect);
nuclear@0 1466 if(result) goto seek_error;
nuclear@0 1467 }
nuclear@0 1468 }else{
nuclear@0 1469 ogg_int64_t granulepos;
nuclear@0 1470
nuclear@0 1471 if(ogg_page_serialno(&og)!=vf->serialnos[link])
nuclear@0 1472 continue;
nuclear@0 1473
nuclear@0 1474 granulepos=ogg_page_granulepos(&og);
nuclear@0 1475 if(granulepos==-1)continue;
nuclear@0 1476
nuclear@0 1477 if(granulepos<target){
nuclear@0 1478 best=result; /* raw offset of packet with granulepos */
nuclear@0 1479 begin=vf->offset; /* raw offset of next page */
nuclear@0 1480 begintime=granulepos;
nuclear@0 1481
nuclear@0 1482 if(target-begintime>44100)break;
nuclear@0 1483 bisect=begin; /* *not* begin + 1 */
nuclear@0 1484 }else{
nuclear@0 1485 if(bisect<=begin+1)
nuclear@0 1486 end=begin; /* found it */
nuclear@0 1487 else{
nuclear@0 1488 if(end==vf->offset){ /* we're pretty close - we'd be stuck in */
nuclear@0 1489 end=result;
nuclear@0 1490 bisect-=CHUNKSIZE; /* an endless loop otherwise. */
nuclear@0 1491 if(bisect<=begin)bisect=begin+1;
nuclear@0 1492 result=_seek_helper(vf,bisect);
nuclear@0 1493 if(result) goto seek_error;
nuclear@0 1494 }else{
nuclear@0 1495 end=bisect;
nuclear@0 1496 endtime=granulepos;
nuclear@0 1497 break;
nuclear@0 1498 }
nuclear@0 1499 }
nuclear@0 1500 }
nuclear@0 1501 }
nuclear@0 1502 }
nuclear@0 1503 }
nuclear@0 1504
nuclear@0 1505 /* found our page. seek to it, update pcm offset. Easier case than
nuclear@0 1506 raw_seek, don't keep packets preceding granulepos. */
nuclear@0 1507 {
nuclear@0 1508 ogg_page og;
nuclear@0 1509 ogg_packet op;
nuclear@0 1510
nuclear@0 1511 /* seek */
nuclear@0 1512 result=_seek_helper(vf,best);
nuclear@0 1513 vf->pcm_offset=-1;
nuclear@0 1514 if(result) goto seek_error;
nuclear@0 1515 result=_get_next_page(vf,&og,-1);
nuclear@0 1516 if(result<0) goto seek_error;
nuclear@0 1517
nuclear@0 1518 if(link!=vf->current_link){
nuclear@0 1519 /* Different link; dump entire decode machine */
nuclear@0 1520 _decode_clear(vf);
nuclear@0 1521
nuclear@0 1522 vf->current_link=link;
nuclear@0 1523 vf->current_serialno=vf->serialnos[link];
nuclear@0 1524 vf->ready_state=STREAMSET;
nuclear@0 1525
nuclear@0 1526 }else{
nuclear@0 1527 vorbis_synthesis_restart(&vf->vd);
nuclear@0 1528 }
nuclear@0 1529
nuclear@0 1530 ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
nuclear@0 1531 ogg_stream_pagein(&vf->os,&og);
nuclear@0 1532
nuclear@0 1533 /* pull out all but last packet; the one with granulepos */
nuclear@0 1534 while(1){
nuclear@0 1535 result=ogg_stream_packetpeek(&vf->os,&op);
nuclear@0 1536 if(result==0){
nuclear@0 1537 /* !!! the packet finishing this page originated on a
nuclear@0 1538 preceding page. Keep fetching previous pages until we
nuclear@0 1539 get one with a granulepos or without the 'continued' flag
nuclear@0 1540 set. Then just use raw_seek for simplicity. */
nuclear@0 1541
nuclear@0 1542 result=_seek_helper(vf,best);
nuclear@0 1543 if(result<0) goto seek_error;
nuclear@0 1544
nuclear@0 1545 while(1){
nuclear@0 1546 result=_get_prev_page(vf,&og);
nuclear@0 1547 if(result<0) goto seek_error;
nuclear@0 1548 if(ogg_page_serialno(&og)==vf->current_serialno &&
nuclear@0 1549 (ogg_page_granulepos(&og)>-1 ||
nuclear@0 1550 !ogg_page_continued(&og))){
nuclear@0 1551 return ov_raw_seek(vf,result);
nuclear@0 1552 }
nuclear@0 1553 vf->offset=result;
nuclear@0 1554 }
nuclear@0 1555 }
nuclear@0 1556 if(result<0){
nuclear@0 1557 result = OV_EBADPACKET;
nuclear@0 1558 goto seek_error;
nuclear@0 1559 }
nuclear@0 1560 if(op.granulepos!=-1){
nuclear@0 1561 vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
nuclear@0 1562 if(vf->pcm_offset<0)vf->pcm_offset=0;
nuclear@0 1563 vf->pcm_offset+=total;
nuclear@0 1564 break;
nuclear@0 1565 }else
nuclear@0 1566 result=ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1567 }
nuclear@0 1568 }
nuclear@0 1569 }
nuclear@0 1570
nuclear@0 1571 /* verify result */
nuclear@0 1572 if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){
nuclear@0 1573 result=OV_EFAULT;
nuclear@0 1574 goto seek_error;
nuclear@0 1575 }
nuclear@0 1576 vf->bittrack=0.f;
nuclear@0 1577 vf->samptrack=0.f;
nuclear@0 1578 return(0);
nuclear@0 1579
nuclear@0 1580 seek_error:
nuclear@0 1581 /* dump machine so we're in a known state */
nuclear@0 1582 vf->pcm_offset=-1;
nuclear@0 1583 _decode_clear(vf);
nuclear@0 1584 return (int)result;
nuclear@0 1585 }
nuclear@0 1586
nuclear@0 1587 /* seek to a sample offset relative to the decompressed pcm stream
nuclear@0 1588 returns zero on success, nonzero on failure */
nuclear@0 1589
nuclear@0 1590 int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 1591 int thisblock,lastblock=0;
nuclear@0 1592 int ret=ov_pcm_seek_page(vf,pos);
nuclear@0 1593 if(ret<0)return(ret);
nuclear@0 1594 if((ret=_make_decode_ready(vf)))return ret;
nuclear@0 1595
nuclear@0 1596 /* discard leading packets we don't need for the lapping of the
nuclear@0 1597 position we want; don't decode them */
nuclear@0 1598
nuclear@0 1599 while(1){
nuclear@0 1600 ogg_packet op;
nuclear@0 1601 ogg_page og;
nuclear@0 1602
nuclear@0 1603 int ret=ogg_stream_packetpeek(&vf->os,&op);
nuclear@0 1604 if(ret>0){
nuclear@0 1605 thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
nuclear@0 1606 if(thisblock<0){
nuclear@0 1607 ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1608 continue; /* non audio packet */
nuclear@0 1609 }
nuclear@0 1610 if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
nuclear@0 1611
nuclear@0 1612 if(vf->pcm_offset+((thisblock+
nuclear@0 1613 vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
nuclear@0 1614
nuclear@0 1615 /* remove the packet from packet queue and track its granulepos */
nuclear@0 1616 ogg_stream_packetout(&vf->os,NULL);
nuclear@0 1617 vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with
nuclear@0 1618 only tracking, no
nuclear@0 1619 pcm_decode */
nuclear@0 1620 vorbis_synthesis_blockin(&vf->vd,&vf->vb);
nuclear@0 1621
nuclear@0 1622 /* end of logical stream case is hard, especially with exact
nuclear@0 1623 length positioning. */
nuclear@0 1624
nuclear@0 1625 if(op.granulepos>-1){
nuclear@0 1626 int i;
nuclear@0 1627 /* always believe the stream markers */
nuclear@0 1628 vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
nuclear@0 1629 if(vf->pcm_offset<0)vf->pcm_offset=0;
nuclear@0 1630 for(i=0;i<vf->current_link;i++)
nuclear@0 1631 vf->pcm_offset+=vf->pcmlengths[i*2+1];
nuclear@0 1632 }
nuclear@0 1633
nuclear@0 1634 lastblock=thisblock;
nuclear@0 1635
nuclear@0 1636 }else{
nuclear@0 1637 if(ret<0 && ret!=OV_HOLE)break;
nuclear@0 1638
nuclear@0 1639 /* suck in a new page */
nuclear@0 1640 if(_get_next_page(vf,&og,-1)<0)break;
nuclear@0 1641 if(ogg_page_bos(&og))_decode_clear(vf);
nuclear@0 1642
nuclear@0 1643 if(vf->ready_state<STREAMSET){
nuclear@0 1644 long serialno=ogg_page_serialno(&og);
nuclear@0 1645 int link;
nuclear@0 1646
nuclear@0 1647 for(link=0;link<vf->links;link++)
nuclear@0 1648 if(vf->serialnos[link]==serialno)break;
nuclear@0 1649 if(link==vf->links) continue;
nuclear@0 1650 vf->current_link=link;
nuclear@0 1651
nuclear@0 1652 vf->ready_state=STREAMSET;
nuclear@0 1653 vf->current_serialno=ogg_page_serialno(&og);
nuclear@0 1654 ogg_stream_reset_serialno(&vf->os,serialno);
nuclear@0 1655 ret=_make_decode_ready(vf);
nuclear@0 1656 if(ret)return ret;
nuclear@0 1657 lastblock=0;
nuclear@0 1658 }
nuclear@0 1659
nuclear@0 1660 ogg_stream_pagein(&vf->os,&og);
nuclear@0 1661 }
nuclear@0 1662 }
nuclear@0 1663
nuclear@0 1664 vf->bittrack=0.f;
nuclear@0 1665 vf->samptrack=0.f;
nuclear@0 1666 /* discard samples until we reach the desired position. Crossing a
nuclear@0 1667 logical bitstream boundary with abandon is OK. */
nuclear@0 1668 {
nuclear@0 1669 /* note that halfrate could be set differently in each link, but
nuclear@0 1670 vorbisfile encoforces all links are set or unset */
nuclear@0 1671 int hs=vorbis_synthesis_halfrate_p(vf->vi);
nuclear@0 1672 while(vf->pcm_offset<((pos>>hs)<<hs)){
nuclear@0 1673 ogg_int64_t target=(pos-vf->pcm_offset)>>hs;
nuclear@0 1674 long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
nuclear@0 1675
nuclear@0 1676 if(samples>target)samples=target;
nuclear@0 1677 vorbis_synthesis_read(&vf->vd,samples);
nuclear@0 1678 vf->pcm_offset+=samples<<hs;
nuclear@0 1679
nuclear@0 1680 if(samples<target)
nuclear@0 1681 if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
nuclear@0 1682 vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
nuclear@0 1683 }
nuclear@0 1684 }
nuclear@0 1685 return 0;
nuclear@0 1686 }
nuclear@0 1687
nuclear@0 1688 /* seek to a playback time relative to the decompressed pcm stream
nuclear@0 1689 returns zero on success, nonzero on failure */
nuclear@0 1690 int ov_time_seek(OggVorbis_File *vf,double seconds){
nuclear@0 1691 /* translate time to PCM position and call ov_pcm_seek */
nuclear@0 1692
nuclear@0 1693 int link=-1;
nuclear@0 1694 ogg_int64_t pcm_total=0;
nuclear@0 1695 double time_total=0.;
nuclear@0 1696
nuclear@0 1697 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1698 if(!vf->seekable)return(OV_ENOSEEK);
nuclear@0 1699 if(seconds<0)return(OV_EINVAL);
nuclear@0 1700
nuclear@0 1701 /* which bitstream section does this time offset occur in? */
nuclear@0 1702 for(link=0;link<vf->links;link++){
nuclear@0 1703 double addsec = ov_time_total(vf,link);
nuclear@0 1704 if(seconds<time_total+addsec)break;
nuclear@0 1705 time_total+=addsec;
nuclear@0 1706 pcm_total+=vf->pcmlengths[link*2+1];
nuclear@0 1707 }
nuclear@0 1708
nuclear@0 1709 if(link==vf->links)return(OV_EINVAL);
nuclear@0 1710
nuclear@0 1711 /* enough information to convert time offset to pcm offset */
nuclear@0 1712 {
nuclear@0 1713 ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
nuclear@0 1714 return(ov_pcm_seek(vf,target));
nuclear@0 1715 }
nuclear@0 1716 }
nuclear@0 1717
nuclear@0 1718 /* page-granularity version of ov_time_seek
nuclear@0 1719 returns zero on success, nonzero on failure */
nuclear@0 1720 int ov_time_seek_page(OggVorbis_File *vf,double seconds){
nuclear@0 1721 /* translate time to PCM position and call ov_pcm_seek */
nuclear@0 1722
nuclear@0 1723 int link=-1;
nuclear@0 1724 ogg_int64_t pcm_total=0;
nuclear@0 1725 double time_total=0.;
nuclear@0 1726
nuclear@0 1727 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1728 if(!vf->seekable)return(OV_ENOSEEK);
nuclear@0 1729 if(seconds<0)return(OV_EINVAL);
nuclear@0 1730
nuclear@0 1731 /* which bitstream section does this time offset occur in? */
nuclear@0 1732 for(link=0;link<vf->links;link++){
nuclear@0 1733 double addsec = ov_time_total(vf,link);
nuclear@0 1734 if(seconds<time_total+addsec)break;
nuclear@0 1735 time_total+=addsec;
nuclear@0 1736 pcm_total+=vf->pcmlengths[link*2+1];
nuclear@0 1737 }
nuclear@0 1738
nuclear@0 1739 if(link==vf->links)return(OV_EINVAL);
nuclear@0 1740
nuclear@0 1741 /* enough information to convert time offset to pcm offset */
nuclear@0 1742 {
nuclear@0 1743 ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
nuclear@0 1744 return(ov_pcm_seek_page(vf,target));
nuclear@0 1745 }
nuclear@0 1746 }
nuclear@0 1747
nuclear@0 1748 /* tell the current stream offset cursor. Note that seek followed by
nuclear@0 1749 tell will likely not give the set offset due to caching */
nuclear@0 1750 ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
nuclear@0 1751 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1752 return(vf->offset);
nuclear@0 1753 }
nuclear@0 1754
nuclear@0 1755 /* return PCM offset (sample) of next PCM sample to be read */
nuclear@0 1756 ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
nuclear@0 1757 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1758 return(vf->pcm_offset);
nuclear@0 1759 }
nuclear@0 1760
nuclear@0 1761 /* return time offset (seconds) of next PCM sample to be read */
nuclear@0 1762 double ov_time_tell(OggVorbis_File *vf){
nuclear@0 1763 int link=0;
nuclear@0 1764 ogg_int64_t pcm_total=0;
nuclear@0 1765 double time_total=0.f;
nuclear@0 1766
nuclear@0 1767 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1768 if(vf->seekable){
nuclear@0 1769 pcm_total=ov_pcm_total(vf,-1);
nuclear@0 1770 time_total=ov_time_total(vf,-1);
nuclear@0 1771
nuclear@0 1772 /* which bitstream section does this time offset occur in? */
nuclear@0 1773 for(link=vf->links-1;link>=0;link--){
nuclear@0 1774 pcm_total-=vf->pcmlengths[link*2+1];
nuclear@0 1775 time_total-=ov_time_total(vf,link);
nuclear@0 1776 if(vf->pcm_offset>=pcm_total)break;
nuclear@0 1777 }
nuclear@0 1778 }
nuclear@0 1779
nuclear@0 1780 return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate);
nuclear@0 1781 }
nuclear@0 1782
nuclear@0 1783 /* link: -1) return the vorbis_info struct for the bitstream section
nuclear@0 1784 currently being decoded
nuclear@0 1785 0-n) to request information for a specific bitstream section
nuclear@0 1786
nuclear@0 1787 In the case of a non-seekable bitstream, any call returns the
nuclear@0 1788 current bitstream. NULL in the case that the machine is not
nuclear@0 1789 initialized */
nuclear@0 1790
nuclear@0 1791 vorbis_info *ov_info(OggVorbis_File *vf,int link){
nuclear@0 1792 if(vf->seekable){
nuclear@0 1793 if(link<0)
nuclear@0 1794 if(vf->ready_state>=STREAMSET)
nuclear@0 1795 return vf->vi+vf->current_link;
nuclear@0 1796 else
nuclear@0 1797 return vf->vi;
nuclear@0 1798 else
nuclear@0 1799 if(link>=vf->links)
nuclear@0 1800 return NULL;
nuclear@0 1801 else
nuclear@0 1802 return vf->vi+link;
nuclear@0 1803 }else{
nuclear@0 1804 return vf->vi;
nuclear@0 1805 }
nuclear@0 1806 }
nuclear@0 1807
nuclear@0 1808 /* grr, strong typing, grr, no templates/inheritence, grr */
nuclear@0 1809 vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
nuclear@0 1810 if(vf->seekable){
nuclear@0 1811 if(link<0)
nuclear@0 1812 if(vf->ready_state>=STREAMSET)
nuclear@0 1813 return vf->vc+vf->current_link;
nuclear@0 1814 else
nuclear@0 1815 return vf->vc;
nuclear@0 1816 else
nuclear@0 1817 if(link>=vf->links)
nuclear@0 1818 return NULL;
nuclear@0 1819 else
nuclear@0 1820 return vf->vc+link;
nuclear@0 1821 }else{
nuclear@0 1822 return vf->vc;
nuclear@0 1823 }
nuclear@0 1824 }
nuclear@0 1825
nuclear@0 1826 static int host_is_big_endian() {
nuclear@0 1827 ogg_int32_t pattern = 0xfeedface; /* deadbeef */
nuclear@0 1828 unsigned char *bytewise = (unsigned char *)&pattern;
nuclear@0 1829 if (bytewise[0] == 0xfe) return 1;
nuclear@0 1830 return 0;
nuclear@0 1831 }
nuclear@0 1832
nuclear@0 1833 /* up to this point, everything could more or less hide the multiple
nuclear@0 1834 logical bitstream nature of chaining from the toplevel application
nuclear@0 1835 if the toplevel application didn't particularly care. However, at
nuclear@0 1836 the point that we actually read audio back, the multiple-section
nuclear@0 1837 nature must surface: Multiple bitstream sections do not necessarily
nuclear@0 1838 have to have the same number of channels or sampling rate.
nuclear@0 1839
nuclear@0 1840 ov_read returns the sequential logical bitstream number currently
nuclear@0 1841 being decoded along with the PCM data in order that the toplevel
nuclear@0 1842 application can take action on channel/sample rate changes. This
nuclear@0 1843 number will be incremented even for streamed (non-seekable) streams
nuclear@0 1844 (for seekable streams, it represents the actual logical bitstream
nuclear@0 1845 index within the physical bitstream. Note that the accessor
nuclear@0 1846 functions above are aware of this dichotomy).
nuclear@0 1847
nuclear@0 1848 ov_read_filter is exactly the same as ov_read except that it processes
nuclear@0 1849 the decoded audio data through a filter before packing it into the
nuclear@0 1850 requested format. This gives greater accuracy than applying a filter
nuclear@0 1851 after the audio has been converted into integral PCM.
nuclear@0 1852
nuclear@0 1853 input values: buffer) a buffer to hold packed PCM data for return
nuclear@0 1854 length) the byte length requested to be placed into buffer
nuclear@0 1855 bigendianp) should the data be packed LSB first (0) or
nuclear@0 1856 MSB first (1)
nuclear@0 1857 word) word size for output. currently 1 (byte) or
nuclear@0 1858 2 (16 bit short)
nuclear@0 1859
nuclear@0 1860 return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
nuclear@0 1861 0) EOF
nuclear@0 1862 n) number of bytes of PCM actually returned. The
nuclear@0 1863 below works on a packet-by-packet basis, so the
nuclear@0 1864 return length is not related to the 'length' passed
nuclear@0 1865 in, just guaranteed to fit.
nuclear@0 1866
nuclear@0 1867 *section) set to the logical bitstream number */
nuclear@0 1868
nuclear@0 1869 long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
nuclear@0 1870 int bigendianp,int word,int sgned,int *bitstream,
nuclear@0 1871 void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param){
nuclear@0 1872 int i,j;
nuclear@0 1873 int host_endian = host_is_big_endian();
nuclear@0 1874 int hs;
nuclear@0 1875
nuclear@0 1876 float **pcm;
nuclear@0 1877 long samples;
nuclear@0 1878
nuclear@0 1879 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 1880
nuclear@0 1881 while(1){
nuclear@0 1882 if(vf->ready_state==INITSET){
nuclear@0 1883 samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
nuclear@0 1884 if(samples)break;
nuclear@0 1885 }
nuclear@0 1886
nuclear@0 1887 /* suck in another packet */
nuclear@0 1888 {
nuclear@0 1889 int ret=_fetch_and_process_packet(vf,NULL,1,1);
nuclear@0 1890 if(ret==OV_EOF)
nuclear@0 1891 return(0);
nuclear@0 1892 if(ret<=0)
nuclear@0 1893 return(ret);
nuclear@0 1894 }
nuclear@0 1895
nuclear@0 1896 }
nuclear@0 1897
nuclear@0 1898 if(samples>0){
nuclear@0 1899
nuclear@0 1900 /* yay! proceed to pack data into the byte buffer */
nuclear@0 1901
nuclear@0 1902 long channels=ov_info(vf,-1)->channels;
nuclear@0 1903 long bytespersample=word * channels;
nuclear@0 1904 vorbis_fpu_control fpu;
nuclear@0 1905 if(samples>length/bytespersample)samples=length/bytespersample;
nuclear@0 1906
nuclear@0 1907 if(samples <= 0)
nuclear@0 1908 return OV_EINVAL;
nuclear@0 1909
nuclear@0 1910 /* Here. */
nuclear@0 1911 if(filter)
nuclear@0 1912 filter(pcm,channels,samples,filter_param);
nuclear@0 1913
nuclear@0 1914 /* a tight loop to pack each size */
nuclear@0 1915 {
nuclear@0 1916 int val;
nuclear@0 1917 if(word==1){
nuclear@0 1918 int off=(sgned?0:128);
nuclear@0 1919 vorbis_fpu_setround(&fpu);
nuclear@0 1920 for(j=0;j<samples;j++)
nuclear@0 1921 for(i=0;i<channels;i++){
nuclear@0 1922 val=vorbis_ftoi(pcm[i][j]*128.f);
nuclear@0 1923 if(val>127)val=127;
nuclear@0 1924 else if(val<-128)val=-128;
nuclear@0 1925 *buffer++=val+off;
nuclear@0 1926 }
nuclear@0 1927 vorbis_fpu_restore(fpu);
nuclear@0 1928 }else{
nuclear@0 1929 int off=(sgned?0:32768);
nuclear@0 1930
nuclear@0 1931 if(host_endian==bigendianp){
nuclear@0 1932 if(sgned){
nuclear@0 1933
nuclear@0 1934 vorbis_fpu_setround(&fpu);
nuclear@0 1935 for(i=0;i<channels;i++) { /* It's faster in this order */
nuclear@0 1936 float *src=pcm[i];
nuclear@0 1937 short *dest=((short *)buffer)+i;
nuclear@0 1938 for(j=0;j<samples;j++) {
nuclear@0 1939 val=vorbis_ftoi(src[j]*32768.f);
nuclear@0 1940 if(val>32767)val=32767;
nuclear@0 1941 else if(val<-32768)val=-32768;
nuclear@0 1942 *dest=val;
nuclear@0 1943 dest+=channels;
nuclear@0 1944 }
nuclear@0 1945 }
nuclear@0 1946 vorbis_fpu_restore(fpu);
nuclear@0 1947
nuclear@0 1948 }else{
nuclear@0 1949
nuclear@0 1950 vorbis_fpu_setround(&fpu);
nuclear@0 1951 for(i=0;i<channels;i++) {
nuclear@0 1952 float *src=pcm[i];
nuclear@0 1953 short *dest=((short *)buffer)+i;
nuclear@0 1954 for(j=0;j<samples;j++) {
nuclear@0 1955 val=vorbis_ftoi(src[j]*32768.f);
nuclear@0 1956 if(val>32767)val=32767;
nuclear@0 1957 else if(val<-32768)val=-32768;
nuclear@0 1958 *dest=val+off;
nuclear@0 1959 dest+=channels;
nuclear@0 1960 }
nuclear@0 1961 }
nuclear@0 1962 vorbis_fpu_restore(fpu);
nuclear@0 1963
nuclear@0 1964 }
nuclear@0 1965 }else if(bigendianp){
nuclear@0 1966
nuclear@0 1967 vorbis_fpu_setround(&fpu);
nuclear@0 1968 for(j=0;j<samples;j++)
nuclear@0 1969 for(i=0;i<channels;i++){
nuclear@0 1970 val=vorbis_ftoi(pcm[i][j]*32768.f);
nuclear@0 1971 if(val>32767)val=32767;
nuclear@0 1972 else if(val<-32768)val=-32768;
nuclear@0 1973 val+=off;
nuclear@0 1974 *buffer++=(val>>8);
nuclear@0 1975 *buffer++=(val&0xff);
nuclear@0 1976 }
nuclear@0 1977 vorbis_fpu_restore(fpu);
nuclear@0 1978
nuclear@0 1979 }else{
nuclear@0 1980 int val;
nuclear@0 1981 vorbis_fpu_setround(&fpu);
nuclear@0 1982 for(j=0;j<samples;j++)
nuclear@0 1983 for(i=0;i<channels;i++){
nuclear@0 1984 val=vorbis_ftoi(pcm[i][j]*32768.f);
nuclear@0 1985 if(val>32767)val=32767;
nuclear@0 1986 else if(val<-32768)val=-32768;
nuclear@0 1987 val+=off;
nuclear@0 1988 *buffer++=(val&0xff);
nuclear@0 1989 *buffer++=(val>>8);
nuclear@0 1990 }
nuclear@0 1991 vorbis_fpu_restore(fpu);
nuclear@0 1992
nuclear@0 1993 }
nuclear@0 1994 }
nuclear@0 1995 }
nuclear@0 1996
nuclear@0 1997 vorbis_synthesis_read(&vf->vd,samples);
nuclear@0 1998 hs=vorbis_synthesis_halfrate_p(vf->vi);
nuclear@0 1999 vf->pcm_offset+=(samples<<hs);
nuclear@0 2000 if(bitstream)*bitstream=vf->current_link;
nuclear@0 2001 return(samples*bytespersample);
nuclear@0 2002 }else{
nuclear@0 2003 return(samples);
nuclear@0 2004 }
nuclear@0 2005 }
nuclear@0 2006
nuclear@0 2007 long ov_read(OggVorbis_File *vf,char *buffer,int length,
nuclear@0 2008 int bigendianp,int word,int sgned,int *bitstream){
nuclear@0 2009 return ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, NULL, NULL);
nuclear@0 2010 }
nuclear@0 2011
nuclear@0 2012 /* input values: pcm_channels) a float vector per channel of output
nuclear@0 2013 length) the sample length being read by the app
nuclear@0 2014
nuclear@0 2015 return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
nuclear@0 2016 0) EOF
nuclear@0 2017 n) number of samples of PCM actually returned. The
nuclear@0 2018 below works on a packet-by-packet basis, so the
nuclear@0 2019 return length is not related to the 'length' passed
nuclear@0 2020 in, just guaranteed to fit.
nuclear@0 2021
nuclear@0 2022 *section) set to the logical bitstream number */
nuclear@0 2023
nuclear@0 2024
nuclear@0 2025
nuclear@0 2026 long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
nuclear@0 2027 int *bitstream){
nuclear@0 2028
nuclear@0 2029 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 2030
nuclear@0 2031 while(1){
nuclear@0 2032 if(vf->ready_state==INITSET){
nuclear@0 2033 float **pcm;
nuclear@0 2034 long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
nuclear@0 2035 if(samples){
nuclear@0 2036 int hs=vorbis_synthesis_halfrate_p(vf->vi);
nuclear@0 2037 if(pcm_channels)*pcm_channels=pcm;
nuclear@0 2038 if(samples>length)samples=length;
nuclear@0 2039 vorbis_synthesis_read(&vf->vd,samples);
nuclear@0 2040 vf->pcm_offset+=samples<<hs;
nuclear@0 2041 if(bitstream)*bitstream=vf->current_link;
nuclear@0 2042 return samples;
nuclear@0 2043
nuclear@0 2044 }
nuclear@0 2045 }
nuclear@0 2046
nuclear@0 2047 /* suck in another packet */
nuclear@0 2048 {
nuclear@0 2049 int ret=_fetch_and_process_packet(vf,NULL,1,1);
nuclear@0 2050 if(ret==OV_EOF)return(0);
nuclear@0 2051 if(ret<=0)return(ret);
nuclear@0 2052 }
nuclear@0 2053
nuclear@0 2054 }
nuclear@0 2055 }
nuclear@0 2056
nuclear@0 2057 extern float *vorbis_window(vorbis_dsp_state *v,int W);
nuclear@0 2058
nuclear@0 2059 static void _ov_splice(float **pcm,float **lappcm,
nuclear@0 2060 int n1, int n2,
nuclear@0 2061 int ch1, int ch2,
nuclear@0 2062 float *w1, float *w2){
nuclear@0 2063 int i,j;
nuclear@0 2064 float *w=w1;
nuclear@0 2065 int n=n1;
nuclear@0 2066
nuclear@0 2067 if(n1>n2){
nuclear@0 2068 n=n2;
nuclear@0 2069 w=w2;
nuclear@0 2070 }
nuclear@0 2071
nuclear@0 2072 /* splice */
nuclear@0 2073 for(j=0;j<ch1 && j<ch2;j++){
nuclear@0 2074 float *s=lappcm[j];
nuclear@0 2075 float *d=pcm[j];
nuclear@0 2076
nuclear@0 2077 for(i=0;i<n;i++){
nuclear@0 2078 float wd=w[i]*w[i];
nuclear@0 2079 float ws=1.-wd;
nuclear@0 2080 d[i]=d[i]*wd + s[i]*ws;
nuclear@0 2081 }
nuclear@0 2082 }
nuclear@0 2083 /* window from zero */
nuclear@0 2084 for(;j<ch2;j++){
nuclear@0 2085 float *d=pcm[j];
nuclear@0 2086 for(i=0;i<n;i++){
nuclear@0 2087 float wd=w[i]*w[i];
nuclear@0 2088 d[i]=d[i]*wd;
nuclear@0 2089 }
nuclear@0 2090 }
nuclear@0 2091
nuclear@0 2092 }
nuclear@0 2093
nuclear@0 2094 /* make sure vf is INITSET */
nuclear@0 2095 static int _ov_initset(OggVorbis_File *vf){
nuclear@0 2096 while(1){
nuclear@0 2097 if(vf->ready_state==INITSET)break;
nuclear@0 2098 /* suck in another packet */
nuclear@0 2099 {
nuclear@0 2100 int ret=_fetch_and_process_packet(vf,NULL,1,0);
nuclear@0 2101 if(ret<0 && ret!=OV_HOLE)return(ret);
nuclear@0 2102 }
nuclear@0 2103 }
nuclear@0 2104 return 0;
nuclear@0 2105 }
nuclear@0 2106
nuclear@0 2107 /* make sure vf is INITSET and that we have a primed buffer; if
nuclear@0 2108 we're crosslapping at a stream section boundary, this also makes
nuclear@0 2109 sure we're sanity checking against the right stream information */
nuclear@0 2110 static int _ov_initprime(OggVorbis_File *vf){
nuclear@0 2111 vorbis_dsp_state *vd=&vf->vd;
nuclear@0 2112 while(1){
nuclear@0 2113 if(vf->ready_state==INITSET)
nuclear@0 2114 if(vorbis_synthesis_pcmout(vd,NULL))break;
nuclear@0 2115
nuclear@0 2116 /* suck in another packet */
nuclear@0 2117 {
nuclear@0 2118 int ret=_fetch_and_process_packet(vf,NULL,1,0);
nuclear@0 2119 if(ret<0 && ret!=OV_HOLE)return(ret);
nuclear@0 2120 }
nuclear@0 2121 }
nuclear@0 2122 return 0;
nuclear@0 2123 }
nuclear@0 2124
nuclear@0 2125 /* grab enough data for lapping from vf; this may be in the form of
nuclear@0 2126 unreturned, already-decoded pcm, remaining PCM we will need to
nuclear@0 2127 decode, or synthetic postextrapolation from last packets. */
nuclear@0 2128 static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd,
nuclear@0 2129 float **lappcm,int lapsize){
nuclear@0 2130 int lapcount=0,i;
nuclear@0 2131 float **pcm;
nuclear@0 2132
nuclear@0 2133 /* try first to decode the lapping data */
nuclear@0 2134 while(lapcount<lapsize){
nuclear@0 2135 int samples=vorbis_synthesis_pcmout(vd,&pcm);
nuclear@0 2136 if(samples){
nuclear@0 2137 if(samples>lapsize-lapcount)samples=lapsize-lapcount;
nuclear@0 2138 for(i=0;i<vi->channels;i++)
nuclear@0 2139 memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
nuclear@0 2140 lapcount+=samples;
nuclear@0 2141 vorbis_synthesis_read(vd,samples);
nuclear@0 2142 }else{
nuclear@0 2143 /* suck in another packet */
nuclear@0 2144 int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */
nuclear@0 2145 if(ret==OV_EOF)break;
nuclear@0 2146 }
nuclear@0 2147 }
nuclear@0 2148 if(lapcount<lapsize){
nuclear@0 2149 /* failed to get lapping data from normal decode; pry it from the
nuclear@0 2150 postextrapolation buffering, or the second half of the MDCT
nuclear@0 2151 from the last packet */
nuclear@0 2152 int samples=vorbis_synthesis_lapout(&vf->vd,&pcm);
nuclear@0 2153 if(samples==0){
nuclear@0 2154 for(i=0;i<vi->channels;i++)
nuclear@0 2155 memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount);
nuclear@0 2156 lapcount=lapsize;
nuclear@0 2157 }else{
nuclear@0 2158 if(samples>lapsize-lapcount)samples=lapsize-lapcount;
nuclear@0 2159 for(i=0;i<vi->channels;i++)
nuclear@0 2160 memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
nuclear@0 2161 lapcount+=samples;
nuclear@0 2162 }
nuclear@0 2163 }
nuclear@0 2164 }
nuclear@0 2165
nuclear@0 2166 /* this sets up crosslapping of a sample by using trailing data from
nuclear@0 2167 sample 1 and lapping it into the windowing buffer of sample 2 */
nuclear@0 2168 int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
nuclear@0 2169 vorbis_info *vi1,*vi2;
nuclear@0 2170 float **lappcm;
nuclear@0 2171 float **pcm;
nuclear@0 2172 float *w1,*w2;
nuclear@0 2173 int n1,n2,i,ret,hs1,hs2;
nuclear@0 2174
nuclear@0 2175 if(vf1==vf2)return(0); /* degenerate case */
nuclear@0 2176 if(vf1->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 2177 if(vf2->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 2178
nuclear@0 2179 /* the relevant overlap buffers must be pre-checked and pre-primed
nuclear@0 2180 before looking at settings in the event that priming would cross
nuclear@0 2181 a bitstream boundary. So, do it now */
nuclear@0 2182
nuclear@0 2183 ret=_ov_initset(vf1);
nuclear@0 2184 if(ret)return(ret);
nuclear@0 2185 ret=_ov_initprime(vf2);
nuclear@0 2186 if(ret)return(ret);
nuclear@0 2187
nuclear@0 2188 vi1=ov_info(vf1,-1);
nuclear@0 2189 vi2=ov_info(vf2,-1);
nuclear@0 2190 hs1=ov_halfrate_p(vf1);
nuclear@0 2191 hs2=ov_halfrate_p(vf2);
nuclear@0 2192
nuclear@0 2193 lappcm=alloca(sizeof(*lappcm)*vi1->channels);
nuclear@0 2194 n1=vorbis_info_blocksize(vi1,0)>>(1+hs1);
nuclear@0 2195 n2=vorbis_info_blocksize(vi2,0)>>(1+hs2);
nuclear@0 2196 w1=vorbis_window(&vf1->vd,0);
nuclear@0 2197 w2=vorbis_window(&vf2->vd,0);
nuclear@0 2198
nuclear@0 2199 for(i=0;i<vi1->channels;i++)
nuclear@0 2200 lappcm[i]=alloca(sizeof(**lappcm)*n1);
nuclear@0 2201
nuclear@0 2202 _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1);
nuclear@0 2203
nuclear@0 2204 /* have a lapping buffer from vf1; now to splice it into the lapping
nuclear@0 2205 buffer of vf2 */
nuclear@0 2206 /* consolidate and expose the buffer. */
nuclear@0 2207 vorbis_synthesis_lapout(&vf2->vd,&pcm);
nuclear@0 2208
nuclear@0 2209 #if 0
nuclear@0 2210 _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0);
nuclear@0 2211 _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0);
nuclear@0 2212 #endif
nuclear@0 2213
nuclear@0 2214 /* splice */
nuclear@0 2215 _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2);
nuclear@0 2216
nuclear@0 2217 /* done */
nuclear@0 2218 return(0);
nuclear@0 2219 }
nuclear@0 2220
nuclear@0 2221 static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos,
nuclear@0 2222 int (*localseek)(OggVorbis_File *,ogg_int64_t)){
nuclear@0 2223 vorbis_info *vi;
nuclear@0 2224 float **lappcm;
nuclear@0 2225 float **pcm;
nuclear@0 2226 float *w1,*w2;
nuclear@0 2227 int n1,n2,ch1,ch2,hs;
nuclear@0 2228 int i,ret;
nuclear@0 2229
nuclear@0 2230 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 2231 ret=_ov_initset(vf);
nuclear@0 2232 if(ret)return(ret);
nuclear@0 2233 vi=ov_info(vf,-1);
nuclear@0 2234 hs=ov_halfrate_p(vf);
nuclear@0 2235
nuclear@0 2236 ch1=vi->channels;
nuclear@0 2237 n1=vorbis_info_blocksize(vi,0)>>(1+hs);
nuclear@0 2238 w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
nuclear@0 2239 persistent; even if the decode state
nuclear@0 2240 from this link gets dumped, this
nuclear@0 2241 window array continues to exist */
nuclear@0 2242
nuclear@0 2243 lappcm=alloca(sizeof(*lappcm)*ch1);
nuclear@0 2244 for(i=0;i<ch1;i++)
nuclear@0 2245 lappcm[i]=alloca(sizeof(**lappcm)*n1);
nuclear@0 2246 _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
nuclear@0 2247
nuclear@0 2248 /* have lapping data; seek and prime the buffer */
nuclear@0 2249 ret=localseek(vf,pos);
nuclear@0 2250 if(ret)return ret;
nuclear@0 2251 ret=_ov_initprime(vf);
nuclear@0 2252 if(ret)return(ret);
nuclear@0 2253
nuclear@0 2254 /* Guard against cross-link changes; they're perfectly legal */
nuclear@0 2255 vi=ov_info(vf,-1);
nuclear@0 2256 ch2=vi->channels;
nuclear@0 2257 n2=vorbis_info_blocksize(vi,0)>>(1+hs);
nuclear@0 2258 w2=vorbis_window(&vf->vd,0);
nuclear@0 2259
nuclear@0 2260 /* consolidate and expose the buffer. */
nuclear@0 2261 vorbis_synthesis_lapout(&vf->vd,&pcm);
nuclear@0 2262
nuclear@0 2263 /* splice */
nuclear@0 2264 _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
nuclear@0 2265
nuclear@0 2266 /* done */
nuclear@0 2267 return(0);
nuclear@0 2268 }
nuclear@0 2269
nuclear@0 2270 int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 2271 return _ov_64_seek_lap(vf,pos,ov_raw_seek);
nuclear@0 2272 }
nuclear@0 2273
nuclear@0 2274 int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 2275 return _ov_64_seek_lap(vf,pos,ov_pcm_seek);
nuclear@0 2276 }
nuclear@0 2277
nuclear@0 2278 int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){
nuclear@0 2279 return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page);
nuclear@0 2280 }
nuclear@0 2281
nuclear@0 2282 static int _ov_d_seek_lap(OggVorbis_File *vf,double pos,
nuclear@0 2283 int (*localseek)(OggVorbis_File *,double)){
nuclear@0 2284 vorbis_info *vi;
nuclear@0 2285 float **lappcm;
nuclear@0 2286 float **pcm;
nuclear@0 2287 float *w1,*w2;
nuclear@0 2288 int n1,n2,ch1,ch2,hs;
nuclear@0 2289 int i,ret;
nuclear@0 2290
nuclear@0 2291 if(vf->ready_state<OPENED)return(OV_EINVAL);
nuclear@0 2292 ret=_ov_initset(vf);
nuclear@0 2293 if(ret)return(ret);
nuclear@0 2294 vi=ov_info(vf,-1);
nuclear@0 2295 hs=ov_halfrate_p(vf);
nuclear@0 2296
nuclear@0 2297 ch1=vi->channels;
nuclear@0 2298 n1=vorbis_info_blocksize(vi,0)>>(1+hs);
nuclear@0 2299 w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
nuclear@0 2300 persistent; even if the decode state
nuclear@0 2301 from this link gets dumped, this
nuclear@0 2302 window array continues to exist */
nuclear@0 2303
nuclear@0 2304 lappcm=alloca(sizeof(*lappcm)*ch1);
nuclear@0 2305 for(i=0;i<ch1;i++)
nuclear@0 2306 lappcm[i]=alloca(sizeof(**lappcm)*n1);
nuclear@0 2307 _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
nuclear@0 2308
nuclear@0 2309 /* have lapping data; seek and prime the buffer */
nuclear@0 2310 ret=localseek(vf,pos);
nuclear@0 2311 if(ret)return ret;
nuclear@0 2312 ret=_ov_initprime(vf);
nuclear@0 2313 if(ret)return(ret);
nuclear@0 2314
nuclear@0 2315 /* Guard against cross-link changes; they're perfectly legal */
nuclear@0 2316 vi=ov_info(vf,-1);
nuclear@0 2317 ch2=vi->channels;
nuclear@0 2318 n2=vorbis_info_blocksize(vi,0)>>(1+hs);
nuclear@0 2319 w2=vorbis_window(&vf->vd,0);
nuclear@0 2320
nuclear@0 2321 /* consolidate and expose the buffer. */
nuclear@0 2322 vorbis_synthesis_lapout(&vf->vd,&pcm);
nuclear@0 2323
nuclear@0 2324 /* splice */
nuclear@0 2325 _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
nuclear@0 2326
nuclear@0 2327 /* done */
nuclear@0 2328 return(0);
nuclear@0 2329 }
nuclear@0 2330
nuclear@0 2331 int ov_time_seek_lap(OggVorbis_File *vf,double pos){
nuclear@0 2332 return _ov_d_seek_lap(vf,pos,ov_time_seek);
nuclear@0 2333 }
nuclear@0 2334
nuclear@0 2335 int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){
nuclear@0 2336 return _ov_d_seek_lap(vf,pos,ov_time_seek_page);
nuclear@0 2337 }