• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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