Fix ducking deactivation retrial logic
[platform/core/uifw/tts.git] / server / BackgroundVolume.cpp
1 /*
2 *  Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
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 *  http://www.apache.org/licenses/LICENSE-2.0
7 *  Unless required by applicable law or agreed to in writing, software
8 *  distributed under the License is distributed on an "AS IS" BASIS,
9 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 *  See the License for the specific language governing permissions and
11 *  limitations under the License.
12 */
13
14 #include "ttsd_main.h"
15
16 #include "BackgroundVolume.h"
17
18
19 using namespace std;
20
21
22 static const char* get_ducking_stream(sound_stream_type_e stream_type)
23 {
24         switch (stream_type)
25         {
26         case SOUND_STREAM_TYPE_MEDIA:
27                 return "Media stream";
28         case SOUND_STREAM_TYPE_NOTIFICATION:
29                 return "Notification stream";
30         case SOUND_STREAM_TYPE_ALARM:
31                 return "Alarm stream";
32
33         default:
34                 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] Type is not handled. type(%d)", stream_type);
35                 break;
36         }
37
38         return "Non matched stream";
39 }
40
41 BackgroundVolume::BackgroundVolume(long long int duckingDuration):
42         mDuckingDuration(duckingDuration),
43         mMediaStream(nullptr),
44         mNotificationStream(nullptr),
45         mAlarmStream(nullptr),
46         mVolumeRatio(0.0),
47         mVolumeDucked(false),
48         mChangeVolumeTime(chrono::steady_clock::now()),
49         mPostponedRecoverTimer(nullptr),
50         mPostponedModifyTimer(nullptr)
51 {
52         int ret = createHandles();
53         if (TTSD_ERROR_NONE != ret) {
54                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to create handles.");
55         }
56 }
57
58 BackgroundVolume::~BackgroundVolume()
59 {
60         auto diff = getMsecDurationAfterDucking();
61         if (diff < mDuckingDuration) {
62                 usleep((mDuckingDuration - diff) * 1000);
63         }
64
65         if (false == deactivateDuckingAll()) {
66                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. One last trial");
67                 deactivateDuckingAll();
68         }
69
70         if (nullptr != mPostponedModifyTimer) {
71                 void* result = ecore_timer_del(mPostponedModifyTimer);
72                 mPostponedModifyTimer = nullptr;
73                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove modification timer. result(%p)", result);
74         }
75
76         if (nullptr != mPostponedRecoverTimer) {
77                 void* result = ecore_timer_del(mPostponedRecoverTimer);
78                 mPostponedRecoverTimer = nullptr;
79                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove recover timer. result(%p)", result);
80         }
81
82         if (mMediaStream) {
83                 int ret = sound_manager_destroy_stream_ducking(mMediaStream);
84                 if (SOUND_MANAGER_ERROR_NONE != ret)
85                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy media stream ducking, ret(%d)", ret);
86                 mMediaStream = nullptr;
87         } else {
88                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for media stream is already destroyed");
89         }
90
91         if (mNotificationStream) {
92                 int ret = sound_manager_destroy_stream_ducking(mNotificationStream);
93                 if (SOUND_MANAGER_ERROR_NONE != ret)
94                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy notification stream ducking, ret(%d)", ret);
95                 mNotificationStream = nullptr;
96         } else {
97                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for notification stream is already destroyed");
98         }
99
100         if (mAlarmStream) {
101                 int ret = sound_manager_destroy_stream_ducking(mAlarmStream);
102                 if (SOUND_MANAGER_ERROR_NONE != ret)
103                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy alarm stream ducking, ret(%d)", ret);
104                 mAlarmStream = nullptr;
105         } else {
106                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for alarm stream is already destroyed");
107         }
108 }
109
110 void BackgroundVolume::setVolumeRatio(double ratio)
111 {
112         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume is changed from (%lf) to (%lf).", mVolumeRatio.load(), ratio);
113         mVolumeRatio = ratio;
114         ecore_main_loop_thread_safe_call_async(modifyVolumeOnMainThread, static_cast<void*>(this));
115 }
116
117 void BackgroundVolume::modifyVolumeOnMainThread(void* data)
118 {
119         if (nullptr == data) {
120                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
121                 return;
122         }
123
124         BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
125         if (!backgroundVolume->mVolumeDucked.load()) {
126                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume is not changed yet.");
127                 return;
128         }
129
130         if (backgroundVolume->mPostponedModifyTimer != nullptr) {
131                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Reserved volume modification exist. (%p)",
132                          backgroundVolume->mPostponedModifyTimer);
133                 return;
134         }
135
136         auto diff = backgroundVolume->getMsecDurationAfterDucking();
137         if (diff > backgroundVolume->mDuckingDuration) {
138                 double ratio = backgroundVolume->mVolumeRatio.load();
139                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Modify volume ratio(%lf) directly", ratio);
140
141                 if (backgroundVolume->deactivateDuckingAll()) {
142                         backgroundVolume->activateDuckingAll(0, ratio);
143                         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume modification success. ratio(%lf)", ratio);
144                         return;
145                 } else {
146                         SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
147                 }
148         }
149
150         auto delay = 0.0;
151         if (diff <= backgroundVolume->mDuckingDuration) {
152                 delay = static_cast<double>(backgroundVolume->mDuckingDuration - diff) / 1000.0;
153         }
154         backgroundVolume->mPostponedModifyTimer = ecore_timer_add(delay, postponedModifyTimerCb, data);
155         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delay volume modification (%p), delay(%lf)",
156                  backgroundVolume->mPostponedModifyTimer, delay);
157 }
158
159 Eina_Bool BackgroundVolume::postponedModifyTimerCb(void* data)
160 {
161         if (nullptr == data) {
162                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
163                 return EINA_FALSE;
164         }
165
166         BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
167         auto ratio = backgroundVolume->mVolumeRatio.load();
168
169         if (false == backgroundVolume->deactivateDuckingAll()) {
170                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
171                 ecore_timer_interval_set(backgroundVolume->mPostponedModifyTimer, 0.0);
172                 return EINA_TRUE;
173         }
174         backgroundVolume->activateDuckingAll(0, ratio);
175
176         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed volume modification success. ratio(%lf)", ratio);
177         backgroundVolume->mPostponedModifyTimer = nullptr;
178         return EINA_FALSE;
179 }
180
181 double BackgroundVolume::getVolumeRatio()
182 {
183         return mVolumeRatio.load();
184 }
185
186 void BackgroundVolume::applyVolumeRatio()
187 {
188         mVolumeDucked = true;
189         ecore_main_loop_thread_safe_call_async(changeVolumeOnMainThread, static_cast<void*>(this));
190 }
191
192 void BackgroundVolume::changeVolumeOnMainThread(void* data)
193 {
194         if (nullptr == data) {
195                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
196                 return;
197         }
198
199         BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
200         if (nullptr != backgroundVolume->mPostponedRecoverTimer) {
201                 void* result = ecore_timer_del(backgroundVolume->mPostponedRecoverTimer);
202                 backgroundVolume->mPostponedRecoverTimer = nullptr;
203                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove recover timer. result(%p)", result);
204         }
205
206         double ratio = backgroundVolume->mVolumeRatio.load();
207         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume ratio(%lf)", ratio);
208         if (0.0 > ratio || 1.0 < ratio) {
209                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid ratio(%lf)", ratio);
210                 return;
211         }
212
213         backgroundVolume->activateDuckingAll(backgroundVolume->mDuckingDuration, ratio);
214 }
215
216 void BackgroundVolume::activateDuckingAll(unsigned int duration, double ratio)
217 {
218         if (false == isDuckingHandleValid()) {
219                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] There are some invalid handles. Try to recreate the hnadles");
220                 int ret = createHandles();
221                 if (TTSD_ERROR_NONE != ret) {
222                         SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to create handles. Skip ducking activation.");
223                         return;
224                 }
225         }
226
227         activateDucking(SOUND_STREAM_TYPE_MEDIA, duration, ratio);
228         activateDucking(SOUND_STREAM_TYPE_NOTIFICATION, duration, ratio);
229         activateDucking(SOUND_STREAM_TYPE_ALARM, duration, ratio);
230
231         mChangeVolumeTime = chrono::steady_clock::now();
232 }
233
234 bool BackgroundVolume::activateDucking(sound_stream_type_e type, unsigned int duration, double ratio)
235 {
236         bool isDucked = false;
237         sound_stream_ducking_h handle = getStreamDuckingHandle(type);
238         sound_manager_is_ducked(handle, &isDucked);
239         if (isDucked) {
240                 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s is already ducked", get_ducking_stream(type));
241                 return false;
242         }
243
244         if (SOUND_MANAGER_ERROR_NONE != sound_manager_activate_ducking(handle, duration, ratio)) {
245                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to activate ducking for %s", get_ducking_stream(type));
246                 return false;
247         }
248
249         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Activate ducking for %s", get_ducking_stream(type));
250         return true;
251 }
252
253 void BackgroundVolume::recoverVolumeRatio()
254 {
255         mVolumeDucked = false;
256         ecore_main_loop_thread_safe_call_async(recoverVolumeOnMainThread, static_cast<void*>(this));
257 }
258
259 void BackgroundVolume::recoverVolumeOnMainThread(void* data)
260 {
261         if (nullptr == data) {
262                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
263                 return;
264         }
265
266         BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
267         if (nullptr != backgroundVolume->mPostponedRecoverTimer) {
268                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Reserved recover timer already exist. (%p)",
269                          backgroundVolume->mPostponedRecoverTimer);
270                 return;
271         }
272
273         if (nullptr != backgroundVolume->mPostponedModifyTimer) {
274                 auto result = ecore_timer_del(backgroundVolume->mPostponedModifyTimer);
275                 backgroundVolume->mPostponedModifyTimer = nullptr;
276                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove modification timer. result(%p)", result);
277         }
278
279         auto diff = backgroundVolume->getMsecDurationAfterDucking();
280         if (diff > backgroundVolume->mDuckingDuration) {
281                 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Direct ducking deactivate");
282                 if (backgroundVolume->deactivateDuckingAll()) {
283                         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume recovery success");
284                         return;
285                 } else {
286                         SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
287                 }
288         }
289
290         auto delay = 0.0;
291         if (diff <= backgroundVolume->mDuckingDuration) {
292                 delay = static_cast<double>(backgroundVolume->mDuckingDuration - diff) / 1000.0;
293         }
294         backgroundVolume->mPostponedRecoverTimer = ecore_timer_add(delay, postponedRecoverTimerCb, data);
295         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delay volume recovery (%p), delay(%lf)",
296                  backgroundVolume->mPostponedRecoverTimer, delay);
297 }
298
299 long long int BackgroundVolume::getMsecDurationAfterDucking()
300 {
301         auto currentTime = chrono::steady_clock::now();
302         return chrono::duration_cast<chrono::milliseconds>(currentTime - mChangeVolumeTime).count();
303 }
304
305 bool BackgroundVolume::deactivateDuckingAll()
306 {
307         if (false == isDuckingHandleValid()) {
308                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] There are some invalid handles. Skip ducking deactivation.");
309                 return true;
310         }
311
312         bool result = true;
313         result &= deactivateDucking(SOUND_STREAM_TYPE_MEDIA);
314         result &= deactivateDucking(SOUND_STREAM_TYPE_NOTIFICATION);
315         result &= deactivateDucking(SOUND_STREAM_TYPE_ALARM);
316
317         return result;
318 }
319
320 Eina_Bool BackgroundVolume::postponedRecoverTimerCb(void* data)
321 {
322         if (nullptr == data) {
323                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
324                 return EINA_FALSE;
325         }
326
327         BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
328         if (false == backgroundVolume->deactivateDuckingAll()) {
329                 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
330                 ecore_timer_interval_set(backgroundVolume->mPostponedRecoverTimer, 0.0);
331                 return EINA_TRUE;
332         }
333
334         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed volume recovery success");
335         backgroundVolume->mPostponedRecoverTimer = nullptr;
336         return EINA_FALSE;
337 }
338
339 bool BackgroundVolume::deactivateDucking(sound_stream_type_e type)
340 {
341         bool is_ducked = false;
342         sound_stream_ducking_h handle = getStreamDuckingHandle(type);
343         sound_manager_is_ducked(handle, &is_ducked);
344         if (!is_ducked) {
345                 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s volume is already recovered", get_ducking_stream(type));
346                 return true;
347         }
348
349         if (SOUND_MANAGER_ERROR_NONE != sound_manager_deactivate_ducking(handle)) {
350                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to deactivate ducking for %s", get_ducking_stream(type));
351                 return false;
352         }
353
354         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Deactivate ducking for %s", get_ducking_stream(type));
355         return true;
356 }
357
358 static void ducking_state_changed_cb(sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data)
359 {
360         SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] is ducked : %d", is_ducked);
361 }
362
363 int BackgroundVolume::createHandles()
364 {
365         SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Create ducking handles");
366
367         if (nullptr == mMediaStream) {
368                 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_MEDIA, ducking_state_changed_cb, nullptr,
369                                                                                                           &mMediaStream);
370                 if (SOUND_MANAGER_ERROR_NONE != ret) {
371                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for media, (%d/%s)",
372                                  ret, get_error_message(ret));
373                         mMediaStream = nullptr;
374                         return TTSD_ERROR_OPERATION_FAILED;
375                 }
376         }
377
378         if (nullptr == mNotificationStream) {
379                 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_NOTIFICATION, ducking_state_changed_cb,
380                                                                                                           nullptr, &mNotificationStream);
381                 if (SOUND_MANAGER_ERROR_NONE != ret) {
382                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for notification, (%d/%s)",
383                                  ret, get_error_message(ret));
384                         mNotificationStream = nullptr;
385                         return TTSD_ERROR_OPERATION_FAILED;
386                 }
387         }
388
389         if (nullptr == mAlarmStream) {
390                 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_ALARM, ducking_state_changed_cb, nullptr,
391                                                                                                           &mAlarmStream);
392                 if (SOUND_MANAGER_ERROR_NONE != ret) {
393                         SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for alarm, (%d/%s)",
394                                  ret, get_error_message(ret));
395                         mAlarmStream = nullptr;
396                         return TTSD_ERROR_OPERATION_FAILED;
397                 }
398         }
399
400         return TTSD_ERROR_NONE;
401 }
402
403 bool BackgroundVolume::isDuckingHandleValid()
404 {
405         if (mMediaStream == nullptr || mNotificationStream == nullptr || mAlarmStream == nullptr) {
406                 return false;
407         }
408
409         return true;
410 }
411
412 sound_stream_ducking_h BackgroundVolume::getStreamDuckingHandle(sound_stream_type_e type)
413 {
414         switch (type)
415         {
416         case SOUND_STREAM_TYPE_MEDIA:
417                 return mMediaStream;
418         case SOUND_STREAM_TYPE_NOTIFICATION:
419                 return mNotificationStream;
420         case SOUND_STREAM_TYPE_ALARM:
421                 return mAlarmStream;
422
423         default:
424                 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid stream type (%d)", type);
425                 break;
426         }
427
428         return nullptr;
429 }