1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef LOG_TAG
16 #define LOG_TAG "HpaeLoudnessGainNode"
17 #endif
18
19 #include <dlfcn.h>
20 #include <cinttypes>
21 #include <cmath>
22 #include "hpae_loudness_gain_node.h"
23 #include "hpae_pcm_buffer.h"
24 #include "audio_utils.h"
25 #include "audio_errors.h"
26 #include "audio_effect_log.h"
27
28 namespace OHOS {
29 namespace AudioStandard {
30 namespace HPAE {
31 static const std::string LOUDNESSGAIN_PATH = "/system/lib64/libaudio_integration_loudness.z.so";
32 static constexpr float EPSILON = 1e-6f;
33 static constexpr uint32_t SAMPLE_RATE = 48000;
34 static constexpr float DB_TO_AMPLITUDE_BASE = 10.0f;
35 static constexpr float DB_TO_AMPLITUDE_DIVISOR = 20.0f;
36 static const AudioEffectDescriptor LOUDNESS_DESCRIPTOR = {
37 .libraryName = "loudness",
38 .effectName = "loudness",
39 };
40
IsFloatValueEqual(float a,float b)41 static inline bool IsFloatValueEqual(float a, float b)
42 {
43 return std::abs(a - b) < EPSILON;
44 }
45
LoudnessDbToLinearGain(float loudnessGainDb)46 static inline float LoudnessDbToLinearGain(float loudnessGainDb)
47 {
48 return std::pow(DB_TO_AMPLITUDE_BASE, loudnessGainDb / DB_TO_AMPLITUDE_DIVISOR);
49 }
50
HpaeLoudnessGainNode(HpaeNodeInfo & nodeInfo)51 HpaeLoudnessGainNode::HpaeLoudnessGainNode(HpaeNodeInfo &nodeInfo) : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo),
52 pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout),
53 loudnessGainOutput_(pcmBufferInfo_)
54 {
55 AUDIO_INFO_LOG("HpaeLoudnessGainNode created");
56 dlHandle_ = dlopen(LOUDNESSGAIN_PATH.c_str(), 1);
57 if (!dlHandle_) {
58 AUDIO_ERR_LOG("<log error> dlopen lib %{public}s Fail", LOUDNESSGAIN_PATH.c_str());
59 } else {
60 AUDIO_INFO_LOG("<log info> dlopen lib %{public}s successful", LOUDNESSGAIN_PATH.c_str());
61 }
62 dlerror();
63
64 audioEffectLibHandle_ = static_cast<AudioEffectLibrary *>(dlsym(dlHandle_,
65 AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR));
66 if (!audioEffectLibHandle_) {
67 AUDIO_ERR_LOG("<log error> dlsym failed: error: %{public}s", dlerror());
68 dlclose(dlHandle_);
69 dlHandle_ = nullptr;
70 }
71 AUDIO_INFO_LOG("<log info> dlsym lib %{public}s successful", LOUDNESSGAIN_PATH.c_str());
72
73 #ifdef ENABLE_HIDUMP_DFX
74 SetNodeName("hpaeLoudnessGainNode");
75 #endif
76 }
77
~HpaeLoudnessGainNode()78 HpaeLoudnessGainNode::~HpaeLoudnessGainNode()
79 {
80 if (handle_ && audioEffectLibHandle_) {
81 audioEffectLibHandle_->releaseEffect(handle_);
82 handle_ = nullptr;
83 }
84 if (dlHandle_) {
85 dlclose(dlHandle_);
86 dlHandle_ = nullptr;
87 audioEffectLibHandle_ = nullptr;
88 }
89 #ifdef ENABLE_HIDUMP_DFX
90 AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
91 GetNodeId(), GetNodeName().c_str());
92 #endif
93 }
94
SignalProcess(const std::vector<HpaePcmBuffer * > & inputs)95 HpaePcmBuffer *HpaeLoudnessGainNode::SignalProcess(const std::vector<HpaePcmBuffer *> &inputs)
96 {
97 Trace trace("HpaeLoudnessGainNode::SignalProcess");
98
99 CHECK_AND_RETURN_RET_LOG((!inputs.empty()) && inputs[0], &silenceData_,
100 "NodeId %{public}d, sessionId %{public}d input is empty", GetNodeId(), GetSessionId());
101
102 CHECK_AND_RETURN_RET((!IsFloatValueEqual(loudnessGain_, 0.0f)), inputs[0]);
103 CheckUpdateInfo(inputs[0]);
104 if (!dlHandle_ || !audioEffectLibHandle_) {
105 float *pcmDataBuffer = inputs[0]->GetPcmDataBuffer();
106 uint32_t bufferSize = inputs[0]->GetFrameLen() * inputs[0]->GetChannelCount();
107 float *dataBuffer = loudnessGainOutput_.GetPcmDataBuffer();
108 for (uint32_t i = 0; i < bufferSize; i++) {
109 dataBuffer[i] = pcmDataBuffer[i] * linearGain_;
110 }
111 } else {
112 AudioBuffer inBuffer = {
113 .frameLength = inputs[0]->GetFrameLen(),
114 .raw = inputs[0]->GetPcmDataBuffer(),
115 .metaData = nullptr
116 };
117 AudioBuffer outBuffer = {
118 .frameLength = inputs[0]->GetFrameLen(),
119 .raw = loudnessGainOutput_.GetPcmDataBuffer(),
120 .metaData = nullptr
121 };
122 CHECK_AND_RETURN_RET(handle_, inputs[0]);
123 int32_t ret = (*handle_)->process(handle_, &inBuffer, &outBuffer);
124 CHECK_AND_RETURN_RET_LOG(ret == 0, inputs[0], "loudness algo lib process failed");
125 }
126
127 loudnessGainOutput_.SetBufferState(inputs[0]->GetBufferState());
128 return &loudnessGainOutput_;
129 }
130
CheckUpdateInfo(HpaePcmBuffer * input)131 void HpaeLoudnessGainNode::CheckUpdateInfo(HpaePcmBuffer *input)
132 {
133 CHECK_AND_RETURN(pcmBufferInfo_.ch != input->GetChannelCount() ||
134 pcmBufferInfo_.frameLen != input->GetFrameLen() ||
135 pcmBufferInfo_.rate != input->GetSampleRate() ||
136 pcmBufferInfo_.channelLayout != input->GetChannelLayout());
137 AUDIO_INFO_LOG("Update pcmBufferInfo_: channel count: %{public}u -> %{public}u, frame len: %{public}u -> "
138 "%{public}u, sample rate: %{public}u -> %{public}u, channel layout: %{public}" PRIu64 " -> %{public}" PRIu64,
139 pcmBufferInfo_.ch, input->GetChannelCount(), pcmBufferInfo_.frameLen, input->GetFrameLen(),
140 pcmBufferInfo_.rate, input->GetSampleRate(), pcmBufferInfo_.channelLayout, input->GetChannelLayout());
141 pcmBufferInfo_.ch = input->GetChannelCount();
142 pcmBufferInfo_.frameLen = input->GetFrameLen();
143 pcmBufferInfo_.rate = input->GetSampleRate();
144 pcmBufferInfo_.channelLayout = input->GetChannelLayout();
145
146 loudnessGainOutput_.ReConfig(pcmBufferInfo_);
147 silenceData_.ReConfig(pcmBufferInfo_);
148 silenceData_.SetBufferSilence(true);
149 CHECK_AND_RETURN_LOG(handle_, "no handle.");
150
151 uint32_t replyData = 0;
152 AudioEffectConfig ioBufferConfig;
153 AudioEffectTransInfo replyInfo = {sizeof(int32_t), &replyData};
154 AudioEffectTransInfo cmdInfo = {sizeof(AudioEffectConfig), &ioBufferConfig};
155 ioBufferConfig.inputCfg = {SAMPLE_RATE, pcmBufferInfo_.ch, DATA_FORMAT_F32, pcmBufferInfo_.channelLayout,
156 ENCODING_PCM};
157 ioBufferConfig.outputCfg = ioBufferConfig.inputCfg;
158 int32_t ret = (*handle_)->command(handle_, EFFECT_CMD_SET_CONFIG, &cmdInfo, &replyInfo);
159 CHECK_AND_RETURN_LOG(ret == 0, "Loudness algo lib EFFECT_CMD_SET_CONFIG failed");
160 }
161
ReleaseHandle(float loudnessGain)162 int32_t HpaeLoudnessGainNode::ReleaseHandle(float loudnessGain)
163 {
164 AUDIO_INFO_LOG("Releasing handle...");
165 CHECK_AND_RETURN_RET_LOG(handle_, ERROR, "no handle.");
166 int32_t ret = audioEffectLibHandle_->releaseEffect(handle_);
167 CHECK_AND_RETURN_RET_LOG(ret == 0, ERROR, "handle releasing failed.");
168 handle_ = nullptr;
169 loudnessGain_ = loudnessGain;
170 return SUCCESS;
171 }
172
SetLoudnessGain(float loudnessGain)173 int32_t HpaeLoudnessGainNode::SetLoudnessGain(float loudnessGain)
174 {
175 CHECK_AND_RETURN_RET_LOG(!IsFloatValueEqual(loudnessGain_, loudnessGain), SUCCESS,
176 "SetLoudnessGain: Same loudnessGain: %{public}f", loudnessGain);
177 AUDIO_INFO_LOG("loudnessGain changed from %{public}f to %{public}f", loudnessGain_, loudnessGain);
178 if (!dlHandle_ || !audioEffectLibHandle_) {
179 linearGain_ = LoudnessDbToLinearGain(loudnessGain);
180 loudnessGain_ = loudnessGain;
181 return SUCCESS;
182 }
183
184 if (IsFloatValueEqual(loudnessGain, 0.0f)) {
185 return ReleaseHandle(loudnessGain);
186 }
187
188 uint32_t replyData = 0;
189 AudioEffectTransInfo replyInfo = {sizeof(int32_t), &replyData};
190
191 if (IsFloatValueEqual(loudnessGain_, 0.0f)) {
192 AUDIO_INFO_LOG("Creating handle...");
193 int32_t ret = audioEffectLibHandle_->createEffect(LOUDNESS_DESCRIPTOR, &handle_);
194 CHECK_AND_RETURN_RET_LOG(ret == 0, ERROR, "loudness lib handle create failed");
195 AudioEffectConfig ioBufferConfig;
196 AudioEffectTransInfo cmdInfo = {sizeof(AudioEffectConfig), &ioBufferConfig};
197 ioBufferConfig.inputCfg = {SAMPLE_RATE, pcmBufferInfo_.ch, DATA_FORMAT_F32, pcmBufferInfo_.channelLayout,
198 ENCODING_PCM};
199 ioBufferConfig.outputCfg = ioBufferConfig.inputCfg;
200 ret = (*handle_)->command(handle_, EFFECT_CMD_INIT, &cmdInfo, &replyInfo);
201 CHECK_AND_RETURN_RET_LOG(ret == 0, ERROR, "Loudness algo lib EFFECT_CMD_INIT failed");
202 ret = (*handle_)->command(handle_, EFFECT_CMD_ENABLE, &cmdInfo, &replyInfo);
203 CHECK_AND_RETURN_RET_LOG(ret == 0, ERROR, "Loudness algo lib EFFECT_CMD_ENABLE failed");
204 ret = (*handle_)->command(handle_, EFFECT_CMD_SET_CONFIG, &cmdInfo, &replyInfo);
205 CHECK_AND_RETURN_RET_LOG(ret == 0, ERROR, "Loudness algo lib EFFECT_CMD_SET_CONFIG failed");
206 }
207 std::vector<uint8_t> paramBuffer(sizeof(AudioEffectParam) + MAX_PARAM_INDEX * sizeof(int32_t));
208 AudioEffectParam *effectParam = reinterpret_cast<AudioEffectParam*>(paramBuffer.data());
209 effectParam->status = 0;
210 effectParam->paramSize = sizeof(int32_t);
211 effectParam->valueSize = 0;
212 int32_t *data = &(effectParam->data[0]);
213 data[COMMAND_CODE_INDEX] = EFFECT_SET_PARAM;
214 CHECK_AND_RETURN_RET_LOG(memcpy_s(&data[LOUDNESS_GAIN_INDEX], sizeof(float), &loudnessGain, sizeof(float)) == 0,
215 ERROR, "memcpy failed");
216
217 AudioEffectTransInfo cmdInfo = {sizeof(AudioEffectParam) + sizeof(int32_t) * MAX_PARAM_INDEX, effectParam};
218 int32_t ret = (*handle_)->command(handle_, EFFECT_CMD_SET_PARAM, &cmdInfo, &replyInfo);
219 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "Loudness algo lib EFFECT_CMD_SET_PARAM failed");
220 loudnessGain_ = loudnessGain;
221
222 return SUCCESS;
223 }
224
GetLoudnessGain()225 float HpaeLoudnessGainNode::GetLoudnessGain()
226 {
227 return loudnessGain_;
228 }
229
IsLoudnessAlgoOn()230 bool HpaeLoudnessGainNode::IsLoudnessAlgoOn()
231 {
232 return handle_ != nullptr;
233 }
234
235 } // namespace HPAE
236 } // namespace AudioStandard
237 } // namespace OHOS