4 * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
29 #include <iniparser.h>
31 #include "tizen-audio-internal.h"
32 #include "tizen-audio-impl.h"
34 #define VOLUME_INI_DEFAULT_PATH SYSCONFDIR"/multimedia/mmfw_audio_volume.ini" /* SYSCONFDIR is defined at .spec */
35 #define VOLUME_INI_TEMP_PATH "/opt/system/mmfw_audio_volume.ini"
36 #define VOLUME_VALUE_MAX (1.0f)
37 #define GAIN_VALUE_MAX (1.0f)
39 static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_MAX] = {
40 "file/private/sound/volume/system", /* AUDIO_VOLUME_TYPE_SYSTEM */
41 "file/private/sound/volume/notification", /* AUDIO_VOLUME_TYPE_NOTIFICATION */
42 "file/private/sound/volume/alarm", /* AUDIO_VOLUME_TYPE_ALARM */
43 "file/private/sound/volume/ringtone", /* AUDIO_VOLUME_TYPE_RINGTONE */
44 "file/private/sound/volume/media", /* AUDIO_VOLUME_TYPE_MEDIA */
45 "file/private/sound/volume/call", /* AUDIO_VOLUME_TYPE_CALL */
46 "file/private/sound/volume/voip", /* AUDIO_VOLUME_TYPE_VOIP */
47 "file/private/sound/volume/voice", /* AUDIO_VOLUME_TYPE_VOICE */
48 "file/private/sound/volume/master", /* AUDIO_VOLUME_TYPE_MASTER */
51 static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx)
53 switch (vol_type_idx) {
54 case AUDIO_VOLUME_TYPE_SYSTEM: return "system";
55 case AUDIO_VOLUME_TYPE_NOTIFICATION: return "notification";
56 case AUDIO_VOLUME_TYPE_ALARM: return "alarm";
57 case AUDIO_VOLUME_TYPE_RINGTONE: return "ringtone";
58 case AUDIO_VOLUME_TYPE_MEDIA: return "media";
59 case AUDIO_VOLUME_TYPE_CALL: return "call";
60 case AUDIO_VOLUME_TYPE_VOIP: return "voip";
61 case AUDIO_VOLUME_TYPE_VOICE: return "voice";
62 case AUDIO_VOLUME_TYPE_MASTER: return "master";
63 default: return "invalid";
67 static uint32_t __get_volume_idx_by_string_type(const char *vol_type)
69 if (!strncmp(vol_type, "system", strlen(vol_type)) || !strncmp(vol_type, "0", strlen(vol_type)))
70 return AUDIO_VOLUME_TYPE_SYSTEM;
71 else if (!strncmp(vol_type, "notification", strlen(vol_type)) || !strncmp(vol_type, "1", strlen(vol_type)))
72 return AUDIO_VOLUME_TYPE_NOTIFICATION;
73 else if (!strncmp(vol_type, "alarm", strlen(vol_type)) || !strncmp(vol_type, "2", strlen(vol_type)))
74 return AUDIO_VOLUME_TYPE_ALARM;
75 else if (!strncmp(vol_type, "ringtone", strlen(vol_type)) || !strncmp(vol_type, "3", strlen(vol_type)))
76 return AUDIO_VOLUME_TYPE_RINGTONE;
77 else if (!strncmp(vol_type, "media", strlen(vol_type)) || !strncmp(vol_type, "4", strlen(vol_type)))
78 return AUDIO_VOLUME_TYPE_MEDIA;
79 else if (!strncmp(vol_type, "call", strlen(vol_type)) || !strncmp(vol_type, "5", strlen(vol_type)))
80 return AUDIO_VOLUME_TYPE_CALL;
81 else if (!strncmp(vol_type, "voip", strlen(vol_type)) || !strncmp(vol_type, "6", strlen(vol_type)))
82 return AUDIO_VOLUME_TYPE_VOIP;
83 else if (!strncmp(vol_type, "voice", strlen(vol_type)) || !strncmp(vol_type, "7", strlen(vol_type)))
84 return AUDIO_VOLUME_TYPE_VOICE;
85 else if (!strncmp(vol_type, "master", strlen(vol_type)) || !strncmp(vol_type, "8", strlen(vol_type)))
86 return AUDIO_VOLUME_TYPE_MASTER;
88 return AUDIO_VOLUME_TYPE_MEDIA;
91 static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx)
93 switch (gain_type_idx) {
94 case AUDIO_GAIN_TYPE_DEFAULT: return "default";
95 case AUDIO_GAIN_TYPE_DIALER: return "dialer";
96 case AUDIO_GAIN_TYPE_TOUCH: return "touch";
97 case AUDIO_GAIN_TYPE_AF: return "af";
98 case AUDIO_GAIN_TYPE_SHUTTER1: return "shutter1";
99 case AUDIO_GAIN_TYPE_SHUTTER2: return "shutter2";
100 case AUDIO_GAIN_TYPE_CAMCODING: return "camcording";
101 case AUDIO_GAIN_TYPE_MIDI: return "midi";
102 case AUDIO_GAIN_TYPE_BOOTING: return "booting";
103 case AUDIO_GAIN_TYPE_VIDEO: return "video";
104 case AUDIO_GAIN_TYPE_TTS: return "tts";
105 default: return "invalid";
109 static bool __is_only_mixer_ctl_volume_type(const char *vol_type)
111 return (!strncmp(vol_type, "master", strlen(vol_type)) || !strncmp(vol_type, "8", strlen(vol_type)));
114 static void __dump_tb(audio_hal_t *ah)
116 audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table;
117 uint32_t vol_type_idx, vol_level_idx, gain_type_idx;
118 const char *gain_type_str[] = {
119 "def", /* AUDIO_GAIN_TYPE_DEFAULT */
120 "dial", /* AUDIO_GAIN_TYPE_DIALER */
121 "touch", /* AUDIO_GAIN_TYPE_TOUCH */
122 "af", /* AUDIO_GAIN_TYPE_AF */
123 "shut1", /* AUDIO_GAIN_TYPE_SHUTTER1 */
124 "shut2", /* AUDIO_GAIN_TYPE_SHUTTER2 */
125 "cam", /* AUDIO_GAIN_TYPE_CAMCODING */
126 "midi", /* AUDIO_GAIN_TYPE_MIDI */
127 "boot", /* AUDIO_GAIN_TYPE_BOOTING */
128 "video", /* AUDIO_GAIN_TYPE_VIDEO */
129 "tts", /* AUDIO_GAIN_TYPE_TTS */
131 char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr;
133 /* Dump volume table */
134 AUDIO_LOG_INFO("<<<<< volume table >>>>>");
136 const char *table_str = "volumes";
138 AUDIO_LOG_INFO("<< %s >>", table_str);
140 for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) {
141 const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx);
143 if (__is_only_mixer_ctl_volume_type(vol_type_str))
145 dump_str_ptr = &dump_str[0];
146 memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
147 snprintf(dump_str_ptr, 8, "%6s:", vol_type_str);
148 dump_str_ptr += strlen(dump_str_ptr);
150 for (vol_level_idx = 0; vol_level_idx < ah->volume.volume_level_max[vol_type_idx]; vol_level_idx++) {
151 snprintf(dump_str_ptr, 6, "%01.2f ", volume_value_table->volume[vol_type_idx][vol_level_idx]);
152 dump_str_ptr += strlen(dump_str_ptr);
154 AUDIO_LOG_INFO("%s", dump_str);
157 volume_value_table = ah->volume.volume_value_table;
159 /* Dump gain table */
160 AUDIO_LOG_INFO("<<<<< gain table >>>>>");
162 dump_str_ptr = &dump_str[0];
163 memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
165 snprintf(dump_str_ptr, 11, "%10s", " ");
166 dump_str_ptr += strlen(dump_str_ptr);
168 for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
169 snprintf(dump_str_ptr, 7, "%5s ", gain_type_str[gain_type_idx]);
170 dump_str_ptr += strlen(dump_str_ptr);
172 AUDIO_LOG_INFO("%s", dump_str);
174 dump_str_ptr = &dump_str[0];
175 memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
177 snprintf(dump_str_ptr, 11, "%9s:", table_str);
178 dump_str_ptr += strlen(dump_str_ptr);
180 for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
181 snprintf(dump_str_ptr, 7, "%01.3f ", volume_value_table->gain[gain_type_idx]);
182 dump_str_ptr += strlen(dump_str_ptr);
184 AUDIO_LOG_INFO("%s", dump_str);
188 static audio_return_t __load_volume_value_table_from_ini(audio_hal_t *ah)
190 dictionary * dict = NULL;
191 uint32_t vol_type_idx, vol_level_idx, gain_type_idx;
192 audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table;
195 dict = iniparser_load(VOLUME_INI_TEMP_PATH);
197 AUDIO_LOG_DEBUG("Use default volume&gain ini file");
198 dict = iniparser_load(VOLUME_INI_DEFAULT_PATH);
200 AUDIO_LOG_WARN("Loading volume&gain table from ini file failed");
201 return AUDIO_ERR_UNDEFINED;
205 const char delimiter[] = ", ";
206 char *key, *list_str, *token, *ptr = NULL;
207 const char *table_str = "volumes";
209 /* Load volume table */
210 for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) {
211 const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx);
212 if (__is_only_mixer_ctl_volume_type(vol_type_str))
215 ah->volume.volume_level_max[vol_type_idx] = 0;
216 size = strlen(table_str) + strlen(vol_type_str) + 2;
219 snprintf(key, size, "%s:%s", table_str, vol_type_str);
220 list_str = iniparser_getstring(dict, key, NULL);
222 token = strtok_r(list_str, delimiter, &ptr);
224 /* convert dB volume to linear volume */
225 double vol_value = 0.0f;
226 if (strncmp(token, "0", strlen(token)))
227 vol_value = pow(10.0, (atof(token) - 100) / 20.0);
228 volume_value_table->volume[vol_type_idx][ah->volume.volume_level_max[vol_type_idx]++] = vol_value;
229 token = strtok_r(NULL, delimiter, &ptr);
232 ah->volume.volume_level_max[vol_type_idx] = 1;
233 for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) {
234 volume_value_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX;
241 /* Load gain table */
242 volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT] = GAIN_VALUE_MAX;
243 for (gain_type_idx = AUDIO_GAIN_TYPE_DEFAULT + 1; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
244 const char *gain_type_str = __get_gain_type_string_by_idx(gain_type_idx);
246 size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3;
249 snprintf(key, size, "%s:gain_%s", table_str, gain_type_str);
250 token = iniparser_getstring(dict, key, NULL);
252 volume_value_table->gain[gain_type_idx] = atof(token);
254 volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
258 volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
262 iniparser_freedict(dict);
269 audio_return_t _audio_volume_init(audio_hal_t *ah)
273 audio_return_t audio_ret = AUDIO_RET_OK;
274 int init_value[AUDIO_VOLUME_TYPE_MAX] = {9, 11, 7, 11, 7, 4, 4, 7, 20};
276 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
278 for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) {
279 ah->volume.volume_level[i] = init_value[i];
282 for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) {
283 /* Get volume value string from VCONF */
284 if (vconf_get_int(g_volume_vconf[i], &val) < 0) {
285 AUDIO_LOG_ERROR("vconf_get_int(%s) failed", g_volume_vconf[i]);
289 AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val);
290 ah->volume.volume_level[i] = val;
293 if (!(ah->volume.volume_value_table = malloc(AUDIO_VOLUME_DEVICE_MAX * sizeof(audio_volume_value_table_t)))) {
294 AUDIO_LOG_ERROR("volume_value_table malloc failed");
295 return AUDIO_ERR_RESOURCE;
298 audio_ret = __load_volume_value_table_from_ini(ah);
299 if (audio_ret != AUDIO_RET_OK) {
300 AUDIO_LOG_ERROR("gain table load error");
301 return AUDIO_ERR_UNDEFINED;
304 if (_mixer_control_set_value(ah, ALSA_CARD0, AMIXER_SPK_OUT_GAIN, AMIXER_SPK_OUT_GAIN_DEFAULT) != AUDIO_RET_OK)
305 AUDIO_LOG_ERROR("[Set Mixer] %s -> %d failed", AMIXER_SPK_OUT_GAIN, AMIXER_SPK_OUT_GAIN_DEFAULT);
306 if (_mixer_control_set_value(ah, ALSA_CARD0, AMIXER_SPK_OUT_MUTE, AMIXER_SPK_OUT_MUTE_DEFAULT) != AUDIO_RET_OK)
307 AUDIO_LOG_ERROR("[Set Mixer] %s -> %d failed", AMIXER_SPK_OUT_MUTE, AMIXER_SPK_OUT_MUTE_DEFAULT);
308 if (_mixer_control_set_value(ah, ALSA_CARD0, AMIXER_PCM_GAIN, AMIXER_PCM_GAIN_DEFAULT) != AUDIO_RET_OK)
309 AUDIO_LOG_ERROR("[Set Mixer] %s -> %d failed", AMIXER_PCM_GAIN, AMIXER_PCM_GAIN_DEFAULT);
310 if (_mixer_control_set_value(ah, ALSA_CARD1, AMIXER_AMP_MUTE, AMIXER_AMP_MUTE_DEFAULT) != AUDIO_RET_OK)
311 AUDIO_LOG_ERROR("[Set Mixer] %s -> %d failed", AMIXER_AMP_MUTE, AMIXER_AMP_MUTE_DEFAULT);
316 audio_return_t _audio_volume_deinit(audio_hal_t *ah)
318 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
320 if (ah->volume.volume_value_table) {
321 free(ah->volume.volume_value_table);
322 ah->volume.volume_value_table = NULL;
328 audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
330 audio_hal_t *ah = (audio_hal_t *)audio_handle;
332 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
333 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
334 AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER);
336 /* Get max volume level by device & type */
337 *level = ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)];
339 AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level);
344 audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
346 audio_hal_t *ah = (audio_hal_t *)audio_handle;
348 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
349 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
350 AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER);
352 *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)];
354 AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction);
359 audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value)
361 audio_hal_t *ah = (audio_hal_t *)audio_handle;
362 audio_volume_value_table_t *volume_value_table;
363 char dump_str[AUDIO_DUMP_STR_LEN] = {0,};
365 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
366 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
367 AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER);
368 AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER);
370 if (__is_only_mixer_ctl_volume_type(info->type)) {
371 *value = VOLUME_VALUE_MAX;
375 /* Get basic volume by device & type & level */
376 volume_value_table = ah->volume.volume_value_table;
377 if (ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] < level)
378 *value = VOLUME_VALUE_MAX;
380 *value = volume_value_table->volume[__get_volume_idx_by_string_type(info->type)][level];
381 *value *= volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT]; /* need to fix getting gain via audio_info_t */
384 AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]);
389 audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level)
391 audio_return_t audio_ret = AUDIO_RET_OK;
392 audio_hal_t *ah = (audio_hal_t *)audio_handle;
394 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
395 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
396 AUDIO_RETURN_VAL_IF_FAIL((ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] >= level), AUDIO_ERR_PARAMETER);
398 /* Update volume level */
399 ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)] = level;
400 AUDIO_LOG_INFO("set [%s] volume_level: %d, direction(%d)", info->type, level, info->direction);
402 /* set mixer related to H/W volume if needed */
404 if (__get_volume_idx_by_string_type(info->type) == AUDIO_VOLUME_TYPE_MASTER) {
405 if ((audio_ret = _mixer_control_set_value(ah, ALSA_CARD0, AMIXER_SPK_OUT_GAIN, level)) != AUDIO_RET_OK) {
406 AUDIO_LOG_ERROR("set master volume with mixer failed");
413 audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute)
415 audio_return_t audio_ret = AUDIO_RET_OK;
416 audio_hal_t *ah = (audio_hal_t *)audio_handle;
418 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
419 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
420 AUDIO_RETURN_VAL_IF_FAIL(mute, AUDIO_ERR_PARAMETER);
422 /* TODO. Not implemented for other than master type */
423 if (__get_volume_idx_by_string_type(info->type) == AUDIO_VOLUME_TYPE_MASTER) {
424 if ((audio_ret = _mixer_control_get_value(ah, ALSA_CARD1, AMIXER_AMP_MUTE, (int*) mute)) != AUDIO_RET_OK) {
425 AUDIO_LOG_ERROR("get master mute with mixer failed");
432 audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute)
434 audio_return_t audio_ret = AUDIO_RET_OK;
435 audio_hal_t *ah = (audio_hal_t *)audio_handle;
437 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
438 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
440 /* TODO. Not implemented for other than master type */
441 if (__get_volume_idx_by_string_type(info->type) == AUDIO_VOLUME_TYPE_MASTER) {
442 if ((audio_ret = _mixer_control_set_value(ah, ALSA_CARD1, AMIXER_AMP_MUTE, mute)) != AUDIO_RET_OK) {
443 AUDIO_LOG_ERROR("set master mute with mixer failed");