shithub: opus

Download patch

ref: 82485dd2c4f4dfe8ed0d89eebd4e4f6c0a091532
parent: a4854afac86a3481374e5956813e2cdd797649d4
author: Jean-Marc Valin <jeanmarcv@google.com>
date: Mon Jun 10 20:55:15 EDT 2024

Preserving 24-bit accuracy for fixed-point encoder

--- a/celt/arch.h
+++ b/celt/arch.h
@@ -144,6 +144,9 @@
 #define RES2FLOAT(a)    ((1.f/32768.f/256.)*(a))
 #define INT16TORES(a)   SHL32(EXTEND32(a), RES_SHIFT)
 #define ADD_RES(a, b)   ADD32(a, b)
+#define FLOAT2RES(a)    float2int(32768.f*256.f*(a))
+#define RES2SIG(a)      SHL32((a), SIG_SHIFT-RES_SHIFT)
+#define MULT16_RES_Q15(a,b) MULT16_32_Q15(a,b)
 #else
 typedef opus_val16 opus_res;
 #define RES_SHIFT 0
@@ -153,8 +156,13 @@
 #define RES2FLOAT(a)    ((1.f/32768.f)*(a))
 #define INT16TORES(a)   (a)
 #define ADD_RES(a, b)   SAT16(ADD32((a), (b)));
+#define FLOAT2RES(a)    FLOAT2INT16(a)
+#define RES2SIG(a)      SHL32(EXTEND32(a), SIG_SHIFT)
+#define MULT16_RES_Q15(a,b) MULT16_16_Q15(a,b)
 #endif
 
+#define RES2VAL16(a)    RES2INT16(a)
+
 #define celt_isnan(x) 0
 
 #define Q15ONE 32767
@@ -313,6 +321,12 @@
 #define RES2FLOAT(a)    (a)
 #define INT16TORES(a)   ((a)*(1/CELT_SIG_SCALE))
 #define ADD_RES(a, b)   ADD32(a, b)
+#define FLOAT2RES(a)    (a)
+#define RES2SIG(a)      (CELT_SIG_SCALE*(a))
+#define MULT16_RES_Q15(a,b) MULT16_16_Q15(a,b)
+
+#define RES2VAL16(a)    (a)
+
 
 #endif /* !FIXED_POINT */
 
--- a/celt/celt.h
+++ b/celt/celt.h
@@ -139,7 +139,7 @@
 
 int celt_encoder_get_size(int channels);
 
-int celt_encode_with_ec(OpusCustomEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc);
+int celt_encode_with_ec(OpusCustomEncoder * OPUS_RESTRICT st, const opus_res * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc);
 
 int celt_encoder_init(CELTEncoder *st, opus_int32 sampling_rate, int channels,
                       int arch);
@@ -229,7 +229,7 @@
 
 int resampling_factor(opus_int32 rate);
 
-void celt_preemphasis(const opus_val16 * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp,
+void celt_preemphasis(const opus_res * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp,
                         int N, int CC, int upsample, const opus_val16 *coef, celt_sig *mem, int clip);
 
 void comb_filter(opus_val32 *y, opus_val32 *x, int T0, int T1, int N,
--- a/celt/celt_encoder.c
+++ b/celt/celt_encoder.c
@@ -513,7 +513,7 @@
 }
 
 
-void celt_preemphasis(const opus_val16 * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp,
+void celt_preemphasis(const opus_res * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp,
                         int N, int CC, int upsample, const opus_val16 *coef, celt_sig *mem, int clip)
 {
    int i;
@@ -529,11 +529,11 @@
    {
       for (i=0;i<N;i++)
       {
-         opus_val16 x;
-         x = SCALEIN(pcmp[CC*i]);
+         celt_sig x;
+         x = RES2SIG(pcmp[CC*i]);
          /* Apply pre-emphasis */
-         inp[i] = SHL32(x, SIG_SHIFT) - m;
-         m = SHR32(MULT16_16(coef0, x), 15-SIG_SHIFT);
+         inp[i] = x - m;
+         m = MULT16_32_Q15(coef0, x);
       }
       *mem = m;
       return;
@@ -545,7 +545,7 @@
       OPUS_CLEAR(inp, N);
    }
    for (i=0;i<Nu;i++)
-      inp[i*upsample] = SCALEIN(pcmp[CC*i]);
+      inp[i*upsample] = RES2SIG(pcmp[CC*i]);
 
 #ifndef FIXED_POINT
    if (clip)
@@ -567,7 +567,7 @@
          celt_sig x, tmp;
          x = inp[i];
          /* Apply pre-emphasis */
-         tmp = MULT16_16(coef2, x);
+         tmp = SHL32(MULT16_32_Q15(coef2, x), 15-SIG_SHIFT);
          inp[i] = tmp + m;
          m = MULT16_32_Q15(coef1, inp[i]) - MULT16_32_Q15(coef0, tmp);
       }
@@ -576,11 +576,11 @@
    {
       for (i=0;i<N;i++)
       {
-         opus_val16 x;
+         celt_sig x;
          x = inp[i];
          /* Apply pre-emphasis */
-         inp[i] = SHL32(x, SIG_SHIFT) - m;
-         m = SHR32(MULT16_16(coef0, x), 15-SIG_SHIFT);
+         inp[i] = x - m;
+         m = MULT16_32_Q15(coef0, x);
       }
    }
    *mem = m;
@@ -1597,7 +1597,7 @@
    return target;
 }
 
-int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc)
+int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_res * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc)
 {
    int i, c, N;
    opus_int32 bits;
@@ -1813,8 +1813,8 @@
 
    ALLOC(in, CC*(N+overlap), celt_sig);
 
-   sample_max=MAX32(st->overlap_max, celt_maxabs16(pcm, C*(N-overlap)/st->upsample));
-   st->overlap_max=celt_maxabs16(pcm+C*(N-overlap)/st->upsample, C*overlap/st->upsample);
+   sample_max=MAX32(st->overlap_max, celt_maxabs_res(pcm, C*(N-overlap)/st->upsample));
+   st->overlap_max=celt_maxabs_res(pcm+C*(N-overlap)/st->upsample, C*overlap/st->upsample);
    sample_max=MAX32(sample_max, st->overlap_max);
 #ifdef FIXED_POINT
    silence = (sample_max==0);
@@ -2473,7 +2473,7 @@
       } while (++c<CC);
 
       /* We reuse freq[] as scratch space for the de-emphasis */
