Card and device arguments are added to pcm open API
[platform/adaptation/samsung_exynos/audio-hal-ak4953.git] / tizen-audio-volume.c
1 /*
2  * audio-hal
3  *
4  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  */
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <string.h>
27 #include <math.h>
28 #include <vconf.h>
29 #include <iniparser.h>
30
31 #include "tizen-audio-internal.h"
32
33 #define VOLUME_INI_DEFAULT_PATH     SYSCONFDIR"/multimedia/mmfw_audio_volume.ini" /* SYSCONFDIR is defined at .spec */
34 #define VOLUME_INI_TEMP_PATH        "/opt/system/mmfw_audio_volume.ini"
35 #define VOLUME_VALUE_MAX            (1.0f)
36 #define GAIN_VALUE_MAX              (1.0f)
37
38 static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_MAX] = {
39     "file/private/sound/volume/system",         /* AUDIO_VOLUME_TYPE_SYSTEM */
40     "file/private/sound/volume/notification",   /* AUDIO_VOLUME_TYPE_NOTIFICATION */
41     "file/private/sound/volume/alarm",          /* AUDIO_VOLUME_TYPE_ALARM */
42     "file/private/sound/volume/ringtone",       /* AUDIO_VOLUME_TYPE_RINGTONE */
43     "file/private/sound/volume/media",          /* AUDIO_VOLUME_TYPE_MEDIA */
44     "file/private/sound/volume/call",           /* AUDIO_VOLUME_TYPE_CALL */
45     "file/private/sound/volume/voip",           /* AUDIO_VOLUME_TYPE_VOIP */
46     "file/private/sound/volume/voice",          /* AUDIO_VOLUME_TYPE_VOICE */
47 };
48
49 static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx)
50 {
51     switch (vol_type_idx) {
52     case AUDIO_VOLUME_TYPE_SYSTEM:          return "system";
53     case AUDIO_VOLUME_TYPE_NOTIFICATION:    return "notification";
54     case AUDIO_VOLUME_TYPE_ALARM:           return "alarm";
55     case AUDIO_VOLUME_TYPE_RINGTONE:        return "ringtone";
56     case AUDIO_VOLUME_TYPE_MEDIA:           return "media";
57     case AUDIO_VOLUME_TYPE_CALL:            return "call";
58     case AUDIO_VOLUME_TYPE_VOIP:            return "voip";
59     case AUDIO_VOLUME_TYPE_VOICE:           return "voice";
60     default:                                return "invalid";
61     }
62 }
63
64 static uint32_t __get_volume_idx_by_string_type(const char *vol_type)
65 {
66     if (!strncmp(vol_type, "system", strlen(vol_type)) || !strncmp(vol_type, "0", strlen(vol_type)))
67         return AUDIO_VOLUME_TYPE_SYSTEM;
68     else if (!strncmp(vol_type, "notification", strlen(vol_type)) || !strncmp(vol_type, "1", strlen(vol_type)))
69         return AUDIO_VOLUME_TYPE_NOTIFICATION;
70     else if (!strncmp(vol_type, "alarm", strlen(vol_type)) || !strncmp(vol_type, "2", strlen(vol_type)))
71         return AUDIO_VOLUME_TYPE_ALARM;
72     else if (!strncmp(vol_type, "ringtone", strlen(vol_type)) || !strncmp(vol_type, "3", strlen(vol_type)))
73         return AUDIO_VOLUME_TYPE_RINGTONE;
74     else if (!strncmp(vol_type, "media", strlen(vol_type)) || !strncmp(vol_type, "4", strlen(vol_type)))
75         return AUDIO_VOLUME_TYPE_MEDIA;
76     else if (!strncmp(vol_type, "call", strlen(vol_type)) || !strncmp(vol_type, "5", strlen(vol_type)))
77         return AUDIO_VOLUME_TYPE_CALL;
78     else if (!strncmp(vol_type, "voip", strlen(vol_type)) || !strncmp(vol_type, "6", strlen(vol_type)))
79         return AUDIO_VOLUME_TYPE_VOIP;
80     else if (!strncmp(vol_type, "voice", strlen(vol_type)) || !strncmp(vol_type, "7", strlen(vol_type)))
81         return AUDIO_VOLUME_TYPE_VOICE;
82     else
83         return AUDIO_VOLUME_TYPE_MEDIA;
84 }
85
86 static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx)
87 {
88     switch (gain_type_idx) {
89     case AUDIO_GAIN_TYPE_DEFAULT:           return "default";
90     case AUDIO_GAIN_TYPE_DIALER:            return "dialer";
91     case AUDIO_GAIN_TYPE_TOUCH:             return "touch";
92     case AUDIO_GAIN_TYPE_AF:                return "af";
93     case AUDIO_GAIN_TYPE_SHUTTER1:          return "shutter1";
94     case AUDIO_GAIN_TYPE_SHUTTER2:          return "shutter2";
95     case AUDIO_GAIN_TYPE_CAMCODING:         return "camcording";
96     case AUDIO_GAIN_TYPE_MIDI:              return "midi";
97     case AUDIO_GAIN_TYPE_BOOTING:           return "booting";
98     case AUDIO_GAIN_TYPE_VIDEO:             return "video";
99     case AUDIO_GAIN_TYPE_TTS:               return "tts";
100     default:                                return "invalid";
101     }
102 }
103
104 static void __dump_tb(audio_hal_t *ah)
105 {
106     audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table;
107     uint32_t vol_type_idx, vol_level_idx, gain_type_idx;
108     const char *gain_type_str[] = {
109         "def",          /* AUDIO_GAIN_TYPE_DEFAULT */
110         "dial",         /* AUDIO_GAIN_TYPE_DIALER */
111         "touch",        /* AUDIO_GAIN_TYPE_TOUCH */
112         "af",           /* AUDIO_GAIN_TYPE_AF */
113         "shut1",        /* AUDIO_GAIN_TYPE_SHUTTER1 */
114         "shut2",        /* AUDIO_GAIN_TYPE_SHUTTER2 */
115         "cam",          /* AUDIO_GAIN_TYPE_CAMCODING */
116         "midi",         /* AUDIO_GAIN_TYPE_MIDI */
117         "boot",         /* AUDIO_GAIN_TYPE_BOOTING */
118         "video",        /* AUDIO_GAIN_TYPE_VIDEO */
119         "tts",          /* AUDIO_GAIN_TYPE_TTS */
120     };
121     char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr;
122
123     /* Dump volume table */
124     AUDIO_LOG_INFO("<<<<< volume table >>>>>");
125
126     const char *table_str = "volumes";
127
128     AUDIO_LOG_INFO("<< %s >>", table_str);
129
130     for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) {
131         const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx);
132
133         dump_str_ptr = &dump_str[0];
134         memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
135         snprintf(dump_str_ptr, 8, "%6s:", vol_type_str);
136         dump_str_ptr += strlen(dump_str_ptr);
137
138         for (vol_level_idx = 0; vol_level_idx < ah->volume.volume_level_max[vol_type_idx]; vol_level_idx++) {
139             snprintf(dump_str_ptr, 6, "%01.2f ", volume_value_table->volume[vol_type_idx][vol_level_idx]);
140             dump_str_ptr += strlen(dump_str_ptr);
141         }
142         AUDIO_LOG_INFO("%s", dump_str);
143     }
144
145     volume_value_table = ah->volume.volume_value_table;
146
147     /* Dump gain table */
148     AUDIO_LOG_INFO("<<<<< gain table >>>>>");
149
150     dump_str_ptr = &dump_str[0];
151     memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
152
153     snprintf(dump_str_ptr, 11, "%10s", " ");
154     dump_str_ptr += strlen(dump_str_ptr);
155
156     for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
157         snprintf(dump_str_ptr, 7, "%5s ", gain_type_str[gain_type_idx]);
158         dump_str_ptr += strlen(dump_str_ptr);
159     }
160     AUDIO_LOG_INFO("%s", dump_str);
161
162     dump_str_ptr = &dump_str[0];
163     memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
164
165     snprintf(dump_str_ptr, 11, "%9s:", table_str);
166     dump_str_ptr += strlen(dump_str_ptr);
167
168     for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
169         snprintf(dump_str_ptr, 7, "%01.3f ", volume_value_table->gain[gain_type_idx]);
170         dump_str_ptr += strlen(dump_str_ptr);
171     }
172     AUDIO_LOG_INFO("%s", dump_str);
173
174 }
175
176 static audio_return_t __load_volume_value_table_from_ini(audio_hal_t *ah)
177 {
178     dictionary * dict = NULL;
179     uint32_t vol_type_idx, vol_level_idx, gain_type_idx;
180     audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table;
181     int size = 0;
182
183     dict = iniparser_load(VOLUME_INI_TEMP_PATH);
184     if (!dict) {
185         AUDIO_LOG_DEBUG("Use default volume&gain ini file");
186         dict = iniparser_load(VOLUME_INI_DEFAULT_PATH);
187         if (!dict) {
188             AUDIO_LOG_WARN("Loading volume&gain table from ini file failed");
189             return AUDIO_ERR_UNDEFINED;
190         }
191     }
192
193     const char delimiter[] = ", ";
194     char *key, *list_str, *token, *ptr = NULL;
195     const char *table_str = "volumes";
196
197     /* Load volume table */
198     for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) {
199         const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx);
200
201         ah->volume.volume_level_max[vol_type_idx] = 0;
202         size = strlen(table_str) + strlen(vol_type_str) + 2;
203         key = malloc(size);
204         if (key) {
205             snprintf(key, size, "%s:%s", table_str, vol_type_str);
206             list_str = iniparser_getstring(dict, key, NULL);
207             if (list_str) {
208                 token = strtok_r(list_str, delimiter, &ptr);
209                 while (token) {
210                     /* convert dB volume to linear volume */
211                     double vol_value = 0.0f;
212                     if (strncmp(token, "0", strlen(token)))
213                         vol_value = pow(10.0, (atof(token) - 100) / 20.0);
214                     volume_value_table->volume[vol_type_idx][ah->volume.volume_level_max[vol_type_idx]++] = vol_value;
215                     token = strtok_r(NULL, delimiter, &ptr);
216                 }
217             } else {
218                 ah->volume.volume_level_max[vol_type_idx] = 1;
219                 for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) {
220                     volume_value_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX;
221                 }
222             }
223             free(key);
224         }
225     }
226
227     /* Load gain table */
228     volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT] = GAIN_VALUE_MAX;
229     for (gain_type_idx = AUDIO_GAIN_TYPE_DEFAULT + 1; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) {
230         const char *gain_type_str = __get_gain_type_string_by_idx(gain_type_idx);
231
232         size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3;
233         key = malloc(size);
234         if (key) {
235             snprintf(key, size, "%s:gain_%s", table_str, gain_type_str);
236             token = iniparser_getstring(dict, key, NULL);
237             if (token) {
238                 volume_value_table->gain[gain_type_idx] = atof(token);
239             } else {
240                 volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
241             }
242             free(key);
243         } else {
244             volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
245         }
246     }
247
248     iniparser_freedict(dict);
249
250     __dump_tb(ah);
251
252     return AUDIO_RET_OK;
253 }
254
255 audio_return_t _audio_volume_init(audio_hal_t *ah)
256 {
257     int i;
258     int val = 0;
259     audio_return_t audio_ret = AUDIO_RET_OK;
260     int init_value[AUDIO_VOLUME_TYPE_MAX] = { 9, 11, 7, 11, 7, 4, 4, 7 };
261
262     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
263
264     for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) {
265         ah->volume.volume_level[i] = init_value[i];
266     }
267
268     for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) {
269         /* Get volume value string from VCONF */
270         if (vconf_get_int(g_volume_vconf[i], &val) < 0) {
271             AUDIO_LOG_ERROR("vconf_get_int(%s) failed", g_volume_vconf[i]);
272             continue;
273         }
274
275         AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val);
276         ah->volume.volume_level[i] = val;
277     }
278
279     if (!(ah->volume.volume_value_table = malloc(AUDIO_VOLUME_DEVICE_MAX * sizeof(audio_volume_value_table_t)))) {
280         AUDIO_LOG_ERROR("volume_value_table malloc failed");
281         return AUDIO_ERR_RESOURCE;
282     }
283
284     audio_ret = __load_volume_value_table_from_ini(ah);
285     if (audio_ret != AUDIO_RET_OK) {
286         AUDIO_LOG_ERROR("gain table load error");
287         return AUDIO_ERR_UNDEFINED;
288     }
289
290     return audio_ret;
291 }
292
293 audio_return_t _audio_volume_deinit(audio_hal_t *ah)
294 {
295     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
296
297     if (ah->volume.volume_value_table) {
298         free(ah->volume.volume_value_table);
299         ah->volume.volume_value_table = NULL;
300     }
301
302     return AUDIO_RET_OK;
303 }
304
305 audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
306 {
307     audio_hal_t *ah = (audio_hal_t *)audio_handle;
308
309     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
310     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
311     AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER);
312
313     /* Get max volume level by device & type */
314     *level = ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)];
315
316     AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level);
317
318     return AUDIO_RET_OK;
319 }
320
321 audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
322 {
323     audio_hal_t *ah = (audio_hal_t *)audio_handle;
324
325     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
326     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
327     AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER);
328
329     *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)];
330
331     AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction);
332
333     return AUDIO_RET_OK;
334 }
335
336 audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value)
337 {
338     audio_hal_t *ah = (audio_hal_t *)audio_handle;
339     audio_volume_value_table_t *volume_value_table;
340     char dump_str[AUDIO_DUMP_STR_LEN] = {0,};
341
342     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
343     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
344     AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER);
345     AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER);
346
347     /* Get basic volume by device & type & level */
348     volume_value_table = ah->volume.volume_value_table;
349     if (ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] < level)
350         *value = VOLUME_VALUE_MAX;
351     else
352         *value = volume_value_table->volume[__get_volume_idx_by_string_type(info->type)][level];
353     *value *= volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT]; /* need to fix getting gain via audio_info_t */
354
355     AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]);
356
357     return AUDIO_RET_OK;
358 }
359
360 audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level)
361 {
362     audio_return_t audio_ret = AUDIO_RET_OK;
363     audio_hal_t *ah = (audio_hal_t *)audio_handle;
364
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((ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] >= level), AUDIO_ERR_PARAMETER);
368
369     /* Update volume level */
370     ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)] = level;
371     AUDIO_LOG_INFO("set [%s] volume_level: %d, direction(%d)", info->type, level, info->direction);
372
373     /* set mixer related to H/W volume if needed */
374
375     return audio_ret;
376 }
377
378 audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute)
379 {
380     audio_return_t audio_ret = AUDIO_RET_OK;
381     audio_hal_t *ah = (audio_hal_t *)audio_handle;
382
383     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
384     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
385     AUDIO_RETURN_VAL_IF_FAIL(mute, AUDIO_ERR_PARAMETER);
386
387     /* TODO. Not implemented */
388
389     return audio_ret;
390 }
391
392 audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute)
393 {
394     audio_return_t audio_ret = AUDIO_RET_OK;
395     audio_hal_t *ah = (audio_hal_t *)audio_handle;
396
397     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
398     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
399
400     /* TODO. Not implemented */
401
402     return audio_ret;
403 }