dbf-halloween2015

annotate libs/vorbis/vorbisfile.c @ 3:c37fe5d8a4ed

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