-      deemphasis(out_mem, (opus_val16*)pcm, N, CC, st->upsample, mode->preemph, st->preemph_memD, 0);
+      deemphasis(out_mem, (opus_res*)pcm, N, CC, st->upsample, mode->preemph, st->preemph_memD, 0);
       st->prefilter_period_old = st->prefilter_period;
       st->prefilter_gain_old = st->prefilter_gain;
       st->prefilter_tapset_old = st->prefilter_tapset;
@@ -2545,16 +2545,43 @@
 #ifdef CUSTOM_MODES
 
 #ifdef FIXED_POINT
+#ifdef ENABLE_RES24
 int opus_custom_encode(CELTEncoder * OPUS_RESTRICT st, const opus_int16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
 {
+   int j, ret, C, N;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+   ALLOC(in, C*N, opus_res);
+
+   for (j=0;j<C*N;j++)
+     in[j] = INT16TORES(pcm[j]);
+
+   ret=celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
+#ifdef RESYNTH
+   for (j=0;j<C*N;j++)
+      ((opus_int16*)pcm)[j]=RES2INT16(in[j]);
+#endif
+   RESTORE_STACK;
+   return ret;
+}
+#else
+int opus_custom_encode(CELTEncoder * OPUS_RESTRICT st, const opus_int16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
    return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL);
 }
