Allow multichannel effects

Test: Solo Tester with mutichannel file
Bug: 70038539
Change-Id: Id2e47eef62e55710bb6eda54c1f737aa3a3ce6bc
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
index 6e0f2b6..ede8e3f 100644
--- a/services/audioflinger/Configuration.h
+++ b/services/audioflinger/Configuration.h
@@ -47,6 +47,9 @@
 #ifdef FLOAT_EFFECT_CHAIN
 // define FLOAT_AUX to process aux effect buffers in float (FLOAT_EFFECT_CHAIN must be defined)
 #define FLOAT_AUX
+
+// define MULTICHANNEL_EFFECT_CHAIN to allow multichannel effects (FLOAT_EFFECT_CHAIN defined)
+#define MULTICHANNEL_EFFECT_CHAIN
 #endif
 
 #endif // ANDROID_AUDIOFLINGER_CONFIGURATION_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b4ff0d6..b13e551 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -26,6 +26,7 @@
 #include <system/audio_effects/effect_aec.h>
 #include <system/audio_effects/effect_ns.h>
 #include <system/audio_effects/effect_visualizer.h>
+#include <audio_utils/channels.h>
 #include <audio_utils/primitives.h>
 #include <media/AudioEffect.h>
 #include <media/audiohal/EffectHalInterface.h>
@@ -292,7 +293,6 @@
         return;
     }
 
-    // TODO: Implement multichannel effects; here outChannelCount == FCC_2 == 2
     const uint32_t inChannelCount =
             audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
     const uint32_t outChannelCount =
@@ -342,6 +342,7 @@
         if (isProcessImplemented()) {
             if (auxType) {
                 // We overwrite the aux input buffer here and clear after processing.
+                // aux input is always mono.
 #ifdef FLOAT_EFFECT_CHAIN
                 if (mSupportsFloat) {
 #ifndef FLOAT_AUX
@@ -371,6 +372,28 @@
                 }
             }
 #ifdef FLOAT_EFFECT_CHAIN
+            sp<EffectBufferHalInterface> inBuffer = mInBuffer;
+            sp<EffectBufferHalInterface> outBuffer = mOutBuffer;
+
+            if (!auxType && mInChannelCountRequested != inChannelCount) {
+                adjust_channels(
+                        inBuffer->audioBuffer()->f32, mInChannelCountRequested,
+                        mInConversionBuffer->audioBuffer()->f32, inChannelCount,
+                        sizeof(float),
+                        sizeof(float)
+                        * mInChannelCountRequested * mConfig.inputCfg.buffer.frameCount);
+                inBuffer = mInConversionBuffer;
+            }
+            if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE
+                    && mOutChannelCountRequested != outChannelCount) {
+                adjust_selected_channels(
+                        outBuffer->audioBuffer()->f32, mOutChannelCountRequested,
+                        mOutConversionBuffer->audioBuffer()->f32, outChannelCount,
+                        sizeof(float),
+                        sizeof(float)
+                        * mOutChannelCountRequested * mConfig.outputCfg.buffer.frameCount);
+                outBuffer = mOutConversionBuffer;
+            }
             if (!mSupportsFloat) { // convert input to int16_t as effect doesn't support float.
                 if (!auxType) {
                     if (mInConversionBuffer.get() == nullptr) {
@@ -379,8 +402,9 @@
                     }
                     memcpy_to_i16_from_float(
                             mInConversionBuffer->audioBuffer()->s16,
-                            mInBuffer->audioBuffer()->f32,
+                            inBuffer->audioBuffer()->f32,
                             inChannelCount * mConfig.inputCfg.buffer.frameCount);
+                    inBuffer = mInConversionBuffer;
                 }
                 if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
                     if (mOutConversionBuffer.get() == nullptr) {
@@ -389,21 +413,30 @@
                     }
                     memcpy_to_i16_from_float(
                             mOutConversionBuffer->audioBuffer()->s16,
-                            mOutBuffer->audioBuffer()->f32,
+                            outBuffer->audioBuffer()->f32,
                             outChannelCount * mConfig.outputCfg.buffer.frameCount);
+                    outBuffer = mOutConversionBuffer;
                 }
             }
 #endif
-
             ret = mEffectInterface->process();
-
 #ifdef FLOAT_EFFECT_CHAIN
             if (!mSupportsFloat) { // convert output int16_t back to float.
+                sp<EffectBufferHalInterface> target =
+                        mOutChannelCountRequested != outChannelCount
+                        ? mOutConversionBuffer : mOutBuffer;
+
                 memcpy_to_float_from_i16(
-                        mOutBuffer->audioBuffer()->f32,
+                        target->audioBuffer()->f32,
                         mOutConversionBuffer->audioBuffer()->s16,
                         outChannelCount * mConfig.outputCfg.buffer.frameCount);
             }
