1 /*
2  * Copyright (c) 2023 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 #include "enroll_engine.h"
16 #include <fstream>
17 #include "securec.h"
18 #include "intell_voice_log.h"
19 #include "intell_voice_util.h"
20 #include "enroll_adapter_listener.h"
21 #include "time_util.h"
22 #include "scope_guard.h"
23 #include "adapter_callback_service.h"
24 #include "intell_voice_service_manager.h"
25 #include "update_engine_utils.h"
26 #include "engine_host_manager.h"
27 
28 #define LOG_TAG "EnrollEngine"
29 
30 using namespace OHOS::IntellVoiceTrigger;
31 using namespace OHOS::HDI::IntelligentVoice::Engine::V1_0;
32 using namespace OHOS::IntellVoiceUtils;
33 using namespace OHOS::AudioStandard;
34 
35 namespace OHOS {
36 namespace IntellVoiceEngine {
37 static constexpr uint32_t MIN_BUFFER_SIZE = 1280; // 16 * 2 * 40ms
38 static constexpr uint32_t INTERVAL = 125; // 125 * 40ms = 5s
EnrollEngine()39 EnrollEngine::EnrollEngine()
40 {
41     INTELL_VOICE_LOG_INFO("enter");
42 
43     capturerOptions_.streamInfo.samplingRate = AudioSamplingRate::SAMPLE_RATE_16000;
44     capturerOptions_.streamInfo.encoding = AudioEncodingType::ENCODING_PCM;
45     capturerOptions_.streamInfo.format = AudioSampleFormat::SAMPLE_S16LE;
46     capturerOptions_.streamInfo.channels = AudioChannel::MONO;
47     capturerOptions_.capturerInfo.sourceType = SourceType::SOURCE_TYPE_VOICE_RECOGNITION;
48     capturerOptions_.capturerInfo.capturerFlags = 0;
49 }
50 
~EnrollEngine()51 EnrollEngine::~EnrollEngine()
52 {
53     INTELL_VOICE_LOG_INFO("enter");
54     callback_ = nullptr;
55 }
56 
OnEnrollEvent(int32_t msgId,int32_t result)57 void EnrollEngine::OnEnrollEvent(int32_t msgId, int32_t result)
58 {
59     if (msgId == INTELL_VOICE_ENGINE_MSG_ENROLL_COMPLETE) {
60         std::thread(&EnrollEngine::OnEnrollComplete, this).detach();
61     } else if (msgId == INTELL_VOICE_ENGINE_MSG_COMMIT_ENROLL_COMPLETE) {
62         std::lock_guard<std::mutex> lock(mutex_);
63         enrollResult_ = result;
64         IntellVoiceServiceManager::SetEnrollResult(INTELL_VOICE_ENROLL, result == 0 ? true : false);
65     }
66 }
67 
OnEnrollComplete()68 void EnrollEngine::OnEnrollComplete()
69 {
70     std::lock_guard<std::mutex> lock(mutex_);
71     StopAudioSource();
72 }
73 
Init(const std::string & param)74 bool EnrollEngine::Init(const std::string &param)
75 {
76     if (!EngineUtil::CreateAdapterInner(EngineHostManager::GetInstance(), ENROLL_ADAPTER_TYPE)) {
77         INTELL_VOICE_LOG_ERROR("failed to create adapter");
78         return false;
79     }
80     return true;
81 }
82 
SetCallback(sptr<IRemoteObject> object)83 void EnrollEngine::SetCallback(sptr<IRemoteObject> object)
84 {
85     std::lock_guard<std::mutex> lock(mutex_);
86     INTELL_VOICE_LOG_INFO("enter");
87     if (adapter_ == nullptr) {
88         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
89         return;
90     }
91 
92     sptr<IIntelligentVoiceEngineCallback> callback = iface_cast<IIntelligentVoiceEngineCallback>(object);
93     if (callback == nullptr) {
94         INTELL_VOICE_LOG_ERROR("callback is nullptr");
95         return;
96     }
97 
98     std::shared_ptr<IntellVoiceAdapterListener> listener = std::make_shared<EnrollAdapterListener>(callback,
99         std::bind(&EnrollEngine::OnEnrollEvent, this, std::placeholders::_1, std::placeholders::_2));
100     if (listener == nullptr) {
101         INTELL_VOICE_LOG_ERROR("listener is nullptr");
102         return;
103     }
104 
105     callback_ = sptr<IIntellVoiceEngineCallback>(new (std::nothrow) AdapterCallbackService(listener));
106     if (callback_ == nullptr) {
107         INTELL_VOICE_LOG_ERROR("callback_ is nullptr");
108         return;
109     }
110 
111     adapter_->SetCallback(callback_);
112 }
113 
Attach(const IntellVoiceEngineInfo & info)114 int32_t EnrollEngine::Attach(const IntellVoiceEngineInfo &info)
115 {
116     std::lock_guard<std::mutex> lock(mutex_);
117     INTELL_VOICE_LOG_INFO("attach");
118     if (adapter_ == nullptr) {
119         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
120         return -1;
121     }
122 
123     SetDspFeatures();
124     isPcmFromExternal_ = info.isPcmFromExternal;
125     HistoryInfoMgr::GetInstance().SetWakeupPhrase(info.wakeupPhrase);
126 
127     IntellVoiceEngineAdapterInfo adapterInfo = {
128         .wakeupPhrase = info.wakeupPhrase,
129         .minBufSize = info.minBufSize,
130         .sampleChannels = info.sampleChannels,
131         .bitsPerSample = info.bitsPerSample,
132         .sampleRate = info.sampleRate,
133     };
134     return adapter_->Attach(adapterInfo);
135 }
136 
Detach(void)137 int32_t EnrollEngine::Detach(void)
138 {
139     INTELL_VOICE_LOG_INFO("enter");
140     std::lock_guard<std::mutex> lock(mutex_);
141     StopAudioSource();
142     if (adapter_ == nullptr) {
143         INTELL_VOICE_LOG_WARN("already detach");
144         return 0;
145     }
146 
147     if (enrollResult_ == 0) {
148         ProcDspModel(OHOS::HDI::IntelligentVoice::Engine::V1_0::DSP_MODLE);
149         /* save new version number */
150         UpdateEngineUtils::SaveWakeupVesion();
151         INTELL_VOICE_LOG_INFO("enroll save version");
152     }
153 
154     int32_t ret = adapter_->Detach();
155     ReleaseAdapterInner(EngineHostManager::GetInstance());
156     return ret;
157 }
158 
Start(bool isLast)159 int32_t EnrollEngine::Start(bool isLast)
160 {
161     std::lock_guard<std::mutex> lock(mutex_);
162     INTELL_VOICE_LOG_INFO("enter");
163     if (audioSource_ != nullptr) {
164         INTELL_VOICE_LOG_ERROR("audioSource_ existed, wait for last start to finish");
165         return -1;
166     }
167 
168     if (adapter_ == nullptr) {
169         INTELL_VOICE_LOG_ERROR("adapter is nullptr");
170         return -1;
171     }
172 
173     StartInfo info = {
174         .isLast = isLast,
175     };
176     if (adapter_->Start(info)) {
177         INTELL_VOICE_LOG_ERROR("start adapter failed");
178         return -1;
179     }
180 
181     if (isPcmFromExternal_) {
182         INTELL_VOICE_LOG_INFO("pcm is from external");
183         return 0;
184     }
185 
186     if (!StartAudioSource()) {
187         INTELL_VOICE_LOG_ERROR("start audio source failed");
188         adapter_->Stop();
189         return -1;
190     }
191 
192     INTELL_VOICE_LOG_INFO("exit");
193     return 0;
194 }
195 
Stop()196 int32_t EnrollEngine::Stop()
197 {
198     std::lock_guard<std::mutex> lock(mutex_);
199     StopAudioSource();
200 
201     return EngineUtil::Stop();
202 }
203 
SetParameter(const std::string & keyValueList)204 int32_t EnrollEngine::SetParameter(const std::string &keyValueList)
205 {
206     std::lock_guard<std::mutex> lock(mutex_);
207     if (SetParameterInner(keyValueList)) {
208         INTELL_VOICE_LOG_INFO("inner parameter");
209         return 0;
210     }
211 
212     INTELL_VOICE_LOG_INFO("EnrollEngine SetParameter:%{public}s", keyValueList.c_str());
213 
214     return EngineUtil::SetParameter(keyValueList);
215 }
216 
SetParameterInner(const std::string & keyValueList)217 bool EnrollEngine::SetParameterInner(const std::string &keyValueList)
218 {
219     HistoryInfoMgr &historyInfoMgr = HistoryInfoMgr::GetInstance();
220 
221     std::map<std::string, std::string> kvpairs;
222     IntellVoiceUtil::SplitStringToKVPair(keyValueList, kvpairs);
223     for (auto it : kvpairs) {
224         if (it.first == std::string("wakeup_bundle_name")) {
225             INTELL_VOICE_LOG_INFO("set wakeup bundle name:%{public}s", it.second.c_str());
226             historyInfoMgr.SetWakeupEngineBundleName(it.second);
227             return true;
228         }
229         if (it.first == std::string("wakeup_ability_name")) {
230             INTELL_VOICE_LOG_INFO("set wakeup ability name:%{public}s", it.second.c_str());
231             historyInfoMgr.SetWakeupEngineAbilityName(it.second);
232             return true;
233         }
234         if (it.first == std::string("language")) {
235             INTELL_VOICE_LOG_INFO("set language:%{public}s", it.second.c_str());
236             historyInfoMgr.SetLanguage(it.second);
237             continue;
238         }
239         if (it.first == std::string("area")) {
240             INTELL_VOICE_LOG_INFO("set area:%{public}s", it.second.c_str());
241             historyInfoMgr.SetArea(it.second);
242             continue;
243         }
244         if (it.first == std::string("Sensibility")) {
245             INTELL_VOICE_LOG_INFO("set Sensibility:%{public}s", it.second.c_str());
246             historyInfoMgr.SetSensibility(it.second);
247             continue;
248         }
249     }
250 
251     return false;
252 }
253 
GetParameter(const std::string & key)254 std::string EnrollEngine::GetParameter(const std::string &key)
255 {
256     std::lock_guard<std::mutex> lock(mutex_);
257     return EngineUtil::GetParameter(key);
258 }
259 
WriteAudio(const uint8_t * buffer,uint32_t size)260 int32_t EnrollEngine::WriteAudio(const uint8_t *buffer, uint32_t size)
261 {
262     std::lock_guard<std::mutex> lock(mutex_);
263     return EngineUtil::WriteAudio(buffer, size);
264 }
265 
Evaluate(const std::string & word,EvaluationResultInfo & info)266 int32_t EnrollEngine::Evaluate(const std::string &word, EvaluationResultInfo &info)
267 {
268     std::lock_guard<std::mutex> lock(mutex_);
269     return EngineUtil::Evaluate(word, info);
270 }
271 
StartAudioSource()272 bool EnrollEngine::StartAudioSource()
273 {
274     auto listener = std::make_unique<AudioSourceListener>([&] (uint8_t *buffer, uint32_t size, bool isEnd) {
275         if ((adapter_ != nullptr) && (!isEnd)) {
276             std::vector<uint8_t> audioBuff(&buffer[0], &buffer[size]);
277             adapter_->WriteAudio(audioBuff);
278         }}, [&] () {
279             INTELL_VOICE_LOG_INFO("end of pcm");
280             if (adapter_ != nullptr) {
281                 adapter_->SetParameter("end_of_pcm=true");
282             }
283         });
284     if (listener == nullptr) {
285         INTELL_VOICE_LOG_ERROR("create listener failed");
286         return false;
287     }
288 
289     audioSource_ = std::make_unique<AudioSource>(MIN_BUFFER_SIZE, INTERVAL, std::move(listener),
290         capturerOptions_);
291     if (audioSource_ == nullptr) {
292         INTELL_VOICE_LOG_ERROR("create audio source failed");
293         return false;
294     }
295 
296     if (!audioSource_->Start()) {
297         INTELL_VOICE_LOG_ERROR("start capturer failed");
298         audioSource_ = nullptr;
299         return false;
300     }
301 
302     return true;
303 }
304 
StopAudioSource()305 void EnrollEngine::StopAudioSource()
306 {
307     INTELL_VOICE_LOG_INFO("enter");
308     if (audioSource_ != nullptr) {
309         INTELL_VOICE_LOG_INFO("stop audio sopurce");
310         audioSource_->Stop();
311         audioSource_ = nullptr;
312     }
313 }
314 }
315 }
316