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 "HpaeMixerNode"
17 #endif
18
19 #include <iostream>
20 #include "hpae_mixer_node.h"
21 #include "hpae_pcm_buffer.h"
22 #include "audio_utils.h"
23 #include "cinttypes"
24 #include "audio_errors.h"
25 #include "audio_effect_log.h"
26
27 namespace OHOS {
28 namespace AudioStandard {
29 namespace HPAE {
30
31 static constexpr uint32_t WAIT_FRAMES_NUM = 5; // wait 5 * 20ms before disconnect
32 static constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2;
33 static constexpr uint32_t DEFAULT_FRAME_LEN = 960;
34 static constexpr uint32_t DEFAULT_SAMPLE_RATE = 48000;
35
HpaeMixerNode(HpaeNodeInfo & nodeInfo)36 HpaeMixerNode::HpaeMixerNode(HpaeNodeInfo &nodeInfo)
37 : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo),
38 pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout),
39 mixedOutput_(pcmBufferInfo_), tmpOutput_(pcmBufferInfo_)
40 {
41 mixedOutput_.SetSplitStreamType(nodeInfo.GetSplitStreamType());
42 #ifdef ENABLE_HIDUMP_DFX
43 SetNodeName("hpaeMixerNode");
44 #endif
45 }
46
~HpaeMixerNode()47 HpaeMixerNode::~HpaeMixerNode()
48 {
49 #ifdef ENABLE_HIDUMP_DFX
50 AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
51 GetNodeId(), GetNodeName().c_str());
52 #endif
53 }
54
Reset()55 bool HpaeMixerNode::Reset()
56 {
57 return HpaePluginNode::Reset();
58 }
59
SetupAudioLimiter()60 int32_t HpaeMixerNode::SetupAudioLimiter()
61 {
62 if (limiter_ != nullptr) {
63 AUDIO_INFO_LOG("NodeId: %{public}d, limiter has already been setup!", GetNodeId());
64 return ERROR;
65 }
66 return InitAudioLimiter();
67 }
68
InitAudioLimiter()69 int32_t HpaeMixerNode::InitAudioLimiter()
70 {
71 limiter_ = std::make_unique<AudioLimiter>(GetNodeId());
72 // limiter only supports float format
73 int32_t ret = limiter_->SetConfig(GetFrameLen() * GetChannelCount() * sizeof(float), sizeof(float), GetSampleRate(),
74 GetChannelCount());
75 if (ret == SUCCESS) {
76 AUDIO_INFO_LOG("NodeId: %{public}d, limiter init sucess!", GetNodeId());
77 } else {
78 limiter_ = nullptr;
79 AUDIO_INFO_LOG("NodeId: %{public}d, limiter init fail!!", GetNodeId());
80 }
81 return ret;
82 }
83
SignalProcess(const std::vector<HpaePcmBuffer * > & inputs)84 HpaePcmBuffer *HpaeMixerNode::SignalProcess(const std::vector<HpaePcmBuffer *> &inputs)
85 {
86 Trace trace("[sceneType:" + std::to_string(GetSceneType()) + "]" + "HpaeMixerNode::SignalProcess");
87 mixedOutput_.Reset();
88
89 if (GetSceneType() != HPAE_SCENE_EFFECT_OUT) {
90 DrainProcess();
91 }
92
93 uint32_t bufferState = PCM_BUFFER_STATE_INVALID | PCM_BUFFER_STATE_SILENCE;
94 if (limiter_ == nullptr) {
95 bool ret = inputs.empty() ? CheckUpdateInfoForDisConnect() : CheckUpdateInfo(inputs[0]);
96 if (ret) {
97 mixedOutput_.ReConfig(pcmBufferInfo_);
98 }
99 for (auto input: inputs) {
100 mixedOutput_ += *input;
101 bufferState &= input->GetBufferState();
102 }
103 } else { // limiter does not support reconfigging frameLen at runtime
104 tmpOutput_.Reset();
105 for (auto input: inputs) {
106 tmpOutput_ += *input;
107 bufferState &= input->GetBufferState();
108 }
109 limiter_->Process(GetFrameLen() * GetChannelCount(),
110 tmpOutput_.GetPcmDataBuffer(), mixedOutput_.GetPcmDataBuffer());
111 }
112 mixedOutput_.SetBufferState(bufferState);
113 return &mixedOutput_;
114 }
115
CheckUpdateInfo(HpaePcmBuffer * input)116 bool HpaeMixerNode::CheckUpdateInfo(HpaePcmBuffer* input)
117 {
118 struct UpdateCheck {
119 std::string name;
120 uint32_t ¤tVal;
121 uint32_t newVal;
122 } checks[] = {
123 {"channel count", pcmBufferInfo_.ch, input->GetChannelCount()},
124 {"frame len", pcmBufferInfo_.frameLen, input->GetFrameLen()},
125 {"sample rate", pcmBufferInfo_.rate, input->GetSampleRate()}
126 };
127
128 bool isPCMBufferInfoUpdated = false;
129
130 for (auto& check : checks) {
131 if (check.currentVal != check.newVal) {
132 AUDIO_INFO_LOG("Update %{public}s: %{public}d -> %{public}d",
133 check.name.c_str(), check.currentVal, check.newVal);
134 check.currentVal = check.newVal;
135 isPCMBufferInfoUpdated = true;
136 }
137 }
138
139 if (pcmBufferInfo_.channelLayout != input->GetChannelLayout()) {
140 AUDIO_INFO_LOG("Update channel layout %{public}" PRIu64 " -> %{public}" PRIu64 "",
141 pcmBufferInfo_.channelLayout, input->GetChannelLayout());
142 pcmBufferInfo_.channelLayout = input->GetChannelLayout();
143 isPCMBufferInfoUpdated = true;
144 }
145
146 // if other bitwidth is supported, add check here
147
148 return isPCMBufferInfoUpdated;
149 }
150
151
CheckUpdateInfoForDisConnect()152 bool HpaeMixerNode::CheckUpdateInfoForDisConnect()
153 {
154 struct UpdateCheck {
155 std::string name;
156 uint32_t ¤tVal;
157 uint32_t newVal;
158 } checks[] = {
159 {"channel count", pcmBufferInfo_.ch, DEFAULT_CHANNEL_COUNT},
160 {"frame len", pcmBufferInfo_.frameLen, DEFAULT_FRAME_LEN},
161 {"sample rate", pcmBufferInfo_.rate, DEFAULT_SAMPLE_RATE}
162 };
163
164 bool isPCMBufferInfoUpdated = false;
165
166 for (auto& check : checks) {
167 if (check.currentVal != check.newVal) {
168 AUDIO_INFO_LOG("Update %{public}s: %{public}d -> %{public}d",
169 check.name.c_str(), check.currentVal, check.newVal);
170 check.currentVal = check.newVal;
171 isPCMBufferInfoUpdated = true;
172 }
173 }
174
175 if (pcmBufferInfo_.channelLayout != AudioChannelLayout::CH_LAYOUT_STEREO) {
176 AUDIO_INFO_LOG("Update channel layout %{public}" PRIu64 " -> %{public}" PRIu64 "",
177 pcmBufferInfo_.channelLayout, AudioChannelLayout::CH_LAYOUT_STEREO);
178 pcmBufferInfo_.channelLayout = AudioChannelLayout::CH_LAYOUT_STEREO;
179 isPCMBufferInfoUpdated = true;
180 }
181
182 // if other bitwidth is supported, add check here
183
184 return isPCMBufferInfoUpdated;
185 }
186
DrainProcess()187 void HpaeMixerNode::DrainProcess()
188 {
189 if (GetPreOutNum() != 0) {
190 waitFrames_ = 0;
191 } else {
192 waitFrames_++;
193 if (waitFrames_ == WAIT_FRAMES_NUM) {
194 waitFrames_ = 0;
195 auto statusCallback = GetNodeStatusCallback().lock();
196 if (statusCallback) {
197 AUDIO_INFO_LOG("trigger callback to disconnect");
198 statusCallback->OnDisConnectProcessCluster(GetSceneType());
199 }
200 }
201 }
202 }
203 } // namespace HPAE
204 } // namespace AudioStandard
205 } // namespace OHOS