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 "HpaeAudioFormatConverterNode"
17 #endif
18
19 #include "hpae_audio_format_converter_node.h"
20 #include "audio_utils.h"
21 #include <cinttypes>
22 #include "audio_effect_log.h"
23
24 static constexpr uint32_t FRAME_LEN_20MS = 20;
25 static constexpr uint32_t MS_IN_SECOND = 1000;
26 static constexpr uint32_t REASAMPLE_QUAILTY = 1;
27 namespace OHOS {
28 namespace AudioStandard {
29 namespace HPAE {
HpaeAudioFormatConverterNode(HpaeNodeInfo preNodeInfo,HpaeNodeInfo nodeInfo)30 HpaeAudioFormatConverterNode::HpaeAudioFormatConverterNode(HpaeNodeInfo preNodeInfo, HpaeNodeInfo nodeInfo)
31 : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo),
32 pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout),
33 converterOutput_(pcmBufferInfo_), preNodeInfo_(preNodeInfo), tmpOutBuf_(pcmBufferInfo_)
34 {
35 converterOutput_.SetSplitStreamType(preNodeInfo.GetSplitStreamType());
36 UpdateTmpOutPcmBufferInfo(pcmBufferInfo_);
37 // use ProResamppler as default
38 resampler_ = std::make_unique<ProResampler>(preNodeInfo_.samplingRate, nodeInfo.samplingRate,
39 std::min(preNodeInfo_.channels, nodeInfo.channels), REASAMPLE_QUAILTY);
40
41 AudioChannelInfo inChannelInfo = {
42 .channelLayout = preNodeInfo.channelLayout,
43 .numChannels = preNodeInfo.channels,
44 };
45 AudioChannelInfo outChannelInfo = {
46 .channelLayout = nodeInfo.channelLayout,
47 .numChannels = nodeInfo.channels,
48 };
49
50 // for now, work at float32le by default
51 channelConverter_.SetParam(inChannelInfo, outChannelInfo, SAMPLE_F32LE, true);
52 AUDIO_INFO_LOG("node id %{public}d, sessionid %{public}d, "
53 "input: bitformat %{public}d, frameLen %{public}d, sample rate %{public}d, channels %{public}d,"
54 "channelLayout %{public}" PRIu64 ", output: bitformat %{public}d, frameLen %{public}d, sample rate %{public}d,"
55 "channels %{public}d, channelLayout %{public}" PRIu64 "", GetNodeId(), GetSessionId(),
56 preNodeInfo.format, preNodeInfo.frameLen, preNodeInfo.samplingRate, inChannelInfo.numChannels,
57 inChannelInfo.channelLayout, nodeInfo.format, nodeInfo.frameLen, nodeInfo.samplingRate,
58 outChannelInfo.numChannels, outChannelInfo.channelLayout);
59 #ifdef ENABLE_HOOK_PCM
60 outputPcmDumper_ = std::make_unique<HpaePcmDumper>(
61 "HpaeConverterNodeOutput_id_" + std::to_string(GetSessionId()) +
62 + "_nodeId_" + std::to_string(GetNodeId()) +
63 "_ch_" + std::to_string(GetChannelCount()) + "_rate_" +
64 std::to_string(GetSampleRate()) + "_" + GetTime() + ".pcm");
65 #endif
66
67 #ifdef ENABLE_HIDUMP_DFX
68 SetNodeName("hpaeAudioFormatConverterNode");
69 #endif
70 }
71
~HpaeAudioFormatConverterNode()72 HpaeAudioFormatConverterNode::~HpaeAudioFormatConverterNode()
73 {
74 #ifdef ENABLE_HIDUMP_DFX
75 AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
76 GetNodeId(), GetNodeName().c_str());
77 #endif
78 }
79
RegisterCallback(INodeFormatInfoCallback * callback)80 void HpaeAudioFormatConverterNode::RegisterCallback(INodeFormatInfoCallback *callback)
81 {
82 nodeFormatInfoCallback_ = callback;
83 }
84
SignalProcess(const std::vector<HpaePcmBuffer * > & inputs)85 HpaePcmBuffer *HpaeAudioFormatConverterNode::SignalProcess(const std::vector<HpaePcmBuffer *> &inputs)
86 {
87 auto rate = "rate[" + std::to_string(GetSampleRate()) + "]_";
88 auto ch = "ch[" + std::to_string(GetChannelCount()) + "]_";
89 auto len = "len[" + std::to_string(GetFrameLen()) + "]";
90 Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeAudioFormatConverterNode::SignalProcess "
91 + rate + ch + len);
92 if (inputs.empty() || inputs[0] == nullptr) {
93 AUDIO_WARNING_LOG("HpaeConverterNode inputs size is empty, SessionId:%{public}d", GetSessionId());
94 return &silenceData_;
95 }
96 if (inputs.size() != 1) {
97 AUDIO_WARNING_LOG("error inputs size is not eqaul to 1, SessionId:%{public}d", GetSessionId());
98 }
99 CHECK_AND_RETURN_RET_LOG(resampler_, &silenceData_, "NodeId %{public}d resampler_ is nullptr", GetNodeId());
100
101 // make sure size of silenceData_, tmpOutput_, and ConverterOutput_ is correct
102 CheckAndUpdateInfo(inputs[0]);
103 // pass valid tag to next node
104 if (!inputs[0]->IsValid()) {
105 return &silenceData_;
106 }
107 float *srcData = (*(inputs[0])).GetPcmDataBuffer();
108 converterOutput_.Reset();
109 tmpOutBuf_.Reset();
110
111 float *dstData = converterOutput_.GetPcmDataBuffer();
112 float *tmpData = tmpOutBuf_.GetPcmDataBuffer();
113
114 if (resampler_ == nullptr) {
115 return &silenceData_;
116 }
117 int32_t ret = ConverterProcess(srcData, dstData, tmpData, inputs[0]);
118 if (ret != EOK) {
119 AUDIO_ERR_LOG("NodeId %{public}d, sessionId %{public}d, Format Converter fail to process!",
120 GetNodeId(), GetSessionId());
121 return &silenceData_;
122 }
123
124 #ifdef ENABLE_HOOK_PCM
125 if (outputPcmDumper_ != nullptr) {
126 outputPcmDumper_->CheckAndReopenHandle();
127 outputPcmDumper_->Dump((int8_t *)dstData,
128 converterOutput_.GetFrameLen() * sizeof(float) * channelConverter_.GetOutChannelInfo().numChannels);
129 }
130 #endif
131 converterOutput_.SetBufferState(inputs[0]->GetBufferState());
132 return &converterOutput_;
133 }
134
ConverterProcess(float * srcData,float * dstData,float * tmpData,HpaePcmBuffer * input)135 int32_t HpaeAudioFormatConverterNode::ConverterProcess(float *srcData, float *dstData, float *tmpData,
136 HpaePcmBuffer *input)
137 {
138 AudioChannelInfo inChannelInfo = channelConverter_.GetInChannelInfo();
139 AudioChannelInfo outChannelInfo = channelConverter_.GetOutChannelInfo();
140 uint32_t inRate = resampler_->GetInRate();
141 uint32_t outRate = resampler_->GetOutRate();
142
143 uint32_t inputFrameLen = preNodeInfo_.frameLen;
144 uint32_t outputFrameLen = converterOutput_.GetFrameLen();
145 uint32_t inputFrameBytes = inputFrameLen * inChannelInfo.numChannels * sizeof(float);
146 uint32_t outputFrameBytes = outputFrameLen * outChannelInfo.numChannels * sizeof(float);
147 int32_t ret = EOK;
148
149 if ((inChannelInfo.numChannels == outChannelInfo.numChannels) && (inRate == outRate)) {
150 ret = memcpy_s(dstData, outputFrameBytes, srcData, inputFrameBytes);
151 } else if (inChannelInfo.numChannels == outChannelInfo.numChannels) {
152 ret = resampler_->Process(srcData, inputFrameLen, dstData, outputFrameLen);
153 } else if (inRate == outRate) {
154 // for 11025, input output rate cannot be the same now
155 ret = channelConverter_.Process(inputFrameLen, srcData, (*input).Size(), dstData, converterOutput_.Size());
156 } else if (inChannelInfo.numChannels > outChannelInfo.numChannels) { // convert, then resample
157 ret = channelConverter_.Process(inputFrameLen, srcData, (*input).Size(), tmpData, tmpOutBuf_.Size());
158 ret += resampler_->Process(tmpData, inputFrameLen, dstData, outputFrameLen);
159 } else { // output channels larger than input channels, resample, then convert
160 ret = resampler_->Process(srcData, inputFrameLen, tmpData, outputFrameLen);
161 ret += channelConverter_.Process(outputFrameLen, tmpData, tmpOutBuf_.Size(), dstData, converterOutput_.Size());
162 }
163 return ret;
164 }
165
166 // return true if output info is updated
CheckUpdateOutInfo()167 bool HpaeAudioFormatConverterNode::CheckUpdateOutInfo()
168 {
169 if (nodeFormatInfoCallback_ == nullptr) {
170 return false;
171 }
172
173 AudioBasicFormat basicFormat;
174 basicFormat.rate = preNodeInfo_.samplingRate;
175
176 // if there exists an effect node, converter node output is common input of loudness node and effectnode
177 // Must check loudness node input before effectnode
178 nodeFormatInfoCallback_->GetNodeInputFormatInfo(preNodeInfo_.sessionId, basicFormat);
179
180 uint32_t numChannels = basicFormat.audioChannelInfo.numChannels;
181 AudioChannelLayout channelLayout = basicFormat.audioChannelInfo.channelLayout;
182 AudioSamplingRate sampleRate = basicFormat.rate;
183 if (numChannels == 0) {
184 // set to node info, which is device output info
185 AUDIO_WARNING_LOG("Fail to check format info from down stream nodes");
186 numChannels = GetChannelCount();
187 channelLayout = GetChannelLayout();
188 sampleRate = GetSampleRate();
189 }
190
191 AudioChannelInfo curOutChannelInfo = channelConverter_.GetOutChannelInfo();
192 if ((curOutChannelInfo.numChannels == numChannels) && (curOutChannelInfo.channelLayout == channelLayout) &&
193 (sampleRate == resampler_->GetOutRate())) {
194 return false;
195 }
196 // update channel info
197 if (curOutChannelInfo.numChannels != numChannels || curOutChannelInfo.channelLayout != channelLayout) {
198 AudioChannelInfo newOutChannelInfo = {
199 .channelLayout = (AudioChannelLayout)channelLayout,
200 .numChannels = numChannels,
201 };
202 AUDIO_INFO_LOG("NodeId %{public}d, update out channels and channelLayout: channels %{public}d -> %{public}d",
203 GetNodeId(), curOutChannelInfo.numChannels, numChannels);
204 CHECK_AND_RETURN_RET_LOG(channelConverter_.SetOutChannelInfo(newOutChannelInfo) == DMIX_ERR_SUCCESS, false,
205 "NodeId: %{public}d, Fail to set output channel info from effectNode!", GetNodeId());
206 uint32_t resampleChannels = std::min(channelConverter_.GetInChannelInfo().numChannels, numChannels);
207 if (resampleChannels != resampler_->GetChannels()) {
208 AUDIO_INFO_LOG("NodeId: %{public}d, Update resampler work channel from effectNode!", GetNodeId());
209 resampler_->UpdateChannels(resampleChannels);
210 }
211 }
212 // update sample rate
213 if (resampler_->GetOutRate() != sampleRate) {
214 AUDIO_INFO_LOG("NodeId: %{public}d, update output sample rate: %{public}d -> %{public}d",
215 GetNodeId(), resampler_->GetOutRate(), sampleRate);
216 resampler_->UpdateRates(preNodeInfo_.samplingRate, sampleRate);
217 }
218
219 HpaeNodeInfo nodeInfo = GetNodeInfo();
220 nodeInfo.channels = (AudioChannel)numChannels;
221 nodeInfo.channelLayout = (AudioChannelLayout)channelLayout;
222 nodeInfo.samplingRate = (AudioSamplingRate)resampler_->GetOutRate();
223 SetNodeInfo(nodeInfo);
224 return true;
225 }
226
227 // update channel info from processCluster. For now sample rate will not change
CheckUpdateInInfo(HpaePcmBuffer * input)228 bool HpaeAudioFormatConverterNode::CheckUpdateInInfo(HpaePcmBuffer *input)
229 {
230 uint32_t numChannels = input->GetChannelCount();
231 uint64_t channelLayout = input->GetChannelLayout();
232 uint32_t sampleRate = input->GetSampleRate();
233 AudioChannelInfo curInChannelInfo = channelConverter_.GetInChannelInfo();
234 bool isInfoUpdated = false;
235 // update channels and channelLayout
236 if ((curInChannelInfo.numChannels != numChannels) || (curInChannelInfo.channelLayout != channelLayout)) {
237 AUDIO_INFO_LOG("NodeId %{public}d: Update innput channel info from pcmBufferInfo, "
238 "channels: %{public}d -> %{public}d, channellayout: %{public}" PRIu64 " -> %{public}" PRIu64 ".",
239 GetNodeId(), curInChannelInfo.numChannels, numChannels, curInChannelInfo.channelLayout, channelLayout);
240
241 AudioChannelInfo newInChannelInfo = {
242 .channelLayout = (AudioChannelLayout)channelLayout,
243 .numChannels = numChannels,
244 };
245 channelConverter_.SetInChannelInfo(newInChannelInfo);
246 preNodeInfo_.channelLayout = (AudioChannelLayout)channelLayout;
247 preNodeInfo_.channels = (AudioChannel)numChannels;
248
249 uint32_t resampleChannels = std::min(numChannels, channelConverter_.GetOutChannelInfo().numChannels);
250 if (resampleChannels != resampler_->GetChannels()) {
251 AUDIO_INFO_LOG("NodeId %{public}d: Update resampler work channel from effectNode!", GetNodeId());
252 resampler_->UpdateChannels(resampleChannels);
253 }
254 isInfoUpdated = true;
255 }
256 // update sample rate
257 if (sampleRate != resampler_->GetInRate()) {
258 AUDIO_INFO_LOG("NodeId %{public}d: Update resampler input sample rate: %{public}d -> %{public}d",
259 GetNodeId(), resampler_->GetInRate(), sampleRate);
260 preNodeInfo_.frameLen = input->GetFrameLen();
261 preNodeInfo_.samplingRate = (AudioSamplingRate)sampleRate;
262 resampler_->UpdateRates(sampleRate, resampler_->GetOutRate());
263 isInfoUpdated = true;
264 }
265 // special case for 11025, frameLen is 441, 0, 441, 0... alternating
266 // do not influence isInfoUpdated flag, which is used for update tmp data length
267 if (preNodeInfo_.samplingRate == SAMPLE_RATE_11025) {
268 preNodeInfo_.frameLen = input->GetFrameLen();
269 }
270 return isInfoUpdated;
271 }
272
UpdateTmpOutPcmBufferInfo(const PcmBufferInfo & outPcmBufferInfo)273 void HpaeAudioFormatConverterNode::UpdateTmpOutPcmBufferInfo(const PcmBufferInfo &outPcmBufferInfo)
274 {
275 if (outPcmBufferInfo.ch == preNodeInfo_.channels || outPcmBufferInfo.rate == preNodeInfo_.samplingRate) {
276 // do not need tmpOutput Buffer
277 return;
278 }
279 PcmBufferInfo tmpOutPcmBufferInfo = outPcmBufferInfo;
280 if (outPcmBufferInfo.ch < preNodeInfo_.channels) { // downmix, then resample
281 tmpOutPcmBufferInfo.rate = preNodeInfo_.samplingRate;
282 tmpOutPcmBufferInfo.frameLen = preNodeInfo_.frameLen;
283 } else { // resample, then upmix
284 tmpOutPcmBufferInfo.ch = preNodeInfo_.channels;
285 }
286 AUDIO_INFO_LOG("NodeId: %{public}d, updated tmp buffer rate %{public}d, frameLen %{public}d, channels %{public}d",
287 GetNodeId(), tmpOutPcmBufferInfo.rate, tmpOutPcmBufferInfo.frameLen, tmpOutPcmBufferInfo.ch);
288 tmpOutBuf_.ReConfig(tmpOutPcmBufferInfo);
289 }
290
291
CheckAndUpdateInfo(HpaePcmBuffer * input)292 void HpaeAudioFormatConverterNode::CheckAndUpdateInfo(HpaePcmBuffer *input)
293 {
294 bool isInfoUpdated = CheckUpdateInInfo(input);
295 bool isOutInfoUpdated = CheckUpdateOutInfo();
296 if ((!isInfoUpdated) && (!isOutInfoUpdated)) {
297 return;
298 }
299
300 AudioChannelInfo outChannelInfo = channelConverter_.GetOutChannelInfo();
301 PcmBufferInfo outPcmBufferInfo = pcmBufferInfo_; // isMultiFrames_ and frame_ are inheritated from sinkInputNode
302 outPcmBufferInfo.ch = outChannelInfo.numChannels;
303 outPcmBufferInfo.rate = resampler_->GetOutRate();
304 outPcmBufferInfo.frameLen = preNodeInfo_.frameLen * resampler_->GetOutRate() / resampler_->GetInRate();
305 outPcmBufferInfo.channelLayout = outChannelInfo.channelLayout;
306
307 if (preNodeInfo_.samplingRate == SAMPLE_RATE_11025) {
308 // for 11025, fix out frameLen based on output sample rate and fixed frameLen 20ms
309 outPcmBufferInfo.frameLen = resampler_->GetOutRate() * FRAME_LEN_20MS / MS_IN_SECOND;
310 }
311
312 AUDIO_INFO_LOG("NodeId %{public}d: output or input format info is changed, update tmp PCM buffer info!",
313 GetNodeId());
314 UpdateTmpOutPcmBufferInfo(outPcmBufferInfo);
315
316 if (isOutInfoUpdated) {
317 AUDIO_INFO_LOG("NodeId %{public}d: output format info is changed, update output PCM buffer info!", GetNodeId());
318 converterOutput_.ReConfig(outPcmBufferInfo);
319 silenceData_.ReConfig(outPcmBufferInfo);
320 // reconfig need reset valid
321 silenceData_.SetBufferValid(false);
322 silenceData_.SetBufferSilence(true);
323 #ifdef ENABLE_HIDUMP_DFX
324 if (auto callBack = GetNodeStatusCallback().lock()) {
325 callBack->OnNotifyDfxNodeInfoChanged(GetNodeId(), GetNodeInfo());
326 }
327 #endif
328 // update PCM dumper
329 #ifdef ENABLE_HOOK_PCM
330 outputPcmDumper_ = std::make_unique<HpaePcmDumper>(
331 "HpaeConverterNodeOutput_id_" + std::to_string(GetSessionId()) +
332 + "_nodeId_" + std::to_string(GetNodeId()) +
333 "_ch_" + std::to_string(GetChannelCount()) + "_rate_" +
334 std::to_string(GetSampleRate()) + "_" + GetTime() + ".pcm");
335 #endif
336 }
337 }
338
ConnectWithInfo(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode,HpaeNodeInfo & nodeInfo)339 void HpaeAudioFormatConverterNode::ConnectWithInfo(const std::shared_ptr<OutputNode<HpaePcmBuffer*>> &preNode,
340 HpaeNodeInfo &nodeInfo)
341 {
342 inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort(nodeInfo));
343 converterOutput_.SetSourceBufferType(nodeInfo.sourceBufferType);
344 #ifdef ENABLE_HIDUMP_DFX
345 if (auto callback = GetNodeStatusCallback().lock()) {
346 callback->OnNotifyDfxNodeInfo(true, preNode->GetSharedInstance()->GetNodeId(), GetNodeInfo());
347 }
348 #endif
349 }
DisConnectWithInfo(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode,HpaeNodeInfo & nodeInfo)350 void HpaeAudioFormatConverterNode::DisConnectWithInfo(const std::shared_ptr<OutputNode<HpaePcmBuffer*>> &preNode,
351 HpaeNodeInfo &nodeInfo)
352 {
353 inputStream_.DisConnect(preNode->GetOutputPort(nodeInfo, true));
354 #ifdef ENABLE_HIDUMP_DFX
355 if (auto callback = GetNodeStatusCallback().lock()) {
356 callback->OnNotifyDfxNodeInfo(false, GetNodeId(), GetNodeInfo());
357 }
358 #endif
359 }
360 } // Hpae
361 } // AudioStandard
362 } // OHOS