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.
This commit is contained in:
Jean-Marc Valin 2013-03-01 15:18:23 -05:00
parent 69c3dcd105
commit 32c4a0c96e
5 changed files with 138 additions and 14 deletions

View file

@ -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 <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

View file

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

View file

@ -65,6 +65,9 @@ struct OpusDecoder {
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_packet_parse(const unsigned char *data, opus_int32 len,
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 opus_decode_native(OpusDecoder *st, const unsigned char *data,
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 @@ 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;i<ret*st->channels;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;i<ret*st->channels;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

View file

@ -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

View file

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