+#endif
 
 #ifndef DISABLE_FLOAT_API
 int opus_custom_encode_float(CELTEncoder * OPUS_RESTRICT st, const float * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
 {
    int j, ret, C, N;
-   VARDECL(opus_int16, in);
+   VARDECL(opus_res, in);
    ALLOC_STACK;
 
    if (pcm==NULL)
@@ -2562,15 +2589,15 @@
 
    C = st->channels;
    N = frame_size;
-   ALLOC(in, C*N, opus_int16);
+   ALLOC(in, C*N, opus_res);
 
    for (j=0;j<C*N;j++)
-     in[j] = FLOAT2INT16(pcm[j]);
+     in[j] = FLOAT2RES(pcm[j]);
 
    ret=celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
 #ifdef RESYNTH
    for (j=0;j<C*N;j++)
-      ((float*)pcm)[j]=in[j]*(1.f/32768.f);
+      ((float*)pcm)[j]=RES2FLOAT(in[j]);
 #endif
    RESTORE_STACK;
    return ret;
--- a/celt/mathops.h
+++ b/celt/mathops.h
@@ -91,6 +91,26 @@
 }
 #endif
 
+#ifdef ENABLE_RES24
+static OPUS_INLINE opus_res celt_maxabs_res(const opus_res *x, int len)
+{
+   int i;
+   opus_res maxval = 0;
+   opus_res minval = 0;
+   for (i=0;i<len;i++)
+   {
+      maxval = MAX32(maxval, x[i]);
+      minval = MIN32(minval, x[i]);
+   }
+   /* opus_res should never reach such amplitude, so we should be safe. */
+   celt_sig_assert(minval != -2147483648);
+   return MAX32(maxval,-minval);
+}
+#else
+#define celt_maxabs_res celt_maxabs16
+#endif
+
+
 #ifndef OVERRIDE_CELT_MAXABS32
 #ifdef FIXED_POINT
 static OPUS_INLINE opus_val32 celt_maxabs32(const opus_val32 *x, int len)
--- a/silk/API.h
+++ b/silk/API.h
@@ -80,7 +80,7 @@
 opus_int silk_Encode(                                   /* O    Returns error code                              */
     void                            *encState,          /* I/O  State                                           */
     silk_EncControlStruct           *encControl,        /* I    Control status                                  */
-    const opus_int16                *samplesIn,         /* I    Speech sample input vector                      */
+    const opus_res                  *samplesIn,         /* I    Speech sample input vector                      */
     opus_int                        nSamplesIn,         /* I    Number of samples in input vector               */
     ec_enc                          *psRangeEnc,        /* I/O  Compressor data structure                       */
     opus_int32                      *nBytesOut,         /* I/O  Number of bytes in payload (input: Max bytes)   */
--- a/silk/enc_API.c
+++ b/silk/enc_API.c
@@ -144,7 +144,7 @@
 opus_int silk_Encode(                                   /* O    Returns error code                              */
     void                            *encState,          /* I/O  State                                           */
     silk_EncControlStruct           *encControl,        /* I    Control status                                  */
-    const opus_int16                *samplesIn,         /* I    Speech sample input vector                      */
+    const opus_res                  *samplesIn,         /* I    Speech sample input vector                      */
     opus_int                        nSamplesIn,         /* I    Number of samples in input vector               */
     ec_enc                          *psRangeEnc,        /* I/O  Compressor data structure                       */
     opus_int32                      *nBytesOut,         /* I/O  Number of bytes in payload (input: Max bytes)   */
@@ -282,7 +282,7 @@
         if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 2 ) {
             opus_int id = psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded;
             for( n = 0; n < nSamplesFromInput; n++ ) {
-                buf[ n ] = samplesIn[ 2 * n ];
+                buf[ n ] = RES2INT16(samplesIn[ 2 * n ]);
             }
             /* Making sure to start both resamplers from the same state when switching from mono to stereo */
             if( psEnc->nPrevChannelsInternal == 1 && id==0 ) {
@@ -296,7 +296,7 @@
             nSamplesToBuffer  = psEnc->state_Fxx[ 1 ].sCmn.frame_length - psEnc->state_Fxx[ 1 ].sCmn.inputBufIx;
             nSamplesToBuffer  = silk_min( nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc->state_Fxx[ 1 ].sCmn.fs_kHz );
             for( n = 0; n < nSamplesFromInput; n++ ) {
-                buf[ n ] = samplesIn[ 2 * n + 1 ];
+                buf[ n ] = RES2INT16(samplesIn[ 2 * n + 1 ]);
             }
             ret += silk_resampler( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state,
                 &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
@@ -305,7 +305,7 @@
         } else if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 1 ) {
             /* Combine left and right channels before resampling */
             for( n = 0; n < nSamplesFromInput; n++ ) {
-                sum = samplesIn[ 2 * n ] + samplesIn[ 2 * n + 1 ];
+                sum = RES2INT16(samplesIn[ 2 * n ] + samplesIn[ 2 * n + 1 ]);
                 buf[ n ] = (opus_int16)silk_RSHIFT_ROUND( sum,  1 );
             }
             ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state,
@@ -323,7 +323,9 @@
             psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer;
         } else {
             celt_assert( encControl->nChannelsAPI == 1 && encControl->nChannelsInternal == 1 );
-            silk_memcpy(buf, samplesIn, nSamplesFromInput*sizeof(opus_int16));
+            for( n = 0; n < nSamplesFromInput; n++ ) {
+                buf[n] = RES2INT16(samplesIn[n]);
+            }
             ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state,
                 &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
             psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer;
--- a/src/mapping_matrix.c
+++ b/src/mapping_matrix.c
@@ -86,7 +86,7 @@
     const MappingMatrix *matrix,
     const float *input,
     int input_rows,
-    opus_val16 *output,
+    opus_res *output,
     int output_row,
     int output_rows,
     int frame_size)
@@ -108,11 +108,7 @@
         matrix_data[MATRIX_INDEX(matrix->rows, output_row, col)] *
         input[MATRIX_INDEX(input_rows, col, i)];
     }
-#if defined(FIXED_POINT)
-    output[output_rows * i] = FLOAT2INT16((1/32768.f)*tmp);
-#else
-    output[output_rows * i] = (1/32768.f)*tmp;
-#endif
+    output[output_rows * i] = FLOAT2RES((1/32768.f)*tmp);
   }
 }
 
@@ -153,7 +149,7 @@
     const MappingMatrix *matrix,
     const opus_int16 *input,
     int input_rows,
-    opus_val16 *output,
+    opus_res *output,
     int output_row,
     int output_rows,
     int frame_size)
@@ -182,7 +178,7 @@
 #endif
     }
 #if defined(FIXED_POINT)
-    output[output_rows * i] = (opus_int16)((tmp + 64) >> 7);
+    output[output_rows * i] = INT16TORES((tmp + 64) >> 7);
 #else
     output[output_rows * i] = (1/(32768.f*32768.f))*tmp;
 #endif
--- a/src/mapping_matrix.h
+++ b/src/mapping_matrix.h
@@ -66,7 +66,7 @@
     const MappingMatrix *matrix,
     const float *input,
     int input_rows,
-    opus_val16 *output,
+    opus_res *output,
     int output_row,
     int output_rows,
     int frame_size
@@ -87,7 +87,7 @@
     const MappingMatrix *matrix,
     const opus_int16 *input,
     int input_rows,
-    opus_val16 *output,
+    opus_res *output,
     int output_row,
     int output_rows,
     int frame_size
--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -121,7 +121,7 @@
     int          first;
     opus_val16 * energy_masking;
     StereoWidthState width_mem;
-    opus_val16   delay_buffer[MAX_ENCODER_BUFFER*2];
+    opus_res     delay_buffer[MAX_ENCODER_BUFFER*2];
 #ifndef DISABLE_FLOAT_API
     int          detected_bandwidth;
     int          nb_no_activity_ms_Q1;
@@ -328,13 +328,52 @@
    return toc;
 }
 
