Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19e533cc authored by Judy Hsiao's avatar Judy Hsiao Committed by Andy Hung
Browse files

AudioMixer: Preserve stereo volume in multi-channel mixing

CrOS exposes 4-channel output for ARC++ and mixes samples in CRAS
server to real channel format, but AudioMixer will discard right
channel volume when mixing a 2-channel track to 4-channel output.
This behavior will cause ARC++ fail on Audio Frequency Line Test
in CTS-V since the right channel can't be tested.

The code is also updated to work for 3, 5, 6, 7, 8 canonical output
channel position masks, as well as continue the mono volume
handling for output channel index masks.

Bug: 110551766
Test: Run CTS-V Audio Frequency Line Test
Test: atest mixerops_benchmark
Change-Id: I4c6ee86d30bb8296f0e32f9a17b1135e1313fd64
parent 61a941ad
Loading
Loading
Loading
Loading
+122 −35
Original line number Diff line number Diff line
@@ -643,8 +643,16 @@ void AudioMixerBase::process__validate()
            if (n & NEEDS_RESAMPLE) {
                all16BitsStereoNoResample = false;
                resampling = true;
                t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
                if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2
                    && t->useStereoVolume()) {
                    t->hook = TrackBase::getTrackHook(
                            TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                } else {
                    t->hook = TrackBase::getTrackHook(
                            TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                }
                ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                        "Track %d needs downmix + resample", name);
            } else {
@@ -658,8 +666,11 @@ void AudioMixerBase::process__validate()
                    all16BitsStereoNoResample = false;
                }
                if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
                    t->hook = TrackBase::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                    t->hook = TrackBase::getTrackHook(
                            t->useStereoVolume() ? TRACKTYPE_NORESAMPLESTEREO
                                    : TRACKTYPE_NORESAMPLE,
                            t->mMixerChannelCount, t->mMixerInFormat,
                            t->mMixerFormat);
                    ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                            "Track %d needs downmix", name);
                }
@@ -691,7 +702,8 @@ void AudioMixerBase::process__validate()
                        // special case handling due to implicit channel duplication.
                        // Stereo or Multichannel should actually be fine here.
                        mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
                                t->useStereoVolume());
                    }
                }
            }
@@ -726,7 +738,8 @@ void AudioMixerBase::process__validate()
                const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
                // Muted single tracks handled by allMuted above.
                mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
                        t->useStereoVolume());
            }
        }
    }
@@ -1450,7 +1463,7 @@ void AudioMixerBase::process__noResampleOneTrack()
        }

        const size_t outFrames = b.frameCount;
        t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
        t->volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, false /* ADJUSTVOL */> (
                out, outFrames, in, aux, ramp);

        out += outFrames * channels;
@@ -1463,7 +1476,7 @@ void AudioMixerBase::process__noResampleOneTrack()
        t->bufferProvider->releaseBuffer(&b);
    }
    if (ramp) {
        t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
        t->adjustVolumeRamp(aux != NULL, std::is_same_v<TI, float>);
    }
}

@@ -1489,7 +1502,7 @@ void AudioMixerBase::TrackBase::track__Resample(TO* out, size_t outFrameCount, T
        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
        mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);

        volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
        volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
                out, outFrameCount, temp, aux, ramp);

    } else { // constant volume gain
@@ -1513,7 +1526,7 @@ void AudioMixerBase::TrackBase::track__NoResample(
    ALOGVV("track__NoResample\n");
    const TI *in = static_cast<const TI *>(mIn);

    volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
    volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
            out, frameCount, in, aux, needsRamp());

    // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
@@ -1601,6 +1614,21 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
            break;
        }
        break;
    case TRACKTYPE_RESAMPLESTEREO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
                    TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
                    TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    case TRACKTYPE_NORESAMPLEMONO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
@@ -1627,6 +1655,21 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
            break;
        }
        break;
    case TRACKTYPE_NORESAMPLESTEREO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
                    TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
                    TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    default:
        LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
        break;
