summaryrefslogtreecommitdiff
path: root/tizen-audio-volume.c
diff options
context:
space:
mode:
authorSeungbae Shin <seungbae.shin@samsung.com>2022-01-27 17:02:51 +0900
committerSeungbae Shin <seungbae.shin@samsung.com>2022-01-27 17:49:22 +0900
commitb0320539d30cbbcd2fd67f381f1381073ba1fe5e (patch)
tree9fb34c3eeabb4affd54ec01642c40bc108032834 /tizen-audio-volume.c
parentd9cf41d3229bcd18259aa0fcad25489593487eb7 (diff)
downloadaudio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.tar.gz
audio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.tar.bz2
audio-alsa-b0320539d30cbbcd2fd67f381f1381073ba1fe5e.zip
[Version] 0.0.1 [Issue Type] Init Change-Id: I3aab132bfe06c0ccf29f7f91d69f366506153a79
Diffstat (limited to 'tizen-audio-volume.c')
-rw-r--r--tizen-audio-volume.c452
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;
+}