-#ifndef FIXED_POINT
-static void silk_biquad_float(
-    const opus_val16      *in,            /* I:    Input signal                   */
+#ifdef FIXED_POINT
+/* Second order ARMA filter, alternative implementation */
+void silk_biquad_res(
+    const opus_res              *in,                /* I     input signal                                               */
+    const opus_int32            *B_Q28,             /* I     MA coefficients [3]                                        */
+    const opus_int32            *A_Q28,             /* I     AR coefficients [2]                                        */
+    opus_int32                  *S,                 /* I/O   State vector [2]                                           */
+    opus_res                    *out,               /* O     output signal                                              */
+    const opus_int32            len,                /* I     signal length (must be even)                               */
+    int stride
+)
+{
+    /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
+    opus_int   k;
+    opus_int32 inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
+
+    /* Negate A_Q28 values and split in two parts */
+    A0_L_Q28 = ( -A_Q28[ 0 ] ) & 0x00003FFF;        /* lower part */
+    A0_U_Q28 = silk_RSHIFT( -A_Q28[ 0 ], 14 );      /* upper part */
+    A1_L_Q28 = ( -A_Q28[ 1 ] ) & 0x00003FFF;        /* lower part */
+    A1_U_Q28 = silk_RSHIFT( -A_Q28[ 1 ], 14 );      /* upper part */
+
+    for( k = 0; k < len; k++ ) {
+        /* S[ 0 ], S[ 1 ]: Q12 */
+        inval = RES2INT16(in[ k*stride ]);
+        out32_Q14 = silk_LSHIFT( silk_SMLAWB( S[ 0 ], B_Q28[ 0 ], inval ), 2 );
+
+        S[ 0 ] = S[1] + silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A0_L_Q28 ), 14 );
+        S[ 0 ] = silk_SMLAWB( S[ 0 ], out32_Q14, A0_U_Q28 );
+        S[ 0 ] = silk_SMLAWB( S[ 0 ], B_Q28[ 1 ], inval);
+
+        S[ 1 ] = silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A1_L_Q28 ), 14 );
+        S[ 1 ] = silk_SMLAWB( S[ 1 ], out32_Q14, A1_U_Q28 );
+        S[ 1 ] = silk_SMLAWB( S[ 1 ], B_Q28[ 2 ], inval );
+
+        /* Scale back to Q0 and saturate */
+        out[ k*stride ] = INT16TORES( silk_SAT16( silk_RSHIFT( out32_Q14 + (1<<14) - 1, 14 ) ) );
+    }
+}
+#else
+static void silk_biquad_res(
+    const opus_res        *in,            /* I:    Input signal                   */
     const opus_int32      *B_Q28,         /* I:    MA coefficients [3]            */
     const opus_int32      *A_Q28,         /* I:    AR coefficients [2]            */
     opus_val32            *S,             /* I/O:  State vector [2]               */
-    opus_val16            *out,           /* O:    Output signal                  */
+    opus_res              *out,           /* O:    Output signal                  */
     const opus_int32      len,            /* I:    Signal length (must be even)   */
     int stride
 )
@@ -368,7 +407,7 @@
 }
 #endif
 
-static void hp_cutoff(const opus_val16 *in, opus_int32 cutoff_Hz, opus_val16 *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs, int arch)
+static void hp_cutoff(const opus_res *in, opus_int32 cutoff_Hz, opus_res *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs, int arch)
 {
    opus_int32 B_Q28[ 3 ], A_Q28[ 2 ];
    opus_int32 Fc_Q19, r_Q28, r_Q22;
@@ -391,7 +430,7 @@
    A_Q28[ 0 ] = silk_SMULWW( r_Q22, silk_SMULWW( Fc_Q19, Fc_Q19 ) - SILK_FIX_CONST( 2.0,  22 ) );
    A_Q28[ 1 ] = silk_SMULWW( r_Q22, r_Q22 );
 
-#ifdef FIXED_POINT
+#if defined(FIXED_POINT) && !defined(ENABLE_RES24)
    if( channels == 1 ) {
       silk_biquad_alt_stride1( in, B_Q28, A_Q28, hp_mem, out, len );
    } else {
@@ -398,15 +437,15 @@
       silk_biquad_alt_stride2( in, B_Q28, A_Q28, hp_mem, out, len, arch );
    }
 #else
-   silk_biquad_float( in, B_Q28, A_Q28, hp_mem, out, len, channels );
+   silk_biquad_res( in, B_Q28, A_Q28, hp_mem, out, len, channels );
    if( channels == 2 ) {
-       silk_biquad_float( in+1, B_Q28, A_Q28, hp_mem+2, out+1, len, channels );
+       silk_biquad_res( in+1, B_Q28, A_Q28, hp_mem+2, out+1, len, channels );
    }
 #endif
 }
 
 #ifdef FIXED_POINT
-static void dc_reject(const opus_val16 *in, opus_int32 cutoff_Hz, opus_val16 *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs)
+static void dc_reject(const opus_res *in, opus_int32 cutoff_Hz, opus_res *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs)
 {
    int c, i;
    int shift;
@@ -418,10 +457,15 @@
       for (i=0;i<len;i++)
       {
          opus_val32 x, y;
-         x = SHL32(EXTEND32(in[channels*i+c]), 14);
+         x = SHL32((opus_val32)in[channels*i+c], 14-RES_SHIFT);
          y = x-hp_mem[2*c];
          hp_mem[2*c] = hp_mem[2*c] + PSHR32(x - hp_mem[2*c], shift);
-         out[channels*i+c] = EXTRACT16(SATURATE(PSHR32(y, 14), 32767));
+#ifdef ENABLE_RES24
+         /* Don't saturate if we have the headroom to avoid it. */
+         out[channels*i+c] = PSHR32(y, 14-RES_SHIFT);
+#else
+         out[channels*i+c] = SATURATE(PSHR32(y, 14-RES_SHIFT), 32767);
+#endif
       }
    }
 }
