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"
22 static const char* get_ducking_stream(sound_stream_type_e stream_type)
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";
34 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] Type is not handled. type(%d)", stream_type);
38 return "Non matched stream";
41 BackgroundVolume::BackgroundVolume(long long int duckingDuration):
42 mDuckingDuration(duckingDuration),
43 mMediaStream(nullptr),
44 mNotificationStream(nullptr),
45 mAlarmStream(nullptr),
48 mChangeVolumeTime(chrono::steady_clock::now()),
49 mPostponedRecoverTimer(nullptr),
50 mPostponedModifyTimer(nullptr)
52 int ret = createHandles();
53 if (TTSD_ERROR_NONE != ret) {
54 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Fail to create handles.");
58 BackgroundVolume::~BackgroundVolume()
60 auto diff = getMsecDurationAfterDucking();
61 if (diff < mDuckingDuration) {
62 usleep((mDuckingDuration - diff) * 1000);
65 if (false == deactivateDuckingAll()) {
66 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. One last trial");
67 deactivateDuckingAll();
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);
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);
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;
88 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for media stream is already destroyed");
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;
97 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for notification stream is already destroyed");
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;
106 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Ducking handle for alarm stream is already destroyed");
110 void BackgroundVolume::setVolumeRatio(double ratio)
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));
117 void BackgroundVolume::modifyVolumeOnMainThread(void* data)
119 if (nullptr == data) {
120 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
124 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
125 if (!backgroundVolume->mVolumeDucked.load()) {
126 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume is not changed yet.");
130 if (backgroundVolume->mPostponedModifyTimer != nullptr) {
131 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Reserved volume modification exist. (%p)",
132 backgroundVolume->mPostponedModifyTimer);
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);
141 if (backgroundVolume->deactivateDuckingAll()) {
142 backgroundVolume->activateDuckingAll(0, ratio);
143 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Volume modification success. ratio(%lf)", ratio);
146 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
151 if (diff <= backgroundVolume->mDuckingDuration) {
152 delay = static_cast<double>(backgroundVolume->mDuckingDuration - diff) / 1000.0;
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);
159 Eina_Bool BackgroundVolume::postponedModifyTimerCb(void* data)
161 if (nullptr == data) {
162 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
166 BackgroundVolume* backgroundVolume = static_cast<BackgroundVolume*>(data);
167 auto ratio = backgroundVolume->mVolumeRatio.load();
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);
174 backgroundVolume->activateDuckingAll(0, ratio);
176 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed volume modification success. ratio(%lf)", ratio);
177 backgroundVolume->mPostponedModifyTimer = nullptr;
181 double BackgroundVolume::getVolumeRatio()
183 return mVolumeRatio.load();
186 void BackgroundVolume::applyVolumeRatio()
188 mVolumeDucked = true;
189 ecore_main_loop_thread_safe_call_async(changeVolumeOnMainThread, static_cast<void*>(this));
192 void BackgroundVolume::changeVolumeOnMainThread(void* data)
194 if (nullptr == data) {
195 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
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);
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);
213 backgroundVolume->activateDuckingAll(backgroundVolume->mDuckingDuration, ratio);
216 void BackgroundVolume::activateDuckingAll(unsigned int duration, double ratio)
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.");
227 activateDucking(SOUND_STREAM_TYPE_MEDIA, duration, ratio);
228 activateDucking(SOUND_STREAM_TYPE_NOTIFICATION, duration, ratio);
229 activateDucking(SOUND_STREAM_TYPE_ALARM, duration, ratio);
231 mChangeVolumeTime = chrono::steady_clock::now();
234 bool BackgroundVolume::activateDucking(sound_stream_type_e type, unsigned int duration, double ratio)
236 bool isDucked = false;
237 sound_stream_ducking_h handle = getStreamDuckingHandle(type);
238 sound_manager_is_ducked(handle, &isDucked);
240 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s is already ducked", get_ducking_stream(type));
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));
249 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Activate ducking for %s", get_ducking_stream(type));
253 void BackgroundVolume::recoverVolumeRatio()
255 mVolumeDucked = false;
256 ecore_main_loop_thread_safe_call_async(recoverVolumeOnMainThread, static_cast<void*>(this));
259 void BackgroundVolume::recoverVolumeOnMainThread(void* data)
261 if (nullptr == data) {
262 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
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);
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);
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");
286 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] Unknown ducking deactivation failure. Try again.");
291 if (diff <= backgroundVolume->mDuckingDuration) {
292 delay = static_cast<double>(backgroundVolume->mDuckingDuration - diff) / 1000.0;
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);
299 long long int BackgroundVolume::getMsecDurationAfterDucking()
301 auto currentTime = chrono::steady_clock::now();
302 return chrono::duration_cast<chrono::milliseconds>(currentTime - mChangeVolumeTime).count();
305 bool BackgroundVolume::deactivateDuckingAll()
307 if (false == isDuckingHandleValid()) {
308 SLOG(LOG_WARN, tts_tag(), "[BackgroundVolume] There are some invalid handles. Skip ducking deactivation.");
313 result &= deactivateDucking(SOUND_STREAM_TYPE_MEDIA);
314 result &= deactivateDucking(SOUND_STREAM_TYPE_NOTIFICATION);
315 result &= deactivateDucking(SOUND_STREAM_TYPE_ALARM);
320 Eina_Bool BackgroundVolume::postponedRecoverTimerCb(void* data)
322 if (nullptr == data) {
323 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid data is passed");
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);
334 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Delayed volume recovery success");
335 backgroundVolume->mPostponedRecoverTimer = nullptr;
339 bool BackgroundVolume::deactivateDucking(sound_stream_type_e type)
341 bool is_ducked = false;
342 sound_stream_ducking_h handle = getStreamDuckingHandle(type);
343 sound_manager_is_ducked(handle, &is_ducked);
345 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] The %s volume is already recovered", get_ducking_stream(type));
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));
354 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Deactivate ducking for %s", get_ducking_stream(type));
358 static void ducking_state_changed_cb(sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data)
360 SLOG(LOG_DEBUG, tts_tag(), "[BackgroundVolume] is ducked : %d", is_ducked);
363 int BackgroundVolume::createHandles()
365 SLOG(LOG_INFO, tts_tag(), "[BackgroundVolume] Create ducking handles");
367 if (nullptr == mMediaStream) {
368 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_MEDIA, ducking_state_changed_cb, nullptr,
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;
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;
389 if (nullptr == mAlarmStream) {
390 int ret = sound_manager_create_stream_ducking(SOUND_STREAM_TYPE_ALARM, ducking_state_changed_cb, nullptr,
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;
400 return TTSD_ERROR_NONE;
403 bool BackgroundVolume::isDuckingHandleValid()
405 if (mMediaStream == nullptr || mNotificationStream == nullptr || mAlarmStream == nullptr) {
412 sound_stream_ducking_h BackgroundVolume::getStreamDuckingHandle(sound_stream_type_e type)
416 case SOUND_STREAM_TYPE_MEDIA:
418 case SOUND_STREAM_TYPE_NOTIFICATION:
419 return mNotificationStream;
420 case SOUND_STREAM_TYPE_ALARM:
424 SLOG(LOG_ERROR, tts_tag(), "[BackgroundVolume] Invalid stream type (%d)", type);