From 32c4a0c96e239bee7623aef8ae592a5c7f7ec753 Mon Sep 17 00:00:00 2001 From: Jean-Marc Valin Date: Fri, 1 Mar 2013 15:18:23 -0500 Subject: Applies soft-clipping to the int decoder API. opus_decode() and opus_multistream_decode() now apply soft clipping before converting to 16-bit int. This should produce better a higher quality result than hard clipping like we were doing before. The _float() API isn't affected, but the clipping function is exported so users can manually apply the soft clipping. --- include/opus.h | 14 ++++++ src/opus.c | 99 ++++++++++++++++++++++++++++++++++++++++++ src/opus_decoder.c | 23 +++++++--- src/opus_multistream_decoder.c | 13 +++--- src/opus_private.h | 3 +- 5 files changed, 138 insertions(+), 14 deletions(-) diff --git a/include/opus.h b/include/opus.h index 02033a9d..180146ae 100644 --- a/include/opus.h +++ b/include/opus.h @@ -592,6 +592,20 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigne * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2); + +/** Applies soft-clipping to bring a float signal within the [-1,1] range. If + * the signal is already in that range, nothing is done. If there are values + * outside of [-1,1], then the signal is clipped as smoothly as possible to + * both fit in the range and avoid creating excessive distortion in the + * process. + * @param [in,out] pcm float*: Input PCM and modified PCM + * @param [in] frame_size int Number of samples per channel to process + * @param [in] channels int: Number of channels + * @param [in,out] softclip_mem float*: State memory for the soft clipping process + */ +OPUS_EXPORT void opus_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem); + + /**@}*/ /** @defgroup opus_repacketizer Repacketizer diff --git a/src/opus.c b/src/opus.c index d6ae7bab..170bc4b6 100644 --- a/src/opus.c +++ b/src/opus.c @@ -32,6 +32,105 @@ #include "opus.h" #include "opus_private.h" +#ifndef DISABLE_FLOAT_API +OPUS_EXPORT void opus_pcm_soft_clip(float *_x, int N, int C, float *declip_mem) +{ + int c; + int i; + float *x; + + /* First thing: saturate everything to +/- 2 which is the highest level our + non-linearity can handle. At the point where the signal reaches +/-2, + the derivative will be zero anyway, so this doesn't introduce any + discontinuity in the derivative. */ + for (i=0;i=0) + break; + x[i*C] = x[i*C]+a*x[i*C]*x[i*C]; + } + + curr=0; + x0 = x[0]; + while(1) + { + int start, end; + float maxval; + int special=0; + int peak_pos; + for (i=curr;i1 || x[i*C]<-1) + break; + } + if (i==N) + { + a=0; + break; + } + peak_pos = i; + start=end=i; + maxval=ABS16(x[i*C]); + /* Look for first zero crossing before clipping */ + while (start>0 && x[i*C]*x[(start-1)*C]>=0) + start--; + /* Look for first zero crossing after clipping */ + while (end=0) + { + /* Look for other peaks until the next zero-crossing. */ + if (ABS16(x[end*C])>maxval) + { + maxval = ABS16(x[end*C]); + peak_pos = end; + } + end++; + } + /* Detect the special case where we clip before the first zero crossing */ + special = (start==0 && x[i*C]*x[0]>=0); + + /* Compute a such that maxval + a*maxval^2 = 1 */ + a=(maxval-1)/(maxval*maxval); + if (x[i*C]>0) + a = -a; + /* Apply soft clipping */ + for (i=start;i=2) + { + /* Add a linear ramp from the first sample to the signal peak. + This avoids a discontinuity at the beginning of the frame. */ + float delta; + float offset = x0-x[0]; + delta = offset / peak_pos; + for (i=curr;imode == MODE_CELT_ONLY) - return opus_decode_native(st, NULL, 0, pcm, frame_size, 0, 0, NULL); + return opus_decode_native(st, NULL, 0, pcm, frame_size, 0, 0, NULL, soft_clip); /* Otherwise, run the PLC on everything except the size for which we might have FEC */ duration_copy = st->last_packet_duration; - ret = opus_decode_native(st, NULL, 0, pcm, frame_size-packet_frame_size, 0, 0, NULL); + ret = opus_decode_native(st, NULL, 0, pcm, frame_size-packet_frame_size, 0, 0, NULL, soft_clip); if (ret<0) { st->last_packet_duration = duration_copy; @@ -837,6 +840,12 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data, st->last_packet_duration = nb_samples; if (OPUS_CHECK_ARRAY(pcm, nb_samples*st->channels)) OPUS_PRINT_INT(nb_samples); +#ifndef FIXED_POINT + if (soft_clip) + opus_pcm_soft_clip(pcm, nb_samples, st->channels, st->softclip_mem); + else + st->softclip_mem[0]=st->softclip_mem[1]=0; +#endif return nb_samples; } @@ -845,7 +854,7 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data, int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec) { - return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL); + return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0); } #ifndef DISABLE_FLOAT_API @@ -858,7 +867,7 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data, ALLOC(out, frame_size*st->channels, opus_int16); - ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL); + ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 0); if (ret > 0) { for (i=0;ichannels;i++) @@ -886,7 +895,7 @@ int opus_decode(OpusDecoder *st, const unsigned char *data, ALLOC(out, frame_size*st->channels, float); - ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL); + ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 1); if (ret > 0) { for (i=0;ichannels;i++) @@ -899,7 +908,7 @@ int opus_decode(OpusDecoder *st, const unsigned char *data, int opus_decode_float(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec) { - return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL); + return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0); } #endif diff --git a/src/opus_multistream_decoder.c b/src/opus_multistream_decoder.c index 7564c735..47f87b19 100644 --- a/src/opus_multistream_decoder.c +++ b/src/opus_multistream_decoder.c @@ -159,7 +159,8 @@ static int opus_multistream_decode_native( void *pcm, opus_copy_channel_out_func copy_channel_out, int frame_size, - int decode_fec + int decode_fec, + int soft_clip ) { opus_int32 Fs; @@ -199,7 +200,7 @@ static int opus_multistream_decode_native( return OPUS_INVALID_PACKET; } packet_offset = 0; - ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset); + ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset, soft_clip); data += packet_offset; len -= packet_offset; if (ret > frame_size) @@ -333,7 +334,7 @@ int opus_multistream_decode( ) { return opus_multistream_decode_native(st, data, len, - pcm, opus_copy_channel_out_short, frame_size, decode_fec); + pcm, opus_copy_channel_out_short, frame_size, decode_fec, 0); } #ifndef DISABLE_FLOAT_API @@ -341,7 +342,7 @@ int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec) { return opus_multistream_decode_native(st, data, len, - pcm, opus_copy_channel_out_float, frame_size, decode_fec); + pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0); } #endif @@ -351,7 +352,7 @@ int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec) { return opus_multistream_decode_native(st, data, len, - pcm, opus_copy_channel_out_short, frame_size, decode_fec); + pcm, opus_copy_channel_out_short, frame_size, decode_fec, 1); } int opus_multistream_decode_float( @@ -364,7 +365,7 @@ int opus_multistream_decode_float( ) { return opus_multistream_decode_native(st, data, len, - pcm, opus_copy_channel_out_float, frame_size, decode_fec); + pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0); } #endif diff --git a/src/opus_private.h b/src/opus_private.h index 977f4a25..c9a4ff53 100644 --- a/src/opus_private.h +++ b/src/opus_private.h @@ -88,7 +88,8 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_ unsigned char *data, opus_int32 out_data_bytes, int lsb_depth); int opus_decode_native(OpusDecoder *st, const unsigned char *data, opus_int32 len, - opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, int *packet_offset); + opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, + int *packet_offset, int soft_clip); /* Make sure everything's aligned to sizeof(void *) bytes */ static inline int align(int i) -- cgit v1.2.3