Fix float->int(short) clipping issue when playing ogg content
[platform/core/multimedia/libmm-sound.git] / mm_sound_device.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Sangchul Lee <sc11.lee@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include <mm_debug.h>
26
27 #include "include/mm_sound.h"
28 #include "include/mm_sound_device.h"
29 #include "include/mm_sound_client.h"
30
31 #define VOLUME_TYPE_LEN 64
32
33 mm_sound_device_list_t g_device_list;
34 pthread_mutex_t g_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
35
36 static int _check_for_valid_mask(int flags)
37 {
38         int ret = MM_ERROR_NONE;
39         bool at_least_cond = false;
40
41         if (flags > 0 && flags <= MM_SOUND_DEVICE_ALL_FLAG) {
42                 if (flags & MM_SOUND_DEVICE_IO_DIRECTION_IN_FLAG)
43                         at_least_cond = true;
44                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_OUT_FLAG))
45                         at_least_cond = true;
46                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_BOTH_FLAG))
47                         at_least_cond = true;
48                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_INTERNAL_FLAG))
49                         at_least_cond = true;
50                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_EXTERNAL_FLAG))
51                         at_least_cond = true;
52                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_DEACTIVATED_FLAG))
53                         at_least_cond = true;
54                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_ACTIVATED_FLAG))
55                         at_least_cond = true;
56         } else {
57                 ret = MM_ERROR_INVALID_ARGUMENT;
58         }
59
60         if (!at_least_cond)
61                 ret = MM_ERROR_INVALID_ARGUMENT;
62
63         if (ret)
64                 debug_error("flags[0x%x] is not valid", flags);
65
66         return ret;
67 }
68
69 static int __convert_device_type_to_enum(char *device_type, mm_sound_device_type_e *device_type_enum)
70 {
71         int ret = MM_ERROR_NONE;
72
73         if (!device_type || !device_type_enum)
74                 return MM_ERROR_INVALID_ARGUMENT;
75
76         if (!strncmp(device_type, "builtin-speaker", VOLUME_TYPE_LEN)) {
77                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_SPEAKER;
78         } else if (!strncmp(device_type, "builtin-receiver", VOLUME_TYPE_LEN)) {
79                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_RECEIVER;
80         } else if (!strncmp(device_type, "builtin-mic", VOLUME_TYPE_LEN)) {
81                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_MIC;
82         } else if (!strncmp(device_type, "audio-jack", VOLUME_TYPE_LEN)) {
83                 *device_type_enum = MM_SOUND_DEVICE_TYPE_AUDIOJACK;
84         } else if (!strncmp(device_type, "bt-a2dp", VOLUME_TYPE_LEN)) {
85                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_A2DP;
86         } else if (!strncmp(device_type, "bt-sco", VOLUME_TYPE_LEN)) {
87                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_SCO;
88         } else if (!strncmp(device_type, "hdmi", VOLUME_TYPE_LEN)) {
89                 *device_type_enum = MM_SOUND_DEVICE_TYPE_HDMI;
90         } else if (!strncmp(device_type, "forwarding", VOLUME_TYPE_LEN)) {
91                 *device_type_enum = MM_SOUND_DEVICE_TYPE_MIRRORING;
92         } else if (!strncmp(device_type, "usb-audio", VOLUME_TYPE_LEN)) {
93                 *device_type_enum = MM_SOUND_DEVICE_TYPE_USB_AUDIO;
94         } else {
95                 ret = MM_ERROR_INVALID_ARGUMENT;
96                 debug_error("not supported device_type(%s), err(0x%08x)", device_type, ret);
97         }
98
99         return ret;
100 }
101
102 static int __free_device_list(mm_sound_device_list_t *device_list_t)
103 {
104         if (!device_list_t)
105                 return MM_ERROR_INVALID_ARGUMENT;
106
107         debug_log("free device list %p", device_list_t);
108         g_list_free_full(g_list_first(device_list_t->list), g_free);
109         g_free(device_list_t);
110
111         return MM_ERROR_NONE;
112 }
113
114 EXPORT_API
115 int mm_sound_add_device_connected_callback(int flags, mm_sound_device_connected_cb func, void *user_data, unsigned int *id)
116 {
117         int ret = MM_ERROR_NONE;
118
119         if (func == NULL || id == NULL) {
120                 debug_error("argument is not valid");
121                 return MM_ERROR_INVALID_ARGUMENT;
122         }
123         ret = _check_for_valid_mask(flags);
124         if (ret == MM_ERROR_NONE) {
125                 ret = mm_sound_client_add_device_connected_callback(flags, func, user_data, id);
126                 if (ret < 0)
127                         debug_error("Could not add device connected callback, ret = %x", ret);
128         }
129
130         return ret;
131 }
132
133 EXPORT_API
134 int mm_sound_remove_device_connected_callback(unsigned int id)
135 {
136         int ret = MM_ERROR_NONE;
137
138         ret = mm_sound_client_remove_device_connected_callback(id);
139         if (ret < 0)
140                 debug_error("Could not remove device connected callback, ret = %x", ret);
141
142         return ret;
143 }
144
145 EXPORT_API
146 int mm_sound_add_device_information_changed_callback(int flags, mm_sound_device_info_changed_cb func, void *user_data, unsigned int *id)
147 {
148         int ret = MM_ERROR_NONE;
149
150         if (func == NULL || id == NULL) {
151                 debug_error("argument is not valid");
152                 return MM_ERROR_INVALID_ARGUMENT;
153         }
154         ret = _check_for_valid_mask(flags);
155         if (ret == MM_ERROR_NONE) {
156                 ret = mm_sound_client_add_device_info_changed_callback(flags, func, user_data, id);
157                 if (ret < 0)
158                         debug_error("Could not add device information changed callback, ret = %x", ret);
159         }
160
161         return ret;
162 }
163
164 EXPORT_API
165 int mm_sound_remove_device_information_changed_callback(unsigned int id)
166 {
167         int ret = MM_ERROR_NONE;
168
169         ret = mm_sound_client_remove_device_info_changed_callback(id);
170         if (ret < 0)
171                 debug_error("Could not remove device information changed callback, ret = %x", ret);
172
173         return ret;
174 }
175
176 EXPORT_API
177 int mm_sound_add_device_state_changed_callback(int flags, mm_sound_device_state_changed_cb func, void *user_data, unsigned int *id)
178 {
179         int ret = MM_ERROR_NONE;
180
181         if (func == NULL || id == NULL) {
182                 debug_error("argument is not valid");
183                 return MM_ERROR_INVALID_ARGUMENT;
184         }
185         ret = _check_for_valid_mask(flags);
186         if (ret == MM_ERROR_NONE) {
187                 ret = mm_sound_client_add_device_state_changed_callback(flags, func, user_data, id);
188                 if (ret < 0)
189                         debug_error("Could not add device state changed callback, ret = %x", ret);
190         }
191
192         return ret;
193 }
194
195 EXPORT_API
196 int mm_sound_remove_device_state_changed_callback(unsigned int id)
197 {
198         int ret = MM_ERROR_NONE;
199
200         ret = mm_sound_client_remove_device_state_changed_callback(id);
201         if (ret < 0)
202                 debug_error("Could not remove device state changed callback, ret = %x", ret);
203
204         return ret;
205 }
206
207 EXPORT_API
208 int mm_sound_add_device_running_changed_callback(int flags, mm_sound_device_running_changed_cb func, void *user_data, unsigned int *id)
209 {
210         int ret = MM_ERROR_NONE;
211
212         if (func == NULL || id == NULL) {
213                 debug_error("argument is not valid");
214                 return MM_ERROR_INVALID_ARGUMENT;
215         }
216         ret = _check_for_valid_mask(flags);
217         if (ret == MM_ERROR_NONE) {
218                 ret = mm_sound_client_add_device_running_changed_callback(flags, func, user_data, id);
219                 if (ret < 0) {
220                         debug_error("Could not add device running changed callback, ret = %x", ret);
221                 }
222         }
223
224         return ret;
225 }
226
227 EXPORT_API
228 int mm_sound_remove_device_running_changed_callback(unsigned int id)
229 {
230         int ret = MM_ERROR_NONE;
231
232         ret = mm_sound_client_remove_device_running_changed_callback(id);
233         if (ret < 0) {
234                 debug_error("Could not remove device running changed callback, ret = %x", ret);
235         }
236
237         return ret;
238 }
239
240 EXPORT_API
241 int mm_sound_get_current_device_list(mm_sound_device_flags_e flags, MMSoundDeviceList_t *device_list)
242 {
243         int ret = MM_ERROR_NONE;
244
245         if (!device_list)
246                 return MM_ERROR_INVALID_ARGUMENT;
247
248         ret = _check_for_valid_mask(flags);
249         if (ret != MM_ERROR_NONE) {
250                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
251                 return ret;
252         }
253
254         pthread_mutex_lock(&g_thread_mutex);
255
256         if (g_device_list.list != NULL) {
257                 g_list_free_full(g_device_list.list, g_free);
258                 g_device_list.list = NULL;
259         }
260
261         g_device_list.is_new_device_list = true;
262
263         ret = mm_sound_client_get_current_connected_device_list(flags, &g_device_list);
264         if (ret < 0) {
265                 debug_error("Could not get current connected device list, ret = %x", ret);
266                 g_device_list.list = NULL;
267         } else {
268                 *device_list = &g_device_list;
269         }
270
271         pthread_mutex_unlock(&g_thread_mutex);
272
273         return ret;
274 }
275
276 EXPORT_API
277 int mm_sound_get_device_list(int flags, MMSoundDeviceList_t *device_list)
278 {
279         int ret = MM_ERROR_NONE;
280         mm_sound_device_list_t *_device_list;
281
282         if (!device_list)
283                 return MM_ERROR_INVALID_ARGUMENT;
284
285         ret = _check_for_valid_mask(flags);
286         if (ret != MM_ERROR_NONE) {
287                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
288                 return ret;
289         }
290
291         if (!(_device_list = g_malloc0(sizeof(mm_sound_device_list_t)))) {
292                 debug_error("Allocate device list failed");
293                 return MM_ERROR_SOUND_INTERNAL;
294         }
295
296         _device_list->is_new_device_list = true;
297
298         ret = mm_sound_client_get_current_connected_device_list(flags, _device_list);
299         if (ret < 0) {
300                 debug_error("Could not get current connected device list, ret = %x", ret);
301                 g_free(_device_list);
302         } else {
303                 *device_list = _device_list;
304         }
305
306         return ret;
307 }
308
309 EXPORT_API
310 int mm_sound_free_device_list(MMSoundDeviceList_t device_list)
311 {
312         return __free_device_list((mm_sound_device_list_t*) device_list);
313 }
314
315 EXPORT_API
316 int mm_sound_free_device(MMSoundDevice_t device_h)
317 {
318         if (device_h == NULL)
319                 return MM_ERROR_INVALID_ARGUMENT;
320
321         g_free(device_h);
322
323         return MM_ERROR_NONE;
324 }
325
326 EXPORT_API
327 int mm_sound_get_device_by_id(int device_id, MMSoundDevice_t *device_h)
328 {
329         int ret = MM_ERROR_NONE;
330         mm_sound_device_t *device = NULL;
331
332         if (device_id < 1 || device_h == NULL)
333                 return MM_ERROR_INVALID_ARGUMENT;
334
335         ret = mm_sound_client_get_device_by_id(device_id, &device);
336         if (ret < 0)
337                 debug_error("Could not get device by id, ret = %x", ret);
338         else
339                 *device_h = device;
340
341         return ret;
342 }
343
344 EXPORT_API
345 int mm_sound_get_next_device(MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
346 {
347         int ret = MM_ERROR_NONE;
348         mm_sound_device_list_t *device_list_t = NULL;
349         GList *node = NULL;
350
351         if (!device_list || !device)
352                 return MM_ERROR_INVALID_ARGUMENT;
353
354         device_list_t = (mm_sound_device_list_t*) device_list;
355         if (device_list_t->is_new_device_list)
356                 node = g_list_first(device_list_t->list);
357         else
358                 node = g_list_next(device_list_t->list);
359
360         if (!node) {
361                 ret = MM_ERROR_SOUND_NO_DATA;
362         } else {
363                 if (device_list_t->is_new_device_list)
364                         device_list_t->is_new_device_list = false;
365                 else
366                         device_list_t->list = node;
367
368                 *device = (mm_sound_device_t*)node->data;
369                 debug_log("next device[%p]", *device);
370         }
371         return ret;
372 }
373
374 EXPORT_API
375 int mm_sound_get_prev_device(MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
376 {
377         int ret = MM_ERROR_NONE;
378         mm_sound_device_list_t *device_list_t = NULL;
379         GList *node = NULL;
380
381         if (!device_list || !device)
382                 return MM_ERROR_INVALID_ARGUMENT;
383
384         device_list_t = (mm_sound_device_list_t*) device_list;
385         node = g_list_previous(device_list_t->list);
386         if (!node) {
387                 ret = MM_ERROR_SOUND_NO_DATA;
388                 debug_error("Could not get previous device, ret = %x", ret);
389         } else {
390                 device_list_t->list = node;
391                 *device = (mm_sound_device_t*)node->data;
392                 debug_log("previous device[%p]", *device);
393         }
394         return ret;
395 }
396
397 EXPORT_API
398 int mm_sound_get_device_type(MMSoundDevice_t device_h, mm_sound_device_type_e *type)
399 {
400         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
401         if (!device || !type) {
402                 debug_error("invalid argument");
403                 return MM_ERROR_INVALID_ARGUMENT;
404         }
405         __convert_device_type_to_enum(device->type, type);
406         debug_log("device_handle:%p, type:%d", device, *type);
407
408         return MM_ERROR_NONE;
409 }
410
411 EXPORT_API
412 int mm_sound_get_device_io_direction(MMSoundDevice_t device_h, mm_sound_device_io_direction_e *io_direction)
413 {
414         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
415         if (!device) {
416                 debug_error("invalid handle");
417                 return MM_ERROR_INVALID_ARGUMENT;
418         }
419         *io_direction = device->io_direction;
420         debug_log("device_handle:%p, io_direction:%d (1:IN,2:OUT,3:INOUT)", device, *io_direction);
421
422         return MM_ERROR_NONE;
423 }
424
425 EXPORT_API
426 int mm_sound_get_device_id(MMSoundDevice_t device_h, int *id)
427 {
428         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
429         if (!device) {
430                 debug_error("invalid handle");
431                 return MM_ERROR_INVALID_ARGUMENT;
432         }
433         *id = device->id;
434         debug_log("device_handle:%p, id:%d", device, *id);
435
436         return MM_ERROR_NONE;
437 }
438
439 EXPORT_API
440 int mm_sound_get_device_state(MMSoundDevice_t device_h, mm_sound_device_state_e *state)
441 {
442         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
443         if (!device) {
444                 debug_error("invalid handle");
445                 return MM_ERROR_INVALID_ARGUMENT;
446         }
447         *state = device->state;
448         debug_log("device_handle:%p, state:%d (0:INACTIVATED,1:ACTIVATED)", device, *state);
449
450         return MM_ERROR_NONE;
451 }
452
453 EXPORT_API
454 int mm_sound_get_device_name(MMSoundDevice_t device_h, char **name)
455 {
456         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
457         if (!device) {
458                 debug_error("invalid handle");
459                 return MM_ERROR_INVALID_ARGUMENT;
460         }
461         *name = device->name;
462         debug_log("device_handle:%p, name:%s", device, *name);
463
464         return MM_ERROR_NONE;
465 }
466
467 EXPORT_API
468 int mm_sound_get_device_vendor_id(MMSoundDevice_t device_h, int *vendor_id)
469 {
470         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
471         if (!device) {
472                 debug_error("invalid handle");
473                 return MM_ERROR_INVALID_ARGUMENT;
474         }
475         *vendor_id = device->vendor_id;
476         debug_log("device_handle:%p, vendor id:%04x", device, *vendor_id);
477
478         return MM_ERROR_NONE;
479 }
480
481 EXPORT_API
482 int mm_sound_get_device_product_id(MMSoundDevice_t device_h, int *product_id)
483 {
484         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
485         if (!device) {
486                 debug_error("invalid handle");
487                 return MM_ERROR_INVALID_ARGUMENT;
488         }
489         *product_id = device->product_id;
490         debug_log("device_handle:%p, product id:%04x", device, *product_id);
491
492         return MM_ERROR_NONE;
493 }
494
495 EXPORT_API
496 int mm_sound_is_stream_on_device(int stream_id, MMSoundDevice_t device_h, bool *is_on)
497 {
498         int ret = MM_ERROR_NONE;
499         int i;
500         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
501         bool _is_on = false;
502
503         if (!device || !is_on) {
504                 debug_error("invalid argument");
505                 return MM_ERROR_INVALID_ARGUMENT;
506         }
507
508         if (device->stream_num >= 0) {
509                 debug_log("device_handle has stream id");
510                 for (i = 0; i < device->stream_num; i++) {
511                         if (device->stream_id[i] == stream_id) {
512                                 _is_on = true;
513                                 break;
514                         }
515                 }
516         } else {
517                 debug_log("device_handle dosn't have stream id");
518                 /* No information about stream in client-side, should ask to server-side */
519                 if ((ret = mm_sound_client_is_stream_on_device(stream_id, device->id, &_is_on)) < 0) {
520                         debug_error("Failed to query is stream on");
521                         return MM_ERROR_SOUND_INTERNAL;
522                 }
523         }
524
525         debug_log("device(%d) %s stream(%d)", device->id, _is_on ? "has" : "doesn't have", stream_id);
526         *is_on = _is_on;
527
528         return ret;
529 }
530
531 EXPORT_API
532 int mm_sound_is_device_running(MMSoundDevice_t device_h, bool *is_running)
533 {
534         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
535         if (!device || !is_running) {
536                 debug_error("invalid argument");
537                 return MM_ERROR_INVALID_ARGUMENT;
538         }
539         *is_running = device->is_running;
540         debug_log("device_handle:0x%x, running:%d", device, *is_running);
541
542         return MM_ERROR_NONE;
543 }