shithub: opus

Download patch

ref: 8b16ce9055b80c6092cbc66a91c778f4ccf5b92d
parent: 58208f448dd1287ae11ed3030cf24b7046090f49
author: Jean-Marc Valin <jeanmarcv@google.com>
date: Wed Jun 12 07:48:44 EDT 2024

Adds 24-bit API to Opus custom

--- a/celt/arch.h
+++ b/celt/arch.h
@@ -140,8 +140,10 @@
 #define RES_SHIFT 8
 #define SIG2RES(a)      PSHR32(a, SIG_SHIFT-RES_SHIFT)
 #define RES2INT16(a)    SAT16(PSHR32(a, RES_SHIFT))
+#define RES2INT24(a)    (a)
 #define RES2FLOAT(a)    ((1.f/32768.f/256.)*(a))
 #define INT16TORES(a)   SHL32(EXTEND32(a), RES_SHIFT)
+#define INT24TORES(a)   (a)
 #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)
@@ -151,8 +153,10 @@
 #define RES_SHIFT 0
 #define SIG2RES(a)      SIG2WORD16(a)
 #define RES2INT16(a)    (a)
+#define RES2INT24(a)    SHL32(EXTEND32(a), 8)
 #define RES2FLOAT(a)    ((1.f/32768.f)*(a))
 #define INT16TORES(a)   (a)
+#define INT24TORES(a)   SAT16(PSHR32(a, 8))
 #define ADD_RES(a, b)   SAT16(ADD32((a), (b)));
 #define FLOAT2RES(a)    FLOAT2INT16(a)
 #define RES2SIG(a)      SHL32(EXTEND32(a), SIG_SHIFT)
@@ -316,8 +320,10 @@
 
 #define SIG2RES(a)      ((1/CELT_SIG_SCALE)*(a))
 #define RES2INT16(a)    FLOAT2INT16(a)
+#define RES2INT24(a)    float2int(32768.f*256.f*(a))
 #define RES2FLOAT(a)    (a)
 #define INT16TORES(a)   ((a)*(1/CELT_SIG_SCALE))
+#define INT24TORES(a)   ((1.f/32768.f/256.)*(a))
 #define ADD_RES(a, b)   ADD32(a, b)
 #define FLOAT2RES(a)    (a)
 #define RES2SIG(a)      (CELT_SIG_SCALE*(a))
--- a/celt/celt_decoder.c
+++ b/celt/celt_decoder.c
@@ -1395,11 +1395,38 @@
    RESTORE_STACK;
    return ret;
 }
+
+int opus_custom_decode24(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int32 * OPUS_RESTRICT pcm, int frame_size)
+{
+   return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL, 0);
+}
 #else
 int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size)
 {
    return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL, 0);
 }
+
+int opus_custom_decode24(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int32 * OPUS_RESTRICT pcm, int frame_size)
+{
+   int j, ret, C, N;
+   VARDECL(opus_res, out);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+
+   ALLOC(out, C*N, opus_res);
+   ret = celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0);
+   if (ret>0)
+      for (j=0;j<C*ret;j++)
+         pcm[j]=RES2INT24(out[j]);
+
+   RESTORE_STACK;
+   return ret;
+}
 #endif
 
 #ifndef DISABLE_FLOAT_API
@@ -1450,12 +1477,34 @@
 
    if (ret>0)
       for (j=0;j<C*ret;j++)
-         pcm[j] = RES2INT16 (out[j]);
+         pcm[j] = RES2INT16(out[j]);
 
    RESTORE_STACK;
    return ret;
 }
 
+int opus_custom_decode24(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int32 * OPUS_RESTRICT pcm, int frame_size)
+{
+   int j, ret, C, N;
+   VARDECL(celt_sig, out);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+   ALLOC(out, C*N, celt_sig);
+
+   ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0);
+
+   if (ret>0)
+      for (j=0;j<C*ret;j++)
+         pcm[j] = RES2INT24(out[j]);
+
+   RESTORE_STACK;
+   return ret;
+}
 #endif
 #endif /* CUSTOM_MODES */
 
