While reviewing a patch to port Tremor r17541, noticed that the
authorMonty <xiphmont@xiph.org>
Sat, 23 Oct 2010 10:29:11 +0000 (10:29 +0000)
committerMonty <xiphmont@xiph.org>
Sat, 23 Oct 2010 10:29:11 +0000 (10:29 +0000)
half-rate decode code in Vorbisfile was never completed past the
original 'quick and dirty' feature request.  Specifically, it made no
attempt to keep the pcm offset tracking consistent in seeks.

Complete this code, and add a testing mode to seeking_example.c to
torture test seeking in halfrate mode.  Also remove requirement that
halfrate mode only work with seekable files.

svn path=/trunk/vorbis/; revision=17560

examples/seeking_example.c
lib/vorbisfile.c

index a968a81..6355359 100644 (file)
@@ -29,11 +29,13 @@ void _verify(OggVorbis_File *ov,
              ogg_int64_t val,ogg_int64_t pcmval,double timeval,
              ogg_int64_t pcmlength,
              char *bigassbuffer){
+  off_t i;
   int j;
   long bread;
   char buffer[4096];
   int dummy;
   ogg_int64_t pos;
+  int hs = ov_halfrate_p(ov);
 
   /* verify the raw position, the pcm position and position decode */
   if(val!=-1 && ov_raw_tell(ov)<val){
@@ -58,15 +60,24 @@ void _verify(OggVorbis_File *ov,
   }
   bread=ov_read(ov,buffer,4096,1,1,1,&dummy);
   for(j=0;j<bread;j++){
-    if(buffer[j]!=bigassbuffer[j+pos*2]){
-      fprintf(stderr,"data position after seek doesn't match pcm position\n");
-
+    if(buffer[j]!=bigassbuffer[j+((pos>>hs)*2)]){
+      fprintf(stderr,"data after seek doesn't match declared pcm position %lld\n",pos);
+
+      for(i=0;i<(pcmlength>>hs)*2-bread;i++){
+        for(j=0;j<bread;j++)
+          if(buffer[j] != bigassbuffer[i+j])break;
+        if(j==bread){
+          fprintf(stderr,"data after seek appears to match position %lld\n",(i/2)<<hs);
+        }
+      }
       {
         FILE *f=fopen("a.m","w");
-        for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)buffer[j]);
+        for(j=0;j<bread;j++)fprintf(f,"%d %d\n",j,(int)buffer[j]);
         fclose(f);
         f=fopen("b.m","w");
-        for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)bigassbuffer[j+pos*2]);
+        for(j=-4096;j<bread+4096;j++)
+          if(j+((pos*2)>>hs)>=0 && (j+((pos*2)>>hs))<(pcmlength>>hs)*2)
+             fprintf(f,"%d %d\n",j,(int)bigassbuffer[j+((pos*2)>>hs)]);
         fclose(f);
       }
 
@@ -82,6 +93,7 @@ int main(){
   double timelength;
   char *bigassbuffer;
   int dummy;
+  int hs=0;
 
 #ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
   _setmode( _fileno( stdin ), _O_BINARY );
@@ -94,6 +106,14 @@ int main(){
     exit(1);
   }
 
+#if 0 /*enable this code to test seeking with halfrate decode */
+  if(ov_halfrate(&ov,1)){
+    fprintf(stderr,"Sorry; unable to set half-rate decode.\n\n");
+    exit(1);
+  }else
+    hs=1;
+#endif
+
   if(ov_seekable(&ov)){
 
     /* to simplify our own lives, we want to assume the whole file is
@@ -112,10 +132,10 @@ int main(){
        does what it claimed, decode the entire file into memory */
     pcmlength=ov_pcm_total(&ov,-1);
     timelength=ov_time_total(&ov,-1);
-    bigassbuffer=malloc(pcmlength*2); /* w00t */
+    bigassbuffer=malloc((pcmlength>>hs)*2); /* w00t */
     i=0;
-    while(i<pcmlength*2){
-      int ret=ov_read(&ov,bigassbuffer+i,pcmlength*2-i,1,1,1,&dummy);
+    while(i<(pcmlength>>hs)*2){
+      int ret=ov_read(&ov,bigassbuffer+i,((pcmlength>>hs)*2)-i,1,1,1,&dummy);
       if(ret<0){
         fprintf(stderr,"Error reading file.\n");
         exit(1);
@@ -123,10 +143,10 @@ int main(){
       if(ret){
         i+=ret;
       }else{
-        pcmlength=i/2;
+        pcmlength=(i/2)<<hs;
       }
       fprintf(stderr,"\rloading.... [%ld left]              ",
-              (long)(pcmlength*2-i));
+              (long)((pcmlength>>hs)*2-i));
     }
     
     {
@@ -166,7 +186,7 @@ int main(){
 
       }
     }
-    
+
     fprintf(stderr,"\r");
     {
       fprintf(stderr,"testing pcm exact seeking to random places in %ld samples....\n",
@@ -180,7 +200,7 @@ int main(){
           fprintf(stderr,"seek failed: %d\n",ret);
           exit(1);
         }
-        if(ov_pcm_tell(&ov)!=val){
+        if(ov_pcm_tell(&ov)!=((val>>hs)<<hs)){
           fprintf(stderr,"Declared position didn't perfectly match request: %ld != %ld\n",
                  (long)val,(long)ov_pcm_tell(&ov));
           exit(1);
index c775c50..029226c 100644 (file)
@@ -689,6 +689,8 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
     /* process a packet if we can. */
 
     if(vf->ready_state==INITSET){
+      int hs=vorbis_synthesis_halfrate_p(vf->vi);
+
       while(1) {
               ogg_packet op;
               ogg_packet *op_ptr=(op_in?op_in:&op);
@@ -716,7 +718,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
               if(oldsamples)return(OV_EFAULT);
 
               vorbis_synthesis_blockin(&vf->vd,&vf->vb);
-              vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
+              vf->samptrack+=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs);
               vf->bittrack+=op_ptr->bytes*8;
             }
 
@@ -745,7 +747,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
                                                here unless the stream
                                                is very broken */
 
-              samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+              samples=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs);
 
               granulepos-=samples;
               for(i=0;i<link;i++)
@@ -1022,17 +1024,22 @@ int ov_fopen(const char *path,OggVorbis_File *vf){
 int ov_halfrate(OggVorbis_File *vf,int flag){
   int i;
   if(vf->vi==NULL)return OV_EINVAL;
-  if(!vf->seekable)return OV_EINVAL;
-  if(vf->ready_state>=STREAMSET)
-    _decode_clear(vf); /* clear out stream state; later on libvorbis
-                          will be able to swap this on the fly, but
-                          for now dumping the decode machine is needed
-                          to reinit the MDCT lookups.  1.1 libvorbis
-                          is planned to be able to switch on the fly */
+  if(vf->ready_state>STREAMSET){
+    /* clear out stream state; dumping the decode machine is needed to
+       reinit the MDCT lookups. */
+    vorbis_dsp_clear(&vf->vd);
+    vorbis_block_clear(&vf->vb);
+    vf->ready_state=STREAMSET;
+    if(vf->pcm_offset>=0){
+      ogg_int64_t pos=vf->pcm_offset;
+      vf->pcm_offset=-1; /* make sure the pos is dumped if unseekable */
+      ov_pcm_seek(vf,pos);
+    }
+  }
 
   for(i=0;i<vf->links;i++){
     if(vorbis_synthesis_halfrate(vf->vi+i,flag)){
-      ov_halfrate(vf,0);
+      if(flag) ov_halfrate(vf,0);
       return OV_EINVAL;
     }
   }
@@ -1657,17 +1664,22 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
   vf->samptrack=0.f;
   /* discard samples until we reach the desired position. Crossing a
      logical bitstream boundary with abandon is OK. */
-  while(vf->pcm_offset<pos){
-    ogg_int64_t target=pos-vf->pcm_offset;
-    long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
-
-    if(samples>target)samples=target;
-    vorbis_synthesis_read(&vf->vd,samples);
-    vf->pcm_offset+=samples;
-
-    if(samples<target)
-      if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
-        vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
+  {
+    /* note that halfrate could be set differently in each link, but
+       vorbisfile encoforces all links are set or unset */
+    int hs=vorbis_synthesis_halfrate_p(vf->vi);
+    while(vf->pcm_offset<((pos>>hs)<<hs)){
+      ogg_int64_t target=(pos-vf->pcm_offset)>>hs;
+      long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+
+      if(samples>target)samples=target;
+      vorbis_synthesis_read(&vf->vd,samples);
+      vf->pcm_offset+=samples<<hs;
+
+      if(samples<target)
+        if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
+          vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
+    }
   }
   return 0;
 }
@@ -1858,6 +1870,7 @@ long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
                     void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param){
   int i,j;
   int host_endian = host_is_big_endian();
+  int hs;
 
   float **pcm;
   long samples;
@@ -1981,7 +1994,8 @@ long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
     }
 
     vorbis_synthesis_read(&vf->vd,samples);
-    vf->pcm_offset+=samples;
+    hs=vorbis_synthesis_halfrate_p(vf->vi);
+    vf->pcm_offset+=(samples<<hs);
     if(bitstream)*bitstream=vf->current_link;
     return(samples*bytespersample);
   }else{
@@ -2018,10 +2032,11 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
       float **pcm;
       long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
       if(samples){
+        int hs=vorbis_synthesis_halfrate_p(vf->vi);
         if(pcm_channels)*pcm_channels=pcm;
         if(samples>length)samples=length;
         vorbis_synthesis_read(&vf->vd,samples);
-        vf->pcm_offset+=samples;
+        vf->pcm_offset+=samples<<hs;
         if(bitstream)*bitstream=vf->current_link;
         return samples;