+            if (mOutChannelCountRequested != outChannelCount) {
+                adjust_selected_channels(mOutConversionBuffer->audioBuffer()->f32, outChannelCount,
+                        mOutBuffer->audioBuffer()->f32, mOutChannelCountRequested,
+                        sizeof(float),
+                        sizeof(float) * outChannelCount * mConfig.outputCfg.buffer.frameCount);
+            }
 #endif
         } else {
 #ifdef FLOAT_EFFECT_CHAIN
@@ -476,15 +509,28 @@
     }
 
     // TODO: handle configuration of effects replacing track process
+    // TODO: handle configuration of input (record) SW effects above the HAL,
+    // similar to output EFFECT_FLAG_TYPE_INSERT/REPLACE,
+    // in which case input channel masks should be used here.
     channelMask = thread->channelMask();
+    mConfig.inputCfg.channels = channelMask;
     mConfig.outputCfg.channels = channelMask;
 
     if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
-        mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
-        mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
-        ALOGV("Overriding auxiliary effect input as MONO and output as STEREO");
+        if (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_MONO) {
+            mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
+            ALOGV("Overriding auxiliary effect input channels %#x as MONO",
+                    mConfig.inputCfg.channels);
+        }
+#ifndef MULTICHANNEL_EFFECT_CHAIN
+        if (mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) {
+            mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+            ALOGV("Overriding auxiliary effect output channels %#x as STEREO",
+                    mConfig.outputCfg.channels);
+        }
+#endif
     } else {
-        mConfig.inputCfg.channels = channelMask;
+#ifndef MULTICHANNEL_EFFECT_CHAIN
         // TODO: Update this logic when multichannel effects are implemented.
         // For offloaded tracks consider mono output as stereo for proper effect initialization
         if (channelMask == AUDIO_CHANNEL_OUT_MONO) {
@@ -492,7 +538,12 @@
             mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
             ALOGV("Overriding effect input and output as STEREO");
         }
+#endif
     }
+    mInChannelCountRequested =
+            audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
+    mOutChannelCountRequested =
+            audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
 
     mConfig.inputCfg.format = EFFECT_BUFFER_FORMAT;
     mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
@@ -530,28 +581,58 @@
     status_t cmdStatus;
     size = sizeof(int);
     status = mEffectInterface->command(EFFECT_CMD_SET_CONFIG,
-                                       sizeof(effect_config_t),
+                                       sizeof(mConfig),
                                        &mConfig,
                                        &size,
                                        &cmdStatus);
     if (status == NO_ERROR) {
         status = cmdStatus;
-#ifdef FLOAT_EFFECT_CHAIN
-        mSupportsFloat = true;
-#endif
     }
