Add OPUS_{GET|SET}_GAIN CTLs for adjusting output gain.
authorGregory Maxwell <greg@xiph.org>
Wed, 11 Jul 2012 04:04:24 +0000 (00:04 -0400)
committerGregory Maxwell <greg@xiph.org>
Wed, 11 Jul 2012 04:04:24 +0000 (00:04 -0400)
This CTL was requested by Nicolas George for FFmpeg.

celt/arch.h
celt/fixed_debug.h
celt/fixed_generic.h
include/opus_defines.h
src/opus_decoder.c
src/opus_multistream.c
tests/test_opus_api.c

index a7cfbe8..03cda40 100644 (file)
@@ -188,6 +188,7 @@ typedef float celt_ener;
 #define MULT16_16_P15(a,b)     ((a)*(b))
 #define MULT16_16_P13(a,b)     ((a)*(b))
 #define MULT16_16_P14(a,b)     ((a)*(b))
+#define MULT16_32_P16(a,b)     ((a)*(b))
 
 #define DIV32_16(a,b)     (((opus_val32)(a))/(opus_val16)(b))
 #define DIV32(a,b)     (((opus_val32)(a))/(opus_val32)(b))
index 89b3cf7..ed95cba 100644 (file)
@@ -47,6 +47,8 @@ extern long long celt_mips;
 /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */
 #define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR32((b),16)), SHR32(MULT16_16SU((a),((b)&0x0000ffff)),16))
 
+#define MULT16_32_P16(a,b) MULT16_32_PX(a,b,16)
+
 #define QCONST16(x,bits) ((opus_val16)(.5+(x)*(((opus_val32)1)<<(bits))))
 #define QCONST32(x,bits) ((opus_val32)(.5+(x)*(((opus_val32)1)<<(bits))))
 
@@ -459,6 +461,39 @@ static inline int MULT16_32_QX_(int a, long long b, int Q, char *file, int line)
    return res;
 }
 
+#define MULT16_32_PX(a, b, Q) MULT16_32_PX_(a, b, Q, __FILE__, __LINE__)
+static inline int MULT16_32_PX_(int a, long long b, int Q, char *file, int line)
+{
+   long long res;
+   if (!VERIFY_SHORT(a) || !VERIFY_INT(b))
+   {
+      fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d in %s: line %d\n\n", Q, (int)a, (int)b, file, line);
+#ifdef FIXED_DEBUG_ASSERT
+      celt_assert(0);
+#endif
+   }
+   if (ABS32(b)>=((opus_val32)(1)<<(15+Q)))
+   {
+      fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n\n", Q, (int)a, (int)b, file, line);
+#ifdef FIXED_DEBUG_ASSERT
+      celt_assert(0);
+#endif
+   }
+   res = ((((long long)a)*(long long)b) + (((opus_val32)(1)<<Q)>>1))>> Q;
+   if (!VERIFY_INT(res))
+   {
+      fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d in %s: line %d\n\n", Q, (int)a, (int)b,(int)res, file, line);
+#ifdef FIXED_DEBUG_ASSERT
+      celt_assert(0);
+#endif
+   }
+   if (Q==15)
+      celt_mips+=4;
+   else
+      celt_mips+=5;
+   return res;
+}
+
 #define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15)
 #define MAC16_32_Q15(c,a,b) (celt_mips-=2,ADD32((c),MULT16_32_Q15((a),(b))))
 
index d5ea121..71e28d6 100644 (file)
@@ -39,6 +39,9 @@
 /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */
 #define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16))
 
+/** 16x32 multiplication, followed by a 16-bit shift right (round-to-nearest). Results fits in 32 bits */
+#define MULT16_32_P16(a,b) ADD32(MULT16_16((a),SHR((b),16)), PSHR(MULT16_16((a),((b)&0x0000ffff)),16))
+
 /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */
 #define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15))
 
