[opus] [PATCH] Add Functions to Create Ambisonic Multistream Encoder

Michael Graczyk mgraczyk at google.com
Wed May 4 22:24:16 UTC 2016


This patch adds top level functions to create an ambisonic multistream
encoder. The implementation currently just calls the analogous
surround sound functions with channel mapping 255 to create an encoder
that bundles uncoupled streams. Forthcoming patches will actually set
channel bitrate and other configuration.

My main concern is that adding additional
opus_multistream_*_encoder_create/init/get_size functions is
unnecessary. Should we instead just add new channel mappings to
opus_multistream_surround_encoder_*, despite the name?

Thanks,
Michael Graczyk
-------------- next part --------------
From 6c0dab8d20b168e4ecfd905d2cf7f29359993409 Mon Sep 17 00:00:00 2001
From: Michael Graczyk <michael at mgraczyk.com>
Date: Mon, 2 May 2016 21:42:18 -0700
Subject: [PATCH] Add functions to create opus ambisonic encoder.

The implementation currently uses the surround encoder with mapping family 255.
---
 include/opus_multistream.h     | 27 ++++++++++++
 src/opus_multistream_encoder.c | 95 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 107 insertions(+), 15 deletions(-)

diff --git a/include/opus_multistream.h b/include/opus_multistream.h
index 47e0390..7850482 100644
--- a/include/opus_multistream.h
+++ b/include/opus_multistream.h
@@ -210,6 +210,11 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_surround_encoder
       int mapping_family
 );
 
+OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_ambisonic_encoder_get_size(
+      int channels,
+      int mapping_family
+);
+
 
 /** Allocates and initializes a multistream encoder state.
   * Call opus_multistream_encoder_destroy() to release
@@ -275,6 +280,17 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_surround_enc
       int *error
 ) OPUS_ARG_NONNULL(5);
 
+OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_ambisonic_encoder_create(
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application,
+      int *error
+) OPUS_ARG_NONNULL(5);
+
 /** Initialize a previously allocated multistream encoder state.
   * The memory pointed to by \a st must be at least the size returned by
   * opus_multistream_encoder_get_size().
@@ -344,6 +360,17 @@ OPUS_EXPORT int opus_multistream_surround_encoder_init(
       int application
 ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
 
+OPUS_EXPORT int opus_multistream_ambisonic_encoder_init(
+      OpusMSEncoder *st,
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application
+) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
+
 /** Encodes a multistream Opus frame.
   * @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
   * @param[in] pcm <tt>const opus_int16*</tt>: The input signal as interleaved
diff --git a/src/opus_multistream_encoder.c b/src/opus_multistream_encoder.c
index 9e85773..9e5bb1b 100644
--- a/src/opus_multistream_encoder.c
+++ b/src/opus_multistream_encoder.c
@@ -70,13 +70,19 @@ typedef void (*opus_copy_channel_in_func)(
   int frame_size
 );
 
+typedef enum {
+  ALLOCATION_MODE_NONE = 0,
+  ALLOCATION_MODE_SURROUND = 1,
+  ALLOCATION_MODE_AMBISONICS = 2,
+} AllocationMode;
+
 struct OpusMSEncoder {
    ChannelLayout layout;
    int arch;
    int lfe_stream;
    int application;
    int variable_duration;
-   int surround;
+   AllocationMode allocation_mode;
    opus_int32 bitrate_bps;
    float subframe_mem[3];
    /* Encoder states go here */
