Simple mode selection logic
authorJean-Marc Valin <jean-marc.valin@octasic.com>
Tue, 8 Mar 2011 19:57:46 +0000 (14:57 -0500)
committerJean-Marc Valin <jean-marc.valin@octasic.com>
Tue, 8 Mar 2011 19:57:46 +0000 (14:57 -0500)
src/opus.h
src/opus_decoder.c
src/opus_encoder.c
src/opus_encoder.h
src/test_opus.c

index a7c8569..36cf26a 100644 (file)
@@ -43,8 +43,30 @@ extern "C" {
 #define __check_int(x) (((void)((x) == (int)0)), (int)(x))
 #define __check_int_ptr(ptr) ((ptr) + ((ptr) - (int*)(ptr)))
 
+/* Error codes */
+/** No error */
+#define OPUS_OK                0
+/** An (or more) invalid argument (e.g. out of range) */
+#define OPUS_BAD_ARG          -1
+/** The mode struct passed is invalid */
+#define OPUS_INVALID_MODE     -2
+/** An internal error was detected */
+#define OPUS_INTERNAL_ERROR   -3
+/** The data passed (e.g. compressed data to decoder) is corrupted */
+#define OPUS_CORRUPTED_DATA   -4
+/** Invalid/unsupported request number */
+#define OPUS_UNIMPLEMENTED    -5
+/** An encoder or decoder structure is invalid or already freed */
+#define OPUS_INVALID_STATE    -6
+/** Memory allocation has failed */
+#define OPUS_ALLOC_FAIL       -7
+
 #define OPUS_TEST_RANGE_CODER_STATE     1
 
+#define OPUS_MODE_AUTO          2000
+#define OPUS_MODE_VOICE         2001
+#define OPUS_MODE_AUDIO         2002
+
 #define MODE_SILK_ONLY 1000
 #define MODE_HYBRID    1001
 #define MODE_CELT_ONLY 1002
@@ -97,6 +119,11 @@ extern "C" {
 #define OPUS_GET_DTX_FLAG_REQUEST 17
 #define OPUS_GET_DTX_FLAG(x) OPUS_GET_DTX_FLAG_REQUEST, __check_int_ptr(x)
 
+#define OPUS_SET_VOICE_RATIO_REQUEST 18
+#define OPUS_SET_VOICE_RATIO(x) OPUS_SET_VOICE_RATIO_REQUEST, __check_int(x)
+#define OPUS_GET_VOICE_RATIO_REQUEST 19
+#define OPUS_GET_VOICE_RATIO(x) OPUS_GET_VOICE_RATIO_REQUEST, __check_int_ptr(x)
+
 typedef struct OpusEncoder OpusEncoder;
 typedef struct OpusDecoder OpusDecoder;
 
index 13daf6a..d1b784a 100644 (file)
@@ -142,6 +142,12 @@ int opus_decode(OpusDecoder *st, const unsigned char *data,
         mode = st->prev_mode;
     }
 
+    if (st->stream_channels > st->channels)
+        return OPUS_CORRUPTED_DATA;
+
+    if (st->stream_channels == 2 && mode != MODE_CELT_ONLY)
+        return OPUS_UNIMPLEMENTED;
+
     if (data!=NULL && !st->prev_redundancy && mode != st->prev_mode && st->prev_mode > 0
                && !(mode == MODE_SILK_ONLY && st->prev_mode == MODE_HYBRID)
                && !(mode == MODE_HYBRID && st->prev_mode == MODE_SILK_ONLY))
index 30dd4d4..8008878 100644 (file)
@@ -39,6 +39,7 @@
 
 OpusEncoder *opus_encoder_create(int Fs, int channels)
 {
+    int err;
     char *raw_state;
        OpusEncoder *st;
        int ret, silkEncSizeBytes, celtEncSizeBytes;
@@ -74,12 +75,14 @@ OpusEncoder *opus_encoder_create(int Fs, int channels)
 
     /* Create CELT encoder */
        /* Initialize CELT encoder */
-       st->celt_enc = celt_encoder_init(st->celt_enc, Fs, channels, NULL);
+       st->celt_enc = celt_encoder_init(st->celt_enc, Fs, channels, &err);
 
        st->mode = MODE_HYBRID;
        st->bandwidth = BANDWIDTH_FULLBAND;
        st->use_vbr = 0;
        st->bitrate_bps = 32000;
+       st->user_mode = OPUS_MODE_AUTO;
+       st->voice_ratio = 90;
 
        st->encoder_buffer = st->Fs/100;
        st->delay_compensation = st->Fs/400;
@@ -106,6 +109,88 @@ int opus_encode(OpusEncoder *st, const short *pcm, int frame_size,
     short pcm_buf[960*2];
     int nb_compr_bytes;
     int to_celt = 0;
+    celt_int32 mono_rate;
+
+    if (st->channels == 2)
+    {
+        celt_int32 decision_rate;
+        decision_rate = st->bitrate_bps + st->voice_ratio*st->voice_ratio;
+        if (st->stream_channels == 2)
+            decision_rate += 4000;
+        else
+            decision_rate -= 4000;
+        if (decision_rate>48000)
+            st->stream_channels = 2;
+        else
+            st->stream_channels = 1;
+    } else {
+        st->stream_channels = 1;
+    }
+    /* Equivalent bit-rate for mono */
+    mono_rate = st->bitrate_bps;
+    if (st->stream_channels==2)
+        mono_rate = (mono_rate+10000)/2;
+
+    /* Mode selection */
+    if (st->user_mode==OPUS_MODE_AUTO)
+    {
+        celt_int32 decision_rate;
+        decision_rate = mono_rate - 3*st->voice_ratio*st->voice_ratio;
+        if (st->prev_mode == MODE_CELT_ONLY)
+            decision_rate += 4000;
+        else if (st->prev_mode>0)
+            decision_rate -= 4000;
+        if (decision_rate>24000)
+            st->mode = MODE_CELT_ONLY;
+        else
+            st->mode = MODE_SILK_ONLY;
+    } else if (st->user_mode==OPUS_MODE_VOICE)
+    {
+        st->mode = MODE_SILK_ONLY;
+    } else {/* OPUS_AUDIO_MODE */
+        st->mode = MODE_CELT_ONLY;
+    }
+
+    /* FIXME: Remove this once SILK supports stereo */
+    if (st->channels == 2)
+        st->mode = MODE_CELT_ONLY;
+
+    /* Bandwidth selection */
+    if (st->mode == MODE_CELT_ONLY)
+    {
+        if (mono_rate>35000 || (mono_rate>28000 && st->bandwidth==BANDWIDTH_FULLBAND))
+            st->bandwidth = BANDWIDTH_FULLBAND;
+        else if (mono_rate>28000 || (mono_rate>24000 && st->bandwidth==BANDWIDTH_SUPERWIDEBAND))
+            st->bandwidth = BANDWIDTH_SUPERWIDEBAND;
+        else if (mono_rate>24000 || (mono_rate>18000 && st->bandwidth==BANDWIDTH_WIDEBAND))
+            st->bandwidth = BANDWIDTH_WIDEBAND;
+        else
+            st->bandwidth = BANDWIDTH_NARROWBAND;
+    } else {
+        if (mono_rate>28000 || (mono_rate>24000 && st->bandwidth==BANDWIDTH_FULLBAND))
+            st->bandwidth = BANDWIDTH_FULLBAND;
+        else if (mono_rate>24000 || (mono_rate>18000 && st->bandwidth==BANDWIDTH_SUPERWIDEBAND))
+            st->bandwidth = BANDWIDTH_SUPERWIDEBAND;
+        else if (mono_rate>18000 || (mono_rate>14000 && st->bandwidth==BANDWIDTH_WIDEBAND))
+            st->bandwidth = BANDWIDTH_WIDEBAND;
+        else if (mono_rate>14000 || (mono_rate>11000 && st->bandwidth==BANDWIDTH_MEDIUMBAND))
+            st->bandwidth = BANDWIDTH_MEDIUMBAND;
+        else
+            st->bandwidth = BANDWIDTH_NARROWBAND;
+    }
+    /* Preventing non-sensical configurations */
+    if (frame_size < st->Fs/100 && st->mode != MODE_CELT_ONLY)
+        st->mode = MODE_CELT_ONLY;
+    if (frame_size > st->Fs/50 && st->mode != MODE_SILK_ONLY)
+        st->mode = MODE_SILK_ONLY;
+    if (st->mode == MODE_CELT_ONLY && st->bandwidth == BANDWIDTH_MEDIUMBAND)
+        st->bandwidth = BANDWIDTH_WIDEBAND;
+    if (st->mode == MODE_SILK_ONLY && st->bandwidth > BANDWIDTH_WIDEBAND)
+        st->mode = MODE_HYBRID;
+    if (st->mode == MODE_HYBRID && st->bandwidth <= BANDWIDTH_WIDEBAND)
+        st->mode = MODE_SILK_ONLY;
+
+    printf("%d %d %d\n", st->stream_channels, st->mode, st->bandwidth);
 
        bytes_target = st->bitrate_bps * frame_size / (st->Fs * 8) - 1;
 
@@ -399,13 +484,13 @@ void opus_encoder_ctl(OpusEncoder *st, int request, ...)
         case OPUS_SET_MODE_REQUEST:
         {
             int value = va_arg(ap, int);
-            st->mode = value;
+            st->user_mode = value;
         }
         break;
         case OPUS_GET_MODE_REQUEST:
         {
             int *value = va_arg(ap, int*);
-            *value = st->mode;
+            *value = st->user_mode;
         }
         break;
         case OPUS_SET_BITRATE_REQUEST:
@@ -501,6 +586,18 @@ void opus_encoder_ctl(OpusEncoder *st, int request, ...)
             *value = st->use_vbr;
         }
         break;
+        case OPUS_SET_VOICE_RATIO_REQUEST:
+        {
+            int value = va_arg(ap, int);
+            st->voice_ratio = value;
+        }
+        break;
+        case OPUS_GET_VOICE_RATIO_REQUEST:
+        {
+            int *value = va_arg(ap, int*);
+            *value = st->voice_ratio;
+        }
+        break;
         default:
             fprintf(stderr, "unknown opus_encoder_ctl() request: %d", request);
             break;
index 5eda2b2..45dbac8 100644 (file)
@@ -43,8 +43,10 @@ struct OpusEncoder {
        int          stream_channels;
 
     int          mode;
+    int          user_mode;
     int          prev_mode;
        int          bandwidth;
+       int          voice_ratio;
     /* Sampling rate (at the API level) */
     int          Fs;
     int          use_vbr;
index e322be0..57e610a 100644 (file)
@@ -44,7 +44,7 @@ void print_usage( char* argv[] )
 {
     fprintf(stderr, "Usage: %s <mode (0/1/2)> <sampling rate (Hz)> <channels> "
         "<bits per second>  [options] <input> <output>\n\n", argv[0]);
-    fprintf(stderr, "mode: 0 for SILK, 1 for hybrid, 2 for CELT:\n" );
+    fprintf(stderr, "mode: 0 for audo, 1 for voice, 2 for audio:\n" );
     fprintf(stderr, "options:\n" );
     fprintf(stderr, "-cbr                 : enable constant bitrate; default: VBR\n" );
     fprintf(stderr, "-bandwidth <NB|MB|WB|SWB|FB>  : audio bandwidth (from narrowband to fullband); default: sampling rate\n" );
@@ -103,7 +103,7 @@ int main(int argc, char *argv[])
       return 1;
    }
 
-   mode = atoi(argv[1]) + MODE_SILK_ONLY;
+   mode = atoi(argv[1]) + OPUS_MODE_AUTO;
    sampling_rate = atoi(argv[2]);
    channels = atoi(argv[3]);
    bitrate_bps = atoi(argv[4]);
@@ -200,7 +200,7 @@ int main(int argc, char *argv[])
         }
    }
 
-   if( mode < MODE_SILK_ONLY || mode > MODE_CELT_ONLY ) {
+   if( mode < OPUS_MODE_AUTO || mode > OPUS_MODE_AUDIO) {
       fprintf (stderr, "mode must be: 0, 1 or 2\n");
       return 1;
    }
@@ -233,7 +233,7 @@ int main(int argc, char *argv[])
       return 1;
    }
 
-   if (mode==MODE_SILK_ONLY)
+   /*if (mode==MODE_SILK_ONLY)
    {
        if (bandwidth == BANDWIDTH_SUPERWIDEBAND || bandwidth == BANDWIDTH_FULLBAND)
        {
@@ -256,7 +256,7 @@ int main(int argc, char *argv[])
            fprintf (stderr, "Transform mode does not support mediumband\n");
            return 1;
        }
-   }
+   }*/
 
    enc = opus_encoder_create(sampling_rate, channels);
    dec = opus_decoder_create(sampling_rate, channels);
@@ -277,8 +277,8 @@ int main(int argc, char *argv[])
 
    skip = 5*sampling_rate/1000;
    /* When SILK resamples, add 18 samples delay */
-   if (mode != MODE_SILK_ONLY || sampling_rate > 16000)
-          skip += 18;
+   /*if (mode != MODE_SILK_ONLY || sampling_rate > 16000)
+          skip += 18;*/
 
    switch(bandwidth)
    {