index 54d198b..de04ec5 100644 (file)
@@ -128,6 +128,8 @@ extern "C" {
 /* #define OPUS_RESET_STATE 4028 */
 #define OPUS_GET_FINAL_RANGE_REQUEST         4031
 #define OPUS_GET_PITCH_REQUEST               4033
+#define OPUS_SET_GAIN_REQUEST                4034
+#define OPUS_GET_GAIN_REQUEST                4045
 
 /* Macros to trigger compilation errors when the wrong types are provided to a CTL */
 #define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
@@ -400,7 +402,7 @@ extern "C" {
   * }
   * @endcode
   *
-  * @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl
+  * @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl, opus_decoderctls, opus_encoderctls
   * @{
   */
 
@@ -440,6 +442,29 @@ extern "C" {
 
 /**@}*/
 
+/** @defgroup opus_decoderctls Decoder related CTLs
+  * @see opus_genericctls, opus_encoderctls, opus_decoder
+  * @{
+  */
+
+/** Configures decoder gain adjustment.
+  * Scales the decoded output by a factor specified in Q8 dB units.
+  * This has a maximum range of -32768 to 32767 inclusive, and returns
+  * OPUS_BAD_ARG otherwise.
+  *
+  * gain = pow(10, x/(20.0*256))
+  *
+  * @param[in] x <tt>opus_int32</tt>:   Amount to scale PCM signal by in Q8 dB units.
+  * @hideinitializer */
+#define OPUS_SET_GAIN(x) OPUS_SET_GAIN_REQUEST, __opus_check_int(x)
+/** Gets the decoder's configured gain adjustment. @see OPUS_SET_GAIN
+  *
+  * @param[out] x <tt>opus_int32*</tt>: Amount to scale PCM signal by in Q8 dB units.
+  * @hideinitializer */
+#define OPUS_GET_GAIN(x) OPUS_GET_GAIN_REQUEST, __opus_check_int_ptr(x)
+
+/**@}*/
+
 /** @defgroup opus_libinfo Opus library information functions
   * @{
   */
index 24869e7..2ce4c95 100644 (file)
@@ -45,6 +45,7 @@
 #include "os_support.h"
 #include "structs.h"
 #include "define.h"
+#include "mathops.h"
 
 struct OpusDecoder {
    int          celt_dec_offset;
@@ -62,6 +63,7 @@ struct OpusDecoder {
    int          prev_mode;
    int          frame_size;
    int          prev_redundancy;
+   int          decode_gain;
 
    opus_uint32  rangeFinal;
 };
@@ -503,6 +505,18 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
       }
    }
 
+   if(st->decode_gain)
+   {
+      opus_val32 gain;
+      gain = celt_exp2(MULT16_16_P15(QCONST16(6.48814081e-4f, 25), st->decode_gain));
+      for (i=0;i<frame_size*st->channels;i++)
+      {
+         opus_val32 x;
+         x = MULT16_32_P16(pcm[i],gain);
+         pcm[i] = SATURATE(x, 32767);
+      }
+   }
+
    if (len <= 1)
       st->rangeFinal = 0;
    else
@@ -856,6 +870,28 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
          *value = st->DecControl.prevPitchLag;
    }
    break;
+   case OPUS_GET_GAIN_REQUEST:
+   {
+      opus_int32 *value = va_arg(ap, opus_int32*);
+      if (value==NULL)
+      {
+         ret = OPUS_BAD_ARG;
+         break;
+      }
+      *value = st->decode_gain;
+   }
+   break;
+   case OPUS_SET_GAIN_REQUEST:
+   {
+       opus_int32 value = va_arg(ap, opus_int32);
+       if (value<-32768 || value>32767)
+       {
+          ret = OPUS_BAD_ARG;
+          break;
+       }
+       st->decode_gain = value;
+   }
+   break;
    default:
       /*fprintf(stderr, "unknown opus_decoder_ctl() request: %d", request);*/
       ret = OPUS_UNIMPLEMENTED;
index b593bf3..1128c07 100644 (file)
@@ -845,7 +845,27 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
           }
           *value = (OpusDecoder*)ptr;
        }
-          break;
+       break;
+       case OPUS_SET_GAIN_REQUEST:
+       {
+          int s;
+          /* This works for int32 params */
+          opus_int32 value = va_arg(ap, opus_int32);
+          for (s=0;s<st->layout.nb_streams;s++)
+          {
+             OpusDecoder *dec;
+
+             dec = (OpusDecoder*)ptr;
+             if (s < st->layout.nb_coupled_streams)
+                ptr += align(coupled_size);
+             else
+                ptr += align(mono_size);
+             ret = opus_decoder_ctl(dec, request, value);
+             if (ret != OPUS_OK)
+                break;
+          }
+       }
+       break;
        default:
           ret = OPUS_UNIMPLEMENTED;
        break;
index 31ca5a5..641f6ea 100644 (file)
@@ -187,6 +187,31 @@ opus_int32 test_dec_api(void)
    cfgs++;
    fprintf(stdout,"    OPUS_GET_PITCH ............................... OK.\n");
 
+   VG_UNDEF(&i,sizeof(i));
+   err=opus_decoder_ctl(dec, OPUS_GET_GAIN(&i));
+   VG_CHECK(&i,sizeof(i));
+   if(err != OPUS_OK || i!=0)test_failed();
+   cfgs++;
+   err=opus_decoder_ctl(dec, OPUS_GET_GAIN(nullvalue));
+   if(err != OPUS_BAD_ARG)test_failed();
+   cfgs++;
+   err=opus_decoder_ctl(dec, OPUS_SET_GAIN(-32769));
+   if(err != OPUS_BAD_ARG)test_failed();
+   cfgs++;
+   err=opus_decoder_ctl(dec, OPUS_SET_GAIN(32768));
+   if(err != OPUS_BAD_ARG)test_failed();
+   cfgs++;
+   err=opus_decoder_ctl(dec, OPUS_SET_GAIN(-15));
+   if(err != OPUS_OK)test_failed();
+   cfgs++;
+   VG_UNDEF(&i,sizeof(i));
+   err=opus_decoder_ctl(dec, OPUS_GET_GAIN(&i));
+   VG_CHECK(&i,sizeof(i));
+   if(err != OPUS_OK || i!=-15)test_failed();
+   cfgs++;
+   fprintf(stdout,"    OPUS_SET_GAIN ................................ OK.\n");
+   fprintf(stdout,"    OPUS_GET_GAIN ................................ OK.\n");
+
    /*Reset the decoder*/
    dec2=malloc(opus_decoder_get_size(2));
    memcpy(dec2,dec,opus_decoder_get_size(2));
