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.
14 #include "ttsd_main.h"
16 #include "BackgroundVolume.h"
21 static const char* __get_ducking_stream(sound_stream_type_e stream_type)
25 case SOUND_STREAM_TYPE_MEDIA:
26 return "Media stream";
27 case SOUND_STREAM_TYPE_NOTIFICATION:
28 return "Notification stream";
29 case SOUND_STREAM_TYPE_ALARM:
30 return "Alarm stream";
33 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] Type is not handled. type(%d)", stream_type);
37 return "Non matched stream";
40 static void __sound_stream_ducking_state_changed_cb(sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data)
42 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] is ducked : %d", is_ducked);
46 BackgroundVolume::BackgroundVolume(long long int duckingDuration):
47 __duckingDuration(duckingDuration),
48 __mediaStream(nullptr),
49 __notificationStream(nullptr),
50 __alarmStream(nullptr),
52 __isVolumeDucked(false),
53 __changeVolumeTime(chrono::steady_clock::now()),
54 __postponedRecoverTimer(nullptr),
55 __postponedModifyTimer(nullptr)
57 int ret = createHandles();
58 if (TTSD_ERROR_NONE != ret) {
59 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to create handles.");
63 BackgroundVolume::~BackgroundVolume()
65 long long int diff = getDurationAfterDucking();
66 if (diff < __duckingDuration) {
67 usleep(__duckingDuration * 1000);
70 deactivateDuckingAll();
72 if (nullptr != __postponedModifyTimer) {
73 void* result = ecore_timer_del(__postponedModifyTimer);
74 __postponedModifyTimer = nullptr;
75 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove modification timer. result(%p)", result);
78 if (nullptr != __postponedRecoverTimer) {
79 void* result = ecore_timer_del(__postponedRecoverTimer);
80 __postponedRecoverTimer = nullptr;
81 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove recover timer. result(%p)", result);
85 int ret = sound_manager_destroy_stream_ducking(__mediaStream);
86 if (SOUND_MANAGER_ERROR_NONE != ret)
87 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy media stream ducking, ret(%d)", ret);
88 __mediaStream = nullptr;
90 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for media stream is already destroyed");
93 if (__notificationStream) {
94 int ret = sound_manager_destroy_stream_ducking(__notificationStream);
95 if (SOUND_MANAGER_ERROR_NONE != ret)
96 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy notification stream ducking, ret(%d)", ret);
97 __notificationStream = nullptr;
99 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for notification stream is already destroyed");
103 int ret = sound_manager_destroy_stream_ducking(__alarmStream);
104 if (SOUND_MANAGER_ERROR_NONE != ret)
105 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to destroy alarm stream ducking, ret(%d)", ret);
106 __alarmStream = nullptr;
108 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for alarm stream is already destroyed");
112 void BackgroundVolume::setVolumeRatio(double ratio)
114 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume is changed from (%lf) to (%lf).", __volumeRatio.load(), ratio);
115 __volumeRatio = ratio;
116 ecore_main_loop_thread_safe_call_async(modifyVolumeOnMainThread, static_cast<void*>(this));
119 void BackgroundVolume::modifyVolumeOnMainThread(void* data)
121 if (nullptr == data) {
122 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
126 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
127 if (!backgroundVolume->__isVolumeDucked.load()) {
128 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume is not changed yet.");
132 if (backgroundVolume->__postponedModifyTimer != nullptr) {
133 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Reserved volume modification exist. (%p)", backgroundVolume->__postponedModifyTimer);
137 long long int diff = backgroundVolume->getDurationAfterDucking();
138 if (diff >= backgroundVolume->__duckingDuration) {
139 double ratio = backgroundVolume->__volumeRatio.load();
140 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Modify volume ratio(%lf) directly", ratio);
142 backgroundVolume->deactivateDuckingAll();
143 backgroundVolume->activateDuckingAll(0, ratio);
145 double delay = static_cast<double>(backgroundVolume->__duckingDuration - diff) / 1000.0;
146 backgroundVolume->__postponedModifyTimer = ecore_timer_add(delay, postponedModifyTimerCb, data);
147 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delay volume modification (%p), delay(%lf)", backgroundVolume->__postponedModifyTimer, delay);
151 Eina_Bool BackgroundVolume::postponedModifyTimerCb(void* data)
153 if (nullptr == data) {
154 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
158 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
159 double ratio = backgroundVolume->__volumeRatio.load();
160 backgroundVolume->deactivateDuckingAll();
161 backgroundVolume->activateDuckingAll(0, ratio);
163 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed volume modification success. ratio(%lf)", ratio);
164 backgroundVolume->__postponedModifyTimer = nullptr;
168 double BackgroundVolume::getVolumeRatio()
170 return __volumeRatio.load();
173 void BackgroundVolume::applyVolumeRatio()
175 __isVolumeDucked = true;
176 ecore_main_loop_thread_safe_call_async(changeVolumeOnMainThread, static_cast<void*>(this));
179 void BackgroundVolume::changeVolumeOnMainThread(void* data)
181 if (nullptr == data) {
182 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
186 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
187 if (nullptr != backgroundVolume->__postponedRecoverTimer) {
188 void* result = ecore_timer_del(backgroundVolume->__postponedRecoverTimer);
189 backgroundVolume->__postponedRecoverTimer = nullptr;
190 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove recover timer. result(%p)", result);
193 double ratio = backgroundVolume->__volumeRatio.load();
194 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume ratio(%lf)", ratio);
195 if (0.0 > ratio || 1.0 < ratio) {
196 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid ratio(%lf)", ratio);
200 backgroundVolume->activateDuckingAll(backgroundVolume->__duckingDuration, ratio);
203 void BackgroundVolume::activateDuckingAll(unsigned int duration, double ratio)
205 if (false == isDuckingHandleValid()) {
206 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] There are some invalid handles. Try to recreate the hnadles");
207 int ret = createHandles();
208 if (TTSD_ERROR_NONE != ret) {
209 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to create handles. Skip ducking activation.");
214 activateDucking(SOUND_STREAM_TYPE_MEDIA, duration, ratio);
215 activateDucking(SOUND_STREAM_TYPE_NOTIFICATION, duration, ratio);
216 activateDucking(SOUND_STREAM_TYPE_ALARM, duration, ratio);
218 __changeVolumeTime = chrono::steady_clock::now();
221 bool BackgroundVolume::activateDucking(sound_stream_type_e type, unsigned int duration, double ratio)
223 bool isDucked = false;
224 sound_stream_ducking_h handle = getStreamDuckingHandle(type);
225 sound_manager_is_ducked(handle, &isDucked);
227 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s is already ducked", __get_ducking_stream(type));
231 if (SOUND_MANAGER_ERROR_NONE != sound_manager_activate_ducking(handle, duration, ratio)) {
232 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to activate ducking for %s", __get_ducking_stream(type));
236 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Activate ducking for %s", __get_ducking_stream(type));
240 void BackgroundVolume::recoverVolumeRatio()
242 __isVolumeDucked = false;
243 ecore_main_loop_thread_safe_call_async(recoverVolumeOnMainThread, static_cast<void*>(this));
246 void BackgroundVolume::recoverVolumeOnMainThread(void* data)
248 if (nullptr == data) {
249 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
253 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
254 if (nullptr != backgroundVolume->__postponedRecoverTimer) {
255 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Reserved volume recover exist. (%p)", backgroundVolume->__postponedRecoverTimer);
259 if (nullptr != backgroundVolume->__postponedModifyTimer) {
260 void* result = ecore_timer_del(backgroundVolume->__postponedModifyTimer);
261 backgroundVolume->__postponedModifyTimer = nullptr;
262 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Remove modification timer. result(%p)", result);
265 long long int diff = backgroundVolume->getDurationAfterDucking();
266 if (diff >= backgroundVolume->__duckingDuration) {
267 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Direct deactivate ducking");
269 backgroundVolume->deactivateDuckingAll();
271 double delay = static_cast<double>(backgroundVolume->__duckingDuration - diff) / 1000.0;
272 backgroundVolume->__postponedRecoverTimer = ecore_timer_add(delay, postponedRecoverTimerCb, data);
273 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delay deactivate ducking (%p), delay(%f)", backgroundVolume->__postponedRecoverTimer, delay);
277 long long int BackgroundVolume::getDurationAfterDucking()
279 auto currentTime = chrono::steady_clock::now();
280 chrono::milliseconds diff = chrono::duration_cast<chrono::milliseconds>(currentTime - __changeVolumeTime);
285 void BackgroundVolume::deactivateDuckingAll()
287 if (false == isDuckingHandleValid()) {
288 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] There are some invalid handles. Skip ducking deactivation.");
292 deactivateDucking(SOUND_STREAM_TYPE_MEDIA);
293 deactivateDucking(SOUND_STREAM_TYPE_NOTIFICATION);
294 deactivateDucking(SOUND_STREAM_TYPE_ALARM);
297 Eina_Bool BackgroundVolume::postponedRecoverTimerCb(void* data)
299 if (nullptr == data) {
300 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
304 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
305 backgroundVolume->deactivateDuckingAll();
306 backgroundVolume->__postponedRecoverTimer = nullptr;
308 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed unset policy success");
312 void BackgroundVolume::deactivateDucking(sound_stream_type_e type)
314 bool isDucked = false;
315 sound_stream_ducking_h handle = getStreamDuckingHandle(type);
316 sound_manager_is_ducked(handle, &isDucked);
318 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s is already recovered from ducking", __get_ducking_stream(type));
322 if (SOUND_MANAGER_ERROR_NONE != sound_manager_deactivate_ducking(handle)) {
323 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to deactivate ducking for %s", __get_ducking_stream(type));
325 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Deactivate ducking for %s", __get_ducking_stream(type));
329 int BackgroundVolume::createHandles()
331 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Create ducking handles");
333 if (nullptr == __mediaStream) {
334 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_MEDIA, __sound_stream_ducking_state_changed_cb, nullptr, &__mediaStream);
335 if (SOUND_MANAGER_ERROR_NONE != ret) {
336 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for type media, ret(%d/%s)", ret, get_error_message(ret));
337 __mediaStream = nullptr;
338 return TTSD_ERROR_OPERATION_FAILED;
342 if (nullptr == __notificationStream) {
343 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_NOTIFICATION, __sound_stream_ducking_state_changed_cb, nullptr, &__notificationStream);
344 if (SOUND_MANAGER_ERROR_NONE != ret) {
345 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for notification type, ret(%d/%s)", ret, get_error_message(ret));
346 __notificationStream = nullptr;
347 return TTSD_ERROR_OPERATION_FAILED;
351 if (nullptr == __alarmStream) {
352 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_ALARM, __sound_stream_ducking_state_changed_cb, nullptr, &__alarmStream);
353 if (SOUND_MANAGER_ERROR_NONE != ret) {
354 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Fail to create stream ducking for alarm type, ret(%d/%s)", ret, get_error_message(ret));
355 __alarmStream = nullptr;
356 return TTSD_ERROR_OPERATION_FAILED;
360 return TTSD_ERROR_NONE;
363 bool BackgroundVolume::isDuckingHandleValid()
365 if (__mediaStream == nullptr || __notificationStream == nullptr || __alarmStream == nullptr) {
372 sound_stream_ducking_h BackgroundVolume::getStreamDuckingHandle(sound_stream_type_e type)
376 case SOUND_STREAM_TYPE_MEDIA:
377 return __mediaStream;
378 case SOUND_STREAM_TYPE_NOTIFICATION:
379 return __notificationStream;
380 case SOUND_STREAM_TYPE_ALARM:
381 return __alarmStream;
384 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid stream type (%d)", type);