Dup first haptic channel to the second when it is not provided.

In this CL, if the device supports two haptic channels while the app
only provides one, the second haptic channel will be duplicating the
first haptic channel. With this change, it can help the users to get
benefit from two haptic channels when it is supported.

Bug: 203725447
Test: atest SoundPoolHapticTest
Test: play single and dual haptic channels
Test: audio smoke test
Change-Id: I6d54b65a5e48b9b49f5464523542aa4cc6a9efa3
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index e68c002..07b2520 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -108,15 +108,11 @@
 
     if (track->mHapticChannelCount > 0) {
         track->mAdjustInChannelCount = track->channelCount + track->mHapticChannelCount;
-        track->mAdjustOutChannelCount = track->channelCount + track->mMixerHapticChannelCount;
-        track->mAdjustNonDestructiveInChannelCount = track->mAdjustOutChannelCount;
-        track->mAdjustNonDestructiveOutChannelCount = track->channelCount;
+        track->mAdjustOutChannelCount = track->channelCount;
         track->mKeepContractedChannels = track->mHapticPlaybackEnabled;
     } else {
         track->mAdjustInChannelCount = 0;
         track->mAdjustOutChannelCount = 0;
-        track->mAdjustNonDestructiveInChannelCount = 0;
-        track->mAdjustNonDestructiveOutChannelCount = 0;
         track->mKeepContractedChannels = false;
     }
 
@@ -131,8 +127,7 @@
     // do it after downmix since track format may change!
     track->prepareForReformat();
 
-    track->prepareForAdjustChannelsNonDestructive(mFrameCount);
-    track->prepareForAdjustChannels();
+    track->prepareForAdjustChannels(mFrameCount);
 
     // Resampler channels may have changed.
     track->recreateResampler(mSampleRate);
@@ -265,48 +260,20 @@
     }
 }
 
-status_t AudioMixer::Track::prepareForAdjustChannels()
+status_t AudioMixer::Track::prepareForAdjustChannels(size_t frames)
 {
     ALOGV("AudioMixer::prepareForAdjustChannels(%p) with inChannelCount: %u, outChannelCount: %u",
             this, mAdjustInChannelCount, mAdjustOutChannelCount);
     unprepareForAdjustChannels();
     if (mAdjustInChannelCount != mAdjustOutChannelCount) {
-        mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider(
-                mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, kCopyBufferFrameCount));
-        reconfigureBufferProviders();
-    }
-    return NO_ERROR;
-}
-
-void AudioMixer::Track::unprepareForAdjustChannelsNonDestructive()
-{
-    ALOGV("AUDIOMIXER::unprepareForAdjustChannelsNonDestructive");
-    if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) {
-        mContractChannelsNonDestructiveBufferProvider.reset(nullptr);
-        reconfigureBufferProviders();
-    }
-}
-
-status_t AudioMixer::Track::prepareForAdjustChannelsNonDestructive(size_t frames)
-{
-    ALOGV("AudioMixer::prepareForAdjustChannelsNonDestructive(%p) with inChannelCount: %u, "
-          "outChannelCount: %u, keepContractedChannels: %d",
-            this, mAdjustNonDestructiveInChannelCount, mAdjustNonDestructiveOutChannelCount,
-            mKeepContractedChannels);
-    unprepareForAdjustChannelsNonDestructive();
-    if (mAdjustNonDestructiveInChannelCount != mAdjustNonDestructiveOutChannelCount) {
         uint8_t* buffer = mKeepContractedChannels
                 ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame(
                         mMixerChannelCount, mMixerFormat)
-                : NULL;
-        mContractChannelsNonDestructiveBufferProvider.reset(
-                new AdjustChannelsBufferProvider(
-                        mFormat,
-                        mAdjustNonDestructiveInChannelCount,
-                        mAdjustNonDestructiveOutChannelCount,
-                        frames,
-                        mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID,
-                        buffer));
+                : nullptr;
+        mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider(
+                mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, frames,
+                mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID,
+                buffer, mMixerHapticChannelCount));
         reconfigureBufferProviders();
     }
     return NO_ERROR;
