• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef LOG_TAG
16 #define LOG_TAG "AudioLimiter"
17 #endif
18 
19 #include "audio_errors.h"
20 #include "audio_limiter.h"
21 #include "audio_common_log.h"
22 #include "audio_utils.h"
23 
24 #include "securec.h"
25 
26 namespace OHOS {
27 namespace AudioStandard {
28 
29 const float NEXT_LEVEL = 0.5f;
30 const float THRESHOLD = 0.92f;
31 const float LEVEL_ATTACK = 0.3f;
32 const float LEVEL_RELEASE = 0.7f;
33 const float GAIN_ATTACK = 0.1f;
34 const float GAIN_RELEASE = 0.6f;
35 const int32_t AUDIO_FORMAT_PCM_FLOAT = 4;
36 const int32_t PROC_COUNT = 4;  // process 4 times
37 const int32_t AUDIO_MS_PER_S = 1000;
38 const int32_t AUDIO_LMT_ALGO_CHANNEL = 2;    // 2 channel for stereo
39 
AudioLimiter(int32_t sinkIndex)40 AudioLimiter::AudioLimiter(int32_t sinkIndex)
41 {
42     sinkIndex_ = sinkIndex;
43     nextLev_ = NEXT_LEVEL;
44     threshold_ = THRESHOLD;
45     levelAttack_ = LEVEL_ATTACK;
46     levelRelease_ = LEVEL_RELEASE;
47     gainAttack_ = GAIN_ATTACK;
48     gainRelease_ = GAIN_RELEASE;
49     format_ = AUDIO_FORMAT_PCM_FLOAT;
50     latency_ = 0;
51     algoFrameLen_ = 0;
52     curMaxLev_ = 0.0f;
53     gain_ = 0.0f;
54     bufHis_ = nullptr;
55     AUDIO_INFO_LOG("AudioLimiter");
56 }
57 
~AudioLimiter()58 AudioLimiter::~AudioLimiter()
59 {
60     ReleaseBuffer();
61     DumpFileUtil::CloseDumpFile(&dumpFileInput_);
62     DumpFileUtil::CloseDumpFile(&dumpFileOutput_);
63     AUDIO_INFO_LOG("~AudioLimiter");
64 }
65 
ReleaseBuffer()66 void AudioLimiter::ReleaseBuffer()
67 {
68     if (bufHis_ != nullptr) {
69         delete[] bufHis_;
70         bufHis_ = nullptr;
71     }
72     return;
73 }
74 
SetConfig(int32_t maxRequest,int32_t biteSize,int32_t sampleRate,int32_t channels)75 int32_t AudioLimiter::SetConfig(int32_t maxRequest, int32_t biteSize, int32_t sampleRate, int32_t channels)
76 {
77     CHECK_AND_RETURN_RET_LOG(maxRequest > 0 && biteSize > 0 && sampleRate > 0 && channels == AUDIO_LMT_ALGO_CHANNEL,
78         ERROR, "Invalid input parameters");
79     algoFrameLen_ = maxRequest / (biteSize * PROC_COUNT);
80     latency_ = static_cast<uint32_t>(algoFrameLen_ * AUDIO_MS_PER_S / (sampleRate * channels));
81     AUDIO_INFO_LOG("maxRequest = %{public}d, biteSize = %{public}d, sampleRate = %{public}d, channels = %{public}d,"
82         "algoFrameLen_ = %{public}d, latency_ = %{public}d",
83         maxRequest, biteSize, sampleRate, channels, algoFrameLen_, latency_);
84     bufHis_ = new (std::nothrow) float[algoFrameLen_]();
85     CHECK_AND_RETURN_RET_LOG(bufHis_ != nullptr, ERROR, "allocate limit algorithm buffer failed");
86 
87     dumpFileNameIn_ = std::to_string(sinkIndex_) + "_limiter_in_" + GetTime() + "_" + std::to_string(sampleRate) + "_"
88         + std::to_string(channels) + "_" + std::to_string(format_) + ".pcm";
89     DumpFileUtil::OpenDumpFile(DumpFileUtil::DUMP_SERVER_PARA, dumpFileNameIn_, &dumpFileInput_);
90     dumpFileNameOut_ = std::to_string(sinkIndex_) + "_limiter_out_" + GetTime() + "_" + std::to_string(sampleRate) + "_"
91         + std::to_string(channels) + "_" + std::to_string(format_) + ".pcm";
92     DumpFileUtil::OpenDumpFile(DumpFileUtil::DUMP_SERVER_PARA, dumpFileNameOut_, &dumpFileOutput_);
93 
94     return SUCCESS;
95 }
96 
Process(int32_t frameLen,float * inBuffer,float * outBuffer)97 int32_t AudioLimiter::Process(int32_t frameLen, float *inBuffer, float *outBuffer)
98 {
99     CHECK_AND_RETURN_RET_LOG(algoFrameLen_ * PROC_COUNT == frameLen, ERROR,
100         "error, algoFrameLen_ = %{public}d, frameLen = %{public}d", algoFrameLen_, frameLen);
101     int32_t ptrIndex = 0;
102     DumpFileUtil::WriteDumpFile(dumpFileInput_, static_cast<void *>(inBuffer), frameLen * sizeof(float));
103     for (int32_t i = 0; i < PROC_COUNT; i++) {
104         ProcessAlgo(algoFrameLen_, inBuffer + ptrIndex, outBuffer + ptrIndex);
105         ptrIndex += algoFrameLen_;
106     }
107     DumpFileUtil::WriteDumpFile(dumpFileOutput_, static_cast<void *>(outBuffer), frameLen * sizeof(float));
108     return SUCCESS;
109 }
110 
ProcessAlgo(int algoFrameLen,float * inBuffer,float * outBuffer)111 void AudioLimiter::ProcessAlgo(int algoFrameLen, float *inBuffer, float *outBuffer)
112 {
113     // calculate envelope energy
114     CHECK_AND_RETURN_LOG(algoFrameLen > 0, "algoFrameLen is invalid");
115     float maxEnvelopeLevel = 0.0f;
116     for (int32_t i = 0; i < algoFrameLen - 1; i += AUDIO_LMT_ALGO_CHANNEL) {
117         float tempBufInLeft = inBuffer[i];
118         float tempBufInRight = inBuffer[i + 1];
119         float tempLevel = std::max(std::abs(tempBufInLeft), std::abs(tempBufInRight));
120         float coeff = tempLevel > nextLev_ ? levelAttack_ : levelRelease_;
121         nextLev_ = coeff * nextLev_ + (1 - coeff) * tempLevel;
122         maxEnvelopeLevel = std::max(maxEnvelopeLevel, nextLev_);
123     }
124 
125     // calculate gain
126     float tempMaxLevel = std::max(maxEnvelopeLevel, curMaxLev_);
127     curMaxLev_ = maxEnvelopeLevel;
128     float targetGain = 1.0f;
129     if (tempMaxLevel != 0) {
130         targetGain = tempMaxLevel > threshold_ ? threshold_ / tempMaxLevel : targetGain;
131     }
132     float lastGain = gain_;
133     float coeff = gain_ > targetGain ? gainAttack_ : gainRelease_;
134     gain_ = coeff * gain_ + (1 - coeff) * targetGain;
135     float deltaGain = (gain_ - lastGain) * AUDIO_LMT_ALGO_CHANNEL / algoFrameLen;
136 
137     // apply gain
138     if (algoFrameLen % AUDIO_LMT_ALGO_CHANNEL == 0) {
139         for (int32_t i = 0; i < algoFrameLen; i += AUDIO_LMT_ALGO_CHANNEL) {
140             lastGain += deltaGain;
141             outBuffer[i] = bufHis_[i] * lastGain;
142             outBuffer[i + 1] = bufHis_[i + 1] * lastGain;
143             bufHis_[i] = inBuffer[i];
144             bufHis_[i + 1] = inBuffer[i + 1];
145         }
146     } else {
147         outBuffer[0] = bufHis_[0];
148         bufHis_[0] = bufHis_[algoFrameLen];
149         for (int32_t i = 1; i < algoFrameLen - 1; i += AUDIO_LMT_ALGO_CHANNEL) {
150             lastGain += deltaGain;
151             outBuffer[i] = bufHis_[i] * lastGain;
152             outBuffer[i + 1] = bufHis_[i + 1] * lastGain;
153             bufHis_[i] = inBuffer[i - 1];
154             bufHis_[i + 1] = inBuffer[i];
155         }
156         bufHis_[algoFrameLen] = inBuffer[algoFrameLen - 1];
157     }
158 }
159 
GetLatency()160 uint32_t AudioLimiter::GetLatency()
161 {
162     return latency_;
163 }
164 } // namespace AudioStandard
165 } // namespace OHOS
166