@@ -468,7 +512,7 @@
 }
 #endif
 
-static void stereo_fade(const opus_val16 *in, opus_val16 *out, opus_val16 g1, opus_val16 g2,
+static void stereo_fade(const opus_res *in, opus_res *out, opus_val16 g1, opus_val16 g2,
         int overlap48, int frame_size, int channels, const opus_val16 *window, opus_int32 Fs)
 {
     int i;
@@ -485,8 +529,8 @@
        w = MULT16_16_Q15(window[i*inc], window[i*inc]);
        g = SHR32(MAC16_16(MULT16_16(w,g2),
              Q15ONE-w, g1), 15);
-       diff = EXTRACT16(HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]));
-       diff = MULT16_16_Q15(g, diff);
+       diff = HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]);
+       diff = MULT16_RES_Q15(g, diff);
        out[i*channels] = out[i*channels] - diff;
        out[i*channels+1] = out[i*channels+1] + diff;
     }
@@ -493,14 +537,14 @@
     for (;i<frame_size;i++)
     {
        opus_val32 diff;
-       diff = EXTRACT16(HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]));
-       diff = MULT16_16_Q15(g2, diff);
+       diff = HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]);
+       diff = MULT16_RES_Q15(g2, diff);
        out[i*channels] = out[i*channels] - diff;
        out[i*channels+1] = out[i*channels+1] + diff;
     }
 }
 
-static void gain_fade(const opus_val16 *in, opus_val16 *out, opus_val16 g1, opus_val16 g2,
+static void gain_fade(const opus_res *in, opus_res *out, opus_val16 g1, opus_val16 g2,
         int overlap48, int frame_size, int channels, const opus_val16 *window, opus_int32 Fs)
 {
     int i;
@@ -517,7 +561,7 @@
           w = MULT16_16_Q15(window[i*inc], window[i*inc]);
           g = SHR32(MAC16_16(MULT16_16(w,g2),
                 Q15ONE-w, g1), 15);
-          out[i] = MULT16_16_Q15(g, in[i]);
+          out[i] = MULT16_RES_Q15(g, in[i]);
        }
     } else {
        for (i=0;i<overlap;i++)
@@ -526,14 +570,14 @@
           w = MULT16_16_Q15(window[i*inc], window[i*inc]);
           g = SHR32(MAC16_16(MULT16_16(w,g2),
                 Q15ONE-w, g1), 15);
-          out[i*2] = MULT16_16_Q15(g, in[i*2]);
-          out[i*2+1] = MULT16_16_Q15(g, in[i*2+1]);
+          out[i*2] = MULT16_RES_Q15(g, in[i*2]);
+          out[i*2+1] = MULT16_RES_Q15(g, in[i*2+1]);
        }
     }
     c=0;do {
        for (i=overlap;i<frame_size;i++)
        {
-          out[i*channels+c] = MULT16_16_Q15(g2, in[i*channels+c]);
+          out[i*channels+c] = MULT16_RES_Q15(g2, in[i*channels+c]);
        }
     }
     while (++c<channels);
@@ -726,7 +770,7 @@
    return new_size;
 }
 
-opus_val16 compute_stereo_width(const opus_val16 *pcm, int frame_size, opus_int32 Fs, StereoWidthState *mem)
+opus_val16 compute_stereo_width(const opus_res *pcm, int frame_size, opus_int32 Fs, StereoWidthState *mem)
 {
    opus_val32 xx, xy, yy;
    opus_val16 sqrt_xx, sqrt_yy;
@@ -747,23 +791,23 @@
       opus_val32 pxy=0;
       opus_val32 pyy=0;
       opus_val16 x, y;
-      x = pcm[2*i];
-      y = pcm[2*i+1];
+      x = RES2VAL16(pcm[2*i]);
+      y = RES2VAL16(pcm[2*i+1]);
       pxx = SHR32(MULT16_16(x,x),2);
       pxy = SHR32(MULT16_16(x,y),2);
       pyy = SHR32(MULT16_16(y,y),2);
-      x = pcm[2*i+2];
-      y = pcm[2*i+3];
+      x = RES2VAL16(pcm[2*i+2]);
+      y = RES2VAL16(pcm[2*i+3]);
       pxx += SHR32(MULT16_16(x,x),2);
       pxy += SHR32(MULT16_16(x,y),2);
       pyy += SHR32(MULT16_16(y,y),2);
-      x = pcm[2*i+4];
-      y = pcm[2*i+5];
+      x = RES2VAL16(pcm[2*i+4]);
+      y = RES2VAL16(pcm[2*i+5]);
       pxx += SHR32(MULT16_16(x,x),2);
       pxy += SHR32(MULT16_16(x,y),2);
       pyy += SHR32(MULT16_16(y,y),2);
-      x = pcm[2*i+6];
-      y = pcm[2*i+7];
+      x = RES2VAL16(pcm[2*i+6]);
+      y = RES2VAL16(pcm[2*i+7]);
       pxx += SHR32(MULT16_16(x,x),2);
       pxy += SHR32(MULT16_16(x,y),2);
       pyy += SHR32(MULT16_16(y,y),2);
@@ -930,7 +974,7 @@
 
 #ifndef DISABLE_FLOAT_API
 
-int is_digital_silence(const opus_val16* pcm, int frame_size, int channels, int lsb_depth)
+int is_digital_silence(const opus_res* pcm, int frame_size, int channels, int lsb_depth)
 {
    int silence = 0;
    opus_val32 sample_max = 0;
@@ -937,7 +981,7 @@
 #ifdef MLP_TRAINING
    return 0;
 #endif
-   sample_max = celt_maxabs16(pcm, frame_size*channels);
+   sample_max = celt_maxabs_res(pcm, frame_size*channels);
 
 #ifdef FIXED_POINT
    silence = (sample_max == 0);
@@ -950,7 +994,7 @@
 }
 
 #ifdef FIXED_POINT
-static opus_val32 compute_frame_energy(const opus_val16 *pcm, int frame_size, int channels, int arch)
+static opus_val32 compute_frame_energy(const opus_res *pcm, int frame_size, int channels, int arch)
 {
    int i;
    opus_val32 sample_max;
@@ -960,7 +1004,7 @@
    int len = frame_size*channels;
    (void)arch;
    /* Max amplitude in the signal */
-   sample_max = celt_maxabs16(pcm, len);
+   sample_max = RES2INT16(celt_maxabs_res(pcm, len));
 
    /* Compute the right shift required in the MAC to avoid an overflow */
    max_shift = celt_ilog2(len);
@@ -968,7 +1012,7 @@
 
    /* Compute the energy */
    for (i=0; i<len; i++)
-      energy += SHR32(MULT16_16(pcm[i], pcm[i]), shift);
+      energy += SHR32(MULT16_16(RES2INT16(pcm[i]), RES2INT16(pcm[i])), shift);
 
    /* Normalize energy by the frame size and left-shift back to the original position */
    energy /= len;
@@ -1042,7 +1086,7 @@
    return redundancy_bytes;
 }
 
-static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
+static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
                 unsigned char *data, opus_int32 max_data_bytes,
                 int float_api, int first_frame,
 #ifdef ENABLE_DRED
@@ -1054,7 +1098,7 @@
                 int redundancy, int celt_to_silk, int prefill,
                 opus_int32 equiv_rate, int to_celt);
 
-opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
+opus_int32 opus_encode_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
                 unsigned char *data, opus_int32 out_data_bytes, int lsb_depth,
                 const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2,
                 int analysis_channels, downmix_func downmix, int float_api)
@@ -1696,7 +1740,7 @@
     }
 }
 
-static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
+static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
                 unsigned char *data, opus_int32 max_data_bytes,
                 int float_api, int first_frame,
 #ifdef ENABLE_DRED
@@ -1729,8 +1773,8 @@
     int delay_compensation;
     int total_buffer;
     opus_int activity = VAD_NO_DECISION;
-    VARDECL(opus_val16, pcm_buf);
-    VARDECL(opus_val16, tmp_prefill);
+    VARDECL(opus_res, pcm_buf);
+    VARDECL(opus_res, tmp_prefill);
     SAVE_STACK;
 
     st->rangeFinal = 0;
@@ -1791,7 +1835,7 @@
 
     ec_enc_init(&enc, data, max_data_bytes-1);
 
-    ALLOC(pcm_buf, (total_buffer+frame_size)*st->channels, opus_val16);
+    ALLOC(pcm_buf, (total_buffer+frame_size)*st->channels, opus_res);
     OPUS_COPY(pcm_buf, &st->delay_buffer[(st->encoder_buffer-total_buffer)*st->channels], total_buffer*st->channels);
 
     if (st->mode == MODE_CELT_ONLY)
@@ -1867,12 +1911,7 @@
     if (st->mode != MODE_CELT_ONLY)
     {
         opus_int32 total_bitRate, celt_rate;
-#ifdef FIXED_POINT
-       const opus_int16 *pcm_silk;
-#else
-       VARDECL(opus_int16, pcm_silk);
-       ALLOC(pcm_silk, st->channels*frame_size, opus_int16);
-#endif
+        const opus_res *pcm_silk;
 
         /* Distribute bits between SILK and CELT */
         total_bitRate = 8 * bytes_target * frame_rate;
@@ -2025,23 +2064,13 @@
             gain_fade(st->delay_buffer+prefill_offset, st->delay_buffer+prefill_offset,
                   0, Q15ONE, celt_mode->overlap, st->Fs/400, st->channels, celt_mode->window, st->Fs);
             OPUS_CLEAR(st->delay_buffer, prefill_offset);
-#ifdef FIXED_POINT
             pcm_silk = st->delay_buffer;
-#else
-            for (i=0;i<st->encoder_buffer*st->channels;i++)
-                pcm_silk[i] = FLOAT2INT16(st->delay_buffer[i]);
-#endif
             silk_Encode( silk_enc, &st->silk_mode, pcm_silk, st->encoder_buffer, NULL, &zero, prefill, activity );
             /* Prevent a second switch in the real encode call. */
             st->silk_mode.opusCanSwitch = 0;
         }
 
-#ifdef FIXED_POINT
         pcm_silk = pcm_buf+total_buffer*st->channels;
-#else
-        for (i=0;i<frame_size*st->channels;i++)
-            pcm_silk[i] = FLOAT2INT16(pcm_buf[total_buffer*st->channels + i]);
-#endif
         ret = silk_Encode( silk_enc, &st->silk_mode, pcm_silk, frame_size, &enc, &nBytes, 0, activity );
         if( ret ) {
             /*fprintf (stderr, "SILK encode error: %d\n", ret);*/
@@ -2116,7 +2145,7 @@
         celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(celt_pred));
     }
 
