Add OPUS_{GET|SET}_GAIN CTLs for adjusting output gain.

This CTL was requested by Nicolas George for FFmpeg.
This commit is contained in:
Gregory Maxwell 2012-07-11 00:04:24 -04:00
parent c329045758
commit 28b41ae5ae
7 changed files with 193 additions and 11 deletions

View 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))

View 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))))

View 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))

View 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
* @{
*/

View 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;

View file

@ -846,6 +846,26 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
*value = (OpusDecoder*)ptr;
}
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;

View 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));
@ -359,15 +384,6 @@ opus_int32 test_msdec_api(void)
opus_multistream_decoder_destroy(dec);
cfgs++;
VG_UNDEF(&err,sizeof(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++;
opus_multistream_decoder_destroy(dec);
cfgs++;
VG_UNDEF(&err,sizeof(err));
dec = opus_multistream_decoder_create(48000, 1, 4, 1, mapping, &err);
if(err!=OPUS_OK || dec==NULL)test_failed();
@ -379,6 +395,15 @@ opus_int32 test_msdec_api(void)
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));
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++;
fprintf(stdout," opus_multistream_decoder_create() ............ OK.\n");
fprintf(stdout," opus_multistream_decoder_init() .............. OK.\n");
@ -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;