ref: 32c4a0c96e239bee7623aef8ae592a5c7f7ec753
parent: 69c3dcd105432a72220478cbde851cc7917e5768
author: Jean-Marc Valin <[email protected]>
date: Fri Mar 1 10:18:23 EST 2013
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.
--- a/include/opus.h
+++ b/include/opus.h
@@ -592,6 +592,20 @@
* @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 <tt>float*</tt>: Input PCM and modified PCM
+ * @param [in] frame_size <tt>int</tt> Number of samples per channel to process
+ * @param [in] channels <tt>int</tt>: Number of channels
+ * @param [in,out] softclip_mem <tt>float*</tt>: 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
--- 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<N*C;i++)
+ _x[i] = MAX16(-2.f, MIN16(2.f, _x[i]));
+ for (c=0;c<C;c++)
+ {
+ float a;
+ float x0;
+ int curr;
+
+ x = _x+c;
+ a = declip_mem[c];
+ /* Continue applying the non-linearity from the previous frame to avoid
+ any discontinuity. */
+ for (i=0;i<N;i++)
+ {
+ if (x[i*C]*a>=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;i<N;i++)
+ {
+ if (x[i*C]>1 || 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<N && x[i*C]*x[end*C]>=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<end;i++)
+ x[i*C] = x[i*C]+a*x[i*C]*x[i*C];
+
+ if (special && peak_pos>=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;i<peak_pos;i++)
+ {
+ offset -= delta;
+ x[i*C] += offset;
+ x[i*C] = MAX16(-1.f, MIN16(1.f, x[i*C]));
+ }
+ }
+ curr = end;
+ if (curr==N)
+ break;
+ }
+ declip_mem[c] = a;
+ }
+}
+#endif
+
int encode_size(int size, unsigned char *data)
{
if (size < 252)
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -65,6 +65,9 @@
int frame_size;
int prev_redundancy;
int last_packet_duration;
+#ifndef FIXED_POINT
+ opus_val16 softclip_mem[2];
+#endif
opus_uint32 rangeFinal;
};
@@ -732,7 +735,7 @@
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)
+ int self_delimited, int *packet_offset, int soft_clip)
{
int i, nb_samples;
int count, offset;
@@ -779,10 +782,10 @@
int ret;
/* If no FEC can be present, run the PLC (recursive call) */
if (frame_size <= packet_frame_size || packet_mode == MODE_CELT_ONLY || st->mode == 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 @@
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(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 @@
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;i<ret*st->channels;i++)
@@ -886,7 +895,7 @@
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;i<ret*st->channels;i++)
@@ -899,7 +908,7 @@
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
--- a/src/opus_multistream_decoder.c
+++ b/src/opus_multistream_decoder.c
@@ -159,7 +159,8 @@
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 @@
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 @@
)
{
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 @@
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 @@
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 @@
)
{
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
--- a/src/opus_private.h
+++ b/src/opus_private.h
@@ -88,7 +88,8 @@
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)