-    ALLOC(tmp_prefill, st->channels*st->Fs/400, opus_val16);
+    ALLOC(tmp_prefill, st->channels*st->Fs/400, opus_res);
     if (st->mode != MODE_SILK_ONLY && st->mode != st->prev_mode && st->prev_mode > 0)
     {
        OPUS_COPY(tmp_prefill, &st->delay_buffer[(st->encoder_buffer-total_buffer-st->Fs/400)*st->channels], st->channels*st->Fs/400);
@@ -2467,7 +2496,7 @@
 {
    int i, ret;
    int frame_size;
-   VARDECL(opus_int16, in);
+   VARDECL(opus_res, in);
    ALLOC_STACK;
 
    frame_size = frame_size_select(analysis_frame_size, st->variable_duration, st->Fs);
@@ -2476,10 +2505,10 @@
       RESTORE_STACK;
       return OPUS_BAD_ARG;
    }
-   ALLOC(in, frame_size*st->channels, opus_int16);
+   ALLOC(in, frame_size*st->channels, opus_res);
 
    for (i=0;i<frame_size*st->channels;i++)
-      in[i] = FLOAT2INT16(pcm[i]);
+      in[i] = FLOAT2RES(pcm[i]);
    ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16,
                             pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1);
    RESTORE_STACK;
@@ -2487,14 +2516,40 @@
 }
 #endif
 
+#ifdef ENABLE_RES24
 opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size,
-                unsigned char *data, opus_int32 out_data_bytes)
+                unsigned char *data, opus_int32 max_data_bytes)
 {
+   int i, ret;
    int frame_size;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
    frame_size = frame_size_select(analysis_frame_size, st->variable_duration, st->Fs);
-   return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, 16,
+   if (frame_size <= 0)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   ALLOC(in, frame_size*st->channels, opus_res);
+
+   for (i=0;i<frame_size*st->channels;i++)
+      in[i] = INT16TORES(pcm[i]);
+   ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16,
+                            pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 1);
+   RESTORE_STACK;
+   return ret;
+}
+#else
+opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size,
+                unsigned char *data, opus_int32 max_data_bytes)
+{
+   int frame_size;
+   frame_size = frame_size_select(analysis_frame_size, st->variable_duration, st->Fs);
+   return opus_encode_native(st, pcm, frame_size, data, max_data_bytes, 16,
                              pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0);
 }
+#endif /* ENABLE_RES24 */
 
 #else
 opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size,
--- a/src/opus_multistream_encoder.c
+++ b/src/opus_multistream_encoder.c
@@ -236,7 +236,7 @@
    opus_val32 bandE[21];
    opus_val16 maskLogE[3][21];
    VARDECL(opus_val32, in);
-   VARDECL(opus_val16, x);
+   VARDECL(opus_res, x);
    VARDECL(opus_val32, freq);
    SAVE_STACK;
 
@@ -250,7 +250,7 @@
          break;
 
    ALLOC(in, frame_size+overlap, opus_val32);
-   ALLOC(x, len, opus_val16);
+   ALLOC(x, len, opus_res);
    ALLOC(freq, freq_size, opus_val32);
 
    channel_pos(channels, pos);
@@ -819,7 +819,7 @@
    int s;
    char *ptr;
    int tot_size;
-   VARDECL(opus_val16, buf);
+   VARDECL(opus_res, buf);
    VARDECL(opus_val16, bandSMR);
    unsigned char tmp_data[MS_FRAME_TMP];
    OpusRepacketizer rp;
@@ -862,7 +862,7 @@
       RESTORE_STACK;
       return OPUS_BUFFER_TOO_SMALL;
    }
-   ALLOC(buf, 2*frame_size, opus_val16);
+   ALLOC(buf, 2*frame_size, opus_res);
    coupled_size = opus_encoder_get_size(2);
    mono_size = opus_encoder_get_size(1);
 
@@ -1014,7 +1014,7 @@
 
 #if !defined(DISABLE_FLOAT_API)
 static void opus_copy_channel_in_float(
-  opus_val16 *dst,
+  opus_res *dst,
   int dst_stride,
   const void *src,
   int src_stride,
@@ -1028,16 +1028,12 @@
    (void)user_data;
    float_src = (const float *)src;
    for (i=0;i<frame_size;i++)
-#if defined(FIXED_POINT)
-      dst[i*dst_stride] = FLOAT2INT16(float_src[i*src_stride+src_channel]);
-#else
-      dst[i*dst_stride] = float_src[i*src_stride+src_channel];
-#endif
+      dst[i*dst_stride] = FLOAT2RES(float_src[i*src_stride+src_channel]);
 }
 #endif
 
 static void opus_copy_channel_in_short(
-  opus_val16 *dst,
+  opus_res *dst,
   int dst_stride,
   const void *src,
   int src_stride,
@@ -1051,11 +1047,7 @@
    (void)user_data;
    short_src = (const opus_int16 *)src;
    for (i=0;i<frame_size;i++)
-#if defined(FIXED_POINT)
-      dst[i*dst_stride] = short_src[i*src_stride+src_channel];
-#else
-      dst[i*dst_stride] = (1/32768.f)*short_src[i*src_stride+src_channel];
-#endif
+      dst[i*dst_stride] = INT16TORES(short_src[i*src_stride+src_channel]);
 }
 
 
