Card and device arguments are added to pcm open API
[platform/adaptation/hawkp/audio-hal-hawkp.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 #include "tizen-audio-impl.h"
33
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)
38
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 */
49 };
50
51 static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx)
52 {
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";
64     }
65 }
66
67 static uint32_t __get_volume_idx_by_string_type(const char *vol_type)
68 {
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;
87     else
88         return AUDIO_VOLUME_TYPE_MEDIA;
89 }
90
91 static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx)
92 {
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";
106     }
107 }
108
109 static bool __is_only_mixer_ctl_volume_type(const char *vol_type)
110 {
111     return (!strncmp(vol_type, "master", strlen(vol_type)) || !strncmp(vol_type, "8", strlen(vol_type)));
112 }
113
114 static void __dump_tb(audio_hal_t *ah)
115 {
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 */
130     };
131     char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr;
132
133     /* Dump volume table */
134     AUDIO_LOG_INFO("<<<<< volume table >>>>>");
135
136     const char *table_str = "volumes";
137
138     AUDIO_LOG_INFO("<< %s >>", table_str);
139
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);
142
143         if (__is_only_mixer_ctl_volume_type(vol_type_str))
144             continue;
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);
149
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);
153         }
154         AUDIO_LOG_INFO("%s", dump_str);
155     }
156
157     volume_value_table = ah->volume.volume_value_table;
158
159     /* Dump gain table */
160     AUDIO_LOG_INFO("<<<<< gain table >>>>>");
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, "%10s", " ");
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, "%5s ", gain_type_str[gain_type_idx]);
170         dump_str_ptr += strlen(dump_str_ptr);
171     }
172     AUDIO_LOG_INFO("%s", dump_str);
173
174     dump_str_ptr = &dump_str[0];
175     memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str));
176
177     snprintf(dump_str_ptr, 11, "%9s:", table_str);
178     dump_str_ptr += strlen(dump_str_ptr);
179
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);
183     }
184     AUDIO_LOG_INFO("%s", dump_str);
185
186 }
187
188 static audio_return_t __load_volume_value_table_from_ini(audio_hal_t *ah)
189 {
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;
193     int size = 0;
194
195     dict = iniparser_load(VOLUME_INI_TEMP_PATH);
196     if (!dict) {
197         AUDIO_LOG_DEBUG("Use default volume&gain ini file");
198         dict = iniparser_load(VOLUME_INI_DEFAULT_PATH);
199         if (!dict) {
200             AUDIO_LOG_WARN("Loading volume&gain table from ini file failed");
201             return AUDIO_ERR_UNDEFINED;
202         }
203     }
204
205     const char delimiter[] = ", ";
206     char *key, *list_str, *token, *ptr = NULL;
207     const char *table_str = "volumes";
208
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))
213             continue;
214
215         ah->volume.volume_level_max[vol_type_idx] = 0;
216         size = strlen(table_str) + strlen(vol_type_str) + 2;
217         key = malloc(size);
218         if (key) {
219             snprintf(key, size, "%s:%s", table_str, vol_type_str);
220             list_str = iniparser_getstring(dict, key, NULL);
221             if (list_str) {
222                 token = strtok_r(list_str, delimiter, &ptr);
223                 while (token) {
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);
230                 }
231             } else {
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;
235                 }
236             }
237             free(key);
238         }
239     }
240
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);
245
246         size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3;
247         key = malloc(size);
248         if (key) {
249             snprintf(key, size, "%s:gain_%s", table_str, gain_type_str);
250             token = iniparser_getstring(dict, key, NULL);
251             if (token) {
252                 volume_value_table->gain[gain_type_idx] = atof(token);
253             } else {
254                 volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
255             }
256             free(key);
257         } else {
258             volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX;
259         }
260     }
261
262     iniparser_freedict(dict);
263
264     __dump_tb(ah);
265
266     return AUDIO_RET_OK;
267 }
268
269 audio_return_t _audio_volume_init(audio_hal_t *ah)
270 {
271     int i;
272     int val = 0;
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};
275
276     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
277
278     for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) {
279         ah->volume.volume_level[i] = init_value[i];
280     }
281
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]);
286             continue;
287         }
288
289         AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val);
290         ah->volume.volume_level[i] = val;
291     }
292
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;
296     }
297
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;
302     }
303
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);
312
313     return audio_ret;
314 }
315
316 audio_return_t _audio_volume_deinit(audio_hal_t *ah)
317 {
318     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
319
320     if (ah->volume.volume_value_table) {
321         free(ah->volume.volume_value_table);
322         ah->volume.volume_value_table = NULL;
323     }
324
325     return AUDIO_RET_OK;
326 }
327
328 audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
329 {
330     audio_hal_t *ah = (audio_hal_t *)audio_handle;
331
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);
335
336     /* Get max volume level by device & type */
337     *level = ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)];
338
339     AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level);
340
341     return AUDIO_RET_OK;
342 }
343
344 audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level)
345 {
346     audio_hal_t *ah = (audio_hal_t *)audio_handle;
347
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);
351
352     *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)];
353
354     AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction);
355
356     return AUDIO_RET_OK;
357 }
358
359 audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value)
360 {
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,};
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(value, AUDIO_ERR_PARAMETER);
368     AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER);
369
370     if (__is_only_mixer_ctl_volume_type(info->type)) {
371         *value = VOLUME_VALUE_MAX;
372         goto end;
373     }
374
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;
379     else
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 */
382
383 end:
384     AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]);
385
386     return AUDIO_RET_OK;
387 }
388
389 audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level)
390 {
391     audio_return_t audio_ret = AUDIO_RET_OK;
392     audio_hal_t *ah = (audio_hal_t *)audio_handle;
393
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);
397
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);
401
402     /* set mixer related to H/W volume if needed */
403
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");
407         }
408     }
409
410     return audio_ret;
411 }
412
413 audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute)
414 {
415     audio_return_t audio_ret = AUDIO_RET_OK;
416     audio_hal_t *ah = (audio_hal_t *)audio_handle;
417
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);
421
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");
426         }
427     }
428
429     return audio_ret;
430 }
431
432 audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute)
433 {
434     audio_return_t audio_ret = AUDIO_RET_OK;
435     audio_hal_t *ah = (audio_hal_t *)audio_handle;
436
437     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
438     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
439
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");
444         }
445     }
446
447     return audio_ret;
448 }