--- a/celt/celt_encoder.c
+++ b/celt/celt_encoder.c
@@ -2570,12 +2570,42 @@
    RESTORE_STACK;
    return ret;
 }
+
+int opus_custom_encode24(CELTEncoder * OPUS_RESTRICT st, const opus_int32 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL);
+}
 #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);
 }
+
+int opus_custom_encode24(CELTEncoder * OPUS_RESTRICT st, const opus_int32 * 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] = INT24TORES(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]=RES2INT24(in[j]);
 #endif
+   RESTORE_STACK;
+   return ret;
+}
+#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)
@@ -2618,7 +2648,7 @@
    N=frame_size;
    ALLOC(in, C*N, celt_sig);
    for (j=0;j<C*N;j++) {
-     in[j] = (1.0f/32768)*pcm[j];
+     in[j] = INT16TORES(pcm[j]);
    }
 
    ret = celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
@@ -2625,6 +2655,31 @@
 #ifdef RESYNTH
    for (j=0;j<C*N;j++)
       ((opus_int16*)pcm)[j] = FLOAT2INT16(in[j]);
+#endif
+   RESTORE_STACK;
+   return ret;
+}
+
+int opus_custom_encode24(CELTEncoder * OPUS_RESTRICT st, const opus_int32 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   int j, ret, C, N;
+   VARDECL(celt_sig, in);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C=st->channels;
+   N=frame_size;
+   ALLOC(in, C*N, celt_sig);
+   for (j=0;j<C*N;j++) {
+     in[j] = INT24TORES(pcm[j]);
+   }
+
+   ret = celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
+#ifdef RESYNTH
+   for (j=0;j<C*N;j++)
+      ((opus_int32*)pcm)[j] = RES2INT24(in[j]);
 #endif
    RESTORE_STACK;
    return ret;
--- a/celt/opus_custom_demo.c
+++ b/celt/opus_custom_demo.c
@@ -41,13 +41,17 @@
 
 static void print_usage(char **argv) {
    fprintf (stderr, "Usage: %s [-e | -d] <rate> <channels> <frame size> "
-                  " [<bytes per packet>] [-complexity <0-10>] [-loss <packet loss percent>] "
+                  " [<bytes per packet>] [options] "
                   "<input> <output>\n", argv[0]);
    fprintf (stderr, "     -e encode only (default is encode and decode)\n");
    fprintf (stderr, "     -d decode only (default is encode and decode)\n");
    fprintf (stderr, "     <bytes per packet>: required only when encoding\n");
-   fprintf (stderr, "     -complexity <0-10>: optional only when encoding\n");
-   fprintf (stderr, "     -loss <packet loss percent>: encoding (robsutness setting) and decoding (simulating loss)\n");
+   fprintf (stderr, "options:\n");
+   fprintf (stderr, "     -16                      format is 16-bit little-endian (default)\n");
+   fprintf (stderr, "     -24                      format is 24-bit little-endian\n");
+   fprintf (stderr, "     -f32                     format is 32-bit float little-endian\n");
+   fprintf (stderr, "     -complexity <0-10>       optional only when encoding\n");
+   fprintf (stderr, "     -loss <percentage>       encoding (robsutness setting) and decoding (simulating loss)\n");
 }
 
 static void int_to_char(opus_uint32 i, unsigned char ch[4])
@@ -67,6 +71,18 @@
 #define check_encoder_option(decode_only, opt) do {if (decode_only) {fprintf(stderr, "option %s is only for encoding\n", opt); goto failure;}} while(0)
 #define check_decoder_option(encode_only, opt) do {if (encode_only) {fprintf(stderr, "option %s is only for decoding\n", opt); goto failure;}} while(0)
 
+#define FORMAT_S16_LE 0
+#define FORMAT_S24_LE 1
+#define FORMAT_F32_LE 2
+
+static const int format_size[3] = {2, 3, 4};
+
+typedef union {
+    opus_int32 i;
+    float f;
+} float_bits;
+
+
 int main(int argc, char *argv[])
 {
    int err;
@@ -82,6 +98,7 @@
    OpusCustomDecoder *dec=NULL;
    int len;
    opus_int32 frame_size, channels, rate;
+   int format=FORMAT_S16_LE;
    int bytes_per_packet=0;
    unsigned char data[MAX_PACKET];
    int complexity=-1;
@@ -92,7 +109,7 @@
 #endif
    int count = 0;
    opus_int32 skip;
-   opus_int16 *in=NULL, *out=NULL;
+   opus_int32 *in=NULL, *out=NULL;
    unsigned char *fbytes=NULL;
    args = 1;
    if (argc < 7)
@@ -163,6 +180,15 @@
           args++;
           percent_loss = atof(argv[args]);
           args++;
+       } else if( strcmp( argv[ args ], "-16" ) == 0 ) {
+          format = FORMAT_S16_LE;
+          args++;
+       } else if( strcmp( argv[ args ], "-24" ) == 0 ) {
+          format = FORMAT_S24_LE;
+          args++;
+       } else if( strcmp( argv[ args ], "-f32" ) == 0 ) {
+          format = FORMAT_F32_LE;
+          args++;
        } else {
           printf( "Error: unrecognized setting: %s\n\n", argv[ args ] );
           print_usage( argv );
@@ -212,9 +238,9 @@
       fprintf (stderr, "Could not open output file %s\n", argv[argc-1]);
       goto failure;
    }
-   in = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16));
-   out = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16));
-   fbytes = (unsigned char*)malloc(frame_size*channels*2);
+   in = (opus_int32*)malloc(frame_size*channels*sizeof(opus_int32));
+   out = (opus_int32*)malloc(frame_size*channels*sizeof(opus_int32));
+   fbytes = (unsigned char*)malloc(frame_size*channels*4);
 
    while (!feof(fin))
    {
@@ -244,17 +270,34 @@
               break;
           }
       } else {
-         err = fread(fbytes, sizeof(short), frame_size*channels, fin);
+         err = fread(fbytes, format_size[format], frame_size*channels, fin);
          if (feof(fin))
             break;
-         for(i=0;i<frame_size*channels;i++)
-         {
-             opus_int32 s;
-             s=fbytes[2*i+1]<<8|fbytes[2*i];
-             s=((s&0xFFFF)^0x8000)-0x8000;
-             in[i]=s;
+         if (format == FORMAT_S16_LE) {
+            for(i=0;i<frame_size*channels;i++)
+            {
+               opus_int32 s;
+               s=fbytes[2*i+1]<<8|fbytes[2*i];
+               s=((s&0xFFFF)^0x8000)-0x8000;
+               in[i]=s*256;
+            }
+         } else if (format == FORMAT_S24_LE) {
+            for(i=0;i<frame_size*channels;i++)
+            {
+               opus_int32 s;
+               s=fbytes[3*i+2]<<16|fbytes[3*i+1]<<8|fbytes[3*i];
+               s=((s&0xFFFFFF)^0x800000)-0x800000;
+               in[i]=s;
+            }
+         } else if (format == FORMAT_F32_LE) {
+            for(i=0;i<frame_size*channels;i++)
+            {
+               float_bits s;
+               s.i=fbytes[4*i+3]<<24|fbytes[4*i+2]<<16|fbytes[4*i+1]<<8|fbytes[4*i];
+               in[i]=(int)floor(.5 + s.f*8388608);
+            }
          }
-         len = opus_custom_encode(enc, in, frame_size, data, bytes_per_packet);
+         len = opus_custom_encode24(enc, in, frame_size, data, bytes_per_packet);
          opus_custom_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range));
          if (len <= 0)
             fprintf (stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len));