--- a/src/opus_private.h
+++ b/src/opus_private.h
@@ -125,7 +125,7 @@
 int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev);
 
 typedef void (*opus_copy_channel_in_func)(
-  opus_val16 *dst,
+  opus_res *dst,
   int dst_stride,
   const void *src,
   int src_stride,
@@ -174,13 +174,13 @@
 typedef void (*downmix_func)(const void *, opus_val32 *, int, int, int, int, int);
 void downmix_float(const void *_x, opus_val32 *sub, int subframe, int offset, int c1, int c2, int C);
 void downmix_int(const void *_x, opus_val32 *sub, int subframe, int offset, int c1, int c2, int C);
-int is_digital_silence(const opus_val16* pcm, int frame_size, int channels, int lsb_depth);
+int is_digital_silence(const opus_res* pcm, int frame_size, int channels, int lsb_depth);
 
 int encode_size(int size, unsigned char *data);
 
 opus_int32 frame_size_select(opus_int32 frame_size, int variable_duration, opus_int32 Fs);
 
-opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
+opus_int32 opus_encode_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
       unsigned char *data, opus_int32 out_data_bytes, int lsb_depth,
       const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2,
       int analysis_channels, downmix_func downmix, int float_api);
--- a/src/opus_projection_encoder.c
+++ b/src/opus_projection_encoder.c
@@ -47,7 +47,7 @@
 
 #if !defined(DISABLE_FLOAT_API)
 static void opus_projection_copy_channel_in_float(
-  opus_val16 *dst,
+  opus_res *dst,
   int dst_stride,
   const void *src,
   int src_stride,
@@ -62,7 +62,7 @@
 #endif
 
 static void opus_projection_copy_channel_in_short(
-  opus_val16 *dst,
+  opus_res *dst,
   int dst_stride,
   const void *src,
   int src_stride,
--- a/tests/test_opus_projection.c
+++ b/tests/test_opus_projection.c
@@ -52,17 +52,12 @@
 #define SIMPLE_MATRIX_OUTPUT_SIZE 40
 
 int assert_is_equal(
-  const opus_val16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
+  const opus_res *a, const opus_int16 *b, int size, opus_int16 tolerance)
 {
   int i;
   for (i = 0; i < size; i++)
   {
-#ifdef FIXED_POINT
-    opus_int16 val = a[i];
-#else
-    opus_int16 val = FLOAT2INT16(a[i]);
-#endif
-    if (abs(val - b[i]) > tolerance)
+    if (abs(RES2INT16(a[i]) - b[i]) > tolerance)
       return 1;
   }
   return 0;
@@ -95,7 +90,7 @@
   int i, ret;
   opus_int32 simple_matrix_size;
   opus_res *input_pcm;
-  opus_val16 *output_val16;
+  opus_res *output_pcm;
   opus_int16 *output_int16;
   MappingMatrix *simple_matrix;
 
@@ -102,7 +97,7 @@
   /* Allocate input/output buffers. */
   input_pcm = (opus_res *)opus_alloc(sizeof(opus_res) * SIMPLE_MATRIX_INPUT_SIZE);
   output_int16 = (opus_int16 *)opus_alloc(sizeof(opus_int16) * SIMPLE_MATRIX_OUTPUT_SIZE);
-  output_val16 = (opus_val16 *)opus_alloc(sizeof(opus_val16) * SIMPLE_MATRIX_OUTPUT_SIZE);
+  output_pcm = (opus_res *)opus_alloc(sizeof(opus_res) * SIMPLE_MATRIX_OUTPUT_SIZE);
 
   /* Initialize matrix */
   simple_matrix_size = mapping_matrix_get_size(simple_matrix_params.rows,
@@ -123,14 +118,14 @@
 
   /* _in_short */
   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
-    output_val16[i] = 0;
+    output_pcm[i] = 0;
   for (i = 0; i < simple_matrix->rows; i++)
   {
     mapping_matrix_multiply_channel_in_short(simple_matrix,
-      input_int16, simple_matrix->cols, &output_val16[i], i,
+      input_int16, simple_matrix->cols, &output_pcm[i], i,
       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
   }
-  ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
+  ret = assert_is_equal(output_pcm, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
   if (ret)
     test_failed();
 
@@ -150,27 +145,27 @@
 #if !defined(DISABLE_FLOAT_API) && !defined(FIXED_POINT)
   /* _in_float */
   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
-    output_val16[i] = 0;
+    output_pcm[i] = 0;
   for (i = 0; i < simple_matrix->rows; i++)
   {
     mapping_matrix_multiply_channel_in_float(simple_matrix,
-      input_pcm, simple_matrix->cols, &output_val16[i], i,
+      input_pcm, simple_matrix->cols, &output_pcm[i], i,
       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
   }
-  ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
+  ret = assert_is_equal(output_pcm, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
   if (ret)
     test_failed();
 
   /* _out_float */
   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
-    output_val16[i] = 0;
+    output_pcm[i] = 0;
   for (i = 0; i < simple_matrix->cols; i++)
   {
     mapping_matrix_multiply_channel_out_float(simple_matrix,
-      &input_pcm[i], i, simple_matrix->cols, output_val16,
+      &input_pcm[i], i, simple_matrix->cols, output_pcm,
       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
   }
-  ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
+  ret = assert_is_equal(output_pcm, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
   if (ret)
     test_failed();
 #endif
@@ -177,7 +172,7 @@
 
   opus_free(input_pcm);
   opus_free(output_int16);
-  opus_free(output_val16);
+  opus_free(output_pcm);
   opus_free(simple_matrix);
 }
 
--