diff --git a/frameworks/native/audioadapter/BUILD.gn b/frameworks/native/audioadapter/BUILD.gn index e728a0aa3e56c84928f8a34693061dc03d2ca168..8c32c55f3e75bbb9ca074855201dedfba5a4ae4c 100644 --- a/frameworks/native/audioadapter/BUILD.gn +++ b/frameworks/native/audioadapter/BUILD.gn @@ -49,6 +49,7 @@ ohos_shared_library("pulse_audio_service_adapter") { ] deps = [ + "../../../services/audio_engine:audio_engine_manager", "../../../services/audio_service:audio_common", "../audioutils:audio_utils", ] diff --git a/services/audio_engine/BUILD.gn b/services/audio_engine/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5987a6e52711bc68c21fe83f3c02f34b5e317003 --- /dev/null +++ b/services/audio_engine/BUILD.gn @@ -0,0 +1,234 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +ohos_shared_library("audio_engine_utils") { + stack_protector_ret = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + install_enable = true + + sources = [ + "buffer/hpae_pcm_buffer.cpp", + "buffer/hpae_pcm_process.cpp", + "dfx/hpae_dfx_tree.cpp", + "simd/simd_utils.cpp", + "utils/hpae_format_convert.cpp", + "utils/hpae_no_lock_queue.cpp", + "utils/hpae_pcm_dumper.cpp", + ] + + include_dirs = [ + "buffer", + "simd", + "utils", + "dfx", + "buffer", + "manager/include", + "../audio_service/server/include", + "../../interfaces/inner_api/native/audiocommon/include", + "../../frameworks/native/audioutils/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-D_FORTIFY_SOURCE=2 -O2", + ] + + deps = [ "../../frameworks/native/audioutils:audio_utils" ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "audio_framework" +} + +ohos_shared_library("audio_engine_plugins") { + stack_protector_ret = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + install_enable = true + + sources = [ + "plugin/bitdepth_converter/bitdepth_converter.c", + "plugin/channel_converter/src/channel_converter.cpp", + "plugin/channel_converter/src/down_mixer.cpp", + "plugin/resample/proresampler/audio_proresampler.cpp", + "plugin/resample/proresampler/audio_proresampler_process.c", + ] + + include_dirs = [ + "plugin/resample/include", + "../../interfaces/inner_api/native/audiocommon/include", + "plugin/channel_converter/include", + "pulgin/bitdepth_converter", + ] + + cflags = [ + "-Wall", + "-Werror", + "-D_FORTIFY_SOURCE=2 -O2", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "audio_framework" +} + +config("audio_engine_node_config") { + include_dirs = [ + "node/include", + "manager/include", + "buffer", + "simd", + "utils", + "dfx", + "plugin/resample/include", + "plugin/channel_converter/include", + "plugin/bitdepth_converter", + "../audio_service/common/include", + "../audio_service/server/include", + "../audio_policy/util/include", + "../../frameworks/native/audioeffect/include", + "../../interfaces/inner_api/native/audiocommon/include", + "../../frameworks/native/hdiadapter_new/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-D_FORTIFY_SOURCE=2 -O2", + ] + cflags += [ "-Os" ] + cflags += [ "-DENABLE_HOOK_PCM" ] + cflags += [ "-DENABLE_HIDUMP_DFX" ] + cflags_cc = cflags +} + +ohos_shared_library("audio_engine_node") { + stack_protector_ret = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + install_enable = true + + configs = [ ":audio_engine_node_config" ] + + sources = [ + "node/src/hpae_audio_format_converter_node.cpp", + "node/src/hpae_capture_effect_node.cpp", + "node/src/hpae_gain_node.cpp", + "node/src/hpae_inner_cap_sink_node.cpp", + "node/src/hpae_mixer_node.cpp", + "node/src/hpae_node_common.cpp", + "node/src/hpae_offload_sinkoutput_node.cpp", + "node/src/hpae_output_cluster.cpp", + "node/src/hpae_plugin_node.cpp", + "node/src/hpae_process_cluster.cpp", + "node/src/hpae_render_effect_node.cpp", + "node/src/hpae_resample_node.cpp", + "node/src/hpae_sink_input_node.cpp", + "node/src/hpae_sink_output_node.cpp", + "node/src/hpae_source_input_cluster.cpp", + "node/src/hpae_source_input_node.cpp", + "node/src/hpae_source_output_node.cpp", + "node/src/hpae_source_process_cluster.cpp", + ] + + deps = [ + ":audio_engine_plugins", + ":audio_engine_utils", + "../../frameworks/native/audioeffect:audio_effect", + "../../frameworks/native/audioutils:audio_utils", + "../../frameworks/native/hdiadapter_new:hdiadapter_new", + "../audio_policy:audio_foundation", + "../audio_service:audio_common", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "audio_framework" +} + +ohos_shared_library("audio_engine_manager") { + stack_protector_ret = true + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + install_enable = true + + configs = [ ":audio_engine_node_config" ] + + include_dirs = [ + "manager/include", + "../audio_policy/server/include/service/common", + "../../frameworks/native/audioeffect/include", + "../../frameworks/native/audioschedule/include", + ] + + sources = [ + "manager/src/hpae_capturer_manager.cpp", + "manager/src/hpae_inner_capturer_manager.cpp", + "manager/src/hpae_manager.cpp", + "manager/src/hpae_offload_renderer_manager.cpp", + "manager/src/hpae_policy_manager.cpp", + "manager/src/hpae_renderer_manager.cpp", + "manager/src/hpae_signal_process_thread.cpp", + "manager/src/i_hpae_manager.cpp", + "manager/src/i_hpae_renderer_manager.cpp", + ] + + deps = [ + ":audio_engine_node", + ":audio_engine_utils", + "../../frameworks/native/audioeffect:audio_effect", + "../../frameworks/native/audioschedule:audio_schedule", + "../../frameworks/native/audioutils:audio_utils", + "../audio_policy:audio_foundation", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "audio_framework" +} diff --git a/services/audio_engine/buffer/hpae_pcm_buffer.cpp b/services/audio_engine/buffer/hpae_pcm_buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f0fd9623f06c500c6f321b131ce5ea243439582 --- /dev/null +++ b/services/audio_engine/buffer/hpae_pcm_buffer.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaePcmBuffer" +#endif + +#include "securec.h" +#include "simd_utils.h" +#include "hpae_pcm_buffer.h" +#include "audio_engine_log.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaePcmBuffer::HpaePcmBuffer(PcmBufferInfo &pcmBufferInfo) : pcmBufferInfo_(pcmBufferInfo) +{ + InitPcmProcess(); +} + +HpaePcmBuffer &HpaePcmBuffer::operator=(HpaePcmBuffer &other) +{ + if (this != &other) { + pcmBufferInfo_ = other.pcmBufferInfo_; + InitPcmProcess(); + int32_t ret = memcpy_s(GetPcmDataBuffer(), bufferByteSize_, other.GetPcmDataBuffer(), bufferByteSize_); + if (ret != 0) { + AUDIO_ERR_LOG("memcpy failed when copy PcmBuffer"); + } + } + return *this; +} + +HpaePcmBuffer::HpaePcmBuffer(HpaePcmBuffer &&other) +{ + pcmBufferInfo_ = other.pcmBufferInfo_; + bufferByteSize_ = other.bufferByteSize_; + bufferFloatSize_ = other.bufferFloatSize_; + pcmDataBuffer_ = std::move(other.pcmDataBuffer_); + pcmProcessVec_ = std::move(other.pcmProcessVec_); + other.pcmBufferInfo_.frames = 0; + other.bufferByteSize_ = 0; + other.bufferFloatSize_ = 0; +} + +void HpaePcmBuffer::InitPcmProcess() +{ + size_t ch = GetChannelCount(); + size_t frameLen = GetFrameLen(); + size_t frames = GetFrames(); + size_t addBytes = MEMORY_ALIGN_BYTE_NUM - (frameLen * sizeof(float) * ch) % MEMORY_ALIGN_BYTE_NUM; + frameByteSize_ = frameLen * sizeof(float) * ch + addBytes; + frameFloatSize_ = frameByteSize_ / sizeof(float); + bufferByteSize_ = frameByteSize_ * frames; + bufferFloatSize_ = frameFloatSize_ * frames; + frameSample_ = frameLen * ch; + pcmDataBuffer_.resize(bufferFloatSize_); + readPos_.store(0); + writePos_.store(0); + curFrames_.store(0); + pcmProcessVec_.clear(); + pcmProcessVec_.reserve(frames); + float *itr = pcmDataBuffer_.data(); + for (size_t i = 0; i < frames; ++i) { + pcmProcessVec_.push_back(HpaePcmProcess(itr, frameSample_)); + itr += frameFloatSize_; + } +} + +HpaePcmBuffer &HpaePcmBuffer::operator+=(HpaePcmBuffer &other) +{ + for (size_t i = 0; i < pcmProcessVec_.size(); ++i) { + pcmProcessVec_[i] += other[i]; + } + return *this; +} + +HpaePcmBuffer &HpaePcmBuffer::operator-=(HpaePcmBuffer &other) +{ + for (size_t i = 0; i < pcmProcessVec_.size(); ++i) { + pcmProcessVec_[i] -= other[i]; + } + return *this; +} + +HpaePcmBuffer &HpaePcmBuffer::operator*=(HpaePcmBuffer &other) +{ + for (size_t i = 0; i < pcmProcessVec_.size(); ++i) { + pcmProcessVec_[i] *= other[i]; + } + return *this; +} + +HpaePcmBuffer &HpaePcmBuffer::operator=(const std::vector> &other) +{ + for (size_t i = 0; i < other.size() && i < pcmProcessVec_.size(); ++i) { + if (IsMultiFrames()) { + if (curFrames_.load() < GetFrames()) { + pcmProcessVec_[i + writePos_.load()] = other[i]; + writePos_.store((writePos_.load() + 1) % GetFrames()); + curFrames_.fetch_add(1); + } else { + AUDIO_WARNING_LOG("HpaePcmBuffer::operator=, frames is full index = %{public}zu", i); + } + } else { + pcmProcessVec_[i] = other[i]; + } + } + return *this; +} + +HpaePcmBuffer &HpaePcmBuffer::operator=(const std::vector &other) +{ + if (IsMultiFrames()) { + if (curFrames_.load() < GetFrames()) { + pcmProcessVec_[writePos_.load()] = other; + writePos_.store((writePos_.load() + 1) % GetFrames()); + curFrames_.fetch_add(1); + } else { + AUDIO_WARNING_LOG("HpaePcmBuffer::operator=, frames is full"); + } + } else { + pcmProcessVec_[0] = other; + } + return *this; +} + +void HpaePcmBuffer::Reset() +{ + for (HpaePcmProcess &pcmProc : pcmProcessVec_) { + pcmProc.Reset(); + } +} + +bool HpaePcmBuffer::GetFrameData(std::vector &frameData) +{ + if (!IsMultiFrames()) { + return false; + } + + if (curFrames_.load() <= 0) { + AUDIO_WARNING_LOG("GetFrameData vector frames is empty"); + return false; + } + int32_t ret = memcpy_s(frameData.data(), + sizeof(float) * frameData.size(), + pcmProcessVec_[readPos_.load()].Begin(), + frameSample_ * sizeof(float)); + if (ret != 0) { + return false; + } + readPos_.store((readPos_.load() + 1) % GetFrames()); + curFrames_.fetch_sub(1); + return true; +} +// frameData is not MultiFrames +bool HpaePcmBuffer::GetFrameData(HpaePcmBuffer &frameData) +{ + if (!IsMultiFrames() || frameData.IsMultiFrames()) { + return false; + } + + if (curFrames_.load() <= 0) { + AUDIO_WARNING_LOG("GetFrameData HpaePcmBuffer frames is empty"); + return false; + } + int32_t ret = memcpy_s(frameData.GetPcmDataBuffer(), + sizeof(float) * frameData.Size(), + pcmProcessVec_[readPos_.load()].Begin(), + frameSample_ * sizeof(float)); + if (ret != 0) { + return false; + } + readPos_.store((readPos_.load() + 1) % GetFrames()); + curFrames_.fetch_sub(1); + return true; +} + +bool HpaePcmBuffer::PushFrameData(std::vector &frameData) +{ + if (!IsMultiFrames()) { + return false; + } + + if (curFrames_.load() >= GetFrames()) { + AUDIO_WARNING_LOG("PushFrameData vector frames is full"); + return false; + } + int32_t ret = memcpy_s(pcmProcessVec_[writePos_.load()].Begin(), frameByteSize_, + frameData.data(), sizeof(float) * frameData.size()); + if (ret != 0) { + AUDIO_ERR_LOG("memcpy failed when PushFrameData"); + return false; + } + writePos_.store((writePos_.load() + 1) % GetFrames()); + curFrames_.fetch_add(1); + return true; +} + +bool HpaePcmBuffer::PushFrameData(HpaePcmBuffer &frameData) +{ + if (!IsMultiFrames() || frameData.IsMultiFrames()) { + return false; + } + + if (curFrames_.load() >= GetFrames()) { + AUDIO_WARNING_LOG("PushFrameData HpaePcmBuffer frames is full"); + return false; + } + int32_t ret = memcpy_s(pcmProcessVec_[writePos_.load()].Begin(), frameByteSize_, + frameData.GetPcmDataBuffer(), frameData.Size()); + if (ret != 0) { + AUDIO_ERR_LOG("memcpy failed when PushFrameData"); + return false; + } + writePos_.store((writePos_.load() + 1) % GetFrames()); + curFrames_.fetch_add(1); + return true; +} + +bool HpaePcmBuffer::StoreFrameData(HpaePcmBuffer &frameData) +{ + if (!IsMultiFrames() || frameData.IsMultiFrames()) { + return false; + } + + int32_t ret = memcpy_s(pcmProcessVec_[writePos_.load()].Begin(), frameByteSize_, + frameData.GetPcmDataBuffer(), frameData.Size()); + if (ret != 0) { + AUDIO_ERR_LOG("memcpy failed when StoreFrameData"); + return false; + } + writePos_.store((writePos_.load() + 1) % GetFrames()); + readPos_.store((readPos_.load() + 1) % GetFrames()); + return true; +} + +size_t HpaePcmBuffer::RewindBuffer(size_t frames) +{ + if (!IsMultiFrames()) { + return 0; + } + frames = curFrames_.load() + frames > GetFrames() ? GetFrames() - curFrames_.load() : frames; + readPos_.store((readPos_.load() - frames + GetFrames()) % GetFrames()); + curFrames_.fetch_add(frames); + return frames; +} + +bool HpaePcmBuffer::UpdateReadPos(size_t readPos) +{ + readPos_.store(readPos); + return true; +} + +bool HpaePcmBuffer::UpdateWritePos(size_t writePos) +{ + writePos_.store(writePos); + return true; +} + +void HpaePcmBuffer::SetBufferValid(bool valid) +{ + pcmBufferInfo_.isValid = valid; +} + +size_t HpaePcmBuffer::GetCurFrames() const +{ + return curFrames_.load(); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/buffer/hpae_pcm_buffer.h b/services/audio_engine/buffer/hpae_pcm_buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..3e51a13400ba409ef877fc2cb137baeec0e5ecfa --- /dev/null +++ b/services/audio_engine/buffer/hpae_pcm_buffer.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_PCM_BUFFER_H +#define HPAE_PCM_BUFFER_H +#include +#include +#include +#include +#include "hpae_pcm_process.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr size_t MEMORY_ALIGN_BYTE_NUM = 64; + +enum HpaeSourceBufferType { + HPAE_SOURCE_BUFFER_TYPE_DEFAULT, + HPAE_SOURCE_BUFFER_TYPE_MIC, + HPAE_SOURCE_BUFFER_TYPE_EC, + HPAE_SOURCE_BUFFER_TYPE_MICREF, +}; + +// redefine allocator to ensure memory alignment +template +class AlignedAllocator : public std::allocator { +public: + using pointer = T *; + using size_type = size_t; + + pointer Allocate(size_type n) + { + void *ptr = std::aligned_alloc(Alignment, n * sizeof(T)); + return static_cast(ptr); + } + + void DeAllocate(pointer p, size_type n) + { + std::free(p); + } +}; + +struct PcmBufferInfo { + PcmBufferInfo(uint32_t ch1, uint32_t frameLen1, uint32_t rate1) + : ch(ch1), frameLen(frameLen1), rate(rate1) + {} + PcmBufferInfo(uint32_t ch1, uint32_t frameLen1, uint32_t rate1, uint64_t channelLayout1) + : ch(ch1), frameLen(frameLen1), rate(rate1), channelLayout(channelLayout1) + {} + PcmBufferInfo(uint32_t ch1, uint32_t frameLen1, uint32_t rate1, uint64_t channelLayout1, + uint32_t frames1, bool isMultiFrames) + : ch(ch1), frameLen(frameLen1), rate(rate1), channelLayout(channelLayout1), frames(frames1), + isMultiFrames(isMultiFrames) + {} + PcmBufferInfo() = default; + uint32_t ch; + uint32_t frameLen; + uint32_t rate; + uint64_t channelLayout = 0; + uint32_t frames = 1; + bool isMultiFrames = false; + bool isValid = true; +}; + +// todo: multithread access? +class HpaePcmBuffer { +public: + HpaePcmBuffer() = delete; + explicit HpaePcmBuffer(PcmBufferInfo &pcmBufferInfo); + HpaePcmBuffer(HpaePcmBuffer &&other); + HpaePcmBuffer(const HpaePcmBuffer &other) = delete; + ~HpaePcmBuffer() + { + } + HpaePcmBuffer &operator=(HpaePcmBuffer &other); + HpaePcmBuffer &operator=(HpaePcmBuffer &&other) = delete; + + PcmBufferInfo GetPcmBufferInfo() const + { + return pcmBufferInfo_; + } + + uint32_t GetChannelCount() const + { + return pcmBufferInfo_.ch; + } + + uint32_t GetFrameLen() const + { + return pcmBufferInfo_.frameLen; + } + + uint32_t GetSampleRate() const + { + return pcmBufferInfo_.rate; + } + + bool IsMultiFrames() const + { + return pcmBufferInfo_.isMultiFrames; + } + + bool IsValid() const + { + return pcmBufferInfo_.isValid; + } + + uint64_t GetChannelLayout() const + { + return pcmBufferInfo_.channelLayout; + } + + void ReConfig(const PcmBufferInfo &pcmBufferInfo) + { + pcmBufferInfo_ = pcmBufferInfo; + InitPcmProcess(); + } + + bool GetFrameData(std::vector &frameData); + bool GetFrameData(HpaePcmBuffer &frameData); + bool PushFrameData(std::vector &frameData); + bool PushFrameData(HpaePcmBuffer &frameData); + // store history frame for offload + bool StoreFrameData(HpaePcmBuffer &frameData); + // rewind history frame for offload, return frames that rewinded + size_t RewindBuffer(size_t frames); + + HpaePcmProcess &operator[](size_t index) + { + return pcmProcessVec_[index]; + } + + const HpaePcmProcess &operator[](size_t index) const + { + return pcmProcessVec_[index]; + } + + size_t Size() const + { + return bufferByteSize_; + } + + size_t GetFrames() const + { + return pcmBufferInfo_.frames; + } + + size_t GetReadPos() const + { + return readPos_.load(); + } + + size_t GetWritePos() const + { + return writePos_.load(); + } + + bool UpdateReadPos(size_t readPos); + bool UpdateWritePos(size_t writePos); + void SetBufferValid(bool valid); + size_t GetCurFrames() const; + + HpaePcmBuffer &operator=(const std::vector> &other); + HpaePcmBuffer &operator=(const std::vector &other); + + HpaePcmBuffer &operator+=(HpaePcmBuffer &other); + HpaePcmBuffer &operator-=(HpaePcmBuffer &other); + HpaePcmBuffer &operator*=(HpaePcmBuffer &other); + void Reset(); + + std::vector::iterator begin() + { + return pcmProcessVec_.begin(); + } + + std::vector::iterator end() + { + return pcmProcessVec_.end(); + } + + std::vector::const_iterator begin() const + { + return pcmProcessVec_.begin(); + } + + std::vector::const_iterator end() const + { + return pcmProcessVec_.end(); + } + + float *GetPcmDataBuffer() + { + return pcmDataBuffer_.data(); + } + + size_t GetFrameSample() + { + return frameSample_; + } + + HpaeSourceBufferType GetSourceBufferType() + { + return sourceBufferType_; + } + + void SetSourceBufferType(HpaeSourceBufferType type) + { + sourceBufferType_ = type; + } + +private: + void InitPcmProcess(); + + // todo: add err to deal with operator override + std::vector> pcmDataBuffer_; + size_t bufferFloatSize_; + size_t bufferByteSize_; + size_t frameFloatSize_; + size_t frameByteSize_; + size_t frameSample_; + std::atomic readPos_; + std::atomic writePos_; + std::atomic curFrames_; + std::vector pcmProcessVec_; + PcmBufferInfo pcmBufferInfo_; + HpaeSourceBufferType sourceBufferType_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/buffer/hpae_pcm_process.cpp b/services/audio_engine/buffer/hpae_pcm_process.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce26d827477de1ce43d4a5f782336a0f8a87f159 --- /dev/null +++ b/services/audio_engine/buffer/hpae_pcm_process.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaePcmProcess" +#endif + +#include "hpae_pcm_process.h" +#include "simd_utils.h" +#include "audio_engine_log.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaePcmProcess &HpaePcmProcess::operator = (const std::vector& other) +{ + errNo_ = memcpy_s(pcmDataPtr_, sizeof(float) * size_, other.data(), sizeof(float) * other.size()); + CHECK_AND_RETURN_RET_LOG(errNo_ == 0, *this, "memcpy_s failed, errNo: %{public}d", errNo_); + return *this; +} + +HpaePcmProcess &HpaePcmProcess::operator = (const HpaePcmProcess& other) +{ + if (this != &other) { + errNo_ = memcpy_s(pcmDataPtr_, sizeof(float) * size_, other.Begin(), sizeof(float) * other.Size()); + CHECK_AND_RETURN_RET_LOG(errNo_ == 0, *this, "memcpy_s failed, errNo: %{public}d", errNo_); + } + return *this; +} + +HpaePcmProcess &HpaePcmProcess::operator+=(const HpaePcmProcess &other) +{ + float *curData = Begin(); + const float *otherData = other.Begin(); + SimdPointByPointAdd(size_, otherData, curData, curData); + return *this; +} + +HpaePcmProcess &HpaePcmProcess::operator-=(const HpaePcmProcess &other) +{ + float *curData = Begin(); + const float *otherData = other.Begin(); + SimdPointByPointSub(size_, curData, otherData, curData); + return *this; +} + +HpaePcmProcess &HpaePcmProcess::operator*=(const HpaePcmProcess &other) +{ + float *curData = Begin(); + const float *otherData = other.Begin(); + SimdPointByPointMul(size_, otherData, curData, curData); + return *this; +} + +void HpaePcmProcess::Reset() +{ + errNo_ = memset_s(Begin(), sizeof(float) * size_, 0, sizeof(float) * size_); + CHECK_AND_RETURN_LOG(errNo_ == 0, "memcpy_s failed, errNo: %{public}d", errNo_); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/buffer/hpae_pcm_process.h b/services/audio_engine/buffer/hpae_pcm_process.h new file mode 100644 index 0000000000000000000000000000000000000000..04e758fe0abbc2c089e88f7f8fcce1f78212b7ce --- /dev/null +++ b/services/audio_engine/buffer/hpae_pcm_process.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_PCM_PROCESS_H +#define HPAE_PCM_PROCESS_H +#include +#include "securec.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaePcmProcess { +public: + HpaePcmProcess(float *begin, size_t size) : pcmDataPtr_(begin), size_(size) {} + + float &operator[](size_t index) + { + return *(pcmDataPtr_ + index); + } + + const float &operator[](size_t index) const + { + return *(pcmDataPtr_ + index); + } + + size_t Size() const + { + return size_; + } + + float *Begin() + { + return pcmDataPtr_; + } + + float *End() + { + return pcmDataPtr_ + size_; + } + + const float *Begin() const + { + return pcmDataPtr_; + } + + const float *End() const + { + return pcmDataPtr_ + size_; + } + + HpaePcmProcess(const HpaePcmProcess &other) = default; + + HpaePcmProcess &operator = (const HpaePcmProcess &other); + + HpaePcmProcess &operator = (const std::vector& other); + + HpaePcmProcess &operator += (const HpaePcmProcess &other); + + HpaePcmProcess &operator -= (const HpaePcmProcess &other); + + HpaePcmProcess &operator *= (const HpaePcmProcess &other); + + void Reset() ; + + int32_t GetErrNo() + { + int32_t tmpErrNo = errNo_; + errNo_ = 0; + return tmpErrNo; + } + +private: + int32_t errNo_; + float* const pcmDataPtr_; + const size_t size_; +}; +}}} +#endif // HPAE_PCM_PROCESS_H \ No newline at end of file diff --git a/services/audio_engine/dfx/hpae_dfx_tree.cpp b/services/audio_engine/dfx/hpae_dfx_tree.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46ccdfa3a1b158f55e791c758869a27fe86c6b57 --- /dev/null +++ b/services/audio_engine/dfx/hpae_dfx_tree.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeDfxTree" +#endif +#include "hpae_dfx_tree.h" +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +DfxTreeNode *HpaeDfxTree::FindDfxNode(DfxTreeNode *currentNode, const uint32_t nodeId) +{ + if (!currentNode) { + return nullptr; + } + std::queue q; + q.push(currentNode); + while (!q.empty()) { + DfxTreeNode *node = q.front(); + q.pop(); + if (node->nodeInfo_.nodeId == nodeId) { + return node; + } + for (auto &child : node->children_) { + q.push(child); + } + } + return nullptr; +} + +DfxTreeNode *HpaeDfxTree::FindDfxParent(DfxTreeNode *target) +{ + if (!root_ || target == root_) { + return nullptr; + } + std::queue q; + q.push(root_); + while (!q.empty()) { + DfxTreeNode *node = q.front(); + q.pop(); + for (auto &child : node->children_) { + if (child->nodeInfo_.nodeId == target->nodeInfo_.nodeId) { + return node; + } + q.push(child); + } + } + return nullptr; +} + +bool HpaeDfxTree::Insert(const uint32_t parentNodeId, const HpaeDfxNodeInfo &info) +{ + if (!root_) { + AUDIO_INFO_LOG("Insert Root is null"); + root_ = new DfxTreeNode(info); + return true; + } + DfxTreeNode *parent = FindDfxNode(root_, parentNodeId); + if (!parent) { + AUDIO_INFO_LOG("Insert can not find correct parent"); + return false; + } + parent->children_.push_back(new DfxTreeNode(info)); + return true; +} + +bool HpaeDfxTree::Remove(const uint32_t nodeId) +{ + if (!root_) { + AUDIO_INFO_LOG("Remove Root is null"); + return false; + } + DfxTreeNode *nodeToRemove = FindDfxNode(root_, nodeId); + if (!nodeToRemove) { + return false; + } + + if (nodeToRemove == root_) { + delete root_; + root_ = nullptr; + return true; + } + + DfxTreeNode *parent = FindDfxParent(nodeToRemove); + if (!parent) { + return false; + } + // Remove from parent's children + auto &children = parent->children_; + auto it = find(children.begin(), children.end(), nodeToRemove); + if (it != children.end()) { + children.erase(it); + delete nodeToRemove; + return true; + } + return false; +} + +std::vector> HpaeDfxTree::LevelOrderTraversal() +{ + std::vector> result; + if (!root_) { + return result; + } + std::queue q; + q.push(root_); + while (!q.empty()) { + int32_t levelSize = q.size(); + std::vector curLevelResult; + for (int32_t i = 0; i < levelSize; ++i) { + DfxTreeNode *node = q.front(); + q.pop(); + curLevelResult.push_back(node->nodeInfo_); + for (auto &child : node->children_) { + q.push(child); + } + } + result.push_back(curLevelResult); + } + return result; +} + +void HpaeDfxTree::PrintNodeInfo(std::string &outStr, HpaeDfxNodeInfo &nodeInfo) +{ + outStr = outStr + nodeInfo.nodeName + ": " + "id[" + std::to_string(nodeInfo.sessionId) + "],"; + outStr = outStr + "rate[" + std::to_string(nodeInfo.samplingRate) + "],"; + outStr = outStr + "ch[" + std::to_string(nodeInfo.channels) + "],"; + outStr = outStr + "bw[" + std::to_string(nodeInfo.format) + "],"; + outStr = outStr + "len[" + std::to_string(nodeInfo.frameLen) + "],"; + outStr = outStr + "scene[" + std::to_string(nodeInfo.sceneType) + "] \n"; +} + +void HpaeDfxTree::PrintSubTree(DfxTreeNode *node, const std::string &prefix, bool isLastChild, std::string &outStr) +{ + if (!node) { + return; + } + + outStr = outStr + prefix; + outStr = outStr + (isLastChild ? "|___ " : "|--- "); + PrintNodeInfo(outStr, node->nodeInfo_); + std::string newPrefix = prefix + (isLastChild ? " " : "| "); + for (size_t i = 0; i < node->children_.size(); ++i) { + bool childIsLast = (i == node->children_.size() - 1); + PrintSubTree(node->children_[i], newPrefix, childIsLast, outStr); + } +} + +void HpaeDfxTree::PrintTree(std::string &outStr) +{ + if (!root_) { + return; + } + PrintNodeInfo(outStr, root_->nodeInfo_); + for (size_t i = 0; i < root_->children_.size(); ++i) { + bool isLast = (i == root_->children_.size() - 1); + PrintSubTree(root_->children_[i], "", isLast, outStr); + } +} + +void HpaeDfxTree::UpdateNodeInfo(uint32_t nodeId, const HpaeDfxNodeInfo &nodeInfo) +{ + if (root_ == nullptr) { + AUDIO_WARNING_LOG("Hidumper dfx tree is empty!"); + return; + } + DfxTreeNode *target = FindDfxNode(root_, nodeId); + if (target == nullptr) { + AUDIO_WARNING_LOG("Cannot find Node Id: %{public}d", nodeId); + return; + } + target->nodeInfo_ = nodeInfo; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/dfx/hpae_dfx_tree.h b/services/audio_engine/dfx/hpae_dfx_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..fb57864dbdef7a09b1a7dd23bb620c88847f8f29 --- /dev/null +++ b/services/audio_engine/dfx/hpae_dfx_tree.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_DFX_TREE_H +#define HPAE_DFX_TREE_H +#include "hpae_define.h" +#include +#include +#include +#include + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t MIN_START_NODE_ID = 100; +class DfxTreeNode { +public: + explicit DfxTreeNode(HpaeDfxNodeInfo val) : nodeInfo_(val) + {} + ~DfxTreeNode() + { + for (auto child : children_) { + delete child; + } + } + HpaeDfxNodeInfo nodeInfo_; + std::vector children_; +}; + +class HpaeDfxTree { +public: + HpaeDfxTree() : root_(nullptr) + {} + ~HpaeDfxTree() + { + delete root_; + } + std::vector> LevelOrderTraversal(); + bool Insert(const uint32_t parentNodeId, const HpaeDfxNodeInfo &info); + bool Remove(const uint32_t nodeId); + void PrintTree(std::string &outStr); + void UpdateNodeInfo(uint32_t nodeId, const HpaeDfxNodeInfo &nodeInfo); +private: + DfxTreeNode *FindDfxNode(DfxTreeNode *currentNode, const uint32_t nodeId); + DfxTreeNode *FindDfxParent(DfxTreeNode *target); + void PrintSubTree(DfxTreeNode *node, const std::string &prefix, bool isLastChild, std::string &outStr); + void PrintNodeInfo(std::string &outStr, HpaeDfxNodeInfo &nodeInfo); + DfxTreeNode *root_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/audio_service_hpae_callback.h b/services/audio_engine/manager/include/audio_service_hpae_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..137aeb090be2f4c39985d8b20a8ef6b68dc8a87c --- /dev/null +++ b/services/audio_engine/manager/include/audio_service_hpae_callback.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_SERVICE_HAPE_CALLBACK_H +#define AUDIO_SERVICE_HAPE_CALLBACK_H +#include "audio_effect.h" +namespace OHOS { +namespace AudioStandard { +class AudioServiceHpaeCallback { +public: + virtual void OnOpenAudioPortCb(int32_t portId) = 0; + + virtual void OnCloseAudioPortCb(int32_t result) = 0; + + virtual void OnSetSinkMuteCb(int32_t result) = 0; + + virtual void OnGetAllSinkInputsCb(int32_t result, std::vector &sinkInputs) = 0; + + virtual void OnGetAllSourceOutputsCb(int32_t result, std::vector &sourceOutputs) = 0; + + virtual void OnGetAllSinksCb(int32_t result, std::vector &sinks) = 0; + + virtual void OnMoveSinkInputByIndexOrNameCb(int32_t result) = 0; + + virtual void OnMoveSourceOutputByIndexOrNameCb(int32_t result) = 0; + + virtual void OnSetSourceOutputMuteCb(int32_t result) = 0; + + virtual void OnGetAudioEffectPropertyCbV3(int32_t result) = 0; + + virtual void OnGetAudioEffectPropertyCb(int32_t result) = 0; + + virtual void OnGetAudioEnhancePropertyCbV3(int32_t result) = 0; + + virtual void OnGetAudioEnhancePropertyCb(int32_t result) = 0; + + virtual void HandleSourceAudioStreamRemoved(uint32_t sessionId) = 0; + + virtual ~AudioServiceHpaeCallback() + {} +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_SERVICE_HAPE_CALLBACK_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/audio_service_hpae_dump_callback.h b/services/audio_engine/manager/include/audio_service_hpae_dump_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..d39bcd2fda8dbd11b7354da937052c009f17a4b3 --- /dev/null +++ b/services/audio_engine/manager/include/audio_service_hpae_dump_callback.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_SERVICE_HAPE_DUMP_CALLBACK_H +#define AUDIO_SERVICE_HAPE_DUMP_CALLBACK_H +#include +namespace OHOS { +namespace AudioStandard { +class AudioServiceHpaeDumpCallback { +public: + virtual void OnDumpSinkInfoCb(std::string& dumpStr, int32_t result) = 0; + virtual void OnDumpSourceInfoCb(std::string &dumpStr, int32_t result) = 0; + + virtual ~AudioServiceHpaeDumpCallback() + {} +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_SERVICE_HAPE_CALLBACK_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_capture_move_info.h b/services/audio_engine/manager/include/hpae_capture_move_info.h new file mode 100644 index 0000000000000000000000000000000000000000..f42cab99a9412cb1570b566a7edb982d7f1184c2 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_capture_move_info.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_CAPTURE_MOVE_INFO_H +#define HPAE_CAPTURE_MOVE_INFO_H +#include "hpae_define.h" +#include "hpae_source_output_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +struct HpaeCaptureMoveInfo { + uint32_t sessionId; + std::shared_ptr sourceOutputNode; + HpaeCapturerSessionInfo sessionInfo; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_CAPTURE_MOVE_INFO_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_capturer_manager.h b/services/audio_engine/manager/include/hpae_capturer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..7e84ee8bd21db113d6b7ff8cf8d9349bc4fbe773 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_capturer_manager.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_CAPTURER_MANAGER_H +#define HPAE_CAPTURER_MANAGER_H +#include +#include +#include +#include +#include +#include +#include "audio_effect.h" +#include "hpae_signal_process_thread.h" +#include "hpae_source_input_node.h" +#include "hpae_source_input_cluster.h" +#include "hpae_source_output_node.h" +#include "hpae_source_process_cluster.h" +#include "hpae_no_lock_queue.h" +#include "i_hpae_capturer_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeCapturerManager : public IHpaeCapturerManager { +public: + HpaeCapturerManager(HpaeSourceInfo &sourceInfo); + virtual ~HpaeCapturerManager(); + int32_t CreateStream(const HpaeStreamInfo& streamInfo) override; + int32_t DestroyStream(uint32_t sessionId) override; + + int32_t Start(uint32_t sessionId) override; + int32_t Pause(uint32_t sessionId) override; + int32_t Flush(uint32_t sessionId) override; + int32_t Drain(uint32_t sessionId) override; + int32_t Stop(uint32_t sessionId) override; + int32_t Release(uint32_t sessionId) override; + int32_t MoveStream(uint32_t sessionId, const std::string& sourceName) override; + int32_t MoveAllStream(const std::string& sourceName, const std::vector& sessionIds, + bool isMoveAll = true) override; + int32_t SetMute(bool isMute) override; + void Process() override; + void HandleMsg() override; + int32_t Init() override; + int32_t DeInit(bool isMoveDefault = false) override; + bool IsInit() override; + bool IsRunning(void) override; + bool IsMsgProcessing() override; + bool DeactivateThread() override; + + int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) override; + HpaeSourceInfo GetSourceInfo() override; + std::vector GetAllSourceOutputsInfo() override; + + void OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) override; + void OnNotifyQueue() override; + + int32_t AddNodeToSource(const HpaeCaptureMoveInfo &moveInfo) override; + int32_t AddAllNodesToSource(const std::vector &moveInfos, bool isConnect) override; + std::string GetThreadName() override; + void SetCaptureId(uint32_t captureId); + int32_t ReloadCaptureManager(const HpaeSourceInfo &sourceInfo) override; + void DumpSourceInfo() override; +private: + int32_t CreateOutputSession(const HpaeStreamInfo &streamInfo); + int32_t DeleteOutputSession(uint32_t sessionId); + int32_t ConnectProcessClusterWithEc(HpaeProcessorType &sceneType); + int32_t ConnectProcessClusterWithMicRef(HpaeProcessorType &sceneType); + int32_t ConnectOutputSession(uint32_t sessionId); + int32_t DisConnectOutputSession(uint32_t sessionId); + void DisConnectSceneClusterFromSourceInputCluster(HpaeProcessorType &sceneType); + void SetSessionState(uint32_t sessionId, CapturerState capturerState); + int32_t PrepareCapturerEc(HpaeNodeInfo &ecNodeInfo); + int32_t PrepareCapturerMicRef(HpaeNodeInfo &micRefNodeInfo); + int32_t InitCapturer(); + void AddSingleNodeToSource(const HpaeCaptureMoveInfo &moveInfo, bool isConnect = true); + void MoveAllStreamToNewSource(const std::string &sourceName, + const std::vector& moveIds, bool isMoveAll); + int32_t CaptureEffectCreate(const HpaeProcessorType &sceneType, const AudioEnhanceScene &enhanceScene); + int32_t CaptureEffectRelease(const HpaeProcessorType &sceneType); + int32_t InitCapturerManager(); + void CreateSourceAttr(IAudioSourceAttr &attr); +private: + void SendRequest(Request &&request, bool isInit = false); + HpaeNoLockQueue hpaeNoLockQueue_; + std::unique_ptr hpaeSignalProcessThread_ = nullptr; + std::unordered_map sessionNodeMap_; + std::unordered_map streamInfoMap_; + std::unordered_map> sceneClusterMap_; + std::unordered_map> sourceOutputNodeMap_; + std::unordered_map> sourceInputClusterMap_; + + HpaeSourceInputNodeType mainMicType_; + std::atomic isInit_ = false; + std::atomic isMute_ = false; + HpaeSourceInfo sourceInfo_; + uint32_t captureId_ = 0; + uint32_t renderId_ = 0; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_define.h b/services/audio_engine/manager/include/hpae_define.h new file mode 100644 index 0000000000000000000000000000000000000000..786895e6df240de9c3b271c7c66d514befdace0d --- /dev/null +++ b/services/audio_engine/manager/include/hpae_define.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_DEFINE_H +#define HPAE_DEFINE_H +#include "hpae_msg_channel.h" +#include "i_stream.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t MILLISECOND_PER_SECOND = 1000; + +struct HpaeSessionInfo { + HpaeStreamInfo streamInfo; + uint32_t state = I_STATUS_IDLE; + std::weak_ptr statusCallback; +}; + + +constexpr int32_t SCENE_TYPE_NUM = 9; + +struct HpaeRenderSessionInfo { + uint32_t sinkInputNodeId; + HpaeProcessorType sceneType = HPAE_SCENE_DEFAULT; + uint32_t state = I_STATUS_IDLE; +}; + +struct HpaeSinkInputInfo { + HpaeRenderSessionInfo rendererSessionInfo; + HpaeNodeInfo nodeInfo; +}; + +struct HpaeSinkInfo { + uint32_t sinkId; + std::string deviceNetId; + std::string deviceClass; + std::string adapterName; + std::string filePath; + std::string deviceName; + size_t frameLen; + AudioSamplingRate samplingRate; + AudioSampleFormat format; + AudioChannel channels; + uint32_t suspendTime = 0; // in ms + uint64_t channelLayout = 0ULL; + int32_t deviceType = 0; + float volume = 0.0f; + uint32_t openMicSpeaker = 0; + uint32_t renderInIdleState = 0; + uint32_t sourceType = 0; + uint32_t offloadEnable = 0; + uint32_t fixedLatency = 0; + uint32_t sinkLatency = 0; +}; + +struct HpaeCapturerSessionInfo { + HpaeProcessorType sceneType = HPAE_SCENE_DEFAULT; + uint32_t state = I_STATUS_IDLE; +}; + +struct HpaeSourceOutputInfo { + HpaeCapturerSessionInfo capturerSessionInfo; + HpaeNodeInfo nodeInfo; +}; + +enum HpaeEcType { + HPAE_EC_TYPE_NONE, + HPAE_EC_TYPE_SAME_ADAPTER, + HPAE_EC_TYPE_DIFF_ADAPTER +}; + +enum HpaeMicRefSwitch { + HPAE_REF_OFF = 0, + HPAE_REF_ON +}; + +struct HpaeSourceInfo { + uint32_t sourceId; + std::string deviceNetId; + std::string deviceClass; + std::string adapterName; + std::string sourceName; + SourceType sourceType; + std::string filePath; + std::string deviceName; + size_t frameLen; + AudioSamplingRate samplingRate; + AudioSampleFormat format; + AudioChannel channels; + uint64_t channelLayout = 0ULL; + int32_t deviceType = 0; + float volume = 0.0f; + HpaeEcType ecType; + size_t ecFrameLen; + std::string ecAdapterName; + AudioSamplingRate ecSamplingRate; + AudioSampleFormat ecFormat; + AudioChannel ecChannels; + HpaeMicRefSwitch micRef; + size_t micRefFrameLen; + AudioSamplingRate micRefSamplingRate; + AudioSampleFormat micRefFormat; + AudioChannel micRefChannels; + uint32_t openMicSpeaker; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif // HPAE_DEFINE_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_info.h b/services/audio_engine/manager/include/hpae_info.h new file mode 100644 index 0000000000000000000000000000000000000000..42c0fd4e45bd4b15afa47921741522b3c3fcf415 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_info.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_INFO_H +#define HPAE_INFO_H +#include "audio_effect.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +enum HpaeStreamClassType { + HPAE_STREAM_CLASS_TYPE_INVALID = -1, + HPAE_STREAM_CLASS_TYPE_PLAY, + HPAE_STREAM_CLASS_TYPE_RECORD, +}; + +enum HpaeNodeType { + HPAE_NODE_TYPE_INVALID = -1, + HPAE_NODE_TYPE_SOURCE_INPUT, + HPAE_NODE_TYPE_SOURCE_OUTPUT, + HPAE_NODE_TYPE_SINK_INPUT, + HPAE_NODE_TYPE_SINK_OUTPUT, + HPAE_NODE_TYPE_PLUGIN, +}; + +struct HpaeEffectInfo { + StreamUsage streamUsage; + AudioVolumeType volumeType; + AudioEffectScene effectScene; + AudioEffectMode effectMode; + AudioEnhanceScene enhanceScene; + AudioEnhanceMode enhanceMode; +}; + +enum FadeType { + DEFAULT_FADE = 0, // default one frame fade + SHORT_FADE, // short 5ms fade + NONE_FADE // do not fade +}; +struct HpaeStreamInfo { + uint32_t sessionId; + size_t frameLen; + HpaeNodeType nodeType; + AudioStreamType streamType; + FadeType fadeType = NONE_FADE; + AudioPipeType pipeType; + AudioSamplingRate samplingRate; + AudioSampleFormat format; + AudioChannel channels; + uint64_t channelLayout = 0ULL; + HpaeStreamClassType streamClassType; + SourceType sourceType; + int32_t uid = -1; + int32_t pid = 0; + HpaeEffectInfo effectInfo; + std::string deviceName; +}; + +static inline int32_t GetSizeFromFormat(int32_t format) +{ + return format != SAMPLE_F32LE ? ((format) + 1) : (4); // float 4 +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_inner_capturer_manager.h b/services/audio_engine/manager/include/hpae_inner_capturer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..5148fc6729cc38a9fb3ca9c5ea1f5210827257a3 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_inner_capturer_manager.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_INNER_CAPTURER_MANAGER_H +#define HPAE_INNER_CAPTURER_MANAGER_H +#include +#include +#include +#include +#include +#include +#include "i_renderer_stream.h" +#include "hpae_signal_process_thread.h" +#include "hpae_sink_input_node.h" +#include "hpae_resample_node.h" +#include "hpae_inner_cap_sink_node.h" +#include "hpae_source_output_node.h" +#include "hpae_source_process_cluster.h" +#include "hpae_msg_channel.h" +#include "hpae_no_lock_queue.h" +#include "i_hpae_renderer_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeInnerCapturerManager : public IHpaeRendererManager { +public: + HpaeInnerCapturerManager(HpaeSinkInfo &sinkInfo); + ~HpaeInnerCapturerManager(); + int32_t CreateStream(const HpaeStreamInfo &streamInfo) override; + int32_t DestroyStream(uint32_t sessionId) override; + + int32_t Start(uint32_t sessionId) override; + int32_t Pause(uint32_t sessionId) override; + int32_t Flush(uint32_t sessionId) override; + int32_t Drain(uint32_t sessionId) override; + int32_t Stop(uint32_t sessionId) override; + int32_t Release(uint32_t sessionId) override; + int32_t MoveStream(uint32_t sessionId, const std::string &sinkName) override; + int32_t MoveAllStream(const std::string &sinkName, const std::vector& sessionIds, + bool isMoveAll = true) override; + int32_t SuspendStreamManager(bool isSuspend) override; + int32_t SetMute(bool isMute) override; + void Process() override; + void HandleMsg() override; + int32_t Init() override; + int32_t DeInit(bool isMoveDefault = false) override; + bool IsInit() override; + bool IsRunning(void) override; + bool IsMsgProcessing() override; + bool DeactivateThread() override; + int32_t SetClientVolume(uint32_t sessionId, float volume) override; + int32_t SetRate(uint32_t sessionId, int32_t rate) override; + int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) override; + int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) override; + int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) override; + int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) override; + int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + + size_t GetWritableSize(uint32_t sessionId) override; + int32_t UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) override; + int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) override; + std::vector GetAllSinkInputsInfo() override; + int32_t GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) override; + HpaeSinkInfo GetSinkInfo() override; + + void OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) override; + void OnFadeDone(uint32_t sessionId, IOperation operation) override; + + int32_t AddNodeToSink(const std::shared_ptr &node) override; + int32_t AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) override; + + int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) override; + std::vector GetAllSourceOutputsInfo() override; + std::string GetThreadName() override; + +private: + void TransStreamInfoToNodeInfoInner(const HpaeStreamInfo &streamInfo, HpaeNodeInfo &nodeInfo); + int32_t CreateRendererInputSessionInner(const HpaeStreamInfo &streamInfo); + int32_t CreateCapturerInputSessionInner(const HpaeStreamInfo &streamInfo); + int32_t DeleteRendererInputSessionInner(uint32_t sessionId); + int32_t DeleteCapturerInputSessionInner(uint32_t sessionId); + int32_t ConnectRendererInputSessionInner(uint32_t sessionId); + int32_t ConnectCapturerOutputSessionInner(uint32_t sessionId); + int32_t DisConnectRendererInputSessionInner(uint32_t sessionId); + int32_t DisConnectCapturerInputSessionInner(uint32_t sessionId); + void SetSessionStateInner(uint32_t sessionId, RendererState renderState); + void SetSessionStateInner(uint32_t sessionId, CapturerState capturerState); + void SendRequestInner(Request &&request, bool isInit = false); + uint32_t GetSinkInputNodeIdInner(); + void AddSingleNodeToSinkInner(const std::shared_ptr &node, bool isConnect = true); + void MoveAllStreamToNewSinkInner(const std::string &sinkName, const std::vector &moveIds, bool isMoveAll); + uint32_t sinkInputNodeCounter_ = 0; + std::atomic isInit_ = false; + std::atomic isMute_ = false; + HpaeSinkInfo sinkInfo_; + HpaeNoLockQueue hpaeNoLockQueue_; + std::shared_ptr hpaeInnerCapSinkNode_ = nullptr; + std::unique_ptr hpaeSignalProcessThread_ = nullptr; + std::unordered_map> sinkInputNodeMap_; + std::unordered_map> sourceOutputNodeMap_; + std::unordered_map> capturerResampleNodeMap_; + std::unordered_map> capturerSceneClusterMap_; + std::unordered_map> rendererSceneClusterMap_; + std::unordered_map capturerSessionNodeMap_; + std::unordered_map rendererSessionNodeMap_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_manager.h b/services/audio_engine/manager/include/hpae_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..76ea238ade1e20332fe3cc41bef2292691c20c0f --- /dev/null +++ b/services/audio_engine/manager/include/hpae_manager.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_MANAGER_H +#define HPAE_MANAGER_H +#include +#include +#include "audio_module_info.h" +#include "hpae_capturer_manager.h" +#include "hpae_renderer_manager.h" +#include "hpae_inner_capturer_manager.h" +#include "hpae_msg_channel.h" +#include "i_hpae_manager.h" +#include "i_hpae_renderer_manager.h" +#include "hpae_policy_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeManager; + +class HpaeManagerThread { +public: + HpaeManagerThread() : running_(false) + {} + ~HpaeManagerThread(); + void ActivateThread(HpaeManager *hpaeManager); + void DeactivateThread(); + void Run(); + void Notify(); + bool IsRunning() const + { + return running_.load(); + } + bool IsMsgProcessing() const + { + return recvSignal_.load(); + } + +private: + std::atomic running_; + std::atomic recvSignal_; + HpaeManager *m_hpaeManager; + std::condition_variable condition_; + std::mutex mutex_; + std::thread thread_; +}; + +class HpaeManager : public IHpaeManager, public ISendMsgCallback, public std::enable_shared_from_this { +public: + static constexpr std::string_view SPLIT_STREAM_SINK = "libmodule-split-stream-sink.z.so"; + static constexpr std::string_view HDI_SINK = "libmodule-hdi-sink.z.so"; + static constexpr std::string_view HDI_SOURCE = "libmodule-hdi-source.z.so"; + static constexpr std::string_view INNER_CAPTURER_SINK = "libmodule-inner-capturer-sink.z.so"; + HpaeManager(); + ~HpaeManager(); + // sync interface + int32_t Init() override; + int32_t DeInit() override; + int32_t RegisterSerivceCallback(const std::weak_ptr &callback) override; + int32_t RegisterHpaeDumpCallback(AudioServiceHpaeDumpCallback *callback) override; + void DumpSinkInfo(std::string deviceName) override; + void DumpSourceInfo(std::string deviceName) override; + uint32_t OpenAudioPort(const AudioModuleInfo &audioModuleInfo) override; + int32_t CloseAudioPort(int32_t audioHandleIndex) override; + int32_t GetAllSinkInputs() override; + int32_t GetAllSourceOutputs() override; + int32_t MoveSourceOutputByIndexOrName( + uint32_t sourceOutputId, uint32_t sourceIndex, std::string sourceName) override; + int32_t MoveSinkInputByIndexOrName(uint32_t sinkInputId, uint32_t sinkIndex, std::string sinkName) override; + void HandleMsg() override; + bool IsInit() override; + bool IsRunning() override; + bool IsMsgProcessing() override; + // async interface + int32_t SetDefaultSink(std::string name) override; + int32_t SetDefaultSource(std::string name) override; + int32_t SuspendAudioDevice(std::string &audioPortName, bool isSuspend) override; + bool SetSinkMute(const std::string &sinkName, bool isMute, bool isSync = false) override; + int32_t SetSourceOutputMute(int32_t uid, bool setMute) override; + int32_t GetAllSinks() override; + + int32_t GetMsgCount(); + + void Invoke(HpaeMsgCode cmdID, const std::any &args) override; + // play and record stream interface + int32_t CreateStream(const HpaeStreamInfo &streamInfo) override; + int32_t DestroyStream(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Start(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Pause(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Flush(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Drain(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Stop(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t Release(HpaeStreamClassType streamClassType, uint32_t sessionId) override; + int32_t RegisterStatusCallback(HpaeStreamClassType streamClassType, uint32_t sessionId, + const std::weak_ptr &callback) override; + // record stream interface + void RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeStreamInfo &streamInfo) override; + // play stream interface + int32_t SetClientVolume(uint32_t sessionId, float volume) override; + int32_t SetRate(uint32_t sessionId, int32_t rate) override; + int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) override; + int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) override; + int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) override; + int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) override; + int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + int32_t SetOffloadPolicy(uint32_t sessionId, int32_t state) override; + size_t GetWritableSize(uint32_t sessionId) override; + int32_t UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) override; + int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) override; + // only interface for unit test + int32_t GetSessionInfo(HpaeStreamClassType streamClassType, uint32_t sessionId, HpaeSessionInfo &sessionInfo); + + // interfaces for render effect + void InitAudioEffectChainManager(const std::vector &effectChains, + const EffectChainManagerParam &effectChainManagerParam, + const std::vector> &effectLibraryList) override; + void SetOutputDeviceSink(int32_t device, const std::string &sinkName) override; + int32_t UpdateSpatializationState(AudioSpatializationState spatializationState) override; + int32_t UpdateSpatialDeviceType(AudioSpatialDeviceType spatialDeviceType) override; + int32_t SetSpatializationSceneType(AudioSpatializationSceneType spatializationSceneType) override; + int32_t EffectRotationUpdate(const uint32_t rotationState) override; + int32_t SetEffectSystemVolume(const int32_t systemVolumeType, const float systemVolume) override; + int32_t SetAudioEffectProperty(const AudioEffectPropertyArrayV3 &propertyArray) override; + int32_t GetAudioEffectProperty(AudioEffectPropertyArrayV3 &propertyArray) override; + int32_t SetAudioEffectProperty(const AudioEffectPropertyArray &propertyArray) override; + int32_t GetAudioEffectProperty(AudioEffectPropertyArray &propertyArray) override; + void InitHdiState() override; + void UpdateEffectBtOffloadSupported(const bool &isSupported) override; + void UpdateParamExtra(const std::string &mainkey, const std::string &subkey, const std::string &value) override; + // interfaces for capture effect + void InitAudioEnhanceChainManager(const std::vector &enhanceChains, + const EffectChainManagerParam &managerParam, + const std::vector> &enhanceLibraryList) override; + int32_t SetInputDevice( + const uint32_t &captureId, const DeviceType &inputDevice, const std::string &deviceName = "") override; + int32_t SetOutputDevice(const uint32_t &renderId, const DeviceType &outputDevice) override; + int32_t SetVolumeInfo(const AudioVolumeType &volumeType, const float &systemVol) override; + int32_t SetMicrophoneMuteInfo(const bool &isMute) override; + int32_t SetStreamVolumeInfo(const uint32_t &sessionId, const float &streamVol) override; + int32_t SetAudioEnhanceProperty( + const AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) override; + int32_t GetAudioEnhanceProperty( + AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) override; + int32_t SetAudioEnhanceProperty( + const AudioEnhancePropertyArray &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) override; + int32_t GetAudioEnhanceProperty( + AudioEnhancePropertyArray &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) override; + void UpdateExtraSceneType( + const std::string &mainkey, const std::string &subkey, const std::string &extraSceneType) override; + +private: + void TransModuleInfoToHpaeSinkInfo(const AudioModuleInfo &audioModuleInfo, HpaeSinkInfo &sinkInfo); + void TransModuleInfoToHpaeSourceInfo(const AudioModuleInfo &audioModuleInfo, HpaeSourceInfo &sourceInfo); + AudioSampleFormat TransFormatFromStringToEnum(std::string format); + int32_t CloseOutAudioPort(std::string &sinkName); + void PrintAudioModuleInfo(const AudioModuleInfo &audioModuleInfo); + int32_t CloseInAudioPort(std::string &sourceName); + void AdjustMchSinkInfo(const AudioModuleInfo &audioModuleInfo, HpaeSinkInfo &sinkInfo); + template + void RegisterHandler(HpaeMsgCode cmdID, void (HpaeManager::*func)(Args...)); + void HandleUpdateStatus( + HpaeStreamClassType streamClassType, uint32_t sessionId, uint32_t status, IOperation operation); + void HandleInitDeviceResult(std::string deviceName, int32_t result); + void HandleDeInitDeviceResult(std::string deviceName, int32_t result); + void HandleMoveSinkInput(const std::shared_ptr sinkInputNode, std::string sinkName); + void HandleMoveAllSinkInputs(const std::vector> sinkInputs, std::string sinkNam); + void HandleMoveSourceOutput(const HpaeCaptureMoveInfo moveInfo, std::string sourceName); + void HandleMoveAllSourceOutputs(const std::vector moveInfos, std::string sourceName); + void HandleDumpSinkInfo(std::string deviceName, std::string dumpStr); + void HandleDumpSourceInfo(std::string deviceName, std::string dumpStr); + + void SendRequest(Request &&request); + int32_t OpenAudioPortInner(const AudioModuleInfo &audioModuleInfo); + uint32_t OpenOutputAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex); + uint32_t OpenInputAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex); + uint32_t OpenVirtualAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex); + void HandleRendererManager(const std::string& sinkName, const HpaeStreamInfo &streamInfo); + void CreateStreamForCapInner(const HpaeStreamInfo &streamInfo); + + std::shared_ptr GetRendererManagerById(uint32_t sessionId); + std::shared_ptr GetCapturerManagerById(uint32_t sessionId); + std::shared_ptr GetRendererManagerByNmae(const std::string &sinkName); + std::shared_ptr GetCapturerManagerByName(const std::string &sourceName); + void AddStreamToCollection(const HpaeStreamInfo &streamInfo); + + void MoveToPreferSink(const std::string& name); + void AddSinkIdByName(std::unordered_map> &sinkIdMap, + const std::pair &id, const std::string &name); +private: + std::unique_ptr hpaeManagerThread_ = nullptr; + std::unique_ptr hpaePolicyManager_ = nullptr; + std::unordered_map> capturerManagerMap_; + std::unordered_map> rendererManagerMap_; + std::unordered_map capturerIdSourceNameMap_; + std::unordered_map rendererIdSinkNameMap_; + std::unordered_map idPreferSinkNameMap_; + std::unordered_map rendererIdStreamInfoMap_; + std::unordered_map capturerIdStreamInfoMap_; + std::unordered_map sinkInputs_; + std::unordered_map sourceOutputs_; + std::unordered_map sinkNameSinkIdMap_; // todo + std::unordered_map sinkIdSinkNameMap_; + std::string defaultSink_ = "Speaker"; + std::unordered_map sourceNameSourceIdMap_; + std::unordered_map sourceIdSourceNameMap_; + std::string defaultSource_ = "Built_in_mic"; + std::atomic sinkSourceIndex_ = 0; + std::atomic isInit_ = false; + + HpaeNoLockQueue hpaeNoLockQueue_; + + std::atomic receiveMsgCount_ = 0; + std::weak_ptr serviceCallback_; + AudioServiceHpaeDumpCallback *dumpCallback_ = nullptr; + std::unordered_map deviceDumpSinkInfoMap_; + std::unordered_map> handlers_; +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_HDI_MANAGER_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_msg_channel.h b/services/audio_engine/manager/include/hpae_msg_channel.h new file mode 100644 index 0000000000000000000000000000000000000000..1b93cbe988298da47da1e8af76e7a822c806724b --- /dev/null +++ b/services/audio_engine/manager/include/hpae_msg_channel.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_MSG_CHANNEL_H +#define HPAE_MSG_CHANNEL_H +#include +#include "i_stream.h" +#include "hpae_info.h" +#include "audio_engine_log.h" +#include "hpae_pcm_buffer.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +enum HpaeMsgCode { + UPDATE_STATUS, + INIT_DEVICE_RESULT, + DEINIT_DEVICE_RESULT, + MOVE_SINK_INPUT, + MOVE_ALL_SINK_INPUT, + MOVE_SOURCE_OUTPUT, + MOVE_ALL_SOURCE_OUTPUT, + DUMP_SINK_INFO, + DUMP_SOURCE_INFO, +}; + +enum NodeOperation { UNDERFLOW, FADED, DRAINED }; + +class ISendMsgCallback { +public: + virtual void Invoke(HpaeMsgCode cmdID, const std::any &args) = 0; +}; + +class CallbackSender { +protected: + std::weak_ptr weakCallback_; + +public: + void RegisterSendMsgCallback(std::weak_ptr cb) + { + weakCallback_ = cb; + } + + template + void TriggerCallback(HpaeMsgCode cmdID, Args &&...args) + { + if (auto callback = weakCallback_.lock()) { + // pack the arguments into a tuple + auto packed = std::make_tuple(std::forward(args)...); + callback->Invoke(cmdID, packed); + } else { + AUDIO_ERR_LOG("Hpae TriggerCallback callback is null"); + } + } +}; + +enum HpaeProcessorType { + HPAE_SCENE_DEFAULT = 0, + HPAE_SCENE_MUSIC = 1, + HPAE_SCENE_GAME = 2, + HPAE_SCENE_MOVIE = 3, + HPAE_SCENE_SPEECH = 4, + HPAE_SCENE_RING = 5, + HPAE_SCENE_VOIP_DOWN = 6, + HPAE_SCENE_OTHERS = 7, + HPAE_SCENE_EFFECT_NONE = 8, + HPAE_SCENE_EFFECT_OUT = 9, + + // up processor scene + HPAE_SCENE_VOIP_UP = 20, + HPAE_SCENE_RECORD = 21, + HPAE_SCENE_PRE_ENHANCE = 22, + HPAE_SCENE_ASR = 23, + HPAE_SCENE_VOICE_MESSAGE = 24, +}; + +// mark sourceInputNode(cluster) +enum HpaeSourceInputNodeType { + HPAE_SOURCE_DEFAULT, + HPAE_SOURCE_MIC, + HPAE_SOURCE_MIC_EC, + HPAE_SOURCE_EC, + HPAE_SOURCE_MICREF, +}; + +struct HpaeDfxNodeInfo { + uint32_t nodeId; + uint32_t sessionId; + uint32_t frameLen; + size_t historyFrameCount; + AudioSamplingRate samplingRate; + AudioSampleFormat format = AudioSampleFormat::SAMPLE_F32LE; + AudioChannel channels; + AudioChannelLayout channelLayout = AudioChannelLayout::CH_LAYOUT_UNKNOWN; + FadeType fadeType = NONE_FADE; + AudioStreamType streamType; + HpaeProcessorType sceneType; + std::string deviceClass; + std::string deviceNetId; + std::string nodeName; +}; + +class INodeCallback { +public: + virtual void OnNodeStatusUpdate(uint32_t sessionId, IOperation operation){}; + virtual void OnFadeDone(uint32_t sessionId, IOperation operation){}; + virtual void OnRequestLatency(uint32_t sessionId, uint64_t &latency){}; + virtual void OnRewindAndFlush(uint64_t rewindTime){}; + virtual void OnNotifyQueue(){}; + // add callback + virtual uint32_t OnGetNodeId() + { + return 0; + }; + virtual void OnNotifyDfxNodeInfo(bool isConnect, uint32_t preNodeId, HpaeDfxNodeInfo &nodeInfo){}; + virtual void OnNotifyDfxNodeInfoChanged(uint32_t NodeId, const HpaeDfxNodeInfo &nodeInfo){}; +}; + +struct HpaeNodeInfo : HpaeDfxNodeInfo { + HpaeEffectInfo effectInfo; + std::weak_ptr statusCallback; + HpaeSourceBufferType sourceBufferType = HpaeSourceBufferType::HPAE_SOURCE_BUFFER_TYPE_DEFAULT; + HpaeSourceInputNodeType sourceInputNodeType = HpaeSourceInputNodeType::HPAE_SOURCE_DEFAULT; +}; + +class INodeFormatInfoCallback { +public: + virtual int32_t GetEffectNodeInputChannelInfo(uint32_t &channels, uint64_t &channelLayout) = 0; +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_offload_renderer_manager.h b/services/audio_engine/manager/include/hpae_offload_renderer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..a714257a4bed203ebad92c1796b8888552c0b82b --- /dev/null +++ b/services/audio_engine/manager/include/hpae_offload_renderer_manager.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_OFFLOAD_RENDER_MANAGER_H +#define HPAE_OFFLOAD_RENDER_MANAGER_H +#include +#include +#include +#include +#include +#include +#include "hpae_signal_process_thread.h" +#include "hpae_sink_input_node.h" +#include "hpae_process_cluster.h" +#include "hpae_offload_sinkoutput_node.h" +#include "hpae_msg_channel.h" +#include "hpae_no_lock_queue.h" +#include "i_hpae_renderer_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeOffloadRendererManager : public IHpaeRendererManager { +public: + HpaeOffloadRendererManager(HpaeSinkInfo& sinkInfo); + virtual ~HpaeOffloadRendererManager(); + int32_t CreateStream(const HpaeStreamInfo &streamInfo) override; + int32_t DestroyStream(uint32_t sessionId) override; + + int32_t Start(uint32_t sessionId) override; + int32_t Pause(uint32_t sessionId) override; + int32_t Flush(uint32_t sessionId) override; + int32_t Drain(uint32_t sessionId) override; + int32_t Stop(uint32_t sessionId) override; + int32_t Release(uint32_t sessionId) override; + int32_t MoveStream(uint32_t sessionId, const std::string& sinkName) override; + int32_t MoveAllStream(const std::string& sinkName, const std::vector& sessionIds, + bool isMoveAll = true) override; + int32_t SuspendStreamManager(bool isSuspend) override; + int32_t SetMute(bool isMute) override; + void Process() override; + void HandleMsg() override; + int32_t Init() override; + int32_t DeInit(bool isMoveDefault = false) override; + bool IsInit() override; + bool IsRunning(void) override; + bool IsMsgProcessing() override; + bool DeactivateThread() override; + int32_t SetClientVolume(uint32_t sessionId, float volume) override; + int32_t SetRate(uint32_t sessionId, int32_t rate) override; + int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) override; + int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) override; + int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) override; + int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) override; + int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + + int32_t SetOffloadPolicy(uint32_t sessionId, int32_t state) override; + size_t GetWritableSize(uint32_t sessionId) override; + int32_t UpdateSpatializationState(uint32_t sessionId, bool spatializationEnabled, + bool headTrackingEnabled) override; + int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) override; + std::vector GetAllSinkInputsInfo() override; + int32_t GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) override; + HpaeSinkInfo GetSinkInfo() override; + + int32_t AddNodeToSink(const std::shared_ptr &node) override; + int32_t AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) override; + + void OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) override; + void OnRequestLatency(uint32_t sessionId, uint64_t &latency) override; + void OnRewindAndFlush(uint64_t rewindTime) override; + void OnNotifyQueue() override; + std::string GetThreadName() override; + void DumpSinkInfo() override; +private: + void SendRequest(Request &&request, bool isInit = false); + int32_t StartRenderSink(); + int32_t CreateInputSession(const HpaeStreamInfo &streamInfo); + int32_t ConnectInputSession(); + int32_t DisConnectInputSession(); + void AddSingleNodeToSink(const std::shared_ptr &node, bool isConnect = true); + void MoveAllStreamToNewSink(const std::string &sinkName, const std::vector &moveIds, bool isMoveAll); + + HpaeRenderSessionInfo sessionInfo_; + std::shared_ptr sinkInputNode_ = nullptr; + std::shared_ptr formatConverterNode_ = nullptr; + std::unique_ptr sinkOutputNode_ = nullptr; + HpaeNoLockQueue hpaeNoLockQueue_; + std::unique_ptr hpaeSignalProcessThread_ = nullptr; + std::atomic isInit_ = false; + HpaeSinkInfo sinkInfo_; + bool isMute_ = false; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_OFFLOAD_RENDER_MANAGER_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_policy_manager.h b/services/audio_engine/manager/include/hpae_policy_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..cb8b3b685cc7965dede1e1af23eb6a47622f1191 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_policy_manager.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_POLICY_MANAGER_H +#define HPAE_POLICY_MANAGER_H + +#include "audio_effect.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + + +class HpaePolicyManager { +public: + HpaePolicyManager(); + ~HpaePolicyManager(); + // interfaces for render effect + void InitAudioEffectChainManager(const std::vector &effectChains, + const EffectChainManagerParam &effectChainManagerParam, + const std::vector> &effectLibraryList); + void SetOutputDeviceSink(int32_t device, const std::string &sinkName); + int32_t UpdateSpatializationState(AudioSpatializationState spatializationState); + int32_t UpdateSpatialDeviceType(AudioSpatialDeviceType spatialDeviceType); + int32_t SetSpatializationSceneType(AudioSpatializationSceneType spatializationSceneType); + int32_t EffectRotationUpdate(const uint32_t rotationState); + int32_t SetEffectSystemVolume(const int32_t systemVolumeType, const float systemVolume); + int32_t SetAudioEffectProperty(const AudioEffectPropertyArrayV3 &propertyArray); + int32_t GetAudioEffectProperty(AudioEffectPropertyArrayV3 &propertyArray); + int32_t SetAudioEffectProperty(const AudioEffectPropertyArray &propertyArray); + int32_t GetAudioEffectProperty(AudioEffectPropertyArray &propertyArray); + void InitHdiState(); + void UpdateEffectBtOffloadSupported(const bool &isSupported); + void UpdateParamExtra(const std::string &mainkey, const std::string &subkey, const std::string &value); + + // interfaces for capture effect + void InitAudioEnhanceChainManager(const std::vector &enhanceChains, + const EffectChainManagerParam &managerParam, + const std::vector> &enhanceLibraryList); + int32_t SetInputDevice(const uint32_t &captureId, const DeviceType &inputDevice, + const std::string &deviceName = ""); + int32_t SetOutputDevice(const uint32_t &renderId, const DeviceType &outputDevice); + int32_t SetVolumeInfo(const AudioVolumeType &volumeType, const float &systemVol); + int32_t SetMicrophoneMuteInfo(const bool &isMute); + int32_t SetStreamVolumeInfo(const uint32_t &sessionId, const float &streamVol); + int32_t SetAudioEnhanceProperty(const AudioEffectPropertyArrayV3 &propertyArray, + DeviceType deviceType = DEVICE_TYPE_NONE); + int32_t GetAudioEnhanceProperty(AudioEffectPropertyArrayV3 &propertyArray, + DeviceType deviceType = DEVICE_TYPE_NONE); + int32_t SetAudioEnhanceProperty(const AudioEnhancePropertyArray &propertyArray, + DeviceType deviceType = DEVICE_TYPE_NONE); + int32_t GetAudioEnhanceProperty(AudioEnhancePropertyArray &propertyArray, + DeviceType deviceType = DEVICE_TYPE_NONE); + void UpdateExtraSceneType(const std::string &mainkey, const std::string &subkey, + const std::string &extraSceneType); +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_POLICY_MANAGER_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_renderer_manager.h b/services/audio_engine/manager/include/hpae_renderer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..b977c80cbab531d8e519d0d3916f5c2b100eaa2c --- /dev/null +++ b/services/audio_engine/manager/include/hpae_renderer_manager.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_RENDER_MANAGER_H +#define HPAE_RENDER_MANAGER_H +#include +#include +#include +#include +#include +#include +#include "hpae_signal_process_thread.h" +#include "hpae_sink_input_node.h" +#include "hpae_process_cluster.h" +#include "hpae_output_cluster.h" +#include "hpae_msg_channel.h" +#include "hpae_no_lock_queue.h" +#include "i_hpae_renderer_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeRendererManager : public IHpaeRendererManager { +public: + HpaeRendererManager(HpaeSinkInfo &sinkInfo); + virtual ~HpaeRendererManager(); + int32_t CreateStream(const HpaeStreamInfo &streamInfo) override; + int32_t DestroyStream(uint32_t sessionId) override; + + int32_t Start(uint32_t sessionId) override; + int32_t Pause(uint32_t sessionId) override; + int32_t Flush(uint32_t sessionId) override; + int32_t Drain(uint32_t sessionId) override; + int32_t Stop(uint32_t sessionId) override; + int32_t Release(uint32_t sessionId) override; + int32_t MoveStream(uint32_t sessionId, const std::string &sinkName) override; + int32_t MoveAllStream(const std::string &sinkName, const std::vector& sessionIds, + bool isMoveAll = true) override; + int32_t SuspendStreamManager(bool isSuspend) override; + int32_t SetMute(bool isMute) override; + void Process() override; + void HandleMsg() override; + int32_t Init() override; + int32_t DeInit(bool isMoveDefault = false) override; + bool IsInit() override; + bool IsRunning(void) override; + bool IsMsgProcessing() override; + bool DeactivateThread() override; + int32_t SetClientVolume(uint32_t sessionId, float volume) override; + int32_t SetRate(uint32_t sessionId, int32_t rate) override; + int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) override; + int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) override; + int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) override; + int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) override; + int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + + size_t GetWritableSize(uint32_t sessionId) override; + int32_t UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) override; + int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) override; + std::vector GetAllSinkInputsInfo() override; + int32_t GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) override; + HpaeSinkInfo GetSinkInfo() override; + + int32_t AddNodeToSink(const std::shared_ptr &node) override; + int32_t AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) override; + + int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) override; + void OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) override; + void OnFadeDone(uint32_t sessionId, IOperation operation) override; + void OnRequestLatency(uint32_t sessionId, uint64_t &latency) override; + void OnNotifyQueue() override; + std::string GetThreadName() override; + void DumpSinkInfo() override; +private: + void SendRequest(Request &&request, bool isInit = false); + int32_t StartRenderSink(); + bool IsMchDevice(); + int32_t CreateInputSession(const HpaeStreamInfo &streamInfo); + int32_t DeleteInputSession(uint32_t sessionId); + int32_t ConnectInputSession(uint32_t sessionId); + int32_t DisConnectInputSession(uint32_t sessionId); + int32_t ConnectMchInputSession(uint32_t sessionId); + int32_t DisConnectMchInputSession(uint32_t sessionId); + int32_t DeleteMchInputSession(uint32_t sessionId); + void SetSessionState(uint32_t sessionId, RendererState renderState); + void AddSingleNodeToSink(const std::shared_ptr &node, bool isConnect = true); + void MoveAllStreamToNewSink(const std::string &sinkName, const std::vector& moveIds, bool isMoveAll); + void UpdateProcessClusterConnection(uint32_t sessionId, int32_t effectMode); + void ConnectProcessCluster(uint32_t sessionId, HpaeProcessorType sceneType); + void DisConnectProcessCluster(uint32_t sessionId, HpaeProcessorType sceneType); + void DeleteProcessCluster(const HpaeNodeInfo &nodeInfo, HpaeProcessorType sceneType, uint32_t sessionId); + std::shared_ptr CreateProcessCluster(HpaeNodeInfo &nodeInfo); + bool SetSessionFade(uint32_t sessionId, IOperation operation); + std::shared_ptr CreateDefaultProcessCluster(HpaeNodeInfo &nodeInfo); + void CreateOutputClusterNodeInfo(HpaeNodeInfo &nodeInfo); +private: + std::unordered_map sessionNodeMap_; + std::unordered_map> sceneClusterMap_; + std::unordered_map> sinkInputNodeMap_; + std::unordered_map> mchIdGainNodeMap_; + std::unique_ptr outputCluster_ = nullptr; + HpaeNoLockQueue hpaeNoLockQueue_; + std::unique_ptr hpaeSignalProcessThread_ = nullptr; + std::atomic isInit_ = false; + std::atomic isMute_ = false; + HpaeSinkInfo sinkInfo_; + std::unordered_map sceneTypeToProcessClusterCountMap_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_signal_process_thread.h b/services/audio_engine/manager/include/hpae_signal_process_thread.h new file mode 100644 index 0000000000000000000000000000000000000000..719f42060352f5f5a4b7e4e4c1e64f170fbc5e37 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_signal_process_thread.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_SIGNAL_PROCESS_THREAD_H +#define HPAE_SIGNAL_PROCESS_THREAD_H +#include +#include +#include +#include +#include "hpae_stream_manager.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeSignalProcessThread { +public: + HpaeSignalProcessThread() : running_(false), recvSignal_(false) + {} + ~HpaeSignalProcessThread(); + void ActivateThread(const std::weak_ptr& streamManager); + void DeactivateThread(); + void Notify(); + void Run(); + bool IsRunning() const + { + return running_.load(); + } + bool IsMsgProcessing() const + { + return recvSignal_.load(); + } +private: + + std::atomic running_; + std::atomic recvSignal_; + std::weak_ptr streamManager_; + std::thread thread_; + std::condition_variable condition_; + std::mutex mutex_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/hpae_stream_manager.h b/services/audio_engine/manager/include/hpae_stream_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..df67bd734b44d074aa027e1af6639ec891773517 --- /dev/null +++ b/services/audio_engine/manager/include/hpae_stream_manager.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_STREAM_MANAGER_H +#define HPAE_STREAM_MANAGER_H +#include +#include "audio_stream_info.h" +#include "hpae_define.h" +#include "i_stream.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeStreamManager : public CallbackSender, + public INodeCallback, + public std::enable_shared_from_this { +public: + virtual ~HpaeStreamManager() + {} + virtual int32_t CreateStream(const HpaeStreamInfo &streamInfo) = 0; + virtual int32_t DestroyStream(uint32_t sessionId) = 0; + virtual int32_t Start(uint32_t sessionId) = 0; + virtual int32_t Pause(uint32_t sessionId) = 0; + virtual int32_t Flush(uint32_t sessionId) = 0; + virtual int32_t Drain(uint32_t sessionId) = 0; + virtual int32_t Stop(uint32_t sessionId) = 0; + virtual int32_t Release(uint32_t sessionId) = 0; + virtual int32_t MoveStream(uint32_t sessionId, const std::string &sinkName) = 0; + virtual int32_t MoveAllStream(const std::string &name, const std::vector& sessionIds, + bool isMoveAll = true) = 0; + virtual int32_t SetMute(bool isMute) = 0; + virtual void Process() = 0; + virtual void HandleMsg() = 0; + virtual int32_t Init() = 0; + virtual int32_t DeInit(bool isMoveDefault = false) = 0; + virtual bool IsInit() = 0; + virtual bool IsRunning() = 0; + virtual bool IsMsgProcessing() = 0; + virtual bool DeactivateThread() = 0; + virtual std::string GetThreadName() = 0; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/i_hpae_capturer_manager.h b/services/audio_engine/manager/include/i_hpae_capturer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..d9b07373abead4fd9e3b4c0626f9105547d1660c --- /dev/null +++ b/services/audio_engine/manager/include/i_hpae_capturer_manager.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_I_CAPTURER_MANAGER_H +#define HPAE_I_CAPTURER_MANAGER_H +#include "audio_info.h" +#include "i_capturer_stream.h" +#include "hpae_stream_manager.h" +#include "hpae_capture_move_info.h" +#include "audio_engine_log.h" +#include "hpae_dfx_tree.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class IHpaeCapturerManager : public HpaeStreamManager { +public: + virtual ~IHpaeCapturerManager() {} + virtual int32_t CreateStream(const HpaeStreamInfo& streamInfo) = 0; + virtual int32_t DestroyStream(uint32_t sessionId) = 0; + + virtual int32_t Start(uint32_t sessionId) = 0; + virtual int32_t Pause(uint32_t sessionId) = 0; + virtual int32_t Flush(uint32_t sessionId) = 0; + virtual int32_t Drain(uint32_t sessionId) = 0; + virtual int32_t Stop(uint32_t sessionId) = 0; + virtual int32_t Release(uint32_t sessionId) = 0; + virtual void Process() = 0; + virtual void HandleMsg() = 0; + virtual int32_t Init() = 0; + virtual int32_t DeInit(bool isMoveDefault = false) = 0; + virtual bool IsInit() = 0; + virtual bool IsRunning(void) = 0; + virtual bool IsMsgProcessing() = 0; + virtual bool DeactivateThread() = 0; + + virtual int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) = 0; + virtual int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) = 0; + virtual HpaeSourceInfo GetSourceInfo() = 0; + virtual std::vector GetAllSourceOutputsInfo() = 0; + virtual int32_t AddNodeToSource(const HpaeCaptureMoveInfo &moveInfo) = 0; + virtual int32_t AddAllNodesToSource(const std::vector &moveInfos, bool isConnect) = 0; + virtual std::string GetThreadName() = 0; + virtual int32_t ReloadCaptureManager(const HpaeSourceInfo &sourceInfo) = 0; + virtual void DumpSourceInfo() {}; + virtual void UploadDumpSourceInfo(std::string &deviceName) + { +#ifdef ENABLE_HIDUMP_DFX + std::string dumpStr; + dfxTree_.PrintTree(dumpStr); + TriggerCallback(DUMP_SOURCE_INFO, deviceName, dumpStr); +#endif + }; + virtual void OnNotifyDfxNodeInfo(bool isConnect, uint32_t preNodeId, HpaeDfxNodeInfo &nodeInfo) + { +#ifdef ENABLE_HIDUMP_DFX + AUDIO_INFO_LOG("%{public}s preNodeId %{public}u nodeName:%{public}s, NodeId: %{public}u", + isConnect ? "connect" : "disconnect", + preNodeId, + nodeInfo.nodeName.c_str(), + nodeInfo.nodeId); + if (isConnect) { + dfxTree_.Insert(preNodeId, nodeInfo); + } else { + dfxTree_.Remove(nodeInfo.nodeId); + } +#endif + }; + + virtual uint32_t OnGetNodeId() + { + if (nodeIdCounter_.load() == std::numeric_limits::max()) { + nodeIdCounter_.store(MIN_START_NODE_ID); + } else { + nodeIdCounter_.fetch_add(1); + } + return nodeIdCounter_.load(); + }; +private: + std::atomic nodeIdCounter_ = 0; +#ifdef ENABLE_HIDUMP_DFX + HpaeDfxTree dfxTree_; +#endif +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/include/i_hpae_manager.h b/services/audio_engine/manager/include/i_hpae_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..6346d32977d350f6b5319f1565b61196a5f2f7ca --- /dev/null +++ b/services/audio_engine/manager/include/i_hpae_manager.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef IHPAE_MANAGER_H +#define IHPAE_MANAGER_H +#include "audio_module_info.h" +#include "hpae_info.h" +#include "i_capturer_stream.h" +#include "i_renderer_stream.h" +#include "audio_service_hpae_callback.h" +#include "audio_service_hpae_dump_callback.h" +#include "audio_effect.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class IHpaeManager { +public: + virtual ~IHpaeManager() = default; + + static std::shared_ptr GetHpaeManager(); + + virtual int32_t Init() = 0; + virtual int32_t DeInit() = 0; + virtual int32_t RegisterSerivceCallback(const std::weak_ptr &callback) = 0; + virtual int32_t RegisterHpaeDumpCallback(AudioServiceHpaeDumpCallback *callback) = 0; + virtual void DumpSinkInfo(std::string deviceName) = 0; + virtual void DumpSourceInfo(std::string deviceName) = 0; + virtual uint32_t OpenAudioPort(const AudioModuleInfo &audioModuleInfo) = 0; + virtual int32_t CloseAudioPort(int32_t audioHandleIndex) = 0; + + virtual int32_t SetDefaultSink(std::string name) = 0; + virtual int32_t SetDefaultSource(std::string name) = 0; + virtual int32_t GetAllSinkInputs() = 0; + virtual int32_t GetAllSourceOutputs() = 0; + virtual int32_t MoveSourceOutputByIndexOrName( + uint32_t sourceOutputId, uint32_t sourceIndex, std::string sourceName) = 0; + virtual int32_t MoveSinkInputByIndexOrName(uint32_t sinkInputId, uint32_t sinkIndex, std::string sinkName) = 0; + virtual void HandleMsg() = 0; + virtual bool IsInit() = 0; + virtual bool IsRunning() = 0; + virtual bool IsMsgProcessing() = 0; + virtual int32_t SuspendAudioDevice(std::string &audioPortName, bool isSuspend) = 0; + virtual bool SetSinkMute(const std::string &sinkName, bool isMute, bool isSync = false) = 0; + virtual int32_t SetSourceOutputMute(int32_t uid, bool setMute) = 0; + virtual int32_t GetAllSinks() = 0; + + virtual int32_t CreateStream(const HpaeStreamInfo &streamInfo) = 0; + virtual int32_t DestroyStream(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Start(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Pause(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Flush(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Drain(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Stop(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t Release(HpaeStreamClassType streamClassType, uint32_t sessionId) = 0; + virtual int32_t RegisterStatusCallback( + HpaeStreamClassType streamClassType, uint32_t sessionId, const std::weak_ptr &callback) = 0; + + virtual void RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) = 0; + virtual int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeStreamInfo &streamInfo) = 0; + + virtual int32_t SetClientVolume(uint32_t sessionId, float volume) = 0; + virtual int32_t SetRate(uint32_t sessionId, int32_t rate) = 0; + virtual int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) = 0; + virtual int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) = 0; + virtual int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) = 0; + virtual int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) = 0; + virtual int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) = 0; + + virtual int32_t SetOffloadPolicy(uint32_t sessionId, int32_t state) = 0; + virtual size_t GetWritableSize(uint32_t sessionId) = 0; + virtual int32_t UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) = 0; + virtual int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) = 0; + + // interfaces for render effect + virtual void InitAudioEffectChainManager(const std::vector &effectChains, + const EffectChainManagerParam &effectChainManagerParam, + const std::vector> &effectLibraryList) = 0; + virtual void SetOutputDeviceSink(int32_t device, const std::string &sinkName) = 0; + virtual int32_t UpdateSpatializationState(AudioSpatializationState spatializationState) = 0; + virtual int32_t UpdateSpatialDeviceType(AudioSpatialDeviceType spatialDeviceType) = 0; + virtual int32_t SetSpatializationSceneType(AudioSpatializationSceneType spatializationSceneType) = 0; + virtual int32_t EffectRotationUpdate(const uint32_t rotationState) = 0; + virtual int32_t SetEffectSystemVolume(const int32_t systemVolumeType, const float systemVolume) = 0; + virtual int32_t SetAudioEffectProperty(const AudioEffectPropertyArrayV3 &propertyArray) = 0; + virtual int32_t GetAudioEffectProperty(AudioEffectPropertyArrayV3 &propertyArray) = 0; + virtual int32_t SetAudioEffectProperty(const AudioEffectPropertyArray &propertyArray) = 0; + virtual int32_t GetAudioEffectProperty(AudioEffectPropertyArray &propertyArray) = 0; + virtual void InitHdiState() = 0; + virtual void UpdateEffectBtOffloadSupported(const bool &isSupported) = 0; + virtual void UpdateParamExtra(const std::string &mainkey, const std::string &subkey, const std::string &value) = 0; + // interfaces for capture effect + virtual void InitAudioEnhanceChainManager(const std::vector &enhanceChains, + const EffectChainManagerParam &managerParam, + const std::vector> &enhanceLibraryList) = 0; + virtual int32_t SetInputDevice( + const uint32_t &captureId, const DeviceType &inputDevice, const std::string &deviceName = "") = 0; + virtual int32_t SetOutputDevice(const uint32_t &renderId, const DeviceType &outputDevice) = 0; + virtual int32_t SetVolumeInfo(const AudioVolumeType &volumeType, const float &systemVol) = 0; + virtual int32_t SetMicrophoneMuteInfo(const bool &isMute) = 0; + virtual int32_t SetStreamVolumeInfo(const uint32_t &sessionId, const float &streamVol) = 0; + virtual int32_t SetAudioEnhanceProperty( + const AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) = 0; + virtual int32_t GetAudioEnhanceProperty( + AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) = 0; + virtual int32_t SetAudioEnhanceProperty( + const AudioEnhancePropertyArray &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) = 0; + virtual int32_t GetAudioEnhanceProperty( + AudioEnhancePropertyArray &propertyArray, DeviceType deviceType = DEVICE_TYPE_NONE) = 0; + virtual void UpdateExtraSceneType( + const std::string &mainkey, const std::string &subkey, const std::string &extraSceneType) = 0; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // IHPAE_MANAGER_H \ No newline at end of file diff --git a/services/audio_engine/manager/include/i_hpae_renderer_manager.h b/services/audio_engine/manager/include/i_hpae_renderer_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..df1f169fd5239a460eae854603610107f55617a7 --- /dev/null +++ b/services/audio_engine/manager/include/i_hpae_renderer_manager.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_I_RENDER_MANAGER_H +#define HPAE_I_RENDER_MANAGER_H +#include "audio_info.h" +#include "audio_errors.h" +#include "i_renderer_stream.h" +#include "i_capturer_stream.h" +#include "hpae_sink_input_node.h" +#include "hpae_stream_manager.h" +#include "audio_engine_log.h" +#include "hpae_dfx_tree.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class IHpaeRendererManager : public HpaeStreamManager { +public: + static std::shared_ptr CreateRendererManager(HpaeSinkInfo &sinkInfo); + + virtual ~IHpaeRendererManager() + {} + virtual int32_t CreateStream(const HpaeStreamInfo &streamInfo) = 0; + virtual int32_t DestroyStream(uint32_t sessionId) = 0; + virtual int32_t Start(uint32_t sessionId) = 0; + virtual int32_t Pause(uint32_t sessionId) = 0; + virtual int32_t Flush(uint32_t sessionId) = 0; + virtual int32_t Drain(uint32_t sessionId) = 0; + virtual int32_t Stop(uint32_t sessionId) = 0; + virtual int32_t Release(uint32_t sessionId) = 0; + virtual int32_t SuspendStreamManager(bool isSuspend) = 0; + virtual void Process() = 0; + virtual void HandleMsg() = 0; + virtual int32_t Init() = 0; + virtual int32_t DeInit(bool isMoveDefault = false) = 0; + virtual bool IsInit() = 0; + virtual bool IsRunning(void) = 0; + virtual bool IsMsgProcessing() = 0; + virtual bool DeactivateThread() = 0; + virtual int32_t SetClientVolume(uint32_t sessionId, float volume) = 0; + virtual int32_t SetRate(uint32_t sessionId, int32_t rate) = 0; + virtual int32_t SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) = 0; + virtual int32_t GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) = 0; + virtual int32_t SetPrivacyType(uint32_t sessionId, int32_t privacyType) = 0; + virtual int32_t GetPrivacyType(uint32_t sessionId, int32_t &privacyType) = 0; + virtual int32_t RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) = 0; + + virtual int32_t SetOffloadPolicy(uint32_t sessionId, int32_t state) + { + return ERR_NOT_SUPPORTED; + }; + virtual size_t GetWritableSize(uint32_t sessionId) = 0; + virtual int32_t UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) = 0; + virtual int32_t UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) = 0; + virtual std::vector GetAllSinkInputsInfo() = 0; + virtual int32_t GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) = 0; + virtual HpaeSinkInfo GetSinkInfo() = 0; + virtual int32_t AddNodeToSink(const std::shared_ptr &node) = 0; + virtual int32_t AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) = 0; + virtual int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) = 0; + virtual int32_t GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) + { + return 0; + }; + virtual std::vector GetAllSourceOutputsInfo() + { + return {}; + }; + virtual std::string GetThreadName() = 0; + + virtual void DumpSinkInfo() {}; + + virtual void UploadDumpSinkInfo(std::string& deviceName); + + virtual void OnNotifyDfxNodeInfo(bool isConnect, uint32_t preNodeId, HpaeDfxNodeInfo &nodeInfo); + + virtual uint32_t OnGetNodeId(); + + virtual void OnNotifyDfxNodeInfoChanged(uint32_t nodeId, const HpaeDfxNodeInfo &nodeInfo) + { +#ifdef ENABLE_HIDUMP_DFX + dfxTree_.UpdateNodeInfo(nodeId, nodeInfo); +#endif + } + +private: + std::atomic nodeIdCounter_ = 0; +#ifdef ENABLE_HIDUMP_DFX + HpaeDfxTree dfxTree_; +#endif +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/manager/src/hpae_capturer_manager.cpp b/services/audio_engine/manager/src/hpae_capturer_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8815165921f869bcc30fa85c19544542fb492176 --- /dev/null +++ b/services/audio_engine/manager/src/hpae_capturer_manager.cpp @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeCapturerManager" +#endif + +#include "hpae_capturer_manager.h" +#include "audio_info.h" +#include "audio_engine_log.h" +#include "audio_errors.h" +#include "hpae_node_common.h" +#include "audio_utils.h" +#include "audio_effect_map.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +const std::string DEFAULT_DEVICE_CLASS = "primary"; +const std::string DEFAULT_DEVICE_NETWORKID = "LocalDevice"; + +HpaeCapturerManager::HpaeCapturerManager(HpaeSourceInfo &sourceInfo) + : hpaeNoLockQueue_(CURRENT_REQUEST_COUNT), sourceInfo_(sourceInfo) +{ + AUDIO_INFO_LOG("Source info: mic[%{public}d_%{public}d_%{public}d] "\ + "ec[%{public}d_%{public}d_%{public}d] "\ + "micref[%{public}d_%{public}d_%{public}d]", + sourceInfo.samplingRate, sourceInfo.channels, sourceInfo.format, + sourceInfo.ecSamplingRate, sourceInfo.ecChannels, sourceInfo.ecFormat, + sourceInfo.micRefSamplingRate, sourceInfo.micRefChannels, sourceInfo.micRefFormat); +} + +HpaeCapturerManager::~HpaeCapturerManager() +{ + if (isInit_.load()) { + DeInit(); + } +} + +void HpaeCapturerManager::SetCaptureId(uint32_t captureId) +{ + captureId_ = captureId; +} + +int32_t HpaeCapturerManager::CaptureEffectCreate(const HpaeProcessorType &processorType, + const AudioEnhanceScene &sceneType) +{ + const std::unordered_map &audioEnhanceSupportedSceneTypes = + GetEnhanceSupportedSceneType(); + auto item = audioEnhanceSupportedSceneTypes.find(sceneType); + CHECK_AND_RETURN_RET_LOG(item != audioEnhanceSupportedSceneTypes.end(), ERROR, + "sceneType %{public}d not supported", sceneType); + uint64_t sceneCode = static_cast(sceneType); + uint64_t sceneKeyCode = 0; + sceneKeyCode = (sceneCode << SCENE_TYPE_OFFSET) + (captureId_ << CAPTURER_ID_OFFSET) + renderId_; + AUDIO_INFO_LOG("sceneCode:%{public}" PRIu64 "sceneKeyCode:%{public}" PRIu64, sceneCode, sceneKeyCode); + CaptureEffectAttr attr = {}; + attr.micChannels = static_cast(sourceInfo_.channels); + attr.ecChannels = static_cast(sourceInfo_.ecChannels); + attr.micRefChannels = static_cast(sourceInfo_.micRefChannels); + + int32_t ret = sceneClusterMap_[processorType]->CaptureEffectCreate(sceneKeyCode, attr); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "sceneType[%{public}u] create failed", sceneType); + return SUCCESS; +} + +int32_t HpaeCapturerManager::CreateOutputSession(const HpaeStreamInfo &streamInfo) +{ + AUDIO_INFO_LOG("Create output node:%{public}d", streamInfo.sessionId); + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = OnGetNodeId(); + nodeInfo.nodeName = "HpaeSourceOutputNode"; + nodeInfo.channels = streamInfo.channels; + nodeInfo.format = streamInfo.format; + nodeInfo.frameLen = streamInfo.frameLen; + nodeInfo.streamType = streamInfo.streamType; + nodeInfo.sessionId = streamInfo.sessionId; + nodeInfo.samplingRate = (AudioSamplingRate)streamInfo.samplingRate; + HpaeProcessorType sceneType = TransSourceTypeToSceneType(streamInfo.sourceType); + nodeInfo.sceneType = sceneType; + nodeInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_MIC; + nodeInfo.statusCallback = weak_from_this(); + + // todo: sourceType->processorType->sceneType => sourceType->sceneType + AudioEnhanceScene enhanceScene = TransProcessType2EnhanceScene(sceneType); + nodeInfo.effectInfo.enhanceScene = enhanceScene; + sourceOutputNodeMap_[streamInfo.sessionId] = std::make_shared(nodeInfo); + sessionNodeMap_[streamInfo.sessionId].sceneType = sceneType; + + if (sceneType != HPAE_SCENE_EFFECT_NONE && sceneClusterMap_.find(sceneType) == sceneClusterMap_.end()) { + // todo: algorithm instance count control + sceneClusterMap_[sceneType] = std::make_shared(nodeInfo); + if (CaptureEffectCreate(sceneType, enhanceScene) != SUCCESS) { + sceneClusterMap_.erase(sceneType); + } + } + + return SUCCESS; +} + +int32_t HpaeCapturerManager::CaptureEffectRelease(const HpaeProcessorType &sceneType) +{ + uint64_t sceneCode = static_cast(TransProcessType2EnhanceScene(sceneType)); + uint64_t sceneKeyCode = 0; + sceneKeyCode = (sceneCode << SCENE_TYPE_OFFSET) + (captureId_ << CAPTURER_ID_OFFSET) + renderId_; + int32_t ret = sceneClusterMap_[sceneType]->CaptureEffectRelease(sceneKeyCode); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "sceneType[%{public}u] release failed", sceneType); + return SUCCESS; +} + +void HpaeCapturerManager::DisConnectSceneClusterFromSourceInputCluster(HpaeProcessorType &sceneType) +{ + if (sceneClusterMap_[sceneType]->GetOutputPortNum() != 0) { + return; + } + // need to disconnect sceneCluster and sourceInputCluster + HpaeNodeInfo ecNodeInfo; + if (CheckSceneTypeNeedEc(sceneType) && + sceneClusterMap_[sceneType]->GetCapturerEffectConfig(ecNodeInfo, HPAE_SOURCE_BUFFER_TYPE_EC)) { + if (sourceInfo_.ecType == HPAE_EC_TYPE_SAME_ADAPTER) { + sceneClusterMap_[sceneType]->DisConnectWithInfo( + sourceInputClusterMap_[mainMicType_], ecNodeInfo); // ec from mic + } else if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + sceneClusterMap_[sceneType]->DisConnectWithInfo( + sourceInputClusterMap_[HPAE_SOURCE_EC], ecNodeInfo); // ec + } + } + + HpaeNodeInfo micRefNodeInfo; + if (CheckSceneTypeNeedMicRef(sceneType) && + sceneClusterMap_[sceneType]->GetCapturerEffectConfig(micRefNodeInfo, HPAE_SOURCE_BUFFER_TYPE_MICREF) && + sourceInfo_.micRef == HPAE_REF_ON) { + sceneClusterMap_[sceneType]->DisConnectWithInfo( + sourceInputClusterMap_[HPAE_SOURCE_MICREF], micRefNodeInfo); // micref + } + + HpaeNodeInfo micNodeInfo; + if (sceneClusterMap_[sceneType]->GetCapturerEffectConfig(micNodeInfo, HPAE_SOURCE_BUFFER_TYPE_MIC)) { + sceneClusterMap_[sceneType]->DisConnectWithInfo( + sourceInputClusterMap_[mainMicType_], micNodeInfo); // mic + } + return; +} + +int32_t HpaeCapturerManager::DeleteOutputSession(uint32_t sessionId) +{ + AUDIO_INFO_LOG("delete output node:%{public}d, source name:%{public}s", sessionId, sourceInfo_.deviceClass.c_str()); + if (sourceOutputNodeMap_.find(sessionId) == sourceOutputNodeMap_.end()) { + return SUCCESS; + } + + if (!sourceOutputNodeMap_[sessionId]) { + sourceOutputNodeMap_.erase(sessionId); + sessionNodeMap_.erase(sessionId); + return SUCCESS; + } + + HpaeProcessorType sceneType = sessionNodeMap_[sessionId].sceneType; + if (sceneType != HPAE_SCENE_EFFECT_NONE && sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + sourceOutputNodeMap_[sessionId]->DisConnectWithInfo( + sceneClusterMap_[sceneType], sourceOutputNodeMap_[sessionId]->GetNodeInfo()); + DisConnectSceneClusterFromSourceInputCluster(sceneType); + if (sceneClusterMap_[sceneType]->GetOutputPortNum() == 0) { + CaptureEffectRelease(sceneType); + sceneClusterMap_.erase(sceneType); + } + } else { + sourceOutputNodeMap_[sessionId]->DisConnect(sourceInputClusterMap_[mainMicType_]); + } + sourceOutputNodeMap_.erase(sessionId); + sessionNodeMap_.erase(sessionId); + return SUCCESS; +} + +void HpaeCapturerManager::SetSessionState(uint32_t sessionId, CapturerState capturerState) +{ + sessionNodeMap_[sessionId].state = capturerState; +} + +int32_t HpaeCapturerManager::CreateStream(const HpaeStreamInfo &streamInfo) +{ + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, streamInfo]() { + AUDIO_INFO_LOG("CreateStream sessionId %{public}u deviceName %{public}s", + streamInfo.sessionId, + sourceInfo_.deviceName.c_str()); + CreateOutputSession(streamInfo); + SetSessionState(streamInfo.sessionId, CAPTURER_NEW); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::DestroyStream(uint32_t sessionId) +{ + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, sessionId]() { + DeleteOutputSession(sessionId); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::ConnectProcessClusterWithEc(HpaeProcessorType &sceneType) +{ + HpaeNodeInfo ecNodeInfo; + if (CheckSceneTypeNeedEc(sceneType) && + sceneClusterMap_[sceneType]->GetCapturerEffectConfig(ecNodeInfo, HPAE_SOURCE_BUFFER_TYPE_EC)) { + ecNodeInfo.statusCallback = weak_from_this(); + if (sourceInfo_.ecType == HPAE_EC_TYPE_SAME_ADAPTER) { + sceneClusterMap_[sceneType]->ConnectWithInfo( + sourceInputClusterMap_[mainMicType_], ecNodeInfo); // ec from mic + } else if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + sceneClusterMap_[sceneType]->ConnectWithInfo( + sourceInputClusterMap_[HPAE_SOURCE_EC], ecNodeInfo); // ec + } + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::ConnectProcessClusterWithMicRef(HpaeProcessorType &sceneType) +{ + HpaeNodeInfo micRefNodeInfo; + if (CheckSceneTypeNeedMicRef(sceneType) && + sceneClusterMap_[sceneType]->GetCapturerEffectConfig(micRefNodeInfo, HPAE_SOURCE_BUFFER_TYPE_MICREF)&& + sourceInfo_.micRef == HPAE_REF_ON) { + micRefNodeInfo.statusCallback = weak_from_this(); + sceneClusterMap_[sceneType]->ConnectWithInfo( + sourceInputClusterMap_[HPAE_SOURCE_MICREF], micRefNodeInfo); // micref + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::ConnectOutputSession(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(), ERR_INVALID_PARAM, + "ConnectOutputSession error, sessionId %{public}u can not find in sourceOutputNodeMap.\n", sessionId); + + HpaeProcessorType sceneType = sessionNodeMap_[sessionId].sceneType; + if (sceneType != HPAE_SCENE_EFFECT_NONE && sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + // 1. Determine if the ResampleNode needs to be created + // 2. If ResampleNode needs to be created, it should be connected to the UpEffectNode after creation + // 3. Connect the SourceOutputNode to the ResampleNode + sourceOutputNodeMap_[sessionId]->ConnectWithInfo(sceneClusterMap_[sceneType], + sourceOutputNodeMap_[sessionId]->GetNodeInfo()); + HpaeNodeInfo micNodeInfo; + micNodeInfo.statusCallback = weak_from_this(); + if (sceneClusterMap_[sceneType]->GetCapturerEffectConfig(micNodeInfo, HPAE_SOURCE_BUFFER_TYPE_MIC)) { + sceneClusterMap_[sceneType]->ConnectWithInfo(sourceInputClusterMap_[mainMicType_], micNodeInfo); // mic + } + ConnectProcessClusterWithEc(sceneType); + ConnectProcessClusterWithMicRef(sceneType); + } else { + sourceOutputNodeMap_[sessionId]->ConnectWithInfo(sourceInputClusterMap_[mainMicType_], + sourceOutputNodeMap_[sessionId]->GetNodeInfo()); + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::Start(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeCapturerManager::Start"); + CHECK_AND_RETURN_RET_LOG(sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(), + ERR_INVALID_PARAM, "sessionId %{public}u can not find in sourceOutputNodeMap.", sessionId); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Start sessionId %{public}u", sessionId); + ConnectOutputSession(sessionId); + SetSessionState(sessionId, CAPTURER_RUNNING); + if (sourceInputClusterMap_[mainMicType_]->GetSourceState() != CAPTURER_RUNNING) { + int32_t ret = sourceInputClusterMap_[mainMicType_]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "Capturer source start error, ret = %{public}d.\n", ret); + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + ret = sourceInputClusterMap_[HPAE_SOURCE_EC]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "Capturer ec source start error, ret = %{public}d.\n", ret); + } + if (sourceInfo_.micRef == HPAE_REF_ON) { + ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "Capturer micref source start error, ret = %{public}d.\n", ret); + } + } + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, OPERATION_STARTED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::DisConnectOutputSession(uint32_t sessionId) +{ + if (sourceOutputNodeMap_.find(sessionId) == sourceOutputNodeMap_.end()) { + return SUCCESS; + } + HpaeProcessorType sceneType = sessionNodeMap_[sessionId].sceneType; + if (sceneType != HPAE_SCENE_EFFECT_NONE && sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + // 1. Disconnect SourceOutputNode and ResampleNode + // 2. Disconnect the ResampleNode and UpEffectNode + // 3. If the ResampleNode has no output, it needs to be deleted + sourceOutputNodeMap_[sessionId]->DisConnectWithInfo( + sceneClusterMap_[sceneType], sourceOutputNodeMap_[sessionId]->GetNodeInfo()); + DisConnectSceneClusterFromSourceInputCluster(sceneType); + } else { + sourceOutputNodeMap_[sessionId]->DisConnectWithInfo(sourceInputClusterMap_[mainMicType_], + sourceOutputNodeMap_[sessionId]->GetNodeInfo()); + } + + if (sourceInputClusterMap_[mainMicType_]->GetOutputPortNum() == 0) { + sourceInputClusterMap_[mainMicType_]->CapturerSourceStop(); + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + sourceInputClusterMap_[HPAE_SOURCE_EC]->CapturerSourceStop(); + } + if (sourceInfo_.micRef == HPAE_REF_ON) { + sourceInputClusterMap_[HPAE_SOURCE_MICREF]->CapturerSourceStop(); + } + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::Pause(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeCapturerManager::Pause"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Pause sessionId %{public}u", sessionId); + DisConnectOutputSession(sessionId); + SetSessionState(sessionId, CAPTURER_PAUSED); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, OPERATION_PAUSED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::Flush(uint32_t sessionId) +{ + if (sessionNodeMap_.find(sessionId) == sessionNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + // to do + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, OPERATION_FLUSHED); + return SUCCESS; +} + +int32_t HpaeCapturerManager::Drain(uint32_t sessionId) +{ + if (sessionNodeMap_.find(sessionId) == sessionNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + // to do + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, OPERATION_DRAINED); + return SUCCESS; +} + +int32_t HpaeCapturerManager::Stop(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeCapturerManager::Stop"); + auto request = [this, sessionId]() { + DisConnectOutputSession(sessionId); + SetSessionState(sessionId, CAPTURER_STOPPED); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, OPERATION_STOPPED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::Release(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeCapturerManager::Release"); + return DestroyStream(sessionId); +} + +int32_t HpaeCapturerManager::SetMute(bool isMute) +{ + // to do check pulseaudio + auto request = [this, isMute]() { + if (isMute_ != isMute) { + isMute_ = isMute; // todo: fadein and fadeout and mute feature + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeCapturerManager::Process() +{ + Trace trace("HpaeCapturerManager::Process"); + if (!sourceOutputNodeMap_.empty() && IsRunning()) { + for (const auto &sourceOutputNodePair : sourceOutputNodeMap_) { + sourceOutputNodePair.second->DoProcess(); + } + } +} + +void HpaeCapturerManager::HandleMsg() +{ + hpaeNoLockQueue_.HandleRequests(); +} + +int32_t HpaeCapturerManager::PrepareCapturerEc(HpaeNodeInfo &ecNodeInfo) +{ + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + ecNodeInfo.frameLen = sourceInfo_.ecFrameLen; + ecNodeInfo.channels = sourceInfo_.ecChannels; + ecNodeInfo.format = sourceInfo_.ecFormat; + ecNodeInfo.samplingRate = sourceInfo_.ecSamplingRate; + ecNodeInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_EC; + ecNodeInfo.sourceInputNodeType = HPAE_SOURCE_EC; + ecNodeInfo.statusCallback = weak_from_this(); + sourceInputClusterMap_[HPAE_SOURCE_EC] = std::make_shared(ecNodeInfo); + int32_t ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->GetCapturerSourceInstance( + DEFAULT_DEVICE_CLASS, DEFAULT_DEVICE_NETWORKID, SOURCE_TYPE_INVALID, HDI_ID_INFO_EC); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, + "get ec capturer soruce instance error, ret = %{public}d.\n", ret); + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::PrepareCapturerMicRef(HpaeNodeInfo &micRefNodeInfo) +{ + if (sourceInfo_.micRef == HPAE_REF_ON) { + micRefNodeInfo.frameLen = sourceInfo_.micRefFrameLen; + micRefNodeInfo.channels = sourceInfo_.micRefChannels; + micRefNodeInfo.format = sourceInfo_.micRefFormat; + micRefNodeInfo.samplingRate = sourceInfo_.micRefSamplingRate; + micRefNodeInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_MICREF; + micRefNodeInfo.sourceInputNodeType = HPAE_SOURCE_MICREF; + micRefNodeInfo.statusCallback = weak_from_this(); + sourceInputClusterMap_[HPAE_SOURCE_MICREF] = std::make_shared(micRefNodeInfo); + int32_t ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->GetCapturerSourceInstance( + DEFAULT_DEVICE_CLASS, DEFAULT_DEVICE_NETWORKID, SOURCE_TYPE_INVALID, HDI_ID_INFO_MIC_REF); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, + "get micRef capturer soruce instance error, ret = %{public}d.\n", ret); + } + return SUCCESS; +} + +void HpaeCapturerManager::CreateSourceAttr(IAudioSourceAttr &attr) +{ + attr.adapterName = sourceInfo_.adapterName.c_str(); + attr.sampleRate = sourceInfo_.samplingRate; + attr.channel = sourceInfo_.channels; + attr.format = sourceInfo_.format; + attr.channelLayout = sourceInfo_.channelLayout; + attr.deviceType = sourceInfo_.deviceType; + attr.volume = sourceInfo_.volume; + attr.deviceNetworkId = sourceInfo_.deviceNetId.c_str(); + attr.filePath = sourceInfo_.filePath.c_str(); + attr.isBigEndian = false; + attr.sourceType = static_cast(sourceInfo_.sourceType); + attr.openMicSpeaker = sourceInfo_.openMicSpeaker; + attr.hasEcConfig = mainMicType_ == HPAE_SOURCE_MIC_EC; + return; +} + +int32_t HpaeCapturerManager::InitCapturer() +{ + IAudioSourceAttr attr; + CreateSourceAttr(attr); + if (attr.hasEcConfig) { + attr.formatEc = sourceInfo_.ecFormat; + attr.sampleRateEc = sourceInfo_.ecSamplingRate; + attr.channelEc = sourceInfo_.ecChannels; + } + int32_t ret = sourceInputClusterMap_[mainMicType_]->CapturerSourceInit(attr); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, + "init mic source input node err, , ret = %{public}d.\n", ret); + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER && + sourceInputClusterMap_.find(HPAE_SOURCE_EC) != sourceInputClusterMap_.end()) { + IAudioSourceAttr attrEc; + attrEc.sourceType = SOURCE_TYPE_EC; + attrEc.adapterName = sourceInfo_.ecAdapterName.c_str(); + attrEc.deviceType = DEVICE_TYPE_MIC; + attrEc.sampleRate = sourceInfo_.ecSamplingRate; + attrEc.channel = sourceInfo_.ecChannels; + attrEc.format = sourceInfo_.ecFormat; + attrEc.isBigEndian = false; + attrEc.openMicSpeaker = sourceInfo_.openMicSpeaker; + ret = sourceInputClusterMap_[HPAE_SOURCE_EC]->CapturerSourceInit(attrEc); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, "init ec source input node err"); + } + if (sourceInfo_.micRef == HPAE_REF_ON && + sourceInputClusterMap_.find(HPAE_SOURCE_MICREF) != sourceInputClusterMap_.end()) { + IAudioSourceAttr attrMicRef; + attrMicRef.sourceType = SOURCE_TYPE_MIC_REF; + attrMicRef.adapterName = "primary"; + attrMicRef.deviceType = DEVICE_TYPE_MIC; + attrMicRef.sampleRate = sourceInfo_.micRefSamplingRate; + attrMicRef.channel = sourceInfo_.micRefChannels; + attrMicRef.format = sourceInfo_.micRefFormat; + attrMicRef.isBigEndian = false; + attrMicRef.openMicSpeaker = sourceInfo_.openMicSpeaker; + ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->CapturerSourceInit(attrMicRef); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, "init micRef source input node err"); + } + return SUCCESS; +} + +int32_t HpaeCapturerManager::ReloadCaptureManager(const HpaeSourceInfo &sourceInfo) +{ + if (IsInit()) { + DeInit(); + } + hpaeSignalProcessThread_ = std::make_unique(); + auto request = [this, sourceInfo] { + // disconnect + std::vector moveInfos; + for (const auto &it : sourceOutputNodeMap_) { + HpaeCaptureMoveInfo moveInfo; + moveInfo.sessionId = it.first; + moveInfo.sourceOutputNode = it.second; + if (sessionNodeMap_.find(it.first) != sessionNodeMap_.end()) { + moveInfo.sessionInfo = sessionNodeMap_[it.first]; + moveInfos.emplace_back(moveInfo); + } + } + for (const auto &it : moveInfos) { + DeleteOutputSession(it.sessionId); + } + sourceInfo_ = sourceInfo; + int32_t ret = InitCapturerManager(); + if (ret != SUCCESS) { + AUDIO_INFO_LOG("re-Init HpaeCapturerManager failed"); + return; + } + AUDIO_INFO_LOG("re-Init HpaeCapturerManager success"); + // connect + for (const auto &moveInfo : moveInfos) { + AddSingleNodeToSource(moveInfo, true); + } + TriggerCallback(INIT_DEVICE_RESULT, sourceInfo_.deviceName, ret); + }; + SendRequest(request, true); + hpaeSignalProcessThread_->ActivateThread(shared_from_this()); + return SUCCESS; +} + +int32_t HpaeCapturerManager::InitCapturerManager() +{ + HpaeNodeInfo nodeInfo; + HpaeNodeInfo ecNodeInfo; + HpaeNodeInfo micRefNodeInfo; + nodeInfo.channels = sourceInfo_.channels; + nodeInfo.format = sourceInfo_.format; + nodeInfo.frameLen = sourceInfo_.frameLen; + nodeInfo.samplingRate = sourceInfo_.samplingRate; + nodeInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_MIC; + nodeInfo.statusCallback = weak_from_this(); + mainMicType_ = sourceInfo_.ecType == HPAE_EC_TYPE_SAME_ADAPTER ? HPAE_SOURCE_MIC_EC : HPAE_SOURCE_MIC; + + if (mainMicType_ == HPAE_SOURCE_MIC_EC) { + ecNodeInfo.channels = sourceInfo_.ecChannels; + ecNodeInfo.format = sourceInfo_.ecFormat; + ecNodeInfo.samplingRate = sourceInfo_.ecSamplingRate; + ecNodeInfo.frameLen = sourceInfo_.ecFrameLen; + ecNodeInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_EC; + ecNodeInfo.statusCallback = weak_from_this(); + nodeInfo.sourceInputNodeType = HPAE_SOURCE_MIC_EC; + std::vector nodeInfos = {nodeInfo, ecNodeInfo}; + sourceInputClusterMap_[mainMicType_] = std::make_shared(nodeInfos); + } else { + nodeInfo.sourceInputNodeType = HPAE_SOURCE_MIC; + sourceInputClusterMap_[mainMicType_] = std::make_shared(nodeInfo); + } + + sourceInputClusterMap_[mainMicType_]->SetSourceInputNodeType(mainMicType_); // to do rewrite, optimise + int32_t ret = sourceInputClusterMap_[mainMicType_]->GetCapturerSourceInstance( + sourceInfo_.deviceClass, sourceInfo_.deviceNetId, sourceInfo_.sourceType, sourceInfo_.sourceName); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "get mic capturer soruce instance error, ret = %{public}d.\n", ret); + CHECK_AND_RETURN_RET_LOG(PrepareCapturerEc(ecNodeInfo) == SUCCESS, ret, "PrepareCapturerEc error"); + CHECK_AND_RETURN_RET_LOG(PrepareCapturerMicRef(micRefNodeInfo) == SUCCESS, ret, "PrepareCapturerMicRef error"); + CHECK_AND_RETURN_RET_LOG(InitCapturer() == SUCCESS, ret, "init main capturer error"); + isInit_.store(true); + return SUCCESS; +} + + +int32_t HpaeCapturerManager::Init() +{ + hpaeSignalProcessThread_ = std::make_unique(); + auto request = [this] { + int32_t ret = InitCapturerManager(); + if (ret == SUCCESS) { + AUDIO_INFO_LOG("Init HpaeCapturerManager success"); + TriggerCallback(INIT_DEVICE_RESULT, sourceInfo_.deviceName, ret); + } + }; + SendRequest(request, true); + hpaeSignalProcessThread_->ActivateThread(shared_from_this()); + return SUCCESS; +} + +int32_t HpaeCapturerManager::DeInit(bool isMoveDefault) +{ + AUDIO_INFO_LOG("DeInit device:%{public}s", sourceInfo_.deviceName.c_str()); + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + int32_t ret = sourceInputClusterMap_[mainMicType_]->CapturerSourceDeInit(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_INVALID_OPERATION, + "CapturerSourceDeInit error, ret = %{public}d.\n", ret); + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + ret = sourceInputClusterMap_[HPAE_SOURCE_EC]->CapturerSourceDeInit(); + CHECK_AND_RETURN_RET_LOG(ret == 0, ERR_INVALID_OPERATION, + "deinit ec source input node err.ret = %d.\n", ret); + } + if (sourceInfo_.micRef == HPAE_REF_ON) { + ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->CapturerSourceDeInit(); + CHECK_AND_RETURN_RET_LOG(ret == 0, ERR_INVALID_OPERATION, + "deinit micref source input node err.ret = %d.\n", ret); + } + isInit_.store(false); + + if (isMoveDefault) { + std::string name = ""; + std::vector ids; + AUDIO_INFO_LOG("move all source to default sink"); + MoveAllStreamToNewSource(name, ids, true); + } + return SUCCESS; +} + +bool HpaeCapturerManager::DeactivateThread() +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + return true; +} + +int32_t HpaeCapturerManager::RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + sourceOutputNodeMap_[sessionId]->RegisterReadCallback(callback); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) +{ + if (sourceOutputNodeMap_.find(sessionId) == sourceOutputNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + sourceOutputInfo.nodeInfo = sourceOutputNodeMap_[sessionId]->GetNodeInfo(); + sourceOutputInfo.capturerSessionInfo = sessionNodeMap_[sessionId]; + return SUCCESS; +} + +HpaeSourceInfo HpaeCapturerManager::GetSourceInfo() +{ + return sourceInfo_; +} + +std::vector HpaeCapturerManager::GetAllSourceOutputsInfo() +{ + return {}; +} + +bool HpaeCapturerManager::IsInit() +{ + return isInit_.load(); +} + +bool HpaeCapturerManager::IsMsgProcessing() +{ + return !hpaeNoLockQueue_.IsFinishProcess(); +} + +bool HpaeCapturerManager::IsRunning(void) +{ + if (sourceInputClusterMap_.find(mainMicType_) != sourceInputClusterMap_.end() && + hpaeSignalProcessThread_ != nullptr) { + return sourceInputClusterMap_[mainMicType_]->GetSourceState() == CAPTURER_RUNNING && + hpaeSignalProcessThread_->IsRunning(); + } else { + return false; + } +} + +void HpaeCapturerManager::SendRequest(Request &&request, bool isInit) +{ + if (!isInit && !IsInit()) { + AUDIO_INFO_LOG("HpaeCapturerManager not init"); + return; + } + hpaeNoLockQueue_.PushRequest(std::move(request)); + CHECK_AND_RETURN_LOG(hpaeSignalProcessThread_, "hpaeSignalProcessThread_ capturer is nullptr"); + hpaeSignalProcessThread_->Notify(); +} + +void HpaeCapturerManager::OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) +{ + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + sessionNodeMap_[sessionId].state, operation); +} + +int32_t HpaeCapturerManager::AddAllNodesToSource(const std::vector &moveInfos, bool isConnect) +{ + auto request = [this, moveInfos, isConnect]() { + for (const auto &moveInfo : moveInfos) { + AddSingleNodeToSource(moveInfo, isConnect); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeCapturerManager::AddNodeToSource(const HpaeCaptureMoveInfo &moveInfo) +{ + auto request = [this, moveInfo]() { AddSingleNodeToSource(moveInfo); }; + SendRequest(request); + return SUCCESS; +} + +void HpaeCapturerManager::AddSingleNodeToSource(const HpaeCaptureMoveInfo &moveInfo, bool isConnect) +{ + uint32_t sessionId = moveInfo.sessionId; + AUDIO_INFO_LOG("Add node to source:%{public}d", sessionId); + sourceOutputNodeMap_[sessionId] = moveInfo.sourceOutputNode; + sessionNodeMap_[sessionId] = moveInfo.sessionInfo; + HpaeProcessorType sceneType = sessionNodeMap_[sessionId].sceneType; + AudioEnhanceScene enhanceScene = TransProcessType2EnhanceScene(sceneType); + if (sceneType != HPAE_SCENE_EFFECT_NONE) { + // todo: algorithm instance count control + HpaeNodeInfo nodeInfo = moveInfo.sourceOutputNode->GetNodeInfo(); + if (sceneClusterMap_.find(sceneType) == sceneClusterMap_.end()) { + sceneClusterMap_[sceneType] = std::make_shared(nodeInfo); + } + } + if (CaptureEffectCreate(sceneType, enhanceScene) != SUCCESS) { + sceneClusterMap_.erase(sceneType); + } + if (!isConnect) { + AUDIO_INFO_LOG("not need connect session:%{public}d", sessionId); + } + AUDIO_INFO_LOG("connect node :%{public}d to sink:%{public}s", sessionId, sourceInfo_.deviceClass.c_str()); + ConnectOutputSession(sessionId); + if (moveInfo.sessionInfo.state == CAPTURER_RUNNING) { + if (sourceInputClusterMap_[mainMicType_]->GetSourceState() != CAPTURER_RUNNING) { + int32_t ret = sourceInputClusterMap_[mainMicType_]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "capturer source start error, ret = %{public}d.\n", ret); + if (sourceInfo_.ecType == HPAE_EC_TYPE_DIFF_ADAPTER) { + ret = sourceInputClusterMap_[HPAE_SOURCE_EC]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "ec capturer source start error, ret = %{public}d.\n", ret); + } + if (sourceInfo_.micRef == HPAE_REF_ON) { + ret = sourceInputClusterMap_[HPAE_SOURCE_MICREF]->CapturerSourceStart(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "micref capturer source start error, ret = %{public}d.\n", ret); + } + } + hpaeSignalProcessThread_->Notify(); + } +} + +int32_t HpaeCapturerManager::MoveAllStream(const std::string &sourceName, const std::vector& sessionIds, + bool isMoveAll) +{ + if (!IsInit()) { + AUDIO_INFO_LOG("source is not init ,use sync mode move to: %{public}s", sourceName.c_str()); + MoveAllStreamToNewSource(sourceName, sessionIds, isMoveAll); + } else { + AUDIO_INFO_LOG("source is init ,use async mode move to: %{public}s", sourceName.c_str()); + auto request = [this, sourceName, sessionIds, isMoveAll]() { + MoveAllStreamToNewSource(sourceName, sessionIds, isMoveAll); + }; + SendRequest(request); + } + return SUCCESS; +} + +void HpaeCapturerManager::MoveAllStreamToNewSource(const std::string &sourceName, + const std::vector& moveIds, bool isMoveAll = true) +{ + std::string name = sourceName; + std::vector moveInfos; + for (const auto &it : sourceOutputNodeMap_) { + if (isMoveAll || std::find(moveIds.begin(), moveIds.end(), it.first) != moveIds.end()) { + HpaeCaptureMoveInfo moveInfo; + moveInfo.sessionId = it.first; + moveInfo.sourceOutputNode = it.second; + if (sessionNodeMap_.find(it.first) != sessionNodeMap_.end()) { + moveInfo.sessionInfo = sessionNodeMap_[it.first]; + moveInfos.emplace_back(moveInfo); + } + } + } + + for (const auto &it : moveInfos) { + DeleteOutputSession(it.sessionId); + } + + AUDIO_INFO_LOG("move source count: %{public}zu,source name:%{public}s", moveInfos.size(), sourceName.c_str()); + TriggerCallback(MOVE_ALL_SOURCE_OUTPUT, moveInfos, name); +} + +int32_t HpaeCapturerManager::MoveStream(uint32_t sessionId, const std::string& sourceName) +{ + AUDIO_INFO_LOG("soft stop session:%{public}d,source name:%{public}s", sessionId, sourceName.c_str()); + auto request = [this, sessionId, sourceName]() { + AUDIO_ERR_LOG("trigger call back1, source name:%{public}s", sourceName.c_str()); + if (sourceOutputNodeMap_.find(sessionId) == sourceOutputNodeMap_.end()) { + AUDIO_ERR_LOG("could not find session:%{public}d,source name:%{public}s", sessionId, sourceName.c_str()); + return; + } + AUDIO_ERR_LOG("trigger call back2, source name:%{public}s", sourceName.c_str()); + std::shared_ptr sourceNode = sourceOutputNodeMap_[sessionId]; + if (sessionNodeMap_.find(sessionId)==sessionNodeMap_.end()) { + AUDIO_ERR_LOG("can not find session node:%{public}d,source name:%{public}s", sessionId, sourceName.c_str()); + return; + } + HpaeCapturerSessionInfo sessionInfo = sessionNodeMap_[sessionId]; + if (sessionInfo.state == CAPTURER_RUNNING) { + // todo: do fade out + } + HpaeCaptureMoveInfo moveInfo; + moveInfo.sessionId = sessionId; + moveInfo.sourceOutputNode = sourceNode; + moveInfo.sessionInfo = sessionInfo; + DeleteOutputSession(sessionId); + if (!sourceName.empty()) { + std::string name = sourceName; + AUDIO_ERR_LOG("trigger call back, source name:%{public}s", sourceName.c_str()); + TriggerCallback(MOVE_SOURCE_OUTPUT, moveInfo, name); + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeCapturerManager::OnNotifyQueue() +{ + hpaeSignalProcessThread_->Notify(); +} + +std::string HpaeCapturerManager::GetThreadName() +{ + return sourceInfo_.deviceName; +} + +void HpaeCapturerManager::DumpSourceInfo() +{ + SendRequest([this]() { + AUDIO_INFO_LOG("DumpSourceInfo deviceName %{public}s", sourceInfo_.deviceName.c_str()); + UploadDumpSourceInfo(sourceInfo_.deviceName); + }); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/manager/src/hpae_inner_capturer_manager.cpp b/services/audio_engine/manager/src/hpae_inner_capturer_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46bba58a347c8e25595922b610d59a7b7ac28889 --- /dev/null +++ b/services/audio_engine/manager/src/hpae_inner_capturer_manager.cpp @@ -0,0 +1,745 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeInnerCapturerManager" +#endif +#include "audio_stream_info.h" +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "hpae_node_common.h" +#include "hpae_inner_capturer_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +// todo sinkInfo +HpaeInnerCapturerManager::HpaeInnerCapturerManager(HpaeSinkInfo &sinkInfo) + : sinkInfo_(sinkInfo), hpaeNoLockQueue_(CURRENT_REQUEST_COUNT) +{} + +HpaeInnerCapturerManager::~HpaeInnerCapturerManager() +{ + AUDIO_INFO_LOG("destructor inner capturer sink."); + if (isInit_.load()) { + DeInit(); + } +} + +int32_t HpaeInnerCapturerManager::AddNodeToSink(const std::shared_ptr &node) +{ + auto request = [this, node]() { + AddSingleNodeToSinkInner(node); + }; + SendRequestInner(request); + return SUCCESS; +} + +void HpaeInnerCapturerManager::AddSingleNodeToSinkInner(const std::shared_ptr &node, bool isConnect) +{ + HpaeNodeInfo nodeInfo = node->GetNodeInfo(); + uint32_t sessionId = nodeInfo.sessionId; + AUDIO_INFO_LOG("add node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + sinkInputNodeMap_[sessionId] = node; + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + nodeInfo.statusCallback = weak_from_this(); + sinkInputNodeMap_[sessionId]->SetNodeInfo(nodeInfo); + SetSessionStateInner(sessionId, node->GetState()); + rendererSessionNodeMap_[sessionId].sinkInputNodeId = nodeInfo.nodeId; + rendererSessionNodeMap_[sessionId].sceneType = nodeInfo.sceneType; + + if (rendererSceneClusterMap_.find(nodeInfo.sceneType) == rendererSceneClusterMap_.end()) { + rendererSceneClusterMap_[nodeInfo.sceneType] = std::make_shared(nodeInfo, sinkInfo_); + } + + if (!isConnect) { + AUDIO_INFO_LOG("not need connect session:%{public}d", sessionId); + return; + } + if (node->GetState() == RENDERER_RUNNING) { + AUDIO_INFO_LOG("connect node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + ConnectRendererInputSessionInner(sessionId); // todo: fadein + if (hpaeInnerCapSinkNode_->GetSinkState() != RENDERER_RUNNING) { + hpaeInnerCapSinkNode_->InnerCapturerSinkStart(); + } + } +} + +int32_t HpaeInnerCapturerManager::AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) +{ + auto request = [this, sinkInputs, isConnect]() { + for (const auto &it : sinkInputs) { + AddSingleNodeToSinkInner(it, isConnect); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +void HpaeInnerCapturerManager::MoveAllStreamToNewSinkInner(const std::string &sinkName, + const std::vector& moveIds, bool isMoveAll) +{ + std::string name = sinkName; + std::vector> sinkInputs; + std::vector sessionIds; + for (const auto &it : sinkInputNodeMap_) { + if (isMoveAll || std::find(moveIds.begin(), moveIds.end(), it.first) != moveIds.end()) { + sinkInputs.emplace_back(it.second); + sessionIds.emplace_back(it.first); + } + } + for (const auto &it : sessionIds) { + DisConnectRendererInputSessionInner(it); + } + AUDIO_INFO_LOG("sink input count:%{public}zu", sinkInputs.size()); + TriggerCallback(MOVE_ALL_SINK_INPUT, sinkInputs, name); +} + +int32_t HpaeInnerCapturerManager::MoveAllStream(const std::string &sinkName, const std::vector& sessionIds, + bool isMoveAll) +{ + if (!IsInit()) { + AUDIO_INFO_LOG("sink is not init ,use sync mode move to:%{public}s.", sinkName.c_str()); + MoveAllStreamToNewSinkInner(sinkName, sessionIds, isMoveAll); + } else { + AUDIO_INFO_LOG("sink is init ,use async mode move to:%{public}s.", sinkName.c_str()); + auto request = [this, sinkName, sessionIds, isMoveAll]() { + MoveAllStreamToNewSinkInner(sinkName, sessionIds, isMoveAll); + }; + SendRequestInner(request); + } + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::MoveStream(uint32_t sessionId, const std::string &sinkName) +{ + AUDIO_INFO_LOG("move session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + auto request = [this, sessionId, sinkName]() { + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_ERR_LOG("could not find session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + return; + } + std::shared_ptr inputNode = sinkInputNodeMap_[sessionId]; + if (inputNode->GetState() == RENDERER_RUNNING) { + // todo: do fade out + } + DisConnectRendererInputSessionInner(sessionId); + if (!sinkName.empty()) { + std::string name = sinkName; + AUDIO_ERR_LOG("trigger call back, sink name:%{public}s", sinkName.c_str()); + TriggerCallback(MOVE_SINK_INPUT, inputNode, name); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::CreateStream(const HpaeStreamInfo &streamInfo) +{ + if (!IsInit()) { + AUDIO_INFO_LOG("CreateStream not init"); + return ERR_INVALID_OPERATION; + } + auto request = [this, streamInfo]() { + if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY) { + AUDIO_INFO_LOG("CreateCapRendererStream sessionID: %{public}d", streamInfo.sessionId); + CreateRendererInputSessionInner(streamInfo); + SetSessionStateInner(streamInfo.sessionId, RENDERER_NEW); + sinkInputNodeMap_[streamInfo.sessionId]->SetState(RENDERER_NEW); + } else if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD) { + AUDIO_INFO_LOG("CreateCapCapturerStream sessionID: %{public}d", streamInfo.sessionId); + CreateCapturerInputSessionInner(streamInfo); + SetSessionStateInner(streamInfo.sessionId, CAPTURER_NEW); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::DestroyStream(uint32_t sessionId) +{ + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + AUDIO_INFO_LOG("DestroyStream sessionId %{public}u", sessionId); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("DestroyCapRendererStream sessionID: %{public}d", sessionId); + DeleteRendererInputSessionInner(sessionId); + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("DestroyCapCapturerStream sessionID: %{public}d", sessionId); + DeleteCapturerInputSessionInner(sessionId); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Init() +{ + hpaeSignalProcessThread_ = std::make_unique(); + auto request = [this] { + HpaeNodeInfo nodeInfo; + nodeInfo.channels = sinkInfo_.channels; + nodeInfo.format = sinkInfo_.format; + nodeInfo.frameLen = sinkInfo_.frameLen; + nodeInfo.nodeId = 0; + nodeInfo.samplingRate = sinkInfo_.samplingRate; + nodeInfo.sceneType = HPAE_SCENE_EFFECT_OUT; + hpaeInnerCapSinkNode_ = std::make_unique(nodeInfo); + AUDIO_INFO_LOG("Init innerCapSinkNode"); + hpaeInnerCapSinkNode_->InnerCapturerSinkInit(); + isInit_.store(true); + TriggerCallback(INIT_DEVICE_RESULT, sinkInfo_.deviceName, SUCCESS); + }; + SendRequestInner(request, true); + hpaeSignalProcessThread_->ActivateThread(shared_from_this()); + return SUCCESS; +} + +bool HpaeInnerCapturerManager::DeactivateThread() +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + return true; +} + +int32_t HpaeInnerCapturerManager::DeInit(bool isMoveDefault) +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + int32_t ret = hpaeInnerCapSinkNode_->InnerCapturerSinkDeInit(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "InnerCapManagerDeInit error, ret %{public}d.\n", ret); + hpaeInnerCapSinkNode_->ResetAll(); + isInit_.store(false); + TriggerCallback(DEINIT_DEVICE_RESULT, sinkInfo_.deviceName, ret); + if (isMoveDefault) { + std::string sinkName = ""; + std::vector ids; + AUDIO_INFO_LOG("move all sink to default sink"); + MoveAllStreamToNewSinkInner(sinkName, ids, true); + } + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Start(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("StartCapRendererStream sessionId %{public}u", sessionId); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_RUNNING); + } + ConnectRendererInputSessionInner(sessionId); + SetSessionStateInner(sessionId, RENDERER_RUNNING); + if (hpaeInnerCapSinkNode_->GetSinkState() != RENDERER_RUNNING) { + hpaeInnerCapSinkNode_->InnerCapturerSinkStart(); + } + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("StartCapCapturerStream sessionId %{public}u", sessionId); + ConnectCapturerOutputSessionInner(sessionId); + SetSessionStateInner(sessionId, CAPTURER_RUNNING); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + capturerSessionNodeMap_[sessionId].state, OPERATION_STARTED); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Pause(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("PauseCapRendererStream sessionId %{public}u", sessionId); + DisConnectRendererInputSessionInner(sessionId); + SetSessionStateInner(sessionId, RENDERER_PAUSED); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_PAUSED); + } + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, OPERATION_PAUSED); + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("PauseCapCapturerStream sessionId %{public}u", sessionId); + DisConnectCapturerInputSessionInner(sessionId); + SetSessionStateInner(sessionId, CAPTURER_PAUSED); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + capturerSessionNodeMap_[sessionId].state, OPERATION_PAUSED); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Flush(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("FlushCapRendererStream sessionId %{public}u", sessionId); + CHECK_AND_RETURN_LOG(rendererSessionNodeMap_.find(sessionId) != rendererSessionNodeMap_.end(), + "Flush not find sessionId %{public}u", sessionId); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, OPERATION_FLUSHED); + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("FlushCapCapturerStream sessionId %{public}u", sessionId); + CHECK_AND_RETURN_LOG(capturerSessionNodeMap_.find(sessionId) != capturerSessionNodeMap_.end(), + "Flush not find sessionId %{public}u", sessionId); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + capturerSessionNodeMap_[sessionId].state, OPERATION_FLUSHED); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Drain(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("DrainCapRendererStream sessionId %{public}u", sessionId); + CHECK_AND_RETURN_LOG(rendererSessionNodeMap_.find(sessionId) != rendererSessionNodeMap_.end(), + "Drain not find sessionId %{public}u", sessionId); + sinkInputNodeMap_[sessionId]->Drain(); + if (rendererSessionNodeMap_[sessionId].state != RENDERER_RUNNING) { + AUDIO_INFO_LOG("TriggerCallback Drain sessionId %{public}u", sessionId); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, OPERATION_DRAINED); + } + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("DrainCapCapturerStream sessionId %{public}u", sessionId); + CHECK_AND_RETURN_LOG(capturerSessionNodeMap_.find(sessionId) != capturerSessionNodeMap_.end(), + "Drain not find sessionId %{public}u", sessionId); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + capturerSessionNodeMap_[sessionId].state, OPERATION_DRAINED); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Stop(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("StopCapRendererStream sessionId %{public}u", sessionId); + DisConnectRendererInputSessionInner(sessionId); + SetSessionStateInner(sessionId, RENDERER_STOPPED); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_STOPPED); + } + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, OPERATION_STOPPED); + } else if (sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end()) { + AUDIO_INFO_LOG("StopCapCapturerStream sessionId %{public}u", sessionId); + DisConnectCapturerInputSessionInner(sessionId); + SetSessionStateInner(sessionId, CAPTURER_STOPPED); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_RECORD, sessionId, + capturerSessionNodeMap_[sessionId].state, OPERATION_STOPPED); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::Release(uint32_t sessionId) +{ + return DestroyStream(sessionId); +} + +int32_t HpaeInnerCapturerManager::SuspendStreamManager(bool isSuspend) +{ + auto request = [this, isSuspend]() { + if (isSuspend) { + // todo fadout + hpaeInnerCapSinkNode_->InnerCapturerSinkStop(); + } else { + // todo fadout + hpaeInnerCapSinkNode_->InnerCapturerSinkStart(); + } + }; + SendRequestInner(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::SetMute(bool isMute) +{ + auto request = [this, isMute]() { + if (isMute_ != isMute) { + isMute_ = isMute; // todo: fadein and fadeout and mute feature + } + }; + SendRequestInner(request); + return SUCCESS; +} + +void HpaeInnerCapturerManager::Process() +{ + if (hpaeInnerCapSinkNode_ != nullptr && !sourceOutputNodeMap_.empty() && IsRunning()) { + for (const auto& sourceOutputNodePair : sourceOutputNodeMap_) { + if (capturerSessionNodeMap_[sourceOutputNodePair.first].state == CAPTURER_RUNNING) { + sourceOutputNodePair.second->DoProcess(); + } + } + } +} + +void HpaeInnerCapturerManager::HandleMsg() +{ + hpaeNoLockQueue_.HandleRequests(); +} + +bool HpaeInnerCapturerManager::IsInit() +{ + return isInit_.load(); +} + +bool HpaeInnerCapturerManager::IsRunning(void) +{ + if (hpaeInnerCapSinkNode_ != nullptr && hpaeSignalProcessThread_ != nullptr) { + return hpaeSignalProcessThread_->IsRunning(); + } else { + return false; + } +} + +bool HpaeInnerCapturerManager::IsMsgProcessing() +{ + return !hpaeNoLockQueue_.IsFinishProcess(); +} + +int32_t HpaeInnerCapturerManager::SetClientVolume(uint32_t sessionId, float volume) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::SetRate(uint32_t sessionId, int32_t rate) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::SetPrivacyType(uint32_t sessionId, int32_t privacyType) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::GetPrivacyType(uint32_t sessionId, int32_t &privacyType) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::RegisterWriteCallback(uint32_t sessionId, + const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + AUDIO_INFO_LOG("RegisterWriteCallback sessionId %{public}u", sessionId); + CHECK_AND_RETURN_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end() || + sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(),\ + "no find sessionId in sinkInputNodeMap and sourceOutputNodeMap"); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->RegisterWriteCallback(callback); + } + }; + hpaeNoLockQueue_.PushRequest(request); + return SUCCESS; +} + +int32_t RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + return SUCCESS; +} + +size_t HpaeInnerCapturerManager::GetWritableSize(uint32_t sessionId) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) +{ + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) +{ + return SUCCESS; +} + +std::vector HpaeInnerCapturerManager::GetAllSinkInputsInfo() +{ + std::vector sinkInputs; + return sinkInputs; +} + +int32_t HpaeInnerCapturerManager::GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) +{ + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + sinkInputInfo.nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + sinkInputInfo.rendererSessionInfo = rendererSessionNodeMap_[sessionId]; + return SUCCESS; +} + +HpaeSinkInfo HpaeInnerCapturerManager::GetSinkInfo() +{ + return sinkInfo_; +} + +void HpaeInnerCapturerManager::OnFadeDone(uint32_t sessionId, IOperation operation) +{ + auto request = [this, sessionId, operation]() { + DisConnectRendererInputSessionInner(sessionId); + RendererState state = operation == OPERATION_STOPPED ? RENDERER_STOPPED : RENDERER_PAUSED; + SetSessionStateInner(sessionId, state); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(state); + } + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, operation); + }; + SendRequestInner(request); +} + +void HpaeInnerCapturerManager::OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) +{ + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, + rendererSessionNodeMap_[sessionId].state, operation); +} + +int32_t HpaeInnerCapturerManager::RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + AUDIO_INFO_LOG("RegisterReadCallback sessionId %{public}u", sessionId); + sourceOutputNodeMap_[sessionId]->RegisterReadCallback(callback); + }; + hpaeNoLockQueue_.PushRequest(request); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::GetSourceOutputInfo(uint32_t sessionId, HpaeSourceOutputInfo &sourceOutputInfo) +{ + if (sourceOutputNodeMap_.find(sessionId) == sourceOutputNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + sourceOutputInfo.nodeInfo = sourceOutputNodeMap_[sessionId]->GetNodeInfo(); + sourceOutputInfo.capturerSessionInfo = capturerSessionNodeMap_[sessionId]; + return SUCCESS; +} + +std::vector HpaeInnerCapturerManager::GetAllSourceOutputsInfo() +{ + // to do + std::vector sourceOutputs; + return sourceOutputs; +} + +int32_t HpaeInnerCapturerManager::CreateRendererInputSessionInner(const HpaeStreamInfo &streamInfo) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.channels = streamInfo.channels; + nodeInfo.format = streamInfo.format; + nodeInfo.frameLen = streamInfo.frameLen; + nodeInfo.nodeId = GetSinkInputNodeIdInner(); + nodeInfo.streamType = streamInfo.streamType; + nodeInfo.sessionId = streamInfo.sessionId; + nodeInfo.samplingRate = (AudioSamplingRate)streamInfo.samplingRate; + nodeInfo.sceneType = HPAE_SCENE_EFFECT_NONE; + nodeInfo.statusCallback = weak_from_this(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + AUDIO_INFO_LOG("nodeInfo.channels %{public}d, nodeInfo.format %{public}hhu, nodeInfo.frameLen %{public}d", + nodeInfo.channels, nodeInfo.format, nodeInfo.frameLen); + sinkInputNodeMap_[streamInfo.sessionId] = std::make_shared(nodeInfo); + + if (rendererSceneClusterMap_.find(nodeInfo.sceneType) == rendererSceneClusterMap_.end()) { + rendererSceneClusterMap_[nodeInfo.sceneType] = std::make_shared(nodeInfo, sinkInfo_); + } + // todo change nodeInfo + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::CreateCapturerInputSessionInner(const HpaeStreamInfo &streamInfo) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.channels = streamInfo.channels; + nodeInfo.format = streamInfo.format; + nodeInfo.frameLen = streamInfo.frameLen; + nodeInfo.streamType = streamInfo.streamType; + nodeInfo.sessionId = streamInfo.sessionId; + nodeInfo.samplingRate = (AudioSamplingRate)streamInfo.samplingRate; + nodeInfo.sceneType = HPAE_SCENE_EFFECT_NONE; + AUDIO_INFO_LOG("nodeInfo.channels %{public}d, nodeInfo.format %{public}hhu, nodeInfo.frameLen %{public}d", + nodeInfo.channels, nodeInfo.format, nodeInfo.frameLen); + sourceOutputNodeMap_[streamInfo.sessionId] = std::make_shared(nodeInfo); + HpaeNodeInfo outputNodeInfo = hpaeInnerCapSinkNode_->GetNodeInfo(); + // todo change nodeInfo + capturerResampleNodeMap_[streamInfo.sessionId] = std::make_shared(outputNodeInfo, nodeInfo); + capturerSessionNodeMap_[streamInfo.sessionId].sceneType = nodeInfo.sceneType; + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::DeleteRendererInputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in sinkInputNodeMap_.", sessionId); + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + if (rendererSceneClusterMap_.find(sceneType) != rendererSceneClusterMap_.end()) { + rendererSceneClusterMap_[sceneType]->DisConnect(sinkInputNodeMap_[sessionId]); + if (rendererSceneClusterMap_[sceneType]->GetPreOutNum() == 0) { + hpaeInnerCapSinkNode_->DisConnect(rendererSceneClusterMap_[sceneType]); + rendererSceneClusterMap_.erase(sceneType); + } + } + sinkInputNodeMap_.erase(sessionId); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::DeleteCapturerInputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in sourceOutputNodeMap_.", sessionId); + CHECK_AND_RETURN_RET_LOG(capturerResampleNodeMap_.find(sessionId) != capturerResampleNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in capturerResampleNodeMap_.", sessionId); + // no need process cluster + sourceOutputNodeMap_[sessionId]->DisConnect(capturerResampleNodeMap_[sessionId]); + capturerResampleNodeMap_[sessionId]->DisConnect(hpaeInnerCapSinkNode_); + // if need disconnect all? + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::ConnectRendererInputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end(), ERR_INVALID_PARAM, + "sessionId %{public}u can not find in sinkInputNodeMap_.", sessionId); + CHECK_AND_RETURN_RET_LOG(sinkInputNodeMap_[sessionId]->GetState() == RENDERER_RUNNING, SUCCESS, + "sink input node is running"); + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + CHECK_AND_RETURN_RET_LOG(rendererSceneClusterMap_.find(sceneType) != rendererSceneClusterMap_.end(), SUCCESS, + "miss corresponding process cluster for scene type %{public}d", sceneType); + rendererSceneClusterMap_[sceneType]->Connect(sinkInputNodeMap_[sessionId]); + // todo check if connect process cluster + hpaeInnerCapSinkNode_->Connect(rendererSceneClusterMap_[sceneType]); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::ConnectCapturerOutputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(), ERR_INVALID_PARAM, + "sessionId %{public}u can not find in sourceOutputCLusterMap.", sessionId); + CHECK_AND_RETURN_RET_LOG(capturerResampleNodeMap_.find(sessionId) != capturerResampleNodeMap_.end(), + ERR_INVALID_PARAM, + "sessionId %{public}u can not find in capturerResampleNodeMap_.", sessionId); + // todo connect gain node + sourceOutputNodeMap_[sessionId]->Connect(capturerResampleNodeMap_[sessionId]); + capturerResampleNodeMap_[sessionId]->Connect(hpaeInnerCapSinkNode_); + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::DisConnectRendererInputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in sinkInputNodeMap_.", sessionId); + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + if (rendererSceneClusterMap_.find(sceneType) != rendererSceneClusterMap_.end()) { + rendererSceneClusterMap_[sceneType]->DisConnect(sinkInputNodeMap_[sessionId]); + if (rendererSceneClusterMap_[sceneType]->GetPreOutNum() == 0) { + hpaeInnerCapSinkNode_->DisConnect(rendererSceneClusterMap_[sceneType]); + } + } + return SUCCESS; +} + +int32_t HpaeInnerCapturerManager::DisConnectCapturerInputSessionInner(uint32_t sessionId) +{ + CHECK_AND_RETURN_RET_LOG(sourceOutputNodeMap_.find(sessionId) != sourceOutputNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in sourceOutputNodeMap_.", sessionId); + CHECK_AND_RETURN_RET_LOG(capturerResampleNodeMap_.find(sessionId) != capturerResampleNodeMap_.end(), SUCCESS, + "sessionId %{public}u can not find in capturerResampleNodeMap_.", sessionId); + sourceOutputNodeMap_[sessionId]->DisConnect(capturerResampleNodeMap_[sessionId]); + capturerResampleNodeMap_[sessionId]->DisConnect(hpaeInnerCapSinkNode_); + // todo if need disconnect render + return SUCCESS; +} + +void HpaeInnerCapturerManager::SetSessionStateInner(uint32_t sessionId, RendererState renderState) +{ + rendererSessionNodeMap_[sessionId].state = renderState; +} + +void HpaeInnerCapturerManager::SetSessionStateInner(uint32_t sessionId, CapturerState capturerState) +{ + capturerSessionNodeMap_[sessionId].state = capturerState; +} + +void HpaeInnerCapturerManager::SendRequestInner(Request &&request, bool isInit) +{ + if (!isInit && !IsInit()) { + AUDIO_INFO_LOG("HpaeInnerCapturerManager not init"); + return; + } + hpaeNoLockQueue_.PushRequest(std::move(request)); + CHECK_AND_RETURN_LOG(hpaeSignalProcessThread_, "hpaeSignalProcessThread_ inner capturer sink is nullptr"); + hpaeSignalProcessThread_->Notify(); +} + +uint32_t HpaeInnerCapturerManager::GetSinkInputNodeIdInner() +{ + return sinkInputNodeCounter_++; +} + +std::string HpaeInnerCapturerManager::GetThreadName() +{ + return sinkInfo_.deviceName; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/manager/src/hpae_manager.cpp b/services/audio_engine/manager/src/hpae_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f221cc376827e4317c3ff6f1f89f86c271557e8b --- /dev/null +++ b/services/audio_engine/manager/src/hpae_manager.cpp @@ -0,0 +1,1917 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeManager" +#endif +#include "hpae_manager.h" +#include +#include +#include +#include "audio_errors.h" +#include "audio_schedule.h" +#include "audio_engine_log.h" +#include "audio_utils.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +namespace { +constexpr uint32_t DEFAULT_SUSPEND_TIME_IN_MS = 3000; // 3s to stop hdi +static inline const std::unordered_set INNER_SOURCE_TYPE_SET = { + SOURCE_TYPE_PLAYBACK_CAPTURE, SOURCE_TYPE_REMOTE_CAST}; +} // namespace +static constexpr uint32_t DEFAULT_MULTICHANNEL_NUM = 6; +static constexpr uint32_t DEFAULT_MULTICHANNEL_CHANNELLAYOUT = 1551; +static constexpr float MAX_SINK_VOLUME_LEVEL = 1.0; +static constexpr uint32_t DEFAULT_MULTICHANNEL_FRAME_LEN_MS = 20; +static constexpr uint32_t MS_PER_SECOND = 1000; +static std::map formatFromParserStrToEnum = { + {"s16le", SAMPLE_S16LE}, + {"s24le", SAMPLE_S24LE}, + {"s32le", SAMPLE_S32LE}, + {"f32le", SAMPLE_F32LE}, +}; + +// base + offset * 8 +static uint32_t GetRenderId(const std::string &deviceClass) +{ + uint32_t renderId = 0; + if (deviceClass == "usb") { + renderId = GenerateUniqueID(AUDIO_HDI_RENDER_ID_BASE, HDI_RENDER_OFFSET_USB); + } else if (deviceClass == "dp") { + renderId = GenerateUniqueID(AUDIO_HDI_RENDER_ID_BASE, HDI_RENDER_OFFSET_DP); + } else if (deviceClass == "voip") { + renderId = GenerateUniqueID(AUDIO_HDI_RENDER_ID_BASE, HDI_RENDER_OFFSET_VOIP); + } else if (deviceClass == "direct") { + renderId = GenerateUniqueID(AUDIO_HDI_RENDER_ID_BASE, HDI_RENDER_OFFSET_DIRECT); + } else { + renderId = GenerateUniqueID(AUDIO_HDI_RENDER_ID_BASE, HDI_RENDER_OFFSET_PRIMARY); + } + return renderId; +} + +static uint32_t GetCaptureId(const std::string &deviceClass) +{ + uint32_t captureId = 0; + if (deviceClass == "usb") { + captureId = GenerateUniqueID(AUDIO_HDI_CAPTURE_ID_BASE, HDI_CAPTURE_OFFSET_USB); + } else if (deviceClass == "a2dp") { + captureId = GenerateUniqueID(AUDIO_HDI_CAPTURE_ID_BASE, HDI_CAPTURE_OFFSET_BLUETOOTH); + } else { + captureId = GenerateUniqueID(AUDIO_HDI_CAPTURE_ID_BASE, HDI_CAPTURE_OFFSET_PRIMARY); + } + return captureId; +} + +HpaeManagerThread::~HpaeManagerThread() +{ + DeactivateThread(); +} + +void HpaeManagerThread::ActivateThread(HpaeManager *hpaeManager) +{ + m_hpaeManager = hpaeManager; + auto threadFunc = std::bind(&HpaeManagerThread::Run, this); + thread_ = std::thread(threadFunc); + pthread_setname_np(thread_.native_handle(), "HpaeManager"); +} + +void HpaeManagerThread::Run() +{ + running_.store(true); + ScheduleThreadInServer(getpid(), gettid()); + while (running_.load() && m_hpaeManager != nullptr) { + { + std::unique_lock lock(mutex_); + condition_.wait(lock, [this] { return recvSignal_.load() || m_hpaeManager->IsMsgProcessing(); }); + } + m_hpaeManager->HandleMsg(); + recvSignal_.store(false); + } + UnscheduleThreadInServer(getpid(), gettid()); +} + +void HpaeManagerThread::Notify() +{ + recvSignal_.store(true); + condition_.notify_all(); +} + +void HpaeManagerThread::DeactivateThread() +{ + Notify(); + running_.store(false); + if (thread_.joinable()) { + thread_.join(); + } +} + +HpaeManager::HpaeManager() : hpaeNoLockQueue_(CURRENT_REQUEST_COUNT) // todo Message queue exceeds the upper limit +{ + RegisterHandler(UPDATE_STATUS, &HpaeManager::HandleUpdateStatus); + RegisterHandler(INIT_DEVICE_RESULT, &HpaeManager::HandleInitDeviceResult); + RegisterHandler(DEINIT_DEVICE_RESULT, &HpaeManager::HandleDeInitDeviceResult); + RegisterHandler(MOVE_SINK_INPUT, &HpaeManager::HandleMoveSinkInput); + RegisterHandler(MOVE_ALL_SINK_INPUT, &HpaeManager::HandleMoveAllSinkInputs); + RegisterHandler(MOVE_SOURCE_OUTPUT, &HpaeManager::HandleMoveSourceOutput); + RegisterHandler(MOVE_ALL_SOURCE_OUTPUT, &HpaeManager::HandleMoveAllSourceOutputs); + RegisterHandler(DUMP_SINK_INFO, &HpaeManager::HandleDumpSinkInfo); + RegisterHandler(DUMP_SOURCE_INFO, &HpaeManager::HandleDumpSourceInfo); +} + +HpaeManager::~HpaeManager() +{ + if (IsInit()) { + DeInit(); + } +} + +int32_t HpaeManager::Init() +{ + sinkSourceIndex_ = 0; + hpaeManagerThread_ = std::make_unique(); + hpaeManagerThread_->ActivateThread(this); + hpaePolicyManager_ = std::make_unique(); + isInit_.store(true); + return 0; +} + +int32_t HpaeManager::SuspendAudioDevice(std::string &audioPortName, bool isSuspend) +{ + AUDIO_INFO_LOG("suspend audio device: %{public}s, isSuspend: %{public}d", audioPortName.c_str(), isSuspend); + auto request = [this, audioPortName, isSuspend]() { + if (rendererManagerMap_.find(audioPortName) != rendererManagerMap_.end()) { + rendererManagerMap_[audioPortName]->SuspendStreamManager(isSuspend); + } else if (capturerManagerMap_.find(audioPortName) != capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("capture not support suspend"); + return; + } else { + AUDIO_WARNING_LOG("can not find sink: %{public}s", audioPortName.c_str()); + return; + } + }; + SendRequest(request); + return SUCCESS; +} + +bool HpaeManager::SetSinkMute(const std::string &sinkName, bool isMute, bool isSync) +{ + auto request = [this, sinkName, isMute, isSync]() { + // todo for device change + AUDIO_INFO_LOG("HpaeManager::SetSinkMute sinkName: %{public}s isMute: %{public}d, isSync: %{public}d", + sinkName.c_str(), + isMute, + isSync); + if (rendererManagerMap_.find(sinkName) != rendererManagerMap_.end()) { + rendererManagerMap_[sinkName]->SetMute(isMute); + } else { + AUDIO_WARNING_LOG("can not find sink: %{public}s for mute:%{public}d", sinkName.c_str(), isMute); + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnSetSinkMuteCb(SUCCESS); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetSourceOutputMute(int32_t uid, bool setMute) +{ + auto request = [this, uid, setMute]() { + AUDIO_INFO_LOG("HpaeManager::SetSourceOutputMute uid: %{public}d setMute: %{public}d", uid, setMute); + if (capturerIdSourceNameMap_.find(uid) != capturerIdSourceNameMap_.end()) { + capturerManagerMap_[capturerIdSourceNameMap_[uid]]->SetMute(setMute); + } else { + AUDIO_WARNING_LOG("can not find sink: %{public}d for mute:%{public}d", uid, setMute); + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnSetSourceOutputMuteCb(SUCCESS); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAllSinks() +{ + auto request = [this]() { + std::vector sinks; + // todo for device change + AUDIO_INFO_LOG("HpaeManager::GetAllSinks end"); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnGetAllSinksCb(SUCCESS, sinks); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::DeInit() +{ + if (hpaeManagerThread_ != nullptr) { + hpaeManagerThread_->DeactivateThread(); + hpaeManagerThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); // todo suspend + isInit_.store(false); + AUDIO_INFO_LOG("HpaeManager::DeInit end"); + return SUCCESS; +} + +int32_t HpaeManager::RegisterSerivceCallback(const std::weak_ptr &callback) +{ + auto request = [this, callback]() { + serviceCallback_ = callback; + AUDIO_INFO_LOG("HpaeManager::RegisterSerivceCallback end"); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::RegisterHpaeDumpCallback(AudioServiceHpaeDumpCallback *callback) +{ + auto request = [this, callback]() { + dumpCallback_ = callback; + AUDIO_INFO_LOG("HpaeManager::RegisterHpaeDumpCallback end"); + }; + SendRequest(request); + return SUCCESS; +} + +AudioSampleFormat HpaeManager::TransFormatFromStringToEnum(std::string format) +{ + return static_cast(formatFromParserStrToEnum[format]); +} + +void HpaeManager::AdjustMchSinkInfo(const AudioModuleInfo &audioModuleInfo, HpaeSinkInfo &sinkInfo) +{ + if (sinkInfo.deviceName != "MCH_Speaker") { + return; + } + sinkInfo.channels = static_cast(DEFAULT_MULTICHANNEL_NUM); + sinkInfo.channelLayout = DEFAULT_MULTICHANNEL_CHANNELLAYOUT; + sinkInfo.frameLen = DEFAULT_MULTICHANNEL_FRAME_LEN_MS * sinkInfo.samplingRate / MS_PER_SECOND; + sinkInfo.volume = MAX_SINK_VOLUME_LEVEL; + AUDIO_INFO_LOG("adjust MCH SINK info ch: %{public}u, channelLayout: %{public}" PRIu64 + " frameLen: %{public}zu volume %{public}f", + sinkInfo.channels, + sinkInfo.channelLayout, + sinkInfo.frameLen, + sinkInfo.volume); +} + +void HpaeManager::TransModuleInfoToHpaeSinkInfo(const AudioModuleInfo &audioModuleInfo, HpaeSinkInfo &sinkInfo) +{ + sinkInfo.deviceNetId = audioModuleInfo.networkId; + sinkInfo.deviceClass = audioModuleInfo.className; + AUDIO_INFO_LOG("HpaeManager::deviceNetId: %{public}s, deviceClass: %{public}s", + sinkInfo.deviceNetId.c_str(), + sinkInfo.deviceClass.c_str()); + sinkInfo.adapterName = audioModuleInfo.adapterName; + sinkInfo.filePath = audioModuleInfo.fileName; + + sinkInfo.samplingRate = static_cast(std::atol(audioModuleInfo.rate.c_str())); + sinkInfo.format = static_cast(TransFormatFromStringToEnum(audioModuleInfo.format)); + sinkInfo.channels = static_cast(std::atol(audioModuleInfo.channels.c_str())); + int32_t bufferSize = static_cast(std::atol(audioModuleInfo.bufferSize.c_str())); + sinkInfo.frameLen = bufferSize / (sinkInfo.channels * GetSizeFromFormat(sinkInfo.format)); + sinkInfo.channelLayout = 0ULL; + sinkInfo.deviceType = static_cast(std::atol(audioModuleInfo.deviceType.c_str())); + sinkInfo.volume = static_cast(std::atol(audioModuleInfo.deviceType.c_str())); + sinkInfo.openMicSpeaker = static_cast(std::atol(audioModuleInfo.OpenMicSpeaker.c_str())); + sinkInfo.renderInIdleState = static_cast(std::atol(audioModuleInfo.renderInIdleState.c_str())); + sinkInfo.offloadEnable = static_cast(std::atol(audioModuleInfo.offloadEnable.c_str())); + sinkInfo.sinkLatency = static_cast(std::atol(audioModuleInfo.sinkLatency.c_str())); + sinkInfo.fixedLatency = static_cast(std::atol(audioModuleInfo.fixedLatency.c_str())); + sinkInfo.deviceName = audioModuleInfo.name; + AdjustMchSinkInfo(audioModuleInfo, sinkInfo); +} + +void HpaeManager::TransModuleInfoToHpaeSourceInfo(const AudioModuleInfo &audioModuleInfo, HpaeSourceInfo &sourceInfo) +{ + sourceInfo.deviceNetId = audioModuleInfo.networkId; + sourceInfo.deviceClass = audioModuleInfo.className; + sourceInfo.adapterName = audioModuleInfo.adapterName; + sourceInfo.sourceName = audioModuleInfo.name; // built_in_mic + sourceInfo.deviceName = audioModuleInfo.name; + sourceInfo.sourceType = static_cast(std::atol(audioModuleInfo.sourceType.c_str())); + sourceInfo.filePath = audioModuleInfo.fileName; + int32_t bufferSize = static_cast(std::atol(audioModuleInfo.bufferSize.c_str())); + sourceInfo.channels = static_cast(std::atol(audioModuleInfo.channels.c_str())); + sourceInfo.format = TransFormatFromStringToEnum(audioModuleInfo.format); + sourceInfo.frameLen = bufferSize / (sourceInfo.channels * GetSizeFromFormat(sourceInfo.format)); + sourceInfo.samplingRate = static_cast(std::atol(audioModuleInfo.rate.c_str())); + sourceInfo.channelLayout = 0ULL; + sourceInfo.deviceType = static_cast(std::atol(audioModuleInfo.deviceType.c_str())); + sourceInfo.volume = static_cast(std::atol(audioModuleInfo.deviceType.c_str())); // 1.0f; + + sourceInfo.ecType = static_cast(std::atol(audioModuleInfo.ecType.c_str())); + sourceInfo.ecAdapterName = audioModuleInfo.ecAdapter; + sourceInfo.ecSamplingRate = static_cast(std::atol(audioModuleInfo.ecSamplingRate.c_str())); + sourceInfo.ecFormat = TransFormatFromStringToEnum(audioModuleInfo.ecFormat); + sourceInfo.ecChannels = static_cast(std::atol(audioModuleInfo.ecChannels.c_str())); + sourceInfo.ecFrameLen = DEFAULT_MULTICHANNEL_FRAME_LEN_MS * (sourceInfo.ecSamplingRate / MS_PER_SECOND); + + sourceInfo.micRef = static_cast(std::atol(audioModuleInfo.openMicRef.c_str())); + sourceInfo.micRefSamplingRate = static_cast(std::atol(audioModuleInfo.micRefRate.c_str())); + sourceInfo.micRefFormat = TransFormatFromStringToEnum(audioModuleInfo.micRefFormat); + sourceInfo.micRefChannels = static_cast(std::atol(audioModuleInfo.micRefChannels.c_str())); + sourceInfo.openMicSpeaker = static_cast(std::atol(audioModuleInfo.OpenMicSpeaker.c_str())); + sourceInfo.micRefFrameLen = DEFAULT_MULTICHANNEL_FRAME_LEN_MS * (sourceInfo.micRefSamplingRate / MS_PER_SECOND); +} + +void HpaeManager::PrintAudioModuleInfo(const AudioModuleInfo &audioModuleInfo) +{ + AUDIO_INFO_LOG("rate: %{public}s ch: %{public}s buffersize: %{public}s ", + audioModuleInfo.rate.c_str(), + audioModuleInfo.channels.c_str(), + audioModuleInfo.bufferSize.c_str()); + AUDIO_INFO_LOG("format: %{public}s name: %{public}s lib: %{public}s ", + audioModuleInfo.format.c_str(), + audioModuleInfo.name.c_str(), + audioModuleInfo.lib.c_str()); + AUDIO_INFO_LOG("deviceType: %{public}s className: %{public}s adapterName: %{public}s ", + audioModuleInfo.deviceType.c_str(), + audioModuleInfo.className.c_str(), + audioModuleInfo.adapterName.c_str()); + AUDIO_INFO_LOG("OpenMicSpeaker: %{public}s networkId: %{public}s fileName: %{public}s ", + audioModuleInfo.OpenMicSpeaker.c_str(), + audioModuleInfo.networkId.c_str(), + audioModuleInfo.fileName.c_str()); + AUDIO_INFO_LOG("fixedLatency: %{public}s sinkLatency: %{public}s renderInIdleState: %{public}s ", + audioModuleInfo.fixedLatency.c_str(), + audioModuleInfo.sinkLatency.c_str(), + audioModuleInfo.renderInIdleState.c_str()); + AUDIO_INFO_LOG("sceneName: %{public}s sourceType: %{public}s offloadEnable: %{public}s ", + audioModuleInfo.sceneName.c_str(), + audioModuleInfo.sourceType.c_str(), + audioModuleInfo.offloadEnable.c_str()); +} + +uint32_t HpaeManager::OpenOutputAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex) +{ + if (rendererManagerMap_.find(audioModuleInfo.name) != rendererManagerMap_.end()) { + AUDIO_INFO_LOG("sink name: %{public}s already open", audioModuleInfo.name.c_str()); + if (!rendererManagerMap_[audioModuleInfo.name]->IsInit()) { + rendererManagerMap_[audioModuleInfo.name]->Init(); + MoveToPreferSink(audioModuleInfo.name); + } else if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnOpenAudioPortCb(sinkNameSinkIdMap_[audioModuleInfo.name]); + } + return sinkNameSinkIdMap_[audioModuleInfo.name]; + } + sinkSourceIndex_.fetch_add(1); + HpaeSinkInfo sinkInfo; + sinkInfo.sinkId = sinkSourceIndex; + sinkInfo.suspendTime = DEFAULT_SUSPEND_TIME_IN_MS; + TransModuleInfoToHpaeSinkInfo(audioModuleInfo, sinkInfo); + auto rendererManager = IHpaeRendererManager::CreateRendererManager(sinkInfo); + rendererManagerMap_[audioModuleInfo.name] = rendererManager; + sinkNameSinkIdMap_[audioModuleInfo.name] = sinkSourceIndex; + sinkIdSinkNameMap_[sinkSourceIndex] = audioModuleInfo.name; + rendererManager->Init(); + rendererManager->RegisterSendMsgCallback(weak_from_this()); + MoveToPreferSink(audioModuleInfo.name); + AUDIO_INFO_LOG( + "open sink name: %{public}s end sinkIndex is %{public}u", audioModuleInfo.name.c_str(), sinkSourceIndex); + uint32_t renderId = GetRenderId(sinkInfo.deviceClass); + // todo: set renderId to CaptureManger + hpaePolicyManager_->SetOutputDevice(renderId, static_cast(sinkInfo.deviceType)); + return SUCCESS; +} + +uint32_t HpaeManager::OpenInputAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex) +{ + if (capturerManagerMap_.find(audioModuleInfo.name) != capturerManagerMap_.end()) { + HpaeEcType ecType = static_cast(std::atol(audioModuleInfo.ecType.c_str())); + HpaeMicRefSwitch micRef = static_cast(std::atol(audioModuleInfo.openMicRef.c_str())); + HpaeSourceInfo oldInfo = capturerManagerMap_[audioModuleInfo.name]->GetSourceInfo(); + if (oldInfo.ecType != ecType || oldInfo.micRef != micRef) { + AUDIO_INFO_LOG("source name: %{public}s need reload", audioModuleInfo.name.c_str()); + HpaeSourceInfo sourceInfo; + sourceInfo.sourceId = oldInfo.sourceId; + TransModuleInfoToHpaeSourceInfo(audioModuleInfo, sourceInfo); + capturerManagerMap_[audioModuleInfo.name]->ReloadCaptureManager(sourceInfo); + return sourceNameSourceIdMap_[audioModuleInfo.name]; + } + AUDIO_INFO_LOG("source name: %{public}s already open", audioModuleInfo.name.c_str()); + if (!capturerManagerMap_[audioModuleInfo.name]->IsInit()) { + capturerManagerMap_[audioModuleInfo.name]->Init(); + } else if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnOpenAudioPortCb(sourceNameSourceIdMap_[audioModuleInfo.name]); + } + return sourceNameSourceIdMap_[audioModuleInfo.name]; + } + sinkSourceIndex_.fetch_add(1); + HpaeSourceInfo sourceInfo; + sourceInfo.sourceId = sinkSourceIndex; + TransModuleInfoToHpaeSourceInfo(audioModuleInfo, sourceInfo); + auto capturerManager = std::make_shared(sourceInfo); + capturerManagerMap_[audioModuleInfo.name] = capturerManager; + sourceNameSourceIdMap_[audioModuleInfo.name] = sinkSourceIndex; + sourceIdSourceNameMap_[sinkSourceIndex] = audioModuleInfo.name; + capturerManagerMap_[audioModuleInfo.name]->Init(); + capturerManager->RegisterSendMsgCallback(weak_from_this()); + AUDIO_INFO_LOG( + "open source name: %{public}s end sourceIndex is %{public}u", audioModuleInfo.name.c_str(), sinkSourceIndex); + uint32_t captureId = GetCaptureId(sourceInfo.deviceClass); + capturerManager->SetCaptureId(captureId); + hpaePolicyManager_->SetInputDevice(captureId, static_cast(sourceInfo.deviceType)); + return SUCCESS; +} + +uint32_t HpaeManager::OpenVirtualAudioPort(const AudioModuleInfo &audioModuleInfo, int32_t sinkSourceIndex) +{ + if (rendererManagerMap_.find(audioModuleInfo.name) != rendererManagerMap_.end()) { + AUDIO_INFO_LOG("inner capture name: %{public}s already open", audioModuleInfo.name.c_str()); + if (!rendererManagerMap_[audioModuleInfo.name]->IsInit()) { + rendererManagerMap_[audioModuleInfo.name]->Init(); + } else if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnOpenAudioPortCb(sinkNameSinkIdMap_[audioModuleInfo.name]); + } + return sinkNameSinkIdMap_[audioModuleInfo.name]; + } + sinkSourceIndex_.fetch_add(1); + HpaeSinkInfo sinkInfo; + sinkInfo.sinkId = sinkSourceIndex; + TransModuleInfoToHpaeSinkInfo(audioModuleInfo, sinkInfo); + auto rendererManager = IHpaeRendererManager::CreateRendererManager(sinkInfo); + rendererManagerMap_[audioModuleInfo.name] = rendererManager; + sinkNameSinkIdMap_[audioModuleInfo.name] = sinkSourceIndex; + sinkIdSinkNameMap_[sinkSourceIndex] = audioModuleInfo.name; + rendererManagerMap_[audioModuleInfo.name]->Init(); + rendererManager->RegisterSendMsgCallback(weak_from_this()); + AUDIO_INFO_LOG("HpaeManager::OpenAudioPort name: %{public}s end sinkIndex is %{public}u", + audioModuleInfo.name.c_str(), + sinkSourceIndex); + return SUCCESS; +} + +int32_t HpaeManager::OpenAudioPortInner(const AudioModuleInfo &audioModuleInfo) +{ + int32_t sinkSourceIndex = sinkSourceIndex_.load(); + if ((audioModuleInfo.lib != "libmodule-hdi-source.z.so") && + (audioModuleInfo.lib != "libmodule-inner-capturer-sink.z.so")) { + OpenOutputAudioPort(audioModuleInfo, sinkSourceIndex); + } else if (audioModuleInfo.lib == "libmodule-hdi-source.z.so") { + OpenInputAudioPort(audioModuleInfo, sinkSourceIndex); + } else { + OpenVirtualAudioPort(audioModuleInfo, sinkSourceIndex); + } + return sinkSourceIndex; +} + +uint32_t HpaeManager::OpenAudioPort(const AudioModuleInfo &audioModuleInfo) +{ + auto request = [this, audioModuleInfo]() { + PrintAudioModuleInfo(audioModuleInfo); + OpenAudioPortInner(audioModuleInfo); + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeManager::DumpSinkInfo(std::string deviceName) +{ + auto request = [this, deviceName]() { + AUDIO_INFO_LOG("DumpSinkInfo %{public}s", deviceName.c_str()); + if (rendererManagerMap_.find(deviceName) == rendererManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sinkName: %{public}s in rendererManagerMap_", deviceName.c_str()); + if (dumpCallback_) { + std::string dumpStr; + dumpCallback_->OnDumpSinkInfoCb(dumpStr, ERROR); + } + return; + } + rendererManagerMap_[deviceName]->DumpSinkInfo(); + }; + SendRequest(request); +} + +void HpaeManager::DumpSourceInfo(std::string deviceName) +{ + auto request = [this, deviceName]() { + AUDIO_INFO_LOG("DumpSourceInfo %{public}s", deviceName.c_str()); + if (capturerManagerMap_.find(deviceName) == capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sourceName: %{public}s in capturerManagerMap_", deviceName.c_str()); + if (dumpCallback_) { + std::string dumpStr; + dumpCallback_->OnDumpSourceInfoCb(dumpStr, ERROR); + } + return; + } + capturerManagerMap_[deviceName]->DumpSourceInfo(); + }; + SendRequest(request); +} + +int32_t HpaeManager::CloseOutAudioPort(std::string &sinkName) +{ + if (rendererManagerMap_.find(sinkName) == rendererManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sinkName: %{public}s in rendererManagerMap_", sinkName.c_str()); + return SUCCESS; + } + rendererManagerMap_[sinkName]->DeInit(sinkName != defaultSink_); + if (sinkName != defaultSink_) { + rendererManagerMap_.erase(sinkName); + sinkIdSinkNameMap_.erase(sinkNameSinkIdMap_[sinkName]); + sinkNameSinkIdMap_.erase(sinkName); + } + return SUCCESS; +} + +int32_t HpaeManager::CloseInAudioPort(std::string &sourceName) +{ + if (capturerManagerMap_.find(sourceName) == capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sourceName: %{public}s in capturerManagerMap_", sourceName.c_str()); + return SUCCESS; + } + capturerManagerMap_[sourceName]->DeInit(sourceName != defaultSource_); + if (sourceName != defaultSource_) { + capturerManagerMap_.erase(sourceName); + sourceIdSourceNameMap_.erase(sourceNameSourceIdMap_[sourceName]); + sourceNameSourceIdMap_.erase(sourceName); + } + return SUCCESS; +} + +int32_t HpaeManager::CloseAudioPort(int32_t audioHandleIndex) +{ + auto request = [this, audioHandleIndex]() { + int32_t ret = -1; + if (sinkIdSinkNameMap_.find(audioHandleIndex) != sinkIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("close sink index: %{public}d name %{public}s", + audioHandleIndex, + sinkIdSinkNameMap_[audioHandleIndex].c_str()); + ret = CloseOutAudioPort(sinkIdSinkNameMap_[audioHandleIndex]); + } else { + AUDIO_INFO_LOG("close source index: %{public}d name %{public}s", + audioHandleIndex, + sourceIdSourceNameMap_[audioHandleIndex].c_str()); + ret = CloseInAudioPort(sourceIdSourceNameMap_[audioHandleIndex]); + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnCloseAudioPortCb(ret); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetDefaultSink(std::string name) +{ + auto request = [this, name]() { + AUDIO_INFO_LOG("SetDefaultSink name: %{public}s", name.c_str()); + if (name == defaultSink_) { + AUDIO_INFO_LOG("sink is same as default sink"); + return; + } + std::shared_ptr rendererManager = GetRendererManagerByNmae(defaultSink_); + if (rendererManager == nullptr) { + AUDIO_INFO_LOG("default sink not exist, set default sink direct"); + defaultSink_ = name; + return; + } + if (rendererManagerMap_.find(name) == rendererManagerMap_.end()) { + AUDIO_WARNING_LOG("sink: %{public}s not exist, do not change default sink", name.c_str()); + return; + } + std::vector sessionIds; + rendererManager->MoveAllStream(name, sessionIds, true); + std::string oldDefaultSink = defaultSink_; + defaultSink_ = name; + if (!rendererManager->IsInit()) { + rendererManagerMap_.erase(defaultSink_); + sinkIdSinkNameMap_.erase(sinkNameSinkIdMap_[defaultSink_]); + sinkNameSinkIdMap_.erase(defaultSink_); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetDefaultSource(std::string name) +{ + AUDIO_INFO_LOG("HpaeManager::SetDefaultSource name: %{public}s", name.c_str()); + auto request = [this, name]() { + if (name == defaultSource_) { + AUDIO_INFO_LOG("source is same as default source"); + return; + } + std::shared_ptr capturerManager = GetCapturerManagerByName(defaultSource_); + if (capturerManager == nullptr) { + AUDIO_INFO_LOG("default source not exist, set default source direct"); + defaultSource_ = name; + return; + } + if (capturerManagerMap_.find(name) == capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("source: %{public}s not exist, do not change default source", name.c_str()); + return; + } + std::vector sessionIds; + capturerManager->MoveAllStream(name, sessionIds, true); + std::string oldDefaultSource_ = defaultSource_; + defaultSource_ = name; + if (!capturerManager->IsInit()) { + capturerManagerMap_.erase(oldDefaultSource_); + sourceIdSourceNameMap_.erase(sourceNameSourceIdMap_[oldDefaultSource_]); + sourceNameSourceIdMap_.erase(oldDefaultSource_); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAllSinkInputs() +{ + AUDIO_INFO_LOG("GetAllSinkInputs"); + auto request = [this]() { + std::vector results; + std::transform(sinkInputs_.begin(), sinkInputs_.end(), std::back_inserter(results), [](const auto &pair) { + return pair.second; + }); + AUDIO_INFO_LOG("sink input number:%{public}zu", results.size()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnGetAllSinkInputsCb(SUCCESS, results); + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeManager::AddSinkIdByName(std::unordered_map> &sinkIdMap, + const std::pair &id, const std::string &name) +{ + if (id.second == name && rendererIdSinkNameMap_[id.first] != id.second) { + if (sinkIdMap.find(id.second) == sinkIdMap.end()) { + sinkIdMap[id.second] = std::vector{}; + } + sinkIdMap[id.second].push_back(id.first); + } + return; +} + +void HpaeManager::MoveToPreferSink(const std::string &name) +{ + AUDIO_INFO_LOG("enter in"); + std::unordered_map> sinkIdMap; + for (const auto &id : idPreferSinkNameMap_) { + AddSinkIdByName(sinkIdMap, id, name); + } + for (const auto &sinkId : sinkIdMap) { + std::string sinkName = sinkId.first; + const std::vector ids = sinkId.second; + auto request = [this, sinkName, name, ids]() { + AUDIO_INFO_LOG("MoveToPreferSink name: %{public}s", name.c_str()); + if (rendererManagerMap_.find(sinkName) == rendererManagerMap_.end()) { + AUDIO_ERR_LOG("can not find prefer sink: %{public}s", sinkName.c_str()); + return; + } + rendererManagerMap_[sinkName]->MoveAllStream(name, ids, false); + }; + SendRequest(request); + } +} + +int32_t HpaeManager::GetAllSourceOutputs() +{ + AUDIO_INFO_LOG("GetAllSourceOutputs"); + auto request = [this]() { + std::vector results; + std::transform(sourceOutputs_.begin(), sourceOutputs_.end(), std::back_inserter(results), [](const auto &pair) { + return pair.second; + }); + AUDIO_INFO_LOG("source output number:%{public}zu", results.size()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnGetAllSourceOutputsCb(SUCCESS, results); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::MoveSourceOutputByIndexOrName( + uint32_t sourceOutputId, uint32_t sourceIndex, std::string sourceName) +{ + auto request = [this, sourceOutputId, sourceName]() { + AUDIO_INFO_LOG("start to move id:%{public}d, source name:%{public}s", sourceOutputId, sourceName.c_str()); + if (sourceName.empty()) { + AUDIO_ERR_LOG("source name is empty."); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + + std::shared_ptr oldCaptureManager = GetCapturerManagerById(sourceOutputId); + if (oldCaptureManager == nullptr) { + AUDIO_ERR_LOG("can not find source by id:%{public}d.", sourceOutputId); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + if (capturerManagerMap_.find(sourceName) == capturerManagerMap_.end()) { + AUDIO_ERR_LOG("can not find source by name:%{public}s.", sourceName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + if (sourceName == capturerIdSourceNameMap_[sourceOutputId]) { + AUDIO_INFO_LOG("source is the same, no need move,source name:%{public}s", sourceName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(SUCCESS); + } + return; + } + oldCaptureManager->MoveStream(sourceOutputId, sourceName); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::MoveSinkInputByIndexOrName(uint32_t sinkInputId, uint32_t sinkIndex, std::string sinkName) +{ + auto request = [this, sinkInputId, sinkName]() { + AUDIO_INFO_LOG("start to move id:%{public}d, sink name:%{public}s", sinkInputId, sinkName.c_str()); + if (sinkName.empty()) { + AUDIO_ERR_LOG("sink name is empty."); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + std::shared_ptr oldRendererManager = GetRendererManagerById(sinkInputId); + if (oldRendererManager == nullptr) { + AUDIO_ERR_LOG("can not find sink by id:%{public}d", sinkInputId); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + if (sinkName == rendererIdSinkNameMap_[sinkInputId]) { + AUDIO_INFO_LOG("sink is the same, no need move,sink:%{public}s", sinkName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(SUCCESS); + } + return; + } + if (rendererManagerMap_.find(sinkName) == rendererManagerMap_.end()) { + AUDIO_ERR_LOG("can not find sink:%{public}s.", sinkName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + if (rendererIdStreamInfoMap_.find(sinkInputId) == rendererIdStreamInfoMap_.end()) { + AUDIO_ERR_LOG("can not find session info:%{public}d", sinkInputId); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + oldRendererManager->MoveStream(sinkInputId, sinkName); + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeManager::HandleMsg() +{ + hpaeNoLockQueue_.HandleRequests(); +} + +bool HpaeManager::IsInit() +{ + return isInit_.load(); +} + +bool HpaeManager::IsRunning() +{ + if (hpaeManagerThread_ == nullptr) { + return false; + } + return hpaeManagerThread_->IsRunning(); +} + +bool HpaeManager::IsMsgProcessing() +{ + return !hpaeNoLockQueue_.IsFinishProcess(); +} + +int32_t HpaeManager::GetMsgCount() +{ + return receiveMsgCount_.load(); +} + +void HpaeManager::Invoke(HpaeMsgCode cmdID, const std::any &args) +{ + auto it = handlers_.find(cmdID); + if (it != handlers_.end()) { + auto request = [it, args]() { it->second(args); }; + SendRequest(request); + return; + }; + AUDIO_ERR_LOG("HpaeManager::Invoke cmdID %{public}d not found", (int32_t)cmdID); +} + +template +void HpaeManager::RegisterHandler(HpaeMsgCode cmdID, void (HpaeManager::*func)(Args...)) +{ + handlers_[cmdID] = [this, cmdID, func](const std::any &packedArgs) { + // unpack args + auto args = std::any_cast>(&packedArgs); + // print log if args parse error + CHECK_AND_RETURN_LOG(args != nullptr, "cmdId %{public}d type mismatched", cmdID); + std::apply( + [this, func]( + auto &&...unpackedArgs) { (this->*func)(std::forward(unpackedArgs)...); }, + *args); + }; +} + +void HpaeManager::HandleMoveSinkInput(const std::shared_ptr sinkInputNode, std::string sinkName) +{ + AUDIO_INFO_LOG("move to new sink:%{public}s", sinkName.c_str()); + std::shared_ptr rendererManager = GetRendererManagerByNmae(sinkName); + if (rendererManager == nullptr) { + AUDIO_ERR_LOG("can not find sink by new name:%{public}s", sinkName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + uint32_t sessionId = sinkInputNode->GetNodeInfo().sessionId; + rendererManager->AddNodeToSink(sinkInputNode); + rendererIdSinkNameMap_[sessionId] = sinkName; + idPreferSinkNameMap_[sessionId] = sinkName; + if (sinkInputs_.find(sessionId) != sinkInputs_.end()) { + sinkInputs_[sessionId].deviceSinkId = sinkNameSinkIdMap_[sinkName]; + sinkInputs_[sessionId].sinkName = sinkName; + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSinkInputByIndexOrNameCb(SUCCESS); + } +} + +void HpaeManager::HandleMoveSourceOutput(const HpaeCaptureMoveInfo moveInfo, std::string sourceName) +{ + AUDIO_INFO_LOG("move to new source:%{public}s", sourceName.c_str()); + std::shared_ptr catpureManager = GetCapturerManagerByName(sourceName); + if (catpureManager == nullptr) { + AUDIO_ERR_LOG("can not find source by name:%{public}s", sourceName.c_str()); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(ERROR_INVALID_PARAM); + } + return; + } + uint32_t sessionId = moveInfo.sessionId; + catpureManager->AddNodeToSource(moveInfo); + capturerIdSourceNameMap_[sessionId] = sourceName; + if (sourceOutputs_.find(sessionId) != sourceOutputs_.end()) { + sourceOutputs_[sessionId].deviceSourceId = sourceNameSourceIdMap_[sourceName]; + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnMoveSourceOutputByIndexOrNameCb(SUCCESS); + } +} + +void HpaeManager::HandleMoveAllSinkInputs( + const std::vector> sinkInputs, std::string sinkName) +{ + AUDIO_INFO_LOG("sink name is :%{public}s", sinkName.c_str()); + if (sinkName.empty()) { + AUDIO_INFO_LOG("sink name is empty, move to default sink:%{public}s", defaultSink_.c_str()); + sinkName = defaultSink_; + } + if (rendererManagerMap_.find(sinkName) == rendererManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sink: %{public}s", sinkName.c_str()); + return; + } + AUDIO_INFO_LOG("sink input count:%{public}zu", sinkInputs.size()); + rendererManagerMap_[sinkName]->AddAllNodesToSink(sinkInputs, false); + for (const auto &sinkInput : sinkInputs) { + uint32_t sessionId = sinkInput->GetNodeInfo().sessionId; + rendererIdSinkNameMap_[sessionId] = sinkName; + if (sinkInputs_.find(sessionId) != sinkInputs_.end()) { + sinkInputs_[sessionId].deviceSinkId = sinkNameSinkIdMap_[sinkName]; + sinkInputs_[sessionId].sinkName = sinkName; + } + } +} + +void HpaeManager::HandleMoveAllSourceOutputs(const std::vector moveInfos, std::string sourceName) +{ + AUDIO_INFO_LOG("move all source to :%{public}s", sourceName.c_str()); + if (sourceName.empty()) { + AUDIO_INFO_LOG("source is empty, move to default source:%{public}s", defaultSource_.c_str()); + sourceName = defaultSource_; + } + if (capturerManagerMap_.find(sourceName) == capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find source: %{public}s", sourceName.c_str()); + return; + } + AUDIO_INFO_LOG("sink input count:%{public}zu", moveInfos.size()); + capturerManagerMap_[sourceName]->AddAllNodesToSource(moveInfos, false); + for (const auto &it : moveInfos) { + capturerIdSourceNameMap_[it.sessionId] = sourceName; + if (sourceOutputs_.find(it.sessionId) != sourceOutputs_.end()) { + sourceOutputs_[it.sessionId].deviceSourceId = sourceNameSourceIdMap_[sourceName]; + } + } +} + +void HpaeManager::HandleUpdateStatus( + HpaeStreamClassType streamClassType, uint32_t sessionId, uint32_t status, IOperation operation) +{ + AUDIO_INFO_LOG("HpaeManager::HandleUpdateStatus sessionid:%{public}u " + "status:%{public}d operation:%{public}d", + sessionId, + status, + operation); + if (operation == OPERATION_INVALID) { + // maybe dosomething while move sink inputs + return; + } + auto it = streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY ? rendererIdStreamInfoMap_.find(sessionId) + : capturerIdStreamInfoMap_.find(sessionId); + if (it != rendererIdStreamInfoMap_.end() && it != capturerIdStreamInfoMap_.end()) { + if (auto callback = it->second.statusCallback.lock()) { + callback->OnStatusUpdate(operation); + } + } +} + +void HpaeManager::HandleDumpSinkInfo(std::string deviceName, std::string dumpStr) +{ + AUDIO_INFO_LOG("HpaeManager::HandleDumpSinkInfo deviceName:%{public}s dumpStr:%{public}s", + deviceName.c_str(), + dumpStr.c_str()); + if (dumpCallback_) { + dumpCallback_->OnDumpSinkInfoCb(dumpStr, SUCCESS); + } +} + +void HpaeManager::HandleDumpSourceInfo(std::string deviceName, std::string dumpStr) +{ + AUDIO_INFO_LOG("HpaeManager::HandleDumpSourceInfo deviceName:%{public}s dumpStr:%{public}s", + deviceName.c_str(), + dumpStr.c_str()); + if (dumpCallback_) { + dumpCallback_->OnDumpSourceInfoCb(dumpStr, SUCCESS); + } +} + +void HpaeManager::HandleInitDeviceResult(std::string deviceName, int32_t result) +{ + AUDIO_INFO_LOG("deviceName:%{public}s result:%{public}d ", deviceName.c_str(), result); + auto serviceCallback = serviceCallback_.lock(); + if (serviceCallback && result == SUCCESS) { + if (sinkNameSinkIdMap_.find(deviceName) != sinkNameSinkIdMap_.end()) { + serviceCallback->OnOpenAudioPortCb(sinkNameSinkIdMap_[deviceName]); + } else if (sourceNameSourceIdMap_.find(deviceName) != sourceNameSourceIdMap_.end()) { + serviceCallback->OnOpenAudioPortCb(sourceNameSourceIdMap_[deviceName]); + } else { + AUDIO_ERR_LOG("device:%{public}s is not exist.", deviceName.c_str()); + serviceCallback->OnOpenAudioPortCb(ERROR); + } + } else if (serviceCallback) { + serviceCallback->OnOpenAudioPortCb(ERROR); + AUDIO_INFO_LOG("HandleInitDeviceResult deviceName:%{public}s " + "result:%{public}d error", + deviceName.c_str(), + result); + } else { + AUDIO_INFO_LOG("HandleInitDeviceResult OnOpenAudioPortCb is nullptr"); + } +} + +void HpaeManager::HandleDeInitDeviceResult(std::string deviceName, int32_t result) +{ + AUDIO_INFO_LOG("deviceName:%{public}s result:%{public}d ", deviceName.c_str(), result); + auto serviceCallback = serviceCallback_.lock(); + if (serviceCallback && result == SUCCESS && sinkNameSinkIdMap_.find(deviceName) != sinkNameSinkIdMap_.end()) { + serviceCallback->OnCloseAudioPortCb(sinkNameSinkIdMap_[deviceName]); + } else if (serviceCallback) { + serviceCallback->OnCloseAudioPortCb(ERROR); + AUDIO_INFO_LOG("deviceName:%{public}s " + "result:%{public}d error", + deviceName.c_str(), + result); + } else { + AUDIO_INFO_LOG("HandleDeInitDeviceResult is nullptr"); + } + if (rendererManagerMap_.find(deviceName) != rendererManagerMap_.end()) { + AUDIO_INFO_LOG("OnCloseAudioPortCb is sink"); + rendererManagerMap_[deviceName]->DeactivateThread(); + if (deviceName != defaultSink_) { + rendererManagerMap_.erase(deviceName); + sinkIdSinkNameMap_.erase(sinkNameSinkIdMap_[deviceName]); + sinkNameSinkIdMap_.erase(deviceName); + } + } else if (capturerManagerMap_.find(deviceName) != capturerManagerMap_.end()) { + AUDIO_INFO_LOG("OnCloseAudioPortCb is source"); + capturerManagerMap_[deviceName]->DeactivateThread(); + if (deviceName != defaultSource_) { + capturerManagerMap_.erase(deviceName); + sourceIdSourceNameMap_.erase(sourceNameSourceIdMap_[deviceName]); + sourceNameSourceIdMap_.erase(deviceName); + } + } else { + AUDIO_INFO_LOG("deviceName:%{public}s can not find", deviceName.c_str()); + } +} + +void HpaeManager::SendRequest(Request &&request) +{ + hpaeNoLockQueue_.PushRequest(std::move(request)); + CHECK_AND_RETURN_LOG(hpaeManagerThread_, "hpaeManagerThread_ is nullptr"); + hpaeManagerThread_->Notify(); +} +// play and record stream interface +int32_t HpaeManager::CreateStream(const HpaeStreamInfo &streamInfo) +{ + auto request = [this, streamInfo]() { + AUDIO_INFO_LOG("defaultSink_ is %{public}s defaultSource_ is %{public}s streamClassType %{public}u", + defaultSink_.c_str(), + defaultSource_.c_str(), + streamInfo.streamClassType); + AUDIO_INFO_LOG("streamType is %{public}d sessionId %{public}u sourceType is %{public}d", + streamInfo.streamType, + streamInfo.sessionId, + streamInfo.sourceType); + if (INNER_SOURCE_TYPE_SET.count(streamInfo.sourceType) != 0) { + return CreateStreamForCapInner(streamInfo); + } else if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY) { + std::string deviceName = streamInfo.deviceName == "" ? defaultSink_ : streamInfo.deviceName; + AUDIO_INFO_LOG("devicename:%{public}s, sessionId:%{public}u", deviceName.c_str(), streamInfo.sessionId); + CHECK_AND_RETURN_LOG(rendererManagerMap_.find(deviceName) != rendererManagerMap_.end(), + "can not find sink[%{public}s] in rendererManagerMap_", + deviceName.c_str()); + rendererIdSinkNameMap_[streamInfo.sessionId] = defaultSink_; + rendererManagerMap_[defaultSink_]->CreateStream(streamInfo); + rendererIdStreamInfoMap_[streamInfo.sessionId].streamInfo = streamInfo; + rendererIdStreamInfoMap_[streamInfo.sessionId].state = I_STATUS_IDLE; + AddStreamToCollection(streamInfo); + } else if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD) { + std::string deviceName = streamInfo.deviceName == "" ? defaultSource_ : streamInfo.deviceName; + AUDIO_INFO_LOG("source:%{public}s, sessionId:%{public}u", deviceName.c_str(), streamInfo.sessionId); + CHECK_AND_RETURN_LOG(capturerManagerMap_.find(deviceName) != capturerManagerMap_.end(), + "can not find source[%{public}s] in capturerManagerMap_", + deviceName.c_str()); + capturerIdSourceNameMap_[streamInfo.sessionId] = deviceName; + capturerManagerMap_[deviceName]->CreateStream(streamInfo); + capturerIdStreamInfoMap_[streamInfo.sessionId].streamInfo = streamInfo; + capturerIdStreamInfoMap_[streamInfo.sessionId].state = I_STATUS_IDLE; + AddStreamToCollection(streamInfo); + } else { + AUDIO_WARNING_LOG( + "can not find default sink or source streamClassType %{public}d", streamInfo.streamClassType); + } + }; + SendRequest(request); + AUDIO_WARNING_LOG( + "defaultSink_ is %{public}s streamClassType %{public}u", defaultSink_.c_str(), streamInfo.sessionId); + return SUCCESS; +} + +void HpaeManager::AddStreamToCollection(const HpaeStreamInfo &streamInfo) +{ + auto now = std::chrono::system_clock::now(); + auto ms = std::chrono::duration_cast(now.time_since_epoch()); + if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY) { + SinkInput sinkInput; + sinkInput.streamId = streamInfo.sessionId; + sinkInput.paStreamId = streamInfo.sessionId; + sinkInput.streamType = streamInfo.streamType; + sinkInput.sinkName = defaultSink_; + sinkInput.deviceSinkId = sinkNameSinkIdMap_[defaultSink_]; + sinkInput.pid = streamInfo.pid; + sinkInput.uid = streamInfo.uid; + sinkInput.startTime = ms.count(); + sinkInputs_[streamInfo.sessionId] = sinkInput; + } else if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD) { + SourceOutput sourceOutputInfo; + sourceOutputInfo.streamId = streamInfo.sessionId; + sourceOutputInfo.paStreamId = streamInfo.sessionId; + sourceOutputInfo.streamType = streamInfo.streamType; + sourceOutputInfo.deviceSourceId = sourceNameSourceIdMap_[defaultSource_]; + sourceOutputInfo.pid = streamInfo.pid; + sourceOutputInfo.uid = streamInfo.uid; + sourceOutputInfo.startTime = ms.count(); + sourceOutputs_[streamInfo.sessionId] = sourceOutputInfo; + } +} + +int32_t HpaeManager::DestroyStream(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG("DestroyStream streamClassType %{public}d, sessionId %{public}u", streamClassType, sessionId); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->DestroyStream(sessionId); + rendererIdSinkNameMap_.erase(sessionId); + rendererIdStreamInfoMap_.erase(sessionId); + sinkInputs_.erase(sessionId); + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->DestroyStream(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->DestroyStream(sessionId); + } + capturerIdSourceNameMap_.erase(sessionId); + capturerIdStreamInfoMap_.erase(sessionId); + sourceOutputs_.erase(sessionId); + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->HandleSourceAudioStreamRemoved(sessionId); + } + } else { + AUDIO_WARNING_LOG( + "can not find sessionId streamClassType %{public}d, sessionId %{public}u", streamClassType, sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Start(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG( + "HpaeManager::Start sessionId: %{public}u streamClassType:%{public}d", sessionId, streamClassType); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer Start sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->Start(sessionId); + rendererIdStreamInfoMap_[sessionId].state = I_STATUS_STARTING; + rendererIdStreamInfoMap_[sessionId].statusCallback.lock()->OnStatusUpdate(OPERATION_STARTED); + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer Start sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->Start(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->Start(sessionId); + } + capturerIdStreamInfoMap_[sessionId].state = I_STATUS_STARTING; + } else { + AUDIO_WARNING_LOG("Start can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Pause(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG( + "HpaeManager::Pause sessionId: %{public}u streamClassType:%{public}d", sessionId, streamClassType); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer Pause sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->Pause(sessionId); + rendererIdStreamInfoMap_[sessionId].state = I_STATUS_PAUSING; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer Pause sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->Pause(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->Pause(sessionId); + } + capturerIdStreamInfoMap_[sessionId].state = I_STATUS_PAUSING; + } else { + AUDIO_WARNING_LOG("Pause can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Flush(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG( + "HpaeManager::Flush sessionId: %{public}u streamClassType:%{public}d", sessionId, streamClassType); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer Flush sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->Flush(sessionId); + rendererIdStreamInfoMap_[sessionId].state = I_STATUS_FLUSHING_WHEN_STOPPED; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer Flush sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->Flush(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->Flush(sessionId); + } + capturerIdStreamInfoMap_[sessionId].state = I_STATUS_FLUSHING_WHEN_STOPPED; + } else { + AUDIO_WARNING_LOG("Flush can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Drain(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG( + "HpaeManager::Drain sessionId: %{public}u streamClassType:%{public}d", sessionId, streamClassType); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer Drain sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->Drain(sessionId); + rendererIdStreamInfoMap_[sessionId].state = I_STATUS_DRAINING; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer Drain sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->Drain(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->Drain(sessionId); + } + capturerIdStreamInfoMap_[sessionId].state = I_STATUS_DRAINING; + } else { + AUDIO_WARNING_LOG("Drain can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Stop(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + auto request = [this, streamClassType, sessionId]() { + AUDIO_INFO_LOG( + "HpaeManager::Stop sessionId: %{public}u streamClassType:%{public}d", sessionId, streamClassType); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer Stop sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->Stop(sessionId); + rendererIdStreamInfoMap_[sessionId].state = I_STATUS_STOPPING; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer Stop sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->Stop(sessionId); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->Stop(sessionId); + } + capturerIdStreamInfoMap_[sessionId].state = I_STATUS_STOPPING; + } else { + AUDIO_WARNING_LOG("Stop can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::Release(HpaeStreamClassType streamClassType, uint32_t sessionId) +{ + DestroyStream(streamClassType, sessionId); + return SUCCESS; +} +int32_t HpaeManager::RegisterStatusCallback( + HpaeStreamClassType streamClassType, uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, streamClassType, sessionId, callback]() { + AUDIO_INFO_LOG( + "RegisterStatusCallback streamClassType %{public}d, sessionId %{public}u", streamClassType, sessionId); + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer RegisterStatusCallback sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererIdStreamInfoMap_[sessionId].statusCallback = callback; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer RegisterStatusCallback sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + capturerIdStreamInfoMap_[sessionId].statusCallback = callback; + } else { + AUDIO_WARNING_LOG( + "RegisterStatusCallback can not find sessionId streamClassType %{public}d, sessionId %{public}u", + streamClassType, + sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +// record stream interface +void HpaeManager::RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + AUDIO_INFO_LOG("RegisterReadCallback sessionId %{public}u", sessionId); + if (capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + AUDIO_INFO_LOG("capturer RegisterReadCallback sessionId: %{public}u deviceName:%{public}s", + sessionId, + capturerIdSourceNameMap_[sessionId].c_str()); + if (INNER_SOURCE_TYPE_SET.count(capturerIdStreamInfoMap_[sessionId].streamInfo.sourceType) != 0) { + rendererManagerMap_[capturerIdSourceNameMap_[sessionId]]->RegisterReadCallback(sessionId, callback); + } else { + capturerManagerMap_[capturerIdSourceNameMap_[sessionId]]->RegisterReadCallback(sessionId, callback); + } + } else { + AUDIO_WARNING_LOG("RegisterReadCallback can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return; +} + +int32_t HpaeManager::GetSourceOutputInfo(uint32_t sessionId, HpaeStreamInfo &streamInfo) +{ + // to do + return SUCCESS; +} +// play stream interface +int32_t HpaeManager::SetClientVolume(uint32_t sessionId, float volume) +{ + auto request = [this, sessionId, volume]() { + AUDIO_INFO_LOG("SetClientVolume sessionId %{public}u %{public}f", sessionId, volume); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->SetClientVolume(sessionId, volume); + } else { + AUDIO_WARNING_LOG("SetClientVolume can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetRate(uint32_t sessionId, int32_t rate) +{ + auto request = [this, sessionId, rate]() { + AUDIO_INFO_LOG("SetRate sessionId %{public}u %{public}d", sessionId, rate); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->SetRate(sessionId, rate); + } else { + AUDIO_WARNING_LOG("SetRate can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) +{ + auto request = [this, sessionId, effectMode]() { + AUDIO_INFO_LOG("SetAudioEffectMode sessionId %{public}u %{public}d", sessionId, effectMode); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->SetAudioEffectMode(sessionId, effectMode); + } else { + AUDIO_WARNING_LOG("SetAudioEffectMode can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) +{ + return SUCCESS; +} + +int32_t HpaeManager::SetPrivacyType(uint32_t sessionId, int32_t privacyType) +{ + auto request = [this, sessionId, privacyType]() { + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->SetPrivacyType(sessionId, privacyType); + } else { + AUDIO_WARNING_LOG("SetPrivacyType can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetPrivacyType(uint32_t sessionId, int32_t &privacyType) +{ + return SUCCESS; +} +int32_t HpaeManager::RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + AUDIO_INFO_LOG("RegisterWriteCallback sessionId %{public}u", sessionId); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + AUDIO_INFO_LOG("renderer RegisterWriteCallback sessionId: %{public}u deviceName:%{public}s", + sessionId, + rendererIdSinkNameMap_[sessionId].c_str()); + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->RegisterWriteCallback(sessionId, callback); + } else { + AUDIO_WARNING_LOG("RegisterWriteCallback can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} +int32_t HpaeManager::SetOffloadPolicy(uint32_t sessionId, int32_t state) +{ + auto request = [this, sessionId, state]() { + AUDIO_INFO_LOG("SetOffloadPolicy sessionId %{public}u %{public}d", sessionId, state); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->SetOffloadPolicy(sessionId, state); + } else { + AUDIO_WARNING_LOG("SetOffloadPolicy can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +size_t HpaeManager::GetWritableSize(uint32_t sessionId) +{ + return SUCCESS; +} + +int32_t HpaeManager::UpdateSpatializationState(uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) +{ + auto request = [this, sessionId, spatializationEnabled, headTrackingEnabled]() { + AUDIO_INFO_LOG("UpdateSpatializationState sessionId %{public}u spatializationEnabled %{public}d " + "headTrackingEnabled %{public}d", + sessionId, + spatializationEnabled, + headTrackingEnabled); + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->UpdateSpatializationState( + sessionId, spatializationEnabled, headTrackingEnabled); + } else { + AUDIO_WARNING_LOG("UpdateSpatializationState can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) +{ + auto request = [this, sessionId, maxLength]() { + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + rendererManagerMap_[rendererIdSinkNameMap_[sessionId]]->UpdateMaxLength(sessionId, maxLength); + } else { + AUDIO_WARNING_LOG("UpdateMaxLength can not find sessionId, sessionId %{public}u", sessionId); + } + }; + SendRequest(request); + return SUCCESS; +} + +// only interface for unit test +int32_t HpaeManager::GetSessionInfo( + HpaeStreamClassType streamClassType, uint32_t sessionId, HpaeSessionInfo &sessionInfo) +{ + if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY && + rendererIdStreamInfoMap_.find(sessionId) != rendererIdStreamInfoMap_.end()) { + sessionInfo = rendererIdStreamInfoMap_[sessionId]; + } else if (streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD && + capturerIdStreamInfoMap_.find(sessionId) != capturerIdStreamInfoMap_.end()) { + sessionInfo = capturerIdStreamInfoMap_[sessionId]; + } else { + return ERROR; + } + return SUCCESS; +} + +std::shared_ptr HpaeManager::GetRendererManagerByNmae(const std::string &sinkName) +{ + if (rendererManagerMap_.find(sinkName) == rendererManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sinkName: %{public}s ", sinkName.c_str()); + return nullptr; + } + return rendererManagerMap_[sinkName]; +} + +std::shared_ptr HpaeManager::GetCapturerManagerByName(const std::string &sourceName) +{ + if (capturerManagerMap_.find(sourceName) == capturerManagerMap_.end()) { + AUDIO_WARNING_LOG("can not find sourceName: %{public}s ", sourceName.c_str()); + return nullptr; + } + return capturerManagerMap_[sourceName]; +} + +std::shared_ptr HpaeManager::GetRendererManagerById(uint32_t sessionId) +{ + if (rendererIdSinkNameMap_.find(sessionId) != rendererIdSinkNameMap_.end()) { + return GetRendererManagerByNmae(rendererIdSinkNameMap_[sessionId]); + } + AUDIO_WARNING_LOG("can not find renderer by sessionId: %{public}u", sessionId); + return nullptr; +} + +std::shared_ptr HpaeManager::GetCapturerManagerById(uint32_t sessionId) +{ + if (capturerIdSourceNameMap_.find(sessionId) != capturerIdSourceNameMap_.end()) { + return GetCapturerManagerByName(capturerIdSourceNameMap_[sessionId]); + } + AUDIO_WARNING_LOG("can not find capture by sessionId: %{public}u", sessionId); + return nullptr; +} + +void HpaeManager::InitAudioEffectChainManager(const std::vector &effectChains, + const EffectChainManagerParam &effectChainManagerParam, + const std::vector> &effectLibraryList) +{ + auto request = [this, effectChains, effectChainManagerParam, effectLibraryList]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->InitAudioEffectChainManager(effectChains, effectChainManagerParam, effectLibraryList); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +void HpaeManager::SetOutputDeviceSink(int32_t device, const std::string &sinkName) +{ + auto request = [this, device, sinkName]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetOutputDeviceSink(device, sinkName); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +int32_t HpaeManager::UpdateSpatializationState(AudioSpatializationState spatializationState) +{ + auto request = [this, spatializationState]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->UpdateSpatializationState(spatializationState); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::UpdateSpatialDeviceType(AudioSpatialDeviceType spatialDeviceType) +{ + auto request = [this, spatialDeviceType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->UpdateSpatialDeviceType(spatialDeviceType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetSpatializationSceneType(AudioSpatializationSceneType spatializationSceneType) +{ + auto request = [this, spatializationSceneType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetSpatializationSceneType(spatializationSceneType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::EffectRotationUpdate(const uint32_t rotationState) +{ + auto request = [this, rotationState]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->EffectRotationUpdate(rotationState); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetEffectSystemVolume(const int32_t systemVolumeType, const float systemVolume) +{ + auto request = [this, systemVolumeType, systemVolume]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetEffectSystemVolume(systemVolumeType, systemVolume); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetAudioEffectProperty(const AudioEffectPropertyArrayV3 &propertyArray) +{ + auto request = [this, propertyArray]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetAudioEffectProperty(propertyArray); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAudioEffectProperty(AudioEffectPropertyArrayV3 &propertyArray) +{ + auto request = [this, &propertyArray]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->GetAudioEffectProperty(propertyArray); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnGetAudioEffectPropertyCbV3(SUCCESS); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetAudioEffectProperty(const AudioEffectPropertyArray &propertyArray) +{ + auto request = [this, propertyArray]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetAudioEffectProperty(propertyArray); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAudioEffectProperty(AudioEffectPropertyArray &propertyArray) +{ + auto request = [this, &propertyArray]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->GetAudioEffectProperty(propertyArray); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + if (auto serviceCallback = serviceCallback_.lock()) { + serviceCallback->OnGetAudioEffectPropertyCb(SUCCESS); + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeManager::InitHdiState() +{ + auto request = [this]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->InitHdiState(); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +void HpaeManager::UpdateEffectBtOffloadSupported(const bool &isSupported) +{ + auto request = [this, isSupported]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->UpdateEffectBtOffloadSupported(isSupported); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +void HpaeManager::UpdateParamExtra(const std::string &mainkey, const std::string &subkey, const std::string &value) +{ + auto request = [this, mainkey, subkey, value]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->UpdateParamExtra(mainkey, subkey, value); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +void HpaeManager::HandleRendererManager(const std::string &sinkName, const HpaeStreamInfo &streamInfo) +{ + auto it = rendererManagerMap_.find(sinkName); + if (it == rendererManagerMap_.end()) { + AUDIO_INFO_LOG("can not find sink[%{public}s] in rendererManagerMap_", sinkName.c_str()); + return; + } + it->second->CreateStream(streamInfo); + if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY) { + rendererIdSinkNameMap_[streamInfo.sessionId] = sinkName; + rendererIdStreamInfoMap_[streamInfo.sessionId] = {streamInfo, I_STATUS_IDLE}; + } else if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_RECORD) { + capturerIdSourceNameMap_[streamInfo.sessionId] = sinkName; + capturerIdStreamInfoMap_[streamInfo.sessionId] = {streamInfo, I_STATUS_IDLE}; + } +} + +void HpaeManager::CreateStreamForCapInner(const HpaeStreamInfo &streamInfo) +{ + if (streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_INVALID) { + AUDIO_INFO_LOG("streamInfo.streamClassType == HPAE_STREAM_CLASS_TYPE_INVALID"); + return; + } + std::string deviceName = streamInfo.deviceName; + HandleRendererManager(deviceName, streamInfo); + HandleRendererManager("RemoteCastInnerCapturer", streamInfo); + AddStreamToCollection(streamInfo); + return; +} + +void HpaeManager::InitAudioEnhanceChainManager(const std::vector &enhanceChains, + const EffectChainManagerParam &managerParam, + const std::vector> &enhanceLibraryList) +{ + auto request = [this, enhanceChains, managerParam, enhanceLibraryList]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->InitAudioEnhanceChainManager(enhanceChains, managerParam, enhanceLibraryList); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); +} + +int32_t HpaeManager::SetInputDevice( + const uint32_t &captureId, const DeviceType &inputDevice, const std::string &deviceName) +{ + auto request = [this, captureId, inputDevice, deviceName]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetInputDevice(captureId, inputDevice, deviceName); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetOutputDevice(const uint32_t &renderId, const DeviceType &outputDevice) +{ + auto request = [this, renderId, outputDevice]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetOutputDevice(renderId, outputDevice); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetVolumeInfo(const AudioVolumeType &volumeType, const float &systemVol) +{ + auto request = [this, volumeType, systemVol]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetVolumeInfo(volumeType, systemVol); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetMicrophoneMuteInfo(const bool &isMute) +{ + auto request = [this, isMute]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetMicrophoneMuteInfo(isMute); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetStreamVolumeInfo(const uint32_t &sessionId, const float &streamVol) +{ + auto request = [this, sessionId, streamVol]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetStreamVolumeInfo(sessionId, streamVol); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetAudioEnhanceProperty(const AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType) +{ + auto request = [this, propertyArray, deviceType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetAudioEnhanceProperty(propertyArray, deviceType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAudioEnhanceProperty(AudioEffectPropertyArrayV3 &propertyArray, DeviceType deviceType) +{ + auto request = [this, &propertyArray, deviceType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->GetAudioEnhanceProperty(propertyArray, deviceType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::SetAudioEnhanceProperty(const AudioEnhancePropertyArray &propertyArray, DeviceType deviceType) +{ + auto request = [this, propertyArray, deviceType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->SetAudioEnhanceProperty(propertyArray, deviceType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeManager::GetAudioEnhanceProperty(AudioEnhancePropertyArray &propertyArray, DeviceType deviceType) +{ + auto request = [this, &propertyArray, deviceType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->GetAudioEnhanceProperty(propertyArray, deviceType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeManager::UpdateExtraSceneType( + const std::string &mainkey, const std::string &subkey, const std::string &extraSceneType) +{ + auto request = [this, mainkey, subkey, extraSceneType]() { + if (hpaePolicyManager_ != nullptr) { + hpaePolicyManager_->UpdateExtraSceneType(mainkey, subkey, extraSceneType); + } else { + AUDIO_WARNING_LOG("hpaePolicyManager_ is nullptr"); + } + }; + SendRequest(request); + return; +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/manager/src/hpae_offload_renderer_manager.cpp b/services/audio_engine/manager/src/hpae_offload_renderer_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..369616212ae32e4b3bfbed02c6486d8f6b78aae1 --- /dev/null +++ b/services/audio_engine/manager/src/hpae_offload_renderer_manager.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeOffloadRendererManager" +#endif + +#include "hpae_offload_renderer_manager.h" +#include "audio_stream_info.h" +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "hpae_node_common.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +namespace { +constexpr uint32_t HISTORY_INTERVAL_S = 7; // 7s buffer for rewind +} + +HpaeOffloadRendererManager::HpaeOffloadRendererManager(HpaeSinkInfo &sinkInfo) + : hpaeNoLockQueue_(CURRENT_REQUEST_COUNT), sinkInfo_(sinkInfo) +{} + +HpaeOffloadRendererManager::~HpaeOffloadRendererManager() +{ + AUDIO_INFO_LOG("destructor offload renderer"); + if (isInit_.load()) { + DeInit(); + } +} + +// private method +int32_t HpaeOffloadRendererManager::CreateInputSession(const HpaeStreamInfo &streamInfo) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.channels = streamInfo.channels; + nodeInfo.format = streamInfo.format; + nodeInfo.frameLen = streamInfo.frameLen; + nodeInfo.streamType = streamInfo.streamType; + nodeInfo.sessionId = streamInfo.sessionId; + nodeInfo.samplingRate = static_cast(streamInfo.samplingRate); + nodeInfo.sceneType = TransStreamTypeToSceneType(streamInfo.streamType); + nodeInfo.historyFrameCount = HISTORY_INTERVAL_S * nodeInfo.samplingRate / nodeInfo.frameLen; + nodeInfo.statusCallback = weak_from_this(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + nodeInfo.nodeId = OnGetNodeId(); + nodeInfo.nodeName = "HpaeSinkInputNode"; + sinkInputNode_ = std::make_shared(nodeInfo); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::AddNodeToSink(const std::shared_ptr &node) +{ + auto request = [this, node]() { AddSingleNodeToSink(node); }; + SendRequest(request); + return SUCCESS; +} + +void HpaeOffloadRendererManager::AddSingleNodeToSink(const std::shared_ptr &node, bool isConnect) +{ + HpaeNodeInfo nodeInfo = node->GetNodeInfo(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + // 7s history buffer to rewind + nodeInfo.historyFrameCount = HISTORY_INTERVAL_S * nodeInfo.samplingRate / nodeInfo.frameLen; + nodeInfo.statusCallback = weak_from_this(); + node->SetNodeInfo(nodeInfo); + uint32_t sessionId = nodeInfo.sessionId; + AUDIO_INFO_LOG("add node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + sinkInputNode_ = node; + sessionInfo_.state = node->GetState(); + + if (!isConnect || sinkInputNode_->GetState() != RENDERER_RUNNING) { + AUDIO_INFO_LOG("not need connect session:%{public}d", sessionId); + return; + } + + if (node->GetState() == RENDERER_RUNNING) { + AUDIO_INFO_LOG("connect node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + ConnectInputSession(); // todo: fadein + if (sinkOutputNode_->GetSinkState() != RENDERER_RUNNING) { + sinkOutputNode_->RenderSinkStart(); + } + } +} + +int32_t HpaeOffloadRendererManager::AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) +{ + auto request = [this, sinkInputs, isConnect]() { + for (const auto &it : sinkInputs) { + AddSingleNodeToSink(it, isConnect); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::CreateStream(const HpaeStreamInfo &streamInfo) +{ + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, streamInfo]() { + CreateInputSession(streamInfo); + sessionInfo_.state = RENDERER_NEW; + sinkInputNode_->SetState(RENDERER_NEW); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::DestroyStream(uint32_t sessionId) +{ + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG(sinkInputNode_ && sessionId == sinkInputNode_->GetSessionId(), + "DestroyStream not find sessionId %{public}u", + sessionId); + AUDIO_INFO_LOG("DestroyStream sessionId %{public}u", sessionId); + DisConnectInputSession(); + sinkInputNode_ = nullptr; + formatConverterNode_ = nullptr; + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::ConnectInputSession() +{ + if (sinkInputNode_->GetState() != RENDERER_RUNNING) { + return SUCCESS; + } + + if (CheckHpaeNodeInfoIsSame(sinkInputNode_->GetNodeInfo(), sinkOutputNode_->GetNodeInfo())) { + formatConverterNode_ = nullptr; + sinkOutputNode_->Connect(sinkInputNode_); + OnNotifyDfxNodeInfo(true, sinkOutputNode_->GetNodeId(), sinkInputNode_->GetNodeInfo()); + } else { + HpaeNodeInfo nodeInfo = sinkOutputNode_->GetNodeInfo(); + nodeInfo.nodeName = "HpaeAudioFormatConverterNode"; + nodeInfo.nodeId = OnGetNodeId(); + nodeInfo.sessionId = sinkInputNode_->GetSessionId(); + formatConverterNode_ = std::make_shared( + sinkInputNode_->GetNodeInfo(), nodeInfo); + formatConverterNode_->Connect(sinkInputNode_); + sinkOutputNode_->Connect(formatConverterNode_); + OnNotifyDfxNodeInfo(true, sinkOutputNode_->GetNodeId(), formatConverterNode_->GetNodeInfo()); + OnNotifyDfxNodeInfo(true, formatConverterNode_->GetNodeId(), sinkInputNode_->GetNodeInfo()); + } + // single stream manager + HpaeNodeInfo nodeInfo = sinkOutputNode_->GetNodeInfo(); + nodeInfo.sessionId = sinkInputNode_->GetSessionId(); + nodeInfo.streamType = sinkInputNode_->GetStreamType(); + sinkOutputNode_->SetNodeInfo(nodeInfo); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Start(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG( + sessionId == sinkInputNode_->GetSessionId(), "Start not find sessionId %{public}u", sessionId); + AUDIO_INFO_LOG("Start sessionId %{public}u", sessionId); + sinkInputNode_->SetState(RENDERER_RUNNING); + ConnectInputSession(); + if (sinkOutputNode_->GetSinkState() != RENDERER_RUNNING) { + sinkOutputNode_->RenderSinkStart(); + } + sessionInfo_.state = RENDERER_RUNNING; + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, OPERATION_STARTED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::DisConnectInputSession() +{ + if (formatConverterNode_ == nullptr) { + sinkOutputNode_->DisConnect(sinkInputNode_); + OnNotifyDfxNodeInfo(false, sinkOutputNode_->GetNodeId(), sinkInputNode_->GetNodeInfo()); + } else { + formatConverterNode_->DisConnect(sinkInputNode_); + sinkOutputNode_->DisConnect(formatConverterNode_); + OnNotifyDfxNodeInfo(false, formatConverterNode_->GetNodeId(), sinkInputNode_->GetNodeInfo()); + OnNotifyDfxNodeInfo(false, sinkOutputNode_->GetNodeId(), formatConverterNode_->GetNodeInfo()); + } + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Pause(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG( + sessionId == sinkInputNode_->GetSessionId(), "Pause not find sessionId %{public}u", sessionId); + AUDIO_INFO_LOG("Pause sessionId %{public}u", sessionId); + DisConnectInputSession(); + sinkInputNode_->SetState(RENDERER_PAUSED); + sinkOutputNode_->StopStream(); + sessionInfo_.state = RENDERER_PAUSED; + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, OPERATION_PAUSED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Flush(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG( + sessionId == sinkInputNode_->GetSessionId(), "Pause not find sessionId %{public}u", sessionId); + AUDIO_INFO_LOG("Flush sessionId %{public}u", sessionId); + // flush history buffer + sinkInputNode_->Flush(); + // flush sinkoutput cache + sinkOutputNode_->FlushStream(); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, OPERATION_FLUSHED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Drain(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG( + sessionId == sinkInputNode_->GetSessionId(), "Drain not find sessionId %{public}u", sessionId); + AUDIO_INFO_LOG("Drain sessionId %{public}u", sessionId); + sinkInputNode_->Drain(); + if (sessionInfo_.state != RENDERER_RUNNING) { + TriggerCallback( + UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, OPERATION_DRAINED); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Stop(uint32_t sessionId) +{ + auto request = [this, sessionId]() { + CHECK_AND_RETURN_LOG( + sessionId == sinkInputNode_->GetSessionId(), "Stop not find sessionId %{public}u", sessionId); + AUDIO_INFO_LOG("Stop sessionId %{public}u", sessionId); + DisConnectInputSession(); + sinkInputNode_->SetState(RENDERER_STOPPED); + sessionInfo_.state = RENDERER_STOPPED; + sinkOutputNode_->StopStream(); + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, OPERATION_STOPPED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::Release(uint32_t sessionId) +{ + return DestroyStream(sessionId); +} + +void HpaeOffloadRendererManager::MoveAllStreamToNewSink(const std::string &sinkName, + const std::vector& moveIds, bool isMoveAll) +{ + std::string name = sinkName; + std::vector> sinkInputs; + std::vector sessionIds; + if (sinkInputNode_) { + uint32_t sessionId = sinkInputNode_->GetSessionId(); + if (isMoveAll || std::find(moveIds.begin(), moveIds.end(), sessionId) != moveIds.end()) { + sinkInputs.emplace_back(sinkInputNode_); + sessionIds.emplace_back(sinkInputNode_->GetSessionId()); + DisConnectInputSession(); + } + } + TriggerCallback(MOVE_ALL_SINK_INPUT, sinkInputs, name); +} + +int32_t HpaeOffloadRendererManager::MoveAllStream(const std::string &sinkName, const std::vector& sessionIds, + bool isMoveAll) +{ + if (!IsInit()) { + AUDIO_INFO_LOG("sink is not init ,use sync mode move to:%{public}s.", sinkName.c_str()); + MoveAllStreamToNewSink(sinkName, sessionIds, isMoveAll); + } else { + AUDIO_INFO_LOG("sink is init ,use async mode move to:%{public}s.", sinkName.c_str()); + auto request = [this, sinkName, sessionIds, isMoveAll]() { + MoveAllStreamToNewSink(sinkName, sessionIds, isMoveAll); + }; + SendRequest(request); + } + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::MoveStream(uint32_t sessionId, const std::string &sinkName) +{ + AUDIO_INFO_LOG("move session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + auto request = [this, sessionId, sinkName]() { + CHECK_AND_RETURN_LOG(sinkInputNode_ && sessionId == sinkInputNode_->GetSessionId(), + "could not find session:%{public}d,sink name:%{public}s", + sessionId, + sinkName.c_str()); + + std::shared_ptr inputNode = sinkInputNode_; + if (inputNode->GetState() == RENDERER_RUNNING) { + // todo: do fade out + } + DisConnectInputSession(); + sinkOutputNode_->StopStream(); + sinkInputNode_ = nullptr; + formatConverterNode_ = nullptr; + if (!sinkName.empty()) { + std::string name = sinkName; + AUDIO_ERR_LOG("trigger call back, sink name:%{public}s", sinkName.c_str()); + TriggerCallback(MOVE_SINK_INPUT, inputNode, name); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::SuspendStreamManager(bool isSuspend) +{ + auto request = [this, isSuspend]() { + if (isSuspend) { + // todo fadout + sinkOutputNode_->RenderSinkStop(); + } else { + // todo fadin + sinkOutputNode_->RenderSinkStart(); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::SetMute(bool isMute) +{ + auto request = [this, isMute]() { + isMute_ = isMute; // todo: set to sinkoutputnode + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeOffloadRendererManager::HandleMsg() +{ + hpaeNoLockQueue_.HandleRequests(); +} + +int32_t HpaeOffloadRendererManager::Init() +{ + hpaeSignalProcessThread_ = std::make_unique(); + auto request = [this] { + AUDIO_INFO_LOG("HpaeOffloadRendererManager::init"); + HpaeNodeInfo nodeInfo; + nodeInfo.channels = sinkInfo_.channels; + nodeInfo.format = sinkInfo_.format; + nodeInfo.frameLen = sinkInfo_.frameLen; + nodeInfo.nodeId = 0; + nodeInfo.samplingRate = sinkInfo_.samplingRate; + nodeInfo.sceneType = HPAE_SCENE_EFFECT_OUT; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.statusCallback = weak_from_this(); + nodeInfo.nodeName = "HpaeOffloadSinkOutputNode"; + nodeInfo.nodeId = OnGetNodeId(); + sinkOutputNode_ = std::make_unique(nodeInfo); + OnNotifyDfxNodeInfo(true, 0, nodeInfo); + sinkOutputNode_->SetTimeoutStopThd(sinkInfo_.suspendTime); + // if failed, RenderSinkInit will failed either, so no need to deal ret + AUDIO_INFO_LOG("HpaeOffloadRendererManager::GetRenderSinkInstance"); + sinkOutputNode_->GetRenderSinkInstance(sinkInfo_.deviceClass, sinkInfo_.deviceNetId); + IAudioSinkAttr attr; + attr.adapterName = sinkInfo_.adapterName.c_str(); + attr.sampleRate = sinkInfo_.samplingRate; + attr.channel = sinkInfo_.channels; + attr.format = sinkInfo_.format; + attr.channelLayout = sinkInfo_.channelLayout; + attr.deviceType = sinkInfo_.deviceType; + attr.volume = sinkInfo_.volume; + attr.openMicSpeaker = sinkInfo_.openMicSpeaker; + attr.deviceNetworkId = sinkInfo_.deviceNetId.c_str(); + attr.filePath = sinkInfo_.filePath.c_str(); + int32_t ret = sinkOutputNode_->RenderSinkInit(attr); + isInit_.store(true); + TriggerCallback(INIT_DEVICE_RESULT, sinkInfo_.deviceName, ret); + AUDIO_INFO_LOG("HpaeOffloadRendererManager::inited"); + }; + SendRequest(request, true); + hpaeSignalProcessThread_->ActivateThread(shared_from_this()); + return SUCCESS; +} + +bool HpaeOffloadRendererManager::DeactivateThread() +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + return true; +} + +int32_t HpaeOffloadRendererManager::DeInit(bool isMoveDefault) +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + int32_t ret = sinkOutputNode_->RenderSinkDeInit(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "RenderSinkDeInit error, ret %{public}d.", ret); + sinkOutputNode_->ResetAll(); + isInit_.store(false); + if (isMoveDefault) { + std::string sinkName = ""; + std::vector ids; + AUDIO_INFO_LOG("move all sink to default sink"); + MoveAllStreamToNewSink(sinkName, ids, true); + } + return SUCCESS; +} + +bool HpaeOffloadRendererManager::IsInit() +{ + return isInit_.load(); +} + +bool HpaeOffloadRendererManager::IsRunning(void) +{ + if (sinkOutputNode_ != nullptr && hpaeSignalProcessThread_ != nullptr) { + return sinkOutputNode_->GetSinkState() == RENDERER_RUNNING && hpaeSignalProcessThread_->IsRunning(); + } + return false; +} + +bool HpaeOffloadRendererManager::IsMsgProcessing() +{ + return !hpaeNoLockQueue_.IsFinishProcess(); +} + +int32_t HpaeOffloadRendererManager::SetClientVolume(uint32_t sessionId, float volume) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::SetRate(uint32_t sessionId, int32_t rate) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::SetPrivacyType(uint32_t sessionId, int32_t privacyType) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::GetPrivacyType(uint32_t sessionId, int32_t &privacyType) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::RegisterWriteCallback( + uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + CHECK_AND_RETURN_LOG(sessionId == sinkInputNode_->GetSessionId(), + "RegisterWriteCallback not find sessionId %{public}u", + sessionId); + sinkInputNode_->RegisterWriteCallback(callback); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::RegisterReadCallback( + uint32_t sessionId, const std::weak_ptr &callback) +{ + return ERR_NOT_SUPPORTED; +} + +void HpaeOffloadRendererManager::Process() +{ + if (sinkOutputNode_ != nullptr && IsRunning()) { + sinkOutputNode_->DoProcess(); + } +} + +int32_t HpaeOffloadRendererManager::SetOffloadPolicy(uint32_t sessionId, int32_t state) +{ + auto request = [this, sessionId, state]() { + CHECK_AND_RETURN_LOG(sessionId == sinkInputNode_->GetSessionId(), + "RegisterWriteCallback not find sessionId %{public}u", + sessionId); + if (sinkOutputNode_) { + sinkOutputNode_->SetPolicyState(state); + } + }; + SendRequest(request); + return SUCCESS; +} + +size_t HpaeOffloadRendererManager::GetWritableSize(uint32_t sessionId) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) +{ + return SUCCESS; +} + +int32_t HpaeOffloadRendererManager::UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) +{ + return SUCCESS; +} + +std::vector HpaeOffloadRendererManager::GetAllSinkInputsInfo() +{ + std::vector sinkInputs; + return sinkInputs; +} + +int32_t HpaeOffloadRendererManager::GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) +{ + CHECK_AND_RETURN_RET_LOG(sinkInputNode_ && sessionId == sinkInputNode_->GetSessionId(), + ERR_INVALID_OPERATION, + "RegisterWriteCallback not find sessionId %{public}u", + sessionId); + sinkInputInfo.nodeInfo = sinkInputNode_->GetNodeInfo(); + sinkInputInfo.rendererSessionInfo = sessionInfo_; + return SUCCESS; +} + +HpaeSinkInfo HpaeOffloadRendererManager::GetSinkInfo() +{ + return sinkInfo_; +} + +void HpaeOffloadRendererManager::SendRequest(Request &&request, bool isInit) +{ + CHECK_AND_RETURN_LOG(isInit || IsInit(), "HpaeOffloadRendererManager not init"); + hpaeNoLockQueue_.PushRequest(std::move(request)); + CHECK_AND_RETURN_LOG(hpaeSignalProcessThread_, "hpaeSignalProcessThread_ offloadrenderer is nullptr"); + hpaeSignalProcessThread_->Notify(); +} + +void HpaeOffloadRendererManager::OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) +{ + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionInfo_.state, operation); +} + +void HpaeOffloadRendererManager::OnRequestLatency(uint32_t sessionId, uint64_t &latency) +{ + latency = sinkOutputNode_->GetLatency(); +} + +void HpaeOffloadRendererManager::OnRewindAndFlush(uint64_t rewindTime) +{ + sinkInputNode_->RewindHistoryBuffer(rewindTime); +} + +void HpaeOffloadRendererManager::OnNotifyQueue() +{ + hpaeSignalProcessThread_->Notify(); +} + +std::string HpaeOffloadRendererManager::GetThreadName() +{ + return sinkInfo_.deviceName; +} + +void HpaeOffloadRendererManager::DumpSinkInfo() +{ + auto request = [this]() { + AUDIO_INFO_LOG("DumpSinkInfo deviceName %{public}s", sinkInfo_.deviceName.c_str()); + UploadDumpSinkInfo(sinkInfo_.deviceName); + }; + SendRequest(request); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/manager/src/hpae_policy_manager.cpp b/services/audio_engine/manager/src/hpae_policy_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63900974fa1ac708708ac350956950dc3ca056db --- /dev/null +++ b/services/audio_engine/manager/src/hpae_policy_manager.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaePolicyManager" +#endif +#include "hpae_policy_manager.h" +#include +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "audio_effect_chain_manager.h" +#include "audio_enhance_chain_manager.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +HpaePolicyManager::HpaePolicyManager() +{ + AUDIO_DEBUG_LOG("Hpae policy manager created"); +} + +HpaePolicyManager::~HpaePolicyManager() +{ + AUDIO_WARNING_LOG("Hpae policy manager destroyed"); +} + +void HpaePolicyManager::InitAudioEffectChainManager(const std::vector &effectChains, + const EffectChainManagerParam &effectChainManagerParam, + const std::vector> &effectLibraryList) +{ + AudioEffectChainManager::GetInstance()->InitAudioEffectChainManager(effectChains, + effectChainManagerParam, effectLibraryList); +} + +void HpaePolicyManager::SetOutputDeviceSink(int32_t device, const std::string &sinkName) +{ + AudioEffectChainManager::GetInstance()->SetOutputDeviceSink(device, sinkName); +} + +int32_t HpaePolicyManager::UpdateSpatializationState(AudioSpatializationState spatializationState) +{ + return AudioEffectChainManager::GetInstance()->UpdateSpatializationState(spatializationState); +} + +int32_t HpaePolicyManager::UpdateSpatialDeviceType(AudioSpatialDeviceType spatialDeviceType) +{ + return AudioEffectChainManager::GetInstance()->UpdateSpatialDeviceType(spatialDeviceType); +} + +int32_t HpaePolicyManager::SetSpatializationSceneType(AudioSpatializationSceneType spatializationSceneType) +{ + return AudioEffectChainManager::GetInstance()->SetSpatializationSceneType(spatializationSceneType); +} + +int32_t HpaePolicyManager::EffectRotationUpdate(const uint32_t rotationState) +{ + return AudioEffectChainManager::GetInstance()->EffectRotationUpdate(rotationState); +} + +int32_t HpaePolicyManager::SetEffectSystemVolume(const int32_t systemVolumeType, const float systemVolume) +{ + return AudioEffectChainManager::GetInstance()->SetEffectSystemVolume(systemVolumeType, systemVolume); +} + +int32_t HpaePolicyManager::SetAudioEffectProperty(const AudioEffectPropertyArrayV3 &propertyArray) +{ + return AudioEffectChainManager::GetInstance()->SetAudioEffectProperty(propertyArray); +} + +int32_t HpaePolicyManager::GetAudioEffectProperty(AudioEffectPropertyArrayV3 &propertyArray) +{ + return AudioEffectChainManager::GetInstance()->GetAudioEffectProperty(propertyArray); +} + +int32_t HpaePolicyManager::SetAudioEffectProperty(const AudioEffectPropertyArray &propertyArray) +{ + return AudioEffectChainManager::GetInstance()->SetAudioEffectProperty(propertyArray); +} + +int32_t HpaePolicyManager::GetAudioEffectProperty(AudioEffectPropertyArray &propertyArray) +{ + return AudioEffectChainManager::GetInstance()->GetAudioEffectProperty(propertyArray); +} + +void HpaePolicyManager::InitHdiState() +{ + AudioEffectChainManager::GetInstance()->InitHdiState(); +} + +void HpaePolicyManager::UpdateEffectBtOffloadSupported(const bool &isSupported) +{ + AudioEffectChainManager::GetInstance()->UpdateEffectBtOffloadSupported(isSupported); +} + +void HpaePolicyManager::UpdateParamExtra(const std::string &mainkey, const std::string &subkey, + const std::string &value) +{ + AudioEffectChainManager::GetInstance()->UpdateParamExtra(mainkey, subkey, value); +} + +void HpaePolicyManager::InitAudioEnhanceChainManager(const std::vector &enhanceChains, + const EffectChainManagerParam &managerParam, + const std::vector> &enhanceLibraryList) +{ + AudioEnhanceChainManager::GetInstance()->InitAudioEnhanceChainManager(enhanceChains, managerParam, + enhanceLibraryList); +} + +int32_t HpaePolicyManager::SetInputDevice(const uint32_t &captureId, const DeviceType &inputDevice, + const std::string &deviceName) +{ + return AudioEnhanceChainManager::GetInstance()->SetInputDevice(captureId, inputDevice, deviceName); +} + +int32_t HpaePolicyManager::SetOutputDevice(const uint32_t &renderId, const DeviceType &outputDevice) +{ + return AudioEnhanceChainManager::GetInstance()->SetOutputDevice(renderId, outputDevice); +} + +int32_t HpaePolicyManager::SetVolumeInfo(const AudioVolumeType &volumeType, const float &systemVol) +{ + return AudioEnhanceChainManager::GetInstance()->SetVolumeInfo(volumeType, systemVol); +} + +int32_t HpaePolicyManager::SetMicrophoneMuteInfo(const bool &isMute) +{ + return AudioEnhanceChainManager::GetInstance()->SetMicrophoneMuteInfo(isMute); +} + +int32_t HpaePolicyManager::SetStreamVolumeInfo(const uint32_t &sessionId, const float &streamVol) +{ + return AudioEnhanceChainManager::GetInstance()->SetStreamVolumeInfo(sessionId, streamVol); +} + +int32_t HpaePolicyManager::SetAudioEnhanceProperty(const AudioEffectPropertyArrayV3 &propertyArray, + DeviceType deviceType) +{ + return AudioEnhanceChainManager::GetInstance()->SetAudioEnhanceProperty(propertyArray, deviceType); +} + +int32_t HpaePolicyManager::GetAudioEnhanceProperty(AudioEffectPropertyArrayV3 &propertyArray, + DeviceType deviceType) +{ + return AudioEnhanceChainManager::GetInstance()->GetAudioEnhanceProperty(propertyArray, deviceType); +} + +int32_t HpaePolicyManager::SetAudioEnhanceProperty(const AudioEnhancePropertyArray &propertyArray, + DeviceType deviceType) +{ + return AudioEnhanceChainManager::GetInstance()->SetAudioEnhanceProperty(propertyArray, deviceType); +} + +// todo: change to callback mode +int32_t HpaePolicyManager::GetAudioEnhanceProperty(AudioEnhancePropertyArray &propertyArray, + DeviceType deviceType) +{ + return AudioEnhanceChainManager::GetInstance()->GetAudioEnhanceProperty(propertyArray, deviceType); +} + +void HpaePolicyManager::UpdateExtraSceneType(const std::string &mainkey, const std::string &subkey, + const std::string &extraSceneType) +{ + return AudioEnhanceChainManager::GetInstance()->UpdateExtraSceneType(mainkey, subkey, extraSceneType); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/manager/src/hpae_renderer_manager.cpp b/services/audio_engine/manager/src/hpae_renderer_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f23a18fe1c752f8a59720e98f18bcd353cbcda2 --- /dev/null +++ b/services/audio_engine/manager/src/hpae_renderer_manager.cpp @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeRendererManager" +#endif +#include "hpae_renderer_manager.h" +#include "audio_stream_info.h" +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "hpae_node_common.h" +#include "audio_effect_chain_manager.h" +#include "audio_utils.h" + +constexpr int32_t DEFAULT_EFFECT_RATE = 48000; +constexpr int32_t DEFAULT_EFFECT_FRAME_LEN = 960; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +HpaeRendererManager::HpaeRendererManager(HpaeSinkInfo &sinkInfo) + : hpaeNoLockQueue_(CURRENT_REQUEST_COUNT), sinkInfo_(sinkInfo) +{} + +HpaeRendererManager::~HpaeRendererManager() +{ + AUDIO_INFO_LOG("destructor renderer"); + if (isInit_.load()) { + DeInit(); + } +} + +bool HpaeRendererManager::IsMchDevice() +{ + return sinkInfo_.deviceName == "MCH_Speaker"; +} + +int32_t HpaeRendererManager::CreateInputSession(const HpaeStreamInfo &streamInfo) +{ + Trace trace("[" + std::to_string(streamInfo.sessionId) + "]HpaeRendererManager::CreateInputSession"); + HpaeNodeInfo nodeInfo; + nodeInfo.channels = streamInfo.channels; + nodeInfo.format = streamInfo.format; + nodeInfo.frameLen = streamInfo.frameLen; + nodeInfo.channelLayout = (AudioChannelLayout)streamInfo.channelLayout; + nodeInfo.streamType = streamInfo.streamType; + nodeInfo.sessionId = streamInfo.sessionId; + nodeInfo.samplingRate = static_cast(streamInfo.samplingRate); + nodeInfo.sceneType = TransStreamTypeToSceneType(streamInfo.streamType); + nodeInfo.effectInfo = streamInfo.effectInfo; + nodeInfo.fadeType = streamInfo.fadeType; + nodeInfo.statusCallback = weak_from_this(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + nodeInfo.nodeName = "HpaeSinkInputNode"; + nodeInfo.nodeId = OnGetNodeId(); + sinkInputNodeMap_[streamInfo.sessionId] = std::make_shared(nodeInfo); + + AUDIO_INFO_LOG("streamType %{public}u, sessionId = %{public}u, current sceneType is %{public}d", + nodeInfo.streamType, + nodeInfo.sessionId, + nodeInfo.sceneType); + if (IsMchDevice()) { + AUDIO_INFO_LOG("MCH device, only need create gain node"); + nodeInfo.nodeName = "HpaeGainNode"; + nodeInfo.nodeId = OnGetNodeId(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + mchIdGainNodeMap_[streamInfo.sessionId] = std::make_shared(nodeInfo); + return SUCCESS; + } + std::shared_ptr hpaeProcessCluster = CreateProcessCluster(nodeInfo); + if (hpaeProcessCluster != nullptr) { + sceneClusterMap_[nodeInfo.sceneType] = hpaeProcessCluster; + } + int32_t ret = sceneClusterMap_[nodeInfo.sceneType]->AudioRendererCreate(nodeInfo); + if (ret != SUCCESS) { + AUDIO_WARNING_LOG("update audio effect when creating failed, ret = %{public}d", ret); + } + return SUCCESS; +} + +int32_t HpaeRendererManager::AddNodeToSink(const std::shared_ptr &node) +{ + auto request = [this, node]() { AddSingleNodeToSink(node); }; + SendRequest(request); + return SUCCESS; +} + +void HpaeRendererManager::AddSingleNodeToSink(const std::shared_ptr &node, bool isConnect) +{ + Trace trace("HpaeRendererManager::AddSingleNodeToSink"); + HpaeNodeInfo nodeInfo = node->GetNodeInfo(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + // no need history buffer in not offload sink + nodeInfo.historyFrameCount = 0; + nodeInfo.statusCallback = weak_from_this(); + node->SetNodeInfo(nodeInfo); + uint32_t sessionId = nodeInfo.sessionId; + AUDIO_INFO_LOG("add node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + sinkInputNodeMap_[sessionId] = node; + SetSessionState(sessionId, node->GetState()); + sessionNodeMap_[sessionId].sinkInputNodeId = nodeInfo.nodeId; + sessionNodeMap_[sessionId].sceneType = nodeInfo.sceneType; + + AUDIO_INFO_LOG("streamType %{public}u, sessionId = %{public}u, current sceneType is %{public}d", + nodeInfo.streamType, + nodeInfo.sessionId, + nodeInfo.sceneType); + HpaeNodeInfo processNodeInfo = nodeInfo; + processNodeInfo.samplingRate = (AudioSamplingRate)DEFAULT_EFFECT_RATE; + processNodeInfo.frameLen = (uint32_t)DEFAULT_EFFECT_FRAME_LEN; + processNodeInfo.channels = STEREO; + processNodeInfo.channelLayout = CH_LAYOUT_STEREO; + std::shared_ptr hpaeProcessCluster = CreateProcessCluster(processNodeInfo); + if (hpaeProcessCluster != nullptr) { + sceneClusterMap_[nodeInfo.sceneType] = hpaeProcessCluster; + } + int32_t ret = sceneClusterMap_[nodeInfo.sceneType]->AudioRendererCreate(nodeInfo); + if (ret != SUCCESS) { + AUDIO_WARNING_LOG("update audio effect when creating failed, ret = %{public}d", ret); + } + + if (!isConnect) { + AUDIO_INFO_LOG("not need connect session:%{public}d", sessionId); + return; + } + if (node->GetState() == RENDERER_RUNNING) { + AUDIO_INFO_LOG("connect node :%{public}d to sink:%{public}s", sessionId, sinkInfo_.deviceClass.c_str()); + ConnectInputSession(sessionId); // todo: fadein + if (outputCluster_->GetState() != RENDERER_RUNNING) { + outputCluster_->Start(); + } + } +} + +std::shared_ptr HpaeRendererManager::CreateDefaultProcessCluster(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("use default processCluster"); + std::shared_ptr hpaeProcessCluster = nullptr; + if (sceneClusterMap_.find(HPAE_SCENE_DEFAULT) == sceneClusterMap_.end()) { + AUDIO_INFO_LOG("default processCluster is null, create default processCluster"); + hpaeProcessCluster = std::make_shared(nodeInfo, sinkInfo_); + sceneClusterMap_[HPAE_SCENE_DEFAULT] = hpaeProcessCluster; + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_DEFAULT]++; + } else { + hpaeProcessCluster = sceneClusterMap_[HPAE_SCENE_DEFAULT]; + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_DEFAULT]++; + } + return hpaeProcessCluster; +} + +std::shared_ptr HpaeRendererManager::CreateProcessCluster(HpaeNodeInfo &nodeInfo) +{ + Trace trace("HpaeRendererManager::CreateProcessCluster"); + std::shared_ptr hpaeProcessCluster = nullptr; + std::string sceneType = TransProcessorTypeToSceneType(nodeInfo.sceneType); + int32_t processClusterDecision = AudioEffectChainManager::GetInstance()->CheckProcessClusterInstances(sceneType); + switch (processClusterDecision) { + case NO_NEED_TO_CREATE_PROCESSCLUSTER: + AUDIO_INFO_LOG("no need to create processCluster"); + if (sceneClusterMap_.find(nodeInfo.sceneType) == sceneClusterMap_.end()) { + AUDIO_INFO_LOG("processCluster is null, create a new processCluster"); + hpaeProcessCluster = std::make_shared(nodeInfo, sinkInfo_); + } + break; + case CREATE_NEW_PROCESSCLUSTER: + AUDIO_INFO_LOG("create new processCluster"); + hpaeProcessCluster = std::make_shared(nodeInfo, sinkInfo_); + break; + case CREATE_DEFAULT_PROCESSCLUSTER: + AUDIO_INFO_LOG("begin control, create default processCluster"); + hpaeProcessCluster = std::make_shared(nodeInfo, sinkInfo_); + sceneClusterMap_[HPAE_SCENE_DEFAULT] = hpaeProcessCluster; + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_DEFAULT]++; + break; + case USE_DEFAULT_PROCESSCLUSTER: + hpaeProcessCluster = CreateDefaultProcessCluster(nodeInfo); + break; + case USE_NONE_PROCESSCLUSTER: + AUDIO_INFO_LOG("use none processCluster"); + hpaeProcessCluster = sceneClusterMap_[HPAE_SCENE_EFFECT_NONE]; + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_EFFECT_NONE]++; + break; + case CREATE_EXTRA_PROCESSCLUSTER: + AUDIO_INFO_LOG("out of control"); + if (sceneClusterMap_.find(nodeInfo.sceneType) == sceneClusterMap_.end()) { + AUDIO_INFO_LOG("out of control, create a new processCluster"); + hpaeProcessCluster = std::make_shared(nodeInfo, sinkInfo_); + } + break; + default: + break; + } + sceneTypeToProcessClusterCountMap_[nodeInfo.sceneType]++; + return hpaeProcessCluster; +} + +int32_t HpaeRendererManager::AddAllNodesToSink( + const std::vector> &sinkInputs, bool isConnect) +{ + auto request = [this, sinkInputs, isConnect]() { + for (const auto &it : sinkInputs) { + AddSingleNodeToSink(it, isConnect); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::CreateStream(const HpaeStreamInfo &streamInfo) +{ + Trace trace("HpaeRendererManager::CreateStream id[" + + std::to_string(streamInfo.sessionId) + "]"); + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, streamInfo]() { + AUDIO_INFO_LOG("CreateStream sessionId %{public}u deviceName %{public}s", + streamInfo.sessionId, + sinkInfo_.deviceName.c_str()); + CreateInputSession(streamInfo); + SetSessionState(streamInfo.sessionId, RENDERER_NEW); + sinkInputNodeMap_[streamInfo.sessionId]->SetState(RENDERER_NEW); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::DestroyStream(uint32_t sessionId) +{ + Trace trace("HpaeRendererManager::DestroyStream id[" + + std::to_string(sessionId) + "]"); + if (!IsInit()) { + return ERR_INVALID_OPERATION; + } + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("DestroyStream sessionId %{public}u", sessionId); + DeleteInputSession(sessionId); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::DeleteMchInputSession(uint32_t sessionId) +{ + DisConnectMchInputSession(sessionId); + sinkInputNodeMap_.erase(sessionId); + if (mchIdGainNodeMap_.find(sessionId) != mchIdGainNodeMap_.end()) { + mchIdGainNodeMap_.erase(sessionId); + } else { + AUDIO_ERR_LOG("could not find gain node id:%{public}d", sessionId); + return ERROR; + } + return SUCCESS; +} + +int32_t HpaeRendererManager::DeleteInputSession(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::DeleteInputSession"); + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("could not find session:%{public}d", sessionId); + return SUCCESS; + } + if (IsMchDevice()) { + return DeleteMchInputSession(sessionId); + } else { + HpaeNodeInfo nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + int32_t effectMode = nodeInfo.effectInfo.effectMode; + HpaeProcessorType sceneType = (effectMode == EFFECT_NONE) ? HPAE_SCENE_EFFECT_NONE : nodeInfo.sceneType; + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + DeleteProcessCluster(nodeInfo, sceneType, sessionId); + } + sinkInputNodeMap_.erase(sessionId); + } + return SUCCESS; +} + +void HpaeRendererManager::DeleteProcessCluster( + const HpaeNodeInfo &nodeInfo, HpaeProcessorType sceneType, uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + + "]HpaeRendererManager::DeleteProcessCluster sceneType:" + std::to_string(sessionId)); + sceneClusterMap_[nodeInfo.sceneType]->AudioRendererRelease(sinkInputNodeMap_[sessionId]->GetNodeInfo()); + sceneClusterMap_[sceneType]->DisConnect(sinkInputNodeMap_[sessionId]); + sceneTypeToProcessClusterCountMap_[nodeInfo.sceneType]--; + if (sceneClusterMap_[nodeInfo.sceneType] == sceneClusterMap_[HPAE_SCENE_DEFAULT]) { + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_DEFAULT]--; + } + if (sceneClusterMap_[nodeInfo.sceneType] == sceneClusterMap_[HPAE_SCENE_EFFECT_NONE] && + nodeInfo.sceneType != HPAE_SCENE_EFFECT_NONE) { + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_EFFECT_NONE]--; + } + + if (sceneClusterMap_[sceneType]->GetPreOutNum() == 0) { + outputCluster_->DisConnect(sceneClusterMap_[sceneType]); + } + + if (sceneTypeToProcessClusterCountMap_[nodeInfo.sceneType] == 0) { + sceneClusterMap_.erase(nodeInfo.sceneType); + sceneTypeToProcessClusterCountMap_.erase(nodeInfo.sceneType); + } + if (sceneTypeToProcessClusterCountMap_[HPAE_SCENE_DEFAULT] == 0) { + sceneClusterMap_.erase(HPAE_SCENE_DEFAULT); + sceneTypeToProcessClusterCountMap_.erase(HPAE_SCENE_DEFAULT); + } +} + +int32_t HpaeRendererManager::ConnectMchInputSession(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::ConnectMchInputSession"); + AUDIO_INFO_LOG("Mch Device connect input session:%{public}d", sessionId); + if (mchIdGainNodeMap_.find(sessionId) == mchIdGainNodeMap_.end()) { + AUDIO_INFO_LOG("Mch Device connect can not find gain node, create it session:%{public}d", sessionId); + auto nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + nodeInfo.nodeName = "HpaeGainNode"; + nodeInfo.nodeId = OnGetNodeId(); + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + mchIdGainNodeMap_[sessionId] = std::make_shared(nodeInfo); + } + mchIdGainNodeMap_[sessionId]->Connect(sinkInputNodeMap_[sessionId]); + outputCluster_->Connect(mchIdGainNodeMap_[sessionId]); + OnNotifyDfxNodeInfo(true, mchIdGainNodeMap_[sessionId]->GetNodeId(), sinkInputNodeMap_[sessionId]->GetNodeInfo()); + return SUCCESS; +} + +int32_t HpaeRendererManager::ConnectInputSession(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::ConnectInputSession"); + AUDIO_INFO_LOG("connect input session:%{public}d", sessionId); + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("could not input node by sessionid:%{public}d", sessionId); + return ERR_INVALID_PARAM; + } + if (sinkInputNodeMap_[sessionId]->GetState() != RENDERER_RUNNING) { + return SUCCESS; + } + if (IsMchDevice()) { + return ConnectMchInputSession(sessionId); + } + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + HpaeNodeInfo nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + int32_t effectMode = nodeInfo.effectInfo.effectMode; + if (effectMode == EFFECT_NONE) { + sceneType = HPAE_SCENE_EFFECT_NONE; + } + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + ConnectProcessCluster(sessionId, sceneType); + } + return SUCCESS; +} + +void HpaeRendererManager::ConnectProcessCluster(uint32_t sessionId, HpaeProcessorType sceneType) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::ConnectProcessCluster sceneType:" + + std::to_string(sceneType)); + int32_t ret = sceneClusterMap_[sceneType]->AudioRendererStart(sinkInputNodeMap_[sessionId]->GetNodeInfo()); + if (ret != SUCCESS) { + AUDIO_WARNING_LOG("update audio effect when starting failed, ret = %{public}d", ret); + } + if (!outputCluster_->IsProcessClusterConnected(sceneType)) { + outputCluster_->Connect(sceneClusterMap_[sceneType]); + } + sceneClusterMap_[sceneType]->Connect(sinkInputNodeMap_[sessionId]); +} + +void HpaeRendererManager::MoveAllStreamToNewSink(const std::string &sinkName, + const std::vector& moveIds, bool isMoveAll) +{ + Trace trace("HpaeRendererManager::MoveAllStreamToNewSink[" + sinkName + "]"); + std::string name = sinkName; + std::vector> sinkInputs; + std::vector sessionIds; + for (const auto &it : sinkInputNodeMap_) { + if (isMoveAll || std::find(moveIds.begin(), moveIds.end(), it.first) != moveIds.end()) { + sinkInputs.emplace_back(it.second); + sessionIds.emplace_back(it.first); + } + } + for (const auto &it : sessionIds) { + DisConnectInputSession(it); + } + AUDIO_INFO_LOG("sink input count:%{public}zu", sinkInputs.size()); + TriggerCallback(MOVE_ALL_SINK_INPUT, sinkInputs, name); +} + +int32_t HpaeRendererManager::MoveAllStream(const std::string &sinkName, const std::vector& sessionIds, + bool isMoveAll) +{ + if (!IsInit()) { + AUDIO_INFO_LOG("sink is not init ,use sync mode move to:%{public}s.", sinkName.c_str()); + MoveAllStreamToNewSink(sinkName, sessionIds, isMoveAll); + } else { + AUDIO_INFO_LOG("sink is init ,use async mode move to:%{public}s.", sinkName.c_str()); + auto request = [this, sinkName, sessionIds, isMoveAll]() { + MoveAllStreamToNewSink(sinkName, sessionIds, isMoveAll); + }; + SendRequest(request); + } + return SUCCESS; +} + +int32_t HpaeRendererManager::MoveStream(uint32_t sessionId, const std::string &sinkName) +{ + AUDIO_INFO_LOG("move session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + auto request = [this, sessionId, sinkName]() { + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_ERR_LOG("could not find session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + return; + } + AUDIO_INFO_LOG("move enter session:%{public}d,sink name:%{public}s", sessionId, sinkName.c_str()); + std::shared_ptr inputNode = sinkInputNodeMap_[sessionId]; + if (inputNode->GetState() == RENDERER_RUNNING) { + // todo: do fade out + } + DeleteInputSession(sessionId); + if (!sinkName.empty()) { + std::string name = sinkName; + AUDIO_ERR_LOG("trigger call back, sink name:%{public}s", sinkName.c_str()); + TriggerCallback(MOVE_SINK_INPUT, inputNode, name); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::Start(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Start"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Start sessionId %{public}u, deviceName %{public}s", sessionId, sinkInfo_.deviceName.c_str()); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_RUNNING); + } + ConnectInputSession(sessionId); + SetSessionState(sessionId, RENDERER_RUNNING); + if (outputCluster_->GetState() != RENDERER_RUNNING) { + outputCluster_->Start(); + } + SetSessionFade(sessionId, OPERATION_STARTED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::DisConnectMchInputSession(uint32_t sessionId) +{ + AUDIO_INFO_LOG("Mch Device Disconnect input session:%{public}d", sessionId); + if (mchIdGainNodeMap_.find(sessionId) == mchIdGainNodeMap_.end()) { + AUDIO_INFO_LOG("Mch Device DisConnect can not find gain node session:%{public}u", sessionId); + return ERROR; + } + mchIdGainNodeMap_[sessionId]->DisConnect(sinkInputNodeMap_[sessionId]); + outputCluster_->DisConnect(mchIdGainNodeMap_[sessionId]); + return SUCCESS; +} + +int32_t HpaeRendererManager::DisConnectInputSession(uint32_t sessionId) +{ + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("DisConnectInputSession sessionId %{public}u", sessionId); + return SUCCESS; + } + if (IsMchDevice()) { + return DisConnectMchInputSession(sessionId); + } + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + HpaeNodeInfo nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + int32_t effectMode = nodeInfo.effectInfo.effectMode; + if (effectMode == EFFECT_NONE) { + sceneType = HPAE_SCENE_EFFECT_NONE; + } + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + DisConnectProcessCluster(sessionId, sceneType); + } + return SUCCESS; +} + +void HpaeRendererManager::DisConnectProcessCluster(uint32_t sessionId, HpaeProcessorType sceneType) +{ + sceneClusterMap_[sceneType]->DisConnect(sinkInputNodeMap_[sessionId]); + int32_t ret = sceneClusterMap_[sceneType]->AudioRendererStop(sinkInputNodeMap_[sessionId]->GetNodeInfo()); + if (ret != SUCCESS) { + AUDIO_WARNING_LOG("update audio effect when stopping failed, ret = %{public}d", ret); + } + if (sceneClusterMap_[sceneType]->GetPreOutNum() == 0) { + outputCluster_->DisConnect(sceneClusterMap_[sceneType]); + } +} + +void HpaeRendererManager::SetSessionState(uint32_t sessionId, RendererState renderState) +{ + sessionNodeMap_[sessionId].state = renderState; +} + +int32_t HpaeRendererManager::Pause(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Pause"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Pause sessionId %{public}u deviceName %{public}s", sessionId, sinkInfo_.deviceName.c_str()); + if (!SetSessionFade(sessionId, OPERATION_PAUSED)) { + DisConnectInputSession(sessionId); + } + SetSessionState(sessionId, RENDERER_PAUSED); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_PAUSED); + } + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + std::shared_ptr sessionGainNode = nullptr; + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + sessionGainNode = sceneClusterMap_[sceneType]->GetGainNodeById(sessionId); + } + if (sessionGainNode == nullptr) { + TriggerCallback(UPDATE_STATUS, + HPAE_STREAM_CLASS_TYPE_PLAY, + sessionId, + sessionNodeMap_[sessionId].state, + OPERATION_PAUSED); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::Flush(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Flush"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Flush sessionId %{public}u deviceName %{public}s", sessionId, sinkInfo_.deviceName.c_str()); + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("Flush not find sessionId %{public}u", sessionId); + return; + } + // flush history buffer + sinkInputNodeMap_[sessionId]->Flush(); + TriggerCallback( + UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionNodeMap_[sessionId].state, OPERATION_FLUSHED); + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::Drain(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Drain"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Drain sessionId %{public}u deviceName %{public}s ", sessionId, sinkInfo_.deviceName.c_str()); + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + AUDIO_INFO_LOG("Drain not find sessionId %{public}u", sessionId); + return; + } + sinkInputNodeMap_[sessionId]->Drain(); + if (sessionNodeMap_[sessionId].state != RENDERER_RUNNING) { + AUDIO_INFO_LOG("TriggerCallback Drain sessionId %{public}u", sessionId); + TriggerCallback(UPDATE_STATUS, + HPAE_STREAM_CLASS_TYPE_PLAY, + sessionId, + sessionNodeMap_[sessionId].state, + OPERATION_DRAINED); + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::Stop(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Stop"); + auto request = [this, sessionId]() { + AUDIO_INFO_LOG("Stop sessionId %{public}u deviceName %{public}s ", sessionId, sinkInfo_.deviceName.c_str()); + if (!SetSessionFade(sessionId, OPERATION_STOPPED)) { + DisConnectInputSession(sessionId); + } + SetSessionState(sessionId, RENDERER_STOPPED); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(RENDERER_STOPPED); + } + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + std::shared_ptr sessionGainNode = nullptr; + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + sessionGainNode = sceneClusterMap_[sceneType]->GetGainNodeById(sessionId); + } + if (sessionGainNode == nullptr) { + TriggerCallback(UPDATE_STATUS, + HPAE_STREAM_CLASS_TYPE_PLAY, + sessionId, + sessionNodeMap_[sessionId].state, + OPERATION_STOPPED); // if no gainnode, trigger + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::Release(uint32_t sessionId) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::Release"); + return DestroyStream(sessionId); +} + +int32_t HpaeRendererManager::SuspendStreamManager(bool isSuspend) +{ + Trace trace("HpaeRendererManager::SuspendStreamManager: " + std::to_string(isSuspend)); + auto request = [this, isSuspend]() { + if (isSuspend) { + if (outputCluster_ != nullptr) { + // todo fade out + outputCluster_->Stop(); + } + } else { + if (outputCluster_ != nullptr) { + // todo fade in + outputCluster_->Start(); + } + } + }; + SendRequest(request); + return SUCCESS; +} + +int32_t HpaeRendererManager::SetMute(bool isMute) +{ + // to do check pulseaudio + auto request = [this, isMute]() { + if (isMute_ != isMute) { + isMute_ = isMute; // todo: fadein and fadeout and mute feature + } + }; + SendRequest(request); + return SUCCESS; +} + +void HpaeRendererManager::HandleMsg() +{ + hpaeNoLockQueue_.HandleRequests(); +} + +void HpaeRendererManager::CreateOutputClusterNodeInfo(HpaeNodeInfo &nodeInfo) +{ + nodeInfo.channels = sinkInfo_.channels; + nodeInfo.format = sinkInfo_.format; + nodeInfo.frameLen = sinkInfo_.frameLen; + nodeInfo.nodeId = 0; + nodeInfo.samplingRate = sinkInfo_.samplingRate; + nodeInfo.sceneType = HPAE_SCENE_EFFECT_OUT; + nodeInfo.deviceNetId = sinkInfo_.deviceNetId; + nodeInfo.deviceClass = sinkInfo_.deviceClass; + nodeInfo.statusCallback = weak_from_this(); + return; +} + +int32_t HpaeRendererManager::Init() +{ + Trace trace("HpaeRendererManager::Init"); + hpaeSignalProcessThread_ = std::make_unique(); + auto request = [this] { + AUDIO_INFO_LOG("HpaeRendererManager::init devicename:%{public}s", sinkInfo_.deviceName.c_str()); + HpaeNodeInfo nodeInfo; + CreateOutputClusterNodeInfo(nodeInfo); + outputCluster_ = std::make_unique(nodeInfo); + outputCluster_->SetTimeoutStopThd(sinkInfo_.suspendTime); + int32_t ret = outputCluster_->GetInstance(sinkInfo_.deviceClass, sinkInfo_.deviceNetId); + IAudioSinkAttr attr; + attr.adapterName = sinkInfo_.adapterName.c_str(); + attr.sampleRate = sinkInfo_.samplingRate; + attr.channel = sinkInfo_.channels; + attr.format = sinkInfo_.format; + attr.channelLayout = sinkInfo_.channelLayout; + attr.deviceType = sinkInfo_.deviceType; + attr.volume = sinkInfo_.volume; + attr.openMicSpeaker = sinkInfo_.openMicSpeaker; + attr.deviceNetworkId = sinkInfo_.deviceNetId.c_str(); + attr.filePath = sinkInfo_.filePath.c_str(); + if (!sceneClusterMap_.count(HPAE_SCENE_EFFECT_NONE)) { + HpaeNodeInfo defaultNodeInfo; + defaultNodeInfo.frameLen = (uint32_t)DEFAULT_EFFECT_FRAME_LEN; + defaultNodeInfo.samplingRate = (AudioSamplingRate)DEFAULT_EFFECT_RATE; + defaultNodeInfo.format = AudioSampleFormat::INVALID_WIDTH; + defaultNodeInfo.channels = STEREO; + defaultNodeInfo.channelLayout = AudioChannelLayout::CH_LAYOUT_STEREO; + defaultNodeInfo.streamType = STREAM_DEFAULT; + defaultNodeInfo.sceneType = HPAE_SCENE_EFFECT_NONE; + defaultNodeInfo.deviceNetId = sinkInfo_.deviceNetId; + defaultNodeInfo.deviceClass = sinkInfo_.deviceClass; + defaultNodeInfo.statusCallback = weak_from_this(); + sceneClusterMap_[HPAE_SCENE_EFFECT_NONE] = std::make_shared(defaultNodeInfo, sinkInfo_); + sceneTypeToProcessClusterCountMap_[HPAE_SCENE_EFFECT_NONE] = 1; + } + + ret = outputCluster_->Init(attr); + isInit_.store(ret == SUCCESS); + TriggerCallback(INIT_DEVICE_RESULT, sinkInfo_.deviceName, ret); + }; + SendRequest(request, true); + hpaeSignalProcessThread_->ActivateThread(shared_from_this()); + return SUCCESS; +} + +bool HpaeRendererManager::DeactivateThread() +{ + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + return true; +} + +int32_t HpaeRendererManager::DeInit(bool isMoveDefault) +{ + Trace trace("HpaeRendererManager::DeInit"); + if (hpaeSignalProcessThread_ != nullptr) { + hpaeSignalProcessThread_->DeactivateThread(); + hpaeSignalProcessThread_ = nullptr; + } + hpaeNoLockQueue_.HandleRequests(); + int32_t ret = outputCluster_->DeInit(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "RenderSinkDeInit error, ret %{public}d.", ret); + outputCluster_->ResetAll(); + isInit_.store(false); + if (isMoveDefault) { + std::string sinkName = ""; + std::vector ids; + AUDIO_INFO_LOG("move all sink to default sink"); + MoveAllStreamToNewSink(sinkName, ids, true); + } + return SUCCESS; +} + +int32_t HpaeRendererManager::StartRenderSink() +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::SetClientVolume(uint32_t sessionId, float volume) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::SetRate(uint32_t sessionId, int32_t rate) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::SetAudioEffectMode(uint32_t sessionId, int32_t effectMode) +{ + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + if (effectMode < EFFECT_NONE || effectMode > EFFECT_DEFAULT) { + return ERR_INVALID_OPERATION; + } + + HpaeNodeInfo &nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + if (nodeInfo.effectInfo.effectMode != static_cast(effectMode)) { + nodeInfo.effectInfo.effectMode = static_cast(effectMode); + UpdateProcessClusterConnection(sessionId, effectMode); + } + return SUCCESS; +} + +int32_t HpaeRendererManager::GetAudioEffectMode(uint32_t sessionId, int32_t &effectMode) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::SetPrivacyType(uint32_t sessionId, int32_t privacyType) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::GetPrivacyType(uint32_t sessionId, int32_t &privacyType) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::RegisterWriteCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + auto request = [this, sessionId, callback]() { + AUDIO_INFO_LOG("RegisterWriteCallback sessionId %{public}u", sessionId); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->RegisterWriteCallback(callback); + } + }; + hpaeNoLockQueue_.PushRequest(request); + return SUCCESS; +} + +void HpaeRendererManager::Process() +{ + Trace trace("HpaeRendererManager::Process"); + if (outputCluster_ != nullptr && IsRunning()) { + outputCluster_->DoProcess(); + } +} + +size_t HpaeRendererManager::GetWritableSize(uint32_t sessionId) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::UpdateSpatializationState( + uint32_t sessionId, bool spatializationEnabled, bool headTrackingEnabled) +{ + return SUCCESS; +} + +int32_t HpaeRendererManager::UpdateMaxLength(uint32_t sessionId, uint32_t maxLength) +{ + return SUCCESS; +} + +std::vector HpaeRendererManager::GetAllSinkInputsInfo() +{ + return {}; +} + +int32_t HpaeRendererManager::GetSinkInputInfo(uint32_t sessionId, HpaeSinkInputInfo &sinkInputInfo) +{ + if (sinkInputNodeMap_.find(sessionId) == sinkInputNodeMap_.end()) { + return ERR_INVALID_OPERATION; + } + sinkInputInfo.nodeInfo = sinkInputNodeMap_[sessionId]->GetNodeInfo(); + sinkInputInfo.rendererSessionInfo = sessionNodeMap_[sessionId]; + return SUCCESS; +} + +HpaeSinkInfo HpaeRendererManager::GetSinkInfo() +{ + return sinkInfo_; +} + +bool HpaeRendererManager::IsInit() +{ + return isInit_.load(); +} + +bool HpaeRendererManager::IsMsgProcessing() +{ + return !hpaeNoLockQueue_.IsFinishProcess(); +} + +bool HpaeRendererManager::IsRunning(void) +{ + if (outputCluster_ != nullptr && hpaeSignalProcessThread_ != nullptr) { + return outputCluster_->GetState() == RENDERER_RUNNING && hpaeSignalProcessThread_->IsRunning(); + } else { + return false; + } +} + +void HpaeRendererManager::SendRequest(Request &&request, bool isInit) +{ + AUDIO_DEBUG_LOG("HpaeRendererManager::isInit is %{public}s", isInit ? "true" : "false"); + CHECK_AND_RETURN_LOG(isInit || IsInit(), "HpaeRendererManager not init"); + hpaeNoLockQueue_.PushRequest(std::move(request)); + CHECK_AND_RETURN_LOG(hpaeSignalProcessThread_, "hpaeSignalProcessThread_ renderer is nullptr"); + hpaeSignalProcessThread_->Notify(); +} + +void HpaeRendererManager::OnNodeStatusUpdate(uint32_t sessionId, IOperation operation) +{ + TriggerCallback(UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionNodeMap_[sessionId].state, operation); +} + +void HpaeRendererManager::OnFadeDone(uint32_t sessionId, IOperation operation) +{ + Trace trace("[" + std::to_string(sessionId) + "]HpaeRendererManager::OnFadeDone: " + std::to_string(operation)); + AUDIO_INFO_LOG("Fade done, call back at RendererManager"); + auto request = [this, sessionId, operation]() { + DisConnectInputSession(sessionId); + RendererState state = operation == OPERATION_STOPPED ? RENDERER_STOPPED : RENDERER_PAUSED; + SetSessionState(sessionId, state); + if (sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end()) { + sinkInputNodeMap_[sessionId]->SetState(state); + } + TriggerCallback( + UPDATE_STATUS, HPAE_STREAM_CLASS_TYPE_PLAY, sessionId, sessionNodeMap_[sessionId].state, operation); + }; + SendRequest(request); +} + +int32_t HpaeRendererManager::RegisterReadCallback(uint32_t sessionId, const std::weak_ptr &callback) +{ + return SUCCESS; +} + +void HpaeRendererManager::OnRequestLatency(uint32_t sessionId, uint64_t &latency) +{ + // todo: add processLatency + return; +} + +void HpaeRendererManager::OnNotifyQueue() +{ + hpaeSignalProcessThread_->Notify(); +} + +void HpaeRendererManager::UpdateProcessClusterConnection(uint32_t sessionId, int32_t effectMode) +{ + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + if (sceneClusterMap_.find(sceneType) == sceneClusterMap_.end()) { + AUDIO_WARNING_LOG("miss corresponding process cluster for scene type %{public}d", sceneType); + return; + } + if (effectMode == EFFECT_NONE) { + DisConnectProcessCluster(sessionId, sceneType); + ConnectProcessCluster(sessionId, HPAE_SCENE_EFFECT_NONE); + } else { + DisConnectProcessCluster(sessionId, HPAE_SCENE_EFFECT_NONE); + ConnectProcessCluster(sessionId, sceneType); + } +} + +std::string HpaeRendererManager::GetThreadName() +{ + return sinkInfo_.deviceName; +} + +bool HpaeRendererManager::SetSessionFade(uint32_t sessionId, IOperation operation) +{ + CHECK_AND_RETURN_RET_LOG(sinkInputNodeMap_.find(sessionId) != sinkInputNodeMap_.end(), false, + "can not get input node of session %{public}u", sessionId); + HpaeProcessorType sceneType = sinkInputNodeMap_[sessionId]->GetSceneType(); + std::shared_ptr sessionGainNode = nullptr; + if (sceneClusterMap_.find(sceneType) != sceneClusterMap_.end()) { + sessionGainNode = sceneClusterMap_[sceneType]->GetGainNodeById(sessionId); + AUDIO_INFO_LOG("get gain node of session %{public}d.", sessionId); + } + if (sessionGainNode != nullptr) { + sessionGainNode->SetFadeState(operation); + return true; + } + AUDIO_WARNING_LOG("session %{public}d do not have gain node!", sessionId); + return false; +} + +void HpaeRendererManager::DumpSinkInfo() +{ + auto request = [this]() { + AUDIO_INFO_LOG("DumpSinkInfo deviceName %{public}s", sinkInfo_.deviceName.c_str()); + UploadDumpSinkInfo(sinkInfo_.deviceName); + }; + SendRequest(request); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/manager/src/hpae_signal_process_thread.cpp b/services/audio_engine/manager/src/hpae_signal_process_thread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2736b8a0952dcd2cfc342a0b827898295623d9c1 --- /dev/null +++ b/services/audio_engine/manager/src/hpae_signal_process_thread.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hpae_signal_process_thread.h" +#include "audio_schedule.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeSignalProcessThread::~HpaeSignalProcessThread() +{ + DeactivateThread(); +} + +void HpaeSignalProcessThread::ActivateThread(const std::weak_ptr &streamManager) +{ + streamManager_ = streamManager; + running_.store(true); + auto threadFunc = std::bind(&HpaeSignalProcessThread::Run, this); + thread_ = std::thread(threadFunc); + if (streamManager_.lock() != nullptr) { + pthread_setname_np(thread_.native_handle(), streamManager_.lock()->GetThreadName().c_str()); + } +} + +void HpaeSignalProcessThread::DeactivateThread() +{ + running_.store(false); + Notify(); + if (thread_.joinable()) { + thread_.join(); + } +} + +void HpaeSignalProcessThread::Notify() +{ + recvSignal_.store(true); + condition_.notify_all(); +} + +void HpaeSignalProcessThread::Run() +{ + ScheduleThreadInServer(getpid(), gettid()); + while (running_.load() && streamManager_.lock() != nullptr) { + { + std::unique_lock lock(mutex_); + condition_.wait(lock, [this] { + return !running_.load() || streamManager_.lock()->IsRunning() || recvSignal_.load() || + streamManager_.lock()->IsMsgProcessing(); + }); + } + if (streamManager_.lock()) { + streamManager_.lock()->HandleMsg(); + streamManager_.lock()->Process(); + } + recvSignal_.store(false); + } + UnscheduleThreadInServer(getpid(), gettid()); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/manager/src/i_hpae_manager.cpp b/services/audio_engine/manager/src/i_hpae_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..853901df1cacec2c7bffa78f7159c88f452dca60 --- /dev/null +++ b/services/audio_engine/manager/src/i_hpae_manager.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hpae_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +std::shared_ptr IHpaeManager::GetHpaeManager() +{ + static auto hpaeManager = std::make_shared(); + return hpaeManager; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/manager/src/i_hpae_renderer_manager.cpp b/services/audio_engine/manager/src/i_hpae_renderer_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..226fd2815cac5c1961d9d9201fb78d0abe5664dd --- /dev/null +++ b/services/audio_engine/manager/src/i_hpae_renderer_manager.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "IHpaeRendererManager" +#endif +#include "i_hpae_renderer_manager.h" +#include "hpae_renderer_manager.h" +#include "hpae_offload_renderer_manager.h" +#include "hpae_inner_capturer_manager.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +static const std::string DEVICE_CLASS_OFFLOAD = "offload"; +static const std::string DEVICE_NAME_INNER_CAP = "InnerCapturerSink"; +static const std::string DEVICE_NAME_CAST_INNER_CAP = "RemoteCastInnerCapturer"; +std::shared_ptr IHpaeRendererManager::CreateRendererManager(HpaeSinkInfo &sinkInfo) +{ + if (sinkInfo.deviceClass == DEVICE_CLASS_OFFLOAD) { + return std::make_shared(sinkInfo); + } else if ((sinkInfo.deviceName.compare(0, DEVICE_NAME_INNER_CAP.length(), DEVICE_NAME_INNER_CAP) == 0) + || sinkInfo.deviceName == DEVICE_NAME_CAST_INNER_CAP) { + return std::make_shared(sinkInfo); + } + return std::make_shared(sinkInfo); +} + +void IHpaeRendererManager::UploadDumpSinkInfo(std::string& deviceName) +{ +#ifdef ENABLE_HIDUMP_DFX + std::string dumpStr; + dfxTree_.PrintTree(dumpStr); + TriggerCallback(DUMP_SINK_INFO, deviceName, dumpStr); +#endif +}; + +void IHpaeRendererManager::OnNotifyDfxNodeInfo(bool isConnect, uint32_t preNodeId, HpaeDfxNodeInfo &nodeInfo) +{ +#ifdef ENABLE_HIDUMP_DFX + AUDIO_INFO_LOG("%{public}s preNodeId %{public}u nodeName:%{public}s, NodeId: %{public}u", + isConnect ? "connect" : "disconnect", + preNodeId, + nodeInfo.nodeName.c_str(), + nodeInfo.nodeId); + if (isConnect) { + dfxTree_.Insert(preNodeId, nodeInfo); + } else { + dfxTree_.Remove(nodeInfo.nodeId); + } +#endif +}; + +uint32_t IHpaeRendererManager::OnGetNodeId() +{ + if (nodeIdCounter_.load() == std::numeric_limits::max()) { + nodeIdCounter_.store(MIN_START_NODE_ID); + } else { + nodeIdCounter_.fetch_add(1); + } + return nodeIdCounter_.load(); +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/node/include/hpae_audio_format_converter_node.h b/services/audio_engine/node/include/hpae_audio_format_converter_node.h new file mode 100644 index 0000000000000000000000000000000000000000..d7a01601933a552a905a172980de51e43b467b4c --- /dev/null +++ b/services/audio_engine/node/include/hpae_audio_format_converter_node.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_AUDIOFORMAT_CONVERTER_H +#define HPAE_AUDIOFORMAT_CONVERTER_H +#include "audio_stream_info.h" +#include "hpae_plugin_node.h" +#include "channel_converter.h" +#include "audio_proresampler.h" +#ifdef ENABLE_HOOK_PCM + #include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeAudioFormatConverterNode : public HpaePluginNode { +public: + HpaeAudioFormatConverterNode(HpaeNodeInfo preNodeInfo, HpaeNodeInfo nodeInfo); + void RegisterCallback(INodeFormatInfoCallback *callback); + void ConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) override; + void DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) override; +protected: + HpaePcmBuffer* SignalProcess(const std::vector& inputs) override; +private: + bool CheckUpdateInInfo(HpaePcmBuffer *input); + bool CheckUpdateOutInfo(); + int32_t ConverterProcess(float *srcData, float *dstData, float *tmpData, HpaePcmBuffer *input); + void CheckAndUpdateInfo(HpaePcmBuffer *input); + void UpdateTmpOutPcmBufferInfo(const PcmBufferInfo &outPcmBufferInfo); + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer converterOuput_; + HpaeNodeInfo preNodeInfo_; + std::unique_ptr resampler_ = nullptr; + ChannelConverter channelConverter_; + HpaePcmBuffer tmpOutBuf_; // cache between resample and converter + // if there is render effect, the effect node decides the output format of converter node + INodeFormatInfoCallback *nodeFormatInfoCallback_ = nullptr; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr inputPcmDumper_ = nullptr; + std::unique_ptr outputPcmDumper_ = nullptr; +#endif +}; + +} // HPAE +} // AudioStandard +} // OHOS +#endif diff --git a/services/audio_engine/node/include/hpae_capture_effect_node.h b/services/audio_engine/node/include/hpae_capture_effect_node.h new file mode 100644 index 0000000000000000000000000000000000000000..21bfba8cadbc9fd68888623061d1801c92788a28 --- /dev/null +++ b/services/audio_engine/node/include/hpae_capture_effect_node.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_CAPTURE_EFFECT_NODE_H +#define HPAE_CAPTURE_EFFECT_NODE_H +#include +#include "hpae_plugin_node.h" +#include "hpae_node.h" +#include "audio_effect.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +constexpr uint32_t SCENE_TYPE_OFFSET = 32; +constexpr uint32_t CAPTURER_ID_OFFSET = 16; +constexpr uint32_t BITLENGTH = 8; +constexpr uint32_t FRAME_LEN = 20; + +struct CaptureEffectAttr { + uint32_t micChannels; + uint32_t ecChannels; + uint32_t micRefChannels; +}; + +class HpaeCaptureEffectNode : public HpaePluginNode { +public: + HpaeCaptureEffectNode(HpaeNodeInfo &nodeInfo); + HpaeCaptureEffectNode(std::vector &nodeInfos); + virtual bool Reset() override; + void ConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) override; + void DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) override; + bool GetCapturerEffectConfig(HpaeNodeInfo& nodeInfo, HpaeSourceBufferType type = HPAE_SOURCE_BUFFER_TYPE_MIC); + int32_t CaptureEffectCreate(uint64_t sceneKeyCode, CaptureEffectAttr attr); + int32_t CaptureEffectRelease(uint64_t sceneKeyCode); +protected: + HpaePcmBuffer *SignalProcess(const std::vector &inputs) override; +private: + void SetCapturerEffectConfig(AudioBufferConfig micConfig, AudioBufferConfig ecConfig, + AudioBufferConfig micrefConfig); + + uint64_t sceneKeyCode_ = 0; + std::string sceneType_ = ""; + uint32_t micBufferLength_ = 0; + uint32_t ecBufferLength_ = 0; + uint32_t micrefBufferLength_ = 0; + std::vector cacheDataIn_; + std::vector cacheDataOut_; + std::unordered_map capturerEffectConfigMap_; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr outputPcmDumper_; +#endif +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_CAPTURE_EFFECT_NODE_H \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_gain_node.h b/services/audio_engine/node/include/hpae_gain_node.h new file mode 100644 index 0000000000000000000000000000000000000000..40e395884e06b2e0714a281d4ff927c19e079f6a --- /dev/null +++ b/services/audio_engine/node/include/hpae_gain_node.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_GAIN_NODE_H +#define HPAE_GAIN_NODE_H +#include "hpae_plugin_node.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#include "i_stream.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +enum class FadeOutState { + NO_FADEOUT, + DO_FADEOUT, + DONE_FADEOUT +}; +class HpaeGainNode : public HpaePluginNode { +public: + HpaeGainNode(HpaeNodeInfo &nodeInfo); + bool SetClientVolume(float gain); + float GetClientVolume(); + void SetFadeState(IOperation operation); +protected: + HpaePcmBuffer *SignalProcess(const std::vector &inputs) override; +private: + float preGain_ = 1.0f; + float curGain_ = 1.0f; + bool isGainChanged_ = false; + bool needGainState_ = true; + bool fadeInState_ = false; + FadeOutState fadeOutState_ = FadeOutState::NO_FADEOUT; + IOperation operation_; + void DoGain(HpaePcmBuffer *input, uint32_t frameLen, uint32_t channelCount); + void DoFading(HpaePcmBuffer *input); + void SlienceData(HpaePcmBuffer *pcmBuffer); + bool IsSilentData(HpaePcmBuffer *pcmBuffer); +#ifdef ENABLE_HOOK_PCM + std::unique_ptr inputPcmDumper_; + std::unique_ptr outputPcmDumper_; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_inner_cap_sink_node.h b/services/audio_engine/node/include/hpae_inner_cap_sink_node.h new file mode 100644 index 0000000000000000000000000000000000000000..f59d56207bc166437c3ae4fc77baca83b353beb1 --- /dev/null +++ b/services/audio_engine/node/include/hpae_inner_cap_sink_node.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_INNER_CAP_SINK_NODE_H +#define HPAE_INNER_CAP_SINK_NODE_H +#include +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "source/i_audio_capture_source.h" +#include "hpae_renderer_manager.h" +#include "hpae_source_input_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeInnerCapSinkNode : public OutputNode, public InputNode { +public: + HpaeInnerCapSinkNode(HpaeNodeInfo& nodeInfo); + virtual ~HpaeInnerCapSinkNode() {}; + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + + std::shared_ptr GetSharedInstance() override; + OutputPort* GetOutputPort() override; + void Connect(const std::shared_ptr>& preNode) override; + void DisConnect(const std::shared_ptr>& preNode) override; + size_t GetPreOutNum(); + size_t GetOutputPortNum(); + + int32_t InnerCapturerSinkInit(); + int32_t InnerCapturerSinkDeInit(); + int32_t InnerCapturerSinkFlush(); + int32_t InnerCapturerSinkPause(); + int32_t InnerCapturerSinkReset(); + int32_t InnerCapturerSinkResume(); + int32_t InnerCapturerSinkStart(); + int32_t InnerCapturerSinkStop(); + RendererState GetSinkState(); +private: + OutputPort outputStream_; + InputPort inputStream_; + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer silenceData_; + + HighResolutionTimer intervalTimer_; + RendererState state_ = RENDERER_NEW; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr outputPcmDumper_ = nullptr; +#endif +}; + +}}} + +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_mixer_node.h b/services/audio_engine/node/include/hpae_mixer_node.h new file mode 100644 index 0000000000000000000000000000000000000000..87c470e885ac0e3b741fa9c5b608dc87fe172869 --- /dev/null +++ b/services/audio_engine/node/include/hpae_mixer_node.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_MIXER_NODE_H +#define HPAE_MIXER_NODE_H +#include +#include +#include "hpae_node.h" +#include "hpae_plugin_node.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeMixerNode : public HpaePluginNode { +public: + HpaeMixerNode(HpaeNodeInfo &nodeInfo); + virtual bool Reset() override; +protected: + HpaePcmBuffer *SignalProcess(const std::vector &inputs) override; +private: + std::unordered_map streamVolumeMap_; + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer mixedOutput_; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr outputPcmDumper_ = nullptr;; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_node.h b/services/audio_engine/node/include/hpae_node.h new file mode 100644 index 0000000000000000000000000000000000000000..6d4ae57cd866b64e919c7088e3fc6ee4be7fad2b --- /dev/null +++ b/services/audio_engine/node/include/hpae_node.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_NODE_H +#define HPAE_NODE_H +#include +#include +#include +#include +#include +#include "hpae_pcm_buffer.h" +#include "hpae_define.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeNode : public std::enable_shared_from_this { +public: + HpaeNode(){}; + virtual ~HpaeNode(){}; + HpaeNode(HpaeNodeInfo& nodeInfo) : nodeInfo_(nodeInfo) + {} + virtual void DoProcess() = 0; + // for process node + virtual bool Reset() = 0; + virtual bool ResetAll() = 0; + + virtual HpaeNodeInfo& GetNodeInfo() + { + return nodeInfo_; + } + + virtual void SetNodeInfo(HpaeNodeInfo& nodeInfo) + { + nodeInfo_ = nodeInfo; + } + + virtual AudioSamplingRate GetSampleRate() + { + return nodeInfo_.samplingRate; + } + + virtual AudioSampleFormat GetBitWidth() + { + return nodeInfo_.format; + } + + virtual AudioChannel GetChannelCount() + { + return nodeInfo_.channels; + } + + virtual AudioChannelLayout GetChannelLayout() + { + return nodeInfo_.channelLayout; + } + + virtual size_t GetFrameLen() + { + return nodeInfo_.frameLen; + } + + virtual uint32_t GetNodeId() + { + return nodeInfo_.nodeId; + } + + virtual uint32_t GetSessionId() + { + return nodeInfo_.sessionId; + } + + virtual AudioStreamType GetStreamType() + { + return nodeInfo_.streamType; + } + + virtual HpaeProcessorType GetSceneType() + { + return nodeInfo_.sceneType; + } + + virtual std::string GetDeviceClass() + { + return nodeInfo_.deviceClass; + } + + virtual std::string GetDeviceNetId() + { + return nodeInfo_.deviceNetId; + } + + virtual std::string GetNodeName() + { + return nodeInfo_.nodeName; + } + + virtual std::weak_ptr GetNodeStatusCallback() + { + return nodeInfo_.statusCallback; + } +private: + HpaeNodeInfo nodeInfo_; +}; + +template +class InputPort; + +template +class OutputPort { +public: + explicit OutputPort(HpaeNode *node) : hpaeNode_(node) + {} + void WriteDataToOutput(T data); + OutputPort(const OutputPort &that) = delete; + T PullOutputData(); + void AddInput(InputPort *input); + bool RemoveInput(InputPort *input); + size_t GetInputNum() const; +private: + std::set*> inputPortSet_; + std::vector outputData_; + HpaeNode *hpaeNode_; +}; + +template +class OutputNode : virtual public HpaeNode { +public: + virtual ~OutputNode() + {} + virtual std::shared_ptr GetSharedInstance() = 0; + virtual std::shared_ptr GetSharedInstance(HpaeNodeInfo &nodeInfo) { return nullptr; } + virtual OutputPort* GetOutputPort() = 0; + virtual OutputPort* GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect = false) { return nullptr; } + virtual HpaeSourceBufferType GetOutputPortBufferType(HpaeNodeInfo &nodeInfo) + { return HPAE_SOURCE_BUFFER_TYPE_DEFAULT; } +}; + +template +class InputNode : virtual public HpaeNode { +public: + virtual ~InputNode() + {} + virtual void Connect(const std::shared_ptr> &preNode) = 0; + virtual void ConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) {} + virtual void DisConnect(const std::shared_ptr> &preNode) = 0; + virtual void DisConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) {} +}; + +template +class InputPort { +public: + InputPort() + {} + ~InputPort(); + std::vector& ReadPreOutputData(); + + void Connect(const std::shared_ptr& node, OutputPort* output); + + void DisConnect(OutputPort* output); + + size_t GetPreOutputNum() const; + + const std::unordered_map *, std::shared_ptr>& GetPreOuputMap(); + + InputPort(const InputPort &that) = delete; + + void AddPreOutput(const std::shared_ptr &node, OutputPort* output); + void RemovePreOutput(OutputPort* output); +private: + std::unordered_map*, std::shared_ptr> outputPorts_; + std::vector inputData_; +}; + +template +InputPort::~InputPort() +{ + for (auto &o : outputPorts_) { + o.first->RemoveInput(this); + } +} + +template +std::vector& InputPort::ReadPreOutputData() +{ + inputData_.clear(); + for (auto &o : outputPorts_) { + T pcmData = o.first->PullOutputData(); + if (pcmData != nullptr) { + inputData_.emplace_back(std::move(pcmData)); + } + } + return inputData_; +} + +template +void InputPort::Connect(const std::shared_ptr &node, OutputPort* output) +{ + output->AddInput(this); + AddPreOutput(node, output); +} + +template +void InputPort::DisConnect(OutputPort* output) +{ + output->RemoveInput(this); + RemovePreOutput(output); +} + +template +size_t InputPort::GetPreOutputNum() const +{ + return outputPorts_.size(); +} + +template +const std::unordered_map *, std::shared_ptr>& InputPort::GetPreOuputMap() +{ + return outputPorts_; +} + +template +void InputPort::AddPreOutput(const std::shared_ptr &node, OutputPort *output) +{ + outputPorts_[output] = node; +} + +template +void InputPort::RemovePreOutput(OutputPort *output) +{ + outputPorts_.erase(output); +} + +template +T OutputPort::PullOutputData() +{ + if (outputData_.empty()) { + hpaeNode_->DoProcess(); + } + if (!outputData_.empty()) { + T retValue = std::move(outputData_.back()); + outputData_.pop_back(); + return retValue; + } else { + return nullptr; + } +} + +template +void OutputPort::WriteDataToOutput(T data) +{ + outputData_.clear(); + outputData_.emplace_back(std::move(data)); + + for (size_t i = 1; i < inputPortSet_.size(); i++) { + outputData_.push_back(outputData_[0]); + } +} + +template +void OutputPort::AddInput(InputPort *input) +{ + inputPortSet_.insert(input); +} +template +size_t OutputPort::GetInputNum() const +{ + return inputPortSet_.size(); +} +template +bool OutputPort::RemoveInput(InputPort *input) +{ + auto it = inputPortSet_.find(input); + if (it == inputPortSet_.end()) { + return false; + } + + inputPortSet_.erase(it); + return true; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_node_common.h b/services/audio_engine/node/include/hpae_node_common.h new file mode 100644 index 0000000000000000000000000000000000000000..903b0794e207a5838c9c4733f22941321df49f1b --- /dev/null +++ b/services/audio_engine/node/include/hpae_node_common.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_NODE_COMMON_H +#define HPAE_NODE_COMMON_H +#include "hpae_define.h" +#include "audio_effect.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +bool CheckHpaeNodeInfoIsSame(HpaeNodeInfo &preNodeInfo, HpaeNodeInfo &curNodeInfo); +HpaeProcessorType TransStreamTypeToSceneType(AudioStreamType streamType); +HpaeProcessorType TransSourceTypeToSceneType(SourceType sourceType); +bool CheckSceneTypeNeedEc(HpaeProcessorType processorType); +bool CheckSceneTypeNeedMicRef(HpaeProcessorType processorType); +std::string TransHpaeResampleNodeInfoToStringKey(HpaeNodeInfo& nodeInfo); +AudioEnhanceScene TransProcessType2EnhanceScene(const HpaeProcessorType &processorType); +std::string TransProcessorTypeToSceneType(HpaeProcessorType processorType); +uint64_t ConvertDatalenToUs(size_t bufferSize, const HpaeNodeInfo &nodeInfo); +size_t ConvertUsToFrameCount(uint64_t usTime, const HpaeNodeInfo &nodeInfo); +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_offload_sinkoutput_node.h b/services/audio_engine/node/include/hpae_offload_sinkoutput_node.h new file mode 100644 index 0000000000000000000000000000000000000000..de7678c4134c3ae34778e278823a6b0cdaef3b75 --- /dev/null +++ b/services/audio_engine/node/include/hpae_offload_sinkoutput_node.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_OFFLOAD_SINK_OUTPUT_NODE_H +#define HPAE_OFFLOAD_SINK_OUTPUT_NODE_H +#include +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_info.h" +#include "sink/i_audio_render_sink.h" +#include "common/hdi_adapter_info.h" +#include "manager/hdi_adapter_manager.h" +#ifdef ENABLE_HOOK_PCM +#include "high_resolution_timer.h" +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +typedef void(*AppCallbackFunc)(void* pHndl); +class HpaeOffloadSinkOutputNode : public InputNode { +public: + HpaeOffloadSinkOutputNode(HpaeNodeInfo& nodeInfo); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + void Connect(const std::shared_ptr>& preNode) override; + void DisConnect(const std::shared_ptr>& preNode) override; + int32_t GetRenderSinkInstance(const std::string &deviceClass, const std::string &deviceNetworkId); + int32_t RenderSinkInit(IAudioSinkAttr& attr); + int32_t RenderSinkDeInit(); + int32_t RenderSinkFlush(); + int32_t RenderSinkStart(); + int32_t RenderSinkStop(); + size_t GetPreOutNum(); + RendererState GetSinkState(void); + const char* GetRenderFrameData(void); + // need flush hdi cache and rewind + void StopStream(); + // flush need clear sinkoutputjnode cache + void FlushStream(); + // set offload policy state + void SetPolicyState(int32_t policyState); + // get offload latency for sinkinputnode, maybe extend to all node + uint64_t GetLatency(); + // set timeout to suspend render and stop hdi + int32_t SetTimeoutStopThd(uint32_t timeoutThdMs); +private: + // lock/unlock running lock + void RunningLock(bool isLock); + // Set hdi buffer size, change after render frame success + void SetBufferSizeWhileRenderFrame(); + int32_t ProcessRenderFrame(); + // get presentation position from hdi, only trigger in offloadcallback + int32_t UpdatePresentationPosition(); + // return hdi cache len in us, cal by hdiPos_ + uint64_t CalcOffloadCacheLenInHdi(); + // set hdi volume when first write + void OffloadSetHdiVolume(); + // reset hdipos and firstWriteHdi + void OffloadReset(); + // register callback to hdi + void RegOffloadCallback(); + // offload callback reg to hdi + void OffloadCallback(const RenderCallbackType type); + // check when stop hdi, if need suspend + bool CheckIfSuspend(); + + InputPort inputStream_; + std::vector renderFrameData_; + std::vector interleveData_; + std::shared_ptr audioRendererSink_ = nullptr; + uint32_t renderId_ = HDI_INVALID_ID; + IAudioSinkAttr sinkOutAttr_; + RendererState state_ = RENDERER_NEW; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer intervalTimer_; + std::unique_ptr outputPcmDumper_ = nullptr; +#endif + + AudioOffloadType hdiPolicyState_ = OFFLOAD_ACTIVE_FOREGROUND; + struct OffloadPolicyTask { + bool flag = false; // indicate if task exsit + AudioOffloadType state; + TimePoint time; + } setPolicyStateTask_; + + bool firstWriteHdi_ = true; + uint64_t writePos_ = 0; + int32_t setHdiBufferSizeNum_ = 0; + + std::atomic isHdiFull_ = false; + + uint32_t frameLenMs_ = 0; + uint32_t timeoutThdFrames_ = 0; + // first stand for pos(in us), second stand for time + std::pair hdiPos_; +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_output_cluster.h b/services/audio_engine/node/include/hpae_output_cluster.h new file mode 100644 index 0000000000000000000000000000000000000000..eff788612daf826a7e8eab7033ac4eab957a160c --- /dev/null +++ b/services/audio_engine/node/include/hpae_output_cluster.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_OUTPUT_CLUSTER_H +#define HPAE_OUTPUT_CLUSTER_H +#include "hpae_mixer_node.h" +#include "hpae_sink_output_node.h" +#include "hpae_audio_format_converter_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t TIME_OUT_STOP_THD_DEFAULT_FRAME = 150; +constexpr uint32_t FRAME_LEN_MS_DEFAULT_MS = 20; +class HpaeOutputCluster : public InputNode { +public: + HpaeOutputCluster(HpaeNodeInfo &nodeInfo); + virtual ~HpaeOutputCluster(); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + void Connect(const std::shared_ptr> &preNode) override; + void DisConnect(const std::shared_ptr> &preNode) override; + int32_t GetConverterNodeCount(); + int32_t GetPreOutNum(); + int32_t GetInstance(std::string deviceClass, std::string deviceNetId); + int32_t Init(IAudioSinkAttr &attr); + int32_t DeInit(); + int32_t Flush(void); + int32_t Pause(void); + int32_t ResetRender(void); + int32_t Resume(void); + int32_t Start(void); + int32_t Stop(void); + int32_t SetTimeoutStopThd(uint32_t timeoutThdMs); + const char *GetFrameData(void); + RendererState GetState(void); + bool IsProcessClusterConnected(HpaeProcessorType sceneType); +private: + std::shared_ptr mixerNode_ = nullptr; + std::shared_ptr hpaeSinkOutputNode_ = nullptr; + std::unordered_map> sceneConverterMap_; + uint32_t timeoutThdFrames_ = TIME_OUT_STOP_THD_DEFAULT_FRAME; + uint32_t timeoutStopCount_ = 0; + uint32_t frameLenMs_ = FRAME_LEN_MS_DEFAULT_MS; + std::set connectedProcessCluster_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_plugin_node.h b/services/audio_engine/node/include/hpae_plugin_node.h new file mode 100644 index 0000000000000000000000000000000000000000..26f727fd1ed472d36613be0282a3e72cc3bb9f97 --- /dev/null +++ b/services/audio_engine/node/include/hpae_plugin_node.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_PLUGIN_NODE_H +#define HPAE_PLUGIN_NODE_H +#include +#include "hpae_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaePluginNode : public OutputNode, public InputNode { +public: + HpaePluginNode(HpaeNodeInfo& nodeInfo); + virtual ~HpaePluginNode() {}; + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + + std::shared_ptr GetSharedInstance() override; + OutputPort* GetOutputPort() override; + void Connect(const std::shared_ptr>& preNode) override; + void DisConnect(const std::shared_ptr>& preNode) override; + virtual size_t GetPreOutNum(); + virtual size_t GetOutputPortNum(); + virtual int32_t EnableProcess(bool enable); + virtual bool IsEnableProcess(); + HpaePluginNode(const HpaePluginNode& others) = delete; +private: + OutputPort outputStream_; + bool enableProcess_; + PcmBufferInfo pcmBufferInfo_; +protected: + virtual HpaePcmBuffer* SignalProcess(const std::vector& inputs) = 0; + InputPort inputStream_; + HpaePcmBuffer silenceData_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_process_cluster.h b/services/audio_engine/node/include/hpae_process_cluster.h new file mode 100644 index 0000000000000000000000000000000000000000..87d46c1de7602380952c781f350f22d0223e06a0 --- /dev/null +++ b/services/audio_engine/node/include/hpae_process_cluster.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_PROCESS_CLUSTER_H +#define HPAE_PROCESS_CLUSTER_H +#include "hpae_mixer_node.h" +#include "hpae_audio_format_converter_node.h" +#include "hpae_gain_node.h" +#include "hpae_render_effect_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeProcessCluster : public OutputNode, public InputNode, + public INodeFormatInfoCallback { +public: + HpaeProcessCluster(HpaeNodeInfo &nodeInfo, HpaeSinkInfo &sinkInfo); + virtual ~HpaeProcessCluster(); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + std::shared_ptr GetSharedInstance() override; + OutputPort *GetOutputPort() override; + void Connect(const std::shared_ptr> &preNode) override; + void DisConnect(const std::shared_ptr> &preNode) override; + int32_t GetGainNodeCount(); + int32_t GetConverterNodeCount(); + int32_t GetPreOutNum(); + int32_t AudioRendererCreate(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererStart(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererStop(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererRelease(HpaeNodeInfo &nodeInfo); + int32_t GetEffectNodeInputChannelInfo(uint32_t &channels, uint64_t &channelLayout) override; + std::shared_ptr GetGainNodeById(uint32_t id) const; + std::shared_ptr GetConverterNodeById(uint32_t id) const; +private: + void ConnectMixerNode(); + std::shared_ptr mixerNode_; + std::shared_ptr renderEffectNode_ = nullptr; + std::unordered_map> idConverterMap_; + std::unordered_map> idGainMap_; + HpaeSinkInfo sinkInfo_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif // HPAE_PROCESS_CLUSTER_H \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_render_effect_node.h b/services/audio_engine/node/include/hpae_render_effect_node.h new file mode 100644 index 0000000000000000000000000000000000000000..6c88b490aa2cdc743a2956c36f5fdfc0e703e1f6 --- /dev/null +++ b/services/audio_engine/node/include/hpae_render_effect_node.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_RENDER_EFFECT_NODE_H +#define HPAE_RENDER_EFFECT_NODE_H + +#include "hpae_plugin_node.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +enum ModifyAudioEffectChainInfoReason { + ADD_AUDIO_EFFECT_CHAIN_INFO = 0, + REMOVE_AUDIO_EFFECT_CHAIN_INFO = 1, +}; + +class HpaeRenderEffectNode : public HpaePluginNode { +public: + HpaeRenderEffectNode(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererCreate(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererStart(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererStop(HpaeNodeInfo &nodeInfo); + int32_t AudioRendererRelease(HpaeNodeInfo &nodeInfo); + int32_t GetExpectedInputChannelInfo(uint32_t &channels, uint64_t &channelLayout); +protected: + HpaePcmBuffer* SignalProcess(const std::vector &inputs) override; +private: + void ReconfigOutputBuffer(); + int32_t CreateAudioEffectChain(HpaeNodeInfo &nodeInfo); + int32_t ReleaseAudioEffectChain(HpaeNodeInfo &nodeInfo); + void ModifyAudioEffectChainInfo(HpaeNodeInfo &nodeInfo, ModifyAudioEffectChainInfoReason reason); + void UpdateAudioEffectChainInfo(HpaeNodeInfo &nodeInfo); + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer effectOutput_; + HpaeNodeInfo nodeInfo_; + std::string sceneType_ = "EFFECT_NONE"; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr inputPcmDumper_; + std::unique_ptr outputPcmDumper_; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif // HPAE_RENDER_EFFECT_NODE_H \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_resample_node.h b/services/audio_engine/node/include/hpae_resample_node.h new file mode 100644 index 0000000000000000000000000000000000000000..8c6b45cb991640e060aa4564ffed846d0cd0d8c7 --- /dev/null +++ b/services/audio_engine/node/include/hpae_resample_node.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_RESAMPLE_NODE_H +#define HPAE_RESAMPLE_NODE_H +#include "audio_proresampler.h" +#include "hpae_plugin_node.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +enum class ResamplerType { + PRORESAMPLER +}; + +class HpaeResampleNode : public HpaePluginNode { +public: + HpaeResampleNode(HpaeNodeInfo& nodeInfo, HpaeNodeInfo& preNodeInfo, ResamplerType type); + HpaeResampleNode(HpaeNodeInfo& nodeInfo, HpaeNodeInfo& preNodeInfo); + ~HpaeResampleNode() = default; + virtual bool Reset() override; + void ConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) override; + void DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) override; +protected: + HpaePcmBuffer* SignalProcess(const std::vector& inputs) override; +private: + void ResampleProcess(float *srcData, uint32_t inputFrameLen, float *dstData, uint32_t outputFrameLen); + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer resampleOuput_; + HpaeNodeInfo preNodeInfo_; + std::vector tempOuput_; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr inputPcmDumper_ = nullptr; + std::unique_ptr outputPcmDumper_ = nullptr; +#endif + std::unique_ptr resampler_ = nullptr; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_sink_input_node.h b/services/audio_engine/node/include/hpae_sink_input_node.h new file mode 100644 index 0000000000000000000000000000000000000000..8bfb6719d691b8e772503c8576c965624c1d9a4c --- /dev/null +++ b/services/audio_engine/node/include/hpae_sink_input_node.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_SINK_INPUT_NODE_H +#define HPAE_SINK_INPUT_NODE_H +#include +#include +#include "hpae_msg_channel.h" +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_info.h" +#include "i_renderer_stream.h" +#include "linear_pos_time_model.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +typedef void (*AppCallbackFunc)(void *pHndl); + +class HpaeSinkInputNode : public OutputNode { +public: + HpaeSinkInputNode(HpaeNodeInfo &nodeInfo); + ~HpaeSinkInputNode(); + virtual void DoProcess() override; + virtual bool Reset() override; // no implement, virtual class + virtual bool ResetAll() override; // no implement, virtual class + std::shared_ptr GetSharedInstance() override; + OutputPort *GetOutputPort() override; + bool RegisterWriteCallback(const std::weak_ptr &callback); + void Flush(); + bool Drain(); + int32_t SetState(RendererState renderState); + RendererState GetState(); + uint64_t GetFramesWritten(); + + int32_t GetCurrentPosition(uint64_t &framePosition, uint64_t ×tamp); + int32_t RewindHistoryBuffer(uint64_t rewindTime); + +private: + int32_t GetDataFromSharedBuffer(); + void CheckAndDestoryHistoryBuffer(); + bool GetAudioTime(uint64_t &framePos, int64_t &sec, int64_t &nanoSec); + std::string GetTraceInfo(); + std::weak_ptr writeCallback_; + AudioCallBackStreamInfo streamInfo_; + PcmBufferInfo pcmBufferInfo_; + HpaePcmBuffer inputAudioBuffer_; + OutputPort outputStream_; + std::vector interleveData_; + std::atomic framesWritten_; + uint64_t totalFrames_; + std::unique_ptr handleTimeModel_; + bool isDrain_ = false; + RendererState state_ = RENDERER_NEW; + + std::unique_ptr historyBuffer_; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr inputPcmDumper_ = nullptr; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_sink_output_node.h b/services/audio_engine/node/include/hpae_sink_output_node.h new file mode 100644 index 0000000000000000000000000000000000000000..205cbc8a35c88ca503b8b7569789257ece41f436 --- /dev/null +++ b/services/audio_engine/node/include/hpae_sink_output_node.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_SINK_OUTPUT_NODE_H +#define HPAE_SINK_OUTPUT_NODE_H +#include +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_info.h" +#include "sink/i_audio_render_sink.h" +#include "common/hdi_adapter_info.h" +#include "manager/hdi_adapter_manager.h" +#include "high_resolution_timer.h" +#ifdef ENABLE_HOOK_PCM +#include "high_resolution_timer.h" +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +typedef void (*AppCallbackFunc)(void *pHndl); + +class HpaeSinkOutputNode : public InputNode { +public: + HpaeSinkOutputNode(HpaeNodeInfo &nodeInfo); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + void Connect(const std::shared_ptr> &preNode) override; + void DisConnect(const std::shared_ptr> &preNode) override; + int32_t GetRenderSinkInstance(std::string deviceClass, std::string deviceNetId); + int32_t RenderSinkInit(IAudioSinkAttr &attr); + int32_t RenderSinkDeInit(); + int32_t RenderSinkFlush(void); + int32_t RenderSinkPause(void); + int32_t RenderSinkReset(void); + int32_t RenderSinkResume(void); + int32_t RenderSinkStart(void); + int32_t RenderSinkStop(void); + size_t GetPreOutNum(); + // for ut test + const char *GetRenderFrameData(void); + RendererState GetSinkState(void); + +private: + void HandleRemoteTiming(); + InputPort inputStream_; + std::vector renderFrameData_; + std::vector interleveData_; + std::shared_ptr audioRendererSink_ = nullptr; + uint32_t renderId_ = HDI_INVALID_ID; + IAudioSinkAttr sinkOutAttr_; + RendererState state_ = RENDERER_NEW; + HighResolutionTimer remoteTimer_; + TimePoint remoteTimePoint_; + std::chrono::milliseconds remoteSleepTime_ = std::chrono::milliseconds(0); +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer intervalTimer_; + std::unique_ptr outputPcmDumper_ = nullptr; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_source_input_cluster.h b/services/audio_engine/node/include/hpae_source_input_cluster.h new file mode 100644 index 0000000000000000000000000000000000000000..1eb5a64c7aaccdd363cb0e8e1ccd9ea1a151a481 --- /dev/null +++ b/services/audio_engine/node/include/hpae_source_input_cluster.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_SOURCE_INPUT_CLUSTER_H +#define HPAE_SOURCE_INPUT_CLUSTER_H + +#include "hpae_source_input_node.h" +#include "hpae_audio_format_converter_node.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeSourceInputCluster : public OutputNode { +public: + HpaeSourceInputCluster(HpaeNodeInfo &nodeInfo); + HpaeSourceInputCluster(std::vector &nodeInfos); + virtual ~HpaeSourceInputCluster(); + virtual void DoProcess() final; + virtual bool Reset() final; + virtual bool ResetAll() final; + std::shared_ptr GetSharedInstance() final; + std::shared_ptr GetSharedInstance(HpaeNodeInfo &nodeInfo) final; + OutputPort *GetOutputPort() final; + OutputPort *GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect = false) final; + int32_t GetCapturerSourceInstance(const std::string &deviceClass, const std::string &deviceNetId, + const SourceType &sourceType, const std::string &sourceName); + int32_t CapturerSourceInit(IAudioSourceAttr &attr); + int32_t CapturerSourceDeInit(); + int32_t CapturerSourceFlush(void); + int32_t CapturerSourcePause(void); + int32_t CapturerSourceReset(void); + int32_t CapturerSourceResume(void); + int32_t CapturerSourceStart(void); + int32_t CapturerSourceStop(void); + CapturerState GetSourceState(void); + size_t GetOutputPortNum(); + size_t GetOutputPortNum(HpaeNodeInfo &nodeInfo); + HpaeSourceInputNodeType GetSourceInputNodeType(); + void SetSourceInputNodeType(HpaeSourceInputNodeType type); + + // for test + uint32_t GetConverterNodeCount(); + uint32_t GetSourceInputNodeUseCount(); +private: + std::shared_ptr sourceInputNode_; + std::unordered_map> fmtConverterNodeMap_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_source_input_node.h b/services/audio_engine/node/include/hpae_source_input_node.h new file mode 100644 index 0000000000000000000000000000000000000000..047f2071493d8f516b4aa674b934da8dd52b3286 --- /dev/null +++ b/services/audio_engine/node/include/hpae_source_input_node.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_SOURCE_INTPUT_NODE_H +#define HPAE_SOURCE_INTPUT_NODE_H +#include +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "source/i_audio_capture_source.h" +#include "common/hdi_adapter_type.h" +#include "common/hdi_adapter_info.h" +#include "manager/hdi_adapter_manager.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeSourceInputNode : public OutputNode { +public: + HpaeSourceInputNode(HpaeNodeInfo &nodeInfo); + HpaeSourceInputNode(std::vector &nodeInfos); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + std::shared_ptr GetSharedInstance() final; + + OutputPort *GetOutputPort() final; + OutputPort *GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect = false) final; + HpaeSourceBufferType GetOutputPortBufferType(HpaeNodeInfo &nodeInfo) final; + int32_t GetCapturerSourceInstance(const std::string &deviceClass, const std::string &deviceNetId, + const SourceType &sourceType, const std::string &sourceName); + int32_t CapturerSourceInit(IAudioSourceAttr &attr); + int32_t CapturerSourceDeInit(); + int32_t CapturerSourceFlush(void); + int32_t CapturerSourcePause(void); + int32_t CapturerSourceReset(void); + int32_t CapturerSourceResume(void); + int32_t CapturerSourceStart(void); + int32_t CapturerSourceStop(void); + CapturerState GetSourceState(void); + int32_t WriteCapturerData(char *data, int32_t dataSize); + size_t GetOutputPortNum(); + size_t GetOutputPortNum(HpaeNodeInfo &nodeInfo); + HpaeSourceInputNodeType GetSourceInputNodeType(); + void SetSourceInputNodeType(HpaeSourceInputNodeType type); +private: + int32_t GetCapturerSourceAdapter( + const std::string &deviceClass, const SourceType &sourceType, const std::string &info); + +private: + std::shared_ptr audioCapturerSource_ = nullptr; + uint32_t captureId_ = HDI_INVALID_ID; + IAudioSourceAttr audioSourceAttr_; + std::string defaultSinkName_; + std::string defaultSourceName_; + CapturerState state_ = CAPTURER_NEW; + HpaeSourceInputNodeType sourceInputNodeType_; + + std::unordered_map> outputStreamMap_; // output port + std::unordered_map nodeInfoMap_; // nodeInfo, portInfo + std::unordered_map pcmBufferInfoMap_; // bufferInfo + std::unordered_map inputAudioBufferMap_; // output buffer + std::unordered_map frameByteSizeMap_; + std::unordered_map> capturerFrameDataMap_; // input buffer + std::unordered_map fdescMap_; // CaptureframeWithEc argument struct + +#ifdef ENABLE_HOOK_PCM + std::unordered_map> inputPcmDumperMap_; +#endif +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_source_output_node.h b/services/audio_engine/node/include/hpae_source_output_node.h new file mode 100644 index 0000000000000000000000000000000000000000..67cac88245f0adad13266973d1509292fe5d1962 --- /dev/null +++ b/services/audio_engine/node/include/hpae_source_output_node.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_SOURCE_OUTPUT_NODE_H +#define HPAE_SOURCE_OUTPUT_NODE_H +#include +#include "hpae_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_info.h" +#include "i_capturer_stream.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class HpaeSourceOutputNode : public InputNode { +public: + HpaeSourceOutputNode(HpaeNodeInfo &nodeInfo); + virtual void DoProcess() final; + virtual bool Reset() final; + bool ResetAll() final; + void Connect(const std::shared_ptr> &preNode) override; + void ConnectWithInfo(const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) override; + void DisConnect(const std::shared_ptr> &preNode) override; + void DisConnectWithInfo( + const std::shared_ptr> &preNode, HpaeNodeInfo &nodeInfo) override; + bool RegisterReadCallback(const std::weak_ptr &callback); + +private: + InputPort inputStream_; + std::weak_ptr readCallback_; + std::vector sourceOuputData_; + std::vector interleveData_; +#ifdef ENABLE_HOOK_PCM + std::unique_ptr outputPcmDumper_ = nullptr; +#endif +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/node/include/hpae_source_process_cluster.h b/services/audio_engine/node/include/hpae_source_process_cluster.h new file mode 100644 index 0000000000000000000000000000000000000000..d478e397db3407b082c5894eab23359ce6fd2e6d --- /dev/null +++ b/services/audio_engine/node/include/hpae_source_process_cluster.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HPAE_SOURCE_PROCESS_CLUSTER_H +#define HPAE_SOURCE_PROCESS_CLUSTER_H +#include "hpae_capture_effect_node.h" +#include "hpae_audio_format_converter_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaeSourceProcessCluster : public OutputNode, public InputNode { +public: + HpaeSourceProcessCluster(HpaeNodeInfo &nodeInfo); + virtual ~HpaeSourceProcessCluster(); + virtual void DoProcess() override; + virtual bool Reset() override; + virtual bool ResetAll() override; + std::shared_ptr GetSharedInstance() override; + std::shared_ptr GetSharedInstance(HpaeNodeInfo &nodeInfo) override; + OutputPort *GetOutputPort() override; + OutputPort *GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect = false) override; + + // HpaeNodeInfo& GetOutputNodeInfo() override; + void Connect(const std::shared_ptr>& preNode) override; + void DisConnect(const std::shared_ptr>& preNode) override; + void ConnectWithInfo(const std::shared_ptr>& preNode, HpaeNodeInfo &nodeInfo) override; + void DisConnectWithInfo(const std::shared_ptr>& preNode, + HpaeNodeInfo &nodeInfo) override; + bool GetCapturerEffectConfig(HpaeNodeInfo& nodeInfo, HpaeSourceBufferType type = HPAE_SOURCE_BUFFER_TYPE_MIC); + + size_t GetOutputPortNum(); + int32_t CaptureEffectCreate(uint64_t sceneKeyCode, CaptureEffectAttr attr); + int32_t CaptureEffectRelease(uint64_t sceneKeyCode); +private: + std::shared_ptr captureEffectNode_; + std::unordered_map> fmtConverterNodeMap_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_audio_format_converter_node.cpp b/services/audio_engine/node/src/hpae_audio_format_converter_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..076f771e7439e2bfa11a88799555f4ff995534f4 --- /dev/null +++ b/services/audio_engine/node/src/hpae_audio_format_converter_node.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeAudioFormatConverterNode" +#endif +#include "hpae_audio_format_converter_node.h" +#include "audio_engine_log.h" +#include "audio_utils.h" +#include "cinttypes" + +static constexpr uint32_t DEFAULT_EFFECT_RATE = 48000; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr int REASAMPLE_QUAILTY = 5; + +HpaeAudioFormatConverterNode::HpaeAudioFormatConverterNode(HpaeNodeInfo preNodeInfo, HpaeNodeInfo nodeInfo) + : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout), + converterOuput_(pcmBufferInfo_), preNodeInfo_(preNodeInfo), tmpOutBuf_(pcmBufferInfo_) +{ + UpdateTmpOutPcmBufferInfo(pcmBufferInfo_); + // use ProResamppler as default + resampler_ = std::make_unique(preNodeInfo_.samplingRate, nodeInfo.samplingRate, + std::min(preNodeInfo_.channels, nodeInfo.channels), REASAMPLE_QUAILTY); + + AudioChannelInfo inChannelInfo = { + .channelLayout = preNodeInfo.channelLayout, + .numChannels = preNodeInfo.channels, + }; + AudioChannelInfo outChannelInfo = { + .channelLayout = nodeInfo.channelLayout, + .numChannels = nodeInfo.channels, + }; + + // for now, work at float32le by default + channelConverter_.SetParam(inChannelInfo, outChannelInfo, SAMPLE_F32LE, true); + AUDIO_INFO_LOG("node id %{public}d, sessionid %{public}d, " + "input: bitformat %{public}d, sample rate %{public}d, channels %{public}d," + "channelLayout %{public}" PRIu64 ", output: bitformat %{public}d, sample rate %{public}d," + "channels %{public}d, channelLayout %{public}" PRIu64 "", GetNodeId(), GetSessionId(), + preNodeInfo.format, preNodeInfo.samplingRate, inChannelInfo.numChannels, + inChannelInfo.channelLayout, nodeInfo.format, nodeInfo.samplingRate, + outChannelInfo.numChannels, outChannelInfo.channelLayout); +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique( + "HpaeConverterNodeInput_id_" + std::to_string(GetSessionId()) + + "_ch_" + std::to_string(preNodeInfo_.channels) + "_rate_" + + std::to_string(preNodeInfo_.samplingRate) + "_" + GetTime() + ".pcm"); + outputPcmDumper_ = std::make_unique( + "HpaeConverterNodeOutput_id_" + std::to_string(GetSessionId()) + + "_ch_" + std::to_string(GetChannelCount()) + "_rate_" + + std::to_string(GetSampleRate()) + "_" + GetTime() + ".pcm"); +#endif +} + +void HpaeAudioFormatConverterNode::RegisterCallback(INodeFormatInfoCallback *callback) +{ + nodeFormatInfoCallback_ = callback; +} + +HpaePcmBuffer *HpaeAudioFormatConverterNode::SignalProcess(const std::vector &inputs) +{ + auto rate = "rate[" + std::to_string(GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(GetFrameLen()) + "]"; + Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeAudioFormatConverterNode::SignalProcess " + + rate + ch + len); + if (inputs.empty()) { + AUDIO_WARNING_LOG("HpaeConverterNode inputs size is empty, SessionId:%{public}d", GetSessionId()); + return nullptr; + } + if (inputs.size() != 1) { + AUDIO_WARNING_LOG("error inputs size is not eqaul to 1, SessionId:%{public}d", GetSessionId()); + } + float *srcData = (*(inputs[0])).GetPcmDataBuffer(); +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumper_ != nullptr) { + inputPcmDumper_->Dump((int8_t *)(srcData), + inputs[0]->GetFrameLen() * inputs[0]->GetChannelCount() * sizeof(float)); + } +#endif + converterOuput_.Reset(); + tmpOutBuf_.Reset(); + + CheckAndUpdateInfo(inputs[0]); + + float *dstData = converterOuput_.GetPcmDataBuffer(); + float *tmpData = tmpOutBuf_.GetPcmDataBuffer(); + + if (resampler_ == nullptr) { + return &silenceData_; + } + int32_t ret = ConverterProcess(srcData, dstData, tmpData, inputs[0]); + if (ret != EOK) { + AUDIO_ERR_LOG("NodeId %{public}d, sessionId %{public}d, Format Converter fail to process!", + GetNodeId(), GetSessionId()); + return &silenceData_; + } + +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_ != nullptr) { + outputPcmDumper_->Dump((int8_t *)dstData, + converterOuput_.GetFrameLen() * sizeof(float) * channelConverter_.GetOutChannelInfo().numChannels); + } +#endif + AUDIO_DEBUG_LOG("NodeId %{public}d, buffer valid %{public}d", GetSessionId(), converterOuput_.IsValid()); + return &converterOuput_; +} + +int32_t HpaeAudioFormatConverterNode::ConverterProcess(float *srcData, float *dstData, float *tmpData, + HpaePcmBuffer *input) +{ + AudioChannelInfo inChannelInfo = channelConverter_.GetInChannelInfo(); + AudioChannelInfo outChannelInfo = channelConverter_.GetOutChannelInfo(); + uint32_t inRate = resampler_->GetInRate(); + uint32_t outRate = resampler_->GetOutRate(); + + uint32_t inputFrameLen = preNodeInfo_.frameLen; + uint32_t outputFrameLen = converterOuput_.GetFrameLen(); + uint32_t inputFrameBytes = inputFrameLen * inChannelInfo.numChannels * sizeof(float); + uint32_t outputFrameBytes = outputFrameLen * outChannelInfo.numChannels * sizeof(float); + int32_t ret = EOK; + + if ((inChannelInfo.numChannels == outChannelInfo.numChannels) && (inRate == outRate)) { + ret = memcpy_s(dstData, outputFrameBytes, srcData, inputFrameBytes); + } else if (inChannelInfo.numChannels == outChannelInfo.numChannels) { + ret = resampler_->Process(srcData, &inputFrameLen, dstData, &outputFrameLen); + } else if (inRate == outRate) { + ret = channelConverter_.Process(inputFrameLen, srcData, (*input).Size(), dstData, converterOuput_.Size()); + } else if (inChannelInfo.numChannels > outChannelInfo.numChannels) { // convert, then resample + ret = channelConverter_.Process(inputFrameLen, srcData, (*input).Size(), tmpData, tmpOutBuf_.Size()); + ret += resampler_->Process(tmpData, &inputFrameLen, dstData, &outputFrameLen); + } else { // output channels larger than input channels, resample, then convert + ret = resampler_->Process(srcData, &inputFrameLen, tmpData, &outputFrameLen); + ret += channelConverter_.Process(outputFrameLen, tmpData, tmpOutBuf_.Size(), dstData, converterOuput_.Size()); + } + return ret; +} + +// return true if output info is updated +bool HpaeAudioFormatConverterNode::CheckUpdateOutInfo() +{ + // update channelLayout and numChannels + if (nodeFormatInfoCallback_ == nullptr) { + return false; + } + + uint32_t numChannels = 0; + uint64_t channelLayout = CH_LAYOUT_UNKNOWN; + // effectnode input is 48k by default now + uint32_t sampleRate = DEFAULT_EFFECT_RATE; + + // if there exists an effect node, converter node output is effect node input + // update channels and channelLayout + + nodeFormatInfoCallback_->GetEffectNodeInputChannelInfo(numChannels, channelLayout); + + if (numChannels == 0 || channelLayout == CH_LAYOUT_UNKNOWN) { + // set to node info, which is device output info + AUDIO_INFO_LOG("Fail to check format into from effect node"); + numChannels = GetChannelCount(); + channelLayout = (uint64_t)GetChannelLayout(); + sampleRate = GetSampleRate(); + } + + AudioChannelInfo curOutChannelInfo = channelConverter_.GetOutChannelInfo(); + if ((curOutChannelInfo.numChannels == numChannels) && (curOutChannelInfo.channelLayout == channelLayout) && + (sampleRate == resampler_->GetOutRate())) { + return false; + } + // update channel info + if (curOutChannelInfo.numChannels != numChannels || curOutChannelInfo.channelLayout != channelLayout) { + AudioChannelInfo newOutChannelInfo = { + .channelLayout = (AudioChannelLayout)channelLayout, + .numChannels = numChannels, + }; + AUDIO_INFO_LOG("NodeId %{public}d, update out channels and channelLayout: channels %{public}d -> %{public}d", + GetNodeId(), curOutChannelInfo.numChannels, numChannels); + CHECK_AND_RETURN_RET_LOG(channelConverter_.SetOutChannelInfo(newOutChannelInfo) == DMIX_ERR_SUCCESS, false, + "NodeId: %{public}d, Fail to set output channel info from effectNode!", GetNodeId()); + + uint32_t resampleChannels = std::min(channelConverter_.GetInChannelInfo().numChannels, numChannels); + if (resampleChannels != resampler_->GetChannels()) { + AUDIO_INFO_LOG("NodeId: %{public}d, Update resampler work channel from effectNode!", GetNodeId()); + resampler_->UpdateChannels(resampleChannels); + } + } + // update sample rate + if (resampler_->GetOutRate() != sampleRate) { + AUDIO_INFO_LOG("NodeId: %{public}d, update output sample rate: %{public}d -> %{public}d", + GetNodeId(), resampler_->GetOutRate(), sampleRate); + resampler_->UpdateRates(preNodeInfo_.samplingRate, sampleRate); + } + + HpaeNodeInfo nodeInfo = GetNodeInfo(); + nodeInfo.channels = (AudioChannel)numChannels; + nodeInfo.channelLayout = (AudioChannelLayout)channelLayout; + nodeInfo.samplingRate = (AudioSamplingRate)resampler_->GetOutRate(); + SetNodeInfo(nodeInfo); + return true; +} + +// update channel info from processCluster. For now sample rate will not change +bool HpaeAudioFormatConverterNode::CheckUpdateInInfo(HpaePcmBuffer *input) +{ + uint32_t numChannels = input->GetChannelCount(); + uint64_t channelLayout = input->GetChannelLayout(); + uint32_t sampleRate = input->GetSampleRate(); + AudioChannelInfo curInChannelInfo = channelConverter_.GetInChannelInfo(); + bool isInfoUpdated = false; + // update channels and channelLayout + if ((curInChannelInfo.numChannels != numChannels) || (curInChannelInfo.channelLayout != channelLayout)) { + AUDIO_INFO_LOG("NodeId %{public}d: Update innput channel info from pcmBufferInfo, " + "channels: %{public}d -> %{public}d, channellayout: %{public}" PRIu64 " -> %{public}" PRIu64 ".", + GetNodeId(), curInChannelInfo.numChannels, numChannels, curInChannelInfo.channelLayout, channelLayout); + + AudioChannelInfo newInChannelInfo = { + .channelLayout = (AudioChannelLayout)channelLayout, + .numChannels = numChannels, + }; + channelConverter_.SetInChannelInfo(newInChannelInfo); + preNodeInfo_.channelLayout = (AudioChannelLayout)channelLayout; + preNodeInfo_.channels = (AudioChannel)numChannels; + + uint32_t resampleChannels = std::min(numChannels, channelConverter_.GetOutChannelInfo().numChannels); + if (resampleChannels != resampler_->GetChannels()) { + AUDIO_INFO_LOG("NodeId %{public}d: Update resampler work channel from effectNode!", GetNodeId()); + resampler_->UpdateChannels(resampleChannels); + } + isInfoUpdated = true; + } + // update sample rate + if (sampleRate != resampler_->GetInRate()) { + AUDIO_INFO_LOG("NodeId %{public}d: Update resampler input sample rate: %{public}d -> %{public}d", + GetNodeId(), resampler_->GetInRate(), sampleRate); + preNodeInfo_.frameLen = input->GetFrameLen(); + preNodeInfo_.samplingRate = (AudioSamplingRate)sampleRate; + resampler_->UpdateRates(sampleRate, resampler_->GetOutRate()); + isInfoUpdated = true; + } + return isInfoUpdated; +} + +void HpaeAudioFormatConverterNode::UpdateTmpOutPcmBufferInfo(const PcmBufferInfo &outPcmBufferInfo) +{ + if (outPcmBufferInfo.ch == preNodeInfo_.channels || outPcmBufferInfo.rate == preNodeInfo_.samplingRate) { + // do not need tmpOutput Buffer + return; + } + PcmBufferInfo tmpOutPcmBufferInfo = outPcmBufferInfo; + if (outPcmBufferInfo.ch < preNodeInfo_.channels) { // downmix, then resample + tmpOutPcmBufferInfo.rate = preNodeInfo_.samplingRate; + tmpOutPcmBufferInfo.frameLen = preNodeInfo_.frameLen; + } else { // resample, then upmix + tmpOutPcmBufferInfo.ch = preNodeInfo_.channels; + } + AUDIO_INFO_LOG("NodeId: %{public}d, updated tmp buffer rate %{public}d, frameLen %{public}d, channels %{public}d", + GetNodeId(), tmpOutPcmBufferInfo.rate, tmpOutPcmBufferInfo.frameLen, tmpOutPcmBufferInfo.ch); + tmpOutBuf_.ReConfig(tmpOutPcmBufferInfo); +} + + +void HpaeAudioFormatConverterNode::CheckAndUpdateInfo(HpaePcmBuffer *input) +{ + bool isInfoUpdated = CheckUpdateInInfo(input); + bool isOutInfoUpdated = CheckUpdateOutInfo(); + if ((!isInfoUpdated) && (!isOutInfoUpdated)) { + return; + } + + AudioChannelInfo outChannelInfo = channelConverter_.GetOutChannelInfo(); + PcmBufferInfo outPcmBufferInfo = pcmBufferInfo_; // isMultiFrames_ and frame_ are inheritated from sinkInputNode + outPcmBufferInfo.ch = outChannelInfo.numChannels; + outPcmBufferInfo.rate = resampler_->GetOutRate(); + outPcmBufferInfo.frameLen = preNodeInfo_.frameLen * resampler_->GetOutRate() / resampler_->GetInRate(); + outPcmBufferInfo.channelLayout = outChannelInfo.channelLayout; + + AUDIO_INFO_LOG("NodeId %{public}d: output or input format info is changed, update tmp PCM buffer info!", + GetNodeId()); + UpdateTmpOutPcmBufferInfo(outPcmBufferInfo); + + if (isOutInfoUpdated) { + AUDIO_INFO_LOG("NodeId %{public}d: output format info is changed, update output PCM buffer info!", GetNodeId()); + converterOuput_.ReConfig(outPcmBufferInfo); + silenceData_.ReConfig(outPcmBufferInfo); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfoChanged(GetNodeId(), GetNodeInfo()); + } +#endif + } +} + +void HpaeAudioFormatConverterNode::ConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort(nodeInfo)); + converterOuput_.SetSourceBufferType(nodeInfo.sourceBufferType); +} +void HpaeAudioFormatConverterNode::DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.DisConnect(preNode->GetOutputPort(nodeInfo, true)); +} + +} // Hpae +} // AudioStandard +} // OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_capture_effect_node.cpp b/services/audio_engine/node/src/hpae_capture_effect_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b60fe307dc2f640adcd65f81bebd8915fd1b3b3d --- /dev/null +++ b/services/audio_engine/node/src/hpae_capture_effect_node.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeCaptureEffectNode" +#endif + +#include "hpae_capture_effect_node.h" +#include +#include "hpae_pcm_buffer.h" +#include "audio_engine_log.h" +#include "audio_errors.h" +#include "hpae_format_convert.h" +#include "audio_enhance_chain_manager.h" +#include "audio_effect_map.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeCaptureEffectNode::HpaeCaptureEffectNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo) +{ + const std::unordered_map &audioEnhanceSupportedSceneTypes = + GetEnhanceSupportedSceneType(); + auto item = audioEnhanceSupportedSceneTypes.find(nodeInfo.effectInfo.enhanceScene); + if (item != audioEnhanceSupportedSceneTypes.end()) { + sceneType_ = item->second; + AUDIO_INFO_LOG("HpaeCaptureEffectNode created scenetype: [%{public}s]", sceneType_.c_str()); + } else { + AUDIO_ERR_LOG("scenetype: %{public}u not supported", nodeInfo.effectInfo.enhanceScene); + } +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique( + "HpaeCaptureEffectNode_id_" + std::to_string(GetNodeId()) + "_" + sceneType_ + "_Out.pcm"); +#endif +} + +HpaeCaptureEffectNode::HpaeCaptureEffectNode(std::vector &nodeInfos) + : HpaeNode(*nodeInfos.begin()), HpaePluginNode(*nodeInfos.begin()) +{ + for (auto &nodeInfo : nodeInfos) { + capturerEffectConfigMap_.emplace(nodeInfo.sourceBufferType, nodeInfo); + } +} + +bool HpaeCaptureEffectNode::Reset() +{ + return HpaePluginNode::Reset(); +} + +HpaePcmBuffer *HpaeCaptureEffectNode::SignalProcess(const std::vector &inputs) +{ + Trace trace("[" + sceneType_ + "]HpaeRenderEffectNode::SignalProcess inputs num[" + + std::to_string(inputs.size()) + "]"); + if (inputs.empty()) { + AUDIO_WARNING_LOG("HpaeCaptureEffectNode inputs size is empty, SessionId:%{public}d", GetSessionId()); + return nullptr; + } + + AudioEnhanceChainManager *audioEnhanceChainManager = AudioEnhanceChainManager::GetInstance(); + uint32_t processLength = 0; + uint32_t outputIndex = 0; + for (uint32_t i = 0; i < inputs.size(); i++) { + if (inputs[i]->GetSourceBufferType() == HPAE_SOURCE_BUFFER_TYPE_MIC) { + ConvertFromFloat(SAMPLE_S16LE, micBufferLength_ / GetSizeFromFormat(SAMPLE_S16LE), + inputs[i]->GetPcmDataBuffer(), static_cast(cacheDataIn_.data())); + audioEnhanceChainManager->CopyToEnhanceBuffer(static_cast(cacheDataIn_.data()), micBufferLength_); + processLength = micBufferLength_; + outputIndex = i; + AUDIO_DEBUG_LOG("CopyToEnhanceBuffer size:%{public}u", processLength); + } else if (inputs[i]->GetSourceBufferType() == HPAE_SOURCE_BUFFER_TYPE_EC) { + ConvertFromFloat(SAMPLE_S16LE, ecBufferLength_ / GetSizeFromFormat(SAMPLE_S16LE), + inputs[i]->GetPcmDataBuffer(), static_cast(cacheDataIn_.data())); + audioEnhanceChainManager->CopyEcToEnhanceBuffer(static_cast(cacheDataIn_.data()), ecBufferLength_); + AUDIO_DEBUG_LOG("CopyEcToEnhanceBuffer size:%{public}u", ecBufferLength_); + } else if (inputs[i]->GetSourceBufferType() == HPAE_SOURCE_BUFFER_TYPE_MICREF) { + ConvertFromFloat(SAMPLE_S16LE, micrefBufferLength_ / GetSizeFromFormat(SAMPLE_S16LE), + inputs[i]->GetPcmDataBuffer(), static_cast(cacheDataIn_.data())); + audioEnhanceChainManager->CopyMicRefToEnhanceBuffer(static_cast(cacheDataIn_.data()), + micrefBufferLength_); + AUDIO_DEBUG_LOG("CopyMicRefToEnhanceBuffer size:%{public}u", micrefBufferLength_); + } + cacheDataIn_.clear(); + } + int32_t ret = audioEnhanceChainManager->ApplyAudioEnhanceChain(sceneKeyCode_, processLength); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, inputs[outputIndex], "effect apply failed, ret:%{public}d", ret); + audioEnhanceChainManager->CopyFromEnhanceBuffer(static_cast(cacheDataOut_.data()), processLength); + ConvertToFloat(SAMPLE_S16LE, micBufferLength_ / GetSizeFromFormat(SAMPLE_S16LE), + static_cast(cacheDataOut_.data()), inputs[outputIndex]->GetPcmDataBuffer()); +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_) { + outputPcmDumper_->Dump((int8_t *)inputs[outputIndex]->GetPcmDataBuffer(), + micBufferLength_ / GetSizeFromFormat(SAMPLE_S16LE) * sizeof(float)); + } +#endif + return inputs[outputIndex]; +} + +void HpaeCaptureEffectNode::ConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + std::shared_ptr realPreNode = preNode->GetSharedInstance(nodeInfo); + inputStream_.Connect(realPreNode, preNode->GetOutputPort(nodeInfo)); +#ifdef ENABLE_HIDUMP_DFX + if (auto callback = realPreNode->GetNodeInfo().statusCallback.lock()) { + callback->OnNotifyDfxNodeInfo( + true, realPreNode->GetNodeId(), GetNodeInfo()); + } +#endif +} + +void HpaeCaptureEffectNode::DisConnectWithInfo(const std::shared_ptr>& preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.DisConnect(preNode->GetOutputPort(nodeInfo, true)); +} + +bool HpaeCaptureEffectNode::GetCapturerEffectConfig(HpaeNodeInfo& nodeInfo, HpaeSourceBufferType type) +{ + CHECK_AND_RETURN_RET_LOG(capturerEffectConfigMap_.find(type) != capturerEffectConfigMap_.end(), + false, "not need resample node, type:%{public}u", type); + nodeInfo = capturerEffectConfigMap_[type]; + return true; +} + +void HpaeCaptureEffectNode::SetCapturerEffectConfig(AudioBufferConfig micConfig, AudioBufferConfig ecConfig, + AudioBufferConfig micrefConfig) +{ + HpaeNodeInfo micInfo = {}; + HpaeNodeInfo ecInfo = {}; + HpaeNodeInfo micrefInfo = {}; + micInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_MIC; + micInfo.frameLen = FRAME_LEN * (micConfig.samplingRate / MILLISECOND_PER_SECOND); + micInfo.samplingRate = static_cast(micConfig.samplingRate); + micInfo.channels = static_cast(micConfig.channels); + micInfo.format = static_cast(micConfig.format / BITLENGTH - 1); + ecInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_EC; + ecInfo.frameLen = FRAME_LEN * (ecConfig.samplingRate / MILLISECOND_PER_SECOND); + ecInfo.samplingRate = static_cast(ecConfig.samplingRate); + ecInfo.channels = static_cast(ecConfig.channels); + ecInfo.format = static_cast(ecConfig.format / BITLENGTH - 1); + micrefInfo.sourceBufferType = HPAE_SOURCE_BUFFER_TYPE_MICREF; + micrefInfo.frameLen = FRAME_LEN * (micrefConfig.samplingRate / MILLISECOND_PER_SECOND); + micrefInfo.samplingRate = static_cast(micrefConfig.samplingRate); + micrefInfo.channels = static_cast(micrefConfig.channels); + micrefInfo.format = static_cast(micrefConfig.format / BITLENGTH - 1); + capturerEffectConfigMap_.emplace(HPAE_SOURCE_BUFFER_TYPE_MIC, micInfo); + capturerEffectConfigMap_.emplace(HPAE_SOURCE_BUFFER_TYPE_EC, ecInfo); + capturerEffectConfigMap_.emplace(HPAE_SOURCE_BUFFER_TYPE_MICREF, micrefInfo); +} + +int32_t HpaeCaptureEffectNode::CaptureEffectCreate(uint64_t sceneKeyCode, CaptureEffectAttr attr) +{ + sceneKeyCode_ = sceneKeyCode; + AudioEnhanceChainManager *audioEnhanceChainManager = AudioEnhanceChainManager::GetInstance(); + CHECK_AND_RETURN_RET_LOG(audioEnhanceChainManager, ERR_ILLEGAL_STATE, "audioEnhanceChainManager is nullptr"); + AudioEnhanceDeviceAttr enhanceAttr = {}; + enhanceAttr.micChannels = attr.micChannels; + enhanceAttr.ecChannels = attr.ecChannels; + enhanceAttr.micRefChannels = attr.micRefChannels; + int32_t ret = audioEnhanceChainManager->CreateAudioEnhanceChainDynamic(sceneKeyCode, enhanceAttr); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("CreateAudioEnhanceChainDynamic failed, ret:%{public}d", ret); + } + audioEnhanceChainManager->InitEnhanceBuffer(); + + AudioBufferConfig micConfig = {}; + AudioBufferConfig ecConfig = {}; + AudioBufferConfig micrefConfig = {}; + ret = audioEnhanceChainManager->AudioEnhanceChainGetAlgoConfig(sceneKeyCode, micConfig, ecConfig, micrefConfig); + if (ret != 0 || micConfig.samplingRate == 0) { + AUDIO_ERR_LOG("get algo config failed, ret:%{public}d", ret); + return ERROR; + } + SetCapturerEffectConfig(micConfig, ecConfig, micrefConfig); + micBufferLength_ = FRAME_LEN * micConfig.channels * (micConfig.samplingRate / MILLISECOND_PER_SECOND) * + (micConfig.format / BITLENGTH); + ecBufferLength_ = FRAME_LEN * ecConfig.channels * (ecConfig.samplingRate / MILLISECOND_PER_SECOND) * + (ecConfig.format / BITLENGTH); + micrefBufferLength_ = FRAME_LEN * micrefConfig.channels * (micrefConfig.samplingRate / MILLISECOND_PER_SECOND) * + (micrefConfig.format / BITLENGTH); + uint32_t maxLength = (micBufferLength_ > ecBufferLength_) ? + (micBufferLength_ > micrefBufferLength_ ? micBufferLength_ : micrefBufferLength_) : + (ecBufferLength_ > micrefBufferLength_ ? ecBufferLength_ : micrefBufferLength_); + AUDIO_INFO_LOG("micLength: %{public}u, ecLength: %{public}u, micrefLength: %{public}u, maxLength:%{public}u", + micBufferLength_, ecBufferLength_, micrefBufferLength_, maxLength); + cacheDataIn_.resize(maxLength); + cacheDataOut_.resize(maxLength); + return ret; +} + +int32_t HpaeCaptureEffectNode::CaptureEffectRelease(uint64_t sceneKeyCode) +{ + AudioEnhanceChainManager *audioEnhanceChainManager = AudioEnhanceChainManager::GetInstance(); + CHECK_AND_RETURN_RET_LOG(audioEnhanceChainManager, ERROR_ILLEGAL_STATE, "audioEnhanceChainManager is nullptr"); + return audioEnhanceChainManager->ReleaseAudioEnhanceChainDynamic(sceneKeyCode); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/node/src/hpae_gain_node.cpp b/services/audio_engine/node/src/hpae_gain_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aaecfa687215fb3be829329f8a4436838bfa80ff --- /dev/null +++ b/services/audio_engine/node/src/hpae_gain_node.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeGainNode" +#endif + +#include "hpae_gain_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_volume.h" +#include "audio_engine_log.h" +#include "audio_utils.h" +#include "securec.h" +#include "volume_tools_c.h" +#include "audio_stream_info.h" +#include "hpae_info.h" + +#include +#include + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +static constexpr float FADE_LOW = 0.0f; +static constexpr float FADE_HIGH = 1.0f; +static constexpr float SHORT_FADE_PERIOD = 0.005f; // 5ms fade for 10ms < playback duration <= 40ms +static constexpr float EPSILON = 1e-6f; + +HpaeGainNode::HpaeGainNode(HpaeNodeInfo &nodeInfo) : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo) +{ + struct VolumeValues volumes; + float curSystemGain = AudioVolume::GetInstance()->GetVolume(GetSessionId(), + GetStreamType(), GetDeviceClass(), &volumes); + SetPreVolume(GetSessionId(), curSystemGain); + AUDIO_INFO_LOG("HpaeGainNode curSystemGain:%{public}f streamType :%{public}d", curSystemGain, GetStreamType()); + AUDIO_INFO_LOG( + "HpaeGainNode SessionId:%{public}u deviceClass :%{public}s", GetSessionId(), GetDeviceClass().c_str()); +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique("HpaeGainNodeInput_id_" + std::to_string(GetSessionId()) + + "_ch_" + std::to_string(GetChannelCount()) + "_rate_" + + std::to_string(GetSampleRate()) + "_" + GetTime() + ".pcm"); + outputPcmDumper_ = std::make_unique("HpaeGainNodeOut_id_" + std::to_string(GetSessionId()) + "_ch_" + + std::to_string(GetChannelCount()) + "_rate_" + + std::to_string(GetSampleRate()) + "_" + GetTime() + ".pcm"); +#endif +} + +HpaePcmBuffer *HpaeGainNode::SignalProcess(const std::vector &inputs) +{ + if (inputs.empty()) { + AUDIO_WARNING_LOG("HpaeGainNode inputs size is empty, SessionId:%{public}d", GetSessionId()); + return nullptr; + } + auto rate = "rate[" + std::to_string(inputs[0]->GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(inputs[0]->GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(inputs[0]->GetFrameLen()) + "]"; + Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeGainNode::SignalProcess " + rate + ch + len); + if (fadeOutState_ == FadeOutState::DONE_FADEOUT) { + AUDIO_INFO_LOG("HpaeGainNode: fadeout done, set session %{public}d slience", GetSessionId()); + SlienceData(inputs[0]); + } + float *inputData = (float *)inputs[0]->GetPcmDataBuffer(); + uint32_t frameLen = inputs[0]->GetFrameLen(); + uint32_t channelCount = inputs[0]->GetChannelCount(); + +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumper_ != nullptr) { + inputPcmDumper_->Dump((int8_t *)(inputData), (frameLen * sizeof(float) * channelCount)); + } +#endif + + if (needGainState_) { + DoGain(inputs[0], frameLen, channelCount); + } + DoFading(inputs[0]); + +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_ != nullptr) { + outputPcmDumper_->Dump((int8_t *)(inputData), (frameLen * sizeof(float) * channelCount)); + } +#endif + return inputs[0]; +} + +bool HpaeGainNode::SetClientVolume(float gain) +{ + preGain_ = curGain_; + curGain_ = gain; + isGainChanged_ = true; + return true; +} + +float HpaeGainNode::GetClientVolume() +{ + return curGain_; +} + +void HpaeGainNode::SetFadeState(IOperation operation) +{ + operation_ = operation; + // fade in + if (operation_ == OPERATION_STARTED) { + if (fadeInState_ == false) { // todo: add operation for softstart + fadeInState_ = true; + AUDIO_INFO_LOG("need to fade in"); + } else { + AUDIO_WARNING_LOG("fadeInState already set"); + } + fadeOutState_ = FadeOutState::NO_FADEOUT; // reset fadeOutState_ + } + + // fade out + if (operation_ == OPERATION_PAUSED || operation_ == OPERATION_STOPPED) { + if (fadeOutState_ == FadeOutState::NO_FADEOUT) { + fadeOutState_ = FadeOutState::DO_FADEOUT; + AUDIO_INFO_LOG("need to fade out"); + } else { + AUDIO_WARNING_LOG("current fadeout state %{public}d, cannot prepare fadeout", fadeOutState_); + } + } + AUDIO_DEBUG_LOG("fadeInState_[%{public}d], fadeOutState_[%{public}d]", fadeInState_, fadeOutState_); +} + + +void HpaeGainNode::DoFading(HpaePcmBuffer *input) +{ + if (!input->IsValid() && fadeOutState_ == FadeOutState::DO_FADEOUT) { + AUDIO_WARNING_LOG("after drain, get invalid data, no need to do fade out"); + fadeOutState_ = FadeOutState::DONE_FADEOUT; + auto statusCallback = GetNodeStatusCallback().lock(); + CHECK_AND_RETURN_LOG(statusCallback != nullptr, "statusCallback is null, cannot callback"); + statusCallback->OnFadeDone(GetSessionId(), operation_); + } + AudioRawFormat rawFormat; + rawFormat.format = SAMPLE_F32LE; // for now PCM in gain node is float32 + rawFormat.channels = GetChannelCount(); + uint32_t byteLength = 0; + uint8_t *data = (uint8_t *)input->GetPcmDataBuffer(); + switch (GetNodeInfo().fadeType) { + case FadeType::NONE_FADE: { + break; + } + case FadeType::SHORT_FADE: { + byteLength = (float)GetSampleRate() * SHORT_FADE_PERIOD * rawFormat.channels * sizeof(float); + AUDIO_DEBUG_LOG("GainNode: short fade length in Bytes: %{public}u", byteLength); + break; + } + case FadeType::DEFAULT_FADE: { + byteLength = input->Size(); + AUDIO_DEBUG_LOG("GainNode: default fade length in Bytes: %{public}u", byteLength); + break; + } + default: + break; + } + if (fadeInState_) { + CHECK_AND_RETURN_LOG(input->IsValid(), "GainNode: invalid data no need to do fade in"); + CHECK_AND_RETURN_LOG(!IsSilentData(input), "GainNode: silent data no need to do fade in"); + AUDIO_INFO_LOG("GainNode: fade in started!"); + ProcessVol(data, byteLength, rawFormat, FADE_LOW, FADE_HIGH); + fadeInState_ = false; + } + if (fadeOutState_ == FadeOutState::DO_FADEOUT) { + AUDIO_INFO_LOG("GainNode: fade out started!"); + ProcessVol(data, byteLength, rawFormat, FADE_HIGH, FADE_LOW); + fadeOutState_ = FadeOutState::DONE_FADEOUT; + return; + } + if (fadeOutState_ == FadeOutState::DONE_FADEOUT) { + AUDIO_INFO_LOG("fade out done, session %{public}d callback to update status", GetSessionId()); + auto statusCallback = GetNodeStatusCallback().lock(); + CHECK_AND_RETURN_LOG(statusCallback != nullptr, "statusCallback is null, cannot callback"); + statusCallback->OnFadeDone(GetSessionId(), operation_); // if operation is stop or pause, callback + } +} + +void HpaeGainNode::SlienceData(HpaePcmBuffer *pcmBuffer) +{ + void *data = pcmBuffer->GetPcmDataBuffer(); + if (GetNodeInfo().format == INVALID_WIDTH) { + AUDIO_WARNING_LOG("HpaePcmBuffer.SetDataSlience: invalid format"); + } else if (GetNodeInfo().format == SAMPLE_U8) { + // set silence data for all the frames + memset_s(data, pcmBuffer->Size(), 0x80, pcmBuffer->Size()); + } else { + memset_s(data, pcmBuffer->Size(), 0, pcmBuffer->Size()); + } +} + +void HpaeGainNode::DoGain(HpaePcmBuffer *input, uint32_t frameLen, uint32_t channelCount) +{ + struct VolumeValues volumes; + float *inputData = (float *)input->GetPcmDataBuffer(); + float curSystemGain = AudioVolume::GetInstance()->GetVolume( + GetSessionId(), GetStreamType(), GetDeviceClass(), &volumes); + float preSystemGain = GetPreVolume(GetSessionId()); + CHECK_AND_RETURN_LOG(frameLen != 0, "framelen is zero, invalid val."); + float systemStepGain = (curSystemGain - preSystemGain) / frameLen; + AUDIO_DEBUG_LOG( + "curSystemGain:%{public}f, preSystemGain:%{public}f, systemStepGain:%{public}f deviceClass :%{public}s", + curSystemGain, + preSystemGain, + systemStepGain, + GetDeviceClass().c_str()); + SetPreVolume(GetSessionId(), curSystemGain); + for (uint32_t i = 0; i < frameLen; i++) { + for (uint32_t j = 0; j < channelCount; j++) { + inputData[channelCount * i + j] = inputData[channelCount * i + j] * (preSystemGain + systemStepGain * i); + } + } +} + +bool HpaeGainNode::IsSilentData(HpaePcmBuffer *pcmBuffer) +{ + float *data = pcmBuffer->GetPcmDataBuffer(); + size_t length = pcmBuffer->Size() / sizeof(float); + AUDIO_DEBUG_LOG("HpaeGainNode::Data length:%{public}zu", length); + return std::all_of(data, data + length, [](float value) { + return fabs(value) < EPSILON; + }); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_inner_cap_sink_node.cpp b/services/audio_engine/node/src/hpae_inner_cap_sink_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e82f8e77388a1f344228c5309fe900d0e0db1df --- /dev/null +++ b/services/audio_engine/node/src/hpae_inner_cap_sink_node.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeInnerCapSinkNode" +#endif + +#include "hpae_inner_cap_sink_node.h" +#include "hpae_format_convert.h" +#include "audio_errors.h" +#include "audio_policy_log.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +const int32_t DEFAULT_NANO_SECONDS = 20000000; +const int32_t NANO_SECONDS = 1000000; + +HpaeInnerCapSinkNode::HpaeInnerCapSinkNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), outputStream_(this), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate), silenceData_(pcmBufferInfo_) +{ + silenceData_.Reset(); +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique("HpaeInnerCapSinkNode_bit_" + + std::to_string(GetBitWidth()) + "_ch_" + std::to_string(GetChannelCount()) + + "_rate_" + std::to_string(GetSampleRate()) + ".pcm"); +#endif +} + +void HpaeInnerCapSinkNode::DoProcess() +{ + std::vector &outputVec = inputStream_.ReadPreOutputData(); + if (outputVec.empty()) { + outputStream_.WriteDataToOutput(&silenceData_); + } else { + HpaePcmBuffer *outputData = outputVec.front(); +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_) { + outputPcmDumper_->Dump((int8_t *)outputData->GetPcmDataBuffer(), GetChannelCount() * + GetFrameLen() * GetSizeFromFormat(GetBitWidth())); + } +#endif + // no need convert + outputStream_.WriteDataToOutput(outputVec[0]); + } + // sleep + intervalTimer_.Stop(); + auto elapsed = intervalTimer_.Elapsed(); + std::this_thread::sleep_for(std::chrono::nanoseconds(DEFAULT_NANO_SECONDS - elapsed)); + AUDIO_DEBUG_LOG("sleeptime : %{public}f", DEFAULT_NANO_SECONDS - ((static_cast(elapsed))/NANO_SECONDS)); + intervalTimer_.Start(); +} + +bool HpaeInnerCapSinkNode::Reset() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + inputStream_.DisConnect(output); + } + return true; +} + +bool HpaeInnerCapSinkNode::ResetAll() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + std::shared_ptr hpaeNode = preOutput.second; + if (hpaeNode->ResetAll()) { + inputStream_.DisConnect(output); + } + } + return true; +} + +// todo +std::shared_ptr HpaeInnerCapSinkNode::GetSharedInstance() +{ + return shared_from_this(); +} + +// todo +OutputPort *HpaeInnerCapSinkNode::GetOutputPort() +{ + return &outputStream_; +} + +void HpaeInnerCapSinkNode::Connect(const std::shared_ptr>& preNode) +{ + AUDIO_INFO_LOG("Connect"); + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort()); +} + +void HpaeInnerCapSinkNode::DisConnect(const std::shared_ptr>& preNode) +{ + AUDIO_INFO_LOG("DisConnect"); + inputStream_.DisConnect(preNode->GetOutputPort()); +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkInit() +{ + AUDIO_INFO_LOG("Init"); + state_ = RENDERER_NEW; + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkDeInit() +{ + AUDIO_INFO_LOG("DeInit"); + state_ = RENDERER_INVALID; + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkFlush() +{ + AUDIO_INFO_LOG("Flush"); + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkPause() +{ + AUDIO_INFO_LOG("Pause"); + state_ = RENDERER_PAUSED; + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkReset() +{ + AUDIO_INFO_LOG("Reset"); + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkResume() +{ + AUDIO_INFO_LOG("Resume"); + state_ = RENDERER_RUNNING; + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkStart() +{ + AUDIO_INFO_LOG("Start"); + state_ = RENDERER_RUNNING; + return SUCCESS; +} + +int32_t HpaeInnerCapSinkNode::InnerCapturerSinkStop() +{ + AUDIO_INFO_LOG("Stop"); + state_ = RENDERER_STOPPED; + return SUCCESS; +} + +RendererState HpaeInnerCapSinkNode::GetSinkState(void) +{ + return state_; +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_mixer_node.cpp b/services/audio_engine/node/src/hpae_mixer_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41c2f5d6dcd4e5e0302328f70e94a2e788b6cbea --- /dev/null +++ b/services/audio_engine/node/src/hpae_mixer_node.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeMixerNode" +#endif +#include +#include "hpae_mixer_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_utils.h" +#include "cinttypes" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeMixerNode::HpaeMixerNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout), + mixedOutput_(pcmBufferInfo_) +{ +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique("HpaeMixerNodeOut_ch_" + + std::to_string(nodeInfo.channels) + "_scenType_" + + std::to_string(GetSceneType()) + "_rate_" + std::to_string(GetSampleRate()) + ".pcm"); + AUDIO_INFO_LOG("HpaeMixerNode scene type is %{public}d", GetSceneType()); +#endif +} + +bool HpaeMixerNode::Reset() +{ + return HpaePluginNode::Reset(); +} + +HpaePcmBuffer *HpaeMixerNode::SignalProcess(const std::vector &inputs) +{ + Trace trace("HpaeMixerNode::SignalProcess"); + CHECK_AND_RETURN_RET_LOG(!inputs.empty(), nullptr, "inputs is empty"); + + mixedOutput_.Reset(); + + bool isPCMBufferInfoUpdated = false; + if (pcmBufferInfo_.ch != inputs[0]->GetChannelCount()) { + AUDIO_INFO_LOG("Update channel count: %{public}d -> %{public}d", + pcmBufferInfo_.ch, inputs[0]->GetChannelCount()); + pcmBufferInfo_.ch = inputs[0]->GetChannelCount(); + isPCMBufferInfoUpdated = true; + } + if (pcmBufferInfo_.frameLen != inputs[0]->GetFrameLen()) { + AUDIO_INFO_LOG("Update frame len %{public}d -> %{public}d", + pcmBufferInfo_.frameLen, inputs[0]->GetFrameLen()); + pcmBufferInfo_.frameLen = inputs[0]->GetFrameLen(); + isPCMBufferInfoUpdated = true; + } + if (pcmBufferInfo_.rate != inputs[0]->GetSampleRate()) { + AUDIO_INFO_LOG("Update sample rate %{public}d -> %{public}d", + pcmBufferInfo_.rate, inputs[0]->GetSampleRate()); + pcmBufferInfo_.rate = inputs[0]->GetSampleRate(); + isPCMBufferInfoUpdated = true; + } + if (pcmBufferInfo_.channelLayout != inputs[0]->GetChannelLayout()) { + AUDIO_INFO_LOG("Update channel layout %{public}" PRIu64 " -> %{public}" PRIu64 "", + pcmBufferInfo_.channelLayout, inputs[0]->GetChannelLayout()); + pcmBufferInfo_.channelLayout = inputs[0]->GetChannelLayout(); + isPCMBufferInfoUpdated = true; + } + // if other bitwidth is supported, add check here + + if (isPCMBufferInfoUpdated) { + mixedOutput_.ReConfig(pcmBufferInfo_); + } + + for (auto input : inputs) { + mixedOutput_ += *input; + } +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_->CheckAndReopenHandlde(); + outputPcmDumper_->Dump((int8_t *)(mixedOutput_.GetPcmDataBuffer()), + mixedOutput_.GetChannelCount() * sizeof(float) * mixedOutput_.GetFrameLen()); +#endif + return &mixedOutput_; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_node_common.cpp b/services/audio_engine/node/src/hpae_node_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6bb296ef302623c9d13e740d947789826d16ebef --- /dev/null +++ b/services/audio_engine/node/src/hpae_node_common.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hpae_node_common.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +static constexpr uint64_t TIME_US_PER_S = 1000000; +static std::map g_streamTypeToSceneTypeMap = { + {STREAM_MUSIC, HPAE_SCENE_MUSIC}, + {STREAM_GAME, HPAE_SCENE_GAME}, + {STREAM_MOVIE, HPAE_SCENE_MOVIE}, + {STREAM_GAME, HPAE_SCENE_GAME}, + {STREAM_SPEECH, HPAE_SCENE_SPEECH}, + {STREAM_VOICE_RING, HPAE_SCENE_RING}, + {STREAM_VOICE_COMMUNICATION, HPAE_SCENE_VOIP_DOWN}, + {STREAM_MEDIA, HPAE_SCENE_OTHERS} +}; + +static std::unordered_map g_sourceTypeToSceneTypeMap = { + {SOURCE_TYPE_MIC, HPAE_SCENE_RECORD}, + {SOURCE_TYPE_CAMCORDER, HPAE_SCENE_RECORD}, + {SOURCE_TYPE_VOICE_CALL, HPAE_SCENE_VOIP_UP}, + {SOURCE_TYPE_VOICE_COMMUNICATION, HPAE_SCENE_VOIP_UP}, + {SOURCE_TYPE_VOICE_TRANSCRIPTION, HPAE_SCENE_PRE_ENHANCE}, + {SOURCE_TYPE_VOICE_MESSAGE, HPAE_SCENE_VOICE_MESSAGE} +}; + + +static std::unordered_set g_processorTypeNeedEcSet = { + HPAE_SCENE_VOIP_UP, + HPAE_SCENE_PRE_ENHANCE, +}; + +static std::unordered_set g_processorTypeNeedMicRefSet = { + HPAE_SCENE_VOIP_UP, + HPAE_SCENE_RECORD, +}; + +static std::unordered_map g_processorTypeToSceneTypeMap = { + {HPAE_SCENE_RECORD, SCENE_RECORD}, + {HPAE_SCENE_VOIP_UP, SCENE_VOIP_UP}, + {HPAE_SCENE_PRE_ENHANCE, SCENE_PRE_ENHANCE}, + {HPAE_SCENE_VOICE_MESSAGE, SCENE_VOICE_MESSAGE} +}; + +HpaeProcessorType TransStreamTypeToSceneType(AudioStreamType streamType) +{ + if (g_streamTypeToSceneTypeMap.find(streamType) == g_streamTypeToSceneTypeMap.end()) { + return HPAE_SCENE_EFFECT_NONE; + } else { + return g_streamTypeToSceneTypeMap[streamType]; + } +} + +HpaeProcessorType TransSourceTypeToSceneType(SourceType sourceType) +{ + if (g_sourceTypeToSceneTypeMap.find(sourceType) == g_sourceTypeToSceneTypeMap.end()) { + return HPAE_SCENE_EFFECT_NONE; + } else { + return g_sourceTypeToSceneTypeMap[sourceType]; + } +} + +bool CheckSceneTypeNeedEc(HpaeProcessorType processorType) +{ + return g_processorTypeNeedEcSet.find(processorType) != g_processorTypeNeedEcSet.end(); +} + +bool CheckSceneTypeNeedMicRef(HpaeProcessorType processorType) +{ + return g_processorTypeNeedMicRefSet.find(processorType) != g_processorTypeNeedMicRefSet.end(); +} + +static std::unordered_map g_processorTypeToEffectSceneTypeMap = { + {HPAE_SCENE_OTHERS, "SCENE_OTHERS"}, + {HPAE_SCENE_MUSIC, "SCENE_MUSIC"}, + {HPAE_SCENE_GAME, "SCENE_GAME"}, + {HPAE_SCENE_MOVIE, "SCENE_MOVIE"}, + {HPAE_SCENE_SPEECH, "SCENE_SPEECH"}, + {HPAE_SCENE_RING, "SCENE_RING"}, + {HPAE_SCENE_VOIP_DOWN, "SCENE_VOIP_DOWN"}}; + +std::string TransProcessorTypeToSceneType(HpaeProcessorType processorType) +{ + if (g_processorTypeToEffectSceneTypeMap.find(processorType) == g_processorTypeToEffectSceneTypeMap.end()) { + return "SCENE_EXTRA"; + } else { + return g_processorTypeToEffectSceneTypeMap[processorType]; + } +} + +bool CheckHpaeNodeInfoIsSame(HpaeNodeInfo &preNodeInfo, HpaeNodeInfo &curNodeInfo) +{ + return preNodeInfo.channels == curNodeInfo.channels && //&& preNodeInfo.format == curNodeInfo.format todo + preNodeInfo.samplingRate == curNodeInfo.samplingRate && + preNodeInfo.channelLayout == curNodeInfo.channelLayout; +} + +std::string TransHpaeResampleNodeInfoToStringKey(HpaeNodeInfo& nodeInfo) +{ + std::string nodeKey = std::to_string(nodeInfo.sourceBufferType) + "_" + + std::to_string(nodeInfo.samplingRate) + "_" + + std::to_string(nodeInfo.channels) + "_" + + std::to_string(nodeInfo.format); + return nodeKey; +} + +AudioEnhanceScene TransProcessType2EnhanceScene(const HpaeProcessorType &processorType) +{ + if (g_processorTypeToSceneTypeMap.find(processorType) == g_processorTypeToSceneTypeMap.end()) { + return SCENE_NONE; + } else { + return g_processorTypeToSceneTypeMap[processorType]; + } +} + +size_t ConvertUsToFrameCount(uint64_t usTime, const HpaeNodeInfo &nodeInfo) +{ + return usTime * nodeInfo.samplingRate / TIME_US_PER_S / + (nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); +} + +uint64_t ConvertDatalenToUs(size_t bufferSize, const HpaeNodeInfo &nodeInfo) +{ + if (nodeInfo.channels == 0 || GetSizeFromFormat(nodeInfo.format) == 0 || nodeInfo.samplingRate == 0) { + AUDIO_ERR_LOG("invalid nodeInfo"); + return 0; + } + + double samples = static_cast(bufferSize) / + (nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + double seconds = samples / static_cast(nodeInfo.samplingRate); + double microseconds = seconds * TIME_US_PER_S; + + return static_cast(microseconds); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_offload_sinkoutput_node.cpp b/services/audio_engine/node/src/hpae_offload_sinkoutput_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a893919a8c3f3cc348e5c4e305b3ab52003e768b --- /dev/null +++ b/services/audio_engine/node/src/hpae_offload_sinkoutput_node.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeOffloadSinkOutputNode" +#endif + +#include "hpae_offload_sinkoutput_node.h" +#include "audio_errors.h" +#include +#include + +#include "hpae_format_convert.h" +#include "hpae_node_common.h" +#include "audio_engine_log.h" +#include "audio_volume.h" +#include "audio_common_utils.h" +#ifdef ENABLE_HOOK_PCM +#include "hpae_pcm_dumper.h" +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +namespace { + constexpr uint32_t CACHE_FRAME_COUNT = 2; + constexpr uint32_t TIME_US_PER_MS = 1000; + constexpr uint32_t TIME_MS_PER_SEC = 1000; + constexpr uint32_t ERR_RETRY_COUNT = 20; + constexpr uint32_t FRAME_TIME_IN_MS = 20; + constexpr int32_t OFFLOAD_FULL = -1; + constexpr int32_t OFFLOAD_WRITE_FAILED = -2; + constexpr uint32_t OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS = 7000; + constexpr uint32_t OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS = 200; + // hdi fallback, modify when hdi change + constexpr uint32_t OFFLOAD_FAD_INTERVAL_IN_US = 180; + constexpr uint32_t OFFLOAD_SET_BUFFER_SIZE_NUM = 5; + constexpr uint32_t POLICY_STATE_DELAY_IN_SEC = 3; + + const std::string DEVICE_CLASS_OFFLOAD = "offload"; +} +HpaeOffloadSinkOutputNode::HpaeOffloadSinkOutputNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), + renderFrameData_(nodeInfo.frameLen * nodeInfo.channels * + GetSizeFromFormat(nodeInfo.format) * CACHE_FRAME_COUNT), + interleveData_(nodeInfo.frameLen * nodeInfo.channels) +{ +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique( + "HpaeOffloadSinkOutputNode_Out_bit_" + std::to_string(GetBitWidth()) + "_ch_" + + std::to_string(GetChannelCount()) + "_rate_" + std::to_string(GetSampleRate()) + ".pcm"); +#endif + frameLenMs_ = nodeInfo.frameLen * TIME_MS_PER_SEC / nodeInfo.samplingRate; +} + +bool HpaeOffloadSinkOutputNode::CheckIfSuspend() +{ + static uint32_t suspendCount = 0; + if (!GetPreOutNum()) { + suspendCount++; + usleep(TIME_US_PER_MS * FRAME_TIME_IN_MS); + if (suspendCount > timeoutThdFrames_) { + RenderSinkStop(); + } + return true; + } else { + suspendCount = 0; + return false; + } +} + +void HpaeOffloadSinkOutputNode::DoProcess() +{ + CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + if (CheckIfSuspend()) { + return; + } + // if there are no enough frames in cache, read more data from pre-output + size_t frameSize = GetSizeFromFormat(GetBitWidth()) * GetFrameLen() * GetChannelCount(); + while (renderFrameData_.size() < CACHE_FRAME_COUNT * frameSize) { + std::vector &outputVec = inputStream_.ReadPreOutputData(); + if (outputVec.front()->IsValid()) { + renderFrameData_.resize(renderFrameData_.size() + frameSize); + ConvertFromFloat(GetBitWidth(), GetChannelCount() * GetFrameLen(), + outputVec.front()->GetPcmDataBuffer(), renderFrameData_.data() + renderFrameData_.size() - frameSize); + } else { + break; + } + } + int32_t ret = ProcessRenderFrame(); + // if renderframe faild, sleep and return directly + // if renderframe full, unlock the powerlock + static uint32_t retryCount = 1; + if (ret != SUCCESS) { + if (ret == OFFLOAD_FULL) { + RunningLock(false); + isHdiFull_.store(true); + return; + } + usleep(std::min(retryCount, FRAME_TIME_IN_MS) * TIME_US_PER_MS); + if (retryCount < ERR_RETRY_COUNT) { + retryCount++; + } + return; + } + retryCount = 1; + return; +} + +bool HpaeOffloadSinkOutputNode::Reset() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + inputStream_.DisConnect(output); + } + return true; +} + +bool HpaeOffloadSinkOutputNode::ResetAll() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + std::shared_ptr hpaeNode = preOutput.second; + if (hpaeNode->ResetAll()) { + inputStream_.DisConnect(output); + } + } + return true; +} + +void HpaeOffloadSinkOutputNode::Connect(const std::shared_ptr> &preNode) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort()); +} + +void HpaeOffloadSinkOutputNode::DisConnect(const std::shared_ptr> &preNode) +{ + inputStream_.DisConnect(preNode->GetOutputPort()); +} + +int32_t HpaeOffloadSinkOutputNode::GetRenderSinkInstance(const std::string &deviceClass, + const std::string &deviceNetworkId) +{ + renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass( + deviceClass, HDI_ID_INFO_DEFAULT, true); + audioRendererSink_ = HdiAdapterManager::GetInstance().GetRenderSink(renderId_, true); + if (audioRendererSink_ == nullptr) { + AUDIO_ERR_LOG("get offload sink fail, deviceClass: %{public}s, renderId_: %{public}u", + deviceClass.c_str(), renderId_); + HdiAdapterManager::GetInstance().ReleaseId(renderId_); + return ERROR; + } + return SUCCESS; +} + +void HpaeOffloadSinkOutputNode::OffloadReset() +{ + writePos_ = 0; + hdiPos_ = std::make_pair(0, std::chrono::high_resolution_clock::now()); + firstWriteHdi_ = true; + isHdiFull_.store(false); + renderFrameData_.clear(); + setPolicyStateTask_.flag = false; // unset the task when reset + RunningLock(true); +} + +int32_t HpaeOffloadSinkOutputNode::RenderSinkInit(IAudioSinkAttr &attr) +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + + sinkOutAttr_ = attr; + state_ = RENDERER_PREPARED; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + int32_t ret = audioRendererSink_->Init(attr); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, + "audioRendererSink_ init failed, errCode is %{public}d", ret); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkInit Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + return ret; +} + +int32_t HpaeOffloadSinkOutputNode::RenderSinkDeInit(void) +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + state_ = RENDERER_INVALID; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + audioRendererSink_->DeInit(); + audioRendererSink_ = nullptr; + HdiAdapterManager::GetInstance().ReleaseId(renderId_); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkDeInit Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + return SUCCESS; +} + +int32_t HpaeOffloadSinkOutputNode::RenderSinkFlush(void) +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + int32_t ret; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + ret = audioRendererSink_->Flush(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, + "audioRendererSink_ flush failed, errCode is %{public}d", ret); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkFlush Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + return ret; +} + +int32_t HpaeOffloadSinkOutputNode::RenderSinkStart(void) +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + + int32_t ret; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + ret = audioRendererSink_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, + "audioRendererSink_ start failed, errCode is %{public}d", ret); + RegOffloadCallback(); + // start need lock + RunningLock(true); + OffloadSetHdiVolume(); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkStart Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + state_ = RENDERER_RUNNING; + return SUCCESS; +} + +int32_t HpaeOffloadSinkOutputNode::RenderSinkStop(void) +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + int32_t ret; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + ret = audioRendererSink_->Stop(); + OffloadReset(); + RunningLock(false); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, + "audioRendererSink_ stop failed, errCode is %{public}d", ret); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkStop Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + state_ = RENDERER_STOPPED; + return SUCCESS; +} + +void HpaeOffloadSinkOutputNode::FlushStream() +{ + renderFrameData_.clear(); +} + +size_t HpaeOffloadSinkOutputNode::GetPreOutNum() +{ + return inputStream_.GetPreOutputNum(); +} + +RendererState HpaeOffloadSinkOutputNode::GetSinkState(void) +{ + return isHdiFull_.load() ? RENDERER_PAUSED : state_; +} + +const char *HpaeOffloadSinkOutputNode::GetRenderFrameData(void) +{ + return renderFrameData_.data(); +} + +void HpaeOffloadSinkOutputNode::StopStream() +{ + // flush hdi when disconnect + RunningLock(true); + auto ret = RenderSinkFlush(); + CHECK_AND_RETURN_LOG(ret == SUCCESS, "RenderSinkFlush failed"); + uint64_t cacheLenInHdi = CalcOffloadCacheLenInHdi(); + cacheLenInHdi = cacheLenInHdi > OFFLOAD_FAD_INTERVAL_IN_US ? + cacheLenInHdi - OFFLOAD_FAD_INTERVAL_IN_US : 0; + uint64_t rewindTime = cacheLenInHdi + ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo()); + AUDIO_DEBUG_LOG("OffloadRewindAndFlush rewind time in us %{public}" PRIu64, rewindTime); + auto callback = GetNodeInfo().statusCallback.lock(); + callback->OnRewindAndFlush(rewindTime); + OffloadReset(); +} + +void HpaeOffloadSinkOutputNode::SetPolicyState(int32_t state) +{ + if (setPolicyStateTask_.flag) { + if (state == hdiPolicyState_) { + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: unset policy state task"); + setPolicyStateTask_.flag = false; + } + return; + } + if (hdiPolicyState_ != state && state == OFFLOAD_INACTIVE_BACKGROUND) { + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: set policy state task"); + setPolicyStateTask_.flag = true; + setPolicyStateTask_.time = std::chrono::high_resolution_clock::now(); + setPolicyStateTask_.state = OFFLOAD_INACTIVE_BACKGROUND; + return; + } + hdiPolicyState_ = static_cast(state); + int32_t bufferSize = hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND ? + OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS : OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS; + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: set hdi buffer size to %{public}d", bufferSize); + audioRendererSink_->SetBufferSize(bufferSize); +} + +uint64_t HpaeOffloadSinkOutputNode::GetLatency() +{ + return CalcOffloadCacheLenInHdi() + ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo()); +} + +int32_t HpaeOffloadSinkOutputNode::SetTimeoutStopThd(uint32_t timeoutThdMs) +{ + if (frameLenMs_ != 0) { + timeoutThdFrames_ = timeoutThdMs / frameLenMs_; + } + AUDIO_INFO_LOG( + "SetTimeoutStopThd: timeoutThdFrames_:%{public}u, timeoutThdMs :%{public}u", timeoutThdFrames_, timeoutThdMs); + return SUCCESS; +} + +void HpaeOffloadSinkOutputNode::RunningLock(bool islock) +{ + if (islock) { + audioRendererSink_->LockOffloadRunningLock(); + } else if (!islock && hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND) { + // only unlock when background + audioRendererSink_->UnLockOffloadRunningLock(); + } +} + +void HpaeOffloadSinkOutputNode::SetBufferSizeWhileRenderFrame() +{ + // 3s delay works, when change to BACKGROUND + if (setPolicyStateTask_.flag) { + auto now = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(now - setPolicyStateTask_.time).count() >= + POLICY_STATE_DELAY_IN_SEC) { + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: excute set policy state task"); + setPolicyStateTask_.flag = false; + hdiPolicyState_ = setPolicyStateTask_.state; + audioRendererSink_->SetBufferSize(hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND ? + OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS : OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS); + } + return; // no need to set buffer size twice at one process + } + // first start need to set buffer size 5 times + if (setHdiBufferSizeNum_ > 0) { + setHdiBufferSizeNum_--; + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: set policy state cause first render"); + audioRendererSink_->SetBufferSize(hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND ? + OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS : OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS); + } +} + +int32_t HpaeOffloadSinkOutputNode::ProcessRenderFrame() +{ + if (renderFrameData_.empty()) { + return OFFLOAD_WRITE_FAILED; + } + uint64_t writeLen = 0; + char *renderFrameData = (char *)renderFrameData_.data(); +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); + intervalTimer_.Stop(); + uint64_t interval = intervalTimer_.Elapsed(); + AUDIO_DEBUG_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderFrame interval: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + auto now = std::chrono::high_resolution_clock::now(); + auto ret = audioRendererSink_->RenderFrame(*renderFrameData, renderFrameData_.size(), writeLen); + if (ret == SUCCESS && writeLen == 0 && !firstWriteHdi_) { + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: offload renderFrame full"); + return OFFLOAD_FULL; + } + if (!(ret == SUCCESS && writeLen == renderFrameData_.size())) { + AUDIO_ERR_LOG("HpaeOffloadSinkOutputNode: offload renderFrame failed, errCode is %{public}d", ret); + return OFFLOAD_WRITE_FAILED; + } + // calc written data length + writePos_ += ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo()); + // now is the time to first write hdi + if (firstWriteHdi_) { + firstWriteHdi_ = false; + hdiPos_ = std::make_pair(0, now); + setHdiBufferSizeNum_ = OFFLOAD_SET_BUFFER_SIZE_NUM; + // if the hdi is flushing, it will block the volume setting. + // so the render frame judge it. + OffloadSetHdiVolume(); + AUDIO_INFO_LOG("offload write pos: %{public}" PRIu64 " hdi pos: %{public}" PRIu64 " ", + writePos_, hdiPos_.first); + } + // hdi fallback, dont modify + SetBufferSizeWhileRenderFrame(); +#ifdef ENABLE_HOOK_PCM + AUDIO_DEBUG_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderFrame interval: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); + if (outputPcmDumper_) { + outputPcmDumper_->Dump((int8_t *)renderFrameData, renderFrameData_.size()); + } + timer.Stop(); + uint64_t elapsed = timer.Elapsed(); + AUDIO_DEBUG_LOG("HpaeOffloadSinkOutputNode :name %{public}s, RenderFrame elapsed time: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), elapsed); + intervalTimer_.Start(); +#endif + renderFrameData_.clear(); + return SUCCESS; +} + +int32_t HpaeOffloadSinkOutputNode::UpdatePresentationPosition() +{ + CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE, + "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + uint64_t frames; + int64_t timeSec; + int64_t timeNanoSec; + int ret = audioRendererSink_->GetPresentationPosition(frames, timeSec, timeNanoSec); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetPresentationPosition failed, errCode is %{public}d", ret); + auto total_ns = std::chrono::seconds(timeSec) + std::chrono::nanoseconds(timeNanoSec); + hdiPos_ = std::make_pair(frames, TimePoint(total_ns)); + return 0; +} + +uint64_t HpaeOffloadSinkOutputNode::CalcOffloadCacheLenInHdi() +{ + auto now = std::chrono::high_resolution_clock::now(); + uint64_t time = now > hdiPos_.second ? + std::chrono::duration_cast(now - hdiPos_.second).count() : 0; + uint64_t hdiPos = hdiPos_.first + time; + uint64_t cacheLenInHdi = writePos_ > hdiPos ? (writePos_ - hdiPos) : 0; + AUDIO_DEBUG_LOG("offload latency: %{public}" PRIu64 " write pos: %{public}" PRIu64 + " hdi pos: %{public}" PRIu64 " time: %{public}" PRIu64, + cacheLenInHdi, writePos_, hdiPos, time); + return cacheLenInHdi; +} + +void HpaeOffloadSinkOutputNode::OffloadSetHdiVolume() +{ + struct VolumeValues volumes; + AudioStreamType volumeType = VolumeUtils::GetVolumeTypeFromStreamType(GetStreamType()); + float volumeEnd = AudioVolume::GetInstance()->GetVolume(GetSessionId(), volumeType, DEVICE_CLASS_OFFLOAD, &volumes); + float volumeBeg = AudioVolume::GetInstance()->GetHistoryVolume(GetSessionId()); + if (volumeBeg != volumeEnd) { + AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode::sessionID:%{public}u, volumeBeg:%{public}f, volumeEnd:%{public}f", + GetSessionId(), volumeBeg, volumeEnd); + AudioVolume::GetInstance()->SetHistoryVolume(GetSessionId(), volumeEnd); + AudioVolume::GetInstance()->Monitor(GetSessionId(), true); + } + CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + audioRendererSink_->SetVolume(volumeEnd, volumeEnd); +} + +void HpaeOffloadSinkOutputNode::OffloadCallback(const RenderCallbackType type) +{ + switch (type) { + case CB_NONBLOCK_WRITE_COMPLETED: { + if (isHdiFull_.load()) { + RunningLock(true); + UpdatePresentationPosition(); + auto callback = GetNodeInfo().statusCallback.lock(); + isHdiFull_.store(false); + if (callback) { + callback->OnNotifyQueue(); + } + } + break; + } + default: + break; + } +} + +void HpaeOffloadSinkOutputNode::RegOffloadCallback() +{ + CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + audioRendererSink_->RegistOffloadHdiCallback([this](const RenderCallbackType type) { OffloadCallback(type); }); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/node/src/hpae_output_cluster.cpp b/services/audio_engine/node/src/hpae_output_cluster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59b4f4dd72165ebb0dae78635bb43bc2a22c6784 --- /dev/null +++ b/services/audio_engine/node/src/hpae_output_cluster.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeOutputCluster" +#endif + +#include "hpae_output_cluster.h" +#include "hpae_node_common.h" +#include "audio_engine_log.h" +#include "audio_errors.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +HpaeOutputCluster::HpaeOutputCluster(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), mixerNode_(std::make_shared(nodeInfo)), + hpaeSinkOutputNode_(std::make_shared(nodeInfo)) +{ +#ifdef ENABLE_HIDUMP_DFX + if (nodeInfo.statusCallback.lock()) { + nodeInfo.nodeName = "hpaeSinkOutputNode"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + hpaeSinkOutputNode_->SetNodeInfo(nodeInfo); + nodeInfo.statusCallback.lock()->OnNotifyDfxNodeInfo(true, 0, nodeInfo); + nodeInfo.nodeName = "HpaeMixerNode"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + mixerNode_->SetNodeInfo(nodeInfo); + nodeInfo.statusCallback.lock()->OnNotifyDfxNodeInfo(true, hpaeSinkOutputNode_->GetNodeId(), nodeInfo); + } +#endif + hpaeSinkOutputNode_->Connect(mixerNode_); + frameLenMs_ = nodeInfo.frameLen * MILLISECOND_PER_SECOND / nodeInfo.samplingRate; + AUDIO_INFO_LOG( + "HpaeOutputCluster frameLenMs_:%{public}u ms, timeoutThdFrames_:%{public}u", frameLenMs_, timeoutThdFrames_); +} + +HpaeOutputCluster::~HpaeOutputCluster() +{ + Reset(); +} + +void HpaeOutputCluster::DoProcess() +{ + Trace trace("HpaeOutputCluster::DoProcess"); + hpaeSinkOutputNode_->DoProcess(); + if (mixerNode_->GetPreOutNum() == 0) { + timeoutStopCount_++; + } else { + timeoutStopCount_ = 0; + } + if (timeoutStopCount_ > timeoutThdFrames_) { + int32_t ret = hpaeSinkOutputNode_->RenderSinkStop(); + timeoutStopCount_ = 0; + AUDIO_INFO_LOG("HpaeOutputCluster timeout RenderSinkStop ret :%{public}d", ret); + } +} + +bool HpaeOutputCluster::Reset() +{ + mixerNode_->Reset(); + for (auto converterNode : sceneConverterMap_) { + converterNode.second->Reset(); + } + hpaeSinkOutputNode_->DisConnect(mixerNode_); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = hpaeSinkOutputNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(false, hpaeSinkOutputNode_->GetNodeId(), hpaeSinkOutputNode_->GetNodeInfo()); + } +#endif + return true; +} + +bool HpaeOutputCluster::ResetAll() +{ + return hpaeSinkOutputNode_->ResetAll(); // Complete the code here +} + +void HpaeOutputCluster::Connect(const std::shared_ptr> &preNode) +{ + HpaeNodeInfo &preNodeInfo = preNode->GetSharedInstance()->GetNodeInfo(); + HpaeNodeInfo &curNodeInfo = GetNodeInfo(); + HpaeProcessorType sceneType = preNodeInfo.sceneType; + AUDIO_INFO_LOG("HpaeOutputCluster input sceneType is %{public}u", preNodeInfo.sceneType); + AUDIO_INFO_LOG("HpaeOutputCluster input rate is %{public}u, ch is %{public}u", + preNodeInfo.samplingRate, preNodeInfo.channels); + AUDIO_INFO_LOG(" HpaeOutputCluster output rate is %{public}u, ch is %{public}u", + curNodeInfo.samplingRate, curNodeInfo.channels); + AUDIO_INFO_LOG(" HpaeOutputCluster preNode name %{public}s, curNode name is %{public}s", + preNodeInfo.nodeName.c_str(), curNodeInfo.nodeName.c_str()); + AUDIO_INFO_LOG("HpaeOutputCluster mixer id %{public}u, SinkOut id %{public}u", + mixerNode_->GetNodeId(), hpaeSinkOutputNode_->GetNodeId()); + +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + curNodeInfo.nodeId = callBack->OnGetNodeId(); + curNodeInfo.nodeName = "HpaeAudioFormatConverterNode"; + } +#endif + + if (sceneConverterMap_.find(sceneType) == sceneConverterMap_.end()) { + sceneConverterMap_[sceneType] = std::make_shared(preNodeInfo, curNodeInfo); + } else { +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(false, mixerNode_->GetNodeId(), sceneConverterMap_[sceneType]->GetNodeInfo()); + } +#endif + sceneConverterMap_.erase(sceneType); + sceneConverterMap_[sceneType] = std::make_shared(preNodeInfo, curNodeInfo); + } + sceneConverterMap_[sceneType]->Connect(preNode); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + AUDIO_INFO_LOG("HpaeOutputCluster connect curNodeInfo name %{public}s", curNodeInfo.nodeName.c_str()); + AUDIO_INFO_LOG("HpaeOutputCluster connect preNodeInfo name %{public}s", preNodeInfo.nodeName.c_str()); + callBack->OnNotifyDfxNodeInfo(true, mixerNode_->GetNodeId(), curNodeInfo); + callBack->OnNotifyDfxNodeInfo(true, curNodeInfo.nodeId, preNodeInfo); + } +#endif + mixerNode_->Connect(sceneConverterMap_[sceneType]); + connectedProcessCluster_.insert(sceneType); +} + +void HpaeOutputCluster::DisConnect(const std::shared_ptr> &preNode) +{ + HpaeNodeInfo &preNodeInfo = preNode->GetSharedInstance()->GetNodeInfo(); + HpaeProcessorType sceneType = preNodeInfo.sceneType; + AUDIO_INFO_LOG("HpaeOutputCluster input sceneType is %{public}u", preNodeInfo.sceneType); + if (sceneConverterMap_.find(sceneType) != sceneConverterMap_.end()) { + sceneConverterMap_[sceneType]->DisConnect(preNode); + mixerNode_->DisConnect(sceneConverterMap_[sceneType]); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(false, mixerNode_->GetNodeId(), sceneConverterMap_[sceneType]->GetNodeInfo()); + } +#endif + sceneConverterMap_.erase(sceneType); + } else { + mixerNode_->DisConnect(preNode); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(false, mixerNode_->GetNodeId(), preNodeInfo); + } +#endif + } + connectedProcessCluster_.erase(sceneType); +} + +int32_t HpaeOutputCluster::GetConverterNodeCount() +{ + return sceneConverterMap_.size(); +} + +int32_t HpaeOutputCluster::GetInstance(std::string deviceClass, std::string deviceNetId) +{ + return hpaeSinkOutputNode_->GetRenderSinkInstance(deviceClass, deviceNetId); +} + +int32_t HpaeOutputCluster::Init(IAudioSinkAttr &attr) +{ + return hpaeSinkOutputNode_->RenderSinkInit(attr); +} + +int32_t HpaeOutputCluster::DeInit() +{ + return hpaeSinkOutputNode_->RenderSinkDeInit(); +} + +int32_t HpaeOutputCluster::Flush(void) +{ + return hpaeSinkOutputNode_->RenderSinkFlush(); +} + +int32_t HpaeOutputCluster::Pause(void) +{ + return hpaeSinkOutputNode_->RenderSinkPause(); +} + +int32_t HpaeOutputCluster::ResetRender(void) +{ + return hpaeSinkOutputNode_->RenderSinkReset(); +} + +int32_t HpaeOutputCluster::Resume(void) +{ + return hpaeSinkOutputNode_->RenderSinkResume(); +} + +int32_t HpaeOutputCluster::Start(void) +{ + return hpaeSinkOutputNode_->RenderSinkStart(); +} + +int32_t HpaeOutputCluster::Stop(void) +{ + return hpaeSinkOutputNode_->RenderSinkStop(); +} + +const char *HpaeOutputCluster::GetFrameData(void) +{ + return hpaeSinkOutputNode_->GetRenderFrameData(); +} + +RendererState HpaeOutputCluster::GetState(void) +{ + return hpaeSinkOutputNode_->GetSinkState(); +} + +int32_t HpaeOutputCluster::GetPreOutNum() +{ + return mixerNode_->GetPreOutNum(); +} + +int32_t HpaeOutputCluster::SetTimeoutStopThd(uint32_t timeoutThdMs) +{ + if (frameLenMs_ != 0) { + timeoutThdFrames_ = timeoutThdMs / frameLenMs_; + } + AUDIO_INFO_LOG( + "SetTimeoutStopThd: timeoutThdFrames_:%{public}u, timeoutThdMs :%{public}u", timeoutThdFrames_, timeoutThdMs); + return SUCCESS; +} + +bool HpaeOutputCluster::IsProcessClusterConnected(HpaeProcessorType sceneType) +{ + return connectedProcessCluster_.find(sceneType) != connectedProcessCluster_.end(); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_plugin_node.cpp b/services/audio_engine/node/src/hpae_plugin_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed58c365d29917c751ba5c70481846d7bfd1a8e0 --- /dev/null +++ b/services/audio_engine/node/src/hpae_plugin_node.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hpae_plugin_node.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaePluginNode::HpaePluginNode(HpaeNodeInfo& nodeInfo) + : HpaeNode(nodeInfo), outputStream_(this), enableProcess_(true), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate), silenceData_(pcmBufferInfo_) + +{ + silenceData_.Reset(); + silenceData_.SetBufferValid(false); +} + +void HpaePluginNode::DoProcess() +{ + HpaePcmBuffer *tempOut = nullptr; + std::vector& preOutputs = inputStream_.ReadPreOutputData(); + // if buffer is not valid, write silence data(invalid) to output + if (enableProcess_ && !preOutputs.empty()) { + tempOut = SignalProcess(preOutputs); + outputStream_.WriteDataToOutput(tempOut); + } else if (!preOutputs.empty()) { + outputStream_.WriteDataToOutput(preOutputs[0]); + } else { + outputStream_.WriteDataToOutput(&silenceData_); + } +} + +bool HpaePluginNode::Reset() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + inputStream_.DisConnect(output); + } + return true; +} + +bool HpaePluginNode::ResetAll() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + std::shared_ptr hpaeNode = preOutput.second; + if (hpaeNode->ResetAll()) { + inputStream_.DisConnect(output); + } + } + return true; +} + +int32_t HpaePluginNode::EnableProcess(bool enable) +{ + enableProcess_ = enable; + return SUCCESS; +} + +bool HpaePluginNode::IsEnableProcess() +{ + return enableProcess_; +} + +std::shared_ptr HpaePluginNode::GetSharedInstance() +{ + return shared_from_this(); +} + +OutputPort* HpaePluginNode::GetOutputPort() +{ + return &outputStream_; +} + +void HpaePluginNode::Connect(const std::shared_ptr>& preNode) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort()); +} + +void HpaePluginNode::DisConnect(const std::shared_ptr>& preNode) +{ + inputStream_.DisConnect(preNode->GetOutputPort()); +} + +size_t HpaePluginNode::GetPreOutNum() +{ + return inputStream_.GetPreOutputNum(); +} + +size_t HpaePluginNode::GetOutputPortNum() +{ + return outputStream_.GetInputNum(); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_process_cluster.cpp b/services/audio_engine/node/src/hpae_process_cluster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f2f145c667683d134e24f463a80fc723d7fe6af --- /dev/null +++ b/services/audio_engine/node/src/hpae_process_cluster.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeProcessCluster" +#endif + +#include + +#include "audio_errors.h" +#include "hpae_process_cluster.h" +#include "hpae_node_common.h" +#include "audio_engine_log.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeProcessCluster::HpaeProcessCluster(HpaeNodeInfo &nodeInfo, HpaeSinkInfo &sinkInfo) + : HpaeNode(nodeInfo), mixerNode_(std::make_shared(nodeInfo)), sinkInfo_(sinkInfo) +{ + if (TransProcessorTypeToSceneType(nodeInfo.sceneType) != "SCENE_EXTRA") { + renderEffectNode_ = std::make_shared(nodeInfo); + } else { + renderEffectNode_ = nullptr; + } +#ifdef ENABLE_HIDUMP_DFX + if (nodeInfo.statusCallback.lock()) { + nodeInfo.nodeName = "HpaeMixerNode"; + nodeInfo.sessionId = 0; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + AUDIO_INFO_LOG("HpaeProcessCluster, HpaeMixerNode id %{public}u ", nodeInfo.nodeId); + mixerNode_->SetNodeInfo(nodeInfo); + if (renderEffectNode_) { + nodeInfo.nodeName = "HpaeRenderEffectNode"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + nodeInfo.sessionId = 0; + renderEffectNode_->SetNodeInfo(nodeInfo); + AUDIO_INFO_LOG("HpaeProcessCluster, HpaeRenderEffectNode id %{public}u ", nodeInfo.nodeId); + } + } +#endif +} + +HpaeProcessCluster::~HpaeProcessCluster() +{ + AUDIO_INFO_LOG("process cluster destroyed, processor scene type is %{public}d", GetSceneType()); + Reset(); +} + +void HpaeProcessCluster::DoProcess() +{ + if (renderEffectNode_ != nullptr) { + renderEffectNode_->DoProcess(); + return; + } + mixerNode_->DoProcess(); +} + +bool HpaeProcessCluster::Reset() +{ + mixerNode_->Reset(); + for (auto converterNode : idConverterMap_) { + converterNode.second->Reset(); + } + for (auto gainNode : idGainMap_) { + gainNode.second->Reset(); + } + if (renderEffectNode_ != nullptr) { + renderEffectNode_->Reset(); + renderEffectNode_ = nullptr; + } + return true; +} + +bool HpaeProcessCluster::ResetAll() +{ + return renderEffectNode_ != nullptr ? renderEffectNode_->ResetAll() : mixerNode_->ResetAll(); +} + +std::shared_ptr HpaeProcessCluster::GetSharedInstance() +{ + if (renderEffectNode_ != nullptr) { + AUDIO_INFO_LOG("HpaeProcessCluster, GetSharedInstance renderEffectNode_ name %{public}s id: %{public}u ", + renderEffectNode_->GetNodeName().c_str(), + renderEffectNode_->GetNodeId()); + return renderEffectNode_; + } + AUDIO_INFO_LOG("HpaeProcessCluster, GetSharedInstance mixerNode_ name %{public}s id: %{public}u ", + mixerNode_->GetNodeName().c_str(), + mixerNode_->GetNodeId()); + return mixerNode_; +} + +OutputPort *HpaeProcessCluster::GetOutputPort() +{ + return renderEffectNode_ != nullptr ? renderEffectNode_->GetOutputPort() : mixerNode_->GetOutputPort(); +} + +int32_t HpaeProcessCluster::GetGainNodeCount() +{ + return idGainMap_.size(); +} + +int32_t HpaeProcessCluster::GetConverterNodeCount() +{ + return idConverterMap_.size(); +} + +int32_t HpaeProcessCluster::GetPreOutNum() +{ + return mixerNode_->GetPreOutNum(); +} + +void HpaeProcessCluster::ConnectMixerNode() +{ + if (renderEffectNode_ != nullptr && renderEffectNode_->GetPreOutNum() == 0) { + renderEffectNode_->Connect(mixerNode_); + AUDIO_INFO_LOG("Process Connect mixerNode_"); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = renderEffectNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(true, renderEffectNode_->GetNodeId(), mixerNode_->GetNodeInfo()); + } +#endif + } + return; +} + +void HpaeProcessCluster::Connect(const std::shared_ptr> &preNode) +{ + HpaeNodeInfo &preNodeInfo = preNode->GetNodeInfo(); + uint32_t sessionId = preNodeInfo.sessionId; + AUDIO_INFO_LOG("HpaeProcessCluster sessionId is %{public}u, streamType is %{public}d, " + "HpaeProcessCluster rate is %{public}u, ch is %{public}u, " + "HpaeProcessCluster preNodeId %{public}u, preNodeName is %{public}s", + preNodeInfo.sessionId, preNodeInfo.streamType, preNodeInfo.samplingRate, preNodeInfo.channels, + preNodeInfo.nodeId, preNodeInfo.nodeName.c_str()); + ConnectMixerNode(); + if (idGainMap_.find(sessionId) == idGainMap_.end()) { + HpaeNodeInfo gainNodeInfo = preNodeInfo; +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + gainNodeInfo.nodeName = "HpaeGainNode"; + gainNodeInfo.nodeId = callBack->OnGetNodeId(); + } +#endif + idGainMap_[sessionId] = std::make_shared(gainNodeInfo); + } + HpaeNodeInfo outNodeInfo = preNodeInfo; + outNodeInfo.frameLen = sinkInfo_.frameLen; + outNodeInfo.samplingRate = sinkInfo_.samplingRate; + outNodeInfo.channels = sinkInfo_.channels; + outNodeInfo.channelLayout = (AudioChannelLayout)sinkInfo_.channelLayout; + outNodeInfo.format = sinkInfo_.format; +#ifdef ENABLE_HIDUMP_DFX + outNodeInfo.nodeName = "HpaeAudioFormatConverterNode"; + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + outNodeInfo.nodeId = callBack->OnGetNodeId(); + } +#endif + idConverterMap_[sessionId] = std::make_shared(preNodeInfo, outNodeInfo); + if (renderEffectNode_ != nullptr) { + idConverterMap_[sessionId]->RegisterCallback(this); + } + idGainMap_[sessionId]->Connect(preNode); + idConverterMap_[sessionId]->Connect(idGainMap_[sessionId]); + mixerNode_->Connect(idConverterMap_[sessionId]); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = mixerNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(true, mixerNode_->GetNodeId(), idConverterMap_[sessionId]->GetNodeInfo()); + callBack->OnNotifyDfxNodeInfo( + true, idConverterMap_[sessionId]->GetNodeId(), idGainMap_[sessionId]->GetNodeInfo()); + callBack->OnNotifyDfxNodeInfo(true, idGainMap_[sessionId]->GetNodeId(), preNodeInfo); + } +#endif +} + +void HpaeProcessCluster::DisConnect(const std::shared_ptr> &preNode) +{ + uint32_t sessionId = preNode->GetNodeInfo().sessionId; + AUDIO_INFO_LOG( + "Process DisConnect sessionId is %{public}u, streamType is %{public}d", + sessionId, preNode->GetNodeInfo().streamType); +#ifdef ENABLE_HIDUMP_DFX + auto callBack = mixerNode_->GetNodeStatusCallback().lock(); + if (callBack != nullptr && idConverterMap_.find(sessionId) != idConverterMap_.end()) { + callBack->OnNotifyDfxNodeInfo(false, idConverterMap_[sessionId]->GetNodeId(), + idConverterMap_[sessionId]->GetNodeInfo()); + } +#endif + if (idConverterMap_.find(sessionId) != idConverterMap_.end()) { + idGainMap_[sessionId]->DisConnect(preNode); + idConverterMap_[sessionId]->DisConnect(idGainMap_[sessionId]); + mixerNode_->DisConnect(idConverterMap_[sessionId]); + idConverterMap_.erase(sessionId); + idGainMap_.erase(sessionId); + AUDIO_INFO_LOG("Process DisConnect Exist converterNode preOutNum is %{public}zu", mixerNode_->GetPreOutNum()); + } + if (renderEffectNode_ != nullptr && mixerNode_->GetPreOutNum() == 0) { + renderEffectNode_->DisConnect(mixerNode_); + AUDIO_INFO_LOG("Process DisConnect mixerNode_"); +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = renderEffectNode_->GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfo(false, renderEffectNode_->GetNodeId(), mixerNode_->GetNodeInfo()); + } +#endif + } +} + +int32_t HpaeProcessCluster::GetEffectNodeInputChannelInfo(uint32_t &channels, uint64_t &channelLayout) +{ + if (renderEffectNode_ == nullptr) { + return ERR_READ_FAILED; + } + int32_t ret = renderEffectNode_->GetExpectedInputChannelInfo(channels, channelLayout); + return ret; +} + +int32_t HpaeProcessCluster::AudioRendererCreate(HpaeNodeInfo &nodeInfo) +{ + if (renderEffectNode_ == nullptr) { + return 0; + } + return renderEffectNode_->AudioRendererCreate(nodeInfo); +} + +int32_t HpaeProcessCluster::AudioRendererStart(HpaeNodeInfo &nodeInfo) +{ + if (renderEffectNode_ == nullptr) { + return 0; + } + return renderEffectNode_->AudioRendererStart(nodeInfo); +} + +int32_t HpaeProcessCluster::AudioRendererStop(HpaeNodeInfo &nodeInfo) +{ + if (renderEffectNode_ == nullptr) { + return 0; + } + return renderEffectNode_->AudioRendererStop(nodeInfo); +} + +int32_t HpaeProcessCluster::AudioRendererRelease(HpaeNodeInfo &nodeInfo) +{ + if (renderEffectNode_ == nullptr) { + return 0; + } + return renderEffectNode_->AudioRendererRelease(nodeInfo); +} + +std::shared_ptr HpaeProcessCluster::GetGainNodeById(uint32_t sessionId) const +{ + auto it = idGainMap_.find(sessionId); + if (it != idGainMap_.end()) { + return it->second; + } + return nullptr; +} + +std::shared_ptr HpaeProcessCluster::GetConverterNodeById(uint32_t sessionId) const +{ + auto it = idConverterMap_.find(sessionId); + if (it != idConverterMap_.end()) { + return it->second; + } + return nullptr; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_render_effect_node.cpp b/services/audio_engine/node/src/hpae_render_effect_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..716028fa22da2b49113503ca3e4be834c051ecca --- /dev/null +++ b/services/audio_engine/node/src/hpae_render_effect_node.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeRenderEffectNode" +#endif + +#include +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "hpae_render_effect_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_effect_chain_manager.h" +#include "audio_effect_map.h" +#include "audio_utils.h" + +static constexpr uint32_t DEFUALT_EFFECT_RATE = 48000; +static constexpr uint32_t DEFAULT_EFFECT_FRAMELEN = 960; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +HpaeRenderEffectNode::HpaeRenderEffectNode(HpaeNodeInfo &nodeInfo) : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo), + // DEFAUT effect out format + pcmBufferInfo_(nodeInfo.channels, DEFAULT_EFFECT_FRAMELEN, DEFUALT_EFFECT_RATE, nodeInfo.channelLayout), + effectOutput_(pcmBufferInfo_), + nodeInfo_(nodeInfo) +{ + const std::unordered_map &audioSupportedSceneTypes = GetSupportedSceneType(); + if (audioSupportedSceneTypes.find(nodeInfo.effectInfo.effectScene) != + audioSupportedSceneTypes.end()) { + sceneType_ = audioSupportedSceneTypes.at(nodeInfo.effectInfo.effectScene); + } + AUDIO_INFO_LOG("render effect node created, scene type: %{public}s", sceneType_.c_str()); +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique( + "HpaeRenderEffectNodeInput_id_" + std::to_string(GetNodeId()) + ".pcm"); + outputPcmDumper_ = std::make_unique( + "HpaeRenderEffectNodeOutput_id_" + std::to_string(GetNodeId()) + ".pcm"); +#endif +} + +HpaePcmBuffer *HpaeRenderEffectNode::SignalProcess(const std::vector &inputs) +{ + AUDIO_DEBUG_LOG("render effect node signal process in"); + if (inputs.empty()) { + AUDIO_WARNING_LOG("HpaeRenderEffectNode inputs size is empty"); + return nullptr; + } + auto rate = "rate[" + std::to_string(inputs[0]->GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(inputs[0]->GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(inputs[0]->GetFrameLen()) + "]"; + Trace trace("[" + sceneType_ + "]HpaeRenderEffectNode::SignalProcess " + rate + ch + len); + + if (AudioEffectChainManager::GetInstance()->GetOffloadEnabled()) { + return inputs[0]; + } + +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumper_) { + inputPcmDumper_->Dump((int8_t *)inputs[0]->GetPcmDataBuffer(), + inputs[0]->GetFrameLen() * sizeof(float) * inputs[0]->GetChannelCount()); + } +#endif + + ReconfigOutputBuffer(); + + auto eBufferAttr = std::make_unique( + inputs[0]->GetPcmDataBuffer(), + effectOutput_.GetPcmDataBuffer(), + static_cast(inputs[0]->GetChannelCount()), + static_cast(inputs[0]->GetFrameLen()), + 0, + 0 + ); + + int32_t ret = AudioEffectChainManager::GetInstance()->ApplyAudioEffectChain(sceneType_, eBufferAttr); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, inputs[0], "apply audio effect chain fail"); + +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_) { + outputPcmDumper_->Dump((int8_t *)effectOutput_.GetPcmDataBuffer(), + effectOutput_.GetFrameLen() * sizeof(float) * effectOutput_.GetChannelCount()); + } +#endif + + return &effectOutput_; +} + +int32_t HpaeRenderEffectNode::AudioRendererCreate(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("notify audio effect when audio renderer create in"); + int32_t ret = CreateAudioEffectChain(nodeInfo); + if (ret != 0) { + AUDIO_WARNING_LOG("create audio effect chain failed, ret: %{public}d", ret); + } + AUDIO_INFO_LOG("notify audio effect when audio renderer create out"); + return SUCCESS; +} + +int32_t HpaeRenderEffectNode::AudioRendererStart(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("notify audio effect when audio renderer start in"); + ModifyAudioEffectChainInfo(nodeInfo, ADD_AUDIO_EFFECT_CHAIN_INFO); + AUDIO_INFO_LOG("notify audio effect when audio renderer start out"); + return SUCCESS; +} + +int32_t HpaeRenderEffectNode::AudioRendererStop(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("notify audio effect when audio renderer stop in"); + ModifyAudioEffectChainInfo(nodeInfo, REMOVE_AUDIO_EFFECT_CHAIN_INFO); + AUDIO_INFO_LOG("notify audio effect when audio renderer stop out"); + return SUCCESS; +} + +int32_t HpaeRenderEffectNode::AudioRendererRelease(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("notify audio effect when audio renderer release in"); + int32_t ret = ReleaseAudioEffectChain(nodeInfo); + if (ret != 0) { + AUDIO_WARNING_LOG("release audio effect chain failed, ret: %{public}d", ret); + } + ModifyAudioEffectChainInfo(nodeInfo, REMOVE_AUDIO_EFFECT_CHAIN_INFO); + AUDIO_INFO_LOG("notify audio effect when audio renderer release out"); + return SUCCESS; +} + +int32_t HpaeRenderEffectNode::CreateAudioEffectChain(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("Create Audio Effect Chain In, sessionID is %{public}u, sceneType is %{public}d", + nodeInfo.sessionId, nodeInfo.effectInfo.effectScene); + // todo: deal with remote case + // todo: if boot music, do not create audio effect + AudioEffectChainManager *audioEffectChainManager = AudioEffectChainManager::GetInstance(); + CHECK_AND_RETURN_RET_LOG(audioEffectChainManager != nullptr, ERR_INVALID_HANDLE, "null audioEffectChainManager"); + std::string sceneType = "EFFECT_NONE"; + const std::unordered_map &audioSupportedSceneTypes = GetSupportedSceneType(); + if (audioSupportedSceneTypes.find(nodeInfo.effectInfo.effectScene) != + audioSupportedSceneTypes.end()) { + sceneType = audioSupportedSceneTypes.at(nodeInfo.effectInfo.effectScene); + } + // todo: could be removed + if (!audioEffectChainManager->CheckAndAddSessionID(std::to_string(nodeInfo.sessionId))) { + return SUCCESS; + } + audioEffectChainManager->UpdateSceneTypeList(sceneType, ADD_SCENE_TYPE); + // todo: should be considered + if (audioEffectChainManager->GetOffloadEnabled()) { + return SUCCESS; + } + int32_t ret = audioEffectChainManager->CreateAudioEffectChainDynamic(sceneType); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "create effect chain fail"); + AUDIO_INFO_LOG("Create Audio Effect Chain Success, sessionID is %{public}u, sceneType is %{public}d", + nodeInfo.sessionId, nodeInfo.effectInfo.effectScene); + return SUCCESS; +} + +int32_t HpaeRenderEffectNode::ReleaseAudioEffectChain(HpaeNodeInfo &nodeInfo) +{ + AUDIO_INFO_LOG("Release Audio Effect Chain In, sessionID is %{public}u, sceneType is %{public}d", + nodeInfo.sessionId, nodeInfo.effectInfo.effectScene); + // todo: deal with remote case + // todo: if boot music, do not release audio effect + AudioEffectChainManager *audioEffectChainManager = AudioEffectChainManager::GetInstance(); + CHECK_AND_RETURN_RET_LOG(audioEffectChainManager != nullptr, ERR_INVALID_HANDLE, "null audioEffectChainManager"); + std::string sceneType = "EFFECT_NONE"; + const std::unordered_map &audioSupportedSceneTypes = GetSupportedSceneType(); + if (audioSupportedSceneTypes.find(nodeInfo.effectInfo.effectScene) != + audioSupportedSceneTypes.end()) { + sceneType = audioSupportedSceneTypes.at(nodeInfo.effectInfo.effectScene); + } + // todo: could be removed + if (!audioEffectChainManager->CheckAndRemoveSessionID(std::to_string(nodeInfo.sessionId))) { + return SUCCESS; + } + audioEffectChainManager->UpdateSceneTypeList(sceneType, REMOVE_SCENE_TYPE); + // todo: should be considered + if (audioEffectChainManager->GetOffloadEnabled()) { + return SUCCESS; + } + int32_t ret = audioEffectChainManager->ReleaseAudioEffectChainDynamic(sceneType); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "release effect chain fail"); + AUDIO_INFO_LOG("Release Audio Effect Chain Success, sessionID is %{public}u, sceneType is %{public}d", + nodeInfo.sessionId, nodeInfo.effectInfo.effectScene); + return SUCCESS; +} + +void HpaeRenderEffectNode::ModifyAudioEffectChainInfo(HpaeNodeInfo &nodeInfo, + ModifyAudioEffectChainInfoReason reason) +{ + std::string sessionID = std::to_string(nodeInfo.sessionId); + std::string sceneType = "EFFECT_NONE"; + const std::unordered_map &audioSupportedSceneTypes = GetSupportedSceneType(); + if (audioSupportedSceneTypes.find(nodeInfo.effectInfo.effectScene) != + audioSupportedSceneTypes.end()) { + sceneType = audioSupportedSceneTypes.at(nodeInfo.effectInfo.effectScene); + } + int32_t ret = 0; + switch (reason) { + case ADD_AUDIO_EFFECT_CHAIN_INFO: { + SessionEffectInfo info; + info.sceneMode = std::to_string(nodeInfo.effectInfo.effectMode); + info.sceneType = sceneType; + info.channels = static_cast(nodeInfo.channels); + info.channelLayout = nodeInfo.channelLayout; + info.streamUsage = nodeInfo.effectInfo.streamUsage; + info.systemVolumeType = nodeInfo.effectInfo.volumeType; + ret = AudioEffectChainManager::GetInstance()->SessionInfoMapAdd(sessionID, info); + break; + } + case REMOVE_AUDIO_EFFECT_CHAIN_INFO: + ret = AudioEffectChainManager::GetInstance()->SessionInfoMapDelete(sceneType, sessionID); + break; + default: + break; + } + CHECK_AND_RETURN_LOG(ret == SUCCESS, "modify session info failed"); + UpdateAudioEffectChainInfo(nodeInfo); +} + +void HpaeRenderEffectNode::UpdateAudioEffectChainInfo(HpaeNodeInfo &nodeInfo) +{ + AudioEffectChainManager *audioEffectChainManager = AudioEffectChainManager::GetInstance(); + CHECK_AND_RETURN_LOG(audioEffectChainManager != nullptr, "null audioEffectChainManager"); + std::string sceneType = "EFFECT_NONE"; + const std::unordered_map &audioSupportedSceneTypes = GetSupportedSceneType(); + if (audioSupportedSceneTypes.find(nodeInfo.effectInfo.effectScene) != + audioSupportedSceneTypes.end()) { + sceneType = audioSupportedSceneTypes.at(nodeInfo.effectInfo.effectScene); + } + audioEffectChainManager->UpdateMultichannelConfig(sceneType); + audioEffectChainManager->EffectVolumeUpdate(); + audioEffectChainManager->UpdateDefaultAudioEffect(); + audioEffectChainManager->UpdateStreamUsage(); +} + +void HpaeRenderEffectNode::ReconfigOutputBuffer() +{ + uint32_t channels = static_cast(nodeInfo_.channels); + uint64_t channelLayout = nodeInfo_.channelLayout; + int32_t ret = AudioEffectChainManager::GetInstance()->GetOutputChannelInfo(sceneType_, channels, channelLayout); + if (ret != SUCCESS || channels == 0 || channelLayout == 0) { + AUDIO_WARNING_LOG("output channel info incorrect, scene type: %{public}s, " + "channels: %{public}u, channelLayout: %{public}" PRIu64, sceneType_.c_str(), channels, channelLayout); + } else if (static_cast(nodeInfo_.channels) != channels || + static_cast(nodeInfo_.channelLayout) != channelLayout) { + AUDIO_INFO_LOG("output channel info changed, scene type: %{public}s, " + "channels change from %{public}u to %{public}u, " + "channelLayout change from %{public}" PRIu64 " to %{public}" PRIu64, + sceneType_.c_str(), nodeInfo_.channels, channels, nodeInfo_.channelLayout, channelLayout); + nodeInfo_.channels = static_cast(channels); + nodeInfo_.channelLayout = static_cast(channelLayout); + PcmBufferInfo pcmBufferInfo = PcmBufferInfo(channels, DEFAULT_EFFECT_FRAMELEN, + DEFUALT_EFFECT_RATE, channelLayout, effectOutput_.GetFrames(), effectOutput_.IsMultiFrames()); + effectOutput_.ReConfig(pcmBufferInfo); + nodeInfo_.channels = (AudioChannel)channels; + nodeInfo_.channelLayout = (AudioChannelLayout)channelLayout; + nodeInfo_.samplingRate = (AudioSamplingRate)DEFUALT_EFFECT_RATE; + nodeInfo_.frameLen = (uint32_t)DEFAULT_EFFECT_FRAMELEN; +#ifdef ENABLE_HIDUMP_DFX + if (auto callBack = GetNodeStatusCallback().lock()) { + callBack->OnNotifyDfxNodeInfoChanged(GetNodeId(), nodeInfo_); + } +#endif + } +} + +int32_t HpaeRenderEffectNode::GetExpectedInputChannelInfo(uint32_t &channels, uint64_t &channelLayout) +{ + return AudioEffectChainManager::GetInstance()->ReturnEffectChannelInfo(sceneType_, channels, channelLayout); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_resample_node.cpp b/services/audio_engine/node/src/hpae_resample_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5794d284bfc23a1613b0e4599f80233ca8c8122 --- /dev/null +++ b/services/audio_engine/node/src/hpae_resample_node.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeResampleNode" +#endif + +#include +#include +#include +#include "hpae_resample_node.h" +#include "hpae_pcm_buffer.h" +#include "audio_engine_log.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr int REASAMPLE_QUAILTY = 5; +HpaeResampleNode::HpaeResampleNode(HpaeNodeInfo &preNodeInfo, HpaeNodeInfo &nodeInfo, ResamplerType type) + : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate), + resampleOuput_(pcmBufferInfo_), preNodeInfo_(preNodeInfo), tempOuput_(preNodeInfo.channels * nodeInfo.frameLen) +{ + if (type == ResamplerType::PRORESAMPLER) { + resampler_ = std::make_unique(preNodeInfo_.samplingRate, nodeInfo.samplingRate, + preNodeInfo_.channels, REASAMPLE_QUAILTY); + } +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique("HpaeResampleNodeInput1_id_" + + std::to_string(GetSessionId()) + "_ch_" + std::to_string(preNodeInfo_.channels) + + "_rate_" + std::to_string(preNodeInfo_.samplingRate) + "_scene_" + + std::to_string(HpaeNode::GetSceneType()) + "_" + GetTime() + ".pcm"); + outputPcmDumper_ = std::make_unique("HpaeResampleNodeOutput1_id_" + + std::to_string(GetSessionId()) + "_ch_" + std::to_string(GetChannelCount()) + + "_rate_" + std::to_string(GetSampleRate()) + "_scene_" + + std::to_string(HpaeNode::GetSceneType())+ "_" + GetTime() + ".pcm"); +#endif +} + +HpaeResampleNode::HpaeResampleNode(HpaeNodeInfo &preNodeInfo, HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), HpaePluginNode(nodeInfo), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate), + resampleOuput_(pcmBufferInfo_), preNodeInfo_(preNodeInfo), tempOuput_(preNodeInfo.channels * nodeInfo.frameLen) +{ // use ProResampler as default + resampler_ = std::make_unique(preNodeInfo_.samplingRate, nodeInfo.samplingRate, + preNodeInfo_.channels, REASAMPLE_QUAILTY); + +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique("HpaeResampleNodeInput1_id_" + + std::to_string(HpaeNode::GetSessionId()) + "_ch_" + std::to_string(preNodeInfo_.channels) + "_rate_" + + std::to_string(preNodeInfo_.samplingRate) + "_scene_" + std::to_string(HpaeNode::GetSceneType())+".pcm"); + + outputPcmDumper_ = std::make_unique("HpaeResampleNodeOutput1_id_" + + std::to_string(HpaeNode::GetSessionId()) + "_ch_" + std::to_string(HpaeNode::GetChannelCount()) + + "_rate_" + std::to_string(HpaeNode::GetSampleRate()) + + "_scene_"+ std::to_string(HpaeNode::GetSceneType())+".pcm"); +#endif + AUDIO_INFO_LOG("input rate %{public}u, output rate %{public}u", preNodeInfo_.samplingRate, nodeInfo.samplingRate); + AUDIO_INFO_LOG("input SessionId %{public}u, output streamType %{public}u", HpaeNode::GetSessionId(), + nodeInfo.streamType); + AUDIO_INFO_LOG("input ch %{public}u, output ch %{public}u", preNodeInfo_.channels, HpaeNode::GetChannelCount()); +} + +bool HpaeResampleNode::Reset() +{ + if (resampler_ == nullptr) { + AUDIO_WARNING_LOG("resampler_ is nullptr, SessionId:%{public}d", GetSessionId()); + return false; + } + resampler_->Reset(); + return true; +} + +HpaePcmBuffer *HpaeResampleNode::SignalProcess(const std::vector &inputs) +{ + Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeResampleNode::SignalProcess"); + if (inputs.empty()) { + AUDIO_WARNING_LOG("HpaeResampleNode inputs size is empty, SessionId:%{public}d", GetSessionId()); + return nullptr; + } + if (inputs.size() != 1) { + AUDIO_WARNING_LOG("error inputs size is not eqaul to 1, SessionId:%{public}d", GetSessionId()); + } + if (resampler_ == nullptr) { + return &silenceData_; + } + resampleOuput_.Reset(); + uint32_t inputFrameLen = preNodeInfo_.frameLen; + uint32_t outputFrameLen = GetFrameLen(); + float *srcData = (*(inputs[0])).GetPcmDataBuffer(); + float *dstData = tempOuput_.data(); + if (preNodeInfo_.channels == GetChannelCount()) { + dstData = resampleOuput_.GetPcmDataBuffer(); + } +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_->CheckAndReopenHandlde(); + if (inputPcmDumper_ != nullptr) { + inputPcmDumper_->Dump((int8_t *)(srcData), (inputFrameLen * sizeof(float) * preNodeInfo_.channels)); + } +#endif + ResampleProcess(srcData, inputFrameLen, dstData, outputFrameLen); + return &resampleOuput_; +} + +void HpaeResampleNode::ResampleProcess(float *srcData, uint32_t inputFrameLen, float *dstData, uint32_t outputFrameLen) +{ + resampler_->Process(srcData, &inputFrameLen, dstData, &outputFrameLen); + int32_t addZeroLen = GetFrameLen() - outputFrameLen > 0 ? GetFrameLen() - outputFrameLen : 0; + + if (preNodeInfo_.channels == GetChannelCount()) { +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_->CheckAndReopenHandlde(); + if (outputPcmDumper_ != nullptr) { + outputPcmDumper_->Dump( + (int8_t *)(resampleOuput_.GetPcmDataBuffer()), GetFrameLen() * sizeof(float) * GetChannelCount()); + } +#endif + return; + } + + float *targetData = resampleOuput_.GetPcmDataBuffer(); + size_t targetChannels = GetChannelCount(); + for (int32_t i = 0; i < (int32_t)outputFrameLen; ++i) { + for (int32_t ch = 0; ch < (int32_t)targetChannels; ++ch) { + size_t leftChIndex = std::min(ch, (preNodeInfo_.channels - 1)); + if (i < addZeroLen) { + targetData[i * targetChannels + ch] = 0; + } else { + targetData[i * targetChannels + ch] = + dstData[(i - addZeroLen) * preNodeInfo_.channels + leftChIndex]; + } + } + } + +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_->CheckAndReopenHandlde(); + if (outputPcmDumper_ != nullptr) { + outputPcmDumper_->Dump( + (int8_t *)(resampleOuput_.GetPcmDataBuffer()), GetFrameLen() * sizeof(float) * GetChannelCount()); + } +#endif +} + +void HpaeResampleNode::ConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort(nodeInfo)); + resampleOuput_.SetSourceBufferType(preNode->GetOutputPortBufferType(nodeInfo)); +} + +void HpaeResampleNode::DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.DisConnect(preNode->GetOutputPort(nodeInfo, true)); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_sink_input_node.cpp b/services/audio_engine/node/src/hpae_sink_input_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..57d8c3aed4a1c546d3b7ea985111fcb831fa535f --- /dev/null +++ b/services/audio_engine/node/src/hpae_sink_input_node.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSinkInputNode" +#endif + +#include "hpae_sink_input_node.h" +#include +#include "hpae_format_convert.h" +#include "hpae_node_common.h" +#include "audio_engine_log.h" +#include "audio_errors.h" +#include "audio_utils.h" +#include "cinttypes" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +static constexpr int32_t DEFAULT_BUFFER_MICROSECOND = 20000000; +static constexpr uint64_t AUDIO_NS_PER_S = 1000000000; + +HpaeSinkInputNode::HpaeSinkInputNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), + pcmBufferInfo_(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, (uint64_t)nodeInfo.channelLayout), + inputAudioBuffer_(pcmBufferInfo_), outputStream_(this), + interleveData_(nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)), framesWritten_(0), + totalFrames_(0) +{ + AUDIO_INFO_LOG("sinkinput sessionId %{public}d, channelcount %{public}d, channelLayout %{public}" PRIu64 ", " + "frameLen %{public}d", nodeInfo.sessionId, inputAudioBuffer_.GetChannelCount(), + inputAudioBuffer_.GetChannelLayout(), inputAudioBuffer_.GetFrameLen()); + + handleTimeModel_ = std::make_unique(); + handleTimeModel_->ConfigSampleRate(nodeInfo.samplingRate); +#ifdef ENABLE_HOOK_PCM + inputPcmDumper_ = std::make_unique( + "HpaeSinkInputNode_id_" + std::to_string(GetSessionId()) + "_ch_" + std::to_string(GetChannelCount()) + + "_rate_" + std::to_string(GetSampleRate()) + "_bit_" + std::to_string(GetBitWidth()) + ".pcm"); +#endif + if (nodeInfo.historyFrameCount > 0) { + PcmBufferInfo pcmInfo = PcmBufferInfo{ + nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout, + nodeInfo.historyFrameCount, true}; + historyBuffer_ = std::make_unique(pcmInfo); + AUDIO_INFO_LOG("HpaeSinkInputNode::historybuffer created"); + } else { + historyBuffer_ = nullptr; + } +} + +HpaeSinkInputNode::~HpaeSinkInputNode() +{} + +void HpaeSinkInputNode::CheckAndDestoryHistoryBuffer() +{ + HpaeNodeInfo nodeInfo = GetNodeInfo(); + // historyBuffer_ has no data, check if historyFrameCount is 0 and distory it + if (nodeInfo.historyFrameCount == 0) { + if (historyBuffer_) { + AUDIO_INFO_LOG("HpaeSinkInputNode::historyBuffer_ useless, distory it"); + } + historyBuffer_ = nullptr; + } else if (historyBuffer_ == nullptr) { // this case need to create historyBuffer_ + PcmBufferInfo pcmInfo = PcmBufferInfo{ + nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout, + nodeInfo.historyFrameCount, true}; + historyBuffer_ = std::make_unique(pcmInfo); + AUDIO_INFO_LOG("HpaeSinkInputNode::historybuffer created"); + } +} + +int32_t HpaeSinkInputNode::GetDataFromSharedBuffer() +{ + streamInfo_ = {.framesWritten = framesWritten_.load(), + .inputData = interleveData_.data(), + .requestDataLen = interleveData_.size(), + .deviceClass = GetDeviceClass(), + .deviceNetId = GetDeviceNetId(), + .needData = !(historyBuffer_ && historyBuffer_->GetCurFrames())}; + GetCurrentPosition(streamInfo_.framePosition, streamInfo_.timestamp); + return writeCallback_.lock()->OnStreamData(streamInfo_); +} + +std::string HpaeSinkInputNode::GetTraceInfo() +{ + auto rate = "rate[" + std::to_string(GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(GetFrameLen()) + "]_"; + auto format = "bit[" + std::to_string(GetBitWidth()) + "]"; + return rate + ch + len + format; +} + +void HpaeSinkInputNode::DoProcess() +{ + Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeSinkInputNode::DoProcess " + + GetTraceInfo()); + CHECK_AND_RETURN_LOG( + writeCallback_.lock(), "HpaeSinkInputNode writeCallback_ is nullptr, SessionId:%{public}d", GetSessionId()); + + auto nodeCallback = GetNodeStatusCallback().lock(); + if (nodeCallback) { + nodeCallback->OnRequestLatency(GetSessionId(), streamInfo_.latency); + } + + int32_t ret = GetDataFromSharedBuffer(); + // if historyBuffer has enough data, write to outputStream + if (!streamInfo_.needData) { + historyBuffer_->GetFrameData(inputAudioBuffer_); + outputStream_.WriteDataToOutput(&inputAudioBuffer_); + return; + } + CheckAndDestoryHistoryBuffer(); + if (nodeCallback && ret) { + nodeCallback->OnNodeStatusUpdate(GetSessionId(), OPERATION_UNDERFLOW); + if (isDrain_) { + AUDIO_INFO_LOG("OnNodeStatusUpdate Drain sessionId:%{public}u", GetSessionId()); + nodeCallback->OnNodeStatusUpdate(GetSessionId(), OPERATION_DRAINED); + isDrain_ = false; + } + } + inputAudioBuffer_.SetBufferValid(ret ? false : true); + +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumper_ != nullptr && inputAudioBuffer_.IsValid()) { + inputPcmDumper_->CheckAndReopenHandlde(); + inputPcmDumper_->Dump(static_cast(interleveData_.data()), + GetChannelCount() * GetFrameLen() * GetSizeFromFormat(GetBitWidth())); + } +#endif + + ConvertToFloat( + GetBitWidth(), GetChannelCount() * GetFrameLen(), interleveData_.data(), inputAudioBuffer_.GetPcmDataBuffer()); + if (ret != 0) { + AUDIO_WARNING_LOG("request data is not enough sessionId:%{public}u", GetSessionId()); + memset_s(inputAudioBuffer_.GetPcmDataBuffer(), inputAudioBuffer_.Size(), 0, inputAudioBuffer_.Size()); + } else { + totalFrames_ = totalFrames_ + GetFrameLen(); + framesWritten_.store(totalFrames_); + if (historyBuffer_) { + historyBuffer_->StoreFrameData(inputAudioBuffer_); + } + } + outputStream_.WriteDataToOutput(&inputAudioBuffer_); +} + +bool HpaeSinkInputNode::Reset() +{ + return true; +} + +bool HpaeSinkInputNode::ResetAll() +{ + return true; +} + +std::shared_ptr HpaeSinkInputNode::GetSharedInstance() +{ + return shared_from_this(); +} + +OutputPort *HpaeSinkInputNode::GetOutputPort() +{ + return &outputStream_; +} + +bool HpaeSinkInputNode::RegisterWriteCallback(const std::weak_ptr &callback) +{ + writeCallback_ = callback; + return true; +} +// reset historyBuffer +void HpaeSinkInputNode::Flush() +{ + if (GetNodeInfo().historyFrameCount == 0) { + historyBuffer_ = nullptr; + } else if (historyBuffer_ && historyBuffer_->GetFrames() == GetNodeInfo().historyFrameCount) { + historyBuffer_->Reset(); + } else { + HpaeNodeInfo nodeInfo = GetNodeInfo(); + PcmBufferInfo pcmInfo = PcmBufferInfo{ + nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate, nodeInfo.channelLayout, + nodeInfo.historyFrameCount, true}; + historyBuffer_ = std::make_unique(pcmInfo); + } +} + +bool HpaeSinkInputNode::Drain() +{ + isDrain_ = true; + return true; +} + +int32_t HpaeSinkInputNode::SetState(RendererState renderState) +{ + state_ = renderState; + return SUCCESS; +} + +RendererState HpaeSinkInputNode::GetState() +{ + return state_; +} + +uint64_t HpaeSinkInputNode::GetFramesWritten() +{ + return framesWritten_.load(); +} + +bool HpaeSinkInputNode::GetAudioTime(uint64_t &framePos, int64_t &sec, int64_t &nanoSec) +{ + framePos = GetFramesWritten(); + int64_t time = handleTimeModel_->GetTimeOfPos(framePos); + int64_t deltaTime = DEFAULT_BUFFER_MICROSECOND; // note: 20ms + time += deltaTime; + sec = time / AUDIO_NS_PER_S; + nanoSec = time % AUDIO_NS_PER_S; + return true; +} + +int32_t HpaeSinkInputNode::GetCurrentPosition(uint64_t &framePosition, uint64_t ×tamp) +{ + int64_t timeSec = 0; + int64_t timeNsec = 0; + bool ret = GetAudioTime(framePosition, timeSec, timeNsec); + if (historyBuffer_) { + framePosition = framePosition > historyBuffer_->GetCurFrames() * GetNodeInfo().frameLen + ? framePosition - historyBuffer_->GetCurFrames() * GetNodeInfo().frameLen + : 0; + } + CHECK_AND_RETURN_RET_LOG(ret, ERROR, "GetAudioTime error"); + timespec tm{}; + clock_gettime(CLOCK_MONOTONIC, &tm); + timestamp = static_cast(tm.tv_sec) * AUDIO_NS_PER_S + static_cast(tm.tv_nsec); + return SUCCESS; +} + +int32_t HpaeSinkInputNode::RewindHistoryBuffer(uint64_t rewindTime) +{ + CHECK_AND_RETURN_RET_LOG(historyBuffer_, ERROR, "historyBuffer_ is nullptr"); + AUDIO_INFO_LOG("HpaeSinkInputNode::rewind %{public}zu frames", ConvertUsToFrameCount(rewindTime, GetNodeInfo())); + return historyBuffer_->RewindBuffer(ConvertUsToFrameCount(rewindTime, GetNodeInfo())); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_sink_output_node.cpp b/services/audio_engine/node/src/hpae_sink_output_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fa2988832538bd5e5f81fac509a33bce04eb179 --- /dev/null +++ b/services/audio_engine/node/src/hpae_sink_output_node.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSinkOutputNode" +#endif + +#include +#include "audio_errors.h" +#include +#include "hpae_format_convert.h" +#include "audio_engine_log.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +namespace { +constexpr uint32_t SLEEP_TIME_IN_US = 2000; +} + +HpaeSinkOutputNode::HpaeSinkOutputNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), + renderFrameData_(nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)), + interleveData_(nodeInfo.frameLen * nodeInfo.channels) +{ +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique("HpaeSinkOutputNode_Out_bit_" + std::to_string(GetBitWidth()) + + "_ch_" + std::to_string(GetChannelCount()) + "_rate_" + + std::to_string(GetSampleRate()) + ".pcm"); + AUDIO_INFO_LOG("HpaeSinkOutputNode name is %{public}s", sinkOutAttr_.adapterName.c_str()); +#endif +} + +void HpaeSinkOutputNode::HandleRemoteTiming() +{ + remoteTimer_.Stop(); + uint64_t remoteElapsed = remoteTimer_.Elapsed(); + auto now = std::chrono::high_resolution_clock::now(); + remoteTimePoint_ += std::chrono::milliseconds(20); // 20ms frameLen, need optimize + std::this_thread::sleep_for(remoteSleepTime_); + if (remoteTimePoint_ > now + std::chrono::milliseconds(remoteElapsed)) { + remoteSleepTime_ = std::chrono::duration_cast(remoteTimePoint_ - now) - + std::chrono::milliseconds(remoteElapsed); + } else { + remoteSleepTime_ = std::chrono::milliseconds(0); + } + remoteTimer_.Start(); +} + +void HpaeSinkOutputNode::DoProcess() +{ + auto rate = "rate[" + std::to_string(GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(GetFrameLen()) + "]_"; + auto format = "bit[" + std::to_string(GetBitWidth()) + "]"; + Trace trace("HpaeSinkOutputNode::DoProcess " + rate + ch + len + format); + if (audioRendererSink_ == nullptr) { + AUDIO_WARNING_LOG("audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId()); + return; + } + std::vector &outputVec = inputStream_.ReadPreOutputData(); + if (outputVec.empty()) { + return; + } + HpaePcmBuffer *outputData = outputVec.front(); + ConvertFromFloat( + GetBitWidth(), GetChannelCount() * GetFrameLen(), outputData->GetPcmDataBuffer(), renderFrameData_.data()); + uint64_t writeLen = 0; + char *renderFrameData = (char *)renderFrameData_.data(); + +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); + intervalTimer_.Stop(); + outputPcmDumper_->CheckAndReopenHandlde(); + if (outputPcmDumper_) { + outputPcmDumper_->Dump((int8_t *)renderFrameData, renderFrameData_.size()); + } +#endif + if (GetDeviceClass() == "remote") { + HandleRemoteTiming(); + } + auto ret = audioRendererSink_->RenderFrame(*renderFrameData, renderFrameData_.size(), writeLen); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("HpaeSinkOutputNode: RenderFrame failed"); + usleep(SLEEP_TIME_IN_US); + return; + } +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t elapsed = timer.Elapsed(); + AUDIO_DEBUG_LOG("HpaeSinkOutputNode :name %{public}s, RenderFrame elapsed time: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), + elapsed); + intervalTimer_.Start(); +#endif + return; +} + +const char *HpaeSinkOutputNode::GetRenderFrameData(void) +{ + return renderFrameData_.data(); +} + +bool HpaeSinkOutputNode::Reset() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + inputStream_.DisConnect(output); + } + return true; +} + +bool HpaeSinkOutputNode::ResetAll() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + std::shared_ptr hpaeNode = preOutput.second; + if (hpaeNode->ResetAll()) { + inputStream_.DisConnect(output); + } + } + return true; +} + +void HpaeSinkOutputNode::Connect(const std::shared_ptr> &preNode) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort()); +} + +void HpaeSinkOutputNode::DisConnect(const std::shared_ptr> &preNode) +{ + inputStream_.DisConnect(preNode->GetOutputPort()); +} + +int32_t HpaeSinkOutputNode::GetRenderSinkInstance(std::string deviceClass, std::string deviceNetId) +{ + if (deviceNetId.empty()) { + renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass(deviceClass, HDI_ID_INFO_DEFAULT, true); + } else { + renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass(deviceClass, deviceNetId, true); + } + audioRendererSink_ = HdiAdapterManager::GetInstance().GetRenderSink(renderId_, true); + if (audioRendererSink_ == nullptr) { + AUDIO_ERR_LOG("get sink fail, deviceClass: %{public}s, deviceNetId: %{public}s, renderId_: %{public}u", + deviceClass.c_str(), + deviceNetId.c_str(), + renderId_); + HdiAdapterManager::GetInstance().ReleaseId(renderId_); + return ERROR; + } + return SUCCESS; +} + +int32_t HpaeSinkOutputNode::RenderSinkInit(IAudioSinkAttr &attr) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + + sinkOutAttr_ = attr; + state_ = RENDERER_PREPARED; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + int32_t ret = audioRendererSink_->Init(attr); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkInit Elapsed: %{public}" PRIu64 + " ms ret: %{public}d", + sinkOutAttr_.adapterName.c_str(), + interval, + ret); + std::string adapterName = sinkOutAttr_.adapterName; + outputPcmDumper_ = std::make_unique( + "HpaeSinkOutputNode_" + adapterName + "_bit_" + std::to_string(GetBitWidth()) + "_ch_" + + std::to_string(GetChannelCount()) + "_rate_" + std::to_string(GetSampleRate()) + ".pcm"); +#endif + return ret; +} + +int32_t HpaeSinkOutputNode::RenderSinkDeInit(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + state_ = RENDERER_INVALID; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + audioRendererSink_->DeInit(); + audioRendererSink_ = nullptr; + HdiAdapterManager::GetInstance().ReleaseId(renderId_); +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkDeInit Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), + interval); +#endif + return SUCCESS; +} + +int32_t HpaeSinkOutputNode::RenderSinkFlush(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + return audioRendererSink_->Flush(); +} + +int32_t HpaeSinkOutputNode::RenderSinkPause(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + audioRendererSink_->Pause(); + state_ = RENDERER_PAUSED; + return SUCCESS; +} + +int32_t HpaeSinkOutputNode::RenderSinkReset(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + return audioRendererSink_->Reset(); +} + +int32_t HpaeSinkOutputNode::RenderSinkResume(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + int32_t ret = audioRendererSink_->Resume(); + if (ret != SUCCESS) { + return ret; + } + state_ = RENDERER_RUNNING; + return SUCCESS; +} + +int32_t HpaeSinkOutputNode::RenderSinkStart(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + + int32_t ret; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + ret = audioRendererSink_->Start(); + if (ret != SUCCESS) { + return ERROR; + } +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkStart Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), + interval); +#endif + state_ = RENDERER_RUNNING; + if (GetDeviceClass() == "remote") { + remoteTimePoint_ = std::chrono::high_resolution_clock::now(); + } + return SUCCESS; +} + +int32_t HpaeSinkOutputNode::RenderSinkStop(void) +{ + if (audioRendererSink_ == nullptr) { + return ERROR; + } + int32_t ret; +#ifdef ENABLE_HOOK_PCM + HighResolutionTimer timer; + timer.Start(); +#endif + ret = audioRendererSink_->Stop(); + if (ret != SUCCESS) { + return ret; + } +#ifdef ENABLE_HOOK_PCM + timer.Stop(); + uint64_t interval = timer.Elapsed(); + AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkStop Elapsed: %{public}" PRIu64 " ms", + sinkOutAttr_.adapterName.c_str(), interval); +#endif + state_ = RENDERER_STOPPED; + return SUCCESS; +} + +RendererState HpaeSinkOutputNode::GetSinkState(void) +{ + return state_; +} + +size_t HpaeSinkOutputNode::GetPreOutNum() +{ + return inputStream_.GetPreOutputNum(); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/node/src/hpae_source_input_cluster.cpp b/services/audio_engine/node/src/hpae_source_input_cluster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c50c245ddbaa79900c5972c63fd9cb0b5fe180a --- /dev/null +++ b/services/audio_engine/node/src/hpae_source_input_cluster.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSourceInputCluster" +#endif + +#include "hpae_source_input_cluster.h" +#include "hpae_node_common.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +static std::string TransSourceBufferTypeToString(HpaeSourceBufferType &type) +{ + if (type == HPAE_SOURCE_BUFFER_TYPE_MIC) { + return "MIC"; + } else if (type == HPAE_SOURCE_BUFFER_TYPE_EC) { + return "EC"; + } else if (type == HPAE_SOURCE_BUFFER_TYPE_MICREF) { + return "MICREF"; + } + return "DEFAULT"; +} + +HpaeSourceInputCluster::HpaeSourceInputCluster(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), sourceInputNode_(std::make_shared(nodeInfo)) +{ +#ifdef ENABLE_HIDUMP_DFX + if (nodeInfo.statusCallback.lock()) { + nodeInfo.nodeName = "HpaeSourceInputNode[" + TransSourceBufferTypeToString(nodeInfo.sourceBufferType) + "]"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + sourceInputNode_->SetNodeInfo(nodeInfo); + nodeInfo.statusCallback.lock()->OnNotifyDfxNodeInfo(true, 0, nodeInfo); + } +#endif +} + +HpaeSourceInputCluster::HpaeSourceInputCluster(std::vector &nodeInfos) + : HpaeNode(*nodeInfos.begin()), sourceInputNode_(std::make_shared(nodeInfos)) +{ +#ifdef ENABLE_HIDUMP_DFX + auto nodeInfo = *nodeInfos.begin(); + nodeInfo.nodeName = "HpaeSourceInputNode[" + TransSourceBufferTypeToString(nodeInfo.sourceBufferType) + "]"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + sourceInputNode_->SetNodeInfo(nodeInfo); + nodeInfo.statusCallback.lock()->OnNotifyDfxNodeInfo(true, 0, nodeInfo); +#endif +} + +HpaeSourceInputCluster::~HpaeSourceInputCluster() +{ + Reset(); +} + +void HpaeSourceInputCluster::DoProcess() +{ +} + +bool HpaeSourceInputCluster::Reset() +{ + for (auto fmtConverterNode : fmtConverterNodeMap_) { + fmtConverterNode.second->Reset(); + } + sourceInputNode_->Reset(); + return true; +} + +bool HpaeSourceInputCluster::ResetAll() +{ + for (auto fmtConverterNode : fmtConverterNodeMap_) { + fmtConverterNode.second->ResetAll(); + } + sourceInputNode_->ResetAll(); + return true; +} + +std::shared_ptr HpaeSourceInputCluster::GetSharedInstance() +{ + return sourceInputNode_; +} + +std::shared_ptr HpaeSourceInputCluster::GetSharedInstance(HpaeNodeInfo &nodeInfo) +{ + // todo: change function name + std::string nodeKey = TransHpaeResampleNodeInfoToStringKey(nodeInfo); + std::string inputNodeKey = TransHpaeResampleNodeInfoToStringKey(GetNodeInfo()); + AUDIO_INFO_LOG("sourceinput nodekey:[%{public}s] effectnodekey:[%{public}s]", + inputNodeKey.c_str(), nodeKey.c_str()); + if (CheckHpaeNodeInfoIsSame(nodeInfo, GetNodeInfo())) { + AUDIO_INFO_LOG("sourceinputnode nodekey is same as capture effect"); + return sourceInputNode_; + } + if (fmtConverterNodeMap_.find(nodeKey) == fmtConverterNodeMap_.end()) { + fmtConverterNodeMap_[nodeKey] = std::make_shared(GetNodeInfo(), nodeInfo); + nodeInfo.nodeName = "HpaeAudioFormatConverterNode"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + fmtConverterNodeMap_[nodeKey]->SetNodeInfo(nodeInfo); + } + fmtConverterNodeMap_[nodeKey]->ConnectWithInfo(sourceInputNode_, fmtConverterNodeMap_[nodeKey]->GetNodeInfo()); +#ifdef ENABLE_HIDUMP_DFX + if (auto callback = sourceInputNode_->GetNodeInfo().statusCallback.lock()) { + callback->OnNotifyDfxNodeInfo( + true, sourceInputNode_->GetNodeId(), fmtConverterNodeMap_[nodeKey]->GetNodeInfo()); + } +#endif + return fmtConverterNodeMap_[nodeKey]; +} + +OutputPort *HpaeSourceInputCluster::GetOutputPort() +{ + return sourceInputNode_->GetOutputPort(); +} + +OutputPort *HpaeSourceInputCluster::GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect) +{ + std::string nodeKey = TransHpaeResampleNodeInfoToStringKey(nodeInfo); + std::string inputNodeKey = TransHpaeResampleNodeInfoToStringKey(GetNodeInfo()); + AUDIO_INFO_LOG("sourceinput nodekey:[%{public}s] effectnodekey:[%{public}s]", + inputNodeKey.c_str(), nodeKey.c_str()); + if (CheckHpaeNodeInfoIsSame(nodeInfo, GetNodeInfo())) { + AUDIO_INFO_LOG("sourceinputnode nodekey is same as capture effect"); + return sourceInputNode_->GetOutputPort(nodeInfo); + } + CHECK_AND_RETURN_RET_LOG(fmtConverterNodeMap_.find(nodeKey) != fmtConverterNodeMap_.end(), nullptr, + "HpaeSourceProcessCluster not find the nodeKey = %{public}s", nodeKey.c_str()); + if (isDisConnect && fmtConverterNodeMap_[nodeKey]->GetOutputPortNum() <= 1) { + AUDIO_INFO_LOG("disconnect fmtConverterNode between effectnode[[%{public}s] and sourceinputnode[%{public}s]", + nodeKey.c_str(), inputNodeKey.c_str()); + fmtConverterNodeMap_[nodeKey]->DisConnectWithInfo( + sourceInputNode_, fmtConverterNodeMap_[nodeKey]->GetNodeInfo()); + } + return fmtConverterNodeMap_[nodeKey]->GetOutputPort(); +} + +int32_t HpaeSourceInputCluster::GetCapturerSourceInstance(const std::string &deviceClass, + const std::string &deviceNetId, const SourceType &sourceType, const std::string &sourceName) +{ + return sourceInputNode_->GetCapturerSourceInstance(deviceClass, deviceNetId, sourceType, sourceName); +} + +int32_t HpaeSourceInputCluster::CapturerSourceInit(IAudioSourceAttr &attr) +{ + return sourceInputNode_->CapturerSourceInit(attr); +} + +int32_t HpaeSourceInputCluster::CapturerSourceDeInit() +{ + return sourceInputNode_->CapturerSourceDeInit(); +} + +int32_t HpaeSourceInputCluster::CapturerSourceFlush(void) +{ + return sourceInputNode_->CapturerSourceFlush(); +} + +int32_t HpaeSourceInputCluster::CapturerSourcePause(void) +{ + return sourceInputNode_->CapturerSourcePause(); +} + +int32_t HpaeSourceInputCluster::CapturerSourceReset(void) +{ + return sourceInputNode_->CapturerSourceReset(); +} + +int32_t HpaeSourceInputCluster::CapturerSourceResume(void) +{ + return sourceInputNode_->CapturerSourceResume(); +} + +int32_t HpaeSourceInputCluster::CapturerSourceStart(void) +{ + return sourceInputNode_->CapturerSourceStart(); +} + +int32_t HpaeSourceInputCluster::CapturerSourceStop(void) +{ + return sourceInputNode_->CapturerSourceStop(); +} + +CapturerState HpaeSourceInputCluster::GetSourceState(void) +{ + return sourceInputNode_->GetSourceState(); +} + +size_t HpaeSourceInputCluster::GetOutputPortNum() +{ + return sourceInputNode_->GetOutputPortNum(); +} + +size_t HpaeSourceInputCluster::GetOutputPortNum(HpaeNodeInfo &nodeInfo) +{ + return sourceInputNode_->GetOutputPortNum(nodeInfo); +} + +HpaeSourceInputNodeType HpaeSourceInputCluster::GetSourceInputNodeType() +{ + return sourceInputNode_->GetSourceInputNodeType(); +} + +void HpaeSourceInputCluster::SetSourceInputNodeType(HpaeSourceInputNodeType type) +{ + sourceInputNode_->SetSourceInputNodeType(type); +} + +// for test +uint32_t HpaeSourceInputCluster::GetConverterNodeCount() +{ + return fmtConverterNodeMap_.size(); +} + +uint32_t HpaeSourceInputCluster::GetSourceInputNodeUseCount() +{ + return sourceInputNode_.use_count(); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_source_input_node.cpp b/services/audio_engine/node/src/hpae_source_input_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcb715687c2a123480a4f9194a6cf513060ca690 --- /dev/null +++ b/services/audio_engine/node/src/hpae_source_input_node.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSourceInputNode" +#endif + +#include "hpae_source_input_node.h" +#include "hpae_format_convert.h" +#include "hpae_node_common.h" +#include "audio_errors.h" +#include "audio_engine_log.h" + +#define BYTE_SIZE_SAMPLE_U8 1 +#define BYTE_SIZE_SAMPLE_S16 2 +#define BYTE_SIZE_SAMPLE_S24 3 +#define BYTE_SIZE_SAMPLE_S32 4 +#define FRAME_DURATION_DEFAULT 20 +#define MILLISECOND_PER_SECOND 1000 + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + static std::string TransSourceBufferTypeToString(HpaeSourceBufferType &type) +{ + switch (type) { + case HPAE_SOURCE_BUFFER_TYPE_EC: + return "_EC.pcm"; + case HPAE_SOURCE_BUFFER_TYPE_MICREF: + return "_MICREF.pcm"; + default: + return ".pcm"; + } +} + +HpaeSourceInputNode::HpaeSourceInputNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), sourceInputNodeType_(nodeInfo.sourceInputNodeType) +{ + HpaeSourceBufferType sourceBufferType = nodeInfo.sourceBufferType; + nodeInfoMap_.emplace(sourceBufferType, nodeInfo); + pcmBufferInfoMap_.emplace( + sourceBufferType, PcmBufferInfo(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate)); + inputAudioBufferMap_.emplace(sourceBufferType, HpaePcmBuffer(pcmBufferInfoMap_.at(sourceBufferType))); + inputAudioBufferMap_.at(sourceBufferType).SetSourceBufferType(sourceBufferType); + frameByteSizeMap_.emplace( + sourceBufferType, nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + capturerFrameDataMap_.emplace(sourceBufferType, frameByteSizeMap_.at(sourceBufferType)); + outputStreamMap_.emplace(sourceBufferType, this); + +#ifdef ENABLE_HOOK_PCM + inputPcmDumperMap_.emplace(sourceBufferType, + std::make_unique("HpaeSourceInputNode_id_"+ std::to_string(GetSessionId()) + + "_ch_" + std::to_string(GetChannelCount()) + + "_rate_" + std::to_string(GetSampleRate()) + + "_bit_"+ std::to_string(GetBitWidth()) + TransSourceBufferTypeToString(sourceBufferType))); +#endif +} + +HpaeSourceInputNode::HpaeSourceInputNode(std::vector &nodeInfos) + : HpaeNode(*nodeInfos.begin()), sourceInputNodeType_((*nodeInfos.begin()).sourceInputNodeType) +{ + for (auto nodeInfo : nodeInfos) { + HpaeSourceBufferType sourceBufferType = nodeInfo.sourceBufferType; + nodeInfoMap_.emplace(sourceBufferType, nodeInfo); + pcmBufferInfoMap_.emplace( + sourceBufferType, PcmBufferInfo(nodeInfo.channels, nodeInfo.frameLen, nodeInfo.samplingRate)); + inputAudioBufferMap_.emplace(sourceBufferType, HpaePcmBuffer(pcmBufferInfoMap_.at(sourceBufferType))); + inputAudioBufferMap_.at(sourceBufferType).SetSourceBufferType(sourceBufferType); + frameByteSizeMap_.emplace( + sourceBufferType, nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + capturerFrameDataMap_.emplace(sourceBufferType, frameByteSizeMap_.at(sourceBufferType)); + fdescMap_.emplace(sourceBufferType, + FrameDesc{capturerFrameDataMap_.at(sourceBufferType).data(), frameByteSizeMap_.at(sourceBufferType)}); + outputStreamMap_.emplace(sourceBufferType, this); +#ifdef ENABLE_HOOK_PCM + inputPcmDumperMap_.emplace(sourceBufferType, + std::make_unique("HpaeSourceInputNode_id_"+ std::to_string(GetSessionId()) + + "_ch_" + std::to_string(nodeInfo.channels) + + "_rate_" + std::to_string(nodeInfo.samplingRate) + + "_bit_"+ std::to_string(nodeInfo.format) + TransSourceBufferTypeToString(sourceBufferType))); +#endif + } +} + +void HpaeSourceInputNode::DoProcess() +{ + if (audioCapturerSource_ == nullptr) { + AUDIO_WARNING_LOG("audioCapturerSource_ is nullptr NodeId: %{public}u", GetNodeId()); + return; + } + uint64_t replyBytes = 0; + if (sourceInputNodeType_ == HpaeSourceInputNodeType::HPAE_SOURCE_MIC_EC) { + uint64_t replyBytesEc = 0; + audioCapturerSource_->CaptureFrameWithEc(&fdescMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC), replyBytes, + &fdescMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC), replyBytesEc); +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumperMap_.find(HPAE_SOURCE_BUFFER_TYPE_MIC) != inputPcmDumperMap_.end() && + inputPcmDumperMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC)) { + inputPcmDumperMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC)->Dump( + (int8_t *) capturerFrameDataMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC).data(), replyBytes); + } + if (inputPcmDumperMap_.find(HPAE_SOURCE_BUFFER_TYPE_EC) != inputPcmDumperMap_.end() && + inputPcmDumperMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC)) { + inputPcmDumperMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC)->Dump( + (int8_t *) capturerFrameDataMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).data(), replyBytesEc); + } +#endif + // todo: do not convert to float in SourceInputNode + ConvertToFloat(GetBitWidth(), GetChannelCount() * GetFrameLen(), + capturerFrameDataMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC).data(), + inputAudioBufferMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC).GetPcmDataBuffer()); + ConvertToFloat(nodeInfoMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).format, + nodeInfoMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).channels * nodeInfoMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).frameLen, + capturerFrameDataMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).data(), + inputAudioBufferMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).GetPcmDataBuffer()); + outputStreamMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC).WriteDataToOutput( + &inputAudioBufferMap_.at(HPAE_SOURCE_BUFFER_TYPE_MIC)); + outputStreamMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC).WriteDataToOutput( + &inputAudioBufferMap_.at(HPAE_SOURCE_BUFFER_TYPE_EC)); + } else { + HpaeSourceBufferType sourceBufferType = nodeInfoMap_.begin()->second.sourceBufferType; + audioCapturerSource_->CaptureFrame(capturerFrameDataMap_.at(sourceBufferType).data(), + (uint64_t)frameByteSizeMap_.at(sourceBufferType), replyBytes); +#ifdef ENABLE_HOOK_PCM + if (inputPcmDumperMap_.find(sourceBufferType) != inputPcmDumperMap_.end() && + inputPcmDumperMap_.at(sourceBufferType)) { + inputPcmDumperMap_.at(sourceBufferType)->Dump( + (int8_t *) capturerFrameDataMap_.at(sourceBufferType).data(), replyBytes); + } +#endif + // todo: do not convert to float in SourceInputNode + ConvertToFloat(GetBitWidth(), GetChannelCount() * GetFrameLen(), + capturerFrameDataMap_.at(sourceBufferType).data(), + inputAudioBufferMap_.at(sourceBufferType).GetPcmDataBuffer()); + outputStreamMap_.at(sourceBufferType).WriteDataToOutput(&inputAudioBufferMap_.at(sourceBufferType)); + } +} + +int32_t HpaeSourceInputNode::WriteCapturerData(char *data, int32_t dataSize) +{ + auto itCapturerFrameData = capturerFrameDataMap_.begin(); + auto itFrameByteSize = frameByteSizeMap_.begin(); + CHECK_AND_RETURN_RET_LOG( + itCapturerFrameData != capturerFrameDataMap_.end() && itFrameByteSize != frameByteSizeMap_.end(), + ERROR, "outStreamMap_ is empty.\n"); + int32_t ret = memcpy_s(itCapturerFrameData->second.data(), itFrameByteSize->second, data, dataSize); + CHECK_AND_RETURN_RET_LOG(ret == 0, ret, "memcpy error when WriteCapturerData"); + return 0; +} + +bool HpaeSourceInputNode::Reset() +{ + return true; +} + +bool HpaeSourceInputNode::ResetAll() +{ + return true; +} + +std::shared_ptr HpaeSourceInputNode::GetSharedInstance() +{ + return shared_from_this(); +} + +OutputPort *HpaeSourceInputNode::GetOutputPort() +{ + std::unordered_map>::iterator it; + if (sourceInputNodeType_ != HPAE_SOURCE_MIC_EC) { + it = outputStreamMap_.begin(); + } else { + it = outputStreamMap_.find(HPAE_SOURCE_BUFFER_TYPE_MIC); + } + CHECK_AND_RETURN_RET_LOG(it != outputStreamMap_.end(), nullptr, "outStreamMap_ is empty.\n"); + return &(it->second); +} + +OutputPort *HpaeSourceInputNode::GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect) +{ + auto it = outputStreamMap_.find(nodeInfo.sourceBufferType); + CHECK_AND_RETURN_RET_LOG(it != outputStreamMap_.end(), nullptr, + "can't find nodeKey in outStreamMap_, sourceBufferType = %{public}d.\n", + nodeInfo.sourceBufferType); + return &(it->second); +} + +HpaeSourceBufferType HpaeSourceInputNode::GetOutputPortBufferType(HpaeNodeInfo &nodeInfo) +{ + auto it = outputStreamMap_.find(nodeInfo.sourceBufferType); + CHECK_AND_RETURN_RET_LOG(it != outputStreamMap_.end(), HPAE_SOURCE_BUFFER_TYPE_DEFAULT, + "can't find nodeKey in outStreamMap_, sourceBufferType = %{public}d.\n", nodeInfo.sourceBufferType); + // todo: rewrite this function + if (sourceInputNodeType_ == HpaeSourceInputNodeType::HPAE_SOURCE_MIC_EC) { + if (nodeInfo.sourceBufferType == HPAE_SOURCE_BUFFER_TYPE_MIC) { + return HPAE_SOURCE_BUFFER_TYPE_MIC; + } else { + return HPAE_SOURCE_BUFFER_TYPE_EC; + } + } else { + return inputAudioBufferMap_.at(nodeInfo.sourceBufferType).GetSourceBufferType(); + } +} + +int32_t HpaeSourceInputNode::GetCapturerSourceAdapter( + const std::string &deviceClass, const SourceType &sourceType, const std::string &info) +{ + captureId_ = HDI_INVALID_ID; + if (info.empty()) { + captureId_ = HdiAdapterManager::GetInstance().GetCaptureIdByDeviceClass( + deviceClass, sourceType, HDI_ID_INFO_DEFAULT, true); + } else { + captureId_ = HdiAdapterManager::GetInstance().GetCaptureIdByDeviceClass( + deviceClass, sourceType, info, true); + } + audioCapturerSource_ = HdiAdapterManager::GetInstance().GetCaptureSource(captureId_, true); + if (audioCapturerSource_ == nullptr) { + AUDIO_ERR_LOG("get source fail, deviceClass: %{public}s, info: %{public}s, captureId_: %{public}u", + deviceClass.c_str(), info.c_str(), captureId_); + HdiAdapterManager::GetInstance().ReleaseId(captureId_); + return ERROR; + } + return SUCCESS; +} + +int32_t HpaeSourceInputNode::GetCapturerSourceInstance(const std::string &deviceClass, const std::string &deviceNetId, + const SourceType &sourceType, const std::string &sourceName) +{ + if (sourceType == SOURCE_TYPE_WAKEUP || sourceName == HDI_ID_INFO_EC || sourceName == HDI_ID_INFO_MIC_REF) { + return GetCapturerSourceAdapter(deviceClass, sourceType, sourceName); + } + return GetCapturerSourceAdapter(deviceClass, sourceType, deviceNetId); +} + +int32_t HpaeSourceInputNode::CapturerSourceInit(IAudioSourceAttr &attr) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + + if (audioCapturerSource_->IsInited()) { + return SUCCESS; + } + + audioSourceAttr_ = attr; + CHECK_AND_RETURN_RET_LOG(audioCapturerSource_->Init(attr) == SUCCESS, ERROR, "Source init fail"); + state_ = CAPTURER_NEW; + return SUCCESS; +} + +int32_t HpaeSourceInputNode::CapturerSourceDeInit() +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + audioCapturerSource_->DeInit(); + audioCapturerSource_ = nullptr; + // todo: check where to release captureId_ + HdiAdapterManager::GetInstance().ReleaseId(captureId_); + return SUCCESS; +} + +int32_t HpaeSourceInputNode::CapturerSourceFlush(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + return audioCapturerSource_->Flush(); +} + +int32_t HpaeSourceInputNode::CapturerSourcePause(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + CHECK_AND_RETURN_RET_LOG(audioCapturerSource_->Pause() == SUCCESS, ERROR, "Source pause fail"); + state_ = CAPTURER_PAUSED; + return SUCCESS; +} + +int32_t HpaeSourceInputNode::CapturerSourceReset(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + return audioCapturerSource_->Reset(); +} + +int32_t HpaeSourceInputNode::CapturerSourceResume(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + CHECK_AND_RETURN_RET_LOG(audioCapturerSource_->Resume() == SUCCESS, ERROR, "Source resume fail"); + state_ = CAPTURER_RUNNING; + return SUCCESS; +} + +int32_t HpaeSourceInputNode::CapturerSourceStart(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + CHECK_AND_RETURN_RET_LOG(audioCapturerSource_->Start() == SUCCESS, ERROR, "Source start fail"); + state_ = CAPTURER_RUNNING; + return SUCCESS; +} + +int32_t HpaeSourceInputNode::CapturerSourceStop(void) +{ + if (audioCapturerSource_ == nullptr) { + return ERROR; + } + CHECK_AND_RETURN_RET_LOG(audioCapturerSource_->Stop() == SUCCESS, ERROR, "Source stop fail"); + state_ = CAPTURER_STOPPED; + return SUCCESS; +} + +CapturerState HpaeSourceInputNode::GetSourceState(void) +{ + return state_; +} + +size_t HpaeSourceInputNode::GetOutputPortNum() +{ + std::unordered_map>::iterator it; + if (sourceInputNodeType_ != HPAE_SOURCE_MIC_EC) { + it = outputStreamMap_.begin(); + } else { + it = outputStreamMap_.find(HPAE_SOURCE_BUFFER_TYPE_MIC); + } + CHECK_AND_RETURN_RET_LOG(it != outputStreamMap_.end(), 0, "outStreamMap_ is empty.\n"); + return it->second.GetInputNum(); +} + +size_t HpaeSourceInputNode::GetOutputPortNum(HpaeNodeInfo &nodeInfo) +{ + auto it = outputStreamMap_.find(nodeInfo.sourceBufferType); + CHECK_AND_RETURN_RET_LOG(it != outputStreamMap_.end(), 0, "can't find nodeKey in outStreamMap_.\n"); + return it->second.GetInputNum(); +} + +HpaeSourceInputNodeType HpaeSourceInputNode::GetSourceInputNodeType() +{ + return sourceInputNodeType_; +} + +void HpaeSourceInputNode::SetSourceInputNodeType(HpaeSourceInputNodeType type) +{ + sourceInputNodeType_ = type; +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/node/src/hpae_source_output_node.cpp b/services/audio_engine/node/src/hpae_source_output_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cf94daf4684fb2b62a6795ea603bf24b1282e9f --- /dev/null +++ b/services/audio_engine/node/src/hpae_source_output_node.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSourceOutputNode" +#endif + +#include +#include "audio_engine_log.h" +#include "hpae_format_convert.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeSourceOutputNode::HpaeSourceOutputNode(HpaeNodeInfo &nodeInfo) + : HpaeNode(nodeInfo), + sourceOuputData_(nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)), + interleveData_(nodeInfo.frameLen * nodeInfo.channels) +{ +#ifdef ENABLE_HOOK_PCM + outputPcmDumper_ = std::make_unique( + "HpaeSourceOutputNode_id_" + std::to_string(GetSessionId()) + "_ch_" + std::to_string(GetChannelCount()) + + "_rate_" + std::to_string(GetSampleRate()) + "_bit_" + std::to_string(GetBitWidth()) + ".pcm"); +#endif +} + +void HpaeSourceOutputNode::DoProcess() +{ + auto rate = "rate[" + std::to_string(GetSampleRate()) + "]_"; + auto ch = "ch[" + std::to_string(GetChannelCount()) + "]_"; + auto len = "len[" + std::to_string(GetFrameLen()) + "]_"; + auto format = "bit[" + std::to_string(GetBitWidth()) + "]"; + Trace trace("[" + std::to_string(GetSessionId()) + "]HpaeSourceOutputNode::DoProcess " + + rate + ch + len + format); + if (readCallback_.lock() == nullptr) { + AUDIO_WARNING_LOG("HpaeSourceOutputNode readCallback_ is nullptr"); + return; + } + std::vector &outputVec = inputStream_.ReadPreOutputData(); + if (outputVec.empty()) { + return; + } + HpaePcmBuffer *outputData = outputVec.front(); + ConvertFromFloat( + GetBitWidth(), GetChannelCount() * GetFrameLen(), outputData->GetPcmDataBuffer(), sourceOuputData_.data()); +#ifdef ENABLE_HOOK_PCM + if (outputPcmDumper_) { + outputPcmDumper_->Dump( + (int8_t *)sourceOuputData_.data(), GetChannelCount() * GetFrameLen() * GetSizeFromFormat(GetBitWidth())); + } +#endif + int32_t ret = readCallback_.lock()->OnReadData(sourceOuputData_, sourceOuputData_.size()); + if (ret != 0) { + AUDIO_WARNING_LOG("sessionId %{public}u, readCallback_ write read data error", GetSessionId()); + } + return; +} + +bool HpaeSourceOutputNode::Reset() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + inputStream_.DisConnect(output); + } + return true; +} + +bool HpaeSourceOutputNode::ResetAll() +{ + const auto preOutputMap = inputStream_.GetPreOuputMap(); + for (const auto &preOutput : preOutputMap) { + OutputPort *output = preOutput.first; + std::shared_ptr hpaeNode = preOutput.second; + if (hpaeNode->ResetAll()) { + inputStream_.DisConnect(output); + } + } + return true; +} + +bool HpaeSourceOutputNode::RegisterReadCallback(const std::weak_ptr &callback) +{ + if (callback.lock() == nullptr) { + return false; + } + readCallback_ = callback; + return true; +} + +void HpaeSourceOutputNode::Connect(const std::shared_ptr> &preNode) +{ + inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort()); +} + +void HpaeSourceOutputNode::ConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + std::shared_ptr realPreNode = preNode->GetSharedInstance(nodeInfo); + inputStream_.Connect(realPreNode, preNode->GetOutputPort(nodeInfo)); +#ifdef ENABLE_HIDUMP_DFX + if (auto callback = GetNodeInfo().statusCallback.lock()) { + callback->OnNotifyDfxNodeInfo( + true, realPreNode->GetNodeId(), GetNodeInfo()); + } +#endif +} + +void HpaeSourceOutputNode::DisConnect(const std::shared_ptr> &preNode) +{ + inputStream_.DisConnect(preNode->GetOutputPort()); +} + +void HpaeSourceOutputNode::DisConnectWithInfo(const std::shared_ptr> &preNode, + HpaeNodeInfo &nodeInfo) +{ + inputStream_.DisConnect(preNode->GetOutputPort(nodeInfo, true)); +} + + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/node/src/hpae_source_process_cluster.cpp b/services/audio_engine/node/src/hpae_source_process_cluster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26ad3a9fb9b113cc1eef33537320b7dd7a4b71f2 --- /dev/null +++ b/services/audio_engine/node/src/hpae_source_process_cluster.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "HpaeSourceProcessCluster" +#endif + +#include "hpae_source_process_cluster.h" +#include "hpae_node_common.h" +#include "audio_engine_log.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +HpaeSourceProcessCluster::HpaeSourceProcessCluster(HpaeNodeInfo& nodeInfo) + : HpaeNode(nodeInfo), captureEffectNode_(std::make_shared(nodeInfo)) +{ + AUDIO_INFO_LOG("Create scene ProcessCluster, sceneType = %{public}u", nodeInfo.sceneType); +} + +HpaeSourceProcessCluster::~HpaeSourceProcessCluster() +{ + Reset(); +} + +void HpaeSourceProcessCluster::DoProcess() +{ +} + +bool HpaeSourceProcessCluster::Reset() +{ + captureEffectNode_->Reset(); + for (auto fmtConverterNode : fmtConverterNodeMap_) { + fmtConverterNode.second->Reset(); + } + return true; +} + +bool HpaeSourceProcessCluster::ResetAll() +{ + return captureEffectNode_->ResetAll(); +} + +std::shared_ptr HpaeSourceProcessCluster::GetSharedInstance() +{ + return captureEffectNode_; +} + +OutputPort *HpaeSourceProcessCluster::GetOutputPort() +{ + return captureEffectNode_->GetOutputPort(); +} + +std::shared_ptr HpaeSourceProcessCluster::GetSharedInstance(HpaeNodeInfo &nodeInfo) +{ + std::string nodeKey = TransHpaeResampleNodeInfoToStringKey(nodeInfo); + HpaeNodeInfo effectNodeInfo; + captureEffectNode_->GetCapturerEffectConfig(effectNodeInfo); + std::string effectNodeKey = TransHpaeResampleNodeInfoToStringKey(effectNodeInfo); + AUDIO_INFO_LOG("sourceoutput nodekey:[%{public}s] effectnodekey:[%{public}s]", + nodeKey.c_str(), effectNodeKey.c_str()); + if (CheckHpaeNodeInfoIsSame(nodeInfo, effectNodeInfo)) { + AUDIO_INFO_LOG("sourceoutputnode nodekey is same as capture effect"); + return captureEffectNode_; + } + if (fmtConverterNodeMap_.find(nodeKey) == fmtConverterNodeMap_.end()) { + fmtConverterNodeMap_[nodeKey] = std::make_shared(effectNodeInfo, nodeInfo); + nodeInfo.nodeName = "HpaeAudioFormatConverterNode"; + nodeInfo.nodeId = nodeInfo.statusCallback.lock()->OnGetNodeId(); + fmtConverterNodeMap_[nodeKey]->SetNodeInfo(nodeInfo); + } + fmtConverterNodeMap_[nodeKey]->Connect(captureEffectNode_); +#ifdef ENABLE_HIDUMP_DFX + if (auto callback = nodeInfo.statusCallback.lock()) { + callback->OnNotifyDfxNodeInfo( + true, captureEffectNode_->GetNodeId(), fmtConverterNodeMap_[nodeKey]->GetNodeInfo()); + } +#endif + return fmtConverterNodeMap_[nodeKey]; +} + +OutputPort *HpaeSourceProcessCluster::GetOutputPort(HpaeNodeInfo &nodeInfo, bool isDisConnect) +{ + std::string nodeKey = TransHpaeResampleNodeInfoToStringKey(nodeInfo); + HpaeNodeInfo effectNodeInfo; + captureEffectNode_->GetCapturerEffectConfig(effectNodeInfo); + std::string effectNodeKey = TransHpaeResampleNodeInfoToStringKey(effectNodeInfo); + AUDIO_INFO_LOG("sourceoutput nodekey:[%{public}s] effectnodekey:[%{public}s]", + nodeKey.c_str(), effectNodeKey.c_str()); + if (CheckHpaeNodeInfoIsSame(nodeInfo, effectNodeInfo)) { + AUDIO_INFO_LOG("sourceoutputnode nodekey is same as capture effect"); + return captureEffectNode_->GetOutputPort(); + } + CHECK_AND_RETURN_RET_LOG(fmtConverterNodeMap_.find(nodeKey) != fmtConverterNodeMap_.end(), nullptr, + "HpaeSourceProcessCluster not find the nodeKey = %{public}s", nodeKey.c_str()); + if (isDisConnect && fmtConverterNodeMap_[nodeKey]->GetOutputPortNum() <= 1) { + // disconnect fmtConverterNode->upEffectNode + AUDIO_INFO_LOG("disconnect fmtConverterNode between effectnode[[%{public}s] and sourceoutputnode[%{public}s]", + effectNodeKey.c_str(), nodeKey.c_str()); + fmtConverterNodeMap_[nodeKey]->DisConnect(captureEffectNode_); + } + return fmtConverterNodeMap_[nodeKey]->GetOutputPort(); +} + +void HpaeSourceProcessCluster::Connect(const std::shared_ptr> &preNode) +{ + HpaeNodeInfo effectNodeInfo; + captureEffectNode_->GetCapturerEffectConfig(effectNodeInfo); + captureEffectNode_->ConnectWithInfo(preNode, effectNodeInfo); +} + +void HpaeSourceProcessCluster::DisConnect(const std::shared_ptr> &preNode) +{ + HpaeNodeInfo effectNodeInfo; + captureEffectNode_->GetCapturerEffectConfig(effectNodeInfo); + captureEffectNode_->DisConnectWithInfo(preNode, effectNodeInfo); +} + +void HpaeSourceProcessCluster::ConnectWithInfo(const std::shared_ptr>& preNode, + HpaeNodeInfo &nodeInfo) +{ + captureEffectNode_->ConnectWithInfo(preNode, nodeInfo); +} + +void HpaeSourceProcessCluster::DisConnectWithInfo(const std::shared_ptr>& preNode, + HpaeNodeInfo &nodeInfo) +{ + captureEffectNode_->DisConnectWithInfo(preNode, nodeInfo); +} + +bool HpaeSourceProcessCluster::GetCapturerEffectConfig(HpaeNodeInfo &nodeInfo, HpaeSourceBufferType type) +{ + return captureEffectNode_->GetCapturerEffectConfig(nodeInfo, type); +} + +size_t HpaeSourceProcessCluster::GetOutputPortNum() +{ + return captureEffectNode_->GetOutputPortNum(); +} + +int32_t HpaeSourceProcessCluster::CaptureEffectCreate(uint64_t sceneKeyCode, CaptureEffectAttr attr) +{ + CHECK_AND_RETURN_RET_LOG(captureEffectNode_, ERROR_ILLEGAL_STATE, "captureEffectNode_ is nullptr"); + return captureEffectNode_->CaptureEffectCreate(sceneKeyCode, attr); +} + +int32_t HpaeSourceProcessCluster::CaptureEffectRelease(uint64_t sceneKeyCode) +{ + CHECK_AND_RETURN_RET_LOG(captureEffectNode_, ERROR_ILLEGAL_STATE, "captureEffectNode_ is nullptr"); + return captureEffectNode_->CaptureEffectRelease(sceneKeyCode); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.c b/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.c new file mode 100644 index 0000000000000000000000000000000000000000..200a548b761141d3a952b1dbef2dca209faff89f --- /dev/null +++ b/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "bitdepth_converter.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define INDEX_TWO 2 +#define INDEX_THREE 3 +#define SHIFTS_8BIT 8 +#define SHIFTS_16BIT 16 +#define SHIFTS_24BIT 24 + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +static inline uint32_t READ24LE(const uint8_t *p) +{ + return ((uint32_t)p[INDEX_TWO] << SHIFTS_16BIT) | + ((uint32_t)p[1] << SHIFTS_8BIT) | + ((uint32_t)p[0]); +} + +static inline void WRITE24LE(uint8_t *p, uint32_t u) +{ + p[INDEX_TWO] = (uint8_t)(u >> SHIFTS_16BIT); + p[1] = (uint8_t)(u >> SHIFTS_8BIT); + p[0] = (uint8_t)u; +} + +// SAMPLE_U8 +// U8ToS16Le +static void U8ToS16Le(unsigned n, const uint8_t* a, int16_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (int16_t)(*a - (uint8_t)0x80U) << SHIFTS_8BIT; + } +} +// U8ToS24Le +static void U8ToS24Le(unsigned n, const uint8_t* a, uint8_t* b) +{ + for (; n > 0; n--) { + WRITE24LE(b, (uint32_t)(*a - (uint8_t)0x80U) << SHIFTS_16BIT); + a++; + b += INDEX_THREE; + } +} +// U8ToS32Le +static void U8ToS32Le(unsigned n, const uint8_t* a, int32_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (int32_t)(*a - (uint8_t)0x80U) << SHIFTS_24BIT; + } +} +// U8ToF32Le +static void U8ToF32Le(unsigned n, const uint8_t* a, float* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (float)(*a - (uint8_t)0x80U) * (1.0 / 0x80U); + } +} + +// SAMPLE_S16LE +// S16LeToU8 +static void S16LeToU8(unsigned n, const int16_t* a, uint8_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (uint8_t)((uint16_t)(*a) >> SHIFTS_8BIT) + (uint8_t)0x80U; + } +} +// S16LeToS24Le +static void S16LeToS24Le(unsigned n, const int16_t* a, uint8_t* b) +{ + for (; n > 0; n--) { + WRITE24LE(b, ((uint32_t)*a) << SHIFTS_8BIT); + a++; + b += INDEX_THREE; + } +} +// S16LeToS32Le +static void S16LeToS32Le(unsigned n, const int16_t* a, int32_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = ((int32_t)*a) << SHIFTS_16BIT; + } +} +// S16LeToF32Le +static void S16LeToF32Le(unsigned n, const int16_t* a, float* b) +{ + for (; n > 0; n--) { + *(b++) = *(a++) * (1.0f / (1 << 0x0F)); + } +} + +// SAMPLE_S24LE +// S24LeToU8 +static void S24LeToU8(unsigned n, const uint8_t* a, uint8_t* b) +{ + for (; n > 0; n--) { + *b = (uint8_t)(READ24LE(a) >> SHIFTS_16BIT) + (uint8_t)0x80U; + a += INDEX_THREE; + b++; + } +} +// S24LeToS16Le +static void S24LeToS16Le(unsigned n, const uint8_t* a, int16_t* b) +{ + for (; n > 0; n--) { + *b = (int16_t)(READ24LE(a) >> SHIFTS_8BIT); + a += INDEX_THREE; + b++; + } +} +// S24LeToS32Le +static void S24LeToS32Le(unsigned n, const uint8_t* a, int32_t* b) +{ + for (; n > 0; n--) { + *b = (int32_t)(READ24LE(a) << SHIFTS_8BIT); + a += INDEX_THREE; + b++; + } +} +// S24LeToF32Le +static void S24LeToF32Le(unsigned n, const uint8_t* a, float* b) +{ + for (; n > 0; n--) { + int32_t s = (int32_t)READ24LE(a) << SHIFTS_8BIT; + *b = s * (1.0f / (1U << 0x1F)); + a += INDEX_THREE; + b++; + } +} + +// SAMPLE_S32LE +// S32LeToU8 +static void S32LeToU8(unsigned n, const int32_t* a, uint8_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (uint8_t)((int32_t)(*a) >> SHIFTS_24BIT) + (uint8_t)0x80U; + } +} +// S32LeToS16Le +static void S32LeToS16Le(unsigned n, const int32_t* a, int16_t* b) +{ + for (; n > 0; n--, a++, b++) { + *b = (int16_t)((int32_t)(*a) >> SHIFTS_16BIT); + } +} +// S32LeToS24Le +static void S32LeToS24Le(unsigned n, const int32_t* a, uint8_t* b) +{ + for (; n > 0; n--) { + WRITE24LE(b, ((uint32_t)*a) >> SHIFTS_8BIT); + a++; + b += INDEX_THREE; + } +} +// S32LeToF32Le +static void S32LeToF32Le(unsigned n, const int32_t* a, float* b) +{ + for (; n > 0; n--) { + *(b++) = *(a++) * (1.0f / (1U << 0x1F)); + } +} + +// SAMPLE_F32LE +// F32LeToU8 +static void F32LeToU8(unsigned n, const float* a, uint8_t* b) +{ + for (; n > 0; n--) { + float v = *(a++); + *(b++) = (uint8_t)(CLAMP(v, -1.0, 1.0) * 127.0f + 128.0f); + } +} +// F32LeToS16Le +static void F32LeToS16Le(unsigned n, const float* a, int16_t* b) +{ + for (; n > 0; n--) { + float v = *(a++) * (1 << 15); + *(b++) = (int16_t)CLAMP((int32_t)v, -0x8000, 0x7FFF); + } +} +// F32LeToS24Le +static void F32LeToS24Le(unsigned n, const float* a, uint8_t* b) +{ + for (; n > 0; n--) { + float v = *(a++) * (1 << 23); + WRITE24LE(b, (uint32_t)CLAMP((int32_t)v, -0x800000LL, 0x7FFFFFLL)); + b += INDEX_THREE; + } +} +// F32LeToS32Le +static void F32LeToS32Le(unsigned n, const float* a, int32_t* b) +{ + for (; n > 0; n--) { + float v = *(a++) * (1U << 31); + *(b++) = (int32_t)CLAMP((int64_t)v, -0x80000000LL, 0x7FFFFFFFLL); + } +} + +// function table +static FmtConversionFunction g_sampleU8Table[] = { + [SAMPLE_S16LE] = (FmtConversionFunction)S16LeToU8, + [SAMPLE_S24LE] = (FmtConversionFunction)S24LeToU8, + [SAMPLE_S32LE] = (FmtConversionFunction)S32LeToU8, + [SAMPLE_F32LE] = (FmtConversionFunction)F32LeToU8, +}; + +static FmtConversionFunction g_sampleS16leTable[] = { + [SAMPLE_U8] = (FmtConversionFunction)U8ToS16Le, + [SAMPLE_S24LE] = (FmtConversionFunction)S24LeToS16Le, + [SAMPLE_S32LE] = (FmtConversionFunction)S32LeToS16Le, + [SAMPLE_F32LE] = (FmtConversionFunction)F32LeToS16Le, +}; + +static FmtConversionFunction g_sampleS24leTable[] = { + [SAMPLE_U8] = (FmtConversionFunction)U8ToS24Le, + [SAMPLE_S16LE] = (FmtConversionFunction)S16LeToS24Le, + [SAMPLE_S32LE] = (FmtConversionFunction)S32LeToS24Le, + [SAMPLE_F32LE] = (FmtConversionFunction)F32LeToS24Le, +}; + +static FmtConversionFunction g_sampleS32leTable[] = { + [SAMPLE_U8] = (FmtConversionFunction)U8ToS32Le, + [SAMPLE_S16LE] = (FmtConversionFunction)S16LeToS32Le, + [SAMPLE_S24LE] = (FmtConversionFunction)S24LeToS32Le, + [SAMPLE_F32LE] = (FmtConversionFunction)F32LeToS32Le, +}; + +static FmtConversionFunction g_sampleF32leTable[] = { + [SAMPLE_U8] = (FmtConversionFunction)U8ToF32Le, + [SAMPLE_S16LE] = (FmtConversionFunction)S16LeToF32Le, + [SAMPLE_S24LE] = (FmtConversionFunction)S24LeToF32Le, + [SAMPLE_S32LE] = (FmtConversionFunction)S32LeToF32Le, +}; + +// choose function from function table +FmtConversionFunction GetFmtConversionU8(AudioSampleFormat fmt) +{ + return g_sampleU8Table[fmt]; +} + +FmtConversionFunction GetFmtConversionS16Le(AudioSampleFormat fmt) +{ + return g_sampleS16leTable[fmt]; +} + +FmtConversionFunction GetFmtConversionS24Le(AudioSampleFormat fmt) +{ + return g_sampleS24leTable[fmt]; +} + +FmtConversionFunction GetFmtConversionS32Le(AudioSampleFormat fmt) +{ + return g_sampleS32leTable[fmt]; +} + +FmtConversionFunction GetFmtConversionF32Le(AudioSampleFormat fmt) +{ + return g_sampleF32leTable[fmt]; +} + +// BitDepthConversion implementation +BitDepthConversionState* FmtConversionInit(uint32_t inputFormat, uint32_t outputFormat, + uint32_t numChannels, int* err) +{ + BitDepthConversionState* state; + + if (numChannels == 0 || inputFormat == outputFormat) { + if (err) { + *err = FMTCONV_ERR_INVALID_ARG; + } + return NULL; + } + state = (BitDepthConversionState*)calloc(sizeof(BitDepthConversionState), 1); + if (!state) { + if (err) { + *err = FMTCONV_ERR_ALLOC_FAILED; + } + return NULL; + } + state->inputFormat = inputFormat; + state->outputFormat = outputFormat; + state->numChannels = numChannels; + + if (inputFormat != outputFormat) { + switch (outputFormat) { + case SAMPLE_U8: + state->fmtConversionProcess = GetFmtConversionU8(inputFormat); + break; + case SAMPLE_S16LE: + state->fmtConversionProcess = GetFmtConversionS16Le(inputFormat); + break; + case SAMPLE_S24LE: + state->fmtConversionProcess = GetFmtConversionS24Le(inputFormat); + break; + case SAMPLE_S32LE: + state->fmtConversionProcess = GetFmtConversionS32Le(inputFormat); + break; + case SAMPLE_F32LE: + default: + state->fmtConversionProcess = GetFmtConversionF32Le(inputFormat); + break; + } + } + + return state; +} +void FmtConversionStateFree(BitDepthConversionState* state) +{ + free(state); + state = NULL; +} \ No newline at end of file diff --git a/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.h b/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.h new file mode 100644 index 0000000000000000000000000000000000000000..e13d1f3548027e6e82873d32767d5c9524a90eac --- /dev/null +++ b/services/audio_engine/plugin/bitdepth_converter/bitdepth_converter.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef BITDEPTH_CONVERTER_H +#define BITDEPTH_CONVERTER_H +#include + +#ifdef __cplusplus +extern "C" { +#endif + enum { + FMTCONV_ERR_SUCCESS = 0, + FMTCONV_ERR_ALLOC_FAILED = 1, + FMTCONV_ERR_INVALID_ARG = 2 + }; + + /** Audio sample format */ + typedef enum AudioSampleFormat { + SAMPLE_U8 = 0, + SAMPLE_S16LE = 1, + SAMPLE_S24LE = 2, + SAMPLE_S32LE = 3, + SAMPLE_F32LE = 4, + INVALID_WIDTH = -1 + } AudioSampleFormat; + + + typedef void (*FmtConversionFunction)(unsigned n, const void* in, const void* out); + + FmtConversionFunction GetFmtConversionU8(AudioSampleFormat fmt); + FmtConversionFunction GetFmtConversionS16Le(AudioSampleFormat fmt); + FmtConversionFunction GetFmtConversionS24Le(AudioSampleFormat fmt); + FmtConversionFunction GetFmtConversionS32Le(AudioSampleFormat fmt); + FmtConversionFunction GetFmtConversionF32Le(AudioSampleFormat fmt); + + typedef struct BitDepthConversionState BitDepthConversionState; + + struct BitDepthConversionState { + uint32_t inputFormat; + uint32_t outputFormat; + uint32_t numChannels; + uint32_t length; + + FmtConversionFunction fmtConversionProcess; + }; + + BitDepthConversionState* FmtConversionInit(uint32_t inputFormat, uint32_t outputFormat, + uint32_t numChannels, int* err); + + void FmtConversionStateFree(BitDepthConversionState* state); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/include/channel_converter.h b/services/audio_engine/plugin/channel_converter/include/channel_converter.h new file mode 100644 index 0000000000000000000000000000000000000000..9b3089ac455e9f4ecc18e68368ece240332a26da --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/include/channel_converter.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CHANNEL_CONVERTER_H +#define CHANNEL_CONVERTER_H + +#include "down_mixer.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +class ChannelConverter { +public: + ChannelConverter() = default; + int32_t Process(uint32_t framesize, float* in, uint32_t inLen, float* out, uint32_t outLen); + // input and output stream format must be workFormat + int32_t SetParam(AudioChannelInfo inChannelInfo, AudioChannelInfo outChannelInfo, + AudioSampleFormat workFormat, bool mixLfe); + AudioChannelInfo GetInChannelInfo() const; + AudioChannelInfo GetOutChannelInfo() const; + int32_t SetInChannelInfo(AudioChannelInfo inChannelInfo); + int32_t SetOutChannelInfo(AudioChannelInfo outChannelInfo); + void Reset(); +private: + int32_t Upmix(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen); + DownMixer downMixer_; + AudioChannelInfo inChannelInfo_; + AudioChannelInfo outChannelInfo_; + AudioSampleFormat workFormat_ = INVALID_WIDTH; // work format, for now only supports float + uint32_t workSize_ = 0; // work format, for now only supports float + bool mixLfe_ = true; +}; +} // HPAE +} // AudioStandard +} // OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/include/down_mixer.h b/services/audio_engine/plugin/channel_converter/include/down_mixer.h new file mode 100644 index 0000000000000000000000000000000000000000..63c13350f5321afb46ed5f33b5669953c15627b7 --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/include/down_mixer.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DOWN_MIXER_H +#define DOWN_MIXER_H +#include +#include "audio_stream_info.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t MAX_CHANNELS = 16; + +constexpr float COEF_ZERO_F = 0.0f; +constexpr float COEF_0DB_F = 1.0f; +constexpr float COEF_M3DB_F = 0.7071f; +constexpr float COEF_M6DB_F = 0.5f; +constexpr float COEF_M435DB_F = 0.6057f; +constexpr float COEF_M45DB_F = 0.5946f; +constexpr float COEF_M9DB_F = 0.3544f; +constexpr float COEF_M899DB_F = 0.3552f; +constexpr float COEF_M12DB_F = 0.2509f; + +enum { + DMIX_ERR_SUCCESS = 0, + DMIX_ERR_ALLOC_FAILED = -1, + DMIX_ERR_INVALID_ARG = -2 +}; + +class DownMixer { +public: + DownMixer(); + int32_t Process(uint32_t framesize, float* in, uint32_t inLen, float* out, uint32_t outLen); + + int32_t SetParam(AudioChannelInfo inChannelInfo_, AudioChannelInfo outChannelInfo_, + uint32_t formatSize, bool mixLfe); + void Reset(); +private: + AudioChannelLayout inLayout_ = CH_LAYOUT_UNKNOWN; + uint32_t inChannels_ = 0; + AudioChannelLayout outLayout_ = CH_LAYOUT_UNKNOWN; + uint32_t outChannels_ = 0; + uint32_t formatSize_ = INVALID_WIDTH; // work format, for now only supports float + std::vector> downMixTable_; + bool mixLfe_ = true; + bool isInitialized_ = false; + void SetupStereoDmixTable(); + void Setup5Point1DmixTable(); + void Setup5Point1Point2DmixTable(); + void Setup5Point1Point4DmixTable(); + void Setup7Point1DmixTable(); + void Setup7Point1Point2DmixTable(); + void Setup7Point1Point4DmixTable(); + void SetupGeneralDmixTable(); + void ResetSelf(); + int32_t SetupDownMixTable(); + /**** helper functions for settiing up specific downmix table ***/ + void SetupStereoDmixTablePart1(uint64_t bit_t, uint32_t i); + void SetupStereoDmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup5Point1DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup5Point1DmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup5Point1Point2DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup5Point1Point2DmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup5Point1Point4DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup5Point1Point4DmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup7Point1DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup7Point1DmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup7Point1Point2DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup7Point1Point2DmixTablePart2(uint64_t bit_t, uint32_t i); + void Setup7Point1Point4DmixTablePart1(uint64_t bit_t, uint32_t i); + void Setup7Point1Point4DmixTablePart2(uint64_t bit_t, uint32_t i); + /**** helper functions for setting up general downmix table ***/ + void DownMixBottom(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixLfe(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixMidFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixMidRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixTopCenter(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixTopFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void DownMixTopRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j); + void NormalizeDMixTable(); +}; +} // HPAE +} // AudioStandard +} // OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp b/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dca918ea6f8f428ad6ed3dcd4340b899a7da7597 --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/src/channel_converter.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeChannelConverter" +#endif +#include "channel_converter.h" +#include "audio_engine_log.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +static inline uint32_t Min(uint32_t a, uint32_t b) +{ + return a < b ? a : b; +} + +static uint32_t GetFormatSize(AudioSampleFormat format) +{ + uint32_t sampleSize = 0; + switch (format) { + case SAMPLE_U8: + sampleSize = sizeof(uint8_t); + break; + case SAMPLE_S16LE: + sampleSize = sizeof(int16_t); + break; + case SAMPLE_S24LE: + sampleSize = SAMPLE_S24LE + 1; + break; + case SAMPLE_S32LE: + sampleSize = sizeof(int32_t); + break; + case SAMPLE_F32LE: + sampleSize = sizeof(float); + break; + default: + AUDIO_ERR_LOG("unsupported format %{public}d", format); + } + return sampleSize; +} + +int32_t ChannelConverter::SetParam(AudioChannelInfo inChannelInfo, AudioChannelInfo outChannelInfo, + AudioSampleFormat format, bool mixLfe) +{ + inChannelInfo_.channelLayout = inChannelInfo.channelLayout; + outChannelInfo_.channelLayout = outChannelInfo.channelLayout; + inChannelInfo_.numChannels = inChannelInfo.numChannels; + outChannelInfo_.numChannels = outChannelInfo.numChannels; + workFormat_ = format; + workSize_ = GetFormatSize(format); + mixLfe_ = mixLfe; + int32_t ret = DMIX_ERR_SUCCESS; + if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { + ret = downMixer_.SetParam(inChannelInfo, outChannelInfo, workSize_, mixLfe); + } + return ret; +} + +int32_t ChannelConverter::SetInChannelInfo(AudioChannelInfo inChannelInfo) +{ + inChannelInfo_.channelLayout = inChannelInfo.channelLayout; + inChannelInfo_.numChannels = inChannelInfo.numChannels; + if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { + return downMixer_.SetParam(inChannelInfo_, outChannelInfo_, workSize_, mixLfe_); + } + return DMIX_ERR_SUCCESS; +} + + +int32_t ChannelConverter::SetOutChannelInfo(AudioChannelInfo outChannelInfo) +{ + outChannelInfo_.channelLayout = outChannelInfo.channelLayout; + outChannelInfo_.numChannels = outChannelInfo.numChannels; + if (inChannelInfo_.numChannels > outChannelInfo_.numChannels) { + return downMixer_.SetParam(inChannelInfo_, outChannelInfo_, workSize_, mixLfe_); + } + return DMIX_ERR_SUCCESS; +} + +AudioChannelInfo ChannelConverter::GetInChannelInfo() const +{ + return inChannelInfo_; +} + +AudioChannelInfo ChannelConverter::GetOutChannelInfo() const +{ + return outChannelInfo_; +} + +int32_t ChannelConverter::Process(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen) +{ + if ((in == nullptr) || (out == nullptr) || frameSize <= 0 || inLen <= 0 || outLen <= 0) { + return DMIX_ERR_ALLOC_FAILED; + } + if (inChannelInfo_.numChannels < outChannelInfo_.numChannels) { + return Upmix(frameSize, in, inLen, out, outLen); + } + return downMixer_.Process(frameSize, in, inLen, out, outLen); +} + +void ChannelConverter::Reset() +{ + downMixer_.Reset(); +} + +int32_t ChannelConverter::Upmix(uint32_t frameSize, float* in, uint32_t inLen, float* out, uint32_t outLen) +{ + uint32_t expectInLen = frameSize * inChannelInfo_.numChannels * workSize_; // to be added size of other formats + uint32_t expectOutLen = frameSize * outChannelInfo_.numChannels * workSize_; + if ((expectInLen > inLen) || (expectOutLen > outLen)) { + AUDIO_ERR_LOG("expect Input Len: %{public}d, inLen: %{public}d," + "expected output len: %{public}d, outLen %{public}d", + expectInLen, inLen, expectOutLen, outLen); + return DMIX_ERR_ALLOC_FAILED; + } + for (uint32_t i = 0; i < frameSize; ++i) { + for (uint32_t ch = 0; ch < outChannelInfo_.numChannels; ++ch) { + uint32_t leftChIndex = Min(ch, inChannelInfo_.numChannels - 1); + out[i * outChannelInfo_.numChannels + ch] = in[i * inChannelInfo_.numChannels + leftChIndex]; + } + } + return DMIX_ERR_SUCCESS; +} + +} // HPAE +} // AudioStandard +} // OHOS \ No newline at end of file diff --git a/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp b/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ac3f1500b7eb0ed7d2bd8ef780dae40239e52ce --- /dev/null +++ b/services/audio_engine/plugin/channel_converter/src/down_mixer.cpp @@ -0,0 +1,1101 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_TAG +#define LOG_TAG "HpaeDownMixer" +#endif +#include +#include +#include "securec.h" +#include "audio_engine_log.h" +#include "down_mixer.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +// Initial output channel index +enum OutChannelIndex : uint32_t { + FL = 0, + FR, + FC, + SW, +// Back channel should be placed before side channel + BL, + BR +}; + +static uint32_t g_sl = 6; +static uint32_t g_sr = 7; +static uint32_t g_tfl = 8; +static uint32_t g_tfr = 9; +static uint32_t g_tbl = 10; +static uint32_t g_tbr = 11; +static uint32_t g_tsl = 12; +static uint32_t g_tsr = 13; + +static constexpr uint32_t INDEX_SIX = 6; +static constexpr uint32_t INDEX_SEVEN = 7; +static constexpr uint32_t INDEX_EIGHT = 8; +static constexpr uint32_t INDEX_NINE = 9; +static constexpr uint32_t INDEX_TEN = 10; +static constexpr uint32_t INDEX_ELEVEN = 11; + +// channel masks for downmixing general output channel layout +static constexpr uint64_t MASK_MIDDLE_FRONT = FRONT_LEFT | FRONT_RIGHT | FRONT_CENTER | +FRONT_LEFT_OF_CENTER | FRONT_RIGHT_OF_CENTER | WIDE_LEFT | WIDE_RIGHT; + +static constexpr uint64_t MASK_MIDDLE_REAR = BACK_LEFT | BACK_RIGHT | BACK_CENTER +| SIDE_LEFT +| SIDE_RIGHT; + +static constexpr uint64_t MASK_TOP_FRONT = TOP_FRONT_LEFT +| TOP_FRONT_CENTER +| TOP_FRONT_RIGHT; + +static constexpr uint64_t MASK_TOP_REAR = TOP_CENTER +| TOP_BACK_LEFT +| TOP_BACK_CENTER +| TOP_BACK_RIGHT +| TOP_SIDE_LEFT +| TOP_SIDE_RIGHT; + +static constexpr uint64_t MASK_BOTTOM = BOTTOM_FRONT_CENTER +| BOTTOM_FRONT_LEFT +| BOTTOM_FRONT_RIGHT; + +static constexpr uint64_t MASK_LFE = LOW_FREQUENCY +| LOW_FREQUENCY_2; + +static uint32_t BitCounts(uint64_t bits); +static bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts); +static AudioChannelLayout SetDefaultChannelLayout(AudioChannel channels); + +// 改成默认构造 +DownMixer::DownMixer() +{ + downMixTable_.resize(MAX_CHANNELS, std::vector(MAX_CHANNELS, 0)); +} + +// setParam +int32_t DownMixer::SetParam(AudioChannelInfo inChannelInfo, AudioChannelInfo outChannelInfo, + uint32_t formatSize, bool mixLfe) +{ + ResetSelf(); + inLayout_ = inChannelInfo.channelLayout; + outLayout_ = outChannelInfo.channelLayout; + inChannels_ = inChannelInfo.numChannels; + outChannels_ = outChannelInfo.numChannels; + mixLfe_ = mixLfe; + + formatSize_ = formatSize; + int32_t ret = SetupDownMixTable(); + if (ret == DMIX_ERR_SUCCESS) { + isInitialized_ = true; + } + return ret; +} + +int32_t DownMixer::Process(uint32_t frameLen, float* in, uint32_t inLen, float* out, uint32_t outLen) +{ + if ((in == nullptr) || (out == nullptr) || frameLen <= 0) { + AUDIO_ERR_LOG("invalid input params for downmix process, frameLen %{public}d", frameLen); + return DMIX_ERR_ALLOC_FAILED; + } + if (!isInitialized_) { + AUDIO_DEBUG_LOG("Downmixe table has not been initialized!"); + return DMIX_ERR_ALLOC_FAILED; + } + uint32_t expectInLen = frameLen * inChannels_ * formatSize_; + uint32_t expectOutLen = frameLen * outChannels_ * formatSize_; + if ((expectInLen > inLen) || (expectOutLen > outLen)) { + int32_t ret = memcpy_s(out, outLen, in, inLen); + if (ret != 0) { + AUDIO_ERR_LOG("memcpy failed when down-mixer process"); + return DMIX_ERR_ALLOC_FAILED; + } + AUDIO_ERR_LOG("expect Input Len: %{public}d, inLen: %{public}d," + "expected output len: %{public}d, outLen %{public}d", + expectInLen, inLen, expectOutLen, outLen); + return DMIX_ERR_ALLOC_FAILED; + } + AUDIO_DEBUG_LOG("Downmixing: frameLen: %{public}d,", frameLen); + float a; + for (; frameLen > 0; frameLen--) { + for (uint32_t i = 0; i < outChannels_; i++) { + a = 0.0f; + for (uint32_t j = 0; j < inChannels_; j++) { + a += in[j] * downMixTable_[i][j]; + } + *(out++) = a; + } + in += inChannels_; + } + return DMIX_ERR_SUCCESS; +} + +int32_t DownMixer::SetupDownMixTable() +{ + if ((!IsValidChLayout(inLayout_, inChannels_)) || (!IsValidChLayout(outLayout_, outChannels_)) + || inLayout_ == outLayout_ || inChannels_ <= outChannels_) { + AUDIO_ERR_LOG("invalid input or output channellayout: input channel count %{public}d, " + "inLayout_ %{public}" PRIu64 "output channel count %{public}d, outLayout_ %{public}" PRIu64 "", + inChannels_, inLayout_, outChannels_, outLayout_); + return DMIX_ERR_INVALID_ARG; + } + switch (outLayout_) { + case CH_LAYOUT_STEREO: { + SetupStereoDmixTable(); + break; + } + case CH_LAYOUT_5POINT1: { + Setup5Point1DmixTable(); + break; + } + case CH_LAYOUT_5POINT1POINT2: { + Setup5Point1Point2DmixTable(); + break; + } + case CH_LAYOUT_5POINT1POINT4: { + Setup5Point1Point4DmixTable(); + break; + } + case CH_LAYOUT_7POINT1: { + Setup7Point1DmixTable(); + break; + } + case CH_LAYOUT_7POINT1POINT2: { + Setup7Point1Point2DmixTable(); + break; + } + case CH_LAYOUT_7POINT1POINT4: { + Setup7Point1Point4DmixTable(); + break; + } + default: { + SetupGeneralDmixTable(); + break; + } + } + NormalizeDMixTable(); + AUDIO_INFO_LOG("setup downmix table success!"); + isInitialized_ = true; + return DMIX_ERR_SUCCESS; +} + +void DownMixer::NormalizeDMixTable() +{ + float maxx = 0.0f; + for (uint32_t i = 0; i < outChannels_; i++) { + float summ = 0.0f; + for (uint32_t j = 0; j < inChannels_; j++) { + summ += downMixTable_[i][j]; + } + maxx = std::max(maxx, summ); + } + + if (maxx < 1e-6) { + AUDIO_ERR_LOG("invalid channel num: in_ch = %{public}u, out_ch = %{public}u", + inChannels_, outChannels_); + maxx = 1.0f; + } else { + maxx = 1.0f / maxx; + } + + for (uint32_t i = 0; i < outChannels_; i++) { + for (uint32_t j = 0; j < inChannels_; j++) { + downMixTable_[i][j] *= maxx; + } + } +} + +void DownMixer::ResetSelf() +{ + isInitialized_ = false; + inChannels_ = 0; + outChannels_ = 0; + inLayout_ = CH_LAYOUT_UNKNOWN; + outLayout_ = CH_LAYOUT_UNKNOWN; + for (auto &row : downMixTable_) { + std::fill(row.begin(), row.end(), 0.0f); + } +} + +void DownMixer::Reset() +{ + ResetSelf(); +} + +void DownMixer::SetupStereoDmixTable() +{ + uint64_t inChMsk = inLayout_; + for (uint32_t i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = 0.f; + downMixTable_[FR][i] = 0.f; + SetupStereoDmixTablePart1(bit, i); + SetupStereoDmixTablePart2(bit, i); + inChMsk ^= bit; + } +} + +void DownMixer::Setup5Point1DmixTable() +{ + uint64_t inChMsk = inLayout_; + for (uint32_t i = 0; i < inChannels_; i++) { + downMixTable_[FL][i] = 0.f; + downMixTable_[FR][i] = 0.f; + downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = 0.f; + downMixTable_[BL][i] = 0.f; + downMixTable_[BR][i] = 0.f; + uint64_t bit = inChMsk & -(int64_t)inChMsk; + Setup5Point1DmixTablePart1(bit, i); + Setup5Point1DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} +void DownMixer::Setup5Point1Point2DmixTable() +{ + g_tsl = INDEX_SIX; + g_tsr = INDEX_SEVEN; + uint64_t inChMsk = inLayout_; + for (unsigned i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = downMixTable_[FR][i] = downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = downMixTable_[BL][i] = downMixTable_[BR][i] = 0.f; + downMixTable_[g_tsl][i] = downMixTable_[g_tsr][i] = 0.f; + Setup5Point1Point2DmixTablePart1(bit, i); + Setup5Point1Point2DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} +void DownMixer::Setup5Point1Point4DmixTable() +{ + g_tfl = INDEX_SIX; + g_tfr = INDEX_SEVEN; + g_tbl = INDEX_EIGHT; + g_tbr = INDEX_NINE; + uint64_t inChMsk = inLayout_; + for (unsigned i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = downMixTable_[FR][i] = downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = downMixTable_[BL][i] = downMixTable_[BR][i] = 0.f; + downMixTable_[g_tfl][i] = downMixTable_[g_tfr][i] = 0.f; + downMixTable_[g_tbl][i] = downMixTable_[g_tbr][i] = 0.f; + Setup5Point1Point4DmixTablePart1(bit, i); + Setup5Point1Point4DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} +void DownMixer::Setup7Point1DmixTable() +{ + g_sl = INDEX_SIX; + g_sr = INDEX_SEVEN; + uint64_t inChMsk = inLayout_; + for (unsigned i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = downMixTable_[FR][i] = downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = downMixTable_[g_sl][i] = downMixTable_[g_sr][i] = 0.f; + downMixTable_[BL][i] = downMixTable_[BR][i] = 0.f; + Setup7Point1DmixTablePart1(bit, i); + Setup7Point1DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} +void DownMixer::Setup7Point1Point2DmixTable() +{ + g_sl = INDEX_SIX; + g_sr = INDEX_SEVEN; + g_tsl = INDEX_EIGHT; + g_tsr = INDEX_NINE; + uint64_t inChMsk = inLayout_; + for (unsigned i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = downMixTable_[FR][i] = downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = downMixTable_[g_sl][i] = downMixTable_[g_sr][i] = 0.f; + downMixTable_[BL][i] = downMixTable_[BR][i] = 0.f; + downMixTable_[g_tsl][i] = downMixTable_[g_tsr][i] = 0.f; + Setup7Point1Point2DmixTablePart1(bit, i); + Setup7Point1Point2DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} +void DownMixer::Setup7Point1Point4DmixTable() +{ + g_sl = INDEX_SIX; + g_sr = INDEX_SEVEN; + g_tfl = INDEX_EIGHT; + g_tfr = INDEX_NINE; + g_tbl = INDEX_TEN; + g_tbr = INDEX_ELEVEN; + uint64_t inChMsk = inLayout_; + for (unsigned i = 0; i < inChannels_; i++) { + uint64_t bit = inChMsk & -(int64_t)inChMsk; + downMixTable_[FL][i] = downMixTable_[FR][i] = downMixTable_[FC][i] = 0.f; + downMixTable_[SW][i] = downMixTable_[g_sl][i] = downMixTable_[g_sr][i] = 0.f; + downMixTable_[BL][i] = downMixTable_[BR][i] = 0.f; + downMixTable_[g_tfl][i] = downMixTable_[g_tfr][i] = 0.f; + downMixTable_[g_tbl][i] = downMixTable_[g_tbr][i] = 0.f; + Setup7Point1Point4DmixTablePart1(bit, i); + Setup7Point1Point4DmixTablePart2(bit, i); + inChMsk ^= bit; + } +} + +void DownMixer::SetupGeneralDmixTable() +{ + // MONO + if (outLayout_ == CH_LAYOUT_MONO) { + for (uint32_t i = 0; i < inChannels_; i++) { + downMixTable_[0][i] = COEF_0DB_F; + } + } + // check invalid output musk later in init() + uint64_t outChMsk = outLayout_; + + for (uint32_t i = 0; i < outChannels_; i++) { + uint64_t outBit = outChMsk & -(int64_t)outChMsk; + uint64_t inChMsk = inLayout_; + for (uint32_t j = 0; j < inChannels_; j++) { + uint64_t inBit = inChMsk & -(int64_t)inChMsk; + if (inBit & outBit) { // if in channel and out channel is the same + downMixTable_[i][j] = COEF_0DB_F; + } else if (inBit == TOP_CENTER) { + // check general downmix table! + DownMixTopCenter(inBit, outBit, i, j); + } else if ((inBit & MASK_MIDDLE_FRONT) != 0) { + DownMixMidFront(inBit, outBit, i, j); + } else if ((inBit & MASK_MIDDLE_REAR) != 0) { + DownMixMidRear(inBit, outBit, i, j); + } else if ((inBit & MASK_BOTTOM) != 0) { + DownMixBottom(inBit, outBit, i, j); + } else if ((inBit & MASK_LFE) != 0) { + DownMixLfe(inBit, outBit, i, j); + } else if ((inBit & MASK_TOP_FRONT) != 0) { + DownMixTopFront(inBit, outBit, i, j); + } else if ((inBit & MASK_TOP_REAR) != 0) { + DownMixTopRear(inBit, outBit, i, j); + } + inChMsk ^= inBit; + } + outChMsk ^= outBit; + } +} + +void DownMixer::SetupStereoDmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case FRONT_LEFT: + case TOP_FRONT_LEFT: + case BOTTOM_FRONT_LEFT: + downMixTable_[FL][i] = COEF_0DB_F; + downMixTable_[FR][i] = COEF_ZERO_F; + break; + case FRONT_RIGHT: + case TOP_FRONT_RIGHT: + case BOTTOM_FRONT_RIGHT: + downMixTable_[FL][i] = COEF_ZERO_F; + downMixTable_[FR][i] = COEF_0DB_F; + break; + case FRONT_CENTER: + case TOP_FRONT_CENTER: + case BOTTOM_FRONT_CENTER: + downMixTable_[FL][i] = COEF_M3DB_F; + downMixTable_[FR][i] = COEF_M3DB_F; + break; + case BACK_LEFT: + case SIDE_LEFT: + case TOP_BACK_LEFT: + case TOP_SIDE_LEFT: + case WIDE_LEFT: + downMixTable_[FL][i] = COEF_M3DB_F; + downMixTable_[FR][i] = COEF_ZERO_F; + break; + case BACK_RIGHT: + case SIDE_RIGHT: + case TOP_BACK_RIGHT: + case TOP_SIDE_RIGHT: + case WIDE_RIGHT: + downMixTable_[FL][i] = COEF_ZERO_F; + downMixTable_[FR][i] = COEF_M3DB_F; + break; + case BACK_CENTER: + downMixTable_[FL][i] = COEF_M6DB_F; + downMixTable_[FR][i] = COEF_M6DB_F; + break; + default: + break; + } +} + +void DownMixer::SetupStereoDmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case FRONT_LEFT_OF_CENTER: + downMixTable_[FL][i] = COEF_M435DB_F; + downMixTable_[FR][i] = COEF_M12DB_F; + break; + case FRONT_RIGHT_OF_CENTER: + downMixTable_[FL][i] = COEF_M12DB_F; + downMixTable_[FR][i] = COEF_M435DB_F; + break; + case TOP_BACK_CENTER: + downMixTable_[FL][i] = COEF_M9DB_F; + downMixTable_[FR][i] = COEF_M9DB_F; + break; + case TOP_CENTER: + downMixTable_[FL][i] = COEF_M899DB_F; + downMixTable_[FR][i] = COEF_M899DB_F; + break; + case LOW_FREQUENCY_2: + if (mixLfe_) { + downMixTable_[FL][i] = COEF_ZERO_F; + downMixTable_[FR][i] = COEF_M6DB_F; + } + break; + case LOW_FREQUENCY: + if ((mixLfe_) & (inLayout_ & (LOW_FREQUENCY_2))) { + downMixTable_[FL][i] = COEF_M6DB_F; + downMixTable_[FR][i] = COEF_ZERO_F; + } else if (mixLfe_) { + downMixTable_[FL][i] = COEF_M6DB_F; + downMixTable_[FR][i] = COEF_M6DB_F; + } + break; + default: + break; + } +} + +void DownMixer::Setup5Point1DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case FRONT_LEFT: + case TOP_FRONT_LEFT: + case BOTTOM_FRONT_LEFT: + case WIDE_LEFT: + downMixTable_[FL][i] = COEF_0DB_F; + break; + case FRONT_RIGHT: + case TOP_FRONT_RIGHT: + case BOTTOM_FRONT_RIGHT: + case WIDE_RIGHT: + downMixTable_[FR][i] = COEF_0DB_F; + break; + case FRONT_CENTER: + case TOP_FRONT_CENTER: + case BOTTOM_FRONT_CENTER: + downMixTable_[FC][i] = COEF_0DB_F; + break; + case SIDE_LEFT: + case BACK_LEFT: + case TOP_BACK_LEFT: + case TOP_SIDE_LEFT: + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + case (BACK_RIGHT): + case (TOP_BACK_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup5Point1DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + case (TOP_BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_CENTER): + downMixTable_[FC][i] = COEF_M6DB_F; + downMixTable_[BL][i] = COEF_M6DB_F; + downMixTable_[BR][i] = COEF_M6DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup5Point1Point2DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT): + case (TOP_FRONT_LEFT): + case (BOTTOM_FRONT_LEFT): + case (WIDE_LEFT): + downMixTable_[FL][i] = COEF_0DB_F; + break; + case (FRONT_RIGHT): + case (TOP_FRONT_RIGHT): + case (BOTTOM_FRONT_RIGHT): + case (WIDE_RIGHT): + downMixTable_[FR][i] = COEF_0DB_F; + break; + case (FRONT_CENTER): + case (TOP_FRONT_CENTER): + case (BOTTOM_FRONT_CENTER): + downMixTable_[FC][i] = COEF_0DB_F; + break; + case (SIDE_LEFT): + case (BACK_LEFT): + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + case (BACK_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup5Point1Point2DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (TOP_BACK_LEFT): + case (TOP_SIDE_LEFT): + downMixTable_[g_tsl][i] = COEF_0DB_F; + break; + case (TOP_BACK_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[g_tsr][i] = COEF_0DB_F; + break; + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_BACK_CENTER): + case (TOP_CENTER): + downMixTable_[g_tsl][i] = COEF_M3DB_F; + downMixTable_[g_tsr][i] = COEF_M3DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup5Point1Point4DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT): + case (BOTTOM_FRONT_LEFT): + case (WIDE_LEFT): + downMixTable_[FL][i] = COEF_0DB_F; + break; + case (FRONT_RIGHT): + case (BOTTOM_FRONT_RIGHT): + case (WIDE_RIGHT): + downMixTable_[FR][i] = COEF_0DB_F; + break; + case (FRONT_CENTER): + case (BOTTOM_FRONT_CENTER): + downMixTable_[FC][i] = COEF_0DB_F; + break; + case (SIDE_LEFT): + case (BACK_LEFT): + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + case (BACK_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + case (TOP_FRONT_LEFT): + downMixTable_[g_tfl][i] = COEF_0DB_F; + break; + case (TOP_FRONT_RIGHT): + downMixTable_[g_tfr][i] = COEF_0DB_F; + break; + case (TOP_BACK_LEFT): + case (TOP_SIDE_LEFT): + downMixTable_[g_tbl][i] = COEF_0DB_F; + break; + case (TOP_BACK_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[g_tbr][i] = COEF_0DB_F; + break; + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + } +} + +void DownMixer::Setup5Point1Point4DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_FRONT_CENTER): + downMixTable_[g_tfl][i] = COEF_M3DB_F; + downMixTable_[g_tfr][i] = COEF_M3DB_F; + break; + case (TOP_BACK_CENTER): + downMixTable_[g_tbl][i] = COEF_M3DB_F; + downMixTable_[g_tbr][i] = COEF_M3DB_F; + break; + case (TOP_CENTER): + downMixTable_[g_tfl][i] = COEF_M6DB_F; + downMixTable_[g_tfr][i] = COEF_M6DB_F; + downMixTable_[g_tbl][i] = COEF_M6DB_F; + downMixTable_[g_tbr][i] = COEF_M6DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup7Point1DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT): + case (TOP_FRONT_LEFT): + case (BOTTOM_FRONT_LEFT): + case (WIDE_LEFT): + downMixTable_[FL][i] = COEF_0DB_F; + break; + case (FRONT_RIGHT): + case (TOP_FRONT_RIGHT): + case (BOTTOM_FRONT_RIGHT): + case (WIDE_RIGHT): + downMixTable_[FR][i] = COEF_0DB_F; + break; + case (FRONT_CENTER): + case (TOP_FRONT_CENTER): + case (BOTTOM_FRONT_CENTER): + downMixTable_[FC][i] = COEF_0DB_F; + break; + case (SIDE_LEFT): + case (TOP_SIDE_LEFT): + downMixTable_[g_sl][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[g_sr][i] = COEF_0DB_F; + break; + case (BACK_LEFT): + case (TOP_BACK_LEFT): + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (BACK_RIGHT): + case (TOP_BACK_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup7Point1DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + case (TOP_BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_CENTER): + downMixTable_[FC][i] = COEF_M6DB_F; + downMixTable_[g_sl][i] = COEF_M6DB_F; + downMixTable_[g_sr][i] = COEF_M6DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup7Point1Point2DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT): + case (TOP_FRONT_LEFT): + case (BOTTOM_FRONT_LEFT): + case (WIDE_LEFT): + downMixTable_[FL][i] = COEF_0DB_F; + break; + case (FRONT_RIGHT): + case (TOP_FRONT_RIGHT): + case (BOTTOM_FRONT_RIGHT): + case (WIDE_RIGHT): + downMixTable_[FR][i] = COEF_0DB_F; + break; + case (FRONT_CENTER): + case (TOP_FRONT_CENTER): + case (BOTTOM_FRONT_CENTER): + downMixTable_[FC][i] = COEF_0DB_F; + break; + case (SIDE_LEFT): + downMixTable_[g_sl][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + downMixTable_[g_sr][i] = COEF_0DB_F; + break; + case (BACK_LEFT): + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (BACK_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + case (TOP_BACK_LEFT): + case (TOP_SIDE_LEFT): + downMixTable_[g_tsl][i] = COEF_0DB_F; + break; + case (TOP_BACK_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[g_tsr][i] = COEF_0DB_F; + break; + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + default: + break; + } +} + +void DownMixer::Setup7Point1Point2DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_BACK_CENTER): + case (TOP_CENTER): + downMixTable_[g_tsl][i] = COEF_M3DB_F; + downMixTable_[g_tsr][i] = COEF_M3DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup7Point1Point4DmixTablePart1(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (FRONT_LEFT): + case (BOTTOM_FRONT_LEFT): + case (WIDE_LEFT): + downMixTable_[FL][i] = COEF_0DB_F; + break; + case (FRONT_RIGHT): + case (BOTTOM_FRONT_RIGHT): + case (WIDE_RIGHT): + downMixTable_[FR][i] = COEF_0DB_F; + break; + case (FRONT_CENTER): + case (BOTTOM_FRONT_CENTER): + downMixTable_[FC][i] = COEF_0DB_F; + break; + case (SIDE_LEFT): + downMixTable_[g_sl][i] = COEF_0DB_F; + break; + case (SIDE_RIGHT): + downMixTable_[g_sr][i] = COEF_0DB_F; + break; + case (BACK_LEFT): + downMixTable_[BL][i] = COEF_0DB_F; + break; + case (BACK_RIGHT): + downMixTable_[BR][i] = COEF_0DB_F; + break; + case (TOP_FRONT_LEFT): + downMixTable_[g_tfl][i] = COEF_0DB_F; + break; + case (TOP_FRONT_RIGHT): + downMixTable_[g_tfr][i] = COEF_0DB_F; + break; + case (TOP_BACK_LEFT): + case (TOP_SIDE_LEFT): + downMixTable_[g_tbl][i] = COEF_0DB_F; + break; + case (TOP_BACK_RIGHT): + case (TOP_SIDE_RIGHT): + downMixTable_[g_tbr][i] = COEF_0DB_F; + break; + default: + break; + } +} + +void DownMixer::Setup7Point1Point4DmixTablePart2(uint64_t bit, uint32_t i) +{ + switch (bit) { + case (LOW_FREQUENCY): + case (LOW_FREQUENCY_2): + if (mixLfe_) { + downMixTable_[SW][i] = COEF_0DB_F; + } + break; + case (FRONT_LEFT_OF_CENTER): + downMixTable_[FL][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (FRONT_RIGHT_OF_CENTER): + downMixTable_[FR][i] = COEF_M45DB_F; + downMixTable_[FC][i] = COEF_M3DB_F; + break; + case (BACK_CENTER): + downMixTable_[BL][i] = COEF_M3DB_F; + downMixTable_[BR][i] = COEF_M3DB_F; + break; + case (TOP_FRONT_CENTER): + downMixTable_[g_tfl][i] = COEF_M3DB_F; + downMixTable_[g_tfr][i] = COEF_M3DB_F; + break; + case (TOP_BACK_CENTER): + downMixTable_[g_tbl][i] = COEF_M3DB_F; + downMixTable_[g_tbr][i] = COEF_M3DB_F; + break; + case (TOP_CENTER): + downMixTable_[g_tfl][i] = COEF_M6DB_F; + downMixTable_[g_tfr][i] = COEF_M6DB_F; + downMixTable_[g_tbl][i] = COEF_M6DB_F; + downMixTable_[g_tbr][i] = COEF_M6DB_F; + break; + default: + break; + } +} + +void DownMixer::DownMixBottom(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + if ((inBit & MASK_BOTTOM) && (outBit & MASK_BOTTOM)) { + downMixTable_[i][j] = COEF_M3DB_F; + } else { + DownMixMidFront(inBit, outBit, i, j); + } +} + +void DownMixer::DownMixMidFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + if ((inBit & MASK_MIDDLE_FRONT) && (outBit & MASK_MIDDLE_FRONT)) { + downMixTable_[i][j] = COEF_M3DB_F; + } +} + +void DownMixer::DownMixMidRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + // Middle layer + switch (inBit) { + case BACK_CENTER: + if ((outBit == BACK_LEFT) || (outBit == SIDE_LEFT) || (outBit == WIDE_LEFT) || (outBit == FRONT_LEFT) || + (outBit == BACK_RIGHT) || outBit == SIDE_RIGHT || outBit == WIDE_RIGHT || outBit == FRONT_RIGHT) { + downMixTable_[i][j] = COEF_M3DB_F; + } + break; + case BACK_LEFT: + if (outBit == SIDE_LEFT) { + downMixTable_[i][j] = COEF_0DB_F; + } else if (outBit == BACK_CENTER) { + downMixTable_[i][j] = COEF_0DB_F; + } else { + DownMixMidFront(WIDE_LEFT, outBit, i, j); + } + break; + case BACK_RIGHT: + if (outBit == SIDE_RIGHT) { + downMixTable_[i][j] = COEF_0DB_F; + } else if (outBit == BACK_CENTER) { + downMixTable_[i][j] = COEF_0DB_F; + } else { + DownMixMidFront(WIDE_RIGHT, outBit, i, j); + } + break; + } +} + +void DownMixer::DownMixLfe(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + if ((MASK_LFE & inBit) && (MASK_LFE & outBit)) { + downMixTable_[i][j] = COEF_0DB_F; + } else { + if ((inBit == LOW_FREQUENCY) && ((outBit & CH_LAYOUT_STEREO)!= 0)) { + downMixTable_[i][j] = COEF_M6DB_F; + } else if ((inBit == LOW_FREQUENCY_2) && ((outBit & CH_LAYOUT_STEREO) != 0)) { + downMixTable_[i][j] = COEF_M6DB_F; + } + } +} + +void DownMixer::DownMixTopCenter(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + uint64_t exitTopOuts = outLayout_ & (MASK_TOP_FRONT | MASK_TOP_REAR); + uint64_t exitMiddleOuts = outLayout_ & (MASK_MIDDLE_FRONT | MASK_MIDDLE_REAR); + if (exitTopOuts != 0) { // exist top outs + uint32_t numChannels = BitCounts(exitTopOuts); + uint32_t coeff = 1.0f / sqrt((float)numChannels); + if ((outBit & exitTopOuts) != 0) { + downMixTable_[i][j] = coeff; + } + } else if (exitMiddleOuts != 0) { + uint32_t numChannels = BitCounts(exitMiddleOuts); + uint32_t coeff = 1.0f / sqrt((float)numChannels); + if ((outBit & exitMiddleOuts) != 0) { + downMixTable_[i][j] = coeff; + } + } +} + +void DownMixer::DownMixTopFront(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + uint64_t existTopFrontOuts = outLayout_ & MASK_TOP_FRONT; + if (existTopFrontOuts != 0) { + if ((outBit & MASK_TOP_FRONT) != 0) { + downMixTable_[i][j] = COEF_M3DB_F; + } + } else { + DownMixMidFront(TOP_FRONT_CENTER, outBit, i, j); + } +} + +void DownMixer::DownMixTopRear(uint64_t inBit, uint64_t outBit, uint32_t i, uint32_t j) +{ + uint64_t existTopRearOuts = outLayout_ & MASK_TOP_REAR; + if (existTopRearOuts != 0) { + if ((outBit & MASK_TOP_REAR) != 0) { + downMixTable_[i][j] = COEF_M3DB_F; + } + } else { + DownMixMidRear(BACK_CENTER, outBit, i, j); + } +} + +static uint32_t BitCounts(uint64_t bits) +{ + uint32_t num = 0; + for (; bits != 0; bits &= bits - 1) { + num++; + } + return num; +} + +static bool IsValidChLayout(AudioChannelLayout &chLayout, uint32_t chCounts) +{ + if (chCounts < MONO || chCounts > CHANNEL_16) { + return false; + } + if (chLayout == CH_LAYOUT_UNKNOWN || BitCounts(chLayout) != chCounts) { + chLayout = SetDefaultChannelLayout((AudioChannel)chCounts); + } + return true; +} + +static AudioChannelLayout SetDefaultChannelLayout(AudioChannel channels) +{ + if (channels < MONO || channels > CHANNEL_16) { + return CH_LAYOUT_UNKNOWN; + } + switch (channels) { + case MONO: + return CH_LAYOUT_MONO; + case STEREO: + return CH_LAYOUT_STEREO; + case CHANNEL_3: + return CH_LAYOUT_SURROUND; + case CHANNEL_4: + return CH_LAYOUT_3POINT1; + case CHANNEL_5: + return CH_LAYOUT_4POINT1; + case CHANNEL_6: + return CH_LAYOUT_5POINT1; + case CHANNEL_7: + return CH_LAYOUT_6POINT1; + case CHANNEL_8: + return CH_LAYOUT_5POINT1POINT2; + case CHANNEL_10: + return CH_LAYOUT_7POINT1POINT2; + case CHANNEL_12: + return CH_LAYOUT_7POINT1POINT4; + case CHANNEL_14: + return CH_LAYOUT_9POINT1POINT4; + case CHANNEL_16: + return CH_LAYOUT_9POINT1POINT6; + default: + return CH_LAYOUT_UNKNOWN; + } +} + +} // HPAE +} // AudioStandard +} // OHOS \ No newline at end of file diff --git a/services/audio_engine/plugin/resample/include/audio_proresampler.h b/services/audio_engine/plugin/resample/include/audio_proresampler.h new file mode 100644 index 0000000000000000000000000000000000000000..0b3aa0f00ed01bc322deaf9941995952bdd37d20 --- /dev/null +++ b/services/audio_engine/plugin/resample/include/audio_proresampler.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef AUDIO_PRORESAMPLER_H +#define AUDIO_PRORESAMPLER_H + +#include "resampler.h" +#include "audio_proresampler_process.h" +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class ProResampler : public Resampler { +public: + ProResampler(uint32_t inRate, uint32_t outRate, uint32_t channels, uint32_t quality); + ~ProResampler() override; + // disable deep copy, enable move semantics to manage memory allocated by C malloc + ProResampler(const ProResampler &) = delete; + ProResampler &operator=(const ProResampler &) = delete; + ProResampler(ProResampler &&) noexcept; + ProResampler &operator=(ProResampler &&) noexcept; + void Reset() override; + int Process(const float *inBuffer, uint32_t *inFrameSize, float *outBuffer, uint32_t *outFrameSize) + override; + int32_t UpdateRates(uint32_t inRate, uint32_t outRate) override; + void UpdateChannels(uint32_t channels) override; + uint32_t GetInRate() const override; + uint32_t GetOutRate() const override; + uint32_t GetChannels() const override; + uint32_t GetQuality() const; +private: + std::string ErrCodeToString(int32_t errCode); + uint32_t inRate_; + uint32_t outRate_; + uint32_t channels_; + uint32_t quality_; + SingleStagePolyphaseResamplerState* state_ = nullptr; +}; +} // HPAE +} // AudioStandard +} // OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/resample/include/audio_proresampler_process.h b/services/audio_engine/plugin/resample/include/audio_proresampler_process.h new file mode 100644 index 0000000000000000000000000000000000000000..506dd40fddfb0acfd4381245a9c226d9e70c5518 --- /dev/null +++ b/services/audio_engine/plugin/resample/include/audio_proresampler_process.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef AUDIO_PRORESAMPLER_PROCESS_H +#define AUDIO_PRORESAMPLER_PROCESS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum MultiplyFilterFunMethod { + MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_UP, + MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_UP, + MULTIPLY_FILTER_FUN_UP, + MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_DOWN, + MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_DOWN, + MULTIPLY_FILTER_FUN_DOWN, + MULTIPLY_FILTER_FUN_MAX + } MultiplyFilterFunT; + + enum { + RESAMPLER_ERR_SUCCESS = 0, + RESAMPLER_ERR_ALLOC_FAILED = -1, + RESAMPLER_ERR_INVALID_ARG = -2, + RESAMPLER_ERR_OVERFLOW = -3 + }; + + #define MAX_RATIO_INTEGRAL_METHOD 32 + + typedef struct SingleStagePolyphaseResamplerState SingleStagePolyphaseResamplerState; + + typedef int32_t (*ResamplerMethod)(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength); + typedef void (*MultiplyFilterFun)(SingleStagePolyphaseResamplerState* state, + const float* coeffs, const float* inputs, float* outputs, int32_t subfilterNum); + + + /** + * @brief Resampler state + * + */ + struct SingleStagePolyphaseResamplerState { + uint32_t decimateFactor; /** Integer decimation factor */ + uint32_t interpolateFactor; /** Integer interpolation factor */ + + int32_t quality; /** Parameter for resampler quality (0~10) */ + uint32_t numChannels; /** Number of channels */ + uint32_t filterLength; /** Number of taps of anti-aliasing/imaging filter */ + uint32_t bufferSize; /** Number of buffer samples for each channel */ + int32_t quoSamplerateRatio; /** Quotient of (input sampling frequency)/(output sampling frequency) */ + int32_t remSamplerateRatio; /** remainder of (input sampling frequency)/(output sampling frequency) */ + float cutoff; /** Normalized cutoff frequency of anti-aliasing/imaging filter */ + float coshParameter; /** Parameter of cosh window for adjusting side-lobe decay of filter */ + int32_t isInitialized; /** If the state is initialized, isInitialized=1. */ + int32_t isStarted; /** Once the resampler has processed, isStarted = 1. */ + + int32_t inputIndex; /** Index of the input to be processed. */ + uint32_t subfilterNum; /** What number of polyphase subfilters to use. */ + uint32_t magicSamples; /** Used for variable sampling frequency (don't need this?) */ + + float* inputMemory; /** An array that stores the inputs to be processed. */ + uint32_t inputMemorySize; /** Size of inputMemory. (bufferSize + filterLength + 1) */ + float* filterCoefficients; /** An array that stores the polyphase filters. */ + uint32_t filterCoefficientsSize; /** Size of filterCoefficients. */ + ResamplerMethod resamplerFunction; /** A pointer to the function used for resampling. */ + MultiplyFilterFun multiplyFunSeq[MAX_RATIO_INTEGRAL_METHOD]; + }; + +/** + * @brief Create a new resampler state. + * + * @param numChannels Number of channels. + * @param decimateFactor Integer decimation factor (input sampling frequency). + * @param interpolateFactor Integer interpolation factor (output sampling frequency). + * @param quality Parameter for determining resampling quality level between 0 (poor) and 10 (best). + * @param err + * @return SingleStagePolyphaseResamplerState* Created resampler state used for process + */ + SingleStagePolyphaseResamplerState* SingleStagePolyphaseResamplerInit(uint32_t numChannels, + uint32_t decimateFactor, uint32_t interpolateFactor, int32_t quality, int32_t* err); + + + /** + * @brief Set (update) the input/output sampling rates. + * + * @param state Resampler state + * @param decimateFactor Integer decimation factor (input sampling frequency). + * @param interpolateFactor Integer interpolation factor (output sampling frequency). + * @return int32_t returns 0 if the function terminates normally. + */ + int32_t SingleStagePolyphaseResamplerSetRate(SingleStagePolyphaseResamplerState* state, + uint32_t decimateFactor, uint32_t interpolateFactor); + + /** + * @brief + * + * @param state Resampler state. + * @return int32_t returns 0 if the function terminates normally. + */ + int32_t SingleStagePolyphaseResamplerSkipHalfTaps(SingleStagePolyphaseResamplerState* state); + + + /** + * @brief Reset the buffers of the resampler. + * + * @param state Resampler state. + * @return int32_t returns 0 if the function terminates normally. + */ + int32_t SingleStagePolyphaseResamplerResetMem(SingleStagePolyphaseResamplerState* state); + + /** + * @brief Release the memory for the resampler state + * + * @param state Resampler state. + */ + void SingleStagePolyphaseResamplerFree(SingleStagePolyphaseResamplerState* state); + + + /** + * @brief Resample an input array. + * + * @param state resampler state + * @param in Input array. + * @param inputLength Number of input samples. + * @param out Output array. + * @param outputLength Number of input samples. + * @return int32_t returns 0 if the function terminates normally. + */ + int32_t SingleStagePolyphaseResamplerProcess(SingleStagePolyphaseResamplerState* state, + const float* in, uint32_t* inputLength, float* out, uint32_t* outputLength); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/resample/include/resampler.h b/services/audio_engine/plugin/resample/include/resampler.h new file mode 100644 index 0000000000000000000000000000000000000000..7c088a2f97f307a0d2a5fd12dcef290b79a3e94f --- /dev/null +++ b/services/audio_engine/plugin/resample/include/resampler.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RESAMPLER_H +#define RESAMPLER_H +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class Resampler { +public: + virtual ~Resampler() = default; + virtual void Reset() = 0; + virtual int Process(const float *in_buffer, uint32_t *inFrameSize, float *out_buffer, + uint32_t *outFrameSize) = 0; + virtual int32_t UpdateRates(uint32_t inRate, uint32_t outRate) = 0; + virtual uint32_t GetInRate() const = 0; + virtual uint32_t GetOutRate() const = 0; + virtual uint32_t GetChannels() const = 0; + virtual void UpdateChannels(uint32_t channels) = 0; +}; + +} // HPAE +} // AudioStandard +} // OHOS + +#endif \ No newline at end of file diff --git a/services/audio_engine/plugin/resample/proresampler/audio_proresampler.cpp b/services/audio_engine/plugin/resample/proresampler/audio_proresampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99d0e0b33b5f8dd907c05e87b2c2476cdbbc9b78 --- /dev/null +++ b/services/audio_engine/plugin/resample/proresampler/audio_proresampler.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "audio_proresampler.h" +#include "audio_policy_log.h" +#include "securec.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +ProResampler::ProResampler(uint32_t inRate, uint32_t outRate, uint32_t channels, uint32_t quality) + : inRate_(inRate), outRate_(outRate), channels_(channels), quality_(quality) +{ + int32_t errRet; + state_ = SingleStagePolyphaseResamplerInit(channels_, inRate_, outRate_, quality_, &errRet); + if (state_) { + SingleStagePolyphaseResamplerSkipHalfTaps(state_); + AUDIO_INFO_LOG("Proresampler: Init success inRate: %{public}d, outRate: %{public}d, channels: %{public}d, " + "quality: %{public}d.", inRate_, outRate_, channels_, quality_); + } else { + AUDIO_ERR_LOG("Proresampler: Init failed! failed with error %{public}s.", + ErrCodeToString(errRet).c_str()); + } +} + +int32_t ProResampler::Process(const float *inBuffer, uint32_t *inFrameSize, float *outBuffer, + uint32_t *outFrameSize) +{ + CHECK_AND_RETURN_RET_LOG(state_ != nullptr, RESAMPLER_ERR_ALLOC_FAILED, + "ProResampler Process: resampler is %{public}s", ErrCodeToString(RESAMPLER_ERR_ALLOC_FAILED).c_str()); + uint32_t expectedOutFrameSize = *outFrameSize; + std::vector tmpOutBuf(expectedOutFrameSize * channels_, 0.0f); + int32_t ret = + SingleStagePolyphaseResamplerProcess(state_, inBuffer, inFrameSize, tmpOutBuf.data(), outFrameSize); + if (ret != 0) { + AUDIO_WARNING_LOG("ProResampler process failed with error %{public}s", ErrCodeToString(ret).c_str()); + } + uint32_t fillZeroSize = expectedOutFrameSize - *outFrameSize > 0 ? expectedOutFrameSize - *outFrameSize : 0; + ret += memset_s(outBuffer, expectedOutFrameSize * channels_ * sizeof(float), 0, + fillZeroSize * channels_ * sizeof(float)); + ret += memcpy_s(outBuffer + fillZeroSize * channels_, + (expectedOutFrameSize - fillZeroSize) * channels_ * sizeof(float), + tmpOutBuf.data(), *outFrameSize * channels_ * sizeof(float)); + if (ret != EOK) { + ret = RESAMPLER_ERR_ALLOC_FAILED; + } + return ret; +} + +int32_t ProResampler::UpdateRates(uint32_t inRate, uint32_t outRate) +{ + inRate_ = inRate; + outRate_ = outRate; + CHECK_AND_RETURN_RET_LOG(state_ != nullptr, RESAMPLER_ERR_ALLOC_FAILED, "ProResampler: resampler is null"); + int32_t ret = SingleStagePolyphaseResamplerSetRate(state_, inRate_, outRate_); + if (ret != 0) { + AUDIO_WARNING_LOG("ProResampler update rate failed with error code %{public}s", ErrCodeToString(ret).c_str()); + } + return ret; +} + +void ProResampler::UpdateChannels(uint32_t channels) +{ + uint32_t oldChannels = channels_; + channels_ = channels; + SingleStagePolyphaseResamplerFree(state_); + int32_t errRet; + state_ = SingleStagePolyphaseResamplerInit(channels_, inRate_, outRate_, quality_, &errRet); + if (state_) { + SingleStagePolyphaseResamplerSkipHalfTaps(state_); + AUDIO_INFO_LOG("Proresampler: update work channel success old channels: %{public}d, new channels: %{public}d", + oldChannels, channels_); + } else { + AUDIO_ERR_LOG("Proresampler: update work channels failed with error %{public}s.", + ErrCodeToString(errRet).c_str()); + } +} + +ProResampler::ProResampler(ProResampler &&other) noexcept + : inRate_(other.inRate_), outRate_(other.outRate_), channels_(other.channels_), + quality_(other.quality_), state_(other.state_) +{ + other.state_ = nullptr; +} + +ProResampler &ProResampler::operator=(ProResampler &&other) noexcept +{ + if (this != &other) { + if (state_ != nullptr) { + SingleStagePolyphaseResamplerFree(state_); + } + inRate_ = other.inRate_; + outRate_ = other.outRate_; + channels_ = other.channels_; + quality_ = other.quality_; + state_ = other.state_; + other.state_ = nullptr; + } + return *this; +} + +void ProResampler::Reset() +{ + CHECK_AND_RETURN_LOG(state_ != nullptr, "ProResampler: resampler is null"); + SingleStagePolyphaseResamplerResetMem(state_); + SingleStagePolyphaseResamplerSkipHalfTaps(state_); +} + +uint32_t ProResampler::GetInRate() const +{ + return inRate_; +} + +uint32_t ProResampler::GetOutRate() const +{ + return outRate_; +} + +uint32_t ProResampler::GetChannels() const +{ + return channels_; +} + +uint32_t ProResampler::GetQuality() const +{ + return quality_; +} + +ProResampler::~ProResampler() +{ + if (state_ != nullptr) { + SingleStagePolyphaseResamplerFree(state_); + state_ = nullptr; + } +} + +std::string ProResampler::ErrCodeToString(int32_t errCode) +{ + switch (errCode) { + case RESAMPLER_ERR_SUCCESS: { + return "RESAMPLER_ERR_SUCCESS"; + break; + } + case RESAMPLER_ERR_ALLOC_FAILED: { + return "RESAMPLER_ERR_ALLOC_FAILED"; + break; + } + case RESAMPLER_ERR_INVALID_ARG: { + return "RESAMPLER_ERR_INVALID_ARG"; + break; + } + case RESAMPLER_ERR_OVERFLOW: { + return "RESAMPLER_ERR_OVERFLOW"; + break; + } + default: { + return "Unknown Error Code"; + } + } +} + +} // HPAE +} // AudioStandard +} // OHOS \ No newline at end of file diff --git a/services/audio_engine/plugin/resample/proresampler/audio_proresampler_process.c b/services/audio_engine/plugin/resample/proresampler/audio_proresampler_process.c new file mode 100644 index 0000000000000000000000000000000000000000..1d9449d8ab4bd19cde51925554d5e6d87efe997a --- /dev/null +++ b/services/audio_engine/plugin/resample/proresampler/audio_proresampler_process.c @@ -0,0 +1,1544 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "audio_proresampler_process.h" +#include "audio_engine_log.h" +#include "securec.h" + +/** + * @brief Ratio of circumference to diameter + * + */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/** + * @brief Maximum number of numChannels + * + */ +#define MAX_NUM_CHANNEL 16 + + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef MONO +#define MONO 1 +#endif + +#ifndef STEREO +#define STEREO 2 +#endif + +#define TWO_STEPS 2 +#define THREE_STEPS 3 +#define FOUR_STEPS 4 +#define QUALITY_LEVEL_TEN 10 +#define BUFFER_SIZE 160 + +// WARNING: Code for support to sudden changes in sampling frequency is deprecated! +// It is disabled because it is complex and untested. +// It is desirable to re-initialize for such a change in the sampling frequency. + +/** + * @brief A function that compares two values and returns the smaller one + * + */ + +static inline uint32_t CompareMin(uint32_t a, uint32_t b) +{ + return a < b ? a : b; +} + +/** + * @brief A function that compares two values and returns the larger one + * + */ +static inline uint32_t CompareMax(uint32_t a, uint32_t b) +{ + return a < b ? b : a; +} + +/** + * @brief A function that helps update resampler state + * +*/ +static int32_t UpdateFilterMemory(SingleStagePolyphaseResamplerState* state, uint32_t oldFilterLength); +struct QualityTable { + int32_t filterLength; + float coshParameter; +}; + + +/** + * @brief This table contains internal parameters corresponding to quality levels. + * + * The relationship between coshParameter and side lobe decay is as follows: + * coshParameter = -8.722e-5 * attenuation^TWO_STEPS + 0.1335 * attenuation - 1.929 (50 < attenuation) + */ +static const struct QualityTable QUALITY_TABLE[11] = { + { 8, 5.767008}, /* Q0 */ + { 16, 5.767008}, /* Q1 */ + { 32, 5.767008}, /* Q2 */ /* ( ~60 dB stop) 6 */ + { 48, 8.192792}, /* Q3 */ /* ( ~80 dB stop) 8 */ + { 64, 8.192792}, /* Q4 */ /* ( ~80 dB stop) 8 */ + { 80, 10.5488}, /* Q5 */ /* (~100 dB stop) 10 */ + { 96, 10.5488}, /* Q6 */ /* (~100 dB stop) 10 */ + {128, 10.5488}, /* Q7 */ /* (~100 dB stop) 10 */ + {160, 10.5488}, /* Q8 */ /* (~100 dB stop) 10 */ + {192, 10.5488}, /* Q9 */ /* (~100 dB stop) 10 */ + {256, 10.5488}, /* Q10 */ /* (~100 dB stop) 10 */ +}; + + +static double CompHyperbolicCosineWindow(double x, float alpha) +{ + double x2; + double w; + + x2 = x * x; + if (x2 >= 1.0) { + return 0.f; + } + + w = alpha * sqrt(1.0f - x2); + w = cosh(w) / cosh(alpha); + return w; +} + + +static float Sinc(float x) +{ + if (fabs(x) < 1e-6) { + return 1.0f; + } + return sin(M_PI * x) / (M_PI * x); +} + + +static int32_t CalculateFilter(SingleStagePolyphaseResamplerState* state) +{ + uint32_t i; + uint32_t j; + float phi0 = 0; + float phi = 0; + double w; + uint32_t requiredFilterCoefficientsSize; + float cutoff = state->cutoff; + + if (INT_MAX / sizeof(float) / state->interpolateFactor < state->filterLength) { + return RESAMPLER_ERR_ALLOC_FAILED; + } + + requiredFilterCoefficientsSize = state->filterLength * state->interpolateFactor; + + if (state->filterCoefficientsSize < requiredFilterCoefficientsSize) { + if (state->filterCoefficients == NULL) { + state->filterCoefficients = (float*)malloc(requiredFilterCoefficientsSize * sizeof(float)); + if (!state->filterCoefficients) { + return RESAMPLER_ERR_ALLOC_FAILED; + } + } else { + float* filterCoefficients = (float*)malloc(requiredFilterCoefficientsSize * sizeof(float)); + if (!filterCoefficients) { + return RESAMPLER_ERR_ALLOC_FAILED; + } + if (memcpy_s(filterCoefficients, requiredFilterCoefficientsSize * sizeof(float), + state->filterCoefficients, state->filterCoefficientsSize * sizeof(float)) != 0) { + return RESAMPLER_ERR_ALLOC_FAILED; + } + free(state->filterCoefficients); + state->filterCoefficients = filterCoefficients; + } + state->filterCoefficientsSize = requiredFilterCoefficientsSize; + } + + for (i = 0; i < state->interpolateFactor; i++) { + for (j = 0; j < state->filterLength; j++) { + phi = (j - (int32_t)state->filterLength / TWO_STEPS + 1) - phi0; + w = CompHyperbolicCosineWindow(fabs((double)TWO_STEPS * phi / state->filterLength), + state->coshParameter); + state->filterCoefficients[i * state->filterLength + j] = w * cutoff * Sinc(cutoff * phi); + } + phi0 += 1.0 / state->interpolateFactor; + } + return 0; +} + +/*====== Filter multiplication function for general cases =====*/ +static void MultiplyFilterMono(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + float sum = 0; + + for (int32_t j = 0; j < state->filterLength; j += FOUR_STEPS) { + sum += (*coeffs++) * (*inputs++); + sum += (*coeffs++) * (*inputs++); + sum += (*coeffs++) * (*inputs++); + sum += (*coeffs++) * (*inputs++); + } + *outputs = sum; +} + +static void MultiplyFilterStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + float sumL = 0; + float sumR = 0; + float h; + + for (int32_t j = 0; j < state->filterLength; j += FOUR_STEPS) { + h = *coeffs++; + sumL += h * (*inputs++); + sumR += h * (*inputs++); + + h = *coeffs++; + sumL += h * (*inputs++); + sumR += h * (*inputs++); + + h = *coeffs++; + sumL += h * (*inputs++); + sumR += h * (*inputs++); + + h = *coeffs++; + sumL += h * (*inputs++); + sumR += h * (*inputs++); + } + *outputs++ = sumL; + *outputs = sumR; +} + +static void MultiplyFilterMultichannel(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t numChannels = state->numChannels; + int32_t ch; + int32_t j; + float h; + float sum[MAX_NUM_CHANNEL]; + + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = 0; + } + for (j = 0; j < state->filterLength; j += FOUR_STEPS) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (*inputs++); + } + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (*inputs++); + } + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (*inputs++); + } + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (*inputs++); + } + } + for (ch = 0; ch < numChannels; ch++) { + *outputs++ = sum[ch]; + } +} + +/*===== Filter multiplication function for coarse (integral) upsampling =====*/ +static void MultiplyFilterSymmetricOddUpMono(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + *outputs = inputs[state->filterLength / TWO_STEPS - 1]; +} + +static void MultiplyFilterSymmetricOddUpStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + *outputs++ = inputs[state->filterLength - TWO_STEPS]; + *outputs = inputs[state->filterLength - 1]; +} + +static void MultiplyFilterSymmetricOddUpMultichannel(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t indCenter = state->filterLength / TWO_STEPS - 1; + for (int32_t ch = 0; ch < state->numChannels; ch++) { + *outputs++ = inputs[state->numChannels * indCenter + ch]; + } +} + +static void MultiplyFilterSymmetricEvenUpMono(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + float sum = 0; + + for (int32_t j = 0; j < n / TWO_STEPS; j += FOUR_STEPS) { + sum += (*coeffs++) * (inputs[j] + inputs[(n - j - 1)]); + sum += (*coeffs++) * (inputs[j + 1] + inputs[(n - (j + 1) - 1)]); + sum += (*coeffs++) * (inputs[j + TWO_STEPS] + inputs[(n - (j + TWO_STEPS) - 1)]); + sum += (*coeffs++) * (inputs[j + THREE_STEPS] + inputs[(n - (j + THREE_STEPS) - 1)]); + } + *outputs = sum; +} + +static void MultiplyFilterSymmetricEvenUpStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + float sumL = 0; + float sumR = 0; + float h; + + for (int32_t j = 0; j < n / TWO_STEPS; j += FOUR_STEPS) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - 1) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - 1) * STEREO + 1]); + h = *coeffs++; + sumL += h * (inputs[(j + 1) * STEREO] + inputs[(n - (j + 1) - 1) * STEREO]); + sumR += h * (inputs[(j + 1) * STEREO + 1] + inputs[(n - (j + 1) - 1) * STEREO + 1]); + h = *coeffs++; + sumL += h * (inputs[(j + TWO_STEPS) * STEREO] + inputs[(n - (j + TWO_STEPS) - 1) * STEREO]); + sumR += h * (inputs[(j + TWO_STEPS) * STEREO + 1] + inputs[(n - (j + TWO_STEPS) - 1) * STEREO + 1]); + h = *coeffs++; + sumL += h * (inputs[(j + THREE_STEPS) * STEREO] + inputs[(n - (j + THREE_STEPS) - 1) * STEREO]); + sumR += h * (inputs[(j + THREE_STEPS) * STEREO + 1] + inputs[(n - (j + THREE_STEPS) - 1) * STEREO + 1]); + } + *outputs++ = sumL; + *outputs++ = sumR; +} + +static void MultiplyFilterSymmetricEvenUpMultichannel(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t numChannels = state->numChannels; + int32_t ch; + int32_t j; + float sum[MAX_NUM_CHANNEL]; + float h; + + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = 0; + } + for (j = 0; j < n / TWO_STEPS; j++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - 1) * numChannels + ch]); + } + } + for (ch = 0; ch < numChannels; ch++) { + *outputs++ = sum[ch]; + } +} + +static void MultiplyFilterSymmetricOddDownMono(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + float sum; + int32_t rem = indCenter % decimateFactor; + int32_t len = indCenter / decimateFactor; + int32_t i; + int32_t j; + int32_t l; + // center index + sum = coeffs[indCenter] * inputs[indCenter]; + // symmetric indices + for (j = 0; j < rem; j++) { + sum += (*coeffs++) * (inputs[j] + inputs[(n - j - TWO_STEPS)]); + } + for (l = 0; l < len; l++) { + coeffs++; + j++; + for (i = 1; i < decimateFactor; i++) { + sum += (*coeffs++) * (inputs[j] + inputs[(n - j - TWO_STEPS)]); + j++; + } + } + + *outputs = sum; +} + +static void MultiplyFilterSymmetricOddDownStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + float sumL; + float sumR; + float h; + int32_t rem = indCenter % decimateFactor; + int32_t len = indCenter / decimateFactor; + int32_t i; + int32_t j; + int32_t l; + + // center + h = coeffs[indCenter]; + sumL = h * inputs[n - TWO_STEPS]; + sumR = h * inputs[n - 1]; + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - TWO_STEPS) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - TWO_STEPS) * STEREO + 1]); + } + for (l = 0; l < len; l++) { + coeffs++; + j++; + for (i = 1; i < decimateFactor; i++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - TWO_STEPS) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - TWO_STEPS) * STEREO + 1]); + j++; + } + } + + *outputs++ = sumL; + *outputs = sumR; +} + +static void MultiplyFilterSymmetricOddDownMultichannel(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t numChannels = state->numChannels; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + int32_t i; + int32_t j; + int32_t l; + int32_t ch; + float sum[MAX_NUM_CHANNEL]; + float h; + int32_t rem = indCenter % decimateFactor; + int32_t len = indCenter / decimateFactor; + + // center index + h = coeffs[indCenter]; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = h * (inputs[indCenter * numChannels + ch]); + } + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - TWO_STEPS) * numChannels + ch]); + } + } + for (l = 0; l < len; l++) { + coeffs++; + j++; + for (i = 1; i < decimateFactor; i++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - TWO_STEPS) * numChannels + ch]); + } + j++; + } + } + + for (ch = 0; ch < numChannels; ch++) { + *outputs++ = sum[ch]; + } +} + + +static void MultiplyFilterSymmetricEvenDownMono(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const uint32_t decimateFactor = state->decimateFactor; + int32_t i; + int32_t j; + int32_t l; + int32_t rem = (n / TWO_STEPS) % decimateFactor; + int32_t len = (n / TWO_STEPS) / decimateFactor; + float h; + + float sum = 0; + + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + sum += h * (inputs[j] + inputs[(n - j - 1)]); + } + for (l = 0; l < len; l++) { + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + sum += h * (inputs[j] + inputs[(n - j - 1)]); + j++; + } + // Skip zero + coeffs++; + j++; + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + sum += h * (inputs[j] + inputs[(n - j - 1)]); + j++; + } + } + *outputs = sum; +} + +static void MultiplyFilterSymmetricEvenDownStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const uint32_t decimateFactor = state->decimateFactor; + int32_t i; + int32_t j; + int32_t l; + int32_t rem = (n / TWO_STEPS) % decimateFactor; + int32_t len = (n / TWO_STEPS) / decimateFactor; + float h; + + float sumL = 0; + float sumR = 0; + + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - 1) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - 1) * STEREO + 1]); + } + for (l = 0; l < len; l++) { + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - 1) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - 1) * STEREO + 1]); + j++; + } + // Skip zero + coeffs++; + j++; + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - 1) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - 1) * STEREO + 1]); + j++; + } + } + *outputs++ = sumL; + *outputs++ = sumR; +} + + +static void MultiplyFilterSymmetricEvenDownMultichannel(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t numChannels = state->numChannels; + const uint32_t decimateFactor = state->decimateFactor; + int32_t i; + int32_t j; + int32_t l; + int32_t ch; + float sum[MAX_NUM_CHANNEL]; + float h; + int32_t rem = (n / TWO_STEPS) % decimateFactor; + int32_t len = (n / TWO_STEPS) / decimateFactor; + + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = 0; + } + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - 1) * numChannels + ch]); + } + } + for (l = 0; l < len; l++) { + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - 1) * numChannels + ch]); + } + j++; + } + // Skip zero + coeffs++; + j++; + for (i = 0; i < decimateFactor / TWO_STEPS; i++) { + h = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - 1) * numChannels + ch]); + } + j++; + } + } + + for (ch = 0; ch < numChannels; ch++) { + *outputs++ = sum[ch]; + } +} + +static void MultiplyFilterDownMono(SingleStagePolyphaseResamplerState *state, const float *coeffs, + const float *inputs, float *outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + int32_t j; + float h; + float sum = 0; + + int32_t counts = indCenter % decimateFactor - subfilterNum; + if (counts < 0) { + counts += state->interpolateFactor; + } + + for (j = 0; j < n; j++) { + h = *coeffs++; + if (counts == 0) { // Skip zero coefficients + counts = decimateFactor - 1; + inputs++; + continue; + } + sum += h * (*inputs++); + counts--; + } + *outputs = sum; +} + +static void MultiplyFilterDownStereo(SingleStagePolyphaseResamplerState* state, const float* coeffs, + const float* inputs, float* outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + int32_t j; + float sumL = 0; + float sumR = 0; + float h; + + int32_t counts = indCenter % decimateFactor - subfilterNum; + if (counts < 0) { + counts += state->interpolateFactor; + } + + for (j = 0; j < n; j++) { + h = *coeffs++; + if (counts == 0) { // Skip zero coefficients + counts = decimateFactor - 1; + inputs += STEREO; + continue; + } + sumL += h * (*inputs++); + sumR += h * (*inputs++); + counts--; + } + *outputs++ = sumL; + *outputs = sumR; +} + +static void MultiplyFilterDownMultichannel(SingleStagePolyphaseResamplerState *state, const float *coeffs, + const float *inputs, float *outputs, int32_t subfilterNum) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + const uint32_t decimateFactor = state->decimateFactor; + const int32_t numChannels = state->numChannels; + int32_t j; + int32_t ch; + float h; + float sum[MAX_NUM_CHANNEL]; + + int32_t counts = indCenter % decimateFactor - subfilterNum; + if (counts < 0) { + counts += state->interpolateFactor; + } + + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = 0; + } + for (j = 0; j < n; j++) { + h = *coeffs++; + if (counts == 0) { // Skip zero coefficients + counts = decimateFactor - 1; + inputs += numChannels; + continue; + } + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (*inputs++); + } + counts--; + } + for (ch = 0; ch < numChannels; ch++) { + *outputs++ = sum[ch]; + } +} + + +/*===== Function pointers of filter multiplication functions =====*/ +static MultiplyFilterFun multiplyFilterFunTable[] = { + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_UP] = MultiplyFilterSymmetricOddUpMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_UP + 1] = MultiplyFilterSymmetricOddUpStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_UP + TWO_STEPS] = MultiplyFilterSymmetricOddUpMultichannel, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_UP] = MultiplyFilterSymmetricEvenUpMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_UP + 1] = MultiplyFilterSymmetricEvenUpStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_UP + TWO_STEPS] = MultiplyFilterSymmetricEvenUpMultichannel, + [THREE_STEPS * MULTIPLY_FILTER_FUN_UP] = MultiplyFilterMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_UP + 1] = MultiplyFilterStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_UP + TWO_STEPS] = MultiplyFilterMultichannel, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_DOWN] = MultiplyFilterSymmetricOddDownMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_DOWN + 1] = MultiplyFilterSymmetricOddDownStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_DOWN + TWO_STEPS] = + MultiplyFilterSymmetricOddDownMultichannel, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_DOWN] = MultiplyFilterSymmetricEvenDownMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_DOWN + 1] = MultiplyFilterSymmetricEvenDownStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_DOWN + TWO_STEPS] = + MultiplyFilterSymmetricEvenDownMultichannel, + [THREE_STEPS * MULTIPLY_FILTER_FUN_DOWN] = MultiplyFilterDownMono, + [THREE_STEPS * MULTIPLY_FILTER_FUN_DOWN + 1] = MultiplyFilterDownStereo, + [THREE_STEPS * MULTIPLY_FILTER_FUN_DOWN + TWO_STEPS] = MultiplyFilterDownMultichannel +}; + +static int32_t PolyphaseResamplerMono(SingleStagePolyphaseResamplerState *state, const float *in, uint32_t *inputLength, + float *out, uint32_t *outputLength) +{ + const int32_t n = state->filterLength; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + uint32_t subfilterNum = state->subfilterNum; + const float* filterCoefficients = state->filterCoefficients; + const int32_t quoSamplerateRatio = state->quoSamplerateRatio; + const int32_t remSamplerateRatio = state->remSamplerateRatio; + const uint32_t decimateFactor = state->decimateFactor; + const uint32_t interpolateFactor = state->interpolateFactor; + int32_t i; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), ((interpolateFactor * ((*inputLength) - inputIndex) - + subfilterNum) - 1) / decimateFactor + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[subfilterNum * n]; + const float* inputs = &in[inputIndex * MONO]; + MultiplyFilterMono(state, coeffs, inputs, out, subfilterNum); + out++; + + inputIndex += quoSamplerateRatio; + subfilterNum += remSamplerateRatio; + if (subfilterNum >= interpolateFactor) { + subfilterNum -= interpolateFactor; + inputIndex++; + } + } + + state->inputIndex = inputIndex; + state->subfilterNum = subfilterNum; + return outSample; +} + +static int32_t PolyphaseResamplerStereo(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + uint32_t subfilterNum = state->subfilterNum; + const float* filterCoefficients = state->filterCoefficients; + const int32_t quoSamplerateRatio = state->quoSamplerateRatio; + const int32_t remSamplerateRatio = state->remSamplerateRatio; + const uint32_t decimateFactor = state->decimateFactor; + const uint32_t interpolateFactor = state->interpolateFactor; + int32_t i; + + if (inputIndex < (int32_t) (*inputLength)) { + outSample = CompareMin((*outputLength), ((interpolateFactor * ((*inputLength) - inputIndex) - + subfilterNum) - 1) / decimateFactor + 1); + } + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[subfilterNum * n]; + const float* inputs = &in[inputIndex * STEREO]; + MultiplyFilterStereo(state, coeffs, inputs, out, subfilterNum); + out += STEREO; + + inputIndex += quoSamplerateRatio; + subfilterNum += remSamplerateRatio; + if (subfilterNum >= interpolateFactor) { + subfilterNum -= interpolateFactor; + inputIndex++; + } + } + + state->inputIndex = inputIndex; + state->subfilterNum = subfilterNum; + return outSample; +} + +static int32_t PolyphaseResamplerMultichannel(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + uint32_t subfilterNum = state->subfilterNum; + const float* filterCoefficients = state->filterCoefficients; + const int32_t quoSamplerateRatio = state->quoSamplerateRatio; + const int32_t remSamplerateRatio = state->remSamplerateRatio; + const uint32_t decimateFactor = state->decimateFactor; + const uint32_t interpolateFactor = state->interpolateFactor; + const int32_t numChannels = state->numChannels; + int32_t i; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), ((interpolateFactor * + ((*inputLength) - inputIndex) - subfilterNum) - 1) / decimateFactor + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[subfilterNum * n]; + const float* inputs = &in[inputIndex * numChannels]; + MultiplyFilterMultichannel(state, coeffs, inputs, out, subfilterNum); + out += numChannels; + + inputIndex += quoSamplerateRatio; + subfilterNum += remSamplerateRatio; + if (subfilterNum >= interpolateFactor) { + subfilterNum -= interpolateFactor; + inputIndex++; + } + } + + state->inputIndex = inputIndex; + state->subfilterNum = subfilterNum; + return outSample; +} + +static int32_t PolyphaseDownsamplerHalfbandMono(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + float hCenter = filterCoefficients[indCenter]; + int32_t i; + int32_t j; + float sum; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / TWO_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float *coeffs = &filterCoefficients[0]; + + const float *inputs = &in[inputIndex]; + // center + sum = hCenter * inputs[indCenter]; + // symmetric indices + for (j = 0; j < indCenter; j += TWO_STEPS) { + sum += (*coeffs) * (inputs[j] + inputs[(n - j - TWO_STEPS)]); + coeffs += TWO_STEPS; + } + *out++ = sum; + inputIndex += TWO_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseDownsamplerHalfbandStereo(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + float hCenter = filterCoefficients[indCenter]; + int32_t i; + int32_t j; + float sumL; + float sumR; + float h; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / TWO_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[0]; + const float* inputs = &in[inputIndex * STEREO]; + + // center + sumL = hCenter * inputs[n - TWO_STEPS]; + sumR = hCenter * inputs[n - 1]; + // symmetric indices + for (j = 0; j < indCenter; j += TWO_STEPS) { + h = *coeffs; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - TWO_STEPS) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - TWO_STEPS) * STEREO + 1]); + coeffs += TWO_STEPS; + } + *out++ = sumL; + *out++ = sumR; + inputIndex += TWO_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseDownsamplerHalfbandMultichannel(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + float hCenter = filterCoefficients[indCenter]; + int32_t i; + int32_t j; + int32_t ch; + float h; + float sum[MAX_NUM_CHANNEL]; + const int32_t numChannels = state->numChannels; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / TWO_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[0]; + const float* inputs = &in[inputIndex * numChannels]; + + // center + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = hCenter * inputs[indCenter * numChannels + ch]; + } + // symmetric indices + for (j = 0; j < indCenter; j += TWO_STEPS) { + h = *coeffs; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h * (inputs[j * numChannels + ch] + inputs[(n - j - TWO_STEPS) * numChannels + ch]); + } + coeffs += TWO_STEPS; + } + for (ch = 0; ch < numChannels; ch++) { + *out++ = sum[ch]; + } + inputIndex += TWO_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseDownsamplerThirdbandMono(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + int32_t i; + int32_t j; + float sum; + int32_t rem = indCenter % THREE_STEPS; + float hCenter = filterCoefficients[indCenter]; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / THREE_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[0]; + const float* inputs = &in[inputIndex]; + + // center + sum = hCenter * inputs[indCenter]; + // symmetric indices + for (j = 0; j < rem; j++) { + sum += (*coeffs++) * (inputs[j] + inputs[(n - j - TWO_STEPS)]); + } + coeffs++; + for (j = rem + 1; j < indCenter; j += THREE_STEPS) { + sum += (*coeffs++) * (inputs[j] + inputs[(n - j - TWO_STEPS)]); + sum += (*coeffs++) * (inputs[(j + 1)] + inputs[(n - (j + 1) - TWO_STEPS)]); + coeffs++; + } + *out++ = sum; + inputIndex += THREE_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseDownsamplerThirdbandStereo(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + int32_t i; + int32_t j; + float sumL; + float sumR; + float h; + + int32_t rem = indCenter % THREE_STEPS; + float hCenter = filterCoefficients[indCenter]; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / THREE_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[0]; + const float* inputs = &in[inputIndex * STEREO]; + + // center + sumL = hCenter * inputs[n - TWO_STEPS]; + sumR = hCenter * inputs[n - 1]; + + // symmetric indices + for (j = 0; j < rem; j++) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - TWO_STEPS) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - TWO_STEPS) * STEREO + 1]); + } + coeffs++; + for (j = rem + 1; j < indCenter; j += THREE_STEPS) { + h = *coeffs++; + sumL += h * (inputs[j * STEREO] + inputs[(n - j - TWO_STEPS) * STEREO]); + sumR += h * (inputs[j * STEREO + 1] + inputs[(n - j - TWO_STEPS) * STEREO + 1]); + h = *coeffs++; + sumL += h * (inputs[(j + 1) * STEREO] + inputs[(n - (j + 1) - TWO_STEPS) * STEREO]); + sumR += h * (inputs[(j + 1) * STEREO + 1] + inputs[(n - (j + 1) - TWO_STEPS) * STEREO + 1]); + coeffs++; + } + *out++ = sumL; + *out++ = sumR; + inputIndex += THREE_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseDownsamplerThirdbandMultichannel(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + const int32_t indCenter = n / TWO_STEPS - 1; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + const float* filterCoefficients = state->filterCoefficients; + int32_t i; + int32_t j; + int32_t ch; + float h1; + float h2; + float sum[MAX_NUM_CHANNEL]; + const int32_t numChannels = state->numChannels; + int32_t rem = indCenter % THREE_STEPS; + float hCenter = filterCoefficients[indCenter]; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), (((*inputLength) - inputIndex) - 1) / THREE_STEPS + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[0]; + const float* inputs = &in[inputIndex * numChannels]; + + // center + for (ch = 0; ch < numChannels; ch++) { + sum[ch] = hCenter * (inputs[indCenter * numChannels + ch]); + } + + // symmetric indices + for (j = 0; j < rem; j++) { + h1 = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h1 * (inputs[j * numChannels + ch] + inputs[(n - j - TWO_STEPS) * numChannels + ch]); + } + } + coeffs++; + for (j = rem + 1; j < indCenter; j += THREE_STEPS) { + h1 = *coeffs++; + h2 = *coeffs++; + for (ch = 0; ch < numChannels; ch++) { + sum[ch] += h1 * (inputs[j * numChannels + ch] + inputs[(n - j - TWO_STEPS) * numChannels + ch]); + sum[ch] += h2 * (inputs[(j + 1) * numChannels + ch] + inputs[(n - (j + 1) - TWO_STEPS) * + numChannels + ch]); + } + coeffs++; + } + for (ch = 0; ch < numChannels; ch++) { + *out++ = sum[ch]; + } + inputIndex += THREE_STEPS; + } + + state->inputIndex = inputIndex; + return outSample; +} + +static int32_t PolyphaseResamplerCoarse(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + uint32_t subfilterNum = state->subfilterNum; + const float* filterCoefficients = state->filterCoefficients; + const int32_t quoSamplerateRatio = state->quoSamplerateRatio; + const int32_t remSamplerateRatio = state->remSamplerateRatio; + const uint32_t decimateFactor = state->decimateFactor; + const uint32_t interpolateFactor = state->interpolateFactor; + const int32_t numChannels = state->numChannels; + int32_t i; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), ((interpolateFactor * ((*inputLength) - inputIndex) - + subfilterNum) - 1) / decimateFactor + 1); + } + + for (i = 0; i < outSample; i++) { + const float* coeffs = &filterCoefficients[subfilterNum * n]; + const float* inputs = &in[inputIndex * numChannels]; + + state->multiplyFunSeq[subfilterNum](state, coeffs, inputs, out, subfilterNum); + out += numChannels; + + inputIndex += quoSamplerateRatio; + subfilterNum += remSamplerateRatio; + if (subfilterNum >= interpolateFactor) { + subfilterNum -= interpolateFactor; + inputIndex++; + } + } + + state->inputIndex = inputIndex; + state->subfilterNum = subfilterNum; + return outSample; +} + +/* + * This resampler is used to produce zero output in situations where memory for the filter could not be allocated. + */ +static int32_t PolyphaseResamplerZero(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + int32_t outSample = 0; + int32_t inputIndex = state->inputIndex; + uint32_t subfilterNum = state->subfilterNum; + const int32_t quoSamplerateRatio = state->quoSamplerateRatio; + const int32_t remSamplerateRatio = state->remSamplerateRatio; + const uint32_t decimateFactor = state->decimateFactor; + const uint32_t interpolateFactor = state->interpolateFactor; + const int32_t numChannels = state->numChannels; + int32_t i; + int32_t ch; + + (void)in; + + if (inputIndex < (int32_t)(*inputLength)) { + outSample = CompareMin((*outputLength), ((interpolateFactor * ((*inputLength) - inputIndex) - + subfilterNum) - 1) / decimateFactor + 1); + } + + for (i = 0; i < outSample; i++) { + for (ch = 0; ch < numChannels; ch++) { + *out++ = 0; + } + + inputIndex += quoSamplerateRatio; + subfilterNum += remSamplerateRatio; + if (subfilterNum >= interpolateFactor) { + subfilterNum -= interpolateFactor; + inputIndex++; + } + } + + state->inputIndex = inputIndex; + state->subfilterNum = subfilterNum; + return outSample; +} + +static MultiplyFilterFun GetMultiplyFilterFun(SingleStagePolyphaseResamplerState* state, int32_t i) +{ + int32_t channelMode = CompareMin(state->numChannels - 1, STEREO); + + if (state->interpolateFactor < state->decimateFactor) { // downsampling + if (i == 0) { + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_DOWN + channelMode]; + } + if (TWO_STEPS * i == state->interpolateFactor) { + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_DOWN + channelMode]; + } + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_DOWN + channelMode]; + } else { // upsampling + if (i == 0) { + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_ODD_UP + channelMode]; + } + if (TWO_STEPS * i == state->interpolateFactor) { + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_SYMMETRIC_EVEN_UP + channelMode]; + } + return multiplyFilterFunTable[THREE_STEPS * MULTIPLY_FILTER_FUN_UP + channelMode]; + } +} + +static ResamplerMethod SetResamplerFunctionCoarse(SingleStagePolyphaseResamplerState* state) +{ + if (TWO_STEPS * state->interpolateFactor == state->decimateFactor) { + // Specific function for downsample by 2 + switch (state->numChannels) { + case MONO: + return PolyphaseDownsamplerHalfbandMono; + case STEREO: + return PolyphaseDownsamplerHalfbandStereo; + default: + return PolyphaseDownsamplerHalfbandMultichannel; + } + } + if (THREE_STEPS * state->interpolateFactor == state->decimateFactor) { + // Specific function for downsample by 3 + switch (state->numChannels) { + case MONO: + return PolyphaseDownsamplerThirdbandMono; + case STEREO: + return PolyphaseDownsamplerThirdbandStereo; + default: + return PolyphaseDownsamplerThirdbandMultichannel; + } + } + + for (int32_t j = 0; j < state->interpolateFactor; j++) { + state->multiplyFunSeq[j] = GetMultiplyFilterFun(state, j); + } + return PolyphaseResamplerCoarse; +} + +static int32_t UpdateResamplerState(SingleStagePolyphaseResamplerState* state) +{ + uint32_t oldFilterLength = state->filterLength; + + state->quoSamplerateRatio = state->decimateFactor / state->interpolateFactor; + state->remSamplerateRatio = state->decimateFactor % state->interpolateFactor; + state->filterLength = QUALITY_TABLE[state->quality].filterLength; + state->coshParameter = QUALITY_TABLE[state->quality].coshParameter; + + if (state->interpolateFactor < state->decimateFactor) { // downsampling + state->cutoff = (float)state->interpolateFactor / state->decimateFactor; + state->filterLength = state->filterLength * state->decimateFactor / state->interpolateFactor; + + // Round up to make sure filterLength be multiple of 8 + state->filterLength = 8 * ((state->filterLength - 1) / 8) + 8; + } else { // upsampling + state->cutoff = 1; + } + + // modified for new requirements (extended i/o sample rate combination) 2025.2.28 + if ((CompareMax(state->decimateFactor, state->interpolateFactor) <= MAX_RATIO_INTEGRAL_METHOD) & + ((state->decimateFactor == 1 || state->interpolateFactor == 1) || + ((float)state->decimateFactor / (float)state->interpolateFactor < 2.0f))) { + state->resamplerFunction = SetResamplerFunctionCoarse(state); + } else { // fine (non-integral) sampling rate ratio + switch (state->numChannels) { + case MONO: + state->resamplerFunction = PolyphaseResamplerMono; + break; + case STEREO: + state->resamplerFunction = PolyphaseResamplerStereo; + break; + default: + state->resamplerFunction = PolyphaseResamplerMultichannel; + } + } + + CalculateFilter(state); + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + return UpdateFilterMemory(state, oldFilterLength); +} + +static int32_t UpdateFilterMemory(SingleStagePolyphaseResamplerState* state, uint32_t oldFilterLength) +{ + /* Adding bufferSize to filterLength won't overflow here because filterLength + could be multiplied by sizeof(float) above. */ + uint32_t requiredInputMemorySize = state->filterLength - 1 + state->bufferSize; + if (requiredInputMemorySize > state->inputMemorySize) { + if (state->inputMemory == NULL) { // first time initiaization + state->inputMemory = (float*)malloc(state->numChannels * requiredInputMemorySize * sizeof(float)); + if (state->inputMemory == NULL) { + return RESAMPLER_ERR_ALLOC_FAILED; + } + } else { + float* inputMemory = (float*)malloc(state->numChannels * requiredInputMemorySize * sizeof(float)); + int32_t ret = memcpy_s(inputMemory, requiredInputMemorySize * state->numChannels * sizeof(float), + state->inputMemory, state->inputMemorySize * state->numChannels * sizeof(float)); + if (INT_MAX / sizeof(float) / state->numChannels < requiredInputMemorySize || ret != 0) { + state->resamplerFunction = PolyphaseResamplerZero; + /* state->mem may still contain consumed input samples for the filter. + Restore filterLength so that filterLength - 1 still points to the position after + the last of these samples. */ + state->filterLength = oldFilterLength; + return RESAMPLER_ERR_ALLOC_FAILED; + } + free(state->inputMemory); + state->inputMemory = inputMemory; + } + state->inputMemorySize = requiredInputMemorySize; + } + /* codes for sudden sample rate change are deprecated and deleted so far */ + uint32_t i; + for (i = 0; i < state->numChannels * state->inputMemorySize; i++) { + state->inputMemory[i] = 0; + } + return RESAMPLER_ERR_SUCCESS; +} + +static int32_t SingleStagePolyphaseResamplerSetQuality(SingleStagePolyphaseResamplerState* state, int32_t quality) +{ + if (quality > QUALITY_LEVEL_TEN || quality < 0) { + return RESAMPLER_ERR_INVALID_ARG; + } + if (state->quality == quality) { + return RESAMPLER_ERR_SUCCESS; + } + state->quality = quality; + if (state->isInitialized) { + return UpdateResamplerState(state); + } + return RESAMPLER_ERR_SUCCESS; +} + +SingleStagePolyphaseResamplerState* SingleStagePolyphaseResamplerInit(uint32_t numChannels, + uint32_t decimateFactor, uint32_t interpolateFactor, int32_t quality, int32_t* err) +{ + SingleStagePolyphaseResamplerState* state; + int32_t filterErr; + + if (numChannels == 0 || decimateFactor == 0 || interpolateFactor == 0 || quality > QUALITY_LEVEL_TEN || + quality < 0) { + if (err) { + *err = RESAMPLER_ERR_INVALID_ARG; + } + return NULL; + } + state = (SingleStagePolyphaseResamplerState*)calloc(sizeof(SingleStagePolyphaseResamplerState), 1); + if (!state) { + if (err) { + *err = RESAMPLER_ERR_ALLOC_FAILED; + } + return NULL; + } + state->isInitialized = 0; + state->isStarted = 0; + state->decimateFactor = 0; + state->interpolateFactor = 0; + state->quality = -1; + state->filterCoefficientsSize = 0; + state->inputMemorySize = 0; + state->filterLength = 0; + state->filterCoefficients = NULL; + state->inputMemory = NULL; + state->resamplerFunction = 0; + + state->cutoff = 1.f; + state->numChannels = numChannels; + + state->bufferSize = BUFFER_SIZE; + + state->inputIndex = 0; + state->magicSamples = 0; + state->subfilterNum = 0; + + SingleStagePolyphaseResamplerSetQuality(state, quality); + filterErr = SingleStagePolyphaseResamplerSetRate(state, decimateFactor, interpolateFactor); + filterErr = UpdateResamplerState(state); + if (filterErr == RESAMPLER_ERR_SUCCESS) { + state->isInitialized = 1; + } else { + SingleStagePolyphaseResamplerFree(state); + state = NULL; + } + if (err) { + *err = filterErr; + } + return state; +} + +static void ApplyResampler(SingleStagePolyphaseResamplerState* state, uint32_t* inputLength, + float* out, uint32_t* outputLength) +{ + const int32_t n = state->filterLength; + int32_t outSample = 0; + float* inputMemory = state->inputMemory; + uint32_t inputSize; + const int32_t numChannels = state->numChannels; + int32_t j; + + state->isStarted = 1; + /* Call resampler function */ + outSample = state->resamplerFunction(state, inputMemory, inputLength, out, outputLength); + + if (state->inputIndex < (int32_t)*inputLength) { + *inputLength = state->inputIndex; + } + *outputLength = outSample; + state->inputIndex -= *inputLength; + + inputSize = (*inputLength) * numChannels; + for (j = 0; j < (n - 1) * numChannels; j++) { + inputMemory[j] = inputMemory[j + inputSize]; + } +} + + +static inline uint32_t ComputeGcd(uint32_t a, uint32_t b) +{ + while (b != 0) { + uint32_t temp = a; + + a = b; + b = temp % b; + } + return a; +} + + +int32_t SingleStagePolyphaseResamplerProcess(SingleStagePolyphaseResamplerState* state, const float* in, + uint32_t* inputLength, float* out, uint32_t* outputLength) +{ + int32_t j; + uint32_t remainingInputLength = *inputLength; + uint32_t remainingOutputLength = *outputLength; + const int32_t filtOffs = state->filterLength - 1; + const uint32_t bufferLen = state->inputMemorySize - filtOffs; + const int32_t numChannels = state->numChannels; + float* buf = state->inputMemory + filtOffs * numChannels; + + while (remainingInputLength && remainingOutputLength) { + uint32_t processInputLength = (remainingInputLength > bufferLen) ? bufferLen : remainingInputLength; + uint32_t processOutputLength = remainingOutputLength; + + if (in) { + for (j = 0; j < processInputLength * numChannels; j++) { + buf[j] = in[j]; + } + } else { + for (j = 0; j < processInputLength * numChannels; j++) { + buf[j] = 0; + } + } + ApplyResampler(state, &processInputLength, out, &processOutputLength); + remainingInputLength -= processInputLength; + remainingOutputLength -= processOutputLength; + out += processOutputLength * numChannels; + if (in) { + in += processInputLength * numChannels; + } + } + + *inputLength -= remainingInputLength; + *outputLength -= remainingOutputLength; + return state->resamplerFunction == PolyphaseResamplerZero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +int32_t SingleStagePolyphaseResamplerSetRate(SingleStagePolyphaseResamplerState* state, uint32_t decimateFactor, + uint32_t interpolateFactor) +{ + uint32_t fact; + uint32_t oldInterpolateFactor; + + if (decimateFactor == 0 || interpolateFactor == 0) { + return RESAMPLER_ERR_INVALID_ARG; + } + + if (state->decimateFactor == decimateFactor && state->interpolateFactor == interpolateFactor) { + return RESAMPLER_ERR_SUCCESS; + } + + oldInterpolateFactor = state->interpolateFactor; + state->decimateFactor = decimateFactor; + state->interpolateFactor = interpolateFactor; + + fact = ComputeGcd(state->decimateFactor, state->interpolateFactor); + CHECK_AND_RETURN_RET_LOG(fact != 0, RESAMPLER_ERR_OVERFLOW, "fact is zero, invalid"); + state->decimateFactor /= fact; + state->interpolateFactor /= fact; + + if (oldInterpolateFactor > 0) { + state->subfilterNum = state->subfilterNum * state->interpolateFactor / oldInterpolateFactor; + + /* Safety net */ + if (state->subfilterNum >= state->interpolateFactor) { + state->subfilterNum = state->interpolateFactor - 1; + } + } + + if (state->isInitialized) { + return UpdateResamplerState(state); + } + return RESAMPLER_ERR_SUCCESS; +} + +int32_t SingleStagePolyphaseResamplerSkipHalfTaps(SingleStagePolyphaseResamplerState* state) +{ + state->inputIndex = state->filterLength / TWO_STEPS; + return RESAMPLER_ERR_SUCCESS; +} + + +void SingleStagePolyphaseResamplerFree(SingleStagePolyphaseResamplerState* state) +{ + free(state->inputMemory); + state->inputMemory = NULL; + free(state->filterCoefficients); + state->filterCoefficients = NULL; + free(state); + state = NULL; +} + + +int32_t SingleStagePolyphaseResamplerResetMem(SingleStagePolyphaseResamplerState* state) +{ + uint32_t i; + state->inputIndex = 0; + state->magicSamples = 0; + state->subfilterNum = 0; + + for (i = 0; i < state->numChannels * state->inputMemorySize; i++) { + state->inputMemory[i] = 0; + } + return RESAMPLER_ERR_SUCCESS; +} \ No newline at end of file diff --git a/services/audio_engine/simd/simd_utils.cpp b/services/audio_engine/simd/simd_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8733b82a72499cea8e5df798e37f24ecc48a2069 --- /dev/null +++ b/services/audio_engine/simd/simd_utils.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "simd_utils.h" +#include +#include + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +#if USE_ARM_NEON == 1 +constexpr int ALIGIN_FLOAT_SIZE = 4; +#endif +void SimdPointByPointAdd(size_t length, const float* inputLeft, const float* inputRight, float* output) +{ +#if USE_ARM_NEON == 1 + if (length < ALIGIN_FLOAT_SIZE) { + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] + inputRight[i]; + } + } else { + size_t procLen = length >> 2; + float32x4_t left32x4; + float32x4_t right32x4; + float32x4_t out32x4; + for (size_t i = 0; i < procLen; i++) { + left32x4 = vld1q_f32(inputLeft + i * ALIGIN_FLOAT_SIZE); + right32x4 = vld1q_f32(inputRight + i * ALIGIN_FLOAT_SIZE); + out32x4 = vaddq_f32(left32x4, right32x4); + vst1q_f32(output + i * ALIGIN_FLOAT_SIZE, out32x4); + } + size_t odd = length - procLen * ALIGIN_FLOAT_SIZE; + if (odd) { + for (size_t j = length - odd; j < length; j++) { + output[j] = inputLeft[j] + inputRight[j]; + } + } + } +#else + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] + inputRight[i]; + } +#endif +} +void SimdPointByPointSub(size_t length, const float* inputLeft, const float* inputRight, float* output) +{ +#if USE_ARM_NEON == 1 + if (length < ALIGIN_FLOAT_SIZE) { + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] - inputRight[i]; + } + } else { + size_t procLen = length >> 2; + float32x4_t left32x4; + float32x4_t right32x4; + float32x4_t out32x4; + for (size_t i = 0; i < procLen; i++) { + left32x4 = vld1q_f32(inputLeft + i * ALIGIN_FLOAT_SIZE); + right32x4 = vld1q_f32(inputRight + i * ALIGIN_FLOAT_SIZE); + out32x4 = vsubq_f32(left32x4, right32x4); + vst1q_f32(output + i * ALIGIN_FLOAT_SIZE, out32x4); + } + size_t odd = length - procLen * ALIGIN_FLOAT_SIZE; + if (odd) { + for (size_t j = length - odd; j < length; j++) { + output[j] = inputLeft[j] - inputRight[j]; + } + } + } +#else + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] - inputRight[i]; + } +#endif +} + +void SimdPointByPointMul(size_t length, const float* inputLeft, const float* inputRight, float* output) +{ +#if USE_ARM_NEON == 1 + if (length < ALIGIN_FLOAT_SIZE) { + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] * inputRight[i]; + } + } else { + size_t procLen = length >> 2; + float32x4_t left32x4; + float32x4_t right32x4; + float32x4_t out32x4; + for (size_t i = 0; i < procLen; i++) { + left32x4 = vld1q_f32(inputLeft + i * ALIGIN_FLOAT_SIZE); + right32x4 = vld1q_f32(inputRight + i * ALIGIN_FLOAT_SIZE); + out32x4 = vmulq_f32(left32x4, right32x4); + vst1q_f32(output + i * ALIGIN_FLOAT_SIZE, out32x4); + } + size_t odd = length - procLen * ALIGIN_FLOAT_SIZE; + if (odd) { + for (size_t j = length - odd; j < length; j++) { + output[j] = inputLeft[j] * inputRight[j]; + } + } + } +#else + for (size_t i = 0; i < length; i++) { + output[i] = inputLeft[i] * inputRight[i]; + } +#endif +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/simd/simd_utils.h b/services/audio_engine/simd/simd_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..beb7e889f17acfd3a48a207c719e1016dcb1d89a --- /dev/null +++ b/services/audio_engine/simd/simd_utils.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIMD_UTILS_H +#define SIMD_UTILS_H + +#include +#include + +#if !defined(DISABLE_SIMD) && \ + (defined(__aarch64__) || (defined(__arm__) && defined(__ARM_NEON__))) +// enable arm Simd +#include +#define USE_ARM_NEON 1 +#else +// disable SIMD. +#define USE_ARM_NEON 0 +#endif +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +void SimdPointByPointAdd(size_t length, const float* inputLeft, const float* inputRight, float* output); +void SimdPointByPointSub(size_t length, const float* inputLeft, const float* inputRight, float* output); +void SimdPointByPointMul(size_t length, const float* inputLeft, const float* inputRight, float* output); +}}} + +#endif \ No newline at end of file diff --git a/services/audio_engine/test/unittest/BUILD.gn b/services/audio_engine/test/unittest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bd9da17709c2d981768b4953b3e93f519793aed8 --- /dev/null +++ b/services/audio_engine/test/unittest/BUILD.gn @@ -0,0 +1,88 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("//build/test.gni") +import("../../../../config.gni") + +module_output_path = "multimedia_audio_framework/audio_engine" + +config("audio_engine_private_config") { + visibility = [ ":*" ] + + include_dirs = [ + "./common", + "./unittest/include", + "../../simd", + "../../dfx", + "../../buffer", + "../../node/include", + "../../common", + "../../utils", + "../../plugin/resample/include", + "../../plugin/channel_converter/include", + "../../plugin/bitdepth_converter", + "../../manager/include", + "../../../../interfaces/inner_api/native/audiocommon/include", + "../../../audio_service/server/include", + "../../../audio_service/common/include", + "../../../audio_policy/server/include/service/common", + "../../../../frameworks/native/audioeffect/include", + "../../../../frameworks/native/hdiadapter_new/include", + ] +} + +ohos_unittest("audio_engine_unit_test") { + module_out_path = module_output_path + sources = [ + "common/test_case_common.cpp", + "dfx/hpae_dfx_tree_test.cpp", + "manager/hpae_audio_service_callback_unit_test.cpp", + "manager/hpae_capturer_manager_test.cpp", + "manager/hpae_inner_capturer_unit_test.cpp", + "manager/hpae_manager_test.cpp", + "manager/hpae_render_manager_test.cpp", + "node/hpae_gain_node_test.cpp", + "node/hpae_mixer_node_test.cpp", + "node/hpae_output_cluster_test.cpp", + "node/hpae_pcm_buffer_test.cpp", + "node/hpae_pcm_process_test.cpp", + "node/hpae_process_cluster_test.cpp", + "node/hpae_resample_node_test.cpp", + "node/hpae_sink_input_node_test.cpp", + "node/hpae_sink_output_node_test.cpp", + "node/hpae_source_input_cluster_test.cpp", + "node/hpae_source_input_node_test.cpp", + "node/hpae_source_output_node_test.cpp", + ] + + configs = [ ":audio_engine_private_config" ] + + deps = [ + "../../:audio_engine_manager", + "../../:audio_engine_node", + "../../:audio_engine_utils", + ] + + external_deps = [ + "c_utils:utils", + "googletest:gtest", + "hilog:libhilog", + "hisysevent:libhisysevent", + "ipc:ipc_single", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] + + resource_config_file = "./resource/ohos_test.xml" +} diff --git a/services/audio_engine/test/unittest/common/hpae_audio_service_callback_unit_test.h b/services/audio_engine/test/unittest/common/hpae_audio_service_callback_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..4935a1e879d46019926eb068fb9b09accc0fb8fd --- /dev/null +++ b/services/audio_engine/test/unittest/common/hpae_audio_service_callback_unit_test.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_AUDIO_AUDIO_SERVICE_CALLBACK_UNIT_TEST_H +#define HPAE_AUDIO_AUDIO_SERVICE_CALLBACK_UNIT_TEST_H +#include "audio_service_hpae_callback.h" + +namespace OHOS { +namespace AudioStandard { +class HpaeAudioServiceCallbackUnitTest : public AudioServiceHpaeCallback { +public: + ~HpaeAudioServiceCallbackUnitTest() override; + + void OnOpenAudioPortCb(int32_t portId) override; + + void OnCloseAudioPortCb(int32_t result) override; + + void OnSetSinkMuteCb(int32_t result) override; + + void OnGetAllSinkInputsCb(int32_t result, std::vector &sinkInputs) override; + + void OnGetAllSourceOutputsCb(int32_t result, std::vector &sourceOutputs) override; + + void OnGetAllSinksCb(int32_t result, std::vector &sinks) override; + + void OnMoveSinkInputByIndexOrNameCb(int32_t result) override; + + void OnMoveSourceOutputByIndexOrNameCb(int32_t result) override; + + void OnSetSourceOutputMuteCb(int32_t result) override; + + void OnGetAudioEffectPropertyCbV3(int32_t result) override; + + void OnGetAudioEffectPropertyCb(int32_t result) override; + + void OnGetAudioEnhancePropertyCbV3(int32_t result) override; + + void OnGetAudioEnhancePropertyCb(int32_t result) override; + + void HandleSourceAudioStreamRemoved(uint32_t sessionId) override; + + int32_t GetPortId() const noexcept; + + int32_t GetCloseAudioPortResult() const noexcept; + + int32_t GetSetSinkMuteResult() const noexcept; + + int32_t GetGetAllSinkInputsResult() const noexcept; + + int32_t GetGetAllSourceOutputsResult() const noexcept; + + int32_t GetGetAllSinksResult() const noexcept; + + int32_t GetMoveSinkInputByIndexOrNameResult() const noexcept; + + int32_t GetMoveSourceOutputByIndexOrNameResult() const noexcept; + + int32_t GetSetSourceOutputMuteResult() const noexcept; + + int32_t GetGetAudioEffectPropertyResult() const noexcept; + + int32_t GetGetAudioEnhancePropertyResult() const noexcept; + + std::vector GetSinkInputs() const noexcept; + + std::vector GetSourceOutputs() const noexcept; + + std::vector GetSinks() const noexcept; + +private: + int32_t portId_ = -1; + int32_t closeAudioPortResult_ = -1; + int32_t setSinkMuteResult_ = -1; + int32_t getAllSinkInputsResult_ = -1; + int32_t getAllSourceOutputsResult_ = -1; + int32_t getAllSinksResult_ = -1; + int32_t moveSinkInputByIndexOrNameResult_ = -1; + int32_t moveSourceOutputByIndexOrNameResult_ = -1; + int32_t setSourceOutputMuteResult_ = -1; + int32_t getAudioEffectPropertyResult_ = -1; + int32_t getAudioEnhancePropertyResult_ = -1; + std::vector sinkInputs_; + std::vector sourceOutputs_; + std::vector sinks_; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/test/unittest/common/hpae_manager_unit_test.h b/services/audio_engine/test/unittest/common/hpae_manager_unit_test.h new file mode 100644 index 0000000000000000000000000000000000000000..d4d23c4feee8f6d73842c541bd5bd1f946847802 --- /dev/null +++ b/services/audio_engine/test/unittest/common/hpae_manager_unit_test.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_MANAGER_UNIT_TEST_H +#define HPAE_MANAGER_UNIT_TEST_H +#include "gtest/gtest.h" +#include "hpae_manager.h" +#include "hpae_info.h" +#include "hpae_audio_service_callback_unit_test.h" + +namespace OHOS { +namespace AudioStandard { +class HpaeManagerUnitTest : public testing::Test { +public: + void SetUp(); + void TearDown(); + +protected: + std::shared_ptr hpaeManager_; + +protected: + void WaitForMsgProcessing(); + AudioModuleInfo GetSinkAudioModeInfo(); + AudioModuleInfo GetSourceAudioModeInfo(); + HPAE::HpaeStreamInfo GetRenderStreamInfo(); + HPAE::HpaeStreamInfo GetCaptureStreamInfo(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/test/unittest/common/test_case_common.cpp b/services/audio_engine/test/unittest/common/test_case_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b88ed842daaff96c33b11c0b62b83204332951d2 --- /dev/null +++ b/services/audio_engine/test/unittest/common/test_case_common.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_TAG +#define LOG_TAG "TestCaseCommon" +#endif + +#include "test_case_common.h" +#include "hpae_info.h" +#include "audio_engine_log.h" + +namespace OHOS { +namespace AudioStandard { + +int32_t WriteFixedDataCb::OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) +{ + size_t sampleSize = HPAE::GetSizeFromFormat(format_); + CHECK_AND_RETURN_RET_LOG(sampleSize != 0, SUCCESS, "sampleSize is zero, invalid format"); + for (size_t i = 0; i < callBackStremInfo.requestDataLen / sampleSize; i++) { + switch (format_) { + case AudioSampleFormat::SAMPLE_U8: { + *(callBackStremInfo.inputData + i) = writeNum_; + break; + } + case SAMPLE_S16LE: { + *((int16_t*)callBackStremInfo.inputData + i) = writeNum_; + break; + } + case SAMPLE_S24LE: { + uint8_t *p = (uint8_t *)(callBackStremInfo.inputData + OFFSET_BIT_24 * i); + p[BIT_DEPTH_TWO] = (uint8_t) (writeNum_ >> BIT_16); + p[1] = (uint8_t) (writeNum_ >> BIT_8); + p[0] = (uint8_t) writeNum_; + break; + } + case SAMPLE_S32LE: { + *((int32_t*)callBackStremInfo.inputData + i) = writeNum_; + break; + } + case SAMPLE_F32LE: { + *((float*)callBackStremInfo.inputData + i) = writeNum_; + break; + } + default: + break; + } + } + writeNum_++; + return 0; +} + +int32_t WriteFixedValueCb::OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) +{ + size_t sampleSize = HPAE::GetSizeFromFormat(format_); + CHECK_AND_RETURN_RET_LOG(sampleSize != 0, SUCCESS, "sampleSize is zero, invalid format"); + for (size_t i = 0; i < callBackStremInfo.requestDataLen / sampleSize; i++) { + switch (format_) { + case AudioSampleFormat::SAMPLE_U8: { + *(callBackStremInfo.inputData + i) = fixValue_; + break; + } + case SAMPLE_S16LE: { + *((int16_t*)callBackStremInfo.inputData + i) = fixValue_; + break; + } + case SAMPLE_S24LE: { + uint8_t *p = (uint8_t *)(callBackStremInfo.inputData + OFFSET_BIT_24 * i); + p[BIT_DEPTH_TWO] = (uint8_t) (fixValue_ >> BIT_16); + p[1] = (uint8_t) (fixValue_ >> BIT_8); + p[0] = (uint8_t) fixValue_; + break; + } + case SAMPLE_S32LE: { + *((int32_t*)callBackStremInfo.inputData + i) = fixValue_; + break; + } + case SAMPLE_F32LE: { + *((float*)callBackStremInfo.inputData + i) = fixValue_; + break; + } + default: + break; + } + } + return 0; +} + +int32_t WriteIncDataCb::OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) +{ + for (size_t i = 0; i < callBackStremInfo.requestDataLen / + HPAE::GetSizeFromFormat(format_); i++) { + switch (format_) { + case AudioSampleFormat::SAMPLE_U8: { + *(callBackStremInfo.inputData + i) = i; + break; + } + case SAMPLE_S16LE: { + *((int16_t*)callBackStremInfo.inputData + i) = i; + break; + } + case SAMPLE_S24LE: { + uint8_t *p = (uint8_t *)(callBackStremInfo.inputData + OFFSET_BIT_24 * i); + p[BIT_DEPTH_TWO] = (uint8_t) (i >> BIT_16); + p[1] = (uint8_t) (i >> BIT_8); + p[0] = (uint8_t) i; + break; + } + case SAMPLE_S32LE: { + *((int32_t*)callBackStremInfo.inputData + i) = i; + break; + } + case SAMPLE_F32LE: { + *((float*)callBackStremInfo.inputData + i) = i; + break; + } + default: + break; + } + } + writeNum_++; + return 0; +} + +void StatusChangeCb::OnStatusUpdate(IOperation operation) +{ + switch (operation) { + case OPERATION_STARTED: + status_ = I_STATUS_STARTED; + break; + case OPERATION_PAUSED: + status_ = I_STATUS_PAUSED; + break; + case OPERATION_STOPPED: + status_ = I_STATUS_STOPPED; + break; + default: + status_ = I_STATUS_INVALID; + } +} + +IStatus StatusChangeCb::GetStatus() +{ + return status_; +} + +ReadDataCb::ReadDataCb(const std::string &fileName) +{ + testFile_ = fopen(fileName.c_str(), "ab"); + if (testFile_ == nullptr) { + AUDIO_ERR_LOG("Open file failed"); + } +} + +ReadDataCb::~ReadDataCb() +{ + if (testFile_) { + fclose(testFile_); + testFile_ = nullptr; + } +} + +int32_t ReadDataCb::OnReadData(size_t length) +{ + AUDIO_WARNING_LOG("ProAudio do not support!"); + return SUCCESS; +} + +int32_t ReadDataCb::OnReadData(std::vector& outputData, size_t requestDataLen) +{ + CHECK_AND_RETURN_RET_LOG(testFile_ != nullptr, ERROR, "testFile_ is nullptr"); + int32_t ret = fwrite(outputData.data(), 1, requestDataLen, testFile_); + if (ret != 0) { + AUDIO_ERR_LOG(" something wrong when writing pcm! "); + } + return SUCCESS; +} +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/common/test_case_common.h b/services/audio_engine/test/unittest/common/test_case_common.h new file mode 100644 index 0000000000000000000000000000000000000000..237846f9ec743f004efa2ca138ebb948765c92be --- /dev/null +++ b/services/audio_engine/test/unittest/common/test_case_common.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_CASE_COMMON_H +#define TEST_CASE_COMMON_H +#include +#include "i_renderer_stream.h" +#include "i_capturer_stream.h" +#include "audio_errors.h" +#include "hpae_info.h" + +namespace OHOS { +namespace AudioStandard { +constexpr int OFFSET_BIT_24 = 3; +constexpr int BIT_DEPTH_TWO = 2; +constexpr int BIT_16 = 16; +constexpr int BIT_8 = 8; +constexpr float TEST_VALUE_PRESION = 0.001; +constexpr int TEST_FREAME_LEN = 125; +constexpr int TEST_SUB_FREAME_LEN = 50; + +#define DEFAULT_TEST_SINK_NAME "hdi_output" +#define DEFAULT_TEST_AUDIO_DEVICE_NAME "Speaker" +#define DEFAULT_TEST_DEVICE_CLASS "file_io" +#define DEFAULT_TEST_DEVICE_NETWORKID "LocalDevice" + +class WriteFixedDataCb : public IStreamCallback, public std::enable_shared_from_this { +public: + int32_t OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) override; + + explicit WriteFixedDataCb(AudioSampleFormat format) : format_(format) + {} + virtual ~WriteFixedDataCb() + {} + +private: + int32_t writeNum_ = 0; + AudioSampleFormat format_ = SAMPLE_F32LE; +}; + +class WriteFixedValueCb : public IStreamCallback, public std::enable_shared_from_this { +public: + int32_t OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) override; + WriteFixedValueCb(AudioSampleFormat format, int32_t fixedValue) : format_(format), fixValue_(fixedValue) + {} + virtual ~WriteFixedValueCb() + {} + +private: + AudioSampleFormat format_ = SAMPLE_F32LE; + int32_t fixValue_ = 0; +}; + +class WriteIncDataCb : public IStreamCallback, public std::enable_shared_from_this { +public: + int32_t OnStreamData(AudioCallBackStreamInfo& callBackStremInfo) override; + explicit WriteIncDataCb(AudioSampleFormat format) : format_(format) + {} + virtual ~WriteIncDataCb() + {} + +private: + int32_t writeNum_ = 0; + AudioSampleFormat format_ = SAMPLE_F32LE; +}; + +class StatusChangeCb : public IStatusCallback, public std::enable_shared_from_this { +public: + void OnStatusUpdate(IOperation operation) override; + IStatus GetStatus(); + virtual ~StatusChangeCb() = default; +private: + IStatus status_; +}; + +class ReadDataCb : public IReadCallback, public std::enable_shared_from_this { +public: + explicit ReadDataCb(const std::string &fileName); + virtual ~ReadDataCb(); + int32_t OnReadData(size_t length) override; + int32_t OnReadData(std::vector& outputData, size_t requestDataLen) override; +private: + FILE *testFile_ = nullptr; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/test/unittest/dfx/hpae_dfx_tree_test.cpp b/services/audio_engine/test/unittest/dfx/hpae_dfx_tree_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9d4479fa2382c9d35ef95311a00f292476dc83a --- /dev/null +++ b/services/audio_engine/test/unittest/dfx/hpae_dfx_tree_test.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "hpae_dfx_tree.h" +#include + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +class HpaeDfxTreeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeDfxTreeTest::SetUp() +{ + std::locale::global(std::locale("")); + std::wcout.imbue(std::locale()); +} + +void HpaeDfxTreeTest::TearDown() +{} + +TEST_F(HpaeDfxTreeTest, constructHpaeDfxTreeTest) +{ + HpaeDfxTree hpaeDfxTree; + HpaeNodeInfo info; + uint32_t nodeId = 123; + uint32_t sessionId = 12345; + size_t frameLen = 960; + uint32_t preNodeId = 0; + int32_t testNum = 10; + for (int32_t i = 0; i < testNum; i++) { + preNodeId = info.nodeId; + info.nodeId = nodeId + i; + info.sessionId = sessionId + i; + info.nodeName = "testNode1"; + info.frameLen = frameLen; + info.channels = STEREO; + info.samplingRate = SAMPLE_RATE_48000; + info.format = SAMPLE_F32LE; + info.sceneType = HPAE_SCENE_DEFAULT; + EXPECT_EQ(hpaeDfxTree.Insert(preNodeId, info), true); + } + std::vector> result = hpaeDfxTree.LevelOrderTraversal(); + std::string outStr; + hpaeDfxTree.PrintTree(outStr); + std::cout << outStr.c_str() << std::endl; + int32_t index = 0; + for (int32_t i = 0; i < result.size(); i++) { + for (int32_t j = 0; j < result[i].size(); j++) { + EXPECT_EQ(result[i][j].nodeId, index + nodeId); + EXPECT_EQ(result[i][j].sessionId, index + sessionId); + EXPECT_EQ(result[i][j].frameLen, frameLen); + EXPECT_EQ(result[i][j].samplingRate, SAMPLE_RATE_48000); + EXPECT_EQ(result[i][j].channels, STEREO); + EXPECT_EQ(result[i][j].format, SAMPLE_F32LE); + index++; + } + } +} + +TEST_F(HpaeDfxTreeTest, RemoveDfxTreeTest) +{ + HpaeDfxTree hpaeDfxTree; + HpaeNodeInfo info; + uint32_t nodeId = 123; + uint32_t sessionId = 12345; + size_t frameLen = 960; + uint32_t preNodeId = 0; + int32_t testNum = 10; + for (int32_t i = 0; i < testNum; i++) { + preNodeId = info.nodeId; + info.nodeId = nodeId + i; + info.sessionId = sessionId + i; + info.nodeName = "testNode2"; + info.frameLen = frameLen; + info.channels = MONO; + info.samplingRate = SAMPLE_RATE_16000; + info.format = SAMPLE_F32LE; + info.sceneType = HPAE_SCENE_MUSIC; + EXPECT_EQ(hpaeDfxTree.Insert(preNodeId, info), true); + } + std::vector> result = hpaeDfxTree.LevelOrderTraversal(); + EXPECT_EQ(result.size(), testNum); + uint32_t removeNodeIndex = 3; + EXPECT_EQ(hpaeDfxTree.Remove(nodeId + removeNodeIndex), true); + std::string outStr; + hpaeDfxTree.PrintTree(outStr); + std::cout << outStr.c_str() << std::endl; + result = hpaeDfxTree.LevelOrderTraversal(); + EXPECT_EQ(result.size(), removeNodeIndex); +} diff --git a/services/audio_engine/test/unittest/manager/hpae_audio_service_callback_unit_test.cpp b/services/audio_engine/test/unittest/manager/hpae_audio_service_callback_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..545fa94f7f5e69b51232973611757363e59ca9e6 --- /dev/null +++ b/services/audio_engine/test/unittest/manager/hpae_audio_service_callback_unit_test.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hpae_audio_service_callback_unit_test.h" +namespace OHOS { +namespace AudioStandard { +HpaeAudioServiceCallbackUnitTest::~HpaeAudioServiceCallbackUnitTest() +{} + +void HpaeAudioServiceCallbackUnitTest::OnOpenAudioPortCb(int32_t portId) +{ + portId_ = portId; +} + +void HpaeAudioServiceCallbackUnitTest::OnCloseAudioPortCb(int32_t result) +{ + closeAudioPortResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnSetSinkMuteCb(int32_t result) +{ + setSinkMuteResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAllSinkInputsCb(int32_t result, std::vector &sinkInputs) +{ + getAllSinkInputsResult_ = result; + sinkInputs_ = sinkInputs; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAllSourceOutputsCb(int32_t result, std::vector &sourceOutputs) +{ + getAllSourceOutputsResult_ = result; + sourceOutputs_ = sourceOutputs; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAllSinksCb(int32_t result, std::vector &sinks) +{ + getAllSinksResult_ = result; + sinks_ = sinks; +} + +void HpaeAudioServiceCallbackUnitTest::OnMoveSinkInputByIndexOrNameCb(int32_t result) +{ + moveSinkInputByIndexOrNameResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnMoveSourceOutputByIndexOrNameCb(int32_t result) +{ + moveSourceOutputByIndexOrNameResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnSetSourceOutputMuteCb(int32_t result) +{ + setSourceOutputMuteResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAudioEffectPropertyCbV3(int32_t result) +{ + getAudioEffectPropertyResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAudioEffectPropertyCb(int32_t result) +{ + getAudioEffectPropertyResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAudioEnhancePropertyCbV3(int32_t result) +{ + getAudioEnhancePropertyResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::OnGetAudioEnhancePropertyCb(int32_t result) +{ + getAudioEnhancePropertyResult_ = result; +} + +void HpaeAudioServiceCallbackUnitTest::HandleSourceAudioStreamRemoved(uint32_t sessionId) +{} + +int32_t HpaeAudioServiceCallbackUnitTest::GetPortId() const noexcept +{ + return portId_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetCloseAudioPortResult() const noexcept +{ + return closeAudioPortResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetSetSinkMuteResult() const noexcept +{ + return setSinkMuteResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetGetAllSinkInputsResult() const noexcept +{ + return getAllSinkInputsResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetGetAllSourceOutputsResult() const noexcept +{ + return getAllSourceOutputsResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetGetAllSinksResult() const noexcept +{ + return getAllSinksResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetMoveSinkInputByIndexOrNameResult() const noexcept +{ + return moveSinkInputByIndexOrNameResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetMoveSourceOutputByIndexOrNameResult() const noexcept +{ + return moveSourceOutputByIndexOrNameResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetSetSourceOutputMuteResult() const noexcept +{ + return setSourceOutputMuteResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetGetAudioEffectPropertyResult() const noexcept +{ + return getAudioEffectPropertyResult_; +} + +int32_t HpaeAudioServiceCallbackUnitTest::GetGetAudioEnhancePropertyResult() const noexcept +{ + return getAudioEnhancePropertyResult_; +} + +std::vector HpaeAudioServiceCallbackUnitTest::GetSinkInputs() const noexcept +{ + return sinkInputs_; +} + +std::vector HpaeAudioServiceCallbackUnitTest::GetSourceOutputs() const noexcept +{ + return sourceOutputs_; +} + +std::vector HpaeAudioServiceCallbackUnitTest::GetSinks() const noexcept +{ + return sinks_; +} + +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/manager/hpae_capturer_manager_test.cpp b/services/audio_engine/test/unittest/manager/hpae_capturer_manager_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b3713cfc2498763c8128c023bc4d135df42ca64 --- /dev/null +++ b/services/audio_engine/test/unittest/manager/hpae_capturer_manager_test.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_capturer_manager.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +std::string g_rootCapturerPath = "/data/data/.pulse_dir/"; +const uint32_t DEFAULT_FRAME_LENGTH = 960; +const uint32_t DEFAULT_SESSION_ID = 123456; + +class HpaeCapturerManagerTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeCapturerManagerTest::SetUp() +{} + +void HpaeCapturerManagerTest::TearDown() +{} + +static void TestCheckSourceOutputInfo(HpaeSourceOutputInfo& sourceOutputInfo, const HpaeStreamInfo& streamInfo) +{ + EXPECT_EQ(sourceOutputInfo.nodeInfo.channels == streamInfo.channels, true); + EXPECT_EQ(sourceOutputInfo.nodeInfo.format == streamInfo.format, true); + EXPECT_EQ(sourceOutputInfo.nodeInfo.frameLen == streamInfo.frameLen, true); + EXPECT_EQ(sourceOutputInfo.nodeInfo.sessionId == streamInfo.sessionId, true); + EXPECT_EQ(sourceOutputInfo.nodeInfo.samplingRate == streamInfo.samplingRate, true); + EXPECT_EQ(sourceOutputInfo.nodeInfo.streamType == streamInfo.streamType, true); +} + +static void WaitForMsgProcessing(std::shared_ptr &capturerManager) +{ + int waitCount = 0; + const int waitCountThd = 5; + while (capturerManager->IsMsgProcessing()) { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 20 for sleep + waitCount++; + if (waitCount >= waitCountThd) { + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(40)); // 40 for sleep + EXPECT_EQ(capturerManager->IsMsgProcessing(), false); + EXPECT_EQ(waitCount < waitCountThd, true); +} + +TEST_F(HpaeCapturerManagerTest, HpaeCapturerManagerConstructTest) +{ + HpaeSourceInfo sourceInfo; + sourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sourceInfo.sourceType = SOURCE_TYPE_MIC; + sourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + sourceInfo.samplingRate = SAMPLE_RATE_48000; + sourceInfo.channels = STEREO; + sourceInfo.format = SAMPLE_S16LE; + sourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + sourceInfo.ecType = HPAE_EC_TYPE_NONE; + sourceInfo.micRef = HPAE_REF_OFF; + + std::shared_ptr capturerManager = std::make_shared(sourceInfo); + HpaeSourceInfo dstSourceInfo = capturerManager->GetSourceInfo(); + EXPECT_EQ(dstSourceInfo.deviceNetId == sourceInfo.deviceNetId, true); + EXPECT_EQ(dstSourceInfo.deviceClass == sourceInfo.deviceClass, true); + EXPECT_EQ(dstSourceInfo.frameLen == sourceInfo.frameLen, true); + EXPECT_EQ(dstSourceInfo.samplingRate == sourceInfo.samplingRate, true); + EXPECT_EQ(dstSourceInfo.format == sourceInfo.format, true); + EXPECT_EQ(dstSourceInfo.channels == sourceInfo.channels, true); + EXPECT_EQ(dstSourceInfo.ecType == sourceInfo.ecType, true); + EXPECT_EQ(dstSourceInfo.micRef == sourceInfo.micRef, true); +} + +TEST_F(HpaeCapturerManagerTest, HpaeCapturerManagerInitTest) +{ + HpaeSourceInfo sourceInfo; + sourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sourceInfo.sourceType = SOURCE_TYPE_MIC; + sourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + sourceInfo.samplingRate = SAMPLE_RATE_48000; + sourceInfo.channels = STEREO; + sourceInfo.format = SAMPLE_S16LE; + sourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + sourceInfo.ecType = HPAE_EC_TYPE_NONE; + sourceInfo.micRef = HPAE_REF_OFF; + + std::shared_ptr capturerManager = std::make_shared(sourceInfo); + EXPECT_EQ(capturerManager->Init() == SUCCESS, true); +} + +TEST_F(HpaeCapturerManagerTest, HpaeCapturerManagerCreateDestoryStreamTest) +{ + HpaeSourceInfo sourceInfo; + sourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sourceInfo.sourceType = SOURCE_TYPE_MIC; + sourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + sourceInfo.samplingRate = SAMPLE_RATE_48000; + sourceInfo.channels = STEREO; + sourceInfo.format = SAMPLE_S16LE; + sourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + sourceInfo.ecType = HPAE_EC_TYPE_NONE; + sourceInfo.micRef = HPAE_REF_OFF; + + std::shared_ptr capturerManager = std::make_shared(sourceInfo); + EXPECT_EQ(capturerManager->Init() == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->IsInit(), true); + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_48000; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = DEFAULT_FRAME_LENGTH; + streamInfo.sessionId = DEFAULT_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + streamInfo.deviceName = "Built_in_mic"; + EXPECT_EQ(capturerManager->CreateStream(streamInfo) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager.use_count() == 1, true); + HpaeSourceOutputInfo sourceOutputInfo; + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + TestCheckSourceOutputInfo(sourceOutputInfo, streamInfo); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_NEW); + EXPECT_EQ(capturerManager->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ( + capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == ERR_INVALID_OPERATION, true); +} + +static void StateControlTest(std::shared_ptr &capturerManager, HpaeStreamInfo &streamInfo, + HpaeSourceOutputInfo &sourceOutputInfo) +{ + EXPECT_EQ(capturerManager->Start(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_RUNNING); + EXPECT_EQ(capturerManager->IsRunning(), true); + + EXPECT_EQ(capturerManager->Pause(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_PAUSED); + EXPECT_EQ(capturerManager->IsRunning(), false); + + EXPECT_EQ(capturerManager->Start(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_RUNNING); + EXPECT_EQ(capturerManager->IsRunning(), true); + + EXPECT_EQ(capturerManager->Stop(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_STOPPED); + EXPECT_EQ(capturerManager->IsRunning(), false); + + EXPECT_EQ(capturerManager->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ( + capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == ERR_INVALID_OPERATION, true); + EXPECT_EQ(capturerManager->IsRunning(), false); +} + +TEST_F(HpaeCapturerManagerTest, HpaeCapturerManagerStartStopTest) +{ + HpaeSourceInfo sourceInfo; + sourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sourceInfo.sourceType = SOURCE_TYPE_MIC; + sourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + sourceInfo.samplingRate = SAMPLE_RATE_48000; + sourceInfo.channels = STEREO; + sourceInfo.format = SAMPLE_S16LE; + sourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + sourceInfo.ecType = HPAE_EC_TYPE_NONE; + sourceInfo.micRef = HPAE_REF_OFF; + + std::shared_ptr capturerManager = std::make_shared(sourceInfo); + EXPECT_EQ(capturerManager->Init() == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->IsInit(), true); + + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_48000; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = DEFAULT_FRAME_LENGTH; + streamInfo.sessionId = DEFAULT_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + streamInfo.deviceName = "Built_in_mic"; + EXPECT_EQ(capturerManager->CreateStream(streamInfo) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager.use_count() == 1, true); + + HpaeSourceOutputInfo sourceOutputInfo; + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + TestCheckSourceOutputInfo(sourceOutputInfo, streamInfo); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_NEW); + EXPECT_EQ(capturerManager->IsRunning(), false); + + std::shared_ptr readDataCb = + std::make_shared(g_rootCapturerPath + "HpaeCapturerManagerTest.pcm"); + EXPECT_EQ(capturerManager->RegisterReadCallback(streamInfo.sessionId, readDataCb), SUCCESS); + EXPECT_EQ(readDataCb.use_count() == 1, true); + + StateControlTest(capturerManager, streamInfo, sourceOutputInfo); +} + +static void InitReloadSourceInfo(HpaeSourceInfo &sourceInfo, HpaeSourceInfo &newSourceInfo) +{ + sourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sourceInfo.sourceType = SOURCE_TYPE_MIC; + sourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + sourceInfo.samplingRate = SAMPLE_RATE_48000; + sourceInfo.channels = STEREO; + sourceInfo.format = SAMPLE_S16LE; + sourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + sourceInfo.ecType = HPAE_EC_TYPE_NONE; + sourceInfo.micRef = HPAE_REF_OFF; + + newSourceInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + newSourceInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + newSourceInfo.sourceType = SOURCE_TYPE_VOICE_TRANSCRIPTION; + newSourceInfo.filePath = g_rootCapturerPath + "constructHpaeRendererManagerTest.pcm"; + + newSourceInfo.samplingRate = SAMPLE_RATE_48000; + newSourceInfo.channels = STEREO; + newSourceInfo.format = SAMPLE_S16LE; + newSourceInfo.frameLen = DEFAULT_FRAME_LENGTH; + newSourceInfo.ecType = HPAE_EC_TYPE_SAME_ADAPTER; + newSourceInfo.micRef = HPAE_REF_OFF; +} + +static void InitReloadStreamInfo(HpaeStreamInfo &streamInfo) +{ + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_48000; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = DEFAULT_FRAME_LENGTH; + streamInfo.sessionId = DEFAULT_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + streamInfo.deviceName = "Built_in_mic"; +} + +TEST_F(HpaeCapturerManagerTest, HpaeCapturerManagerReloadTest) +{ + HpaeSourceInfo sourceInfo; + HpaeSourceInfo newSourceInfo; + InitReloadSourceInfo(sourceInfo, newSourceInfo); + + std::shared_ptr capturerManager = std::make_shared(sourceInfo); + EXPECT_EQ(capturerManager->Init() == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->IsInit(), true); + HpaeStreamInfo streamInfo; + InitReloadStreamInfo(streamInfo); + EXPECT_EQ(capturerManager->CreateStream(streamInfo) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager.use_count() == 1, true); + HpaeSourceOutputInfo sourceOutputInfo; + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); + TestCheckSourceOutputInfo(sourceOutputInfo, streamInfo); + EXPECT_EQ(sourceOutputInfo.capturerSessionInfo.state, CAPTURER_NEW); + EXPECT_EQ(capturerManager->ReloadCaptureManager(newSourceInfo) == SUCCESS, true); + WaitForMsgProcessing(capturerManager); + EXPECT_EQ(capturerManager->GetSourceOutputInfo(streamInfo.sessionId, sourceOutputInfo) == SUCCESS, true); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/manager/hpae_inner_capturer_unit_test.cpp b/services/audio_engine/test/unittest/manager/hpae_inner_capturer_unit_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f0c18aa6fcb584030086d60eff0ed45ebf6a875 --- /dev/null +++ b/services/audio_engine/test/unittest/manager/hpae_inner_capturer_unit_test.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "test_case_common.h" +#include "hpae_inner_capturer_manager.h" +#include +#include "audio_errors.h" +#include +#include + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +const uint32_t DEFAULT_SESSION_ID = 123456; +const float FRAME_LENGTH_IN_SECOND = 0.02; +std::string g_rootPath = "/data/data/.pulse_dir/"; + + +static HpaeSinkInfo GetInCapSinkInfo() +{ + HpaeSinkInfo sinkInfo; + sinkInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sinkInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.adapterName = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.filePath = g_rootPath + "constructHpaeInnerCapturerManagerTest.pcm"; + sinkInfo.samplingRate = SAMPLE_RATE_48000; + sinkInfo.frameLen = SAMPLE_RATE_48000 * FRAME_LENGTH_IN_SECOND; + sinkInfo.format = SAMPLE_F32LE; + sinkInfo.channels = STEREO; + sinkInfo.deviceType = DEVICE_TYPE_SPEAKER; + return sinkInfo; +} + +class HpaeInnerCapturerManagerUnitTest : public testing::Test { +public: + void SetUp(); + void TearDown(); + std::shared_ptr hpaeInnerCapturerManager_ = nullptr; +}; + +void HpaeInnerCapturerManagerUnitTest::SetUp(void) +{ + HpaeSinkInfo sinkInfo = GetInCapSinkInfo(); + hpaeInnerCapturerManager_ = std::make_shared(sinkInfo); +} + +void HpaeInnerCapturerManagerUnitTest::TearDown(void) +{ + hpaeInnerCapturerManager_->DeInit(); + hpaeInnerCapturerManager_ = nullptr; +} + +static HpaeStreamInfo GetInCapPlayStreamInfo() +{ + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_44100; + streamInfo.frameLen = SAMPLE_RATE_44100 * FRAME_LENGTH_IN_SECOND; + streamInfo.format = SAMPLE_S16LE; + streamInfo.sessionId = DEFAULT_SESSION_ID + 1; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_PLAY; + streamInfo.sourceType = SOURCE_TYPE_PLAYBACK_CAPTURE; + return streamInfo; +} + +static HpaeStreamInfo GetInCapRecordStreamInfo() +{ + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_44100; + streamInfo.frameLen = SAMPLE_RATE_44100 * FRAME_LENGTH_IN_SECOND; + streamInfo.format = SAMPLE_S16LE; + streamInfo.sessionId = DEFAULT_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + streamInfo.sourceType = SOURCE_TYPE_PLAYBACK_CAPTURE; + return streamInfo; +} + +static void WaitForMsgProcessing(std::shared_ptr& hpaeInnerCapturerManager) +{ + int waitCount = 0; + const int32_t waitCountThd = 5; // 5ms + while (hpaeInnerCapturerManager->IsMsgProcessing()) { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 20ms frameLen, need optimize + waitCount++; + if (waitCount >= waitCountThd) { + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(40)); // 40ms wait time, need optimize + EXPECT_EQ(hpaeInnerCapturerManager->IsMsgProcessing(), false); + EXPECT_EQ(waitCount < waitCountThd, true); +} + +/** + * @tc.name : Test Construct + * @tc.type : FUNC + * @tc.number: Construct_001 + * @tc.desc : Test Construct when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, Construct_001) +{ + EXPECT_NE(hpaeInnerCapturerManager_, nullptr); + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSinkInfo sinkInfo = GetInCapSinkInfo(); + HpaeSinkInfo dstSinkInfo = hpaeInnerCapturerManager_->GetSinkInfo(); + EXPECT_EQ(dstSinkInfo.deviceNetId == sinkInfo.deviceNetId, true); + EXPECT_EQ(dstSinkInfo.deviceClass == sinkInfo.deviceClass, true); + EXPECT_EQ(dstSinkInfo.adapterName == sinkInfo.adapterName, true); + EXPECT_EQ(dstSinkInfo.frameLen == sinkInfo.frameLen, true); + EXPECT_EQ(dstSinkInfo.samplingRate == sinkInfo.samplingRate, true); + EXPECT_EQ(dstSinkInfo.format == sinkInfo.format, true); + EXPECT_EQ(dstSinkInfo.channels == sinkInfo.channels, true); + EXPECT_EQ(dstSinkInfo.deviceType == sinkInfo.deviceType, true); +} + +/** + * @tc.name : Test Init + * @tc.type : FUNC + * @tc.number: Init_001 + * @tc.desc : Test Init. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, Init_001) +{ + EXPECT_NE(hpaeInnerCapturerManager_, nullptr); + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsInit(), true); +} + +/** + * @tc.name : Test DeInit + * @tc.type : FUNC + * @tc.number: DeInit_001 + * @tc.desc : Test DeInit. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, DeInit_001) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->DeInit(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsInit(), false); +} + +/** + * @tc.name : Test CreateStream + * @tc.type : FUNC + * @tc.number: CreateStream_001 + * @tc.desc : Test CreateRendererStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, CreateStream_001) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsInit(), true); + HpaeStreamInfo streamInfo = GetInCapPlayStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(streamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSinkInputInfo sinkInputInfo; + EXPECT_EQ(hpaeInnerCapturerManager_->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo), SUCCESS); +} + +/** + * @tc.name : Test CreateStream + * @tc.type : FUNC + * @tc.number: CreateStream_002 + * @tc.desc : Test CreateCapturerStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, CreateStream_002) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsInit(), true); + HpaeStreamInfo streamInfo = GetInCapRecordStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(streamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSourceOutputInfo sourceOutoputInfo; + EXPECT_EQ(hpaeInnerCapturerManager_->GetSourceOutputInfo(streamInfo.sessionId, sourceOutoputInfo), SUCCESS); +} + +/** + * @tc.name : Test DestroyStream + * @tc.type : FUNC + * @tc.number: DestroyStream_001 + * @tc.desc : Test DestroyRendererStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, DestroyStream_001) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsInit(), true); + HpaeStreamInfo streamInfo = GetInCapPlayStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(streamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->Start(streamInfo.sessionId), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSinkInputInfo sinkInputInfo; + EXPECT_EQ(hpaeInnerCapturerManager_->GetSinkInputInfo(streamInfo.sessionId, + sinkInputInfo) == ERR_INVALID_OPERATION, true); +} + +/** + * @tc.name : Test DestroyStream + * @tc.type : FUNC + * @tc.number: DestroyStream_002 + * @tc.desc : Test DestroyCapturerStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, DestroyStream_002) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeStreamInfo streamInfo = GetInCapRecordStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(streamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSourceOutputInfo sourceOutoputInfo; + EXPECT_EQ(hpaeInnerCapturerManager_->GetSourceOutputInfo(streamInfo.sessionId, sourceOutoputInfo) + == ERR_INVALID_OPERATION, SUCCESS); +} + +/** + * @tc.name : Test StreamStartPauseChange_001 + * @tc.type : FUNC + * @tc.number: StreamStartPauseChange_001 + * @tc.desc : Test StartRendererStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, StreamStartPauseChange_001) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeStreamInfo recordStreamInfo = GetInCapRecordStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(recordStreamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->Start(recordStreamInfo.sessionId), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSourceOutputInfo sourceOutoputInfo; + + HpaeStreamInfo playStreamInfo = GetInCapPlayStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(playStreamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + std::shared_ptr writeInPlayDataCb = std::make_shared(SAMPLE_S16LE); + EXPECT_EQ(hpaeInnerCapturerManager_->RegisterWriteCallback(playStreamInfo.sessionId, writeInPlayDataCb), SUCCESS); + EXPECT_EQ(hpaeInnerCapturerManager_->Start(playStreamInfo.sessionId), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSinkInputInfo sinkInputInfo; + + EXPECT_EQ(hpaeInnerCapturerManager_->Pause(recordStreamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsRunning(), true); + EXPECT_EQ(hpaeInnerCapturerManager_->Pause(playStreamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsRunning(), true); + EXPECT_EQ(hpaeInnerCapturerManager_->GetSinkInputInfo(playStreamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(hpaeInnerCapturerManager_->GetSourceOutputInfo(recordStreamInfo.sessionId, sourceOutoputInfo), SUCCESS); + EXPECT_EQ(sourceOutoputInfo.capturerSessionInfo.state, CAPTURER_PAUSED); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_PAUSED); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(recordStreamInfo.sessionId) == SUCCESS, true); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(playStreamInfo.sessionId) == SUCCESS, true); +} + +/** + * @tc.name : Test StreamStartStopChange_001 + * @tc.type : FUNC + * @tc.number: StreamStartStopChange_001 + * @tc.desc : Test StartCapturerStream when config in vaild. + */ +TEST_F(HpaeInnerCapturerManagerUnitTest, StreamStartStopChange_001) +{ + EXPECT_EQ(hpaeInnerCapturerManager_->Init(), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeStreamInfo recordStreamInfo; + recordStreamInfo.channels = STEREO; + recordStreamInfo.samplingRate = SAMPLE_RATE_44100; + recordStreamInfo.frameLen = SAMPLE_RATE_44100 * FRAME_LENGTH_IN_SECOND; + recordStreamInfo.format = SAMPLE_S16LE; + recordStreamInfo.sessionId = DEFAULT_SESSION_ID; + recordStreamInfo.streamType = STREAM_MUSIC; + recordStreamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + recordStreamInfo.sourceType = SOURCE_TYPE_PLAYBACK_CAPTURE; + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(recordStreamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->Start(recordStreamInfo.sessionId), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSourceOutputInfo sourceOutoputInfo; + + HpaeStreamInfo playStreamInfo = GetInCapPlayStreamInfo(); + EXPECT_EQ(hpaeInnerCapturerManager_->CreateStream(playStreamInfo), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + std::shared_ptr writeInPlayDataCb = std::make_shared(SAMPLE_S16LE); + EXPECT_EQ(hpaeInnerCapturerManager_->RegisterWriteCallback(playStreamInfo.sessionId, writeInPlayDataCb), SUCCESS); + EXPECT_EQ(hpaeInnerCapturerManager_->Start(playStreamInfo.sessionId), SUCCESS); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + HpaeSinkInputInfo sinkInputInfo; + + EXPECT_EQ(hpaeInnerCapturerManager_->Stop(recordStreamInfo.sessionId) == SUCCESS, true); + EXPECT_EQ(hpaeInnerCapturerManager_->Stop(playStreamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeInnerCapturerManager_); + EXPECT_EQ(hpaeInnerCapturerManager_->IsRunning(), true); + EXPECT_EQ(hpaeInnerCapturerManager_->GetSinkInputInfo(playStreamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(hpaeInnerCapturerManager_->GetSourceOutputInfo(recordStreamInfo.sessionId, sourceOutoputInfo), SUCCESS); + EXPECT_EQ(sourceOutoputInfo.capturerSessionInfo.state, CAPTURER_STOPPED); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_STOPPED); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(recordStreamInfo.sessionId) == SUCCESS, true); + EXPECT_EQ(hpaeInnerCapturerManager_->DestroyStream(playStreamInfo.sessionId) == SUCCESS, true); +} +}; +} // namespace OHOS::AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/manager/hpae_manager_test.cpp b/services/audio_engine/test/unittest/manager/hpae_manager_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e421cbe333130a9cb4f05558859401dbaafdcc1b --- /dev/null +++ b/services/audio_engine/test/unittest/manager/hpae_manager_test.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_manager_unit_test.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; +namespace { +static std::string g_rootPath = "/data/data/.pulse_dir/"; +constexpr int32_t FRAME_LENGTH = 882; +constexpr int32_t TEST_STREAM_SESSION_ID = 123456; +constexpr int32_t TEST_SLEEP_TIME_20 = 20; +constexpr int32_t TEST_SLEEP_TIME_40 = 40; + +class HpaeManagerUnitTest : public testing::Test { +public: + void SetUp(); + void TearDown(); + std::shared_ptr hpaeManager_ = nullptr; +}; +void HpaeManagerUnitTest::SetUp() +{ + hpaeManager_ = std::make_shared(); +} + +void HpaeManagerUnitTest::TearDown() +{ + hpaeManager_->DeInit(); + hpaeManager_ = nullptr; +} + +void WaitForMsgProcessing(std::shared_ptr &hpaeManager) +{ + int waitCount = 0; + const int waitCountThd = 5; + while (hpaeManager->IsMsgProcessing()) { + std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_20)); + waitCount++; + if (waitCount >= waitCountThd) { + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_40)); + EXPECT_EQ(hpaeManager->IsMsgProcessing(), false); + EXPECT_EQ(waitCount < waitCountThd, true); +} + +AudioModuleInfo GetSinkAudioModeInfo() +{ + AudioModuleInfo audioModuleInfo; + audioModuleInfo.lib = "libmodule-hdi-sink.z.so"; + audioModuleInfo.channels = "2"; + audioModuleInfo.rate = "48000"; + audioModuleInfo.name = "Speaker_File"; + audioModuleInfo.adapterName = "file_io"; + audioModuleInfo.className = "file_io"; + audioModuleInfo.bufferSize = "7680"; + audioModuleInfo.format = "s32le"; + audioModuleInfo.fixedLatency = "1"; + audioModuleInfo.offloadEnable = "0"; + audioModuleInfo.networkId = "LocalDevice"; + audioModuleInfo.fileName = g_rootPath + audioModuleInfo.adapterName + "_" + audioModuleInfo.rate + "_" + + audioModuleInfo.channels + "_" + audioModuleInfo.format + ".pcm"; + std::stringstream typeValue; + typeValue << static_cast(DEVICE_TYPE_SPEAKER); + audioModuleInfo.deviceType = typeValue.str(); + return audioModuleInfo; +} + +AudioModuleInfo GetSourceAudioModeInfo() +{ + AudioModuleInfo audioModuleInfo; + audioModuleInfo.lib = "libmodule-hdi-source.z.so"; + audioModuleInfo.channels = "2"; + audioModuleInfo.rate = "48000"; + audioModuleInfo.name = "mic"; + audioModuleInfo.adapterName = "file_io"; + audioModuleInfo.className = "file_io"; + audioModuleInfo.bufferSize = "3840"; + audioModuleInfo.format = "s16le"; + audioModuleInfo.fixedLatency = "1"; + audioModuleInfo.offloadEnable = "0"; + audioModuleInfo.networkId = "LocalDevice"; + audioModuleInfo.fileName = g_rootPath + "source_" + audioModuleInfo.adapterName + "_" + audioModuleInfo.rate + "_" + + audioModuleInfo.channels + "_" + audioModuleInfo.format + ".pcm"; + std::stringstream typeValue; + typeValue << static_cast(DEVICE_TYPE_FILE_SOURCE); + audioModuleInfo.deviceType = typeValue.str(); + return audioModuleInfo; +} + +HpaeStreamInfo GetRenderStreamInfo() +{ + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_44100; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = FRAME_LENGTH; + streamInfo.sessionId = TEST_STREAM_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_PLAY; + return streamInfo; +} + +HpaeStreamInfo GetCaptureStreamInfo() +{ + HpaeStreamInfo streamInfo; + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_48000; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = FRAME_LENGTH; + streamInfo.sessionId = TEST_STREAM_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_RECORD; + return streamInfo; +} + +TEST_F(HpaeManagerUnitTest, constructHpaeManagerTest) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + EXPECT_EQ(hpaeManager_->IsRunning(), true); + hpaeManager_->DeInit(); + EXPECT_EQ(hpaeManager_->IsInit(), false); + sleep(1); + EXPECT_EQ(hpaeManager_->IsRunning(), false); +} + +TEST_F(HpaeManagerUnitTest, GetHpaeRenderManagerTest) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + EXPECT_EQ(hpaeManager_->IsRunning(), true); + + std::shared_ptr callback = std::make_shared(); + hpaeManager_->RegisterSerivceCallback(callback); + AudioModuleInfo audioModuleInfo = GetSinkAudioModeInfo(); + EXPECT_EQ(hpaeManager_->OpenAudioPort(audioModuleInfo), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + int32_t portId = callback->GetPortId(); + + hpaeManager_->CloseAudioPort(portId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetCloseAudioPortResult(), SUCCESS); + + hpaeManager_->DeInit(); + EXPECT_EQ(hpaeManager_->IsInit(), false); + EXPECT_EQ(hpaeManager_->IsRunning(), false); +} + +TEST_F(HpaeManagerUnitTest, IHpaeRenderManagerTest) +{ + IHpaeManager::GetHpaeManager()->Init(); + EXPECT_EQ(IHpaeManager::GetHpaeManager()->IsInit(), true); + sleep(1); + EXPECT_EQ(IHpaeManager::GetHpaeManager()->IsRunning(), true); + + AudioModuleInfo audioModuleInfo = GetSinkAudioModeInfo(); + EXPECT_EQ(IHpaeManager::GetHpaeManager()->OpenAudioPort(audioModuleInfo), SUCCESS); + IHpaeManager::GetHpaeManager()->DeInit(); + EXPECT_EQ(IHpaeManager::GetHpaeManager()->IsInit(), false); + EXPECT_EQ(IHpaeManager::GetHpaeManager()->IsRunning(), false); +} + +TEST_F(HpaeManagerUnitTest, IHpaeRenderStreamManagerTest) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + AudioModuleInfo audioModuleInfo = GetSinkAudioModeInfo(); + std::shared_ptr callback = std::make_shared(); + int32_t result = hpaeManager_->RegisterSerivceCallback(callback); + EXPECT_EQ(result, SUCCESS); + EXPECT_EQ(hpaeManager_->OpenAudioPort(audioModuleInfo), SUCCESS); + hpaeManager_->SetDefaultSink(audioModuleInfo.name); + WaitForMsgProcessing(hpaeManager_); + HpaeStreamInfo streamInfo = GetRenderStreamInfo(); + hpaeManager_->CreateStream(streamInfo); + WaitForMsgProcessing(hpaeManager_); + + EXPECT_EQ(hpaeManager_->SetSinkMute(audioModuleInfo.name, true, true), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetSetSinkMuteResult(), SUCCESS); + EXPECT_EQ(hpaeManager_->SetSinkMute(audioModuleInfo.name, false, true), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetSetSinkMuteResult(), SUCCESS); + + EXPECT_EQ(hpaeManager_->SuspendAudioDevice(audioModuleInfo.name, true), SUCCESS); + EXPECT_EQ(hpaeManager_->SuspendAudioDevice(audioModuleInfo.name, false), SUCCESS); + + EXPECT_EQ(hpaeManager_->GetAllSinkInputs(), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetGetAllSinkInputsResult(), SUCCESS); + std::vector sinkInputs = callback->GetSinkInputs(); + EXPECT_EQ(sinkInputs.size(), 1); + for (const auto &it : sinkInputs) { + std::cout << "sinkInputs.sinkName:" << it.sinkName << std::endl; + EXPECT_EQ(it.paStreamId, streamInfo.sessionId); + EXPECT_EQ(it.sinkName, audioModuleInfo.name); + } + hpaeManager_->Release(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + + EXPECT_EQ(hpaeManager_->GetAllSinkInputs(), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetGetAllSinkInputsResult(), SUCCESS); + sinkInputs = callback->GetSinkInputs(); + EXPECT_EQ(sinkInputs.size(), 0); +} + +TEST_F(HpaeManagerUnitTest, IHpaeCaptureStreamManagerTest) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + std::shared_ptr callback = std::make_shared(); + int32_t result = hpaeManager_->RegisterSerivceCallback(callback); + EXPECT_EQ(result, SUCCESS); + + AudioModuleInfo audioModuleInfo = GetSourceAudioModeInfo(); + EXPECT_EQ(hpaeManager_->OpenAudioPort(audioModuleInfo), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + hpaeManager_->SetDefaultSource(audioModuleInfo.name); + int32_t portId = callback->GetPortId(); + HpaeStreamInfo streamInfo = GetCaptureStreamInfo(); + hpaeManager_->CreateStream(streamInfo); + WaitForMsgProcessing(hpaeManager_); + + EXPECT_EQ(hpaeManager_->SetSourceOutputMute(portId, true), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetSetSourceOutputMuteResult(), SUCCESS); + EXPECT_EQ(hpaeManager_->SetSourceOutputMute(portId, false), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetSetSourceOutputMuteResult(), SUCCESS); + + EXPECT_EQ(hpaeManager_->GetAllSourceOutputs(), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(callback->GetGetAllSourceOutputsResult(), SUCCESS); + std::vector sourceOutputs = callback->GetSourceOutputs(); + EXPECT_EQ(sourceOutputs.size(), 1); + for (const auto &it : sourceOutputs) { + std::cout << "deviceSourceId:" << it.deviceSourceId << std::endl; + EXPECT_EQ(it.paStreamId, streamInfo.sessionId); + EXPECT_EQ(it.deviceSourceId, portId); + } + + hpaeManager_->Release(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); +} + +TEST_F(HpaeManagerUnitTest, IHpaeRenderStreamManagerTest002) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + AudioModuleInfo audioModuleInfo = GetSinkAudioModeInfo(); + EXPECT_EQ(hpaeManager_->OpenAudioPort(audioModuleInfo), SUCCESS); + hpaeManager_->SetDefaultSink(audioModuleInfo.name); + WaitForMsgProcessing(hpaeManager_); + HpaeStreamInfo streamInfo = GetRenderStreamInfo(); + hpaeManager_->CreateStream(streamInfo); + WaitForMsgProcessing(hpaeManager_); + int32_t fixedNum = 100; + std::shared_ptr writeFixedValueCb = std::make_shared(SAMPLE_S16LE, fixedNum); + hpaeManager_->RegisterWriteCallback(streamInfo.sessionId, writeFixedValueCb); + std::shared_ptr statusChangeCb = std::make_shared(); + hpaeManager_->RegisterStatusCallback(HPAE_STREAM_CLASS_TYPE_PLAY, streamInfo.sessionId, statusChangeCb); + WaitForMsgProcessing(hpaeManager_); + HpaeSessionInfo sessionInfo; + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.streamInfo.sessionId, streamInfo.sessionId); + EXPECT_EQ(sessionInfo.streamInfo.streamType, streamInfo.streamType); + EXPECT_EQ(sessionInfo.streamInfo.frameLen, streamInfo.frameLen); + EXPECT_EQ(sessionInfo.streamInfo.format, streamInfo.format); + EXPECT_EQ(sessionInfo.streamInfo.samplingRate, streamInfo.samplingRate); + EXPECT_EQ(sessionInfo.streamInfo.channels, streamInfo.channels); + EXPECT_EQ(sessionInfo.streamInfo.streamClassType, streamInfo.streamClassType); + EXPECT_EQ(sessionInfo.state, I_STATUS_IDLE); + + hpaeManager_->Start(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo); + EXPECT_EQ(sessionInfo.state, I_STATUS_STARTING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_STARTED); + + hpaeManager_->Pause(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.state, I_STATUS_PAUSING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_PAUSED); + + hpaeManager_->Stop(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.state, I_STATUS_STOPPING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_STOPPED); + + hpaeManager_->Release(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), ERROR); +} + +TEST_F(HpaeManagerUnitTest, IHpaeCaptureStreamManagerTest002) +{ + EXPECT_NE(hpaeManager_, nullptr); + hpaeManager_->Init(); + EXPECT_EQ(hpaeManager_->IsInit(), true); + sleep(1); + AudioModuleInfo audioModuleInfo = GetSourceAudioModeInfo(); + EXPECT_EQ(hpaeManager_->OpenAudioPort(audioModuleInfo), SUCCESS); + WaitForMsgProcessing(hpaeManager_); + hpaeManager_->SetDefaultSource(audioModuleInfo.name); + HpaeStreamInfo streamInfo = GetCaptureStreamInfo(); + hpaeManager_->CreateStream(streamInfo); + WaitForMsgProcessing(hpaeManager_); + int32_t fixedNum = 100; + std::shared_ptr writeFixedValueCb = std::make_shared(SAMPLE_S16LE, fixedNum); + hpaeManager_->RegisterWriteCallback(streamInfo.sessionId, writeFixedValueCb); + std::shared_ptr statusChangeCb = std::make_shared(); + hpaeManager_->RegisterStatusCallback(HPAE_STREAM_CLASS_TYPE_RECORD, streamInfo.sessionId, statusChangeCb); + WaitForMsgProcessing(hpaeManager_); + HpaeSessionInfo sessionInfo; + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.streamInfo.sessionId, streamInfo.sessionId); + EXPECT_EQ(sessionInfo.streamInfo.streamType, streamInfo.streamType); + EXPECT_EQ(sessionInfo.streamInfo.frameLen, streamInfo.frameLen); + EXPECT_EQ(sessionInfo.streamInfo.streamClassType, streamInfo.streamClassType); + EXPECT_EQ(sessionInfo.state, I_STATUS_IDLE); + hpaeManager_->Start(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo); + EXPECT_EQ(sessionInfo.state, I_STATUS_STARTING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_STARTED); + hpaeManager_->Pause(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.state, I_STATUS_PAUSING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_PAUSED); + hpaeManager_->Stop(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), SUCCESS); + EXPECT_EQ(sessionInfo.state, I_STATUS_STOPPING); + EXPECT_EQ(statusChangeCb->GetStatus(), I_STATUS_STOPPED); + hpaeManager_->Release(streamInfo.streamClassType, streamInfo.sessionId); + WaitForMsgProcessing(hpaeManager_); + EXPECT_EQ(hpaeManager_->GetSessionInfo(streamInfo.streamClassType, streamInfo.sessionId, sessionInfo), ERROR); +} +} // namespace \ No newline at end of file diff --git a/services/audio_engine/test/unittest/manager/hpae_render_manager_test.cpp b/services/audio_engine/test/unittest/manager/hpae_render_manager_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b6e7540dd60c5cb78d5404fdf9b2db0409ec6d2 --- /dev/null +++ b/services/audio_engine/test/unittest/manager/hpae_render_manager_test.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_renderer_manager.h" +#include "hpae_offload_renderer_manager.h" +#include +#include +#include +#include +#include +#include + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; +namespace { +static std::string g_rootPath = "/data/data/.pulse_dir/"; +constexpr int32_t FRAME_LENGTH_882 = 882; +constexpr int32_t FRAME_LENGTH_960 = 960; +constexpr int32_t TEST_STREAM_SESSION_ID = 123456; +constexpr int32_t TEST_SLEEP_TIME_20 = 20; +constexpr int32_t TEST_SLEEP_TIME_40 = 40; +class HpaeRendererManagerTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeRendererManagerTest::SetUp() +{} + +void HpaeRendererManagerTest::TearDown() +{} + +static void TestCheckSinkInputInfo(HpaeSinkInputInfo &sinkInputInfo, const HpaeStreamInfo &streamInfo) +{ + EXPECT_EQ(sinkInputInfo.nodeInfo.channels == streamInfo.channels, true); + EXPECT_EQ(sinkInputInfo.nodeInfo.format == streamInfo.format, true); + EXPECT_EQ(sinkInputInfo.nodeInfo.frameLen == streamInfo.frameLen, true); + EXPECT_EQ(sinkInputInfo.nodeInfo.sessionId == streamInfo.sessionId, true); + EXPECT_EQ(sinkInputInfo.nodeInfo.samplingRate == streamInfo.samplingRate, true); + EXPECT_EQ(sinkInputInfo.nodeInfo.streamType == streamInfo.streamType, true); +} + +static void WaitForMsgProcessing(std::shared_ptr &hpaeRendererManager) +{ + int waitCount = 0; + const int waitCountThd = 5; + while (hpaeRendererManager->IsMsgProcessing()) { + std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_20)); + waitCount++; + if (waitCount >= waitCountThd) { + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(TEST_SLEEP_TIME_40)); + EXPECT_EQ(hpaeRendererManager->IsMsgProcessing(), false); + EXPECT_EQ(waitCount < waitCountThd, true); +} + +template +void TestIRendererManagerConstruct() +{ + HpaeSinkInfo sinkInfo; + sinkInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sinkInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.adapterName = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.filePath = g_rootPath + "constructHpaeRendererManagerTest.pcm"; + sinkInfo.frameLen = FRAME_LENGTH_960; + sinkInfo.samplingRate = SAMPLE_RATE_48000; + sinkInfo.format = SAMPLE_F32LE; + sinkInfo.channels = STEREO; + sinkInfo.deviceType = DEVICE_TYPE_SPEAKER; + std::shared_ptr hpaeRendererManager = std::make_shared(sinkInfo); + HpaeSinkInfo dstSinkInfo = hpaeRendererManager->GetSinkInfo(); + EXPECT_EQ(dstSinkInfo.deviceNetId == sinkInfo.deviceNetId, true); + EXPECT_EQ(dstSinkInfo.deviceClass == sinkInfo.deviceClass, true); + EXPECT_EQ(dstSinkInfo.adapterName == sinkInfo.adapterName, true); + EXPECT_EQ(dstSinkInfo.frameLen == sinkInfo.frameLen, true); + EXPECT_EQ(dstSinkInfo.samplingRate == sinkInfo.samplingRate, true); + EXPECT_EQ(dstSinkInfo.format == sinkInfo.format, true); + EXPECT_EQ(dstSinkInfo.channels == sinkInfo.channels, true); + EXPECT_EQ(dstSinkInfo.deviceType == sinkInfo.deviceType, true); +} + +template +void TestIRendererManagerInit() +{ + HpaeSinkInfo sinkInfo; + sinkInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sinkInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.adapterName = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.filePath = g_rootPath + "constructHpaeRendererManagerTest.pcm"; + sinkInfo.frameLen = FRAME_LENGTH_960; + sinkInfo.samplingRate = SAMPLE_RATE_48000; + sinkInfo.format = SAMPLE_F32LE; + sinkInfo.channels = STEREO; + sinkInfo.deviceType = DEVICE_TYPE_SPEAKER; + std::shared_ptr hpaeRendererManager = std::make_shared(sinkInfo); + EXPECT_EQ(hpaeRendererManager->Init() == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->IsInit(), true); + EXPECT_EQ(hpaeRendererManager->DeInit() == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->IsInit(), false); +} + +void TestRendererManagerCrteateStream( + std::shared_ptr &hpaeRendererManager, HpaeStreamInfo &streamInfo) +{ + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_44100; + streamInfo.format = SAMPLE_S16LE; + streamInfo.frameLen = FRAME_LENGTH_882; + streamInfo.sessionId = TEST_STREAM_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_PLAY; + EXPECT_EQ(hpaeRendererManager->CreateStream(streamInfo) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager.use_count() == 1, true); + HpaeSinkInputInfo sinkInputInfo; + int32_t ret = hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo); + EXPECT_EQ(ret == SUCCESS, true); + TestCheckSinkInputInfo(sinkInputInfo, streamInfo); +} + +template +void TestIRendererManagerCreateDestoryStream() +{ + HpaeSinkInfo sinkInfo; + sinkInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sinkInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.adapterName = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.filePath = g_rootPath + "constructHpaeRendererManagerTest.pcm"; + sinkInfo.frameLen = FRAME_LENGTH_960; + sinkInfo.samplingRate = SAMPLE_RATE_48000; + sinkInfo.format = SAMPLE_F32LE; + sinkInfo.channels = STEREO; + sinkInfo.deviceType = DEVICE_TYPE_SPEAKER; + std::shared_ptr hpaeRendererManager = std::make_shared(sinkInfo); + EXPECT_EQ(hpaeRendererManager->Init() == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->IsInit(), true); + HpaeStreamInfo streamInfo; + TestRendererManagerCrteateStream(hpaeRendererManager, streamInfo); + int32_t ret = hpaeRendererManager->DestroyStream(streamInfo.sessionId); + EXPECT_EQ(ret == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + HpaeSinkInputInfo sinkInputInfo; + ret = hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo); + EXPECT_EQ(ret == ERR_INVALID_OPERATION, true); + streamInfo.channels = STEREO; + streamInfo.samplingRate = SAMPLE_RATE_48000; + streamInfo.format = SAMPLE_F32LE; + streamInfo.frameLen = FRAME_LENGTH_960; + streamInfo.sessionId = TEST_STREAM_SESSION_ID; + streamInfo.streamType = STREAM_MUSIC; + streamInfo.streamClassType = HPAE_STREAM_CLASS_TYPE_PLAY; + EXPECT_EQ(hpaeRendererManager->CreateStream(streamInfo) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager.use_count() == 1, true); + EXPECT_EQ(hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + TestCheckSinkInputInfo(sinkInputInfo, streamInfo); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_NEW); + EXPECT_EQ(hpaeRendererManager->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + ret = hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo); + EXPECT_EQ(ret == ERR_INVALID_OPERATION, true); +} + +template +static void TestIRendererManagerStartPuaseStream() +{ + HpaeSinkInfo sinkInfo; + sinkInfo.deviceNetId = DEFAULT_TEST_DEVICE_NETWORKID; + sinkInfo.deviceClass = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.adapterName = DEFAULT_TEST_DEVICE_CLASS; + sinkInfo.filePath = g_rootPath + "constructHpaeRendererManagerTest.pcm"; + sinkInfo.frameLen = FRAME_LENGTH_960; + sinkInfo.samplingRate = SAMPLE_RATE_48000; + sinkInfo.format = SAMPLE_F32LE; + sinkInfo.channels = STEREO; + sinkInfo.deviceType = DEVICE_TYPE_SPEAKER; + std::shared_ptr hpaeRendererManager = std::make_shared(sinkInfo); + EXPECT_EQ(hpaeRendererManager->Init() == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->IsInit(), true); + HpaeStreamInfo streamInfo; + TestRendererManagerCrteateStream(hpaeRendererManager, streamInfo); + HpaeSinkInputInfo sinkInputInfo; + std::shared_ptr writeIncDataCb = std::make_shared(SAMPLE_S16LE); + EXPECT_EQ(hpaeRendererManager->RegisterWriteCallback(streamInfo.sessionId, writeIncDataCb), SUCCESS); + EXPECT_EQ(writeIncDataCb.use_count() == 1, true); + EXPECT_EQ(hpaeRendererManager->Start(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_RUNNING); + EXPECT_EQ(hpaeRendererManager->IsRunning(), true); + EXPECT_EQ(hpaeRendererManager->Pause(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_PAUSED); + EXPECT_EQ(hpaeRendererManager->Start(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_RUNNING); + EXPECT_EQ(hpaeRendererManager->IsRunning(), true); + EXPECT_EQ(hpaeRendererManager->Stop(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ(hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == SUCCESS, true); + EXPECT_EQ(sinkInputInfo.rendererSessionInfo.state, RENDERER_STOPPED); + EXPECT_EQ(hpaeRendererManager->DestroyStream(streamInfo.sessionId) == SUCCESS, true); + WaitForMsgProcessing(hpaeRendererManager); + EXPECT_EQ( + hpaeRendererManager->GetSinkInputInfo(streamInfo.sessionId, sinkInputInfo) == ERR_INVALID_OPERATION, true); +} + +TEST_F(HpaeRendererManagerTest, constructHpaeRendererManagerTest) +{ + TestIRendererManagerConstruct(); + std::cout << "test offload" << std::endl; + TestIRendererManagerConstruct(); +} + +TEST_F(HpaeRendererManagerTest, HpaeRendererManagerInitTest) +{ + TestIRendererManagerInit(); + std::cout << "test offload" << std::endl; + TestIRendererManagerInit(); +} + +TEST_F(HpaeRendererManagerTest, HpaeRendererManagerCreateDestoryStreamTest) +{ + TestIRendererManagerCreateDestoryStream(); + std::cout << "test offload" << std::endl; + TestIRendererManagerCreateDestoryStream(); +} + +TEST_F(HpaeRendererManagerTest, HpaeRendererManagerStartPuaseStreamTest) +{ + TestIRendererManagerStartPuaseStream(); + std::cout << "test offload" << std::endl; + TestIRendererManagerStartPuaseStream(); +} +} // namespace \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_gain_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_gain_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0b970831f05ea67cc6ec573a1729c06dbe90481 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_gain_node_test.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_sink_input_node.h" +#include "hpae_sink_output_node.h" +#include "hpae_gain_node.h" +#include "test_case_common.h" +#include "audio_errors.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +class HpaeGainNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeGainNodeTest::SetUp() +{} + +void HpaeGainNodeTest::TearDown() +{} + +static int32_t g_testValue = 0; + +namespace { + +constexpr uint32_t DEFAULT_NODE_ID = 1234; +constexpr uint32_t DEFAULT_FRAME_LEN = 960; +constexpr uint32_t DEFAULT_NUM_TWO = 2; + +TEST_F(HpaeGainNodeTest, constructHpaeGainNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + nodeInfo.deviceClass = "primary"; + std::shared_ptr hpaeGainNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeGainNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeGainNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeGainNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeGainNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeGainNode->GetBitWidth(), nodeInfo.format); + std::cout << "HpaeGainNodeTest::GetNodeInfo" << std::endl; + HpaeNodeInfo &retNi = hpaeGainNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + std::cout << "samplingRate: " << retNi.samplingRate << std::endl; + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + std::cout << "nodeId: " << retNi.nodeId << std::endl; + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + std::cout << "frameLen: " << retNi.frameLen << std::endl; + EXPECT_EQ(retNi.channels, nodeInfo.channels); + std::cout << "channels: " << retNi.channels << std::endl; + EXPECT_EQ(retNi.format, nodeInfo.format); + std::cout << "format: " << retNi.format << std::endl; + EXPECT_EQ(retNi.deviceClass, nodeInfo.deviceClass); + std::cout << "deviceClass: " << retNi.deviceClass << std::endl; + std::cout << "HpaeGainNodeTest::GetNodeInfo end" << std::endl; +} +static int32_t TestRendererRenderFrame(const char *data, uint64_t len) +{ + float curGain = 0.0f; + float targetGain = 1.0f; + float stepGain = targetGain - curGain; + uint64_t frameLen = len / (SAMPLE_F32LE * STEREO); + stepGain = stepGain / frameLen; + const float *tempData = reinterpret_cast(data); + for (int32_t i = 0; i < frameLen; i++) { + const float left = tempData[DEFAULT_NUM_TWO * i]; + const float right = tempData[DEFAULT_NUM_TWO * i + 1]; + const float expectedValue = g_testValue * (curGain + i * stepGain); + EXPECT_EQ(left, expectedValue); + EXPECT_EQ(right, expectedValue); + } + return 0; +} + +TEST_F(HpaeGainNodeTest, testHpaeGainTestNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + nodeInfo.streamType = STREAM_MUSIC; + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + std::shared_ptr hpaeGainNode = std::make_shared(nodeInfo); + hpaeGainNode->Connect(hpaeSinkInputNode); + hpaeSinkOutputNode->Connect(hpaeGainNode); + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + EXPECT_EQ(hpaeSinkOutputNode->GetRenderSinkInstance(deviceClass, deviceNetId), 0); + EXPECT_EQ(hpaeSinkInputNode.use_count(), DEFAULT_NUM_TWO); + EXPECT_EQ(hpaeGainNode.use_count(), DEFAULT_NUM_TWO); + EXPECT_EQ(hpaeSinkOutputNode.use_count(), 1); + g_testValue = 0; + int32_t testValue = 100; + std::shared_ptr writeFixedValueCb0 = + std::make_shared(SAMPLE_F32LE, testValue); + g_testValue = testValue; + hpaeSinkInputNode->RegisterWriteCallback(writeFixedValueCb0); + hpaeSinkOutputNode->DoProcess(); + TestRendererRenderFrame(hpaeSinkOutputNode->GetRenderFrameData(), + nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + hpaeSinkOutputNode->DoProcess(); + TestRendererRenderFrame(hpaeSinkOutputNode->GetRenderFrameData(), + nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + hpaeSinkOutputNode->DisConnect(hpaeGainNode); + EXPECT_EQ(hpaeGainNode.use_count(), 1); + hpaeGainNode->DisConnect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); +} +} // namespace diff --git a/services/audio_engine/test/unittest/node/hpae_mixer_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_mixer_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1579b80f29098a3cc6a7e4e6fd1401e3e06c74cb --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_mixer_node_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include "hpae_sink_input_node.h" +#include "hpae_mixer_node.h" +#include "hpae_sink_output_node.h" +#include "test_case_common.h" +#include "audio_errors.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; +namespace { +static constexpr int32_t TEST_VALUE1 = 100; +static constexpr int32_t TEST_VALUE2 = 200; +static constexpr uint32_t TEST_ID = 1243; +static constexpr uint32_t TEST_FRAMELEN = 960; +class HpaeMixerNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeMixerNodeTest::SetUp() +{} + +void HpaeMixerNodeTest::TearDown() +{} + +static int32_t g_testValue = 0; + +TEST_F(HpaeMixerNodeTest, constructHpaeMixerNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = TEST_ID; + nodeInfo.frameLen = TEST_FRAMELEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeMixerNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeMixerNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeMixerNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeMixerNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeMixerNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeMixerNode->GetBitWidth(), nodeInfo.format); + HpaeNodeInfo &retNi = hpaeMixerNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); +} +static int32_t TestRendererRenderFrame(const char *data, uint64_t len) +{ + for (int32_t i = 0; i < len / SAMPLE_F32LE; i++) { + float diff = *((float*)data + i) - g_testValue; + EXPECT_EQ(fabs(diff) < TEST_VALUE_PRESION, true); + } + return 0; +} + +TEST_F(HpaeMixerNodeTest, testHpaePlayOutConnectNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = TEST_ID; + nodeInfo.frameLen = TEST_FRAMELEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + std::shared_ptr hpaeSinkInputNode0 = std::make_shared(nodeInfo); + std::shared_ptr hpaeSinkInputNode1 = std::make_shared(nodeInfo); + std::shared_ptr hpaeMixerNode = std::make_shared(nodeInfo); + hpaeMixerNode->Connect(hpaeSinkInputNode0); + hpaeMixerNode->Connect(hpaeSinkInputNode1); + std::cout << "hpaeMixerNode->GetPreOutNum():" << hpaeMixerNode->GetPreOutNum() << std::endl; + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 0); + hpaeSinkOutputNode->Connect(hpaeMixerNode); + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 1); + EXPECT_EQ(hpaeSinkOutputNode->GetRenderSinkInstance(deviceClass, deviceNetId), 0); + EXPECT_EQ(hpaeSinkInputNode0.use_count(), 1 + 1); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), 1 + 1); + EXPECT_EQ(hpaeMixerNode.use_count(), 1 + 1); + g_testValue = 0; + int32_t testValue = TEST_VALUE1; + std::shared_ptr writeFixedValueCb0 = + std::make_shared(SAMPLE_F32LE, testValue); + g_testValue = g_testValue + testValue; + hpaeSinkInputNode0->RegisterWriteCallback(writeFixedValueCb0); + testValue = TEST_VALUE2; + std::shared_ptr writeFixedValueCb1 = + std::make_shared(SAMPLE_F32LE, testValue); + g_testValue = g_testValue + testValue; + hpaeSinkInputNode1->RegisterWriteCallback(writeFixedValueCb1); + hpaeSinkOutputNode->DoProcess(); + TestRendererRenderFrame(hpaeSinkOutputNode->GetRenderFrameData(), nodeInfo.frameLen * nodeInfo.channels * + GetSizeFromFormat(nodeInfo.format)); + hpaeSinkOutputNode->DisConnect(hpaeMixerNode); + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 0); + EXPECT_EQ(hpaeMixerNode.use_count(), 1); + hpaeMixerNode->DisConnect(hpaeSinkInputNode0); + EXPECT_EQ(hpaeSinkInputNode0.use_count(), 1); + EXPECT_EQ(hpaeMixerNode->GetPreOutNum(), 1); + hpaeMixerNode->DisConnect(hpaeSinkInputNode1); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), 1); + EXPECT_EQ(hpaeMixerNode->GetPreOutNum(), 0); +} + +} // namespace \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_output_cluster_test.cpp b/services/audio_engine/test/unittest/node/hpae_output_cluster_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..78cc46c8ab4e3ef166cd6fa53fe8196d54564204 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_output_cluster_test.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_process_cluster.h" +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_sink_input_node.h" +#include "hpae_output_cluster.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t NODE_ID = 1243; +constexpr uint32_t SESSION_ID_1 = 12345; +constexpr uint32_t SESSION_ID_2 = 12346; +constexpr uint32_t FRAME_LEN = 960; +constexpr uint32_t FRAME_LEN_2 = 820; +constexpr uint32_t NUM_TWO = 2; +constexpr int32_t TEST_VALUE_1 = 300; +constexpr int32_t TEST_VALUE_2 = 400; + +static std::string g_deviceClass = "file_io"; +static std::string g_deviceNetId = "LocalDevice"; + + +static int32_t TestRendererRenderFrame(const char *data, uint64_t len) +{ + float curGain = 0.0f; + float targetGain = 1.0f; + uint64_t frameLen = len / (SAMPLE_F32LE * STEREO); + float stepGain = (targetGain - curGain) / frameLen; + + const float *tempData = reinterpret_cast(data); + for (int32_t i = 0; i < frameLen; i++) { + const float left = tempData[NUM_TWO * i]; + const float right = tempData[NUM_TWO * i + 1]; + const float expectedValue = TEST_VALUE_1 * (curGain + i * stepGain) + TEST_VALUE_2 * (curGain + i * stepGain); + EXPECT_EQ(left, expectedValue); + EXPECT_EQ(right, expectedValue); + } + return 0; +} + +static void InitHpaeWriteDataOutSessionTest(HpaeNodeInfo &nodeInfo, HpaeSinkInfo &dummySinkInfo) +{ + nodeInfo.nodeId = NODE_ID; + nodeInfo.frameLen = FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + + dummySinkInfo.frameLen = FRAME_LEN; + dummySinkInfo.samplingRate = SAMPLE_RATE_48000; + dummySinkInfo.channels = STEREO; + dummySinkInfo.format = SAMPLE_F32LE; + dummySinkInfo.deviceClass = g_deviceClass; + dummySinkInfo.deviceNetId = g_deviceNetId; +} + +class HpaeOutputClusterTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeOutputClusterTest::SetUp() +{} + +void HpaeOutputClusterTest::TearDown() +{} + +TEST_F(HpaeOutputClusterTest, constructHpaeOutputClusterNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = NODE_ID; + nodeInfo.frameLen = FRAME_LEN; + nodeInfo.sessionId = SESSION_ID_1; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + + std::shared_ptr hpaeoutputCluster = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeoutputCluster->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeoutputCluster->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeoutputCluster->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeoutputCluster->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeoutputCluster->GetBitWidth(), nodeInfo.format); + + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + hpaeoutputCluster->Connect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), NUM_TWO); + EXPECT_EQ(hpaeoutputCluster->GetConverterNodeCount(), 1); + nodeInfo.frameLen = FRAME_LEN_2; + nodeInfo.sessionId = SESSION_ID_2; + nodeInfo.samplingRate = SAMPLE_RATE_44100; + std::shared_ptr hpaeSinkInputNode1 = std::make_shared(nodeInfo); + hpaeoutputCluster->Connect(hpaeSinkInputNode1); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), NUM_TWO); + EXPECT_EQ(hpaeoutputCluster->GetConverterNodeCount(), 1); +} + +TEST_F(HpaeOutputClusterTest, testHpaeWriteDataOutSessionTest) +{ + HpaeNodeInfo nodeInfo; + HpaeSinkInfo dummySinkInfo; + InitHpaeWriteDataOutSessionTest(nodeInfo, dummySinkInfo); + std::shared_ptr hpaeOutputCluster = std::make_shared(nodeInfo); + nodeInfo.sessionId = SESSION_ID_1; + nodeInfo.streamType = STREAM_MUSIC; + std::shared_ptr musicSinkInputNode = std::make_shared(nodeInfo); + nodeInfo.sessionId = SESSION_ID_2; + nodeInfo.streamType = STREAM_RING; + std::shared_ptr ringSinkInputNode = std::make_shared(nodeInfo); + nodeInfo.sceneType = HPAE_SCENE_MUSIC; + std::shared_ptr muiscProcessCluster = + std::make_shared(nodeInfo, dummySinkInfo); + nodeInfo.sceneType = HPAE_SCENE_RING; + std::shared_ptr ringProcessCluster = + std::make_shared(nodeInfo, dummySinkInfo); + muiscProcessCluster->Connect(musicSinkInputNode); + ringProcessCluster->Connect(ringSinkInputNode); + hpaeOutputCluster->Connect(muiscProcessCluster); + hpaeOutputCluster->Connect(ringProcessCluster); + + EXPECT_EQ(ringProcessCluster->GetGainNodeCount(), 1); + EXPECT_EQ(muiscProcessCluster->GetGainNodeCount(), 1); + EXPECT_EQ(muiscProcessCluster->GetConverterNodeCount(), 1); + EXPECT_EQ(ringProcessCluster->GetConverterNodeCount(), 1); + EXPECT_EQ(hpaeOutputCluster->GetConverterNodeCount(), NUM_TWO); + EXPECT_EQ(hpaeOutputCluster->GetPreOutNum(), NUM_TWO); + + EXPECT_EQ(hpaeOutputCluster->GetInstance(g_deviceClass, g_deviceNetId), 0); + EXPECT_EQ(musicSinkInputNode.use_count(), NUM_TWO); + EXPECT_EQ(ringSinkInputNode.use_count(), NUM_TWO); + EXPECT_EQ(muiscProcessCluster.use_count(), 1); + std::shared_ptr writeFixedValueCb0 = + std::make_shared(SAMPLE_F32LE, TEST_VALUE_1); + musicSinkInputNode->RegisterWriteCallback(writeFixedValueCb0); + std::shared_ptr writeFixedValueCb1 = + std::make_shared(SAMPLE_F32LE, TEST_VALUE_2); + ringSinkInputNode->RegisterWriteCallback(writeFixedValueCb1); + hpaeOutputCluster->DoProcess(); + TestRendererRenderFrame(hpaeOutputCluster->GetFrameData(), + nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + muiscProcessCluster->DisConnect(musicSinkInputNode); + EXPECT_EQ(musicSinkInputNode.use_count(), 1); + EXPECT_EQ(muiscProcessCluster->GetGainNodeCount(), 0); + ringProcessCluster->DisConnect(ringSinkInputNode); + EXPECT_EQ(ringSinkInputNode.use_count(), 1); + hpaeOutputCluster->DisConnect(muiscProcessCluster); + EXPECT_EQ(hpaeOutputCluster->GetPreOutNum(), 1); + hpaeOutputCluster->DisConnect(ringProcessCluster); + EXPECT_EQ(hpaeOutputCluster->GetPreOutNum(), 0); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_pcm_buffer_test.cpp b/services/audio_engine/test/unittest/node/hpae_pcm_buffer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90be2886b1c143f1329ddab1926ee3124b9de9b8 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_pcm_buffer_test.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "hpae_pcm_buffer.h" +#include "test_case_common.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +class HpaePcmBufferTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaePcmBufferTest::SetUp() +{} + +void HpaePcmBufferTest::TearDown() +{} + +namespace { + +constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2; +constexpr uint32_t DEFAULT_FRAME_LEN = 480; +constexpr uint32_t DEFAULT_SAMPLE_RATE = 48000; +constexpr uint32_t DEFAULT_FRAME_NUM = 2; + + +TEST_F(HpaePcmBufferTest, constructHpaePcmBufferTest) +{ + PcmBufferInfo pcmBufferInfo; + pcmBufferInfo.ch = DEFAULT_CHANNEL_COUNT; + pcmBufferInfo.frameLen = DEFAULT_FRAME_LEN; + pcmBufferInfo.rate = DEFAULT_SAMPLE_RATE; + pcmBufferInfo.frames = DEFAULT_FRAME_NUM; + HpaePcmBuffer hpaePcmBuffer(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(hpaePcmBuffer.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + EXPECT_EQ(hpaePcmBuffer.GetChannelCount(), pcmBufferInfo.ch); + EXPECT_EQ(hpaePcmBuffer.GetFrameLen(), pcmBufferInfo.frameLen); + EXPECT_EQ(hpaePcmBuffer.GetSampleRate(), pcmBufferInfo.rate); + EXPECT_EQ(hpaePcmBuffer.GetFrames(), pcmBufferInfo.frames); + EXPECT_EQ(hpaePcmBuffer.GetReadPos(), 0); + EXPECT_EQ(hpaePcmBuffer.GetWritePos(), 0); + EXPECT_EQ(hpaePcmBuffer.GetCurFrames(), 0); + EXPECT_EQ(hpaePcmBuffer.IsMultiFrames(), false); + size_t addBytes = MEMORY_ALIGN_BYTE_NUM - + (pcmBufferInfo.frameLen * sizeof(float) * pcmBufferInfo.ch) % MEMORY_ALIGN_BYTE_NUM; + size_t frameByteSize = pcmBufferInfo.frameLen * sizeof(float) * pcmBufferInfo.ch + addBytes; + size_t bufferSize = frameByteSize * pcmBufferInfo.frames; + EXPECT_EQ(hpaePcmBuffer.Size(), bufferSize); +} + +TEST_F(HpaePcmBufferTest, assignHpaePcmBufferTest) +{ + PcmBufferInfo pcmBufferInfo; + pcmBufferInfo.ch = DEFAULT_CHANNEL_COUNT; + pcmBufferInfo.frameLen = DEFAULT_FRAME_LEN; + pcmBufferInfo.rate = DEFAULT_SAMPLE_RATE; + pcmBufferInfo.frames = DEFAULT_FRAME_NUM; + HpaePcmBuffer hpaePcmBuffer(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(hpaePcmBuffer.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + size_t tempFrameLen = pcmBufferInfo.frameLen * pcmBufferInfo.ch; + std::vector> testVec; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + testVec.push_back(std::vector(tempFrameLen, 3.14f)); + } + hpaePcmBuffer = testVec; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + for (size_t j = 0; j < tempFrameLen; j++) { + EXPECT_EQ(fabs(hpaePcmBuffer[i][j] - 3.14f) < TEST_VALUE_PRESION, true); + } + } +} + +TEST_F(HpaePcmBufferTest, calHpaePcmBufferTest) +{ + PcmBufferInfo pcmBufferInfo; + pcmBufferInfo.ch = DEFAULT_CHANNEL_COUNT; + pcmBufferInfo.frameLen = DEFAULT_FRAME_LEN; + pcmBufferInfo.rate = DEFAULT_SAMPLE_RATE; + pcmBufferInfo.frames = DEFAULT_FRAME_NUM; + HpaePcmBuffer hpaePcmBuffer(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(hpaePcmBuffer.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + size_t tempFrameLen = pcmBufferInfo.frameLen * pcmBufferInfo.ch; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + for (size_t j = 0; j < tempFrameLen; j++) { + hpaePcmBuffer[i][j] = 3.14f; + } + } + std::vector> testVec; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + testVec.push_back(std::vector(tempFrameLen, 3.14f)); + } + HpaePcmBuffer hpaePcmBuffer2(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(hpaePcmBuffer2.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + hpaePcmBuffer2 = testVec; + hpaePcmBuffer += hpaePcmBuffer2; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + for (size_t j = 0; j < tempFrameLen; j++) { + EXPECT_EQ(fabs(hpaePcmBuffer[i][j] - 6.28f) < TEST_VALUE_PRESION, true); + } + } + hpaePcmBuffer -= hpaePcmBuffer2; + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + for (size_t j = 0; j < tempFrameLen; j++) { + EXPECT_EQ(fabs(hpaePcmBuffer[i][j] - 3.14f) < TEST_VALUE_PRESION, true); + } + } + hpaePcmBuffer.Reset(); + for (size_t i = 0; i < pcmBufferInfo.frames; i++) { + for (size_t j = 0; j < tempFrameLen; j++) { + EXPECT_EQ(fabs(hpaePcmBuffer[i][j] - 0.0f) < TEST_VALUE_PRESION, true); + } + } +} + +TEST_F(HpaePcmBufferTest, calHpaePcmBufferMultiFrameTest) +{ + PcmBufferInfo pcmBufferInfo; + size_t inputFrames = 4; + pcmBufferInfo.ch = DEFAULT_CHANNEL_COUNT; + pcmBufferInfo.frameLen = DEFAULT_FRAME_LEN; + pcmBufferInfo.rate = DEFAULT_SAMPLE_RATE; + pcmBufferInfo.frames = inputFrames; + pcmBufferInfo.isMultiFrames = true; + HpaePcmBuffer hpaePcmBuffer(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(hpaePcmBuffer.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + EXPECT_EQ(hpaePcmBuffer.IsMultiFrames(), true); + + size_t tempFrameLen = pcmBufferInfo.frameLen * pcmBufferInfo.ch; + EXPECT_EQ(hpaePcmBuffer.GetFrameSample(), tempFrameLen); + for (size_t i = 0; i < inputFrames; i++) { + std::vector testVec(tempFrameLen, 3.14f); + hpaePcmBuffer = testVec; + } + std::cout << "tempFrameLen is: " << tempFrameLen << std::endl; + + EXPECT_EQ(hpaePcmBuffer.GetCurFrames(), inputFrames); + EXPECT_EQ(hpaePcmBuffer.GetWritePos(), 0); + + std::vector testVec2(tempFrameLen, 0.0f); + EXPECT_EQ(hpaePcmBuffer.GetFrameSample(), tempFrameLen); + EXPECT_EQ(hpaePcmBuffer.PushFrameData(testVec2), false); + pcmBufferInfo.frames = 1; + pcmBufferInfo.isMultiFrames = false; + HpaePcmBuffer testHpaePcmBuffer(pcmBufferInfo); + EXPECT_EQ(reinterpret_cast(testHpaePcmBuffer.GetPcmDataBuffer()) % MEMORY_ALIGN_BYTE_NUM, 0); + EXPECT_EQ(hpaePcmBuffer.PushFrameData(testHpaePcmBuffer), false); + size_t curFrames = inputFrames; + std::cout << "inputFrames is: " << inputFrames << std::endl; + for (size_t i = 0; i < inputFrames; i++) { + EXPECT_EQ(hpaePcmBuffer.GetFrameData(testHpaePcmBuffer), true); + curFrames--; + EXPECT_EQ(hpaePcmBuffer.GetCurFrames(), curFrames); + for (size_t j = 0; j < tempFrameLen; j++) { + EXPECT_EQ(testHpaePcmBuffer[0][j], 3.14f); + } + } + EXPECT_EQ(hpaePcmBuffer.GetCurFrames(), 0); + EXPECT_EQ(hpaePcmBuffer.GetReadPos(), 0); + EXPECT_EQ(hpaePcmBuffer.GetFrameData(testHpaePcmBuffer), false); + EXPECT_EQ(hpaePcmBuffer.GetFrameData(testVec2), false); +} +} \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_pcm_process_test.cpp b/services/audio_engine/test/unittest/node/hpae_pcm_process_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..749b5856cafb919a4239300b734106844a862114 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_pcm_process_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "hpae_pcm_process.h" +#include "test_case_common.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +constexpr uint32_t NUM_TWO = 2; + +class HpaePcmProcessTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaePcmProcessTest::SetUp() +{} + +void HpaePcmProcessTest::TearDown() +{} + +std::aligned_storage aligned_memory; + +TEST_F(HpaePcmProcessTest, constructHpaePcmProcess) +{ + std::vector testData(TEST_FREAME_LEN); + HpaePcmProcess hpaePcmProcess(testData.data(), TEST_FREAME_LEN); + EXPECT_EQ(hpaePcmProcess.Size(), TEST_FREAME_LEN); + for (int i = 0; i < TEST_FREAME_LEN; i++) { + testData[i] = i; + EXPECT_EQ(hpaePcmProcess[i], i); + const float testValue = hpaePcmProcess[i]; + EXPECT_EQ(hpaePcmProcess[i], testValue); + } + EXPECT_EQ(&testData[0], hpaePcmProcess.Begin()); + EXPECT_EQ(&testData[TEST_FREAME_LEN - 1], hpaePcmProcess.End() - 1); +} + +TEST_F(HpaePcmProcessTest, assignHpaeProcessTest) +{ + std::vector testData(TEST_FREAME_LEN); + for (int i = 0; i < TEST_FREAME_LEN; i++) { + testData[i] = i; + } + std::vector testData2(TEST_SUB_FREAME_LEN); + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + testData2[i] = i; + } + std::vector tmpData(TEST_FREAME_LEN); + for (int i = 0; i < TEST_FREAME_LEN; i++) { + tmpData[i] = i; + } + HpaePcmProcess tmpPcmProcess(tmpData.data(), TEST_FREAME_LEN); + std::vector pcmData(TEST_SUB_FREAME_LEN); + std::vector pcmData2(TEST_FREAME_LEN); + HpaePcmProcess hpaePcmProcessTest(pcmData.data(), TEST_SUB_FREAME_LEN); + hpaePcmProcessTest = testData; + // errcase + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest[i], 0); + } + hpaePcmProcessTest = testData2; + // normalcase + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest[i], i); + } + HpaePcmProcess hpaePcmProcessTest2(pcmData2.data(), TEST_SUB_FREAME_LEN); + hpaePcmProcessTest2 = tmpPcmProcess; + // errcase + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], 0); + } + hpaePcmProcessTest2 = hpaePcmProcessTest; + // normalcase + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], i); + } +} + +TEST_F(HpaePcmProcessTest, calHpaeProcessTest) +{ + std::vector testData(TEST_SUB_FREAME_LEN); + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + testData[i] = i; + } + HpaePcmProcess hpaePcmProcessTest(testData.data(), TEST_SUB_FREAME_LEN); + std::vector testData2(TEST_SUB_FREAME_LEN, NUM_TWO); + HpaePcmProcess hpaePcmProcessTest2(testData2.data(), TEST_SUB_FREAME_LEN); + hpaePcmProcessTest2 += hpaePcmProcessTest; + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], i + NUM_TWO); + } + hpaePcmProcessTest2 -= hpaePcmProcessTest; + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], NUM_TWO); + } + hpaePcmProcessTest2 *= hpaePcmProcessTest; + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], NUM_TWO * i); + } + hpaePcmProcessTest2.Reset(); + for (int i = 0; i < TEST_SUB_FREAME_LEN; i++) { + EXPECT_EQ(hpaePcmProcessTest2[i], 0); + } +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_process_cluster_test.cpp b/services/audio_engine/test/unittest/node/hpae_process_cluster_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..96daba31d41a805188598c162a23250c2acb9f3d --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_process_cluster_test.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_process_cluster.h" +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_sink_input_node.h" +#include "hpae_sink_output_node.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +namespace OHOS { +namespace AudioStandard { + +const int32_t DEFAULT_VALUE_TWO = 2; +const uint32_t DEFAULT_SESSIONID_NUM_FIRST = 12345; +const uint32_t DEFAULT_SESSIONID_NUM_SECOND = 12346; +const uint32_t DEFAULT_NODEID_NUM_FIRST = 1243; +const size_t DEFAULT_FRAMELEN_FIRST = 820; +const size_t DEFAULT_FRAMELEN_SECOND = 960; +const int32_t DEFAULT_TEST_VALUE_FIRST = 100; +const int32_t DEFAULT_TEST_VALUE_SECOND = 200; + + +class HpaeProcessClusterTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeProcessClusterTest::SetUp() +{} + +void HpaeProcessClusterTest::TearDown() +{} + +TEST_F(HpaeProcessClusterTest, constructHpaeProcessClusterNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODEID_NUM_FIRST; + nodeInfo.frameLen = DEFAULT_FRAMELEN_SECOND; + nodeInfo.sessionId = DEFAULT_SESSIONID_NUM_FIRST; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + + HpaeSinkInfo dummySinkInfo; + + std::shared_ptr hpaeProcessCluster = + std::make_shared(nodeInfo, dummySinkInfo); + EXPECT_EQ(hpaeProcessCluster->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeProcessCluster->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeProcessCluster->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeProcessCluster->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeProcessCluster->GetBitWidth(), nodeInfo.format); + HpaeNodeInfo &retNi = hpaeProcessCluster->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); + + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + hpaeProcessCluster->Connect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), static_cast(DEFAULT_VALUE_TWO)); + EXPECT_EQ(hpaeProcessCluster->GetGainNodeCount(), 1); + EXPECT_EQ(hpaeProcessCluster->GetConverterNodeCount(), 1); + nodeInfo.frameLen = DEFAULT_FRAMELEN_FIRST; + nodeInfo.sessionId = DEFAULT_SESSIONID_NUM_SECOND; + nodeInfo.samplingRate = SAMPLE_RATE_44100; + std::shared_ptr hpaeSinkInputNode1 = std::make_shared(nodeInfo); + hpaeProcessCluster->Connect(hpaeSinkInputNode1); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), static_cast(DEFAULT_VALUE_TWO)); + EXPECT_EQ(hpaeProcessCluster->GetGainNodeCount(), (DEFAULT_VALUE_TWO)); + EXPECT_EQ(hpaeProcessCluster->GetConverterNodeCount(), 1 + 1); +} +static int32_t g_testValue1 = 0; +static int32_t g_testValue2 = 0; +static int32_t TestRendererRenderFrame(const char *data, uint64_t len) +{ + float curGain = 0.0f; + float targetGain = 1.0f; + uint64_t frameLen = len / (SAMPLE_F32LE * STEREO); + float stepGain = (targetGain - curGain) / frameLen; + for (int32_t i = 0; i < frameLen; i++) { + EXPECT_EQ(*((float *)data + i * STEREO + 1), (g_testValue1 * (curGain + i * stepGain) + + g_testValue2 * (curGain + i * stepGain))); + EXPECT_EQ(*((float *)data + i * STEREO), (g_testValue1 * (curGain + i * stepGain) + + g_testValue2 * (curGain + i * stepGain))); + } + return 0; +} +static void CrteateHpaeInfo(HpaeNodeInfo &nodeInfo, HpaeSinkInfo &dummySinkInfo) +{ + nodeInfo.nodeId = DEFAULT_NODEID_NUM_FIRST; + nodeInfo.frameLen = DEFAULT_FRAMELEN_SECOND; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + dummySinkInfo.channels = STEREO; + dummySinkInfo.frameLen = DEFAULT_FRAMELEN_SECOND; + dummySinkInfo.format = SAMPLE_F32LE; + dummySinkInfo.samplingRate = SAMPLE_RATE_48000; +} + +TEST_F(HpaeProcessClusterTest, testHpaeWriteDataProcessSessionTest) +{ + HpaeNodeInfo nodeInfo; + HpaeSinkInfo dummySinkInfo; + CrteateHpaeInfo(nodeInfo, dummySinkInfo); + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + nodeInfo.sessionId = DEFAULT_SESSIONID_NUM_FIRST; + std::shared_ptr hpaeSinkInputNode0 = std::make_shared(nodeInfo); + nodeInfo.sessionId = DEFAULT_SESSIONID_NUM_SECOND; + std::shared_ptr hpaeSinkInputNode1 = std::make_shared(nodeInfo); + std::shared_ptr hpaeProcessCluster = + std::make_shared(nodeInfo, dummySinkInfo); + hpaeProcessCluster->Connect(hpaeSinkInputNode0); + hpaeProcessCluster->Connect(hpaeSinkInputNode1); + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 0); + EXPECT_EQ(hpaeProcessCluster->GetGainNodeCount(), DEFAULT_VALUE_TWO); + EXPECT_EQ(hpaeProcessCluster->GetConverterNodeCount(), DEFAULT_VALUE_TWO); + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 0); + hpaeSinkOutputNode->Connect(hpaeProcessCluster); + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 1); + EXPECT_EQ(hpaeSinkOutputNode->GetRenderSinkInstance(deviceClass, deviceNetId), 0); + EXPECT_EQ(hpaeSinkInputNode0.use_count(), static_cast(DEFAULT_VALUE_TWO)); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), static_cast(DEFAULT_VALUE_TWO)); + EXPECT_EQ(hpaeProcessCluster.use_count(), 1); + g_testValue1 = DEFAULT_TEST_VALUE_FIRST; + std::shared_ptr writeFixedValueCb0 = + std::make_shared(SAMPLE_F32LE, g_testValue1); + hpaeSinkInputNode0->RegisterWriteCallback(writeFixedValueCb0); + g_testValue2 = DEFAULT_TEST_VALUE_SECOND; + std::shared_ptr writeFixedValueCb1 = + std::make_shared(SAMPLE_F32LE, g_testValue2); + hpaeSinkInputNode1->RegisterWriteCallback(writeFixedValueCb1); + hpaeSinkOutputNode->DoProcess(); + TestRendererRenderFrame(hpaeSinkOutputNode->GetRenderFrameData(), + nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + hpaeSinkOutputNode->DisConnect(hpaeProcessCluster); + EXPECT_EQ(hpaeSinkOutputNode->GetPreOutNum(), 0); + EXPECT_EQ(hpaeProcessCluster.use_count(), 1); + hpaeProcessCluster->DisConnect(hpaeSinkInputNode0); + EXPECT_EQ(hpaeSinkInputNode0.use_count(), 1); + EXPECT_EQ(hpaeProcessCluster->GetGainNodeCount(), 1); + hpaeProcessCluster->DisConnect(hpaeSinkInputNode1); + EXPECT_EQ(hpaeSinkInputNode1.use_count(), 1); + EXPECT_EQ(hpaeProcessCluster->GetGainNodeCount(), 0); +} + +} // AudioStandard +} // OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_resample_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_resample_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5aec9a2b9e477f197bb4bbe9502f79afbe522ca --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_resample_node_test.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "hpae_sink_input_node.h" +#include "hpae_resample_node.h" +#include "hpae_sink_output_node.h" +#include +#include +#include +#include "test_case_common.h" +#include "audio_errors.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +static constexpr uint32_t TEST_ID = 1243; +static constexpr uint32_t TEST_ID2 = 1246; +static constexpr uint32_t TEST_FRAMELEN1 = 960; +static constexpr uint32_t TEST_FRAMELEN2 = 640; +namespace { +class HpaeResampleNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeResampleNodeTest::SetUp() +{} + +void HpaeResampleNodeTest::TearDown() +{} + +TEST_F(HpaeResampleNodeTest, constructHpaeResampleNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = TEST_ID; + nodeInfo.frameLen = TEST_FRAMELEN1; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + HpaeNodeInfo dstNodeInfo; + dstNodeInfo.nodeId = TEST_ID2; + dstNodeInfo.frameLen = TEST_FRAMELEN1; + dstNodeInfo.samplingRate = SAMPLE_RATE_44100; + dstNodeInfo.channels = CHANNEL_4; + dstNodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeResampleNode = std::make_shared(nodeInfo, dstNodeInfo); + EXPECT_EQ(hpaeResampleNode->GetSampleRate(), dstNodeInfo.samplingRate); + EXPECT_EQ(hpaeResampleNode->GetNodeId(), dstNodeInfo.nodeId); + EXPECT_EQ(hpaeResampleNode->GetFrameLen(), dstNodeInfo.frameLen); + EXPECT_EQ(hpaeResampleNode->GetChannelCount(), dstNodeInfo.channels); + EXPECT_EQ(hpaeResampleNode->GetBitWidth(), dstNodeInfo.format); + HpaeNodeInfo &retNi = hpaeResampleNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, dstNodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, dstNodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, dstNodeInfo.frameLen); + EXPECT_EQ(retNi.channels, dstNodeInfo.channels); + EXPECT_EQ(retNi.format, dstNodeInfo.format); +} + +TEST_F(HpaeResampleNodeTest, testHpaeReampleNodeProcess) +{ + HpaeNodeInfo srcNodeInfo; + srcNodeInfo.nodeId = TEST_ID; + srcNodeInfo.frameLen = TEST_FRAMELEN1; + srcNodeInfo.samplingRate = SAMPLE_RATE_48000; + srcNodeInfo.channels = STEREO; + srcNodeInfo.format = SAMPLE_S32LE; + std::shared_ptr hpaeSinkInputNode = std::make_shared(srcNodeInfo); + HpaeNodeInfo dstNodeInfo; + dstNodeInfo.nodeId = TEST_ID; + dstNodeInfo.frameLen = TEST_FRAMELEN2; + dstNodeInfo.samplingRate = SAMPLE_RATE_32000; + dstNodeInfo.channels = CHANNEL_4; + dstNodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeResampleNode = std::make_shared(srcNodeInfo, dstNodeInfo); +} +} // namespace diff --git a/services/audio_engine/test/unittest/node/hpae_sink_input_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_sink_input_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abf6d41b09ab13d4d8c4bfc2da45971f5a62bec3 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_sink_input_node_test.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_sink_input_node.h" +#include "hpae_sink_output_node.h" +#include "test_case_common.h" +#include "audio_errors.h" +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +class HpaeSinkInputNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeSinkInputNodeTest::SetUp() +{} + +void HpaeSinkInputNodeTest::TearDown() +{} + +namespace { +constexpr int32_t NORMAL_FRAME_LEN = 960; +constexpr int32_t NORMAL_ID = 1243; +TEST_F(HpaeSinkInputNodeTest, constructHpaeSinkInputNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = NORMAL_ID; + nodeInfo.frameLen = NORMAL_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::unique_ptr hpaeSinkInputNode = std::make_unique(nodeInfo); + EXPECT_EQ(hpaeSinkInputNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeSinkInputNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeSinkInputNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeSinkInputNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeSinkInputNode->GetBitWidth(), nodeInfo.format); + HpaeNodeInfo &retNi = hpaeSinkInputNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); +} + +TEST_F(HpaeSinkInputNodeTest, testSinkInputOutputCase) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = NORMAL_ID; + nodeInfo.frameLen = NORMAL_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); + { + std::shared_ptr> outputNode = hpaeSinkInputNode; + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1 + 1); // add 1 count because outputNode + std::shared_ptr hpaeNode = outputNode->GetSharedInstance(); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1 + 1 + 1); // add 1 count because hpaeNode + EXPECT_EQ(hpaeNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeNode->GetBitWidth(), nodeInfo.format); + } + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSinkOutputNode.use_count(), 1); + hpaeSinkOutputNode->Connect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkOutputNode.use_count(), 1); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1 + 1); + OutputPort *outputPort = hpaeSinkInputNode->GetOutputPort(); + EXPECT_EQ(outputPort->GetInputNum(), 1); + hpaeSinkOutputNode->DisConnect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); + outputPort = hpaeSinkInputNode->GetOutputPort(); + EXPECT_EQ(outputPort->GetInputNum(), 0); +} + +TEST_F(HpaeSinkInputNodeTest, testWriteDataToSinkInputDataCase) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = NORMAL_ID; + nodeInfo.frameLen = NORMAL_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + int32_t testNum = 10; + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + std::shared_ptr writeFixedDataCb = std::make_shared(SAMPLE_F32LE); + hpaeSinkInputNode->RegisterWriteCallback(writeFixedDataCb); + for (int32_t i = 0; i < testNum; i++) { + OutputPort *outputPort = hpaeSinkInputNode->GetOutputPort(); + HpaePcmBuffer* outPcmBuffer = outputPort->PullOutputData(); + float* outputPcmData = outPcmBuffer->GetPcmDataBuffer(); + for (int32_t j = 0; j < nodeInfo.frameLen; j++) { + for (int32_t k = 0; k < nodeInfo.channels; k++) { + float diff = outputPcmData[j * nodeInfo.channels + k] - i; + EXPECT_EQ(fabs(diff) < TEST_VALUE_PRESION, true); + } + } + } +} + +TEST_F(HpaeSinkInputNodeTest, testWriteDataToSinkInputAndSinkOutputDataCase) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = NORMAL_ID; + nodeInfo.frameLen = NORMAL_FRAME_LEN; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + int32_t testNum = 10; + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + std::shared_ptr writeFixedDataCb = std::make_shared(SAMPLE_F32LE); + hpaeSinkInputNode->RegisterWriteCallback(writeFixedDataCb); + hpaeSinkOutputNode->Connect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1 + 1); + for (int32_t i = 0; i < testNum; i++) { + OutputPort *outputPort = hpaeSinkInputNode->GetOutputPort(); + HpaePcmBuffer* outPcmBuffer = outputPort->PullOutputData(); + float* outputPcmData = outPcmBuffer->GetPcmDataBuffer(); + for (int32_t j = 0; j < nodeInfo.frameLen; j++) { + for (int32_t k = 0; k < nodeInfo.channels; k++) { + float diff = outputPcmData[j * nodeInfo.channels + k] - i; + EXPECT_EQ(fabs(diff) < TEST_VALUE_PRESION, true); + } + } + } + + hpaeSinkOutputNode->DisConnect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); +} +} \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_sink_output_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_sink_output_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77135816a069a9e4fb831b88ce30e6361dc1b1c7 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_sink_output_node_test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include "hpae_sink_input_node.h" +#include "hpae_sink_output_node.h" +#include "test_case_common.h" +#include "audio_errors.h" +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; +namespace { +class HpaeSinkOutputNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeSinkOutputNodeTest::SetUp() +{} + +void HpaeSinkOutputNodeTest::TearDown() +{} + +TEST_F(HpaeSinkOutputNodeTest, constructHpaeSinkOutputNode) +{ + size_t frameLen = 960; + uint32_t nodeId = 1243; + uint32_t sessionId = 10001; + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = nodeId; + nodeInfo.frameLen = frameLen; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + nodeInfo.sessionId = sessionId; + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSinkOutputNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeSinkOutputNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeSinkOutputNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeSinkOutputNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeSinkOutputNode->GetBitWidth(), nodeInfo.format); + EXPECT_EQ(hpaeSinkOutputNode->GetSessionId(), nodeInfo.sessionId); + + HpaeNodeInfo &retNi = hpaeSinkOutputNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); + EXPECT_EQ(retNi.sessionId, nodeInfo.sessionId); +} +static int32_t TestRendererRenderFrame(const char *data, uint64_t len) +{ + for (int32_t i = 0; i < len / SAMPLE_F32LE; i++) { + float diff = *((float *)data + i) - i; + EXPECT_EQ(diff, 0); + } + return 0; +} + +TEST_F(HpaeSinkOutputNodeTest, testHpaeSinkOutConnectNode) +{ + size_t frameLen = 960; + uint32_t nodeId = 1243; + size_t usedCount = 2; + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = nodeId; + nodeInfo.frameLen = frameLen; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSinkOutputNode = std::make_shared(nodeInfo); + std::shared_ptr hpaeSinkInputNode = std::make_shared(nodeInfo); + hpaeSinkOutputNode->Connect(hpaeSinkInputNode); + std::shared_ptr writeIncDataCb = std::make_shared(SAMPLE_F32LE); + hpaeSinkInputNode->RegisterWriteCallback(writeIncDataCb); + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + EXPECT_EQ(hpaeSinkOutputNode->GetRenderSinkInstance(deviceClass, deviceNetId), 0); + EXPECT_EQ(hpaeSinkOutputNode->GetSinkState() == RENDERER_NEW, true); + IAudioSinkAttr attr; + attr.adapterName = "file_io"; + attr.openMicSpeaker = 0; + attr.format = nodeInfo.format; + attr.sampleRate = nodeInfo.samplingRate; + attr.channel = nodeInfo.channels; + attr.volume = 0.0f; + attr.filePath = nullptr; + attr.deviceNetworkId = deviceNetId.c_str(); + attr.deviceType = 0; + attr.channelLayout = 0; + attr.audioStreamFlag = 0; + + EXPECT_EQ(hpaeSinkOutputNode->RenderSinkInit(attr), ERROR); + EXPECT_EQ(hpaeSinkOutputNode->GetSinkState() == RENDERER_PREPARED, true); + EXPECT_EQ(hpaeSinkOutputNode->RenderSinkStart(), ERROR); + EXPECT_EQ(hpaeSinkOutputNode->GetSinkState() == RENDERER_RUNNING, false); + EXPECT_EQ(hpaeSinkOutputNode->RenderSinkPause(), SUCCESS); + EXPECT_EQ(hpaeSinkOutputNode->GetSinkState() == RENDERER_PAUSED, true); + EXPECT_EQ(hpaeSinkOutputNode->RenderSinkStop(), SUCCESS); + EXPECT_EQ(hpaeSinkOutputNode->GetSinkState() == RENDERER_STOPPED, true); + hpaeSinkOutputNode->DoProcess(); + TestRendererRenderFrame(hpaeSinkOutputNode->GetRenderFrameData(), + nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)); + EXPECT_EQ(hpaeSinkInputNode.use_count(), usedCount); + hpaeSinkOutputNode->DisConnect(hpaeSinkInputNode); + EXPECT_EQ(hpaeSinkInputNode.use_count(), 1); +} +} // namespace \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_source_input_cluster_test.cpp b/services/audio_engine/test/unittest/node/hpae_source_input_cluster_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9cc960284d202391b0e403fc1e0b350f16fd0b9 --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_source_input_cluster_test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_process_cluster.h" +#include "test_case_common.h" +#include "audio_errors.h" +#include "hpae_source_input_node.h" +#include "hpae_source_input_cluster.h" +#include "hpae_source_output_node.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +const uint32_t DEFAULT_FRAME_LENGTH = 960; + +class TestStatusCallback : public INodeCallback, public std::enable_shared_from_this { +public: + std::weak_ptr GetWeakPtr(); + virtual ~TestStatusCallback() = default; +}; + +std::weak_ptr TestStatusCallback::GetWeakPtr() +{ + return weak_from_this(); +} + + +class HpaeSourceInputClusterTest : public ::testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeSourceInputClusterTest::SetUp() +{} + +void HpaeSourceInputClusterTest::TearDown() +{} + +TEST_F(HpaeSourceInputClusterTest, constructHpaeSourceInputClusterNode) +{ + std::shared_ptr g_testStatuscallback = std::make_shared(); + HpaeNodeInfo nodeInfo; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + nodeInfo.statusCallback = g_testStatuscallback->GetWeakPtr(); + + std::shared_ptr hpaeSourceInputCluster = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSourceInputCluster->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeSourceInputCluster->GetNodeId(), 0); + EXPECT_EQ(hpaeSourceInputCluster->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeSourceInputCluster->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeSourceInputCluster->GetBitWidth(), nodeInfo.format); + EXPECT_EQ(hpaeSourceInputCluster->GetSourceInputNodeUseCount(), 1); + std::shared_ptr hpaeSourceOutputNode = std::make_shared(nodeInfo); + hpaeSourceOutputNode->Connect(hpaeSourceInputCluster); + EXPECT_EQ(hpaeSourceInputCluster->GetSourceInputNodeUseCount(), 1 + 1); + EXPECT_EQ(hpaeSourceInputCluster->GetConverterNodeCount(), 0); + + nodeInfo.samplingRate = SAMPLE_RATE_16000; + std::shared_ptr hpaeSourceOutputNode1 = std::make_shared(nodeInfo); + hpaeSourceOutputNode1->ConnectWithInfo(hpaeSourceInputCluster, nodeInfo); + EXPECT_EQ(hpaeSourceInputCluster->GetSourceInputNodeUseCount(), 1 + 1 + 1); + EXPECT_EQ(hpaeSourceInputCluster->GetConverterNodeCount(), 1); +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_source_input_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_source_input_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f9f02596ce98301a55d671b45a52b651c9eb4da --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_source_input_node_test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "hpae_source_input_node.h" +#include "hpae_source_output_node.h" +#include "test_case_common.h" +#include "audio_errors.h" + +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +const uint32_t DEFAULT_FRAME_LENGTH = 960; +const uint32_t DEFAULT_NODE_ID = 1243; + +class HpaeSourceInputNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeSourceInputNodeTest::SetUp() +{} + +void HpaeSourceInputNodeTest::TearDown() +{} + +TEST_F(HpaeSourceInputNodeTest, constructHpaeSourceInputNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSoruceInputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSoruceInputNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeSoruceInputNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeSoruceInputNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeSoruceInputNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeSoruceInputNode->GetBitWidth(), nodeInfo.format); + HpaeNodeInfo &retNi = hpaeSoruceInputNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); +} + +TEST_F(HpaeSourceInputNodeTest, testSourceInputOutputCase) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSoruceInputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 1); + { + std::shared_ptr> ouputNode = hpaeSoruceInputNode; + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 2); // 2 for test + std::shared_ptr hpaeNode = ouputNode->GetSharedInstance(); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 3); // 3 for test + EXPECT_EQ(hpaeNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeNode->GetBitWidth(), nodeInfo.format); + } + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 1); + std::shared_ptr hpaeSourceOutputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSourceOutputNode.use_count(), 1); + hpaeSourceOutputNode->Connect(hpaeSoruceInputNode); + EXPECT_EQ(hpaeSourceOutputNode.use_count(), 1); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 2); // 2 for test + OutputPort *outputPort = hpaeSoruceInputNode->GetOutputPort(); + EXPECT_EQ(outputPort->GetInputNum(), 1); + hpaeSourceOutputNode->DisConnect(hpaeSoruceInputNode); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 1); + outputPort = hpaeSoruceInputNode->GetOutputPort(); + EXPECT_EQ(outputPort->GetInputNum(), 0); +} + +static int32_t TestCapturerSourceFrame(char *frame, uint64_t requestBytes, uint64_t *replyBytes) +{ + for (int32_t i = 0; i < requestBytes / SAMPLE_F32LE; i++) { + *(float *)(frame + i * sizeof(float)) = i; + } + *replyBytes = requestBytes; + return 0; +} + +TEST_F(HpaeSourceInputNodeTest, testWriteDataToSourceInputDataCase) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSoruceInputNode = std::make_shared(nodeInfo); + uint64_t requestBytes = nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format); + std::vector testData(requestBytes); + uint64_t replyBytes = 0; + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + SourceType sourceType = SOURCE_TYPE_MIC; + std::string sourceName = "mic"; + EXPECT_EQ(hpaeSoruceInputNode->GetCapturerSourceInstance(deviceClass, deviceNetId, sourceType, sourceName), 0); + IAudioSourceAttr attr; + attr.adapterName = NULL; + attr.openMicSpeaker = 0; + attr.format = AudioSampleFormat::INVALID_WIDTH; + attr.sampleRate = nodeInfo.samplingRate; + attr.channel = nodeInfo.channels; + attr.volume = 0.0f; + attr.bufferSize = 0; + attr.isBigEndian = false; + attr.filePath = NULL; + attr.deviceNetworkId = NULL; + attr.deviceType = 0; + attr.sourceType = 0; + attr.channelLayout = 0; + attr.audioStreamFlag = 0; + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceInit(attr), ERROR); + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceStart(), SUCCESS); + EXPECT_EQ(hpaeSoruceInputNode->GetSourceState() == CAPTURER_RUNNING, true); + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceStop(), SUCCESS); + EXPECT_EQ(hpaeSoruceInputNode->GetSourceState() == CAPTURER_STOPPED, true); + TestCapturerSourceFrame(testData.data(), requestBytes, &replyBytes); + hpaeSoruceInputNode->WriteCapturerData(testData.data(), requestBytes); + OutputPort *outputPort = hpaeSoruceInputNode->GetOutputPort(); + HpaePcmBuffer* outPcmBuffer = outputPort->PullOutputData(); + float* outputPcmData = outPcmBuffer->GetPcmDataBuffer(); + for (int32_t j = 0; j < nodeInfo.frameLen; j++) { + for (int32_t k = 0; k < nodeInfo.channels; k++) { + float diff = outputPcmData[(j * nodeInfo.channels + k)] - (j * nodeInfo.channels + k); + EXPECT_EQ(fabs(diff) < TEST_VALUE_PRESION, true); + } + } +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/node/hpae_source_output_node_test.cpp b/services/audio_engine/test/unittest/node/hpae_source_output_node_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a3f17c59bf7dc02cbe6b5f6e1a0a2fe99ebf27f --- /dev/null +++ b/services/audio_engine/test/unittest/node/hpae_source_output_node_test.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "hpae_source_input_node.h" +#include "hpae_source_output_node.h" +#include "test_case_common.h" +#include "audio_errors.h" +using namespace OHOS; +using namespace AudioStandard; +using namespace HPAE; + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +const uint32_t DEFAULT_FRAME_LENGTH = 960; +const uint32_t DEFAULT_NODE_ID = 1243; + +class HpaeSourceOutputNodeTest : public testing::Test { +public: + void SetUp(); + void TearDown(); +}; + +void HpaeSourceOutputNodeTest::SetUp() +{} + +void HpaeSourceOutputNodeTest::TearDown() +{} + +class TestReadDataCb : public IReadCallback, public std::enable_shared_from_this { +public: + int32_t OnReadData(std::vector &inputData, size_t requestDataLen) override; + int32_t OnReadData(size_t length) override + { + return SUCCESS; + } + TestReadDataCb() + {} + virtual ~TestReadDataCb() + {} +}; + +int32_t TestReadDataCb::OnReadData(std::vector &inputData, size_t requestDataLen) +{ + for (int32_t i = 0; i < requestDataLen / SAMPLE_F32LE; i++) { + EXPECT_EQ(*(float *)(inputData.data() + i * sizeof(float)), i); + } + return 0; +} + +static int32_t TestCapturerSourceFrame(char *frame, uint64_t requestBytes, uint64_t *replyBytes) +{ + for (int32_t i = 0; i < requestBytes / SAMPLE_F32LE; i++) { + *(float *)(frame + i * sizeof(float)) = i; + } + *replyBytes = requestBytes; + return 0; +} + +TEST_F(HpaeSourceOutputNodeTest, constructHpaeSourceOutputNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSoruceOutputNode = std::make_shared(nodeInfo); + EXPECT_EQ(hpaeSoruceOutputNode->GetSampleRate(), nodeInfo.samplingRate); + EXPECT_EQ(hpaeSoruceOutputNode->GetNodeId(), nodeInfo.nodeId); + EXPECT_EQ(hpaeSoruceOutputNode->GetFrameLen(), nodeInfo.frameLen); + EXPECT_EQ(hpaeSoruceOutputNode->GetChannelCount(), nodeInfo.channels); + EXPECT_EQ(hpaeSoruceOutputNode->GetBitWidth(), nodeInfo.format); + HpaeNodeInfo &retNi = hpaeSoruceOutputNode->GetNodeInfo(); + EXPECT_EQ(retNi.samplingRate, nodeInfo.samplingRate); + EXPECT_EQ(retNi.nodeId, nodeInfo.nodeId); + EXPECT_EQ(retNi.frameLen, nodeInfo.frameLen); + EXPECT_EQ(retNi.channels, nodeInfo.channels); + EXPECT_EQ(retNi.format, nodeInfo.format); +} + +TEST_F(HpaeSourceOutputNodeTest, connectHpaeSourceInputAndOutputNode) +{ + HpaeNodeInfo nodeInfo; + nodeInfo.nodeId = DEFAULT_NODE_ID; + nodeInfo.frameLen = DEFAULT_FRAME_LENGTH; + nodeInfo.samplingRate = SAMPLE_RATE_48000; + nodeInfo.channels = STEREO; + nodeInfo.format = SAMPLE_F32LE; + std::shared_ptr hpaeSoruceInputNode = std::make_shared(nodeInfo); + uint64_t requestBytes = nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format); + std::vector testData(requestBytes); + uint64_t replyBytes = 0; + std::string deviceClass = "file_io"; + std::string deviceNetId = "LocalDevice"; + SourceType sourceType = SOURCE_TYPE_MIC; + std::string sourceName = "mic"; + EXPECT_EQ(hpaeSoruceInputNode->GetCapturerSourceInstance(deviceClass, deviceNetId, sourceType, sourceName), 0); + IAudioSourceAttr attr; + attr.adapterName = NULL; + attr.openMicSpeaker = 0; + attr.format = AudioSampleFormat::INVALID_WIDTH; + attr.sampleRate = nodeInfo.samplingRate; + attr.channel = nodeInfo.channels; + attr.volume = 0.0f; + attr.bufferSize = 0; + attr.isBigEndian = false; + attr.filePath = NULL; + attr.deviceNetworkId = NULL; + attr.deviceType = 0; + attr.sourceType = 0; + attr.channelLayout = 0; + attr.audioStreamFlag = 0; + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceInit(attr), ERROR); + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceStart(), 0); + EXPECT_EQ(hpaeSoruceInputNode->GetSourceState() == CAPTURER_RUNNING, true); + EXPECT_EQ(hpaeSoruceInputNode->CapturerSourceStop(), 0); + EXPECT_EQ(hpaeSoruceInputNode->GetSourceState() == CAPTURER_STOPPED, true); + TestCapturerSourceFrame(testData.data(), requestBytes, &replyBytes); + hpaeSoruceInputNode->WriteCapturerData(testData.data(), requestBytes); + std::shared_ptr hpaeSoruceOutputNode = std::make_shared(nodeInfo); + std::shared_ptr testReadDataCb = std::make_shared(); + hpaeSoruceOutputNode->RegisterReadCallback(testReadDataCb); + hpaeSoruceOutputNode->Connect(hpaeSoruceInputNode); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 2); // 2 for test + hpaeSoruceOutputNode->DoProcess(); + hpaeSoruceOutputNode->DisConnect(hpaeSoruceInputNode); + EXPECT_EQ(hpaeSoruceInputNode.use_count(), 1); +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_engine/test/unittest/resource/ohos_test.xml b/services/audio_engine/test/unittest/resource/ohos_test.xml new file mode 100644 index 0000000000000000000000000000000000000000..21512912b0d5ce7b56f0d38c66d09f73550c09d2 --- /dev/null +++ b/services/audio_engine/test/unittest/resource/ohos_test.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/services/audio_engine/utils/high_resolution_timer.h b/services/audio_engine/utils/high_resolution_timer.h new file mode 100644 index 0000000000000000000000000000000000000000..e7f55c0e587edc756cafa824bdfe7367fa5366d0 --- /dev/null +++ b/services/audio_engine/utils/high_resolution_timer.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HIGH_RRSOLUTION_TIMER_H +#define HIGH_RRSOLUTION_TIMER_H +#include +#include +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +typedef std::chrono::high_resolution_clock::time_point TimePoint; +class HighResolutionTimer { +public: + HighResolutionTimer() : startTime_(), endTime_() + {} + + void Start() + { + startTime_ = std::chrono::high_resolution_clock::now(); + } + + void Stop() + { + endTime_ = std::chrono::high_resolution_clock::now(); + } + + template + auto Elapsed() const + { + return std::chrono::duration_cast(endTime_ - startTime_).count(); + } + +private: + std::chrono::high_resolution_clock::time_point startTime_; + std::chrono::high_resolution_clock::time_point endTime_; +}; + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS + +#endif // __HIGH_RRSOLUTION_TIMER_H__ \ No newline at end of file diff --git a/services/audio_engine/utils/hpae_format_convert.cpp b/services/audio_engine/utils/hpae_format_convert.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ddcde6c0334862dc00d31632194f10589fd2a69 --- /dev/null +++ b/services/audio_engine/utils/hpae_format_convert.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "securec.h" +#include "hpae_format_convert.h" + + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr float FLOAT_EPS = 1e-9f; +constexpr int OFFSET_BIT_24 = 3; +constexpr int BIT_DEPTH_TWO = 2; +constexpr int BIT_8 = 8; +constexpr int BIT_16 = 16; +constexpr int BIT_32 = 32; + +static uint32_t Read24Bit(const uint8_t *p) +{ + return ((uint32_t) p[BIT_DEPTH_TWO] << BIT_16) | ((uint32_t) p[1] << BIT_8) | ((uint32_t) p[0]); +} + +static void Write24Bit(uint8_t *p, uint32_t u) +{ + p[BIT_DEPTH_TWO] = (uint8_t) (u >> BIT_16); + p[1] = (uint8_t) (u >> BIT_8); + p[0] = (uint8_t) u; +} + +static void ConvertFrom16BitToFloat(unsigned n, const int16_t *a, float *b) +{ + for (; n > 0; n--) { + *(b++) = *(a++) * (1.0f / (1 << (BIT_16 - 1))); + } +} + +static void ConvertFrom24BitToFloat(unsigned n, const uint8_t *a, float *b) +{ + for (; n > 0; n--) { + int32_t s = Read24Bit(a) << BIT_8; + *b = s * (1.0f / (1U << (BIT_32 - 1))); + a += OFFSET_BIT_24; + b++; + } +} + +static void ConvertFrom32BitToFloat(unsigned n, const int32_t *a, float *b) +{ + for (; n > 0; n--) { + *(b++) = *(a++) * (1.0f / (1U << (BIT_32 - 1))); + } +} + +static float CapMax(float v) +{ + float value = v; + if (v > 1.0f) { + value = 1.0f - FLOAT_EPS; + } else if (v < -1.0f) { + value = -1.0f + FLOAT_EPS; + } + return value; +} + +static void ConvertFromFloatTo16Bit(unsigned n, const float *a, int16_t *b) +{ + for (; n > 0; n--) { + float tmp = *a++; + float v = CapMax(tmp) * (1 << (BIT_16 - 1)); + *(b++) = (int16_t) v; + } +} + +static void ConvertFromFloatTo24Bit(unsigned n, const float *a, uint8_t *b) +{ + for (; n > 0; n--) { + float tmp = *a++; + float v = CapMax(tmp) * (1U << (BIT_32 - 1)); + Write24Bit(b, ((int32_t) v) >> BIT_8); + b += OFFSET_BIT_24; + } +} + +static void ConvertFromFloatTo32Bit(unsigned n, const float *a, int32_t *b) +{ + for (; n > 0; n--) { + float tmp = *a++; + float v = CapMax(tmp) * (1U << (BIT_32 - 1)); + *(b++) = (int32_t) v; + } +} + +void ConvertToFloat(AudioSampleFormat format, unsigned n, void *src, float *dst) +{ + int32_t ret; + switch (format) { + case SAMPLE_S16LE: + ConvertFrom16BitToFloat(n, (const int16_t *)src, dst); + break; + case SAMPLE_S24LE: + ConvertFrom24BitToFloat(n, (const uint8_t *)src, dst); + break; + case SAMPLE_S32LE: + ConvertFrom32BitToFloat(n, (const int32_t *)src, dst); + break; + default: + ret = memcpy_s(dst, n * sizeof(float), (const float *)src, n * sizeof(float)); + if (ret != 0) { + float *srcFloat = (float *)src; + for (uint32_t i = 0; i < n; i++) { + dst[i] = srcFloat[i]; + } + } + break; + } +} + +void ConvertFromFloat(AudioSampleFormat format, unsigned n, float *src, void *dst) +{ + int32_t ret; + switch (format) { + case SAMPLE_S16LE: + ConvertFromFloatTo16Bit(n, src, (int16_t *)dst); + break; + case SAMPLE_S24LE: + ConvertFromFloatTo24Bit(n, src, (uint8_t *)dst); + break; + case SAMPLE_S32LE: + ConvertFromFloatTo32Bit(n, src, (int32_t *)dst); + break; + default: + ret = memcpy_s(dst, n * sizeof(float), src, n * sizeof(float)); + if (ret != 0) { + float *dstFloat = (float *)dst; + for (uint32_t i = 0; i < n; i++) { + dstFloat[i] = src[i]; + } + } + break; + } +} +}}} \ No newline at end of file diff --git a/services/audio_engine/utils/hpae_format_convert.h b/services/audio_engine/utils/hpae_format_convert.h new file mode 100644 index 0000000000000000000000000000000000000000..3f9ad18fd9aec4407502ff7e6b6e61d4b216e248 --- /dev/null +++ b/services/audio_engine/utils/hpae_format_convert.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_FORMAT_CONVERT_H +#define HPAE_FORMAT_CONVERT_H +#include "audio_stream_info.h" +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +void ConvertToFloat(AudioSampleFormat format, unsigned n, void *src, float *dst); + +void ConvertFromFloat(AudioSampleFormat format, unsigned n, float *src, void *dst); +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/services/audio_engine/utils/hpae_no_lock_queue.cpp b/services/audio_engine/utils/hpae_no_lock_queue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4128a6b4ed44adfa17e31774b353f6475f958a81 --- /dev/null +++ b/services/audio_engine/utils/hpae_no_lock_queue.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "audio_engine_log.h" +#include + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +constexpr uint32_t MAX_REQUEST_COUNT = std::numeric_limits::max() - 1; +constexpr uint32_t INVALID_REQUEST_ID = std::numeric_limits::max(); +constexpr uint64_t SHIFT_32_OFFSET = 32; +HpaeNoLockQueue::HpaeNoLockQueue(size_t maxRequestCount) +{ + if (maxRequestCount > MAX_REQUEST_COUNT) { + AUDIO_WARNING_LOG("[HpaeNoLockQueue] maxRequestCount %{public}zu is beyound Max Count", maxRequestCount); + maxRequestCount = MAX_REQUEST_COUNT; + } + if (maxRequestCount <= 0) { + AUDIO_WARNING_LOG("[HpaeNoLockQueue] maxRequestCount can not be zero"); + return; + } + InitQueue(maxRequestCount); +} + +HpaeNoLockQueue::~HpaeNoLockQueue() +{ + AUDIO_INFO_LOG("HpaeNoLockQueue destroyed"); +} +void HpaeNoLockQueue::InitQueue(size_t maxRequestCount) +{ + requestQueue_.resize(maxRequestCount); + tempRequestQueue_.reserve(maxRequestCount); + + freeRequestHeadIndex_ = 0; + for (size_t i = 0; i < maxRequestCount - 1; ++i) { + requestQueue_[i].nextRequestIndex = i + 1; + } + requestQueue_[maxRequestCount - 1].nextRequestIndex = INVALID_REQUEST_ID; + requestHeadIndex_ = INVALID_REQUEST_ID; + AUDIO_INFO_LOG("Init Queue size is %{public}zu", maxRequestCount); +} +void HpaeNoLockQueue::PushRequest(Request &&request) +{ + const uint64_t freeRequestIndex = GetRequestNode(&freeRequestHeadIndex_); + if (GetRequsetIndex(freeRequestIndex) == INVALID_REQUEST_ID) { + AUDIO_WARNING_LOG("reached Queue Capacity: drop this request"); + return; + } + requestQueue_[GetRequsetIndex(freeRequestIndex)].request = std::move(request); + PushRequestNode(&requestHeadIndex_, freeRequestIndex); +} + +void HpaeNoLockQueue::HandleRequests() +{ + const uint64_t oldRequestFlag = (GetRequsetFlag(requestHeadIndex_) << 32) + INVALID_REQUEST_ID; + const uint64_t oldRequestHeadindex = requestHeadIndex_.exchange(oldRequestFlag); + ProcessRequests(oldRequestHeadindex, true); +} + +void HpaeNoLockQueue::Reset() +{ + const uint64_t oldRequestFlag = (GetRequsetFlag(requestHeadIndex_) << 32) + INVALID_REQUEST_ID; + const uint64_t oldRequestHeadindex = requestHeadIndex_.exchange(oldRequestFlag); + ProcessRequests(oldRequestHeadindex, false); +} + +uint64_t HpaeNoLockQueue::IncRequsetIndex(uint64_t requestIndex) +{ + return requestIndex + (static_cast(1) << SHIFT_32_OFFSET); +} + +uint64_t HpaeNoLockQueue::GetRequsetIndex(uint64_t requestIndex) +{ + return requestIndex & std::numeric_limits::max(); +} + +uint64_t HpaeNoLockQueue::GetRequsetFlag(uint64_t requestFlag) +{ + return requestFlag >> SHIFT_32_OFFSET; +} + +void HpaeNoLockQueue::PushRequestNode(std::atomic *pRequestHeadIndex, uint64_t index) +{ + if (pRequestHeadIndex == nullptr) { + return; + } + uint64_t requestHeadIndex; + do { + requestHeadIndex = pRequestHeadIndex->load(); + requestQueue_[GetRequsetIndex(index)].nextRequestIndex = requestHeadIndex; + } while (!std::atomic_compare_exchange_strong(pRequestHeadIndex, &requestHeadIndex, index)); +} + +uint64_t HpaeNoLockQueue::GetRequestNode(std::atomic *pRequestHeadIndex) +{ + if (pRequestHeadIndex == nullptr) { + return std::numeric_limits::max(); + } + uint64_t requestHeadIndex; + uint64_t nextRequestIndex; + do { + requestHeadIndex = pRequestHeadIndex->load(); + if (GetRequsetIndex(requestHeadIndex) == INVALID_REQUEST_ID) { + return INVALID_REQUEST_ID; + } + nextRequestIndex = requestQueue_[GetRequsetIndex(requestHeadIndex)].nextRequestIndex; + } while (!std::atomic_compare_exchange_strong(pRequestHeadIndex, &requestHeadIndex, nextRequestIndex)); + return IncRequsetIndex(requestHeadIndex); +} + +void HpaeNoLockQueue::ProcessRequests(uint64_t requestHeadIndex, bool isProcess) +{ + uint64_t tempIndex = requestHeadIndex; + while (GetRequsetIndex(tempIndex) != INVALID_REQUEST_ID) { + RequestNode *tempRequest = &requestQueue_[GetRequsetIndex(tempIndex)]; + uint64_t nextRequest = tempRequest->nextRequestIndex; + tempRequestQueue_.emplace_back(std::move(tempRequest->request)); + tempRequest->request = nullptr; + PushRequestNode(&freeRequestHeadIndex_, tempIndex); + tempIndex = nextRequest; + } + + if (isProcess) { + for (std::vector::reverse_iterator requestIter = tempRequestQueue_.rbegin(); + requestIter != tempRequestQueue_.rend(); + ++requestIter) { + if (*requestIter != nullptr) { + (*requestIter)(); + } + } + } + tempRequestQueue_.clear(); +} + +bool HpaeNoLockQueue::IsFinishProcess() +{ + if (GetRequsetIndex(requestHeadIndex_) == INVALID_REQUEST_ID) { + return true; + } else { + return false; + } +} +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/utils/hpae_no_lock_queue.h b/services/audio_engine/utils/hpae_no_lock_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..47d45b86dcd97bc213e97d844858a8c783fef33c --- /dev/null +++ b/services/audio_engine/utils/hpae_no_lock_queue.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NOLOCK_REQUEST_QUEUE_H +#define NOLOCK_REQUEST_QUEUE_H +#include +#include +#include +#include + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +const size_t CURRENT_REQUEST_COUNT = 1000; +using Request = std::function; +struct RequestNode { + RequestNode() = default; + RequestNode(const RequestNode &requestNode) : nextRequestIndex() + {} + RequestNode& operator=(const RequestNode& requestNode) = delete; + Request request; + std::atomic nextRequestIndex; +}; +class HpaeNoLockQueue { +public: + explicit HpaeNoLockQueue(size_t maxRequestCount); + ~HpaeNoLockQueue(); + + void PushRequest(Request &&request); + void HandleRequests(); + void Reset(); + bool IsFinishProcess(); + +private: + void InitQueue(size_t maxRequestCount); + uint64_t IncRequsetIndex(uint64_t requestIndex); + uint64_t GetRequsetIndex(uint64_t requestIndex); + uint64_t GetRequsetFlag(uint64_t requestFlag); + + void PushRequestNode(std::atomic *pRequestHeadIndex, uint64_t index); + uint64_t GetRequestNode(std::atomic *pRequestHeadIndex); + void ProcessRequests(uint64_t requestHeadIndex, bool isProcess); + +private: + std::atomic freeRequestHeadIndex_; + std::atomic requestHeadIndex_; + std::vector requestQueue_; + std::vector tempRequestQueue_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif diff --git a/services/audio_engine/utils/hpae_pcm_dumper.cpp b/services/audio_engine/utils/hpae_pcm_dumper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04080e56a72f5c5b4346eae21ffa33e32707d5eb --- /dev/null +++ b/services/audio_engine/utils/hpae_pcm_dumper.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "hpae_pcm_dumper.h" +#include "audio_errors.h" +#include "audio_engine_log.h" +#include "audio_utils.h" +#include "securec.h" + +namespace OHOS { +namespace AudioStandard { +namespace HPAE { + +HpaePcmDumper::HpaePcmDumper(const std::string &filename) +{ + DumpFileUtil::OpenDumpFile(DumpFileUtil::DUMP_SERVER_PARA, filename, &dumpFile_); + filename_ = filename; +} + +HpaePcmDumper::~HpaePcmDumper() +{ + DumpFileUtil::CloseDumpFile(&dumpFile_); +} + +int32_t HpaePcmDumper::Dump(const int8_t *buffer, int32_t length) +{ + DumpFileUtil::WriteDumpFile(dumpFile_, (void *)(buffer), length); + return SUCCESS; +} + +bool HpaePcmDumper::CheckAndReopenHandlde() +{ + if (dumpFile_ != nullptr) { + return true; + } else { + DumpFileUtil::OpenDumpFile(DumpFileUtil::DUMP_SERVER_PARA, filename_, &dumpFile_); + AUDIO_DEBUG_LOG("Reopen dump file: %{public}s", filename_.c_str()); + } + return false; +} + +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_engine/utils/hpae_pcm_dumper.h b/services/audio_engine/utils/hpae_pcm_dumper.h new file mode 100644 index 0000000000000000000000000000000000000000..ca7d5e3732d631d683ff223bb25a404eeea9e75e --- /dev/null +++ b/services/audio_engine/utils/hpae_pcm_dumper.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HPAE_PCM_DUMPER_H +#define HPAE_PCM_DUMPER_H +#include +namespace OHOS { +namespace AudioStandard { +namespace HPAE { +class HpaePcmDumper { +public: + explicit HpaePcmDumper(const std::string &filename); + ~HpaePcmDumper(); + int32_t Dump(const int8_t *buffer, int32_t length); + bool CheckAndReopenHandlde(); +private: + FILE *dumpFile_ = nullptr; + std::string filename_; +}; +} // namespace HPAE +} // namespace AudioStandard +} // namespace OHOS +#endif \ No newline at end of file diff --git a/test/BUILD.gn b/test/BUILD.gn index 694701a237f26e8e630ab836b3a4c55477b43f8b..fe236f40ce246772c38554c4c769f67be8236af7 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -59,6 +59,7 @@ group("audio_unit_test") { "../frameworks/native/ohaudio/test/unittest/oh_audio_routing_manager_test:audio_oh_routing_manager_unit_test", "../frameworks/native/ohaudio/test/unittest/oh_audio_stream_builder_test:audio_oh_builder_unit_test", "../frameworks/native/toneplayer/test/unittest:audio_toneplayer_unit_test", + "../services/audio_engine/test/unittest:audio_engine_unit_test", "../services/audio_policy/test:audio_policy_unittest_packages", "../services/audio_service/test/unittest:audio_balance_unit_test", "../services/audio_service/test/unittest:audio_dump_pcm_unit_test",