Loading media/libeffects/downmix/EffectDownmix.c +269 −11 Original line number Diff line number Diff line Loading @@ -22,6 +22,11 @@ #include <stdbool.h> #include "EffectDownmix.h" // Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing //#define DOWNMIX_TEST_CHANNEL_INDEX 0 // Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing //#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0 #define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 typedef enum { Loading Loading @@ -87,6 +92,67 @@ static const effect_descriptor_t * const gDescriptors[] = { const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); /*---------------------------------------------------------------------------- * Test code *--------------------------------------------------------------------------*/ #ifdef DOWNMIX_TEST_CHANNEL_INDEX // strictly for testing, logs the indices of the channels for a given mask, // uses the same code as Downmix_foldGeneric() void Downmix_testIndexComputation(uint32_t mask) { ALOGI("Testing index computation for 0x%x:", mask); // check against unsupported channels if (mask & kUnsupported) { ALOGE("Unsupported channels (top or front left/right of center)"); return; } // verify has FL/FR if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) { ALOGE("Front channels must be present"); return; } // verify uses SIDE as a pair (ok if not using SIDE at all) bool hasSides = false; if ((mask & kSides) != 0) { if ((mask & kSides) != kSides) { ALOGE("Side channels must be used as a pair"); return; } hasSides = true; } // verify uses BACK as a pair (ok if not using BACK at all) bool hasBacks = false; if ((mask & kBacks) != 0) { if ((mask & kBacks) != kBacks) { ALOGE("Back channels must be used as a pair"); return; } hasBacks = true; } const int numChan = popcount(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 ALOGI(" FL FR FC LFE BL BR BC SL SR"); ALOGI(" %d %d %d %d %d %d %d %d %d", 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR); } #endif /*---------------------------------------------------------------------------- * Effect API implementation *--------------------------------------------------------------------------*/ Loading Loading @@ -123,6 +189,26 @@ int32_t DownmixLib_Create(const effect_uuid_t *uuid, ALOGV("DownmixLib_Create()"); #ifdef DOWNMIX_TEST_CHANNEL_INDEX // should work (won't log an error) ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:"); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER); Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK); Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER); Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER); // shouldn't work (will log an error, won't display channel indices) ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:"); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT); #endif if (pHandle == NULL || uuid == NULL) { return -EINVAL; } Loading Loading @@ -232,6 +318,7 @@ static int Downmix_Process(effect_handle_t self, const bool accumulate = (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels; switch(pDownmixer->type) { Loading @@ -256,8 +343,17 @@ static int Downmix_Process(effect_handle_t self, break; case DOWNMIX_TYPE_FOLD: #ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER // bypass the optimized downmix routines for the common formats if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); return -EINVAL; } break; #endif // optimize for the common formats switch((downmix_input_channel_mask_t)pDwmModule->config.inputCfg.channels) { switch((downmix_input_channel_mask_t)downmixInputChannelMask) { case CHANNEL_MASK_QUAD_BACK: case CHANNEL_MASK_QUAD_SIDE: Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate); Loading @@ -273,8 +369,11 @@ static int Downmix_Process(effect_handle_t self, Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate); break; default: // FIXME implement generic downmix ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported"); if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); return -EINVAL; } break; } break; Loading Loading @@ -707,6 +806,8 @@ int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pS * Inputs: * pSrc quad audio samples to downmix * numFrames the number of quad frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading Loading @@ -751,6 +852,8 @@ void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool ac * Inputs: * pSrc surround signal to downmix * numFrames the number of surround frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -763,6 +866,8 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo // sample at index 1 is FR // sample at index 2 is FC // sample at index 3 is RC // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusRearContrib = FC(-3dB) + RC(-3dB) Loading @@ -771,6 +876,7 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo lt = (pSrc[0] << 12) + centerPlusRearContrib; // FR + centerPlusRearContrib rt = (pSrc[1] << 12) + centerPlusRearContrib; // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 4; Loading @@ -785,8 +891,9 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo lt = (pSrc[0] << 12) + centerPlusRearContrib; // FR + centerPlusRearContrib rt = (pSrc[1] << 12) + centerPlusRearContrib; pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 4; pDst += 2; numFrames--; Loading @@ -804,6 +911,8 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo * Inputs: * pSrc 5.1 audio samples to downmix * numFrames the number of 5.1 frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -818,6 +927,8 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool // sample at index 3 is LFE // sample at index 4 is RL // sample at index 5 is RR // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) Loading @@ -827,6 +938,7 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); // FR + centerPlusLfeContrib + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 6; Loading @@ -842,8 +954,9 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); // FR + centerPlusLfeContrib + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 6; pDst += 2; numFrames--; Loading @@ -861,6 +974,8 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool * Inputs: * pSrc 7.1 audio samples to downmix * numFrames the number of 7.1 frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -877,6 +992,8 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool // sample at index 5 is RR // sample at index 6 is SL // sample at index 7 is SR // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) Loading @@ -886,8 +1003,9 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); // FR + centerPlusLfeContrib + SR + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); //accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 8; pDst += 2; numFrames--; Loading @@ -901,8 +1019,9 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); // FR + centerPlusLfeContrib + SR + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 8; pDst += 2; numFrames--; Loading @@ -910,3 +1029,142 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool } } /*---------------------------------------------------------------------------- * 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 * * Inputs: * mask the channel mask of pSrc * pSrc multichannel audio buffer to downmix * numFrames the number of multichannel frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples * * Returns: false if multichannel format is not supported * *---------------------------------------------------------------------------- */ bool Downmix_foldGeneric( uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { // 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) bool hasSides = false; if ((mask & kSides) != 0) { if ((mask & kSides) != kSides) { ALOGE("Side channels must be used as a pair"); return false; } hasSides = true; } // verify uses BACK as a pair (ok if not using BACK at all) bool hasBacks = false; if ((mask & kBacks) != 0) { if ((mask & kBacks) != kBacks) { ALOGE("Back channels must be used as a pair"); return false; } hasBacks = true; } const int numChan = popcount(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 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { 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_Q19_12; // always has FL/FR lt = (pSrc[0] << 12); rt = (pSrc[1] << 12); // mix in sides and backs if (hasSides) { lt += pSrc[indexSL] << 12; rt += pSrc[indexSR] << 12; } if (hasBacks) { lt += pSrc[indexBL] << 12; rt += pSrc[indexBR] << 12; } lt += centersLfeContrib; rt += centersLfeContrib; // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += numChan; pDst += 2; numFrames--; } } 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_Q19_12; // always has FL/FR lt = (pSrc[0] << 12); rt = (pSrc[1] << 12); // mix in sides and backs if (hasSides) { lt += pSrc[indexSL] << 12; rt += pSrc[indexSR] << 12; } if (hasBacks) { lt += pSrc[indexBL] << 12; rt += pSrc[indexBR] << 12; } lt += centersLfeContrib; rt += centersLfeContrib; // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += numChan; pDst += 2; numFrames--; } } return true; } media/libeffects/downmix/EffectDownmix.h +13 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,17 @@ typedef struct downmix_module_s { downmix_object_t context; } downmix_module_t; 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; /*------------------------------------ * Effect API Loading Loading @@ -92,5 +103,7 @@ void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool ac void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); bool Downmix_foldGeneric( uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); #endif /*ANDROID_EFFECTDOWNMIX_H_*/ Loading
media/libeffects/downmix/EffectDownmix.c +269 −11 Original line number Diff line number Diff line Loading @@ -22,6 +22,11 @@ #include <stdbool.h> #include "EffectDownmix.h" // Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing //#define DOWNMIX_TEST_CHANNEL_INDEX 0 // Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing //#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0 #define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 typedef enum { Loading Loading @@ -87,6 +92,67 @@ static const effect_descriptor_t * const gDescriptors[] = { const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); /*---------------------------------------------------------------------------- * Test code *--------------------------------------------------------------------------*/ #ifdef DOWNMIX_TEST_CHANNEL_INDEX // strictly for testing, logs the indices of the channels for a given mask, // uses the same code as Downmix_foldGeneric() void Downmix_testIndexComputation(uint32_t mask) { ALOGI("Testing index computation for 0x%x:", mask); // check against unsupported channels if (mask & kUnsupported) { ALOGE("Unsupported channels (top or front left/right of center)"); return; } // verify has FL/FR if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) { ALOGE("Front channels must be present"); return; } // verify uses SIDE as a pair (ok if not using SIDE at all) bool hasSides = false; if ((mask & kSides) != 0) { if ((mask & kSides) != kSides) { ALOGE("Side channels must be used as a pair"); return; } hasSides = true; } // verify uses BACK as a pair (ok if not using BACK at all) bool hasBacks = false; if ((mask & kBacks) != 0) { if ((mask & kBacks) != kBacks) { ALOGE("Back channels must be used as a pair"); return; } hasBacks = true; } const int numChan = popcount(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 ALOGI(" FL FR FC LFE BL BR BC SL SR"); ALOGI(" %d %d %d %d %d %d %d %d %d", 0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR); } #endif /*---------------------------------------------------------------------------- * Effect API implementation *--------------------------------------------------------------------------*/ Loading Loading @@ -123,6 +189,26 @@ int32_t DownmixLib_Create(const effect_uuid_t *uuid, ALOGV("DownmixLib_Create()"); #ifdef DOWNMIX_TEST_CHANNEL_INDEX // should work (won't log an error) ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:"); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER); Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK); Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER); Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER); // shouldn't work (will log an error, won't display channel indices) ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:"); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT); Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT); #endif if (pHandle == NULL || uuid == NULL) { return -EINVAL; } Loading Loading @@ -232,6 +318,7 @@ static int Downmix_Process(effect_handle_t self, const bool accumulate = (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels; switch(pDownmixer->type) { Loading @@ -256,8 +343,17 @@ static int Downmix_Process(effect_handle_t self, break; case DOWNMIX_TYPE_FOLD: #ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER // bypass the optimized downmix routines for the common formats if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); return -EINVAL; } break; #endif // optimize for the common formats switch((downmix_input_channel_mask_t)pDwmModule->config.inputCfg.channels) { switch((downmix_input_channel_mask_t)downmixInputChannelMask) { case CHANNEL_MASK_QUAD_BACK: case CHANNEL_MASK_QUAD_SIDE: Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate); Loading @@ -273,8 +369,11 @@ static int Downmix_Process(effect_handle_t self, Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate); break; default: // FIXME implement generic downmix ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported"); if (!Downmix_foldGeneric( downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) { ALOGE("Multichannel configuration 0x%x is not supported", downmixInputChannelMask); return -EINVAL; } break; } break; Loading Loading @@ -707,6 +806,8 @@ int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pS * Inputs: * pSrc quad audio samples to downmix * numFrames the number of quad frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading Loading @@ -751,6 +852,8 @@ void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool ac * Inputs: * pSrc surround signal to downmix * numFrames the number of surround frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -763,6 +866,8 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo // sample at index 1 is FR // sample at index 2 is FC // sample at index 3 is RC // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusRearContrib = FC(-3dB) + RC(-3dB) Loading @@ -771,6 +876,7 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo lt = (pSrc[0] << 12) + centerPlusRearContrib; // FR + centerPlusRearContrib rt = (pSrc[1] << 12) + centerPlusRearContrib; // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 4; Loading @@ -785,8 +891,9 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo lt = (pSrc[0] << 12) + centerPlusRearContrib; // FR + centerPlusRearContrib rt = (pSrc[1] << 12) + centerPlusRearContrib; pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 4; pDst += 2; numFrames--; Loading @@ -804,6 +911,8 @@ void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, boo * Inputs: * pSrc 5.1 audio samples to downmix * numFrames the number of 5.1 frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -818,6 +927,8 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool // sample at index 3 is LFE // sample at index 4 is RL // sample at index 5 is RR // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) Loading @@ -827,6 +938,7 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); // FR + centerPlusLfeContrib + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 6; Loading @@ -842,8 +954,9 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); // FR + centerPlusLfeContrib + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 6; pDst += 2; numFrames--; Loading @@ -861,6 +974,8 @@ void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool * Inputs: * pSrc 7.1 audio samples to downmix * numFrames the number of 7.1 frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples Loading @@ -877,6 +992,8 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool // sample at index 5 is RR // sample at index 6 is SL // sample at index 7 is SR // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { while (numFrames) { // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) Loading @@ -886,8 +1003,9 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); // FR + centerPlusLfeContrib + SR + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); pDst[0] = clamp16(lt >> 12); pDst[1] = clamp16(rt >> 12); //accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += 8; pDst += 2; numFrames--; Loading @@ -901,8 +1019,9 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); // FR + centerPlusLfeContrib + SR + RR rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += 8; pDst += 2; numFrames--; Loading @@ -910,3 +1029,142 @@ void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool } } /*---------------------------------------------------------------------------- * 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 * * Inputs: * mask the channel mask of pSrc * pSrc multichannel audio buffer to downmix * numFrames the number of multichannel frames to downmix * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, * or overwrite pDst (when false) * * Outputs: * pDst downmixed stereo audio samples * * Returns: false if multichannel format is not supported * *---------------------------------------------------------------------------- */ bool Downmix_foldGeneric( uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { // 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) bool hasSides = false; if ((mask & kSides) != 0) { if ((mask & kSides) != kSides) { ALOGE("Side channels must be used as a pair"); return false; } hasSides = true; } // verify uses BACK as a pair (ok if not using BACK at all) bool hasBacks = false; if ((mask & kBacks) != 0) { if ((mask & kBacks) != kBacks) { ALOGE("Back channels must be used as a pair"); return false; } hasBacks = true; } const int numChan = popcount(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 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format // code is mostly duplicated between the two values of accumulate to avoid repeating the test // for every sample if (accumulate) { 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_Q19_12; // always has FL/FR lt = (pSrc[0] << 12); rt = (pSrc[1] << 12); // mix in sides and backs if (hasSides) { lt += pSrc[indexSL] << 12; rt += pSrc[indexSR] << 12; } if (hasBacks) { lt += pSrc[indexBL] << 12; rt += pSrc[indexBR] << 12; } lt += centersLfeContrib; rt += centersLfeContrib; // accumulate in destination pDst[0] = clamp16(pDst[0] + (lt >> 12)); pDst[1] = clamp16(pDst[1] + (rt >> 12)); pSrc += numChan; pDst += 2; numFrames--; } } 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_Q19_12; // always has FL/FR lt = (pSrc[0] << 12); rt = (pSrc[1] << 12); // mix in sides and backs if (hasSides) { lt += pSrc[indexSL] << 12; rt += pSrc[indexSR] << 12; } if (hasBacks) { lt += pSrc[indexBL] << 12; rt += pSrc[indexBR] << 12; } lt += centersLfeContrib; rt += centersLfeContrib; // store in destination pDst[0] = clamp16(lt >> 12); // differs from when accumulate is true above pDst[1] = clamp16(rt >> 12); // differs from when accumulate is true above pSrc += numChan; pDst += 2; numFrames--; } } return true; }
media/libeffects/downmix/EffectDownmix.h +13 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,17 @@ typedef struct downmix_module_s { downmix_object_t context; } downmix_module_t; 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; /*------------------------------------ * Effect API Loading Loading @@ -92,5 +103,7 @@ void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool ac void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); bool Downmix_foldGeneric( uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate); #endif /*ANDROID_EFFECTDOWNMIX_H_*/