diff options
author | Seungbae Shin <seungbae.shin@samsung.com> | 2022-01-27 17:02:51 +0900 |
---|---|---|
committer | Seungbae Shin <seungbae.shin@samsung.com> | 2022-01-27 17:49:22 +0900 |
commit | b0320539d30cbbcd2fd67f381f1381073ba1fe5e (patch) | |
tree | 9fb34c3eeabb4affd54ec01642c40bc108032834 /tizen-audio-volume.c | |
parent | d9cf41d3229bcd18259aa0fcad25489593487eb7 (diff) | |
download | audio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.tar.gz audio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.tar.bz2 audio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.zip |
Initial copy from audio-hal-emulsubmit/tizen/20220128.113815accepted/tizen/unified/20220206.211909
[Version] 0.0.1
[Issue Type] Init
Change-Id: I3aab132bfe06c0ccf29f7f91d69f366506153a79
Diffstat (limited to 'tizen-audio-volume.c')
-rw-r--r-- | tizen-audio-volume.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/tizen-audio-volume.c b/tizen-audio-volume.c new file mode 100644 index 0000000..e9b9177 --- /dev/null +++ b/tizen-audio-volume.c @@ -0,0 +1,452 @@ +/* + * audio-hal + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <vconf.h> +#include <iniparser.h> + +#include "tizen-audio-internal.h" +#include "tizen-audio-impl.h" + +#define VOLUME_INI_DEFAULT_PATH SYSCONFDIR"/multimedia/mmfw_audio_volume.ini" /* SYSCONFDIR is defined at .spec */ +#define VOLUME_INI_TEMP_PATH "/opt/system/mmfw_audio_volume.ini" +#define VOLUME_VALUE_MAX (1.0f) +#define GAIN_VALUE_MAX (1.0f) + +static uint32_t g_master_volume_level = 100; + +static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_MAX] = { + "file/private/sound/volume/system", /* AUDIO_VOLUME_TYPE_SYSTEM */ + "file/private/sound/volume/notification", /* AUDIO_VOLUME_TYPE_NOTIFICATION */ + "file/private/sound/volume/alarm", /* AUDIO_VOLUME_TYPE_ALARM */ + "file/private/sound/volume/ringtone", /* AUDIO_VOLUME_TYPE_RINGTONE */ + "file/private/sound/volume/media", /* AUDIO_VOLUME_TYPE_MEDIA */ + "file/private/sound/volume/call", /* AUDIO_VOLUME_TYPE_CALL */ + "file/private/sound/volume/voip", /* AUDIO_VOLUME_TYPE_VOIP */ + "file/private/sound/volume/voice", /* AUDIO_VOLUME_TYPE_VOICE */ +}; + +static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx) +{ + switch (vol_type_idx) { + case AUDIO_VOLUME_TYPE_SYSTEM: return "system"; + case AUDIO_VOLUME_TYPE_NOTIFICATION: return "notification"; + case AUDIO_VOLUME_TYPE_ALARM: return "alarm"; + case AUDIO_VOLUME_TYPE_RINGTONE: return "ringtone"; + case AUDIO_VOLUME_TYPE_MEDIA: return "media"; + case AUDIO_VOLUME_TYPE_CALL: return "call"; + case AUDIO_VOLUME_TYPE_VOIP: return "voip"; + case AUDIO_VOLUME_TYPE_VOICE: return "voice"; + default: return "invalid"; + } +} + +static uint32_t __get_volume_idx_by_string_type(const char *vol_type) +{ + if (!strncmp(vol_type, "system", strlen(vol_type)) || !strncmp(vol_type, "0", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_SYSTEM; + else if (!strncmp(vol_type, "notification", strlen(vol_type)) || !strncmp(vol_type, "1", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_NOTIFICATION; + else if (!strncmp(vol_type, "alarm", strlen(vol_type)) || !strncmp(vol_type, "2", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_ALARM; + else if (!strncmp(vol_type, "ringtone", strlen(vol_type)) || !strncmp(vol_type, "3", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_RINGTONE; + else if (!strncmp(vol_type, "media", strlen(vol_type)) || !strncmp(vol_type, "4", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_MEDIA; + else if (!strncmp(vol_type, "call", strlen(vol_type)) || !strncmp(vol_type, "5", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_CALL; + else if (!strncmp(vol_type, "voip", strlen(vol_type)) || !strncmp(vol_type, "6", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOIP; + else if (!strncmp(vol_type, "voice", strlen(vol_type)) || !strncmp(vol_type, "7", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOICE; + else + return AUDIO_VOLUME_TYPE_MEDIA; +} + +static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx) +{ + switch (gain_type_idx) { + case AUDIO_GAIN_TYPE_DEFAULT: return "default"; + case AUDIO_GAIN_TYPE_DIALER: return "dialer"; + case AUDIO_GAIN_TYPE_TOUCH: return "touch"; + case AUDIO_GAIN_TYPE_AF: return "af"; + case AUDIO_GAIN_TYPE_SHUTTER1: return "shutter1"; + case AUDIO_GAIN_TYPE_SHUTTER2: return "shutter2"; + case AUDIO_GAIN_TYPE_CAMCODING: return "camcording"; + case AUDIO_GAIN_TYPE_MIDI: return "midi"; + case AUDIO_GAIN_TYPE_BOOTING: return "booting"; + case AUDIO_GAIN_TYPE_VIDEO: return "video"; + case AUDIO_GAIN_TYPE_TTS: return "tts"; + default: return "invalid"; + } +} + +static void __dump_tb(audio_hal_s *ah) +{ + audio_volume_value_table_s *volume_value_table = ah->volume.volume_value_table; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + const char *gain_type_str[] = { + "def", /* AUDIO_GAIN_TYPE_DEFAULT */ + "dial", /* AUDIO_GAIN_TYPE_DIALER */ + "touch", /* AUDIO_GAIN_TYPE_TOUCH */ + "af", /* AUDIO_GAIN_TYPE_AF */ + "shut1", /* AUDIO_GAIN_TYPE_SHUTTER1 */ + "shut2", /* AUDIO_GAIN_TYPE_SHUTTER2 */ + "cam", /* AUDIO_GAIN_TYPE_CAMCODING */ + "midi", /* AUDIO_GAIN_TYPE_MIDI */ + "boot", /* AUDIO_GAIN_TYPE_BOOTING */ + "video", /* AUDIO_GAIN_TYPE_VIDEO */ + "tts", /* AUDIO_GAIN_TYPE_TTS */ + }; + char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr; + + /* Dump volume table */ + AUDIO_LOG_INFO("<<<<< volume table >>>>>"); + + const char *table_str = "volumes"; + + AUDIO_LOG_INFO("<< %s >>", table_str); + + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + snprintf(dump_str_ptr, 8, "%6s:", vol_type_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (vol_level_idx = 0; vol_level_idx < ah->volume.volume_level_max[vol_type_idx]; vol_level_idx++) { + snprintf(dump_str_ptr, 6, "%01.2f ", volume_value_table->volume[vol_type_idx][vol_level_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + } + + volume_value_table = ah->volume.volume_value_table; + + /* Dump gain table */ + AUDIO_LOG_INFO("<<<<< gain table >>>>>"); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%10s", " "); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%5s ", gain_type_str[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%9s:", table_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%01.3f ", volume_value_table->gain[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + +} + +static audio_return_e __load_volume_value_table_from_ini(audio_hal_s *ah) +{ + dictionary * dict = NULL; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + audio_volume_value_table_s *volume_value_table = ah->volume.volume_value_table; + int size = 0; + const char delimiter[] = ", "; + const char *table_str = "volumes"; + const char *tmp_str = NULL; + const char *gain_str = NULL; + char *list_str = NULL, *ptr = NULL; + char *key, *token; + + if (access(VOLUME_INI_TEMP_PATH, F_OK) == 0) + dict = iniparser_load(VOLUME_INI_TEMP_PATH); + if (!dict) { + AUDIO_LOG_DEBUG("Use default volume&gain ini file"); + dict = iniparser_load(VOLUME_INI_DEFAULT_PATH); + if (!dict) { + AUDIO_LOG_WARN("Loading volume&gain table from ini file failed"); + return AUDIO_ERR_UNDEFINED; + } + } + + /* Load volume table */ + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + ah->volume.volume_level_max[vol_type_idx] = 0; + size = strlen(table_str) + strlen(vol_type_str) + 2; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:%s", table_str, vol_type_str); + if ((tmp_str = iniparser_getstring(dict, key, NULL))) + list_str = strdup(tmp_str); + + if (list_str) { + token = strtok_r(list_str, delimiter, &ptr); + while (token) { + /* convert dB volume to linear volume */ + double vol_value = 0.0f; + if (strncmp(token, "0", strlen(token))) + vol_value = pow(10.0, (atof(token) - 100) / 20.0); + volume_value_table->volume[vol_type_idx][ah->volume.volume_level_max[vol_type_idx]++] = vol_value; + token = strtok_r(NULL, delimiter, &ptr); + } + free(list_str); + list_str = NULL; + } else { + ah->volume.volume_level_max[vol_type_idx] = 1; + for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) { + volume_value_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX; + } + } + free(key); + } + } + + /* Load gain table */ + volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT] = GAIN_VALUE_MAX; + for (gain_type_idx = AUDIO_GAIN_TYPE_DEFAULT + 1; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + const char *gain_type_str = __get_gain_type_string_by_idx(gain_type_idx); + + size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:gain_%s", table_str, gain_type_str); + gain_str = iniparser_getstring(dict, key, NULL); + if (gain_str) { + volume_value_table->gain[gain_type_idx] = atof(gain_str); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + free(key); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + } + + iniparser_freedict(dict); + + __dump_tb(ah); + + return AUDIO_RET_OK; +} + +audio_return_e _audio_volume_init(audio_hal_s *ah) +{ + int i; + int val = 0; + audio_return_e audio_ret = AUDIO_RET_OK; + int init_value[AUDIO_VOLUME_TYPE_MAX] = { 9, 11, 7, 11, 7, 4, 4, 7 }; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + ah->volume.volume_level[i] = init_value[i]; + } + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + /* Get volume value string from VCONF */ + if (vconf_get_int(g_volume_vconf[i], &val) < 0) { + AUDIO_LOG_ERROR("vconf_get_int(%s) failed", g_volume_vconf[i]); + continue; + } + + AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val); + ah->volume.volume_level[i] = val; + } + + if (!(ah->volume.volume_value_table = malloc(AUDIO_VOLUME_DEVICE_MAX * sizeof(audio_volume_value_table_s)))) { + AUDIO_LOG_ERROR("volume_value_table malloc failed"); + return AUDIO_ERR_RESOURCE; + } + + audio_ret = __load_volume_value_table_from_ini(ah); + if (audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("gain table load error"); + return AUDIO_ERR_UNDEFINED; + } + + return audio_ret; +} + +audio_return_e _audio_volume_deinit(audio_hal_s *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if (ah->volume.volume_value_table) { + free(ah->volume.volume_value_table); + ah->volume.volume_value_table = NULL; + } + + return AUDIO_RET_OK; +} + +audio_return_e audio_get_volume_level_max(void *audio_handle, audio_volume_info_s *info, uint32_t *level) +{ + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + + /* Get max volume level by device & type */ + *level = ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level); + + return AUDIO_RET_OK; +} + +audio_return_e audio_get_volume_level(void *audio_handle, audio_volume_info_s *info, uint32_t *level) +{ + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + + if (!strncmp(info->type, "master", strlen("master"))) { + *level = g_master_volume_level; + return AUDIO_RET_OK; + } + + *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction); + + return AUDIO_RET_OK; +} + +audio_return_e audio_get_volume_value(void *audio_handle, audio_volume_info_s *info, uint32_t level, double *value) +{ + audio_hal_s *ah = (audio_hal_s *)audio_handle; + audio_volume_value_table_s *volume_value_table; + char dump_str[AUDIO_DUMP_STR_LEN] = {0,}; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER); + + /* Get basic volume by device & type & level */ + volume_value_table = ah->volume.volume_value_table; + if (ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] < level) + *value = VOLUME_VALUE_MAX; + else + *value = volume_value_table->volume[__get_volume_idx_by_string_type(info->type)][level]; + *value *= volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT]; /* need to fix getting gain via audio_info_t */ + + AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]); + + return AUDIO_RET_OK; +} + +audio_return_e audio_set_volume_level(void *audio_handle, audio_volume_info_s *info, uint32_t level) +{ + audio_return_e audio_ret = AUDIO_RET_OK; + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + if (!strncmp(info->type, "master", strlen("master"))) { + g_master_volume_level = level; + return AUDIO_RET_OK; + } + AUDIO_RETURN_VAL_IF_FAIL((ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] >= level), AUDIO_ERR_PARAMETER); + + /* Update volume level */ + ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)] = level; + AUDIO_LOG_INFO("set [%s] volume_level: %d, direction(%d)", info->type, level, info->direction); + + /* set mixer related to H/W volume if needed */ + + return audio_ret; +} + +audio_return_e audio_get_volume_mute(void *audio_handle, audio_volume_info_s *info, uint32_t *mute) +{ + audio_return_e audio_ret = AUDIO_RET_OK; + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(mute, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_e audio_set_volume_mute(void *audio_handle, audio_volume_info_s *info, uint32_t mute) +{ + audio_return_e audio_ret = AUDIO_RET_OK; + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_e audio_set_volume_ratio(void *audio_handle, audio_stream_info_s *info, double ratio) +{ + audio_return_e audio_ret = AUDIO_RET_OK; + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("set [%s] volume_ratio: %f, direction(%u), index(%u)", info->role, ratio, info->direction, info->idx); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_e audio_notify_ducking_activation_changed(void *audio_handle, audio_ducking_info_s *info, uint32_t is_activated) +{ + audio_return_e audio_ret = AUDIO_RET_OK; + audio_hal_s *ah = (audio_hal_s *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info->target_role, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s, duration:%u, ratio:%lf, is_activated:%u", info->target_role, info->duration, info->ratio, is_activated); + + /* TODO. Not implemented */ + + return audio_ret; +} |