From d186c913f76aaf99ef045e7eef860d8ba79d3cf5 Mon Sep 17 00:00:00 2001
From: Jean-Marc Valin <jmvalin@jmvalin.ca>
Date: Thu, 8 Sep 2011 15:13:46 -0400
Subject: [PATCH] Adds OPUS_SET_RESTRICTED_LOWDELAY() encoder ctl

Right now it will uncleanly switch to SILK if asked for 40 or 60 ms frames
---
 libcelt/opus_defines.h | 26 +++++++++++++++------
 src/opus_encoder.c     | 53 ++++++++++++++++++++++++++++++++----------
 src/test_opus.c        | 10 ++++----
 3 files changed, 66 insertions(+), 23 deletions(-)

diff --git a/libcelt/opus_defines.h b/libcelt/opus_defines.h
index ac1c46626..4445be7b7 100644
--- a/libcelt/opus_defines.h
+++ b/libcelt/opus_defines.h
@@ -103,6 +103,8 @@ extern "C" {
 #define OPUS_SET_DTX_REQUEST                 4016
 #define OPUS_GET_DTX_REQUEST                 4017
 #define OPUS_GET_FINAL_RANGE_REQUEST         4031
+#define OPUS_SET_RESTRICTED_LOWDELAY_REQUEST 4032
+#define OPUS_GET_RESTRICTED_LOWDELAY_REQUEST 4033
 
 /* Macros to trigger compilation errors when the wrong types are provided to a CTL */
 #define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
@@ -117,14 +119,11 @@ extern "C" {
 /** \cond DOXYGEN_EXCLUDE */
 /* Values for the varrious encoder CTLs */
 #define OPUS_AUTO                           -1000 /**<Auto bitrate @hideinitializer*/
-//#define OPUS_BITRATE_AUTO                      -2 /**<Auto bitrate @hideinitializer*/
 #define OPUS_BITRATE_MAX                       -1 /**<Maximum bitrate @hideinitializer*/
 #define OPUS_APPLICATION_VOIP                2000
 #define OPUS_APPLICATION_AUDIO               2001
-//#define OPUS_SIGNAL_AUTO                     3000
 #define OPUS_SIGNAL_VOICE                    3001
 #define OPUS_SIGNAL_MUSIC                    3002
-//#define OPUS_BANDWIDTH_AUTO                  1100 /**<Automatic bandpass @hideinitializer*/
 #define OPUS_BANDWIDTH_NARROWBAND            1101 /**< 4kHz bandpass @hideinitializer*/
 #define OPUS_BANDWIDTH_MEDIUMBAND            1102 /**< 6kHz bandpass @hideinitializer*/
 #define OPUS_BANDWIDTH_WIDEBAND              1103 /**< 8kHz bandpass @hideinitializer*/
@@ -190,14 +189,14 @@ extern "C" {
   * @hideinitializer */
 #define OPUS_GET_VBR_CONSTRAINT(x) OPUS_GET_VBR_CONSTRAINT_REQUEST, __opus_check_int_ptr(x)
 
-/** Configures mono forcing in the encoder.
+/** Configures mono/stereo forcing in the encoder.
   * This is useful when the caller knows that the input signal is currently a mono
   * source embedded in a stereo stream.
-  * \param[in] x <tt>int</tt>:   0 (default); 1 (forced mono)
+  * \param[in] x <tt>int</tt>:   OPUS_AUTO (default); 1 (forced mono); 2 (forced stereo)
   * @hideinitializer */
 #define OPUS_SET_FORCE_CHANNELS(x) OPUS_SET_FORCE_CHANNELS_REQUEST, __opus_check_int(x)
-/** Gets the encoder's forced mono configuration, @see [OPUS_SET_FORCE_MONO]
-  * \param[out] x <tt>int*</tt>: 0; 1
+/** Gets the encoder's forced channel configuration, @see [OPUS_SET_FORCE_CHANNELS]
+  * \param[out] x <tt>int*</tt>: OPUS_AUTO; 0; 1
   * @hideinitializer */
 #define OPUS_GET_FORCE_CHANNELS(x) OPUS_GET_FORCE_CHANNELS_REQUEST, __opus_check_int_ptr(x)
 
@@ -337,6 +336,19 @@ extern "C" {
   *
   * @hideinitializer */
 #define OPUS_GET_FINAL_RANGE(x) OPUS_GET_FINAL_RANGE_REQUEST, __opus_check_uint_ptr(x)
+
+/** Configures low-delay mode that disables the speech-optimized mode in exchange for slightly reduced delay.
+  * This is useful when the caller knows that the speech-optimized modes will not be needed (use with caution).
+  * The setting can only be changed right after initialization or after a reset.
+  * \param[in] x <tt>int</tt>:   0 (default); 1 (lowdelay)
+  * @hideinitializer */
+#define OPUS_SET_RESTRICTED_LOWDELAY(x) OPUS_SET_RESTRICTED_LOWDELAY_REQUEST, __opus_check_int(x)
+/** Gets the encoder's forced channel configuration, @see [OPUS_SET_RESTRICTED_LOWDELAY]
+  * \param[out] x <tt>int*</tt>: 0; 1
+  * @hideinitializer */
+#define OPUS_GET_RESTRICTED_LOWDELAY(x) OPUS_GET_RESTRICTED_LOWDELAY_REQUEST, __opus_check_int_ptr(x)
+
+
 /**@}*/
 
 /** \defgroup libinfo Opus library information functions
diff --git a/src/opus_encoder.c b/src/opus_encoder.c
index e5a94754d..7987e31da 100644
--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -67,6 +67,7 @@ struct OpusEncoder {
     int          vbr_constraint;
     int          bitrate_bps;
     int          user_bitrate_bps;
+    int          lowdelay;
     int          encoder_buffer;
 
 #define OPUS_ENCODER_RESET_START stream_channels
@@ -374,6 +375,7 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
     int cutoff_Hz, hp_freq_smth1;
     int voice_est;
     opus_int32 equiv_rate;
+    int delay_compensation;
     ALLOC_STACK;
 
     st->rangeFinal = 0;
@@ -386,6 +388,11 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
     silk_enc = (char*)st+st->silk_enc_offset;
     celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
 
+    if (st->lowdelay)
+       delay_compensation = 0;
+    else
+       delay_compensation = st->delay_compensation;
+
     if (st->user_bitrate_bps==OPUS_AUTO)
         st->bitrate_bps = 60*st->Fs/frame_size + st->Fs*st->channels;
     else if (st->user_bitrate_bps==OPUS_BITRATE_MAX)
@@ -446,7 +453,10 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
     }
 #else
     /* Mode selection depending on application and signal type */
-    if (st->user_forced_mode == OPUS_AUTO)
+    if (st->lowdelay)
+    {
+       st->mode = MODE_CELT_ONLY;
+    } else if (st->user_forced_mode == OPUS_AUTO)
     {
        int chan;
        opus_int32 mode_voice, mode_music;
@@ -578,9 +588,9 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
 
     ec_enc_init(&enc, data, max_data_bytes-1);
 
-    ALLOC(pcm_buf, (st->delay_compensation+frame_size)*st->channels, opus_val16);
-    for (i=0;i<st->delay_compensation*st->channels;i++)
-       pcm_buf[i] = st->delay_buffer[(st->encoder_buffer-st->delay_compensation)*st->channels+i];
+    ALLOC(pcm_buf, (delay_compensation+frame_size)*st->channels, opus_val16);
+    for (i=0;i<delay_compensation*st->channels;i++)
+       pcm_buf[i] = st->delay_buffer[(st->encoder_buffer-delay_compensation)*st->channels+i];
 
     if (st->mode == MODE_CELT_ONLY)
        hp_freq_smth1 = SKP_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 );
@@ -595,10 +605,10 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
 
     if (st->application == OPUS_APPLICATION_VOIP)
     {
-       hp_cutoff(pcm, cutoff_Hz, &pcm_buf[st->delay_compensation*st->channels], st->hp_mem, frame_size, st->channels, st->Fs);
+       hp_cutoff(pcm, cutoff_Hz, &pcm_buf[delay_compensation*st->channels], st->hp_mem, frame_size, st->channels, st->Fs);
     } else {
        for (i=0;i<frame_size*st->channels;i++)
-          pcm_buf[st->delay_compensation*st->channels + i] = pcm[i];
+          pcm_buf[delay_compensation*st->channels + i] = pcm[i];
     }
 
     /* SILK processing */
@@ -671,10 +681,10 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
         }
 
 #ifdef FIXED_POINT
-        pcm_silk = pcm_buf+st->delay_compensation*st->channels;
+        pcm_silk = pcm_buf+delay_compensation*st->channels;
 #else
         for (i=0;i<frame_size*st->channels;i++)
-            pcm_silk[i] = FLOAT2INT16(pcm_buf[st->delay_compensation*st->channels + i]);
+            pcm_silk[i] = FLOAT2INT16(pcm_buf[delay_compensation*st->channels + i]);
 #endif
         ret = silk_Encode( silk_enc, &st->silk_mode, pcm_silk, frame_size, &enc, &nBytes, 0 );
         if( ret ) {
@@ -736,7 +746,7 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
             celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(0));
             celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0));
             /* TODO: This wastes CPU a bit compared to just prefilling the buffer */
-            celt_encode_with_ec(celt_enc, &st->delay_buffer[(st->encoder_buffer-st->delay_compensation-st->Fs/400)*st->channels], st->Fs/400, dummy, 10, NULL);
+            celt_encode_with_ec(celt_enc, &st->delay_buffer[(st->encoder_buffer-delay_compensation-st->Fs/400)*st->channels], st->Fs/400, dummy, 10, NULL);
         } else {
             celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(2));
         }