@@ -314,9 +281,9 @@
 
 void AudioMixer::Track::clearContractedBuffer()
 {
-    if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) {
+    if (mAdjustChannelsBufferProvider.get() != nullptr) {
         static_cast<AdjustChannelsBufferProvider*>(
-                mContractChannelsNonDestructiveBufferProvider.get())->clearContractedFrames();
+                mAdjustChannelsBufferProvider.get())->clearContractedFrames();
     }
 }
 
@@ -328,10 +295,6 @@
         mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider);
         bufferProvider = mAdjustChannelsBufferProvider.get();
     }
-    if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) {
-        mContractChannelsNonDestructiveBufferProvider->setBufferProvider(bufferProvider);
-        bufferProvider = mContractChannelsNonDestructiveBufferProvider.get();
-    }
     if (mReformatBufferProvider.get() != nullptr) {
         mReformatBufferProvider->setBufferProvider(bufferProvider);
         bufferProvider = mReformatBufferProvider.get();
@@ -377,7 +340,7 @@
                 track->mainBuffer = valueBuf;
                 ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
                 if (track->mKeepContractedChannels) {
-                    track->prepareForAdjustChannelsNonDestructive(mFrameCount);
+                    track->prepareForAdjustChannels(mFrameCount);
                 }
                 invalidate();
             }
@@ -405,7 +368,7 @@
                 track->mMixerFormat = format;
                 ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
                 if (track->mKeepContractedChannels) {
-                    track->prepareForAdjustChannelsNonDestructive(mFrameCount);
+                    track->prepareForAdjustChannels(mFrameCount);
                 }
             }
             } break;
@@ -424,8 +387,7 @@
             if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) {
                 track->mHapticPlaybackEnabled = hapticPlaybackEnabled;
                 track->mKeepContractedChannels = hapticPlaybackEnabled;
-                track->prepareForAdjustChannelsNonDestructive(mFrameCount);
-                track->prepareForAdjustChannels();
+                track->prepareForAdjustChannels(mFrameCount);
             }
             } break;
         case HAPTIC_INTENSITY: {
@@ -518,8 +480,6 @@
         track->mDownmixerBufferProvider->reset();
     } else if (track->mReformatBufferProvider.get() != nullptr) {
         track->mReformatBufferProvider->reset();
-    } else if (track->mContractChannelsNonDestructiveBufferProvider.get() != nullptr) {
-        track->mContractChannelsNonDestructiveBufferProvider->reset();
     } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) {
         track->mAdjustChannelsBufferProvider->reset();
     }
@@ -563,9 +523,7 @@
     t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
     t->mMixerHapticChannelCount = 0;
     t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
-    t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount;
-    t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount;
-    t->mAdjustNonDestructiveOutChannelCount = t->channelCount;
+    t->mAdjustOutChannelCount = t->channelCount;
     t->mKeepContractedChannels = false;
     // Check the downmixing (or upmixing) requirements.
     status_t status = t->prepareForDownmix();
@@ -576,8 +534,7 @@
     // prepareForDownmix() may change mDownmixRequiresFormat
     ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
     t->prepareForReformat();
-    t->prepareForAdjustChannelsNonDestructive(mFrameCount);
-    t->prepareForAdjustChannels();
+    t->prepareForAdjustChannels(mFrameCount);
     return OK;
 }
 
diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp
index 6d31c12..0114355 100644
--- a/media/libaudioprocessing/BufferProviders.cpp
+++ b/media/libaudioprocessing/BufferProviders.cpp
@@ -630,7 +630,8 @@
 
 AdjustChannelsBufferProvider::AdjustChannelsBufferProvider(
         audio_format_t format, size_t inChannelCount, size_t outChannelCount,
-        size_t frameCount, audio_format_t contractedFormat, void* contractedBuffer) :
+        size_t frameCount, audio_format_t contractedFormat, void* contractedBuffer,
+        size_t contractedOutChannelCount) :
         CopyBufferProvider(
                 audio_bytes_per_frame(inChannelCount, format),
                 audio_bytes_per_frame(std::max(inChannelCount, outChannelCount), format),
@@ -640,15 +641,22 @@
         mOutChannelCount(outChannelCount),
         mSampleSizeInBytes(audio_bytes_per_sample(format)),
         mFrameCount(frameCount),
-        mContractedChannelCount(inChannelCount - outChannelCount),
-        mContractedFormat(contractedFormat),
+        mContractedFormat(inChannelCount > outChannelCount
+                ? contractedFormat : AUDIO_FORMAT_INVALID),
+        mContractedInChannelCount(inChannelCount > outChannelCount
+                ? inChannelCount - outChannelCount : 0),
+        mContractedOutChannelCount(contractedOutChannelCount),
+        mContractedSampleSizeInBytes(audio_bytes_per_sample(contractedFormat)),
+        mContractedInputFrameSize(mContractedInChannelCount * mContractedSampleSizeInBytes),
         mContractedBuffer(contractedBuffer),
         mContractedWrittenFrames(0)
 {
-    ALOGV("AdjustChannelsBufferProvider(%p)(%#x, %zu, %zu, %zu, %#x, %p)", this, format,
-            inChannelCount, outChannelCount, frameCount, contractedFormat, contractedBuffer);
+    ALOGV("AdjustChannelsBufferProvider(%p)(%#x, %zu, %zu, %zu, %#x, %p, %zu)",
+          this, format, inChannelCount, outChannelCount, frameCount, contractedFormat,
+          contractedBuffer, contractedOutChannelCount);
     if (mContractedFormat != AUDIO_FORMAT_INVALID && mInChannelCount > mOutChannelCount) {
-        mContractedFrameSize = audio_bytes_per_frame(mContractedChannelCount, mContractedFormat);
+        mContractedOutputFrameSize =
+                audio_bytes_per_frame(mContractedOutChannelCount, mContractedFormat);
     }
 }
 
@@ -667,25 +675,39 @@
 
 void AdjustChannelsBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 {
-    if (mInChannelCount > mOutChannelCount) {
-        // For case multi to mono, adjust_channels has special logic that will mix first two input
-        // channels into a single output channel. In that case, use adjust_channels_non_destructive
-        // to keep only one channel data even when contracting to mono.
-        adjust_channels_non_destructive(src, mInChannelCount, dst, mOutChannelCount,
-                mSampleSizeInBytes, frames * mInChannelCount * mSampleSizeInBytes);
-        if (mContractedFormat != AUDIO_FORMAT_INVALID
-            && mContractedBuffer != nullptr) {
-            const size_t contractedIdx = frames * mOutChannelCount * mSampleSizeInBytes;
+    // For case multi to mono, adjust_channels has special logic that will mix first two input
+    // channels into a single output channel. In that case, use adjust_channels_non_destructive
+    // to keep only one channel data even when contracting to mono.
+    adjust_channels_non_destructive(src, mInChannelCount, dst, mOutChannelCount,
+            mSampleSizeInBytes, frames * mInChannelCount * mSampleSizeInBytes);
+    if (mContractedFormat != AUDIO_FORMAT_INVALID
+        && mContractedBuffer != nullptr) {
+        const size_t contractedIdx = frames * mOutChannelCount * mSampleSizeInBytes;
+        uint8_t* oriBuf = (uint8_t*) dst + contractedIdx;
+        uint8_t* buf = (uint8_t*) mContractedBuffer
+                + mContractedWrittenFrames * mContractedOutputFrameSize;
+        if (mContractedInChannelCount > mContractedOutChannelCount) {
+            // Adjust the channels first as the contracted buffer may not have enough
+            // space for the data.
+            // Use adjust_channels_non_destructive to avoid mix first two channels into one single
+            // output channel when it is multi to mono.
+            adjust_channels_non_destructive(
+                    oriBuf, mContractedInChannelCount, oriBuf, mContractedOutChannelCount,
+                    mSampleSizeInBytes, frames * mContractedInChannelCount * mSampleSizeInBytes);
             memcpy_by_audio_format(
-                    (uint8_t*) mContractedBuffer + mContractedWrittenFrames * mContractedFrameSize,
-                    mContractedFormat, (uint8_t*) dst + contractedIdx, mFormat,
-                    mContractedChannelCount * frames);
-            mContractedWrittenFrames += frames;
+                    buf, mContractedFormat, oriBuf, mFormat, mContractedOutChannelCount * frames);
+        } else {
+            // Copy the data first as the dst buffer may not have enough space for extra channel.
+            memcpy_by_audio_format(
+                buf, mContractedFormat, oriBuf, mFormat, mContractedInChannelCount * frames);
+            // Note that if the contracted data is from MONO to MULTICHANNEL, the first 2 channels
+            // will be duplicated with the original single input channel and all the other channels
+            // will be 0-filled.
+            adjust_channels(
+                    buf, mContractedInChannelCount, buf, mContractedOutChannelCount,
+                    mContractedSampleSizeInBytes, mContractedInputFrameSize * frames);
         }
-    } else {
-        // Prefer expanding data from the end of each audio frame.
-        adjust_channels(src, mInChannelCount, dst, mOutChannelCount,
-                mSampleSizeInBytes, frames * mInChannelCount * mSampleSizeInBytes);
+        mContractedWrittenFrames += frames;
     }
 }
 
diff --git a/media/libaudioprocessing/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
index 5a9fa07..2993a60 100644
--- a/media/libaudioprocessing/include/media/AudioMixer.h
+++ b/media/libaudioprocessing/include/media/AudioMixer.h
@@ -80,7 +80,6 @@
             mPostDownmixReformatBufferProvider.reset(nullptr);
             mDownmixerBufferProvider.reset(nullptr);
             mReformatBufferProvider.reset(nullptr);
-            mContractChannelsNonDestructiveBufferProvider.reset(nullptr);
             mAdjustChannelsBufferProvider.reset(nullptr);
         }
 
@@ -95,10 +94,8 @@
         void        unprepareForDownmix();
         status_t    prepareForReformat();
         void        unprepareForReformat();
-        status_t    prepareForAdjustChannels();
+        status_t    prepareForAdjustChannels(size_t frames);
         void        unprepareForAdjustChannels();
-        status_t    prepareForAdjustChannelsNonDestructive(size_t frames);
-        void        unprepareForAdjustChannelsNonDestructive();
         void        clearContractedBuffer();
         bool        setPlaybackRate(const AudioPlaybackRate &playbackRate);
         void        reconfigureBufferProviders();
@@ -114,24 +111,18 @@
          * 2) mAdjustChannelsBufferProvider: Expands or contracts sample data from one interleaved
          *    channel format to another. Expanded channels are filled with zeros and put at the end
          *    of each audio frame. Contracted channels are copied to the end of the buffer.