@@ -1644,7 +1687,8 @@ AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, ui
/* static */
AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
        int processType, uint32_t channelCount,
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
        bool stereoVolume)
{
    if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
        LOG_ALWAYS_FATAL("bad processType: %d", processType);
@@ -1654,15 +1698,19 @@ AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
        return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
    }
    LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);

    if (stereoVolume) { // templated arguments require explicit values.
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            switch (mixerOutFormat) {
            case AUDIO_FORMAT_PCM_FLOAT:
                return &AudioMixerBase::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
                        float /*TI*/, TYPE_AUX>;
            case AUDIO_FORMAT_PCM_16_BIT:
                return &AudioMixerBase::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
                        float /*TI*/, TYPE_AUX>;
            default:
                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                break;
@@ -1672,10 +1720,12 @@ AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
            switch (mixerOutFormat) {
            case AUDIO_FORMAT_PCM_FLOAT:
                return &AudioMixerBase::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
                        int16_t /*TI*/, TYPE_AUX>;
            case AUDIO_FORMAT_PCM_16_BIT:
                return &AudioMixerBase::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
                        int16_t /*TI*/, TYPE_AUX>;
            default:
                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                break;
@@ -1685,6 +1735,43 @@ AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
    } else {
          switch (mixerInFormat) {
          case AUDIO_FORMAT_PCM_FLOAT:
              switch (mixerOutFormat) {
              case AUDIO_FORMAT_PCM_FLOAT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
                          float /*TI*/, TYPE_AUX>;
              case AUDIO_FORMAT_PCM_16_BIT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
                          float /*TI*/, TYPE_AUX>;
              default:
                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                  break;
              }
              break;
          case AUDIO_FORMAT_PCM_16_BIT:
              switch (mixerOutFormat) {
              case AUDIO_FORMAT_PCM_FLOAT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
                          int16_t /*TI*/, TYPE_AUX>;
              case AUDIO_FORMAT_PCM_16_BIT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
                          int16_t /*TI*/, TYPE_AUX>;
              default:
                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                  break;
              }
              break;
          default:
              LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
              break;
          }
    }
    return NULL;
}

+125 −72
Original line number Diff line number Diff line
@@ -19,21 +19,10 @@

namespace android {

/* Behavior of is_same<>::value is true if the types are identical,
 * false otherwise. Identical to the STL std::is_same.
 */
template<typename T, typename U>
struct is_same
{
    static const bool value = false;
};

template<typename T>
struct is_same<T, T>  // partial specialization
{
    static const bool value = true;
};

// Hack to make static_assert work in a constexpr
// https://en.cppreference.com/w/cpp/language/if
template <int N>
inline constexpr bool dependent_false = false;

/* MixMul is a multiplication operator to scale an audio input signal
 * by a volume gain, with the formula:
@@ -179,7 +168,7 @@ inline int16_t MixMul<int16_t, float, float>(float value, float volume) {

template <typename TO, typename TI>
inline void MixAccum(TO *auxaccum, TI value) {
    if (!is_same<TO, TI>::value) {
    if (!std::is_same_v<TO, TI>) {
        LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n",
                sizeof(TO), sizeof(TI));
    }
@@ -228,7 +217,67 @@ enum {
    MIXTYPE_MULTI_SAVEONLY,
    MIXTYPE_MULTI_MONOVOL,
    MIXTYPE_MULTI_SAVEONLY_MONOVOL,
    MIXTYPE_MULTI_STEREOVOL,
    MIXTYPE_MULTI_SAVEONLY_STEREOVOL,
};

/*
 * TODO: We should work on non-interleaved streams - the
 * complexity of working on interleaved streams is now getting
 * too high, and likely limits compiler optimization.
 */
template <int MIXTYPE, int NCHAN,
        typename TO, typename TI, typename TV,
        typename F>
void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
    static_assert(NCHAN > 0 && NCHAN <= 8);
    static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
            || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL);
    auto proc = [](auto& a, const auto& b) {
        if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
            a += b;
        } else {
            a = b;
        }
    };
    // HALs should only expose the canonical channel masks.
    proc(*out++, f(*in++, vol[0])); // front left
    if constexpr (NCHAN == 1) return;
    proc(*out++, f(*in++, vol[1])); // front right
    if constexpr (NCHAN == 2)  return;
    if constexpr (NCHAN == 4) {
        proc(*out++, f(*in++, vol[0])); // back left
        proc(*out++, f(*in++, vol[1])); // back right
        return;
    }

    // TODO: Precompute center volume if not ramping.
    std::decay_t<TV> center;
    if constexpr (std::is_floating_point_v<TV>) {
        center = (vol[0] + vol[1]) * 0.5;       // do not use divide
    } else {
        center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
    }
    proc(*out++, f(*in++, center)); // center (or 2.1 LFE)
    if constexpr (NCHAN == 3) return;
    if constexpr (NCHAN == 5) {
        proc(*out++, f(*in++, vol[0]));  // back left
        proc(*out++, f(*in++, vol[1]));  // back right
        return;
    }

    proc(*out++, f(*in++, center)); // lfe
    proc(*out++, f(*in++, vol[0])); // back left
    proc(*out++, f(*in++, vol[1])); // back right
    if constexpr (NCHAN == 6) return;
    if constexpr (NCHAN == 7) {
        proc(*out++, f(*in++, center)); // back center
        return;
    }
    // NCHAN == 8
    proc(*out++, f(*in++, vol[0])); // side left
    proc(*out++, f(*in++, vol[1])); // side right
}