@@ -768,10 +778,10 @@ int opus_encode_float(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
         nb_compr_bytes = 0;
     }
 
-    for (i=0;i<st->channels*(st->encoder_buffer-(frame_size+st->delay_compensation));i++)
+    for (i=0;i<st->channels*(st->encoder_buffer-(frame_size+delay_compensation));i++)
         st->delay_buffer[i] = st->delay_buffer[i+st->channels*frame_size];
     for (;i<st->encoder_buffer*st->channels;i++)
-        st->delay_buffer[i] = pcm_buf[(frame_size+st->delay_compensation-st->encoder_buffer)*st->channels+i];
+        st->delay_buffer[i] = pcm_buf[(frame_size+delay_compensation-st->encoder_buffer)*st->channels+i];
 
 
     if( st->mode == MODE_HYBRID && st->stream_channels == 2 ) {
@@ -1117,7 +1127,9 @@ int opus_encoder_ctl(OpusEncoder *st, int request, ...)
         case OPUS_GET_LOOKAHEAD_REQUEST:
         {
             opus_int32 *value = va_arg(ap, opus_int32*);
-            *value = st->delay_compensation+st->Fs/400;
+            *value = st->Fs/400;
+            if (!st->lowdelay)
+               *value += st->delay_compensation;
         }
         break;
         case OPUS_GET_FINAL_RANGE_REQUEST:
@@ -1154,6 +1166,23 @@ int opus_encoder_ctl(OpusEncoder *st, int request, ...)
             st->user_forced_mode = value;
         }
         break;
