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

Commit ef25b6b9 authored by Andy Hung's avatar Andy Hung
Browse files

Downmix: Clean up the generic downmix

Speed improvement 10-40%, primarily from
improved clamping.

Test: atest downmix_benchmark
Test: atest downmix_tests
Bug: 187062102
Change-Id: I147a476cca8bb6313c416a914c649a944a92ed3a
parent a11b17ff
Loading
Loading
Loading
Loading
+96 −128
Original line number Diff line number Diff line
@@ -28,18 +28,6 @@

#define MINUS_3_DB_IN_FLOAT M_SQRT1_2 // -3dB = 0.70710678

const uint32_t kSides = AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT;
const uint32_t kBacks = AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT;
const uint32_t kUnsupported =
        AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
        AUDIO_CHANNEL_OUT_TOP_CENTER |
        AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT |
        AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER |
        AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT |
        AUDIO_CHANNEL_OUT_TOP_BACK_LEFT |
        AUDIO_CHANNEL_OUT_TOP_BACK_CENTER |
        AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT;

typedef enum {
    DOWNMIX_STATE_UNINITIALIZED,
    DOWNMIX_STATE_INITIALIZED,
@@ -135,16 +123,8 @@ static const effect_descriptor_t * const gDescriptors[] = {
// number of effects in this library
const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);

static float clamp_float(float a) {
    if (a > 1.0f) {
        return 1.0f;
    }
    else if (a < -1.0f) {
        return -1.0f;
    }
    else {
        return a;
    }
static inline float clamp_float(float value) {
    return fmin(fmax(value, -1.f), 1.f);
}

/*----------------------------------------------------------------------------
@@ -213,29 +193,10 @@ static bool Downmix_validChannelMask(uint32_t mask)
        return false;
    }
    // check against unsupported channels
    if (mask & kUnsupported) {
        ALOGE("Unsupported channels (top or front left/right of center)");
        return false;
    }
    // verify has FL/FR
    if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
        ALOGE("Front channels must be present");
        return false;
    }
    // verify uses SIDE as a pair (ok if not using SIDE at all)
    if ((mask & kSides) != 0) {
        if ((mask & kSides) != kSides) {
            ALOGE("Side channels must be used as a pair");
    if (mask & ~AUDIO_CHANNEL_OUT_22POINT2) {
        ALOGE("Unsupported channels in %u", mask & ~AUDIO_CHANNEL_OUT_22POINT2);
        return false;
    }
    }
    // verify uses BACK as a pair (ok if not using BACK at all)
    if ((mask & kBacks) != 0) {
        if ((mask & kBacks) != kBacks) {
            ALOGE("Back channels must be used as a pair");
            return false;
        }
    }
    return true;
}

@@ -1044,13 +1005,7 @@ void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool ac
 * Downmix_foldGeneric()
 *----------------------------------------------------------------------------
 * Purpose:
 * downmix to stereo a multichannel signal whose format is:
 *  - has FL/FR
 *  - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
 *  - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
 *  - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
 *  - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
 * Only handles channel masks not enumerated in downmix_input_channel_mask_t
 * downmix to stereo a multichannel signal of arbitrary channel position mask.
 *
 * Inputs:
 *  mask       the channel mask of pSrc
@@ -1072,88 +1027,101 @@ bool Downmix_foldGeneric(
    if (!Downmix_validChannelMask(mask)) {
        return false;
    }

    const bool hasSides = (mask & kSides) != 0;
    const bool hasBacks = (mask & kBacks) != 0;

    const int numChan = audio_channel_count_from_out_mask(mask);
    const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
    const bool hasLFE =
            ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
    const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);

    // compute at what index each channel is: samples will be in the following order:
    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
    // when a channel is not present, its index is set to the same as the index of the preceding
    // channel
    const int indexFC  = hasFC    ? 2            : 1;        // front center
    const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
    const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
    const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
    const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
    const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
    const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
    //
    //  (transfer matrix)
    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
    //   0.5     0.353 0.353 0.5     0.353 0.5
    //       0.5 0.353 0.353     0.5 0.353     0.5

    // derive the indices for the transfer matrix columns that have non-zero values.
    int indexFL = -1;
    int indexFR = -1;
    int indexFC = -1;
    int indexLFE = -1;
    int indexBL = -1;
    int indexBR = -1;
    int indexBC = -1;
    int indexSL = -1;
    int indexSR = -1;
    int index = 0;
    for (unsigned tmp = mask;
         (tmp & (AUDIO_CHANNEL_OUT_7POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER)) != 0;
         ++index) {
        const unsigned lowestBit = tmp & -(signed)tmp;
        switch (lowestBit) {
        case AUDIO_CHANNEL_OUT_FRONT_LEFT:
            indexFL = index;
            break;
        case AUDIO_CHANNEL_OUT_FRONT_RIGHT:
            indexFR = index;
            break;
        case AUDIO_CHANNEL_OUT_FRONT_CENTER:
            indexFC = index;
            break;
        case AUDIO_CHANNEL_OUT_LOW_FREQUENCY:
            indexLFE = index;
            break;
        case AUDIO_CHANNEL_OUT_BACK_LEFT:
            indexBL = index;
            break;
        case AUDIO_CHANNEL_OUT_BACK_RIGHT:
            indexBR = index;
            break;
        case AUDIO_CHANNEL_OUT_BACK_CENTER:
            indexBC = index;
            break;
        case AUDIO_CHANNEL_OUT_SIDE_LEFT:
            indexSL = index;
            break;
        case AUDIO_CHANNEL_OUT_SIDE_RIGHT:
            indexSR = index;
            break;
        }
        tmp ^= lowestBit;
    }

    float lt, rt, centersLfeContrib;
    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
    // for every sample
    if (accumulate) {
    // With good branch prediction, this should run reasonably fast.
    // Also consider using a transfer matrix form.
    while (numFrames) {
        // compute contribution of FC, BC and LFE
            centersLfeContrib = 0;
            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
        float centersLfeContrib = 0;
        if (indexFC >= 0) centersLfeContrib = pSrc[indexFC];
        if (indexLFE >= 0) centersLfeContrib += pSrc[indexLFE];
        if (indexBC >= 0) centersLfeContrib += pSrc[indexBC];
        centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
            // always has FL/FR
            lt = pSrc[0];
            rt = pSrc[1];
            // mix in sides and backs
            if (hasSides) {
                lt += pSrc[indexSL];
                rt += pSrc[indexSR];
            }
            if (hasBacks) {
                lt += pSrc[indexBL];
                rt += pSrc[indexBR];
            }
            lt += centersLfeContrib;
            rt += centersLfeContrib;
            // accumulate in destination
            pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
            pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
            pSrc += numChan;
            pDst += 2;
            numFrames--;

        float ch[2];
        ch[0] = centersLfeContrib;
        ch[1] = centersLfeContrib;

        // mix in left / right channels
        if (indexFL >= 0) ch[0] += pSrc[indexFL];
        if (indexFR >= 0) ch[1] += pSrc[indexFR];

        if (indexSL >= 0) ch[0] += pSrc[indexSL];
        if (indexSR >= 0) ch[1] += pSrc[indexSR]; // note pair checks enforce this if indexSL != 0

        if (indexBL >= 0) ch[0] += pSrc[indexBL];
        if (indexBR >= 0) ch[1] += pSrc[indexBR]; // note pair checks enforce this if indexBL != 0

        // scale to prevent overflow.
        ch[0] *= 0.5f;
        ch[1] *= 0.5f;

        if (accumulate) {
            ch[0] += pDst[0];
            ch[1] += pDst[1];
        }
    } else {
        while (numFrames) {
            // compute contribution of FC, BC and LFE
            centersLfeContrib = 0;
            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
            centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
            // always has FL/FR
            lt = pSrc[0];
            rt = pSrc[1];
            // mix in sides and backs
            if (hasSides) {
                lt += pSrc[indexSL];
                rt += pSrc[indexSR];
            }
            if (hasBacks) {
                lt += pSrc[indexBL];
                rt += pSrc[indexBR];
            }
            lt += centersLfeContrib;
            rt += centersLfeContrib;
            // store in destination
            pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
            pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above

        pDst[0] = clamp_float(ch[0]);
        pDst[1] = clamp_float(ch[1]);
        pSrc += numChan;
        pDst += 2;
        numFrames--;
    }
    }
    return true;
}
+25 −25
Original line number Diff line number Diff line
@@ -64,32 +64,32 @@ static constexpr size_t kFrameCount = 1000;
/*
Pixel 3XL
downmix_benchmark:
  #BM_Downmix/0     5211 ns    5194 ns       135028
  #BM_Downmix/1     5611 ns    5593 ns       126034
  #BM_Downmix/2     2151 ns    2145 ns       325651
  #BM_Downmix/3     2263 ns    2256 ns       325645
  #BM_Downmix/4     2146 ns    2139 ns       310723
  #BM_Downmix/5     6236 ns    6215 ns       112742
  #BM_Downmix/6     6415 ns    6394 ns       109240
  #BM_Downmix/7     6806 ns    6783 ns       102283
  #BM_Downmix/8     6802 ns    6780 ns       103021
  #BM_Downmix/9     6841 ns    6818 ns       102421
  #BM_Downmix/10    7798 ns    7772 ns        90136
  #BM_Downmix/11    8591 ns    8562 ns        81242
  #BM_Downmix/0     4719 ns    4704 ns       148890
  #BM_Downmix/1     5050 ns    5034 ns       139104
  #BM_Downmix/2     1506 ns    1501 ns       466795
  #BM_Downmix/3     1554 ns    1549 ns       444498
  #BM_Downmix/4     1514 ns    1510 ns       463697
  #BM_Downmix/5     4442 ns    4428 ns       158016
  #BM_Downmix/6     4404 ns    4378 ns       159858
  #BM_Downmix/7     4851 ns    4835 ns       144681
  #BM_Downmix/8     4848 ns    4832 ns       144560
  #BM_Downmix/9     4859 ns    4844 ns       144496
  #BM_Downmix/10    5806 ns    5788 ns       120751
  #BM_Downmix/11    5051 ns    5036 ns       138920
--
downmix_benchmark: (generic fold for all channel masks)
  #BM_Downmix/0     5205 ns    5188 ns       134594
  #BM_Downmix/1     5604 ns    5586 ns       124443
  #BM_Downmix/2     5564 ns    5546 ns       126144
  #BM_Downmix/3     5736 ns    5718 ns       126185
  #BM_Downmix/4     5721 ns    5705 ns       121404
  #BM_Downmix/5     6264 ns    6243 ns       112684
  #BM_Downmix/6     6417 ns    6395 ns       109391
  #BM_Downmix/7     6739 ns    6718 ns       103811
  #BM_Downmix/8     6762 ns    6740 ns       103860
  #BM_Downmix/9     6769 ns    6747 ns       103680
  #BM_Downmix/10    7806 ns    7779 ns        90045
  #BM_Downmix/11    7939 ns    7911 ns        88370
downmix_benchmark: (generic fold)
  #BM_Downmix/0     4723 ns    4708 ns       148605
  #BM_Downmix/1     5081 ns    5065 ns       137920
  #BM_Downmix/2     4472 ns    4458 ns       160047
  #BM_Downmix/3     4359 ns    4345 ns       158744
  #BM_Downmix/4     4722 ns    4706 ns       149648
  #BM_Downmix/5     4426 ns    4412 ns       158618
  #BM_Downmix/6     4377 ns    4363 ns       160217
  #BM_Downmix/7     5262 ns    5245 ns       133155
  #BM_Downmix/8     5265 ns    5248 ns       132817
  #BM_Downmix/9     5246 ns    5229 ns       133932
  #BM_Downmix/10    5819 ns    5801 ns       120295
  #BM_Downmix/11    6030 ns    6011 ns       116619
*/

static void BM_Downmix(benchmark::State& state) {