/*
 * The volumeRampMulti and volumeRamp functions take a MIXTYPE
@@ -271,6 +320,12 @@ enum {
 * MIXTYPE_MULTI_SAVEONLY_MONOVOL:
 *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0].
 *
 * MIXTYPE_MULTI_STEREOVOL:
 *   Same as MIXTYPE_MULTI, but uses only volume[0] and volume[1].
 *
 * MIXTYPE_MULTI_SAVEONLY_STEREOVOL:
 *   Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0] and volume[1].
 *
 */

template <int MIXTYPE, int NCHAN,
@@ -284,41 +339,42 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
    if (aux != NULL) {
        do {
            TA auxaccum = 0;
            switch (MIXTYPE) {
            case MIXTYPE_MULTI:
            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                    vol[i] += volinc[i];
                }
                break;
            case MIXTYPE_MONOEXPAND:
            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
                    vol[i] += volinc[i];
                }
                in++;
                break;
            case MIXTYPE_MULTI_SAVEONLY:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                    vol[i] += volinc[i];
                }
                break;
            case MIXTYPE_MULTI_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                }
                vol[0] += volinc[0];
                break;
            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                }
                vol[0] += volinc[0];
                break;
            default:
                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
                break;
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
                stereoVolumeHelper<MIXTYPE, NCHAN>(
                        out, in, vol, [&auxaccum] (auto &a, const auto &b) {
                    return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
                });
                vol[0] += volinc[0];
                vol[1] += volinc[1];
            } else /* constexpr */ {
                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
            }
            auxaccum /= NCHAN;
            *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola);
@@ -326,41 +382,41 @@ inline void volumeRampMulti(TO* out, size_t frameCount,
        } while (--frameCount);
    } else {
        do {
            switch (MIXTYPE) {
            case MIXTYPE_MULTI:
            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
                    vol[i] += volinc[i];
                }
                break;
            case MIXTYPE_MONOEXPAND:
            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
                    vol[i] += volinc[i];
                }
                in++;
                break;
            case MIXTYPE_MULTI_SAVEONLY:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
                    vol[i] += volinc[i];
                }
                break;
            case MIXTYPE_MULTI_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
                }
                vol[0] += volinc[0];
                break;
            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
                }
                vol[0] += volinc[0];
                break;
            default:
                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
                break;
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
                stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
                    return MixMul<TO, TI, TV>(a, b);
                });
                vol[0] += volinc[0];
                vol[1] += volinc[1];
            } else /* constexpr */ {
                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
            }
        } while (--frameCount);
    }