+        case OPUS_SET_RESTRICTED_LOWDELAY_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (!st->first && st->lowdelay != !!value)
+            {
+               ret = OPUS_BAD_ARG;
+               break;
+            }
+            st->lowdelay = !!value;
+        }
+        break;
+        case OPUS_GET_RESTRICTED_LOWDELAY_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            *value = st->lowdelay;
+        }
+        break;
         default:
             /* fprintf(stderr, "unknown opus_encoder_ctl() request: %d", request);*/
             ret = OPUS_UNIMPLEMENTED;
diff --git a/src/test_opus.c b/src/test_opus.c
index cd5975824..be208cc65 100644
--- a/src/test_opus.c
+++ b/src/test_opus.c
@@ -118,6 +118,7 @@ int main(int argc, char *argv[])
     int max_frame_size = 960*6;
     int curr_read=0;
     int sweep_bps = 0;
+    int lowdelay = 0;
 
     if (argc < 7 )
     {
@@ -216,6 +217,9 @@ int main(int argc, char *argv[])
         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-forcemono" ) == 0 ) {
             forcechannels = 1;
             args++;
+        } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-lowdelay" ) == 0 ) {
+            lowdelay = 1;
+            args++;
         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cvbr" ) == 0 ) {
             cvbr = 1;
             args++;
@@ -295,11 +299,9 @@ int main(int argc, char *argv[])
     opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(forcechannels));
     opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx));
     opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
+    opus_encoder_ctl(enc, OPUS_SET_RESTRICTED_LOWDELAY(lowdelay));
 
-    skip = 5*sampling_rate/1000;
-    /* When SILK resamples, add 18 samples delay */
-    /*if (mode != MODE_SILK_ONLY || sampling_rate > 16000)
-        skip += 18;*/
+    opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip));
 
     switch(bandwidth)
     {
-- 
GitLab