diff --git a/src/opus.h b/src/opus.h index a7c8569d..36cf26a4 100644 --- a/src/opus.h +++ b/src/opus.h @@ -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; diff --git a/src/opus_decoder.c b/src/opus_decoder.c index 13daf6a4..d1b784a4 100644 --- a/src/opus_decoder.c +++ b/src/opus_decoder.c @@ -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)) diff --git a/src/opus_encoder.c b/src/opus_encoder.c index 30dd4d4f..80088780 100644 --- a/src/opus_encoder.c +++ b/src/opus_encoder.c @@ -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; diff --git a/src/opus_encoder.h b/src/opus_encoder.h index 5eda2b21..45dbac80 100644 --- a/src/opus_encoder.h +++ b/src/opus_encoder.h @@ -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; diff --git a/src/test_opus.c b/src/test_opus.c index e322be06..57e610a3 100644 --- a/src/test_opus.c +++ b/src/test_opus.c @@ -44,7 +44,7 @@ void print_usage( char* argv[] ) { fprintf(stderr, "Usage: %s " " [options] \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 : 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) {