@@ -377,72 +433,69 @@ inline void volumeMulti(TO* out, size_t frameCount,
    if (aux != NULL) {
        do {
            TA auxaccum = 0;
            switch (MIXTYPE) {
            case MIXTYPE_MULTI:
            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                }
                break;
            case MIXTYPE_MONOEXPAND:
            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum);
                }
                in++;
                break;
            case MIXTYPE_MULTI_SAVEONLY:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum);
                }
                break;
            case MIXTYPE_MULTI_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                }
                break;
            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum);
                }
                break;
            default:
                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
                break;
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
                stereoVolumeHelper<MIXTYPE, NCHAN>(
                        out, in, vol, [&auxaccum] (auto &a, const auto &b) {
                    return MixMulAux<TO, TI, TV, TA>(a, b, &auxaccum);
                });
            } else /* constexpr */ {
                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
            }
            auxaccum /= NCHAN;
            *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola);
        } while (--frameCount);
    } else {
        do {
            switch (MIXTYPE) {
            case MIXTYPE_MULTI:
            if constexpr (MIXTYPE == MIXTYPE_MULTI) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in++, vol[i]);
                }
                break;
            case MIXTYPE_MONOEXPAND:
            } else if constexpr (MIXTYPE == MIXTYPE_MONOEXPAND) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in, vol[i]);
                }
                in++;
                break;
            case MIXTYPE_MULTI_SAVEONLY:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMul<TO, TI, TV>(*in++, vol[i]);
                }
                break;
            case MIXTYPE_MULTI_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ += MixMul<TO, TI, TV>(*in++, vol[0]);
                }
                break;
            case MIXTYPE_MULTI_SAVEONLY_MONOVOL:
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_SAVEONLY_MONOVOL) {
                for (int i = 0; i < NCHAN; ++i) {
                    *out++ = MixMul<TO, TI, TV>(*in++, vol[0]);
                }
                break;
            default:
                LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE);
                break;
            } else if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
                    || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL) {
                stereoVolumeHelper<MIXTYPE, NCHAN>(out, in, vol, [] (auto &a, const auto &b) {
                    return MixMul<TO, TI, TV>(a, b);
                });
            } else /* constexpr */ {
                static_assert(dependent_false<MIXTYPE>, "invalid mixtype");
            }
        } while (--frameCount);
    }
+12 −1
Original line number Diff line number Diff line
@@ -188,13 +188,20 @@ public:
    enum {
        TRACKTYPE_NOP,
        TRACKTYPE_RESAMPLE,
        TRACKTYPE_RESAMPLESTEREO,
        TRACKTYPE_NORESAMPLE,
        TRACKTYPE_NORESAMPLEMONO,
        TRACKTYPE_NORESAMPLESTEREO,
    };

    // process hook functionality
    using process_hook_t = void(AudioMixerBase::*)();

    static bool isAudioChannelPositionMask(audio_channel_mask_t channelMask) {
        return audio_channel_mask_get_representation(channelMask)
                == AUDIO_CHANNEL_REPRESENTATION_POSITION;
    }

    struct TrackBase;
    using hook_t = void(TrackBase::*)(
            int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
@@ -219,6 +226,9 @@ public:
        size_t      getUnreleasedFrames() const { return mResampler.get() != nullptr ?
                                                    mResampler->getUnreleasedFrames() : 0; };

        bool        useStereoVolume() const { return channelMask == AUDIO_CHANNEL_OUT_STEREO
                                        && isAudioChannelPositionMask(mMixerChannelMask); }

        static hook_t getTrackHook(int trackType, uint32_t channelCount,
                audio_format_t mixerInFormat, audio_format_t mixerOutFormat);

@@ -327,7 +337,8 @@ public:
    void process__noResampleOneTrack();

    static process_hook_t getProcessHook(int processType, uint32_t channelCount,
            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
            audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
            bool useStereoVolume);

    static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
            void *in, audio_format_t mixerInFormat, size_t sampleCount);
+23 −0
Original line number Diff line number Diff line
@@ -55,3 +55,26 @@ cc_binary {
    srcs: ["test-resampler.cpp"],
    static_libs: ["libsndfile"],
}

//
// build mixerops objdump
//
// This is used to verify proper optimization of the code.
//
// For example, use:
// ./prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-objdump
//      -d --source ./out/target/product/crosshatch/symbols/system/bin/mixerops_objdump
//
cc_binary {
    name: "mixerops_objdump",
    srcs: ["mixerops_objdump.cpp"],
}

//
// build mixerops benchmark
//
cc_benchmark {
    name: "mixerops_benchmark",
    srcs: ["mixerops_benchmark.cpp"],
    static_libs: ["libgoogle-benchmark"],
}
+102 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading