diff --git a/services/audio_engine/plugin/channel_converter/include/channel_converter.h b/services/audio_engine/plugin/channel_converter/include/channel_converter.h index 3dbb7725be931eeb03e6ce99d4064de3c9eed866..4ddd442df287fe3dc1d6c3cf5c3253714cd2b776 100644 --- a/services/audio_engine/plugin/channel_converter/include/channel_converter.h +++ b/services/audio_engine/plugin/channel_converter/include/channel_converter.h @@ -33,8 +33,9 @@ public: int32_t SetOutChannelInfo(AudioChannelInfo outChannelInfo); void Reset(); private: - int32_t Upmix(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen); + int32_t MixProcess(vector> mixTable, uint32_t frameLen, float* in, float* out); DownMixer downMixer_; + std::vector> upMixTable_; AudioChannelInfo inChannelInfo_; AudioChannelInfo outChannelInfo_; AudioSampleFormat workFormat_ = INVALID_WIDTH; // work format, for now only supports float diff --git a/services/audio_engine/plugin/channel_converter/include/down_mixer.h b/services/audio_engine/plugin/channel_converter/include/down_mixer.h index 83893379005554b3b6b1a403cffb6d782d094e8d..50fd7539d9411d0180742eee85611b0a437ec766 100644 --- a/services/audio_engine/plugin/channel_converter/include/down_mixer.h +++ b/services/audio_engine/plugin/channel_converter/include/down_mixer.h @@ -21,16 +21,6 @@ namespace AudioStandard { namespace HPAE { constexpr uint32_t MAX_CHANNELS = 16; -constexpr float COEF_ZERO_F = 0.0f; -constexpr float COEF_0DB_F = 1.0f; -constexpr float COEF_M3DB_F = 0.7071f; -constexpr float COEF_M6DB_F = 0.5f; -constexpr float COEF_M435DB_F = 0.6057f; -constexpr float COEF_M45DB_F = 0.5946f; -constexpr float COEF_M9DB_F = 0.3544f; -constexpr float COEF_M899DB_F = 0.3552f; -constexpr float COEF_M12DB_F = 0.2509f; - enum { DMIX_ERR_SUCCESS = 0, DMIX_ERR_ALLOC_FAILED = -1, @@ -41,31 +31,11 @@ class DownMixer { public: DownMixer(); int32_t Process(uint32_t framesize, float* in, uint32_t inLen, float* out, uint32_t outLen); - + vector> GetDownMixTable() const; int32_t SetParam(AudioChannelInfo inChannelInfo_, AudioChannelInfo outChannelInfo_, uint32_t formatSize, bool mixLfe); void Reset(); - static AudioChannelLayout SetDefaultChannelLayout(AudioChannel channels); private: - AudioChannelLayout inLayout_ = CH_LAYOUT_UNKNOWN; - uint32_t inChannels_ = 0; - AudioChannelLayout outLayout_ = CH_LAYOUT_UNKNOWN; - uint32_t outChannels_ = 0; - uint32_t formatSize_ = INVALID_WIDTH; // work format, for now only supports float - bool isInLayoutHOA_ = false; - std::vector> downMixTable_; - bool mixLfe_ = true; - bool isInitialized_ = false; - - uint32_t gSl_ = 6; - uint32_t gSr_ = 7; - uint32_t gTfl_ = 8; - uint32_t gTfr_ = 9; - uint32_t gTbl_ = 10; - uint32_t gTbr_ = 11; - uint32_t gTsl_ = 12; - uint32_t gTsr_ = 13; - bool CheckIsHOA(AudioChannelLayout layout); void SetupStereoDmixTable(); void Setup5Point1DmixTable(); void Setup5Point1Point2DmixTable(); @@ -73,7 +43,6 @@ private: void Setup7Point1DmixTable(); void Setup7Point1Point2DmixTable(); void Setup7Point1Point4DmixTable(); - void SetupGeneralDmixTable(); void ResetSelf(); int32_t SetupDownMixTable(); /**** helper functions for settiing up specific downmix table ***/ @@ -91,15 +60,26 @@ private: void Setup7Point1Point2DmixTablePart2(uint64_t bit_t, uint32_t i); void Setup7Point1Point4DmixTablePart1(uint64_t bit_t, uint32_t i); void Setup7Point1Point4DmixTablePart2(uint64_t bit_t, uint32_t i); - /**** helper functions for setting up general downmix table ***/ - void DownMixBottom(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixLfe(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixMidFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixMidRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixTopCenter(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixTopFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); - void DownMixTopRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); void NormalizeDMixTable(); + + AudioChannelLayout inLayout_ = CH_LAYOUT_UNKNOWN; + uint32_t inChannels_ = 0; + AudioChannelLayout outLayout_ = CH_LAYOUT_UNKNOWN; + uint32_t outChannels_ = 0; + uint32_t formatSize_ = INVALID_WIDTH; // work format, for now only supports float + bool isInLayoutHOA_ = false; + std::vector> downMixTable_; + bool mixLfe_ = true; + bool isInitialized_ = false; + + uint32_t gSl_ = 6; + uint32_t gSr_ = 7; + uint32_t gTfl_ = 8; + uint32_t gTfr_ = 9; + uint32_t gTbl_ = 10; + uint32_t gTbr_ = 11; + uint32_t gTsl_ = 12; + uint32_t gTsr_ = 13; }; } // HPAE } // AudioStandard diff --git a/services/audio_engine/plugin/channel_converter/include/mixer_utils.h b/services/audio_engine/plugin/channel_converter/include/mixer_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..57c47ce5dfd0a37dc1dde007808857ae6fbe3a85 --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/include/mixer_utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef GENERNAL_MIXER_H +#define GENERNAL_MIXER_H +#include +#include "audio_stream_info.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t MAX_CHANNELS = 16; +// max framelength is sample rate 192000, 10s +constexpr uint32_t MAX_CHANNEL_CONVERT_FRAME_LENGTH = SAMPLE_RATE_192000 * 10; +constexpr float COEF_ZERO_F = 0.0f; +constexpr float COEF_0DB_F = 1.0f; +constexpr float COEF_M3DB_F = 0.7071f; +constexpr float COEF_M6DB_F = 0.5f; +constexpr float COEF_M435DB_F = 0.6057f; +constexpr float COEF_M45DB_F = 0.5946f; +constexpr float COEF_M9DB_F = 0.3544f; +constexpr float COEF_M899DB_F = 0.3552f; +constexpr float COEF_M12DB_F = 0.2509f; + +// used for setting up general mixing table for downmixing and upmixing +int32_t SetUpGeneralMixingTable(vector> &coeffTable, AudioChannelInfo inChannelInfo_, + AudioChannelInfo outChannelInfo_, bool mixLfe); + +AudioChannelLayout SetDefaultChannelLayout(AudioChannel channels); + +bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts); + +uint32_t BitCounts(uint64_t bits); + +bool CheckIsHOA(AudioChannelLayout layout) + +int32_t ChannelMixProcess(uint32_t frameLen, float* in, uint32_t inByteSize, float* out, uint32_t outByteSize); +} // HPAE +} // AudioStandard +} // OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp b/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp index 8c324082b653cd0f2c164552edc9f0fa663bddc4..ed2b8786ed2100081e57cf44e4dde5c832a67d16 100644 --- a/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp +++ b/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp @@ -17,6 +17,7 @@ #endif #include "channel_converter.h" #include "audio_engine_log.h" +#include namespace OHOS { namespace AudioStandard { namespace HPAE { @@ -65,11 +66,13 @@ int32_t ChannelConverter::SetParam(AudioChannelInfo inChannelInfo, AudioChannelI workFormat_ = format; workSize_ = GetFormatSize(format); mixLfe_ = mixLfe; - isInitialized_ = true; int32_t ret = DMIX_ERR_SUCCESS; if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { ret = downMixer_.SetParam(inChannelInfo, outChannelInfo, workSize_, mixLfe); - } + } else if ( + ret = SetUpGeneralMixingTable(upMixTable_, inChannelInfo_, outChannelInfo_, mixLfe_); + ) + isInitialized_ = (ret == DMIX_ERR_SUCCESS); return ret; } @@ -80,7 +83,7 @@ int32_t ChannelConverter::SetInChannelInfo(AudioChannelInfo inChannelInfo) if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { return downMixer_.SetParam(inChannelInfo_, outChannelInfo_, workSize_, mixLfe_); } - return DMIX_ERR_SUCCESS; + return SetUpGeneralMixingTable(upMixTable_, inChannelInfo_, outChannelInfo_, mixLfe_); } @@ -91,7 +94,7 @@ int32_t ChannelConverter::SetOutChannelInfo(AudioChannelInfo outChannelInfo) if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { return downMixer_.SetParam(inChannelInfo_, outChannelInfo_, workSize_, mixLfe_); } - return DMIX_ERR_SUCCESS; + return SetUpGeneralMixingTable(upMixTable_, inChannelInfo_, outChannelInfo_, mixLfe_); } AudioChannelInfo ChannelConverter::GetInChannelInfo() const @@ -104,40 +107,53 @@ AudioChannelInfo ChannelConverter::GetOutChannelInfo() const return outChannelInfo_; } -int32_t ChannelConverter::Process(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen) +int32_t ChannelConverter::Process(uint32_t frameLen, float* in, uint32_t inByteLen, float* out, uint32_t outByteLen) { - CHECK_AND_RETURN_RET_LOG(isInitialized_, DMIX_ERR_ALLOC_FAILED, "ChannelConverter is not initialized_"); - CHECK_AND_RETURN_RET_LOG(in, DMIX_ERR_INVALID_ARG, "input pointer is nullptr"); - CHECK_AND_RETURN_RET_LOG(out, DMIX_ERR_INVALID_ARG, "output pointer is nullptr"); - CHECK_AND_RETURN_RET_LOG(frameSize >= 0, DMIX_ERR_INVALID_ARG, "invalid frameSize"); + CHECK_AND_RETURN_RET_LOG(isInitialized_, MIX_ERR_ALLOC_FAILED, "ChannelConverter is not initialized_"); + CHECK_AND_RETURN_RET_LOG(in, MIX_ERR_INVALID_ARG, "input pointer is nullptr"); + CHECK_AND_RETURN_RET_LOG(out, MIX_ERR_INVALID_ARG, "output pointer is nullptr"); + CHECK_AND_RETURN_RET_LOG(frameLen >= 0, MIX_ERR_INVALID_ARG, "invalid negative frameSize"); + CHECK_AND_RETURN_RET_LOG(frameLen <= MAX_FRAME_LENGTH, MIX_ERR_INVALID_ARG, "invalid frameSize oversize"); + + uint32_t expectInLen = frameLen * inChannelInfo_.numChannels * workSize_; + uint32_t expectOutLen = frameLen * outChannelInfo_.numChannels * workSize_; + if ((expectInLen > inByteLen) || (expectOutLen > outByteLen)) { + AUDIO_ERR_LOG("unexpected inLen %{public}d or outLen %{public}d", inLen, outLen); + int32_t ret = memcpy_s(out, outByteLen, in, std::min(inByteLen, outByteLen)); + CHECK_AND_RETURN_RET_LOG(ret == EOK, DMIX_ERR_ALLOC_FAILED, "memcpy failed when processing unexpected len"); + return MIX_ERR_ALLOC_FAILED; + } + + if (inChannelInfo_.numChannels < outChannelInfo_.numChannels) { - return Upmix(frameSize, in, inLen, out, outLen); + return MixProcess(upMixTable_, frameLen, in, out); } - return downMixer_.Process(frameSize, in, inLen, out, outLen); + return MixProcess(downMixer_.GetDownMixTable(), frameLen, in, out); } void ChannelConverter::Reset() { isInitialized_ = false; downMixer_.Reset(); + for (auto &row : upMixTable_) { + std::fill(row.begin(), row.end(), 0.0f); + } } -int32_t ChannelConverter::Upmix(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen) +int32_t ChannelConverter::MixProcess(const vector> &mixTable, uint32_t frameLen, float* in, float* out) { - CHECK_AND_RETURN_RET_LOG(frameSize <= MAX_FRAME_LENGTH, DMIX_ERR_INVALID_ARG, - "invalid frameSize %{public}d", frameSize); - uint32_t expectInLen = frameSize * inChannelInfo_.numChannels * workSize_; // to be added size of other formats - uint32_t expectOutLen = frameSize * outChannelInfo_.numChannels * workSize_; - CHECK_AND_RETURN_RET_LOG(expectInLen <= inLen, DMIX_ERR_ALLOC_FAILED, "invalid inLen %{public}d", inLen); - CHECK_AND_RETURN_RET_LOG(expectOutLen <= outLen, DMIX_ERR_ALLOC_FAILED, "invalid outLen %{public}d", outLen); - - for (uint32_t i = 0; i < frameSize; ++i) { - for (uint32_t ch = 0; ch < outChannelInfo_.numChannels; ++ch) { - uint32_t leftChIndex = Min(ch, inChannelInfo_.numChannels - 1); - out[i * outChannelInfo_.numChannels + ch] = in[i * inChannelInfo_.numChannels + leftChIndex]; + float a; + for (; frameLen > 0; frameLen--) { + for (uint32_t i = 0; i < outChannelInfo_.numChannels; i++) { + a = 0.0f; + for (uint32_t j = 0; j < inChannelInfo_.numChannels; j++) { + a += in[j] * mixTable[i][j]; + } + *(out++) = a; } + in += inChannelInfo_.numChannels; } - return DMIX_ERR_SUCCESS; + return MIX_ERR_SUCCESS; } } // HPAE diff --git a/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp b/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp index aa7d31c4a4630e1c29883389b9d63eb911a642ba..59916afab20aeaf14b04fa90c854c5acc7533b29 100644 --- a/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp +++ b/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp @@ -19,6 +19,7 @@ #include #include "securec.h" #include "down_mixer.h" +#include "general_mixer.h" #include "audio_engine_log.h" namespace OHOS { @@ -41,7 +42,6 @@ static constexpr uint32_t INDEX_EIGHT = 8; static constexpr uint32_t INDEX_NINE = 9; static constexpr uint32_t INDEX_TEN = 10; static constexpr uint32_t INDEX_ELEVEN = 11; -static constexpr uint32_t MAX_FRAME_LENGTH = SAMPLE_RATE_192000 * 10; // max framelength is sample rate 192000, 10s // channel masks for downmixing general output channel layout static constexpr uint64_t MASK_MIDDLE_FRONT = FRONT_LEFT | FRONT_RIGHT | FRONT_CENTER | @@ -69,10 +69,6 @@ static constexpr uint64_t MASK_BOTTOM = BOTTOM_FRONT_CENTER static constexpr uint64_t MASK_LFE = LOW_FREQUENCY | LOW_FREQUENCY_2; -static uint32_t BitCounts(uint64_t bits); -static bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts); - -// 改成默认构造 DownMixer::DownMixer() { downMixTable_.resize(MAX_CHANNELS, std::vector(MAX_CHANNELS, 0)); @@ -108,24 +104,7 @@ int32_t DownMixer::Process(uint32_t frameLen, float* in, uint32_t inLen, float* CHECK_AND_RETURN_RET_LOG(out, DMIX_ERR_INVALID_ARG, "output pointer is nullptr"); CHECK_AND_RETURN_RET_LOG(frameLen <= MAX_FRAME_LENGTH, DMIX_ERR_INVALID_ARG, "invalid frameSize"); CHECK_AND_RETURN_RET_LOG(isInitialized_, DMIX_ERR_ALLOC_FAILED, "Downmixe table has not been initialized!"); - - uint32_t expectInLen = frameLen * inChannels_ * formatSize_; - uint32_t expectOutLen = frameLen * outChannels_ * formatSize_; - if ((expectInLen > inLen) || (expectOutLen > outLen)) { - AUDIO_ERR_LOG("unexpected inLen %{public}d or outLen %{public}d", inLen, outLen); - int32_t ret = memcpy_s(out, outLen, in, inLen); - CHECK_AND_RETURN_RET_LOG(ret == EOK, DMIX_ERR_ALLOC_FAILED, "memcpy failed when processing unexpected len"); - return DMIX_ERR_ALLOC_FAILED; - } - // For HOA, copy the first channel into all output channels - if (isInLayoutHOA_) { - for (uint32_t i = 0; i < frameLen; i++) { - for (uint32_t c = 0; c < outChannels_; c++) { - out[outChannels_ * i + c] = in[inChannels_ * i]; - } - } - return DMIX_ERR_SUCCESS; - } + float a; for (; frameLen > 0; frameLen--) { for (uint32_t i = 0; i < outChannels_; i++) { @@ -149,7 +128,17 @@ int32_t DownMixer::SetupDownMixTable() inChannels_, inLayout_, outChannels_, outLayout_); return DMIX_ERR_INVALID_ARG; } - CheckIsHOA(inLayout_); + // for HOA intput, use the first channel input for every output channel + if(CheckIsHOA(inLayout_)) { + for (int i = 0; i < outChannels_; i++) { + downMixTable_[i][0] = COEF_0DB_F; + } + return DMIX_ERR_SUCCESS; + } + // for HOA output set them to default channelLayout + if (CheckIsHOA(outLayout_)) { + outLayout_ = SetDefaultChannelLayout(outChannels_); + } switch (outLayout_) { case CH_LAYOUT_STEREO: { SetupStereoDmixTable(); @@ -180,7 +169,9 @@ int32_t DownMixer::SetupDownMixTable() break; } default: { - SetupGeneralDmixTable(); + AudioChannelInfo inChannelInfo = {inLayout_, inChannels_}; + AudioChannelInfo outChannelInfo = {outLayout_, outChannels_}; + SetUpGeneralMixingTable(downMixTable_, inChannelInfo, outChannelInfo_, mixLfe_); break; } } @@ -350,46 +341,6 @@ void DownMixer::Setup7Point1Point4DmixTable() } } -void DownMixer::SetupGeneralDmixTable() -{ - // MONO - if (outLayout_ == CH_LAYOUT_MONO) { - for (uint32_t i = 0; i < inChannels_; i++) { - downMixTable_[0][i] = COEF_0DB_F; - } - } - // check invalid output musk later in init() - uint64_t outChMsk = outLayout_; - - for (uint32_t i = 0; i < outChannels_; i++) { - uint64_t outBit = outChMsk & (~outChMsk + 1); - uint64_t inChMsk = inLayout_; - for (uint32_t j = 0; j < inChannels_; j++) { - uint64_t inBit = inChMsk & (~inChMsk + 1); - if (inBit & outBit) { // if in channel and out channel is the same - downMixTable_[i][j] = COEF_0DB_F; - } else if (inBit == TOP_CENTER) { - // check general downmix table! - DownMixTopCenter(inBit, outBit, i, j); - } else if ((inBit & MASK_MIDDLE_FRONT) != 0) { - DownMixMidFront(inBit, outBit, i, j); - } else if ((inBit & MASK_MIDDLE_REAR) != 0) { - DownMixMidRear(inBit, outBit, i, j); - } else if ((inBit & MASK_BOTTOM) != 0) { - DownMixBottom(inBit, outBit, i, j); - } else if ((inBit & MASK_LFE) != 0) { - DownMixLfe(inBit, outBit, i, j); - } else if ((inBit & MASK_TOP_FRONT) != 0) { - DownMixTopFront(inBit, outBit, i, j); - } else if ((inBit & MASK_TOP_REAR) != 0) { - DownMixTopRear(inBit, outBit, i, j); - } - inChMsk ^= inBit; - } - outChMsk ^= outBit; - } -} - void DownMixer::SetupStereoDmixTablePart1(uint64_t bit, uint32_t i) { switch (bit) { @@ -937,182 +888,13 @@ void DownMixer::Setup7Point1Point4DmixTablePart2(uint64_t bit, uint32_t i) } } -void DownMixer::DownMixBottom(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - if ((inBit & MASK_BOTTOM) && (outBit & MASK_BOTTOM)) { - downMixTable_[i][j] = COEF_M3DB_F; - } else { - DownMixMidFront(inBit, outBit, i, j); - } -} - -void DownMixer::DownMixMidFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - if ((inBit & MASK_MIDDLE_FRONT) && (outBit & MASK_MIDDLE_FRONT)) { - downMixTable_[i][j] = COEF_M3DB_F; - } -} - -void DownMixer::DownMixMidRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - // Middle layer - switch (inBit) { - case BACK_CENTER: - if ((outBit == BACK_LEFT) || (outBit == SIDE_LEFT) || (outBit == WIDE_LEFT) || (outBit == FRONT_LEFT) || - (outBit == BACK_RIGHT) || outBit == SIDE_RIGHT || outBit == WIDE_RIGHT || outBit == FRONT_RIGHT) { - downMixTable_[i][j] = COEF_M3DB_F; - } - break; - case BACK_LEFT: - if (outBit == SIDE_LEFT) { - downMixTable_[i][j] = COEF_0DB_F; - } else if (outBit == BACK_CENTER) { - downMixTable_[i][j] = COEF_0DB_F; - } else { - DownMixMidFront(WIDE_LEFT, outBit, i, j); - } - break; - case BACK_RIGHT: - if (outBit == SIDE_RIGHT) { - downMixTable_[i][j] = COEF_0DB_F; - } else if (outBit == BACK_CENTER) { - downMixTable_[i][j] = COEF_0DB_F; - } else { - DownMixMidFront(WIDE_RIGHT, outBit, i, j); - } - break; - default: - break; - } -} - -void DownMixer::DownMixLfe(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - if ((MASK_LFE & inBit) && (MASK_LFE & outBit)) { - downMixTable_[i][j] = COEF_0DB_F; - } else { - if ((inBit == LOW_FREQUENCY) && ((outBit & CH_LAYOUT_STEREO)!= 0)) { - downMixTable_[i][j] = COEF_M6DB_F; - } else if ((inBit == LOW_FREQUENCY_2) && ((outBit & CH_LAYOUT_STEREO) != 0)) { - downMixTable_[i][j] = COEF_M6DB_F; - } - } -} - -void DownMixer::DownMixTopCenter(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - uint64_t exitTopOuts = outLayout_ & (MASK_TOP_FRONT | MASK_TOP_REAR); - uint64_t exitMiddleOuts = outLayout_ & (MASK_MIDDLE_FRONT | MASK_MIDDLE_REAR); - if (exitTopOuts != 0) { // exist top outs - uint32_t numChannels = BitCounts(exitTopOuts); - uint32_t coeff = 1.0f / sqrt((float)numChannels); - if ((outBit & exitTopOuts) != 0) { - downMixTable_[i][j] = coeff; - } - } else if (exitMiddleOuts != 0) { - uint32_t numChannels = BitCounts(exitMiddleOuts); - uint32_t coeff = 1.0f / sqrt((float)numChannels); - if ((outBit & exitMiddleOuts) != 0) { - downMixTable_[i][j] = coeff; - } - } -} - -void DownMixer::DownMixTopFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +vector> DownMixer::GetDownMixTable() { - uint64_t existTopFrontOuts = outLayout_ & MASK_TOP_FRONT; - if (existTopFrontOuts != 0) { - if ((outBit & MASK_TOP_FRONT) != 0) { - downMixTable_[i][j] = COEF_M3DB_F; - } - } else { - DownMixMidFront(TOP_FRONT_CENTER, outBit, i, j); + if (isInitialized_) { + AUDIO_WARNING_LOG("downmix Table is not initialized yet!"); } + return downMixTable_; } - -void DownMixer::DownMixTopRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) -{ - uint64_t existTopRearOuts = outLayout_ & MASK_TOP_REAR; - if (existTopRearOuts != 0) { - if ((outBit & MASK_TOP_REAR) != 0) { - downMixTable_[i][j] = COEF_M3DB_F; - } - } else { - DownMixMidRear(BACK_CENTER, outBit, i, j); - } -} - -static uint32_t BitCounts(uint64_t bits) -{ - uint32_t num = 0; - for (; bits != 0; bits &= bits - 1) { - num++; - } - return num; -} - -static bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts) -{ - if (chCounts < MONO || chCounts > CHANNEL_16) { - return false; - } - if (chLayout == CH_LAYOUT_UNKNOWN || BitCounts(chLayout) != chCounts) { - chLayout = DownMixer::SetDefaultChannelLayout((AudioChannel)chCounts); - } - return true; -} - -AudioChannelLayout DownMixer::SetDefaultChannelLayout(AudioChannel channels) -{ - CHECK_AND_RETURN_RET_LOG((channels >= MONO) && (channels <= CHANNEL_16), CH_LAYOUT_UNKNOWN, - "invalid channel count"); - switch (channels) { - case MONO: - return CH_LAYOUT_MONO; - case STEREO: - return CH_LAYOUT_STEREO; - case CHANNEL_3: - return CH_LAYOUT_SURROUND; - case CHANNEL_4: - return CH_LAYOUT_3POINT1; - case CHANNEL_5: - return CH_LAYOUT_4POINT1; - case CHANNEL_6: - return CH_LAYOUT_5POINT1; - case CHANNEL_7: - return CH_LAYOUT_6POINT1; - case CHANNEL_8: - return CH_LAYOUT_5POINT1POINT2; - case CHANNEL_9: - return CH_LAYOUT_HOA_ORDER2_ACN_N3D; - case CHANNEL_10: - return CH_LAYOUT_7POINT1POINT2; - case CHANNEL_12: - return CH_LAYOUT_7POINT1POINT4; - case CHANNEL_14: - return CH_LAYOUT_9POINT1POINT4; - case CHANNEL_16: - return CH_LAYOUT_9POINT1POINT6; - default: - return CH_LAYOUT_UNKNOWN; - } -} - -bool DownMixer::CheckIsHOA(AudioChannelLayout layout) -{ - if ((layout == CH_LAYOUT_HOA_ORDER1_ACN_N3D) || (layout == CH_LAYOUT_HOA_ORDER1_ACN_SN3D) || - (layout == CH_LAYOUT_HOA_ORDER1_FUMA) || (layout == CH_LAYOUT_HOA_ORDER2_ACN_N3D) || - (layout == CH_LAYOUT_HOA_ORDER2_ACN_SN3D) || (layout == CH_LAYOUT_HOA_ORDER2_FUMA) || - (layout == CH_LAYOUT_HOA_ORDER3_ACN_N3D) || (layout == CH_LAYOUT_HOA_ORDER3_ACN_SN3D) || - (layout == CH_LAYOUT_HOA_ORDER3_FUMA)) - { - isInLayoutHOA_ = true; - return true; - } - isInLayoutHOA_ = false; - return false; -} - } // HPAE } // AudioStandard } // OHOS \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/src/mixer_utils.cpp b/services/audio_engine/plugin/channel_converter/src/mixer_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cd530fd734cc3fdc7f0797ea18e2848ded36e18 --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/src/mixer_utils.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "general_mixer.h" +#include +static constexpr uint64_t MASK_MIDDLE_FRONT = FRONT_LEFT | FRONT_RIGHT | FRONT_CENTER | +FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER | WIDE_LEFT | WIDE_RIGHT; + +static constexpr uint64_t MASK_MIDDLE_REAR = BACK_LEFT | BACK_RIGHT | BACK_CENTER +| SIDE_LEFT +| SIDE_RIGHT; + +static constexpr uint64_t MASK_TOP_FRONT = TOP_FRONT_LEFT +| TOP_FRONT_CENTER +| TOP_FRONT_RIGHT; + +static constexpr uint64_t MASK_TOP_REAR = TOP_CENTER +| TOP_BACK_LEFT +| TOP_BACK_CENTER +| TOP_BACK_RIGHT +| TOP_SIDE_LEFT +| TOP_SIDE_RIGHT; + +static constexpr uint64_t MASK_BOTTOM = BOTTOM_FRONT_CENTER +| BOTTOM_FRONT_LEFT +| BOTTOM_FRONT_RIGHT; + +static constexpr uint64_t MASK_LFE = LOW_FREQUENCY +| LOW_FREQUENCY_2; + +static std::map DEFAULT_CHANNEL_MAP = { + {MONO, CH_LAYOUT_MONO}, + {STEREO, CH_LAYOUT_STEREO}, + {CHANNEL_3, CH_LAYOUT_SURROUND}, + {CHANNEL_4, CH_LAYOUT_3POINT1}, + {CHANNEL_5, CH_LAYOUT_4POINT1}, + {CHANNEL_6, CH_LAYOUT_5POINT1}, + {CHANNEL_7, CH_LAYOUT_6POINT1}, + {CHANNEL_8, CH_LAYOUT_5POINT1POINT2}, + {CHANNEL_9, CH_LAYOUT_HOA_ORDER2_ACN_N3D}, + {CHANNEL_10, CH_LAYOUT_7POINT1POINT2}, + {CHANNEL_12, CH_LAYOUT_7POINT1POINT4}, + {CHANNEL_14, CH_LAYOUT_9POINT1POINT4}, + {CHANNEL_16, CH_LAYOUT_9POINT1POINT6} +}; + +static std::set HOA_SET = { + CH_LAYOUT_HOA_ORDER1_ACN_N3D, CH_LAYOUT_HOA_ORDER1_ACN_SN3D, CH_LAYOUT_HOA_ORDER1_FUMA, + CH_LAYOUT_HOA_ORDER2_ACN_N3D, CH_LAYOUT_HOA_ORDER2_ACN_SN3D, CH_LAYOUT_HOA_ORDER2_FUMA, + CH_LAYOUT_HOA_ORDER3_ACN_N3D, CH_LAYOUT_HOA_ORDER3_ACN_SN3D, CH_LAYOUT_HOA_ORDER3_FUMA, +} + +uint32_t BitCounts(uint64_t bits) +{ + uint32_t num = 0; + for (; bits != 0; bits &= bits - 1) { + num++; + } + return num; +} + +static void MixLfe(vector> &coeffTable, std::pair pos_to_bit, + std::pair chMskPair, const std::map &channelPosMap); +static void MixMidFront(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); +static void MixMidRear(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); +static void MixTopCenter(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); +static void MixTopFront(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); +static void MixTopRear(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); +static void MixBottom(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap); + +// channel masks for downmixing general output channel layout +/**** helper functions for setting up general mixing table ***/ +// inBit: switch for output +static void MixMidFront(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + switch (inBit) { + case FRONT_CENTER: + if ((outChLayout & FRONT_LEFT_OF_CENTER) && (outChLayout & FRONT_RIGHT_OF_CENTER)) { + coeffTable[channelPosMap[FRONT_LEFT_OF_CENTER]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[FRONT_RIGHT_OF_CENTER]][inPos] = COEF_M3DB_F; + } else if ((outChLayout & FRONT_LEFT) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_M3DB_F; + } + break; + case FRONT_LEFT_OF_CENTER: + if ((outChLayout & FRONT_CENTER) && (outChLayout & FRONT_LEFT)) { + coeffTable[channelPosMap[FRONT_CENTER]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_M3DB_F; + } else if (outChLayout & FRONT_LEFT) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_0DB_F; + } + break; + case FRONT_RIGHT_OF_CENTER: + if ((outChLayout & FRONT_CENTER) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_CENTER]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_M3DB_F; + } else if (outChLayout & FRONT_RIGHT) { + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_0DB_F; + } + break; + case WIDE_LEFT: + if (outChLayout & FRONT_LEFT) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_0DB_F; + } + break; + case WIDE_RIGHT: + if (outChLayout & FRONT_RIGHT) { + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_0DB_F; + } + break; + default: + break; + } +} + +static void MixMidRear(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + switch (inBit) { + case BACK_CENTER: + if ((outChLayout & BACK_LEFT) && (outChLayout & BACK_RIGHT)) { + coeffTable[channelPosMap[BACK_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[BACK_RIGHT]][inPos] = COEF_M3DB_F; + } else if ((outChLayout & SIDE_LEFT) && (outChLayout & SIDE_RIGHT)) { + coeffTable[channelPosMap[SIDE_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[SIDE_RIGHT]][inPos] = COEF_M3DB_F; + } else if ((outChLayout & WIDE_LEFT) && (outChLayout & WIDE_RIGHT)) { + coeffTable[channelPosMap[WIDE_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[WIDE_RIGHT]][inPos] = COEF_M3DB_F; + } else if ((outChLayout & FRONT_LEFT) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_M3DB_F; + } + break; + case BACK_LEFT: + if (outChLayout & SIDE_LEFT) { + coeffTable[channelPosMap[SIDE_LEFT]][inPos] = COEF_0DB_F; + } else if (outChLayout & BACK_CENTER) { + coeffTable[channelPosMap[BACK_CENTER]][inPos] = COEF_0DB_F; + } else { + MixMidFront(coeffTable, {inPos, WIDE_LEFT}, outChLayout, channelPosMap); + } + break; + case BACK_RIGHT: + if (outChLayout & SIDE_RIGHT) { + coeffTable[channelPosMap[SIDE_RIGHT]][inPos] = COEF_0DB_F; + } else if (outChLayout & BACK_CENTER) { + coeffTable[channelPosMap[BACK_CENTER]][inPos] = COEF_0DB_F; + } else { + MixMidFront(coeffTable, {inPos, WIDE_RIGHT}, outChLayout, channelPosMap); + } + break; + case SIDE_LEFT: + if (outChLayout & BACK_LEFT) { + coeffTable[channelPosMap[BACK_LEFT]][inPos] = COEF_0DB_F; + } else if (outChLayout & BACK_CENTER) { + coeffTable[channelPosMap[BACK_CENTER]][inPos] = COEF_0DB_F; + } else { + MixMidFront(coeffTable, {inPos, WIDE_LEFT}, outChLayout, channelPosMap); + } + break; + case SIDE_RIGHT: + if (outChLayout & BACK_RIGHT) { + coeffTable[channelPosMap[BACK_RIGHT]][inPos] = COEF_0DB_F; + } else if (outChLayout & BACK_CENTER) { + coeffTable[channelPosMap[BACK_CENTER]][inPos] = COEF_0DB_F; + } else { + MixMidFront(coeffTable, {inPos, WIDE_RIGHT}, outChLayout, channelPosMap); + } + break; + default: + break; + } +} + +static void MixBottom(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + uint64_t existBottomOutput = (outChLayout & MASK_BOTTOM); + if (existBottomOutput) { + switch (inBit) { + case (BOTTOM_FRONT_CENTER): + if ((outChLayout & BOTTOM_FRONT_LEFT) || (outChLayout & BOTTOM_FRONT_RIGHT)) { + coeffTable[channelPosMap[BOTTOM_FRONT_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[BOTTOM_FRONT_RIGHT]][inPos] = COEF_M3DB_F; + } + break; + case (BOTTOM_FRONT_LEFT): + if (outChLayout & BOTTOM_FRONT_CENTER) { + coeffTable[channelPosMap[BOTTOM_FRONT_CENTER]][inPos] = COEF_0DB_F; + } + break; + case (BOTTOM_FRONT_RIGHT): + if (outChLayout & BOTTOM_FRONT_CENTER) { + coeffTable[channelPosMap[BOTTOM_FRONT_CENTER]][inPos] = COEF_0DB_F; + } + default: + break; + } + } else { + switch (inBit) { + case (BOTTOM_FRONT_CENTER): + MixMidFront(coeffTable, {inPos, FRONT_CENTER}, outChLayout, channelPosMap); + break; + case (BOTTOM_FRONT_LEFT): + MixMidFront(coeffTable, {inPos, FRONT_LEFT}, outChLayout, channelPosMap); + break; + case (BOTTOM_FRONT_RIGHT): + MixMidFront(coeffTable, {inPos, FRONT_RIGHT}, outChLayout, channelPosMap); + break; + default: + break; + } + } +} + +static void MixTopCenter(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint32_t inBit = pos_to_bit.first; + uint64_t existMiddleOuts = outChLayout & (MASK_MIDDLE_FRONT | MASK_MIDDLE_REAR); + uint64_t existTopOuts = outChLayout & (MASK_TOP_FRONT | MASK_TOP_REAR); + uint32_t outChannelCount = BitCounts(outChLayout); + if (existTopOuts) { + uint64_t numChannels = BitCounts(existTopOuts); + float coeff = 1.0f / sqrt((float)numChannels); + uint64_t bitMsk = existTopOuts; + for (uint32_t i = 0; i < outChannelCount; i++) { + uint64_t outBit = bitMsk & (~bitMsk + 1); + coeffTable[channelPosMap[outBit]][inPos] = coeff; + bitMsk ^= outBit; + } + } + if (existMiddleOuts) { + uint64_t numChannels = BitCounts(existMiddleOuts); + float coeff = 1.0f / sqrt((float)numChannels); + uint64_t bitMsk = existMiddleOuts; + for (uint32_t i = 0; i < outChannelCount; i++) { + uint64_t outBit = bitMsk & (~bitMsk + 1); + coeffTable[channelPosMap[outBit]][inPos] = coeff; + bitMsk ^= outBit; + } + } +} + +static void MixTopFront(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint64_t existTopFrontOut = outChLayout & MASK_TOP_FRONT; + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + if (existTopFrontOut) { + switch (inBit) { + case TOP_FRONT_CENTER: + if ((outChLayout & TOP_FRONT_LEFT) && (outChLayout & TOP_FRONT_RIGHT)) { + coeffTable[channelPosMap[TOP_FRONT_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[TOP_FRONT_RIGHT]][inPos] = COEF_M3DB_F; + } + break; + case TOP_FRONT_LEFT: + if (outChLayout & TOP_FRONT_CENTER) { + coeffTable[channelPosMap[TOP_FRONT_CENTER]][inPos] = COEF_0DB_F; + } + break; + case TOP_FRONT_RIGHT: + if (outChLayout & TOP_FRONT_CENTER) { + coeffTable[channelPosMap[TOP_FRONT_CENTER]][inPos] = COEF_0DB_F; + } + break; + default: + break; + } + } else { + switch (inBit) { + case TOP_FRONT_CENTER: + MixMidFront(coeffTable, {inPos, FRONT_CENTER}, outChLayout, channelPosMap); + break; + case TOP_FRONT_LEFT: + MixMidFront(coeffTable, {inPos, FRONT_LEFT}, outChLayout, channelPosMap); + break; + case TOP_FRONT_RIGHT: + MixMidFront(coeffTable, {inPos, FRONT_RIGHT}, outChLayout, channelPosMap); + break; + default: + break; + } + } +} + +static void MixTopRear(vector> &coeffTable, std::pair pos_to_bit, + uint64_t outChLayout, const std::map &channelPosMap) +{ + uint64_t existTopRearOuts = outChLayout | MASK_TOP_REAR; + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + if (existTopRearOuts) { + switch (inBit) { + case TOP_BACK_CENTER: + if ((outChLayout & TOP_BACK_LEFT) && (outChLayout & TOP_BACK_RIGHT)) { + coeffTable[channelPosMap[TOP_BACK_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[TOP_BACK_RIGHT]][inPos] = COEF_M3DB_F; + } else if ((outChLayout & TOP_SIDE_LEFT) && (outChLayout & TOP_SIDE_RIGHT)) { + coeffTable[channelPosMap[TOP_SIDE_LEFT]][inPos] = COEF_M3DB_F; + coeffTable[channelPosMap[TOP_SIDE_RIGHT]][inPos] = COEF_M3DB_F; + } + break; + case TOP_BACK_LEFT: + if (outChLayout & TOP_SIDE_LEFT) { + coeffTable[channelPosMap[TOP_SIDE_LEFT]][inPos] = COEF_0DB_F; + } else if (outChLayout & TOP_BACK_CENTER) { + coeffTable[channelPosMap[TOP_BACK_CENTER]][inPos] = COEF_0DB_F; + } + break; + case TOP_BACK_RIGHT: + if (outChLayout & TOP_SIDE_RIGHT) { + coeffTable[channelPosMap[TOP_SIDE_RIGHT]][inPos] = COEF_0DB_F; + } else if (outChLayout & TOP_BACK_CENTER) { + coeffTable[channelPosMap[TOP_BACK_CENTER]][inPos] = COEF_0DB_F; + } + break; + case TOP_SIDE_LEFT: + if (outChLayout & TOP_BACK_LEFT) { + coeffTable[channelPosMap[TOP_BACK_LEFT]][inPos] = COEF_0DB_F; + } else if (outChLayout & TOP_BACK_CENTER) { + coeffTable[channelPosMap[TOP_BACK_CENTER]][inPos] = COEF_0DB_F; + } + break; + case TOP_SIDE_RIGHT: + if (outChLayout & TOP_BACK_RIGHT) { + coeffTable[channelPosMap[TOP_BACK_RIGHT]][inPos] = COEF_0DB_F; + } else if (outChLayout & TOP_BACK_CENTER) { + coeffTable[channelPosMap[TOP_BACK_CENTER]][inPos] = COEF_0DB_F; + } + break; + default: + break; + } + } else { + switch (inBit) { + case TOP_BACK_CENTER: + MixMidRear(coeffTable, {inPos, BACK_CENTER}, outChLayout, channelPosMap); + break; + case TOP_BACK_LEFT: + MixMidRear(coeffTable, {inPos, BACK_LEFT}, outChLayout, channelPosMap); + break; + case TOP_BACK_RIGHT: + MixMidRear(coeffTable, {inPos, BACK_RIGHT}, outChLayout, channelPosMap); + break; + case TOP_SIDE_LEFT: + MixMidRear(coeffTable, {inPos, SIDE_LEFT}, outChLayout, channelPosMap); + break; + case TOP_SIDE_RIGHT: + MixMidRear(coeffTable, {inPos, SIDE_RIGHT}, outChLayout, channelPosMap); + break; + default: + break; + } + } +} + +static void MixLfe(vector> &coeffTable, std::pair pos_to_bit, + std::pair chMskPair, const std::map &channelPosMap) +{ + uint64_t existLfeOuts = outChLayout & MASK_LFE; + uint64_t existLfe2In = chMskPair.first & LOW_FREQUENCY_2; + uint32_t inPos = pos_to_bit.first; + uint64_t inBit = pos_to_bit.second; + uint64_t inLayout = chMskPair.first; + uint64_t outChLayout = chMskPair.second; + if (existLfeOuts) { + switch (inBit) { + case LOW_FREQUENCY: + coeffTable[channelPosMap[LOW_FREQUENCY_2]][inPos] = COEF_0DB_F; + break; + case LOW_FREQUENCY_2: + coeffTable[channelPosMap[LOW_FREQUENCY]][inPos] = COEF_0DB_F; + break; + default: + break; + } + } else { + switch (inBit) { + case LOW_FREQUENCY_2: + if ((outChLayout & FRONT_LEFT) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_ZERO_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_M6DB_F; + } + break; + case LOW_FREQUENCY: + if (existLfe2In && (outChLayout & FRONT_LEFT) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_M6DB_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_ZERO_F; + } else if ((outChLayout & FRONT_LEFT) && (outChLayout & FRONT_RIGHT)) { + coeffTable[channelPosMap[FRONT_LEFT]][inPos] = COEF_M6DB_F; + coeffTable[channelPosMap[FRONT_RIGHT]][inPos] = COEF_M6DB_F; + } + break; + default: + break; + } + } +} + +static int32_t SetUpGeneralMixingTableInner(vector> &coeffTable, AudioChannelInfo inChannelInfo, + AudioChannelInfo outChannelInfo, bool mixLfe) +{ + AudioChannelInfo dstChannelInfo; + AudioChannelInfo srcChannelInfo; + // general table made up rule: if input ch positon is output ch positon, coefficient is 1 + if (inChannelInfo.numChannels > outChannelInfo.numChannels) { // downmix + dstChannelInfo = outChannelInfo; + srcChannelInfo = inChannelInfo; + } else { // upmix + dstChannelInfo = inChannelInfo; + srcChannelInfo = outChannelInfo; + } + // Get position map of dst channels in dstChMsk + std::map channelPosMap; + uint64_t dstMchMsk = dstChannelInfo.channelLayout; + for (uint32_t i = 0; i < dstChannelInfo.numChannels; i++) { + uint64_t dstBit = dstMchMsk & (~dstMchMsk + 1); + map[dstBit] = i; + dstMchMsk ^= dstBit; + } + + dstMchMsk = dstChannelInfo.channelLayout; + uint64_t srcChMsk = inChannelInfo.channelLayout; + for (uint32_t i = 0; i < inChannelInfo.numChannels; i++) { + uint64_t inBit = srcChMsk & (~srcChMsk + 1); + if (inBit & dstMchMsk) { // if in channel and out channel is the same + coeffTable[channelPosMap[inBit]][i] = COEF_0DB_F; + } else if (inBit == TOP_CENTER) { + // check general downmix table! + MixTopCenter(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } else if ((inBit & MASK_MIDDLE_FRONT) != 0) { + MixMidFront(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } else if ((inBit & MASK_MIDDLE_REAR) != 0) { + MixMidRear(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } else if ((inBit & MASK_BOTTOM) != 0) { + MixBottom(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } else if (mixLfe && (inBit & MASK_LFE) != 0) { + MixLfe(coeffTable, {i, inBit}, {srcChMsk, dstMchMsk}, channelPosMap); + } else if ((inBit & MASK_TOP_FRONT) != 0) { + MixTopFront(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } else if ((inBit & MASK_TOP_REAR) != 0) { + MixTopRear(coeffTable, {i, inBit}, dstMchMsk, channelPosMap); + } + srcChMsk ^= inBit; + } + return MIX_ERR_SUCCESS; +} + +// coeffTable[outputIndex][inputIndex] +int32_t SetUpGeneralMixingTable(vector> &coeffTable, AudioChannelInfo inChannelInfo, + AudioChannelInfo outChannelInfo, bool mixLfe); +{ + CHECK_AND_RETURN_RET_LOG(inChannelInfo.numChannels <= coeffTable[0].size(), MIX_ERR_INVALID_ARG, + "column size of coeffTable not enough"); + CHECK_AND_RETURN_RET_LOG(outChannelInfo.numChannels <= coeffTable[0], MIX_ERR_INVALID_ARG, + "row size of coeffTable not enough"); + CHECK_AND_RETURN_RET_LOG(IsValidChLayout(inChannelInfo.channelLayout, inChannelInfo.numChannels), + MIX_ERR_INVALID_ARG, "invalid input channel info"); + CHECK_AND_RETURN_RET_LOG(IsValidChLayout(outChannelInfo.channelLayout, outChannelInfo.numChannels), + MIX_ERR_INVALID_ARG, "invalid output channel info"); + + // for HOA intput, use the first channel input for every output channel + if(CheckIsHOA(inChannelInfo.channelLayout)) { + for (int i = 0; i < outChannelInfo.numChannels; i++) { + coeffTable[i][0] = COEF_0DB_F; + } + return MIX_ERR_SUCCESS; + } + // for HOA output set them to default channelLayout + if (CheckIsHOA(outLayout_)) { + outChannelInfo.channelLayout = SetDefaultChannelLayout(outChannelInfo.numChannels); + } + + // MONO upmix + if (outLayout_ == CH_LAYOUT_MONO) { + for (uint32_t i = 0; i < inChannelInfo.numChannels; i++) { + coeffTable[0][i] = COEF_0DB_F; + } + return MIX_ERR_SUCCESS; + } + + return SetUpGeneralMixingTableInner(coeffTable, inChannelInfo, outChannelInfo, mixLfe); +} + +AudioChannelLayout SetDefaultChannelLayout(AudioChannel channels) +{ + CHECK_AND_RETURN_RET_LOG(DEFAULT_CHANNEL_MAP.find(channels) == DEFAULT_CHANNEL_MAP.end(), CH_LAYOUT_UNKNOWN, + "invalid channel count"); + return DEFAULT_CHANNEL_MAP[channels]; +} + +bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts) +{ + if (chCounts < MONO || chCounts > CHANNEL_16) { + return false; + } + if (chLayout == CH_LAYOUT_UNKNOWN || BitCounts(chLayout) != chCounts) { + chLayout = SetDefaultChannelLayout((AudioChannel)chCounts); + } + return true; +} + +bool CheckIsHOA(AudioChannelLayout layout) +{ + return (HOS_SET.find(layout) != HOS_SET.end()); +} + +// Do not use this function before mixTable is initialized! +// Use this function only in +int32_t ChannelMixProcessFloat(const vector> &mixTable, uint32_t frameLen, + std::pair inButterToSize, std::pair outButterToSize) +{ + float *in = inButterToSize.first; + uint32_t inBufferSize = inButterToSize.second; + float *out = outBufferToSize.first; + uint32_t outBufferSize = outButterToSize.second; + CHECK_AND_RETURN_RET_LOG(in, MIX_ERR_INVALID_ARG, "input pointer is nullptr"); + CHECK_AND_RETURN_RET_LOG(out, MIX_ERR_INVALID_ARG, "output pointer is nullptr"); + CHECK_AND_RETURN_RET_LOG(frameLen <= MAX_FRAME_LENGTH, DMIX_ERR_INVALID_ARG, "invalid frameSize"); + + float a; + for (; frameLen > 0; frameLen--) { + for (uint32_t i = 0; i < outChannels_; i++) { + a = 0.0f; + for (uint32_t j = 0; j < inChannels_; j++) { + a += in[j] * downMixTable_[i][j]; + } + *(out++) = a; + } + in += inChannels_; + } + return DMIX_ERR_SUCCESS; +} \ No newline at end of file