@@ -242,6 +248,7 @@ void surround_analysis(const CELTMode *celt_mode, const void *pcm, opus_val16 *b
    upsample = resampling_factor(rate);
    frame_size = len*upsample;
 
+   /* LM = log2(frame_size / 120) */
    for (LM=0;LM<celt_mode->maxLM;LM++)
       if (celt_mode->shortMdctSize<<LM==frame_size)
          break;
@@ -408,6 +415,11 @@ opus_int32 opus_multistream_surround_encoder_get_size(int channels, int mapping_
    return size;
 }
 
+opus_int32 opus_multistream_ambisonic_encoder_get_size(int channels, int mapping_family)
+{
+   return opus_multistream_surround_encoder_get_size(channels, mapping_family);
+}
+
 
 static int opus_multistream_encoder_init_impl(
       OpusMSEncoder *st,
@@ -417,7 +429,7 @@ static int opus_multistream_encoder_init_impl(
       int coupled_streams,
       const unsigned char *mapping,
       int application,
-      int surround
+      AllocationMode allocation_mode
 )
 {
    int coupled_size;
@@ -434,7 +446,7 @@ static int opus_multistream_encoder_init_impl(
    st->layout.nb_streams = streams;
    st->layout.nb_coupled_streams = coupled_streams;
    st->subframe_mem[0]=st->subframe_mem[1]=st->subframe_mem[2]=0;
-   if (!surround)
+   if (allocation_mode != ALLOCATION_MODE_SURROUND)
       st->lfe_stream = -1;
    st->bitrate_bps = OPUS_AUTO;
    st->application = application;
@@ -463,12 +475,12 @@ static int opus_multistream_encoder_init_impl(
       if(ret!=OPUS_OK)return ret;
       ptr += align(mono_size);
    }
-   if (surround)
+   if (allocation_mode == ALLOCATION_MODE_SURROUND)
    {
       OPUS_CLEAR(ms_get_preemph_mem(st), channels);
       OPUS_CLEAR(ms_get_window_mem(st), channels*120);
    }
-   st->surround = surround;
+   st->allocation_mode = allocation_mode;
    return OPUS_OK;
 }
 
@@ -482,7 +494,9 @@ int opus_multistream_encoder_init(
       int application
 )
 {
-   return opus_multistream_encoder_init_impl(st, Fs, channels, streams, coupled_streams, mapping, application, 0);
+   return opus_multistream_encoder_init_impl(st, Fs, channels, streams,
+                                             coupled_streams, mapping,
+                                             application, ALLOCATION_MODE_NONE);
 }
 
 int opus_multistream_surround_encoder_init(
@@ -496,6 +510,7 @@ int opus_multistream_surround_encoder_init(
       int application
 )
 {
+   AllocationMode allocation_mode;
    if ((channels>255) || (channels<1))
       return OPUS_BAD_ARG;
    st->lfe_stream = -1;
@@ -532,8 +547,32 @@ int opus_multistream_surround_encoder_init(
          mapping[i] = i;
    } else
       return OPUS_UNIMPLEMENTED;
-   return opus_multistream_encoder_init_impl(st, Fs, channels, *streams, *coupled_streams,
-         mapping, application, channels>2&&mapping_family==1);
+
+   if (channels > 2 && mapping_family == 1) {
+     allocation_mode = ALLOCATION_MODE_SURROUND;
+   } else {
+     allocation_mode = ALLOCATION_MODE_NONE;
+   }
+
+   return opus_multistream_encoder_init_impl(st, Fs, channels, *streams,
+                                             *coupled_streams, mapping,
+                                             application, allocation_mode);
+}
+
+int opus_multistream_ambisonic_encoder_init(
+      OpusMSEncoder *st,
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application
+)
+{
+   return opus_multistream_surround_encoder_init(
+       st, Fs, channels, mapping_family, streams, coupled_streams, mapping,
+       application);
 }
 
 OpusMSEncoder *opus_multistream_encoder_create(
@@ -618,6 +657,32 @@ OpusMSEncoder *opus_multistream_surround_encoder_create(
    return st;
 }
 
+OpusMSEncoder *opus_multistream_ambisonic_encoder_create(
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application,
+      int *error
+)
+{
+   /* Only support uncoupled and ambisonic mapping families */
+   const int internal_mapping_family = 255;
+   if (!(mapping_family==2 || mapping_family==255))
+   {
+      if (error)
+         *error = OPUS_BAD_ARG;
+      return NULL;
+   }
+
+   /* Encode ambisonics as uncoupled streams */
+   return opus_multistream_surround_encoder_create(
+       Fs, channels, internal_mapping_family, streams, coupled_streams, mapping,
+       application, error);
+}
+
 static opus_int32 surround_rate_allocation(
       OpusMSEncoder *st,
       opus_int32 *rate,
@@ -730,7 +795,7 @@ static int opus_multistream_encode_native
    opus_int32 smallest_packet;
    ALLOC_STACK;
 
-   if (st->surround)
+   if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
    {
       preemph_mem = ms_get_preemph_mem(st);
       mem = ms_get_window_mem(st);
@@ -784,7 +849,7 @@ static int opus_multistream_encode_native
    mono_size = opus_encoder_get_size(1);
 
    ALLOC(bandSMR, 21*st->layout.nb_channels, opus_val16);
-   if (st->surround)
+   if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
    {
       surround_analysis(celt_mode, pcm, bandSMR, mem, preemph_mem, frame_size, 120, st->layout.nb_channels, Fs, copy_channel_in, st->arch);
    }
@@ -813,7 +878,7 @@ static int opus_multistream_encode_native
       else
          ptr += align(mono_size);
       opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrates[s]));
-      if (st->surround)
+      if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
       {
          opus_int32 equiv_rate;
          equiv_rate = st->bitrate_bps;
@@ -859,7 +924,7 @@ static int opus_multistream_encode_native
          (*copy_channel_in)(buf+1, 2,
             pcm, st->layout.nb_channels, right, frame_size);
          ptr += align(coupled_size);
-         if (st->surround)
+         if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
          {
             for (i=0;i<21;i++)
             {
@@ -875,7 +940,7 @@ static int opus_multistream_encode_native
          (*copy_channel_in)(buf, 1,
             pcm, st->layout.nb_channels, chan, frame_size);
          ptr += align(mono_size);
-         if (st->surround)
+         if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
          {
             for (i=0;i<21;i++)
                bandLogE[i] = bandSMR[21*chan+i];
@@ -883,7 +948,7 @@ static int opus_multistream_encode_native
          c1 = chan;
          c2 = -1;
       }
-      if (st->surround)
+      if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
          opus_encoder_ctl(enc, OPUS_SET_ENERGY_MASK(bandLogE));
       /* number of bytes left (+Toc) */
       curr_max = max_data_bytes - tot_size;
@@ -1183,7 +1248,7 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
    {
       int s;
       st->subframe_mem[0] = st->subframe_mem[1] = st->subframe_mem[2] = 0;
-      if (st->surround)
+      if (st->allocation_mode == ALLOCATION_MODE_SURROUND)
       {
          OPUS_CLEAR(ms_get_preemph_mem(st), st->layout.nb_channels);
          OPUS_CLEAR(ms_get_window_mem(st), st->layout.nb_channels*120);
-- 
2.8.0.rc3.226.g39d4020



More information about the opus mailing list