• 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 "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