2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "sound/sound_manager.h"
22 #include <tizen/tizen.h>
23 #include <vconf-keys.h>
26 #include "common/converter.h"
27 #include "common/logger.h"
28 #include "common/scope_exit.h"
29 #include "common/task-queue.h"
30 #include "common/tools.h"
32 // This constant was originally defined in vconf.h. However, in tizen 3, it
33 // appears, it is removed (or defined only in vconf-internals.h)
34 // It is not clear, if it is final solution, or not.
39 #include "sound/sound_instance.h"
44 using namespace common;
45 using namespace common::tools;
47 const std::map<std::string, sound_type_e> SoundManager::platform_enum_map_ = {
48 {"SYSTEM", SOUND_TYPE_SYSTEM}, {"NOTIFICATION", SOUND_TYPE_NOTIFICATION},
49 {"ALARM", SOUND_TYPE_ALARM}, {"MEDIA", SOUND_TYPE_MEDIA},
50 {"VOICE", SOUND_TYPE_VOICE}, {"RINGTONE", SOUND_TYPE_RINGTONE}};
52 PlatformResult SoundManager::StrToPlatformEnum(const std::string& key, sound_type_e* sound_type) {
54 if (platform_enum_map_.find(key) == platform_enum_map_.end()) {
55 std::string message = "Platform enum value not found for key " + key;
56 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message);
59 *sound_type = platform_enum_map_.at(key);
61 return PlatformResult(ErrorCode::NO_ERROR);
64 PlatformResult SoundManager::PlatformEnumToStr(const sound_type_e value, std::string* sound_type) {
66 for (auto& item : platform_enum_map_) {
67 if (item.second == value) {
68 *sound_type = item.first;
70 return PlatformResult(ErrorCode::NO_ERROR);
74 std::string message = "Platform enum value " + std::to_string(value) + " not found";
76 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message);
79 std::string SoundManager::SoundDeviceTypeToString(sound_device_type_e type) {
82 case SOUND_DEVICE_BUILTIN_SPEAKER:
84 case SOUND_DEVICE_BUILTIN_RECEIVER:
86 case SOUND_DEVICE_BUILTIN_MIC:
88 case SOUND_DEVICE_AUDIO_JACK:
90 case SOUND_DEVICE_BLUETOOTH:
92 case SOUND_DEVICE_HDMI:
94 case SOUND_DEVICE_MIRRORING:
96 case SOUND_DEVICE_USB_AUDIO:
99 LoggerE("Invalid sound_device_type_e: %d", type);
104 std::string SoundManager::SoundIOTypeToString(sound_device_io_direction_e type) {
107 case SOUND_DEVICE_IO_DIRECTION_IN:
109 case SOUND_DEVICE_IO_DIRECTION_OUT:
111 case SOUND_DEVICE_IO_DIRECTION_BOTH:
114 LoggerE("Invalid sound_device_io_direction_e: %d", type);
119 SoundManager::SoundManager(SoundInstance& instance)
120 : is_volume_change_listener_(false),
121 volume_change_listener_id_(0),
122 soundModeChangeListening(false),
123 is_sound_device_change_listener_(false),
124 sound_device_connection_listener_id_(0),
125 sound_device_state_listener_id_(0),
127 soundModeListener(nullptr) {
132 SoundManager::~SoundManager() {
135 UnsetSoundModeChangeListener();
137 if (is_sound_device_change_listener_) {
138 if (SOUND_MANAGER_ERROR_NONE !=
139 sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_)) {
140 LoggerE("Cannot unregister connection listener id == %d",
141 sound_device_connection_listener_id_);
144 if (SOUND_MANAGER_ERROR_NONE !=
145 sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_)) {
146 LoggerE("Cannot unregister state listener id == %d", sound_device_state_listener_id_);
150 if (is_volume_change_listener_) {
151 if (SOUND_MANAGER_ERROR_NONE !=
152 sound_manager_remove_volume_changed_cb(volume_change_listener_id_)) {
153 LoggerE("Cannot unregister volume listener id == %d", volume_change_listener_id_);
158 void SoundManager::FillMaxVolumeMap() {
163 for (auto& item : platform_enum_map_) {
166 ret = sound_manager_get_max_volume(item.second, &max);
167 if (ret != SOUND_MANAGER_ERROR_NONE) {
168 LoggerE("SoundManagerGetMaxVolumeFailed : %d", ret);
171 LoggerD("maxVolume: %d - %d", item.second, max);
173 max_volume_map_[item.second] = max;
177 PlatformResult SoundManager::GetMaxVolume(sound_type_e type, int* max_volume) {
179 auto it = max_volume_map_.find(type);
180 if (it == max_volume_map_.end()) {
181 std::string sound_type;
182 PlatformResult status = PlatformEnumToStr(type, &sound_type);
183 if (status.IsError()) return status;
185 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to find maxVolume",
186 ("Failed to find maxVolume of type: %s", sound_type.c_str()));
189 *max_volume = it->second;
191 return PlatformResult(ErrorCode::NO_ERROR);
194 double SoundManager::ConvertToSystemVolume(int max_volume, int volume) {
196 return round(static_cast<double>(volume) * 100 / max_volume) / 100;
199 void SoundManager::VolumeChangeCallback(sound_type_e type, unsigned int value) {
200 ScopeLogger("VolumeChangeCallback: type: %d, value: %d", type, value);
203 picojson::value response = picojson::value(picojson::object());
204 picojson::object& response_obj = response.get<picojson::object>();
206 response_obj.insert(std::make_pair("listenerId", picojson::value("VolumeChangeListener")));
208 std::string sound_type;
209 PlatformResult status = PlatformEnumToStr(type, &sound_type);
210 if (status.IsError()) return;
212 response_obj.insert(std::make_pair("type", picojson::value(sound_type)));
215 status = GetMaxVolume(type, &max_volume);
216 if (status.IsError()) return;
219 std::make_pair("volume", picojson::value(ConvertToSystemVolume(max_volume, value))));
221 Instance::PostMessage(&instance_, response.serialize().c_str());
224 PlatformResult SoundManager::GetSoundMode(std::string* sound_mode_type) {
226 int isEnableSound = 0;
227 int isEnableVibrate = 0;
229 *sound_mode_type = "MUTE";
231 int ret = vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &isEnableSound);
232 if (VCONF_OK != ret) {
233 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error: " + std::to_string(ret),
234 ("vconf_get_bool error: %d (%s)", ret, get_error_message(ret)));
237 ret = vconf_get_bool(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL, &isEnableVibrate);
238 if (VCONF_OK != ret) {
239 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error: " + std::to_string(ret),
240 ("vconf_get_bool error: %d (%s)", ret, get_error_message(ret)));
243 if (isEnableSound && isEnableVibrate) {
244 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Platform has wrong state.",
245 ("Wrong state (sound && vibration)"));
249 *sound_mode_type = "SOUND";
250 } else if (isEnableVibrate) {
251 *sound_mode_type = "VIBRATE";
254 return PlatformResult(ErrorCode::NO_ERROR);
257 PlatformResult SoundManager::SetVolume(const picojson::object& args) {
259 const std::string& type = FromJson<std::string>(args, "type");
260 double volume = FromJson<double>(args, "volume");
262 LoggerD("SoundType: %s", type.c_str());
263 LoggerD("volume: %f", volume);
265 if (volume > 1.0 || volume < 0.0) {
266 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR,
267 "Volume should be the value between 0 and 1.");
270 sound_type_e sound_type;
271 PlatformResult status = SoundManager::StrToPlatformEnum(type, &sound_type);
272 if (status.IsError()) return status;
274 auto it = max_volume_map_.find(sound_type);
275 if (it == max_volume_map_.end()) {
276 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to find maxVolume",
277 ("Failed to find maxVolume of type: %d", type.c_str()));
280 int max_volume = it->second;
281 int value = round(volume * max_volume);
282 LoggerD("volume: %lf, maxVolume: %d, value: %d", volume, max_volume, value);
284 int ret = sound_manager_set_volume(sound_type, value);
285 if (ret != SOUND_MANAGER_ERROR_NONE) {
286 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to set volume",
287 ("Failed to set volume: %d (%s)", ret, get_error_message(ret)));
290 return PlatformResult(ErrorCode::NO_ERROR);
293 PlatformResult SoundManager::GetVolume(const picojson::object& args, double* volume) {
295 const std::string& type = FromJson<std::string>(args, "type");
298 sound_type_e type_enum;
299 PlatformResult status = SoundManager::StrToPlatformEnum(type, &type_enum);
300 if (status.IsError()) return status;
303 status = GetMaxVolume(type_enum, &max_volume);
304 if (status.IsError()) return status;
306 int ret = sound_manager_get_volume(type_enum, &value);
307 if (ret != SOUND_MANAGER_ERROR_NONE) {
308 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to get volume",
309 ("Failed to get volume: %d (%s)", ret, get_error_message(ret)));
312 *volume = ConvertToSystemVolume(max_volume, value);
313 LoggerD("volume: %lf, maxVolume: %d, value: %d", *volume, max_volume, value);
315 return PlatformResult(ErrorCode::NO_ERROR);
318 void SoundManager::soundModeChangedCb(keynode_t*, void* user_data) {
321 if (nullptr == user_data) {
322 LoggerE("Invalid callback data!");
326 auto self = static_cast<SoundManager*>(user_data);
328 if (self->soundModeListener) {
329 std::string soundModeType;
330 PlatformResult status = self->GetSoundMode(&soundModeType);
333 self->soundModeListener->OnSoundModeChange(soundModeType);
336 LoggerE("No SoundModeListener attached");
340 PlatformResult SoundManager::SetSoundModeChangeListener(
341 SoundManagerSoundModeChangedListener* listener) {
343 soundModeListener = listener;
344 if (soundModeChangeListening) return PlatformResult(ErrorCode::NO_ERROR);
346 int status = vconf_notify_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
347 SoundManager::soundModeChangedCb, this);
348 if (VCONF_OK != status) {
349 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not set");
352 status = vconf_notify_key_changed(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL,
353 SoundManager::soundModeChangedCb, this);
354 if (VCONF_OK != status) {
355 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not set");
358 soundModeChangeListening = true;
359 return PlatformResult(ErrorCode::NO_ERROR);
362 PlatformResult SoundManager::UnsetSoundModeChangeListener() {
364 soundModeListener = nullptr;
365 if (!soundModeChangeListening) {
366 return PlatformResult(ErrorCode::NO_ERROR);
369 int status = vconf_ignore_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
370 SoundManager::soundModeChangedCb);
371 if (VCONF_OK != status) {
372 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not unset");
375 status = vconf_ignore_key_changed(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL,
376 SoundManager::soundModeChangedCb);
377 if (VCONF_OK != status) {
378 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not unset");
381 soundModeChangeListening = false;
382 return PlatformResult(ErrorCode::NO_ERROR);
385 PlatformResult SoundManager::SetVolumeChangeListener() {
387 if (!is_volume_change_listener_) {
388 int ret = sound_manager_add_volume_changed_cb(
389 [](sound_type_e type, unsigned int value, void* ud) {
391 "Entered into asynchronous function, sound_manager_add_volume_changed_cb's argument");
392 return static_cast<SoundManager*>(ud)->VolumeChangeCallback(type, value);
394 static_cast<void*>(this), &volume_change_listener_id_);
396 if (ret != SOUND_MANAGER_ERROR_NONE) {
397 return LogAndCreateResult(
398 ErrorCode::UNKNOWN_ERR, "Failed to set volume changed callback",
399 ("Failed to set volume changed callback: error: %d (%s)", ret, get_error_message(ret)));
402 is_volume_change_listener_ = true;
405 return PlatformResult(ErrorCode::NO_ERROR);
408 PlatformResult SoundManager::UnsetVolumeChangeListener() {
410 if (!is_volume_change_listener_) {
411 return PlatformResult(ErrorCode::NO_ERROR);
414 int ret = sound_manager_remove_volume_changed_cb(volume_change_listener_id_);
415 if (ret != SOUND_MANAGER_ERROR_NONE) {
416 return LogAndCreateResult(
417 ErrorCode::UNKNOWN_ERR, "Failed to unset volume changed callback",
418 ("sound_manager_unset_volume_changed_cb error: %d (%s)", ret, get_error_message(ret)));
421 is_volume_change_listener_ = false;
423 return PlatformResult(ErrorCode::NO_ERROR);
426 void SoundManager::GetDeviceList(sound_device_mask_e mask, picojson::object& out) {
429 sound_device_list_h device_list = nullptr;
430 sound_device_h device = nullptr;
432 picojson::value response = picojson::value(picojson::array());
433 picojson::array& response_array = response.get<picojson::array>();
437 sound_manager_free_device_list(device_list);
441 int ret = sound_manager_get_device_list(mask, &device_list);
442 if (SOUND_MANAGER_ERROR_NONE != ret && SOUND_MANAGER_ERROR_NO_DATA != ret) {
444 PlatformResult(ErrorCode::UNKNOWN_ERR, "Getting device list failed"), &out,
445 ("sound_manager_get_device_list error: %d (%s)", ret, get_error_message(ret)));
449 while (!(ret = sound_manager_get_next_device(device_list, &device))) {
450 picojson::value val = picojson::value(picojson::object());
451 picojson::object& obj = val.get<picojson::object>();
452 PlatformResult result = GetDeviceInfo(device, true, false, &obj);
454 if (result.IsError()) {
455 LogAndReportError(result, &out);
458 response_array.push_back(val);
461 ReportSuccess(response, out);
464 PlatformResult SoundManager::GetDeviceInfo(sound_device_h device, bool is_connected,
465 bool check_connection, picojson::object* obj) {
470 int ret = sound_manager_get_device_id(device, &id);
471 if (SOUND_MANAGER_ERROR_NONE != ret) {
472 return LogAndCreateResult(
473 ErrorCode::UNKNOWN_ERR, "Getting device id failed",
474 ("sound_manager_get_device_id error: %d (%s)", ret, get_error_message(ret)));
476 obj->insert(std::make_pair("id", picojson::value(static_cast<double>(id))));
479 char* name = nullptr;
480 ret = sound_manager_get_device_name(device, &name);
481 if (SOUND_MANAGER_ERROR_NONE != ret) {
482 return LogAndCreateResult(
483 ErrorCode::UNKNOWN_ERR, "Getting device name failed",
484 ("sound_manager_get_device_name error: %d (%s)", ret, get_error_message(ret)));
486 obj->insert(std::make_pair("name", picojson::value(name)));
489 sound_device_type_e type = SOUND_DEVICE_BUILTIN_SPEAKER;
490 ret = sound_manager_get_device_type(device, &type);
491 if (SOUND_MANAGER_ERROR_NONE != ret) {
492 return LogAndCreateResult(
493 ErrorCode::UNKNOWN_ERR, "Getting device type failed",
494 ("sound_manager_get_device_type error: %d (%s)", ret, get_error_message(ret)));
496 obj->insert(std::make_pair("device", picojson::value(SoundDeviceTypeToString(type))));
499 sound_device_io_direction_e direction = SOUND_DEVICE_IO_DIRECTION_IN;
500 ret = sound_manager_get_device_io_direction(device, &direction);
501 if (SOUND_MANAGER_ERROR_NONE != ret) {
502 return LogAndCreateResult(
503 ErrorCode::UNKNOWN_ERR, "Getting device direction failed",
504 ("sound_manager_get_device_io_direction error: %d (%s)", ret, get_error_message(ret)));
506 obj->insert(std::make_pair("direction", picojson::value(SoundIOTypeToString(direction))));
509 sound_device_state_e state = SOUND_DEVICE_STATE_DEACTIVATED;
510 ret = sound_manager_get_device_state(device, &state);
511 if (SOUND_MANAGER_ERROR_NONE != ret) {
512 return LogAndCreateResult(
513 ErrorCode::UNKNOWN_ERR, "Getting device state failed",
514 ("sound_manager_get_device_state error: %d (%s)", ret, get_error_message(ret)));
516 obj->insert(std::make_pair("isActivated", picojson::value(static_cast<bool>(state))));
519 if (check_connection) {
520 return IsDeviceConnected(type, direction, obj);
523 obj->insert(std::make_pair("isConnected", picojson::value(is_connected)));
524 return PlatformResult(ErrorCode::NO_ERROR);
527 PlatformResult SoundManager::IsDeviceConnected(sound_device_type_e type,
528 sound_device_io_direction_e direction,
529 picojson::object* obj) {
532 sound_device_mask_e mask = SOUND_DEVICE_ALL_MASK;
534 case SOUND_DEVICE_IO_DIRECTION_IN:
535 mask = SOUND_DEVICE_IO_DIRECTION_IN_MASK;
537 case SOUND_DEVICE_IO_DIRECTION_OUT:
538 mask = SOUND_DEVICE_IO_DIRECTION_OUT_MASK;
540 case SOUND_DEVICE_IO_DIRECTION_BOTH:
541 mask = SOUND_DEVICE_IO_DIRECTION_BOTH_MASK;
544 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Invalid IO type",
545 ("Invalid IOType (%d)", direction));
548 sound_device_list_h device_list = nullptr;
549 sound_device_h device = nullptr;
550 sound_device_type_e device_type = SOUND_DEVICE_BUILTIN_SPEAKER;
554 sound_manager_free_device_list(device_list);
558 int ret = sound_manager_get_device_list(mask, &device_list);
559 if (SOUND_MANAGER_ERROR_NONE != ret) {
560 return LogAndCreateResult(
561 ErrorCode::UNKNOWN_ERR, "Getting device list failed",
562 ("sound_manager_get_device_list error: %d (%s)", ret, get_error_message(ret)));
565 while (!(ret = sound_manager_get_next_device(device_list, &device))) {
566 ret = sound_manager_get_device_type(device, &device_type);
567 if (SOUND_MANAGER_ERROR_NONE != ret) {
568 return LogAndCreateResult(
569 ErrorCode::UNKNOWN_ERR, "Getting device type failed",
570 ("sound_manager_get_device_type error: %d (%s)", ret, get_error_message(ret)));
573 if (type == device_type) {
574 obj->insert(std::make_pair("isConnected", picojson::value(true)));
575 return PlatformResult(ErrorCode::NO_ERROR);
579 obj->insert(std::make_pair("isConnected", picojson::value(false)));
580 return PlatformResult(ErrorCode::NO_ERROR);
583 void SoundManager::DeviceChangeCB(sound_device_h device, bool is_connected, bool check_connection) {
586 picojson::value response = picojson::value(picojson::object());
587 picojson::object& response_obj = response.get<picojson::object>();
589 PlatformResult result = GetDeviceInfo(device, is_connected, check_connection, &response_obj);
591 if (result.IsSuccess()) {
593 std::make_pair("listenerId", picojson::value("SoundDeviceStateChangeCallback")));
595 auto call_response = [this, response]() -> void {
596 ScopeLogger("Entered into asynchronous function, call_response");
597 Instance::PostMessage(&instance_, response.serialize().c_str());
600 TaskQueue::GetInstance().Async(call_response);
604 void DeviceConnectionChangedCB(sound_device_h device, bool is_connected, void* user_data) {
607 SoundManager* h = static_cast<SoundManager*>(user_data);
608 h->DeviceChangeCB(device, is_connected, false);
611 void DeviceStateChangedCB(sound_device_h device, sound_device_state_e unused, void* user_data) {
614 SoundManager* h = static_cast<SoundManager*>(user_data);
615 h->DeviceChangeCB(device, false, true);
618 PlatformResult SoundManager::AddDeviceStateChangeListener() {
621 int ret = SOUND_MANAGER_ERROR_NONE;
622 sound_device_mask_e mask = SOUND_DEVICE_ALL_MASK;
624 if (!is_sound_device_change_listener_) {
625 ret = sound_manager_add_device_connection_changed_cb(mask, DeviceConnectionChangedCB, this,
626 &sound_device_connection_listener_id_);
627 if (SOUND_MANAGER_ERROR_NONE != ret) {
628 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Setting connection listener failed",
629 ("sound_manager_add_device_connection_changed_cb error: %d (%s)",
630 ret, get_error_message(ret)));
633 ret = sound_manager_add_device_state_changed_cb(mask, DeviceStateChangedCB, this,
634 &sound_device_state_listener_id_);
635 if (SOUND_MANAGER_ERROR_NONE != ret) {
636 // silently cleanup connection changed callback registered in previous step
637 sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_);
638 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Setting state listener failed",
639 ("sound_manager_add_device_state_changed_cb error: %d (%s)", ret,
640 get_error_message(ret)));
643 is_sound_device_change_listener_ = true;
646 return PlatformResult(ErrorCode::NO_ERROR);
649 PlatformResult SoundManager::RemoveDeviceStateChangeListener() {
652 if (is_sound_device_change_listener_) {
654 sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_);
655 if (SOUND_MANAGER_ERROR_NONE != ret) {
656 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removing connection listener failed",
657 ("sound_manager_remove_device_connection_changed_cb error: %d (%s)",
658 ret, get_error_message(ret)));
661 ret = sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_);
662 if (SOUND_MANAGER_ERROR_NONE != ret) {
663 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removing state listener failed",
664 ("sound_manager_remove_device_state_changed_cb error: %d (%s)", ret,
665 get_error_message(ret)));
668 is_sound_device_change_listener_ = false;
671 return PlatformResult(ErrorCode::NO_ERROR);
675 } // namespace extension