Add an API to disable dithering.
authorTimothy B. Terriberry <tterribe@xiph.org>
Thu, 29 Aug 2013 07:36:13 +0000 (00:36 -0700)
committerTimothy B. Terriberry <tterribe@xiph.org>
Tue, 3 Sep 2013 22:03:09 +0000 (15:03 -0700)
This is to correspond to the feature in opus-tools's opusdec.
opusdec itself probably won't be able to use it, because it still
 wants to do dithering after resampling, but the motivation for the
 feature is the same.

include/opusfile.h
src/internal.h
src/opusfile.c

index ec8038c..70d2a60 100644 (file)
@@ -1777,6 +1777,18 @@ void op_set_decode_callback(OggOpusFile *_of,
 int op_set_gain_offset(OggOpusFile *_of,
  int _gain_type,opus_int32 _gain_offset_q8) OP_ARG_NONNULL(1);
 
+/**Sets whether or not dithering is enabled for 16-bit decoding.
+   By default, when <tt>libopusfile</tt> is compiled to use floating-point
+    internally, calling op_read() or op_read_stereo() will first decode to
+    float, and then convert to fixed-point using noise-shaping dithering.
+   This flag can be used to disable that dithering.
+   When the application uses op_read_float() or op_read_float_stereo(), or when
+    the library has been compiled to decode directly to fixed point, this flag
+    has no effect.
+   \param _of      The \c OggOpusFile on which to enable or disable dithering.
+   \param _enabled A non-zero value to enable dithering, or 0 to disable it.*/
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1);
+
 /**Reads more samples from the stream.
    \note Although \a _buf_size must indicate the total number of values that
     can be stored in \a _pcm, the return value is the number of samples
index bed5718..0811491 100644 (file)
@@ -235,6 +235,7 @@ struct OggOpusFile{
   float              dither_b[OP_NCHANNELS_MAX*4];
   opus_uint32        dither_seed;
   int                dither_mute;
+  int                dither_disabled;
   /*The number of channels represented by the internal state.
     This gets set to 0 whenever anything that would prevent state propagation
      occurs (switching between the float/short APIs, or between the
index c190731..9e57a88 100644 (file)
@@ -2566,6 +2566,13 @@ int op_set_gain_offset(OggOpusFile *_of,
   return 0;
 }
 
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled){
+#if !defined(OP_FIXED_POINT)
+  _of->dither_disabled=!_enabled;
+  if(!_enabled)_of->dither_mute=65;
+#endif
+}
+
 /*Allocate the decoder scratch buffer.
   This is done lazily, since if the user provides large enough buffers, we'll
    never need it.*/
@@ -3000,69 +3007,74 @@ static const float OP_FCOEF_A[4]={
 
 static void op_shaped_dither16(OggOpusFile *_of,opus_int16 *_dst,
  float *_src,int _nsamples,int _nchannels){
-  opus_uint32 seed;
-  int         mute;
-  int         ci;
-  int         i;
-  mute=_of->dither_mute;
-  seed=_of->dither_seed;
-  if(_of->state_channel_count!=_nchannels){
-    mute=65;
+  int ci;
+  int i;
 # if defined(OP_SOFT_CLIP)
+  if(_of->state_channel_count!=_nchannels){
     for(ci=0;ci<_nchannels;ci++)_of->clip_state[ci]=0;
-# endif
   }
-# if defined(OP_SOFT_CLIP)
   opus_pcm_soft_clip(_src,_nsamples,_nchannels,_of->clip_state);
 # endif
-  /*In order to avoid replacing digital silence with quiet dither noise, we
-     mute if the output has been silent for a while.*/
-  if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
-  for(i=0;i<_nsamples;i++){
-    int silent;
-    silent=1;
-    for(ci=0;ci<_nchannels;ci++){
-      float r;
-      float s;
-      float err;
-      int   si;
-      int   j;
-      s=_src[_nchannels*i+ci];
-      silent&=s==0;
-      s*=OP_GAIN;
-      err=0;
-      for(j=0;j<4;j++){
-        err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
-         -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
-      }
-      for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
-      for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
-      _of->dither_a[ci*4]=err;
-      s-=err;
-      if(mute>16)r=0;
-      else{
-        seed=op_rand(seed);
-        r=seed*OP_PRNG_GAIN;
-        seed=op_rand(seed);
-        r-=seed*OP_PRNG_GAIN;
+  if(_of->dither_disabled){
+    for(i=0;i<_nchannels*_nsamples;i++){
+      _dst[i]=op_float2int(OP_CLAMP(-32768,32768.0F*_src[i],32767));
+    }
+  }
+  else{
+    opus_uint32 seed;
+    int         mute;
+    seed=_of->dither_seed;
+    mute=_of->dither_mute;
+    if(_of->state_channel_count!=_nchannels)mute=65;
+    /*In order to avoid replacing digital silence with quiet dither noise, we
+       mute if the output has been silent for a while.*/
+    if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
+    for(i=0;i<_nsamples;i++){
+      int silent;
+      silent=1;
+      for(ci=0;ci<_nchannels;ci++){
+        float r;
+        float s;
+        float err;
+        int   si;
+        int   j;
+        s=_src[_nchannels*i+ci];
+        silent&=s==0;
+        s*=OP_GAIN;
+        err=0;
+        for(j=0;j<4;j++){
+          err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
+           -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
+        }
+        for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
+        for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
+        _of->dither_a[ci*4]=err;
+        s-=err;
+        if(mute>16)r=0;
+        else{
+          seed=op_rand(seed);
+          r=seed*OP_PRNG_GAIN;
+          seed=op_rand(seed);
+          r-=seed*OP_PRNG_GAIN;
+        }
+        /*Clamp in float out of paranoia that the input will be > 96 dBFS and
+           wrap if the integer is clamped.*/
+        si=op_float2int(OP_CLAMP(-32768,s+r,32767));
+        _dst[_nchannels*i+ci]=(opus_int16)si;
+        /*Including clipping in the noise shaping is generally disastrous: the
+           futile effort to restore the clipped energy results in more clipping.
+          However, small amounts---at the level which could normally be created
+           by dither and rounding---are harmless and can even reduce clipping
+           somewhat due to the clipping sometimes reducing the dither + rounding
+           error.*/
+        _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
       }
-      /*Clamp in float out of paranoia that the input will be > 96 dBFS and
-         wrap if the integer is clamped.*/
-      si=op_float2int(OP_CLAMP(-32768,s+r,32767));
-      _dst[_nchannels*i+ci]=(opus_int16)si;
-      /*Including clipping in the noise shaping is generally disastrous: the
-         futile effort to restore the clipped energy results in more clipping.
-        However, small amounts---at the level which could normally be created
-         by dither and rounding---are harmless and can even reduce clipping
-         somewhat due to the clipping sometimes reducing the dither + rounding
-         error.*/
-      _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
-    }
-    mute++;
-    if(!silent)mute=0;
-  }
-  _of->dither_mute=OP_MIN(mute,65);
-  _of->dither_seed=seed;
+      mute++;
+      if(!silent)mute=0;
+    }
+    _of->dither_mute=OP_MIN(mute,65);
+    _of->dither_seed=seed;
+  }
   _of->state_channel_count=_nchannels;
 }