-#ifdef FLOAT_EFFECT_CHAIN
-    else {
-        ALOGV("EFFECT_CMD_SET_CONFIG failed with float format, retry with int16_t.");
-        mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
-        mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+
+#ifdef MULTICHANNEL_EFFECT_CHAIN
+    if (status != NO_ERROR &&
+            (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
+                    || mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
+        // Older effects may require exact STEREO position mask.
+        if (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) {
+            ALOGV("Overriding effect input channels %#x as STEREO", mConfig.inputCfg.channels);
+            mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+        }
+        if (mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) {
+            ALOGV("Overriding effect output channels %#x as STEREO", mConfig.outputCfg.channels);
+            mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+        }
+        size = sizeof(int);
         status = mEffectInterface->command(EFFECT_CMD_SET_CONFIG,
-                                           sizeof(effect_config_t),
+                                           sizeof(mConfig),
                                            &mConfig,
                                            &size,
                                            &cmdStatus);
         if (status == NO_ERROR) {
             status = cmdStatus;
+        }
+    }
+#endif
+
+#ifdef FLOAT_EFFECT_CHAIN
+    if (status == NO_ERROR) {
+        mSupportsFloat = true;
+    }
+
+    if (status != NO_ERROR) {
+        ALOGV("EFFECT_CMD_SET_CONFIG failed with float format, retry with int16_t.");
+        mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+        mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+        size = sizeof(int);
+        status = mEffectInterface->command(EFFECT_CMD_SET_CONFIG,
+                                           sizeof(mConfig),
+                                           &mConfig,
+                                           &size,
+                                           &cmdStatus);
+        if (status == NO_ERROR) {
+            status = cmdStatus;
+        }
+        if (status == NO_ERROR) {
             mSupportsFloat = false;
             ALOGVV("config worked with 16 bit");
         } else {
@@ -929,11 +1010,15 @@
     // the original buffer) when the output buffer is identical to the input buffer,
     // but we don't optimize for it here.
     const bool auxType = (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY;
-    if (!auxType && !mSupportsFloat && mInBuffer.get() != nullptr) {
+    const uint32_t inChannelCount =
+            audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
+    const bool formatMismatch = !mSupportsFloat || mInChannelCountRequested != inChannelCount;
+    if (!auxType && formatMismatch && mInBuffer.get() != nullptr) {
         // we need to translate - create hidl shared buffer and intercept
         const size_t inFrameCount = mConfig.inputCfg.buffer.frameCount;
-        const int inChannels = audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
-        const size_t size = inChannels * inFrameCount * sizeof(int16_t);
+        // Use FCC_2 in case mInChannelCountRequested is mono and the effect is stereo.
+        const uint32_t inChannels = std::max((uint32_t)FCC_2, mInChannelCountRequested);
+        const size_t size = inChannels * inFrameCount * std::max(sizeof(int16_t), sizeof(float));
 
         ALOGV("%s: setInBuffer updating for inChannels:%d inFrameCount:%zu total size:%zu",
                 __func__, inChannels, inFrameCount, size);
@@ -970,10 +1055,14 @@
 #ifdef FLOAT_EFFECT_CHAIN
     // Note: Any effect that does not accumulate does not need mOutConversionBuffer and
     // can do in-place conversion from int16_t to float.  We don't optimize here.
-    if (!mSupportsFloat && mOutBuffer.get() != nullptr) {
+    const uint32_t outChannelCount =
+            audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
+    const bool formatMismatch = !mSupportsFloat || mOutChannelCountRequested != outChannelCount;
+    if (formatMismatch && mOutBuffer.get() != nullptr) {
         const size_t outFrameCount = mConfig.outputCfg.buffer.frameCount;
-        const int outChannels = audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
-        const size_t size = outChannels * outFrameCount * sizeof(int16_t);
+        // Use FCC_2 in case mOutChannelCountRequested is mono and the effect is stereo.
+        const uint32_t outChannels = std::max((uint32_t)FCC_2, mOutChannelCountRequested);
+        const size_t size = outChannels * outFrameCount * std::max(sizeof(int16_t), sizeof(float));
 
         ALOGV("%s: setOutBuffer updating for outChannels:%d outFrameCount:%zu total size:%zu",
                 __func__, outChannels, outFrameCount, size);
@@ -1813,14 +1902,8 @@
     if (mInBuffer == NULL) {
         return;
     }
-    // TODO: This will change in the future, depending on multichannel
-    // and sample format changes for effects.
-    // Currently effects processing is only available for stereo, AUDIO_FORMAT_PCM_16_BIT
-    // (4 bytes frame size)
-
     const size_t frameSize =
-            audio_bytes_per_sample(EFFECT_BUFFER_FORMAT)
-            * std::min((uint32_t)FCC_2, thread->channelCount());
+            audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * thread->channelCount();
 
     memset(mInBuffer->audioBuffer()->raw, 0, thread->frameCount() * frameSize);
     mInBuffer->commit();
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index eea3208..2327bb9 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -173,6 +173,8 @@
     bool    mSupportsFloat;         // effect supports float processing
     sp<EffectBufferHalInterface> mInConversionBuffer;  // Buffers for HAL conversion if needed.
     sp<EffectBufferHalInterface> mOutConversionBuffer;
+    uint32_t mInChannelCountRequested;
+    uint32_t mOutChannelCountRequested;
 #endif
 };
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d5def48..3a41ac8 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1176,6 +1176,7 @@
 
     switch (mType) {
     case MIXER: {
+#ifndef MULTICHANNEL_EFFECT_CHAIN
         // Reject any effect on mixer multichannel sinks.
         // TODO: fix both format and multichannel issues with effects.
         if (mChannelCount != FCC_2) {
@@ -1183,6 +1184,7 @@
                     " thread %s", desc->name, mChannelCount, mThreadName);
             return BAD_VALUE;
         }
+#endif
         audio_output_flags_t flags = mOutput->flags;
         if (hasFastMixer() || (flags & AUDIO_OUTPUT_FLAG_FAST)) {
             if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
@@ -1229,6 +1231,7 @@
                 desc->name, mThreadName);
         return BAD_VALUE;
     case DUPLICATING:
+#ifndef MULTICHANNEL_EFFECT_CHAIN
         // Reject any effect on mixer multichannel sinks.
         // TODO: fix both format and multichannel issues with effects.
         if (mChannelCount != FCC_2) {
@@ -1236,6 +1239,7 @@
                     " on DUPLICATING thread %s", desc->name, mChannelCount, mThreadName);
             return BAD_VALUE;
         }
+#endif
         if ((sessionId == AUDIO_SESSION_OUTPUT_STAGE) || (sessionId == AUDIO_SESSION_OUTPUT_MIX)) {
             ALOGW("checkEffectCompatibility_l(): global effect %s on DUPLICATING"
                     " thread %s", desc->name, mThreadName);