@@ -306,9 +349,9 @@
          lost = percent_loss != 0 && (float)rand()/RAND_MAX<.01*percent_loss;
          if (lost)
             /*if (errors && (errors%2==0))*/
-            ret = opus_custom_decode(dec, NULL, len, out, frame_size);
+            ret = opus_custom_decode24(dec, NULL, len, out, frame_size);
          else
-            ret = opus_custom_decode(dec, data, len, out, frame_size);
+            ret = opus_custom_decode24(dec, data, len, out, frame_size);
          opus_custom_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range));
          if (ret < 0)
             fprintf(stderr, "opus_custom_decode() failed: %s\n", opus_strerror(ret));
@@ -326,14 +369,39 @@
             }
          }
 #endif
-         for(i=0;i<(ret-skip)*channels;i++)
-         {
-            short s;
-            s=out[i+(skip*channels)];
-            fbytes[2*i]=s&0xFF;
-            fbytes[2*i+1]=(s>>8)&0xFF;
+         if (format == FORMAT_S16_LE) {
+            for(i=0;i<(ret-skip)*channels;i++)
+            {
+               opus_int32 s;
+               s=(out[i+(skip*channels)]+128)>>8;
+               if (s > 32767) s = 32767;
+               if (s < -32767) s = -32767;
+               fbytes[2*i]=s&0xFF;
+               fbytes[2*i+1]=(s>>8)&0xFF;
+            }
+         } else if (format == FORMAT_S24_LE) {
+            for(i=0;i<(ret-skip)*channels;i++)
+            {
+               opus_int32 s;
+               s=out[i+(skip*channels)];
+               if (s > 8388607) s = 8388607;
+               if (s < -8388607) s = -8388607;
+               fbytes[3*i]=s&0xFF;
+               fbytes[3*i+1]=(s>>8)&0xFF;
+               fbytes[3*i+2]=(s>>16)&0xFF;
+            }
+         } else if (format == FORMAT_F32_LE) {
+            for(i=0;i<(ret-skip)*channels;i++)
+            {
+               float_bits s;
+               s.f=out[i+(skip*channels)]*(1.f/8388608.f);
+               fbytes[4*i]=s.i&0xFF;
+               fbytes[4*i+1]=(s.i>>8)&0xFF;
+               fbytes[4*i+2]=(s.i>>16)&0xFF;
+               fbytes[4*i+3]=(s.i>>24)&0xFF;
+            }
          }
-         fwrite(fbytes, 2, (ret-skip)*channels, fout);
+         fwrite(fbytes, format_size[format], (ret-skip)*channels, fout);
       }
 
       /* compare final range encoder rng values of encoder and decoder */
