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 "HpaeCoBufferNode"
17 #endif
18
19 #include "hpae_co_buffer_node.h"
20 #include "hpae_define.h"
21 #include "audio_effect_log.h"
22
23 namespace OHOS {
24 namespace AudioStandard {
25 namespace HPAE {
26 static constexpr int32_t DEFAULT_FRAME_LEN = 960;
27 static constexpr int32_t MAX_CACHE_SIZE = 500;
28 static constexpr int32_t DEFAULT_FRAME_LEN_MS = 20;
29 static constexpr int32_t MS_PER_SECOND = 1000;
30 static constexpr int32_t TEST_LATENCY = 280;
31 static constexpr int32_t ENQUEUE_DONE_FRAME = 10;
32
HpaeCoBufferNode()33 HpaeCoBufferNode::HpaeCoBufferNode()
34 : HpaeNode(),
35 outputStream_(this),
36 pcmBufferInfo_(STEREO, DEFAULT_FRAME_LEN, SAMPLE_RATE_48000),
37 coBufferOut_(pcmBufferInfo_),
38 silenceData_(pcmBufferInfo_)
39 {
40 #ifdef ENABLE_HIDUMP_DFX
41 SetNodeName("HpaeCoBufferNode");
42 #endif
43 const size_t size = static_cast<size_t>(SAMPLE_RATE_48000) *
44 static_cast<size_t>(STEREO) *
45 sizeof(float) *
46 static_cast<size_t>(MAX_CACHE_SIZE) /
47 static_cast<size_t>(MS_PER_SECOND);
48 AUDIO_INFO_LOG("Created ring cache, size: %{public}zu", size);
49 ringCache_ = AudioRingCache::Create(size);
50 CHECK_AND_RETURN_LOG(ringCache_ != nullptr, "Create ring cache failed");
51 }
52
~HpaeCoBufferNode()53 HpaeCoBufferNode::~HpaeCoBufferNode()
54 {
55 #ifdef ENABLE_HIDUMP_DFX
56 AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
57 GetNodeId(), GetNodeName().c_str());
58 #endif
59 }
60
Enqueue(HpaePcmBuffer * buffer)61 void HpaeCoBufferNode::Enqueue(HpaePcmBuffer* buffer)
62 {
63 std::lock_guard<std::mutex> lock(mutex_);
64
65 #ifdef ENABLE_HOOK_PCM
66 if (inputPcmDumper_ && buffer) {
67 const size_t dumpSize = buffer->GetFrameLen() * sizeof(float) * buffer->GetChannelCount();
68 inputPcmDumper_->Dump(reinterpret_cast<int8_t*>(buffer->GetPcmDataBuffer()), dumpSize);
69 }
70 #endif
71 // process input buffer
72 ProcessInputFrameInner(buffer);
73
74 // process enqueue flag
75 if (enqueueCount_ < ENQUEUE_DONE_FRAME) {
76 enqueueCount_++;
77 } else if (enqueueCount_ == ENQUEUE_DONE_FRAME) {
78 enqueueCount_++;
79 enqueueRunning_ = true;
80 // fill silence frames for latency adjustment
81 AUDIO_INFO_LOG("Filling silence frames for latency adjustment");
82 ringCache_->ResetBuffer();
83 FillSilenceFramesInner(TEST_LATENCY);
84 }
85 }
86
DoProcess()87 void HpaeCoBufferNode::DoProcess()
88 {
89 std::unique_lock<std::mutex> lock(mutex_);
90
91 // write silence data if enqueue is not running
92 if (!enqueueRunning_) {
93 outputStream_.WriteDataToOutput(&silenceData_);
94 return;
95 }
96
97 // process output buffer
98 ProcessOutputFrameInner();
99
100 #ifdef ENABLE_HOOK_PCM
101 if (outputPcmDumper_) {
102 const size_t dumpSize = coBufferOut_.GetFrameLen() * sizeof(float) * coBufferOut_.GetChannelCount();
103 outputPcmDumper_->Dump(reinterpret_cast<int8_t*>(coBufferOut_.GetPcmDataBuffer()), dumpSize);
104 }
105 #endif
106 }
107
Reset()108 bool HpaeCoBufferNode::Reset()
109 {
110 const auto preOutputMap = inputStream_.GetPreOutputMap();
111 for (const auto &preOutput : preOutputMap) {
112 OutputPort<HpaePcmBuffer *> *output = preOutput.first;
113 inputStream_.DisConnect(output);
114 }
115 return true;
116 }
117
ResetAll()118 bool HpaeCoBufferNode::ResetAll()
119 {
120 const auto preOutputMap = inputStream_.GetPreOutputMap();
121 for (const auto &preOutput : preOutputMap) {
122 OutputPort<HpaePcmBuffer *> *output = preOutput.first;
123 std::shared_ptr<HpaeNode> hpaeNode = preOutput.second;
124 if (hpaeNode->ResetAll()) {
125 inputStream_.DisConnect(output);
126 }
127 }
128 return true;
129 }
130
GetSharedInstance()131 std::shared_ptr<HpaeNode> HpaeCoBufferNode::GetSharedInstance()
132 {
133 return shared_from_this();
134 }
135
GetOutputPort()136 OutputPort<HpaePcmBuffer *> *HpaeCoBufferNode::GetOutputPort()
137 {
138 return &outputStream_;
139 }
140
Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)141 void HpaeCoBufferNode::Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer *>> &preNode)
142 {
143 HpaeNodeInfo nodeInfo = preNode->GetNodeInfo();
144 if (connectedProcessCluster_.find(nodeInfo.sceneType) == connectedProcessCluster_.end()) {
145 connectedProcessCluster_.insert(nodeInfo.sceneType);
146 nodeInfo.nodeId = GetNodeId();
147 nodeInfo.nodeName = GetNodeName();
148 SetNodeInfo(nodeInfo);
149 inputStream_.Connect(shared_from_this(), preNode->GetOutputPort(), HPAE_BUFFER_TYPE_COBUFFER);
150 AUDIO_INFO_LOG("HpaeCoBufferNode connect to preNode");
151 }
152
153 // reset status flag
154 enqueueCount_ = 1;
155 enqueueRunning_ = false;
156 #ifdef ENABLE_HOOK_PCM
157 inputPcmDumper_ = std::make_unique<HpaePcmDumper>(
158 "HpaeCoBufferNodeInput_id_" + std::to_string(GetNodeId()) + ".pcm");
159 outputPcmDumper_ = std::make_unique<HpaePcmDumper>(
160 "HpaeCoBufferNodeOutput_id_" + std::to_string(GetNodeId()) + ".pcm");
161 #endif
162 }
163
DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)164 void HpaeCoBufferNode::DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer*>>& preNode)
165 {
166 HpaeNodeInfo nodeInfo = preNode->GetNodeInfo();
167 if (connectedProcessCluster_.find(nodeInfo.sceneType) != connectedProcessCluster_.end()) {
168 connectedProcessCluster_.erase(nodeInfo.sceneType);
169 inputStream_.DisConnect(preNode->GetOutputPort(), HPAE_BUFFER_TYPE_COBUFFER);
170 AUDIO_INFO_LOG("HpaeCoBufferNode disconnected from prenode, scenetype %{public}u", nodeInfo.sceneType);
171 }
172 }
173
SetLatency(uint32_t latency)174 void HpaeCoBufferNode::SetLatency(uint32_t latency)
175 {
176 latency_ = latency;
177 AUDIO_INFO_LOG("latency is %{public}d", latency);
178 }
179
FillSilenceFramesInner(uint32_t latencyMs)180 void HpaeCoBufferNode::FillSilenceFramesInner(uint32_t latencyMs)
181 {
182 CHECK_AND_RETURN_LOG(ringCache_ != nullptr, "Ring cache is null");
183
184 uint32_t offset = 0;
185 const size_t frameSize = silenceData_.GetFrameLen() * silenceData_.GetChannelCount() * sizeof(float);
186
187 while (offset < latencyMs) {
188 // check writable size
189 OptResult result = ringCache_->GetWritableSize();
190 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Get writable size failed");
191 if (result.size < frameSize) {
192 AUDIO_WARNING_LOG("Insufficient space for silence frame: %{public}zu < %{public}zu",
193 result.size, frameSize);
194 break;
195 }
196
197 // create silence frame
198 BufferWrap bufferWrap = {reinterpret_cast<uint8_t *>(silenceData_.GetPcmDataBuffer()), frameSize};
199 result = ringCache_->Enqueue(bufferWrap);
200 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Enqueue silence frame failed");
201 offset += DEFAULT_FRAME_LEN_MS;
202 }
203 AUDIO_INFO_LOG("Filled %{public}u ms of silence frames", offset);
204 }
205
ProcessInputFrameInner(HpaePcmBuffer * buffer)206 void HpaeCoBufferNode::ProcessInputFrameInner(HpaePcmBuffer* buffer)
207 {
208 CHECK_AND_RETURN_LOG(ringCache_ != nullptr && buffer != nullptr,
209 "Ring cache or buffer is null");
210
211 const size_t writeLen = buffer->GetFrameLen() * buffer->GetChannelCount() * sizeof(float);
212
213 // check writable size
214 OptResult result = ringCache_->GetWritableSize();
215 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Get writable size failed");
216 CHECK_AND_RETURN_LOG(result.size >= writeLen,
217 "Insufficient cache space: %{public}zu < %{public}zu", result.size, writeLen);
218
219 // enqueue buffer
220 BufferWrap bufferWrap = {reinterpret_cast<uint8_t*>(buffer->GetPcmDataBuffer()), writeLen};
221 result = ringCache_->Enqueue(bufferWrap);
222 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Enqueue data failed");
223 }
224
ProcessOutputFrameInner()225 void HpaeCoBufferNode::ProcessOutputFrameInner()
226 {
227 CHECK_AND_RETURN_LOG(ringCache_ != nullptr, "Ring cache is null");
228
229 const size_t requestDataLen = static_cast<size_t>(SAMPLE_RATE_48000) *
230 static_cast<size_t>(STEREO) *
231 sizeof(float) *
232 static_cast<size_t>(DEFAULT_FRAME_LEN_MS) /
233 static_cast<size_t>(MS_PER_SECOND);
234
235 // check readable size
236 OptResult result = ringCache_->GetReadableSize();
237 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Get readable size failed");
238
239 if (result.size < requestDataLen) {
240 AUDIO_WARNING_LOG("Insufficient data: %{public}zu < %{public}zu, outputting silence",
241 result.size, requestDataLen);
242 outputStream_.WriteDataToOutput(&silenceData_);
243 } else {
244 // read buffer
245 BufferWrap bufferWrap = {reinterpret_cast<uint8_t *>(coBufferOut_.GetPcmDataBuffer()), requestDataLen};
246 result = ringCache_->Dequeue(bufferWrap);
247 CHECK_AND_RETURN_LOG(result.ret == OPERATION_SUCCESS, "Dequeue data failed");
248 if (result.ret != OPERATION_SUCCESS) {
249 outputStream_.WriteDataToOutput(&silenceData_);
250 } else {
251 outputStream_.WriteDataToOutput(&coBufferOut_);
252 }
253 }
254 }
255
SetOutputClusterConnected(bool isConnect)256 void HpaeCoBufferNode::SetOutputClusterConnected(bool isConnect)
257 {
258 isOutputClusterConnected_ = isConnect;
259 AUDIO_INFO_LOG("HpaeCoBufferNode output cluster connected status: %{public}d", isConnect);
260 }
261
IsOutputClusterConnected()262 bool HpaeCoBufferNode::IsOutputClusterConnected()
263 {
264 return isOutputClusterConnected_;
265 }
266 } // namespace HPAE
267 } // namespace AudioStandard
268 } // namespace OHOS