-         * 3) mContractChannelsNonDestructiveBufferProvider: Non-destructively contract sample data.
-         *    This is currently using at audio-haptic coupled playback to separate audio and haptic
-         *    data. Contracted channels could be written to given buffer.
-         * 4) mReformatBufferProvider: If not NULL, performs the audio reformat to
+         * 3) mReformatBufferProvider: If not NULL, performs the audio reformat to
          *    match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
          *    requires reformat. For example, it may convert floating point input to
          *    PCM_16_bit if that's required by the downmixer.
-         * 5) mDownmixerBufferProvider: If not NULL, performs the channel remixing to match
+         * 4) mDownmixerBufferProvider: If not NULL, performs the channel remixing to match
          *    the number of channels required by the mixer sink.
-         * 6) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
+         * 5) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
          *    the downmixer requirements to the mixer engine input requirements.
-         * 7) mTimestretchBufferProvider: Adds timestretching for playback rate
+         * 6) mTimestretchBufferProvider: Adds timestretching for playback rate
          */
         AudioBufferProvider* mInputBufferProvider;    // externally provided buffer provider.
-        // TODO: combine mAdjustChannelsBufferProvider and
-        // mContractChannelsNonDestructiveBufferProvider
         std::unique_ptr<PassthruBufferProvider> mAdjustChannelsBufferProvider;