@@ -360,23 +385,23 @@ opus_int32 test_msdec_api(void)
    cfgs++;
 
    VG_UNDEF(&err,sizeof(err));
-   mapping[0]=0;
-   mapping[1]=1;
-   dec = opus_multistream_decoder_create(48000, 2, 2, 0, mapping, &err);
+   dec = opus_multistream_decoder_create(48000, 1, 4, 1, mapping, &err);
    if(err!=OPUS_OK || dec==NULL)test_failed();
    cfgs++;
    opus_multistream_decoder_destroy(dec);
    cfgs++;
 
    VG_UNDEF(&err,sizeof(err));
-   dec = opus_multistream_decoder_create(48000, 1, 4, 1, mapping, &err);
+   dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err);
    if(err!=OPUS_OK || dec==NULL)test_failed();
    cfgs++;
    opus_multistream_decoder_destroy(dec);
    cfgs++;
 
    VG_UNDEF(&err,sizeof(err));
-   dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err);
+   mapping[0]=0;
+   mapping[1]=1;
+   dec = opus_multistream_decoder_create(48000, 2, 2, 0, mapping, &err);
    if(err!=OPUS_OK || dec==NULL)test_failed();
    cfgs++;
 
@@ -396,6 +421,9 @@ opus_int32 test_msdec_api(void)
    if(err!=OPUS_BAD_ARG)test_failed();
    cfgs++;
    err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(1,&streamdec));
+   if(err!=OPUS_OK||streamdec==NULL)test_failed();
+   cfgs++;
+   err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(2,&streamdec));
    if(err!=OPUS_BAD_ARG)test_failed();
    cfgs++;
    err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(0,&streamdec));
@@ -404,6 +432,33 @@ opus_int32 test_msdec_api(void)
    fprintf(stdout,"    OPUS_MULTISTREAM_GET_DECODER_STATE ........... OK.\n");
    cfgs++;
 
+   for(j=0;j<2;j++)
+   {
+      OpusDecoder *od;
+      err=opus_multistream_decoder_ctl(dec,OPUS_MULTISTREAM_GET_DECODER_STATE(j,&od));
+      if(err != OPUS_OK)test_failed();
+      VG_UNDEF(&i,sizeof(i));
+      err=opus_decoder_ctl(od, OPUS_GET_GAIN(&i));
+      VG_CHECK(&i,sizeof(i));
+      if(err != OPUS_OK || i!=0)test_failed();
+      cfgs++;
+   }
+   err=opus_multistream_decoder_ctl(dec,OPUS_SET_GAIN(15));
+   if(err!=OPUS_OK)test_failed();
+   fprintf(stdout,"    OPUS_SET_GAIN ................................ OK.\n");
+   for(j=0;j<2;j++)
+   {
+      OpusDecoder *od;
+      err=opus_multistream_decoder_ctl(dec,OPUS_MULTISTREAM_GET_DECODER_STATE(j,&od));
+      if(err != OPUS_OK)test_failed();
+      VG_UNDEF(&i,sizeof(i));
+      err=opus_decoder_ctl(od, OPUS_GET_GAIN(&i));
+      VG_CHECK(&i,sizeof(i));
+      if(err != OPUS_OK || i!=15)test_failed();
+      cfgs++;
+   }
+   fprintf(stdout,"    OPUS_GET_GAIN ................................ OK.\n");
+
    err=opus_multistream_decoder_ctl(dec,OPUS_UNIMPLEMENTED);
    if(err!=OPUS_UNIMPLEMENTED)test_failed();
    fprintf(stdout,"    OPUS_UNIMPLEMENTED ........................... OK.\n");
@@ -447,6 +502,13 @@ opus_int32 test_msdec_api(void)
    fprintf(stdout,"    OPUS_RESET_STATE ............................. OK.\n");
    cfgs++;
 
+   opus_multistream_decoder_destroy(dec);
+   cfgs++;
+   VG_UNDEF(&err,sizeof(err));
+   dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err);
+   if(err!=OPUS_OK || dec==NULL)test_failed();
+   cfgs++;
+
    packet[0]=(63<<2)+3;
    packet[1]=49;
    for(j=2;j<51;j++)packet[j]=0;