--- a/include/opus_custom.h
+++ b/include/opus_custom.h
@@ -230,6 +230,27 @@
     int maxCompressedBytes
 ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
 
+/** Encodes a frame of audio.
+  * @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
+  * @param [in] pcm <tt>opus_int32*</tt>: PCM audio in signed 32-bit format (native endian) representing (or slightly exceeding) 24-bit values.
+  *          There must be exactly frame_size samples per channel.
+  * @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
+  * @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
+  * @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
+  *          (can change from one frame to another)
+  * @return Number of bytes written to "compressed".
+  *       If negative, an error has occurred (see error codes). It is IMPORTANT that
+  *       the length returned be somehow transmitted to the decoder. Otherwise, no
+  *       decoding is possible.
+ */
+OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode24(
+    OpusCustomEncoder *st,
+    const opus_int32 *pcm,
+    int frame_size,
+    unsigned char *compressed,
+    int maxCompressedBytes
+) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
+
 /** Perform a CTL function on an Opus custom encoder.
   *
   * Generally the request and subsequent arguments are generated
@@ -323,6 +344,23 @@
     const unsigned char *data,
     int len,
     opus_int16 *pcm,
+    int frame_size
+) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
+
+/** Decode an opus custom frame
+  * @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
+  * @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
+  * @param [in] len <tt>int</tt>: Number of bytes in payload
+  * @param [out] pcm <tt>opus_int32*</tt>: Output signal (interleaved if 2 channels) representing (or slightly exceeding) 24-bit values. length
+  *  is frame_size*channels*sizeof(opus_int32)
+  * @param [in] frame_size Number of samples per channel of available space in *pcm.
+  * @returns Number of decoded samples or @ref opus_errorcodes
+  */
+OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode24(
+    OpusCustomDecoder *st,
+    const unsigned char *data,
+    int len,
+    opus_int32 *pcm,
     int frame_size
 ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
 
--- a/tests/test_opus_custom.c
+++ b/tests/test_opus_custom.c
@@ -43,6 +43,8 @@
    int float_decode;
    int custom_encode;
    int custom_decode;
+   int encoder_bit_depth;
+   int decoder_bit_depth;
 } TestCustomParams;
 
 void* generate_sine_sweep(double amplitude, int bit_depth, int sample_rate, int channels, int use_float, double duration_seconds, int* num_samples_out) {
@@ -135,7 +137,7 @@
 
    /* Generate input data */
    inbuf = generate_sine_sweep(SINE_SWEEP_AMPLITUDE,
-                               16,
+                               params.encoder_bit_depth,
                                params.sample_rate,
                                num_channels,
                                params.float_encode,
@@ -143,7 +145,7 @@
                                &input_samples);
 
    /* Allocate memory for output data */
-   if (params.float_decode) {
+   if (params.float_decode || params.decoder_bit_depth == 24) {
        outbuf = malloc(input_samples*num_channels*sizeof(float));
    }
    else {
@@ -202,20 +204,36 @@
       } else
 #endif
       {
-         opus_int16* input = (opus_int16*)inbuf;
          if (params.custom_encode) {
-            len = opus_custom_encode(encC,
-                                     &input[samp_count*num_channels],
-                                     frame_size,
-                                     packet,
-                                     MAX_PACKET);
-            if (len <= 0) {
-                fprintf(stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len));
-                ret = -1;
-                break;
+            if (params.encoder_bit_depth == 24) {
+               opus_int32* input = (opus_int32*)inbuf;
+               len = opus_custom_encode24(encC,
+                                          &input[samp_count*num_channels],
+                                          frame_size,
+                                          packet,
+                                          MAX_PACKET);
+               if (len <= 0) {
+                  fprintf(stderr, "opus_custom_encode24() failed: %s\n", opus_strerror(len));
+                  ret = -1;
+                  break;
+               }
             }
+            else {
+               opus_int16* input = (opus_int16*)inbuf;
+               len = opus_custom_encode(encC,
+                                        &input[samp_count*num_channels],
+                                        frame_size,
+                                        packet,
+                                        MAX_PACKET);
+               if (len <= 0) {
+                  fprintf(stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len));
+                  ret = -1;
+                  break;
+               }
+            }
          }
          else {
+            opus_int16* input = (opus_int16*)inbuf;
             len = opus_encode(enc,
                               &input[samp_count*num_channels],
                               frame_size,
@@ -307,21 +325,38 @@
       } else
 #endif
       {
-         opus_int16* output = (opus_int16*)outbuf;
          if (params.custom_decode) {
-            samples_decoded = opus_custom_decode(decC,
-                                                 packet,
-                                                 len,
-                                                 &output[samp_count*num_channels],
-                                                 frame_size);
+            if (params.decoder_bit_depth == 24) {
+               opus_int32* output = (opus_int32*)outbuf;
+               samples_decoded = opus_custom_decode24(decC,
+                                                      packet,
+                                                      len,
+                                                      &output[samp_count*num_channels],
+                                                      frame_size);
 
-            if (samples_decoded != frame_size) {
-                fprintf(stderr, "opus_custom_decode() returned %d\n", samples_decoded);
-                ret = -1;
-                break;
+               if (samples_decoded != frame_size) {
+                  fprintf(stderr, "opus_custom_decode24() returned %d\n", samples_decoded);
+                  ret = -1;
+                  break;
+               }
             }
+            else {
+               opus_int16* output = (opus_int16*)outbuf;
+               samples_decoded = opus_custom_decode(decC,
+                                                    packet,
+                                                    len,
+                                                    &output[samp_count*num_channels],
+                                                    frame_size);
+
+               if (samples_decoded != frame_size) {
+                  fprintf(stderr, "opus_custom_decode() returned %d\n", samples_decoded);
+                  ret = -1;
+                  break;
+               }
+            }
          }
          else {
+            opus_int16* output = (opus_int16*)outbuf;
             samples_decoded = opus_decode(dec,
                                           packet,
                                           len,
@@ -350,6 +385,15 @@
          }
          rmsd = sqrt(rmsd/(num_channels*samp_count));
       }
+      else if (params.decoder_bit_depth == 24) {
+         opus_int32* input = (opus_int32*)inbuf;
+         opus_int32* output = (opus_int32*)outbuf;
+         for (i = 0; i < samp_count * num_channels; i++) {
+            rmsd += (input[i]-output[i])*(double)(input[i]-output[i]);
+         }
+         rmsd = sqrt(rmsd/((double)num_channels*samp_count));
+         rmsd /= 8388608;
+      }
       else {
          opus_int16* input = (opus_int16*)inbuf;
          opus_int16* output = (opus_int16*)outbuf;
@@ -404,6 +448,8 @@
 #endif
    int use_custom_encode[2] = {0, 1};
    int use_custom_decode[2] = {0, 1};
+   int encoder_bit_depths[2] = {16, 24};
+   int decoder_bit_depths[2] = {16, 24};
 
    for (i = 0; i < num_encoders; i++) {
       int frame_size_ms_x2;
@@ -506,9 +552,22 @@
          params.float_encode = 0;
          params.float_decode = 0;
 #endif
+         if (params.custom_encode) {
+            params.encoder_bit_depth = RAND_SAMPLE(encoder_bit_depths);
+         }
+         else {
+            params.encoder_bit_depth = 16;
+         }
+         if (params.custom_decode) {
+            params.decoder_bit_depth = RAND_SAMPLE(decoder_bit_depths);
+         }
+         else {
+            params.decoder_bit_depth = 16;
+         }
 #ifdef RESYNTH
          /* Resynth logic works best when encoder/decoder use same datatype */
          params.float_decode = params.float_encode;
+         params.decoder_bit_depth = params.encoder_bit_depth;
 #endif
 
          if (params.custom_encode) {
@@ -529,17 +588,23 @@
          }
          fprintf(stderr,
                  "test_opus_custom: %d kHz, %d ch, float_encode: %d, float_decode: %d, "
+                 "encoder_bit_depth: %d, "
+                 "decoder_bit_depth: %d, "
                  "custom_encode: %d, custom_decode: %d, %d bps, vbr: %d, vbr constraint: %d, complexity: %d, "
                  "pkt loss: %d%%, lsb depth: %d, (%d/2) ms\n",
                  params.sample_rate / 1000, params.num_channels, params.float_encode, params.float_decode,
+                 params.encoder_bit_depth, params.decoder_bit_depth,
                  params.custom_encode, params.custom_decode, bitrate, vbr, vbr_constraint, complexity,
                  pkt_loss, lsb_depth, frame_size_ms_x2);
          if (test_encode(params)) {
             fprintf(stderr,
                     "test_opus_custom error: %d kHz, %d ch, float_encode: %d, float_decode: %d, "
+                    "encoder_bit_depth: %d, "
+                    "decoder_bit_depth: %d, "
                     "custom_encode: %d, custom_decode: %d, %d bps, vbr: %d, vbr constraint: %d, complexity: %d, "
                     "pkt loss: %d%%, lsb depth: %d, (%d/2) ms\n",
                     params.sample_rate / 1000, params.num_channels, params.float_encode, params.float_decode,
+                    params.encoder_bit_depth, params.decoder_bit_depth,
                     params.custom_encode, params.custom_decode, bitrate, vbr, vbr_constraint, complexity,
                     pkt_loss, lsb_depth, frame_size_ms_x2);
             test_failed();
--