-        std::unique_ptr<PassthruBufferProvider> mContractChannelsNonDestructiveBufferProvider;
         std::unique_ptr<PassthruBufferProvider> mReformatBufferProvider;
         std::unique_ptr<PassthruBufferProvider> mDownmixerBufferProvider;
         std::unique_ptr<PassthruBufferProvider> mPostDownmixReformatBufferProvider;
@@ -153,8 +144,6 @@
         uint32_t             mMixerHapticChannelCount;
         uint32_t             mAdjustInChannelCount;
         uint32_t             mAdjustOutChannelCount;
-        uint32_t             mAdjustNonDestructiveInChannelCount;
-        uint32_t             mAdjustNonDestructiveOutChannelCount;
         bool                 mKeepContractedChannels;
     };
 
diff --git a/media/libaudioprocessing/include/media/BufferProviders.h b/media/libaudioprocessing/include/media/BufferProviders.h
index b038854..525ab8c 100644
--- a/media/libaudioprocessing/include/media/BufferProviders.h
+++ b/media/libaudioprocessing/include/media/BufferProviders.h
@@ -223,17 +223,22 @@
 // Extra expanded channels are filled with zeros and put at the end of each audio frame.
 // Contracted channels are copied to the end of the output buffer(storage should be
 // allocated appropriately).
-// Contracted channels could be written to output buffer.
+// Contracted channels could be written to output buffer and got adjusted. When the contracted
+// channels are adjusted in the contracted buffer, the input channel count will be calculated
+// as `inChannelCount - outChannelCount`. The output channel count is provided by caller, which
+// is `contractedOutChannelCount`. Currently, adjusting contracted channels is used for audio
+// coupled haptic playback. If the device supports two haptic channels while apps only provide
+// single haptic channel, the second haptic channel will be duplicated with the first haptic
+// channel's data. If the device supports single haptic channels while apps provide two haptic
+// channels, the second channel will be contracted.
 class AdjustChannelsBufferProvider : public CopyBufferProvider {
 public:
-    AdjustChannelsBufferProvider(audio_format_t format, size_t inChannelCount,
-            size_t outChannelCount, size_t frameCount) : AdjustChannelsBufferProvider(
-                    format, inChannelCount, outChannelCount,
-                    frameCount, AUDIO_FORMAT_INVALID, nullptr) { }
     // Contracted data is converted to contractedFormat and put into contractedBuffer.
     AdjustChannelsBufferProvider(audio_format_t format, size_t inChannelCount,
-            size_t outChannelCount, size_t frameCount, audio_format_t contractedFormat,
-            void* contractedBuffer);
+            size_t outChannelCount, size_t frameCount,
+            audio_format_t contractedFormat = AUDIO_FORMAT_INVALID,
+            void* contractedBuffer = nullptr,
+            size_t contractedOutChannelCount = 0);
     //Overrides
     status_t getNextBuffer(Buffer* pBuffer) override;
     void copyFrames(void *dst, const void *src, size_t frames) override;
@@ -247,11 +252,14 @@
     const size_t         mOutChannelCount;
     const size_t         mSampleSizeInBytes;
     const size_t         mFrameCount;
-    const size_t         mContractedChannelCount;
     const audio_format_t mContractedFormat;
+    const size_t         mContractedInChannelCount;
+    const size_t         mContractedOutChannelCount;
+    const size_t         mContractedSampleSizeInBytes;
+    const size_t         mContractedInputFrameSize; // contracted input frame size
     void                *mContractedBuffer;
     size_t               mContractedWrittenFrames;
-    size_t               mContractedFrameSize;
+    size_t               mContractedOutputFrameSize; // contracted output frame size
 };
 // ----------------------------------------------------------------------------
 } // namespace android