update for beta release
[framework/uifw/e17.git] / src / modules / mixer / sys_alsa.c
1 #include <alsa/asoundlib.h>
2 #include <poll.h>
3 #include "e_mod_system.h"
4 #include "e.h"
5
6 struct e_mixer_callback_desc
7 {
8    int             (*func)(void *data,
9                            E_Mixer_System *self);
10    void           *data;
11    E_Mixer_System *self;
12    Ecore_Idler    *idler;
13    Eina_List      *handlers;
14 };
15
16 static int _mixer_callback_add(E_Mixer_System *self,
17                                int (*func)(void *data, E_Mixer_System *self),
18                                void *data);
19 static int _mixer_callback_del(E_Mixer_System               *self,
20                                struct e_mixer_callback_desc *desc);
21
22 static Eina_Bool
23 _cb_dispatch(void *data)
24 {
25    struct e_mixer_callback_desc *desc;
26    int r;
27
28    desc = data;
29    snd_mixer_handle_events(desc->self);
30    r = desc->func(desc->data, desc->self);
31    desc->idler = NULL;
32
33    if (!r)
34      _mixer_callback_del(desc->self, desc);  /* desc is invalid then. */
35
36    return ECORE_CALLBACK_CANCEL;
37 }
38
39 static Eina_Bool
40 _cb_fd_handler(void             *data,
41                Ecore_Fd_Handler *fd_handler)
42 {
43    struct e_mixer_callback_desc *desc;
44
45    desc = data;
46
47    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR))
48      {
49         desc->handlers = eina_list_remove(desc->handlers, fd_handler);
50         if (!desc->handlers)
51           {
52              E_Mixer_System *s;
53              int (*f)(void *,
54                       E_Mixer_System *);
55              void *d;
56
57              s = desc->self;
58              f = desc->func;
59              d = desc->data;
60              _mixer_callback_del(s, desc);
61              _mixer_callback_add(s, f, d);
62           }
63         return ECORE_CALLBACK_CANCEL;
64      }
65
66    if (!desc->idler)
67      desc->idler = ecore_idler_add(_cb_dispatch, desc);
68    return ECORE_CALLBACK_RENEW;
69 }
70
71 static int
72 _mixer_callback_add(E_Mixer_System *self,
73                     int (*func)(void *data, E_Mixer_System *self),
74                     void *data)
75 {
76    struct e_mixer_callback_desc *desc;
77    struct pollfd *pfds;
78    int len;
79
80    len = snd_mixer_poll_descriptors_count(self);
81    if (len <= 0)
82      return 0;
83
84    desc = malloc(sizeof(struct e_mixer_callback_desc));
85    if (!desc)
86      return 0;
87
88    desc->func = func;
89    desc->data = data;
90    desc->self = self;
91    desc->idler = NULL;
92    desc->handlers = NULL;
93
94    pfds = alloca(len * sizeof(struct pollfd));
95    len = snd_mixer_poll_descriptors(self, pfds, len);
96    if (len <= 0)
97      {
98         free(desc);
99         return 0;
100      }
101
102    while (len > 0)
103      {
104         Ecore_Fd_Handler *fd_handler;
105
106         len--;
107         fd_handler = ecore_main_fd_handler_add(
108             pfds[len].fd, ECORE_FD_READ, _cb_fd_handler, desc, NULL, NULL);
109         desc->handlers = eina_list_prepend(desc->handlers, fd_handler);
110      }
111
112    snd_mixer_set_callback_private(self, desc);
113
114    return 1;
115 }
116
117 static int
118 _mixer_callback_del(E_Mixer_System               *self,
119                     struct e_mixer_callback_desc *desc)
120 {
121    Ecore_Fd_Handler *handler;
122
123    EINA_LIST_FREE(desc->handlers, handler)
124      ecore_main_fd_handler_del(handler);
125
126    snd_mixer_set_callback_private(self, NULL);
127
128    free(desc);
129
130    return 1;
131 }
132
133 static int
134 _mixer_callback_replace(E_Mixer_System *self __UNUSED__,
135                         struct e_mixer_callback_desc *desc,
136                         int (*func)(void *data, E_Mixer_System *self),
137                         void *data)
138 {
139    desc->func = func;
140    desc->data = data;
141
142    return 1;
143 }
144
145 E_Mixer_System *
146 e_mixer_system_new(const char *name)
147 {
148    snd_mixer_t *handle;
149    int err;
150
151    if (!name)
152      return NULL;
153
154    err = snd_mixer_open(&handle, 0);
155    if (err < 0)
156      goto error_open;
157
158    err = snd_mixer_attach(handle, name);
159    if (err < 0)
160      goto error_load;
161
162    err = snd_mixer_selem_register(handle, NULL, NULL);
163    if (err < 0)
164      goto error_load;
165
166    err = snd_mixer_load(handle);
167    if (err < 0)
168      goto error_load;
169
170    return handle;
171
172 error_load:
173    snd_mixer_close(handle);
174 error_open:
175    fprintf(stderr, "MIXER: Cannot get hardware info: %s\n", snd_strerror(err));
176    return NULL;
177 }
178
179 void
180 e_mixer_system_del(E_Mixer_System *self)
181 {
182    struct e_mixer_callback_desc *desc;
183
184    if (!self)
185      return;
186
187    desc = snd_mixer_get_callback_private(self);
188    if (desc)
189      _mixer_callback_del(self, desc);
190
191    snd_mixer_close(self);
192 }
193
194 int
195 e_mixer_system_callback_set(E_Mixer_System *self,
196                             int (*func)(void *data, E_Mixer_System *self),
197                             void *data)
198 {
199    struct e_mixer_callback_desc *desc;
200
201    if (!self)
202      return 0;
203
204    desc = snd_mixer_get_callback_private(self);
205    if (!desc)
206      {
207         if (func)
208           return _mixer_callback_add(self, func, data);
209         return 1;
210      }
211    else
212      {
213         if (func)
214           return _mixer_callback_replace(self, desc, func, data);
215         else
216           return _mixer_callback_del(self, desc);
217      }
218 }
219
220 Eina_List *
221 e_mixer_system_get_cards(void)
222 {
223    int err, card_num;
224    Eina_List *cards;
225
226    cards = NULL;
227    card_num = -1;
228    while (((err = snd_card_next(&card_num)) == 0) && (card_num >= 0))
229      {
230         snd_ctl_t *control;
231         char buf[256];
232
233         snprintf(buf, sizeof(buf), "hw:%d", card_num);
234
235         if (snd_ctl_open(&control, buf, 0) < 0)
236           break;
237         snd_ctl_close(control);
238         cards = eina_list_append(cards, eina_stringshare_add(buf));
239      }
240
241    if (err < 0)
242      fprintf(stderr, "MIXER: Cannot get available card number: %s\n",
243              snd_strerror(err));
244
245    return cards;
246 }
247
248 void
249 e_mixer_system_free_cards(Eina_List *cards)
250 {
251    const char *card;
252
253    EINA_LIST_FREE(cards, card)
254      eina_stringshare_del(card);
255 }
256
257 const char *
258 e_mixer_system_get_default_card(void)
259 {
260    static const char buf[] = "hw:0";
261    snd_ctl_t *control;
262
263    if (snd_ctl_open(&control, buf, 0) < 0)
264      return NULL;
265    snd_ctl_close(control);
266    return eina_stringshare_add(buf);
267 }
268
269 const char *
270 e_mixer_system_get_card_name(const char *card)
271 {
272    snd_ctl_card_info_t *hw_info;
273    const char *name;
274    snd_ctl_t *control;
275    int err;
276
277    if (!card)
278      return NULL;
279
280    snd_ctl_card_info_alloca(&hw_info);
281
282    err = snd_ctl_open(&control, card, 0);
283    if (err < 0)
284      return NULL;
285
286    err = snd_ctl_card_info(control, hw_info);
287    if (err < 0)
288      {
289         fprintf(stderr, "MIXER: Cannot get hardware info: %s: %s\n", card,
290                 snd_strerror(err));
291         snd_ctl_close(control);
292         return NULL;
293      }
294
295    snd_ctl_close(control);
296    name = snd_ctl_card_info_get_name(hw_info);
297    if (!name)
298      {
299         fprintf(stderr, "MIXER: Cannot get hardware name: %s\n", card);
300         return NULL;
301      }
302
303    return eina_stringshare_add(name);
304 }
305
306 Eina_List *
307 e_mixer_system_get_channels(E_Mixer_System *self)
308 {
309    Eina_List *channels;
310    snd_mixer_elem_t *elem;
311
312    if (!self)
313      return NULL;
314
315    channels = NULL;
316
317    elem = snd_mixer_first_elem(self);
318    for (; elem; elem = snd_mixer_elem_next(elem))
319      {
320         if ((!snd_mixer_selem_is_active(elem)) ||
321             (!snd_mixer_selem_has_playback_volume(elem)))
322           continue;
323
324         channels = eina_list_append(channels, elem);
325      }
326
327    return channels;
328 }
329
330 void
331 e_mixer_system_free_channels(Eina_List *channels)
332 {
333    eina_list_free(channels);
334 }
335
336 Eina_List *
337 e_mixer_system_get_channels_names(E_Mixer_System *self)
338 {
339    Eina_List *channels;
340    snd_mixer_elem_t *elem;
341    snd_mixer_selem_id_t *sid;
342
343    if (!self)
344      return NULL;
345
346    channels = NULL;
347    snd_mixer_selem_id_alloca(&sid);
348
349    elem = snd_mixer_first_elem(self);
350    for (; elem; elem = snd_mixer_elem_next(elem))
351      {
352         const char *name;
353         if ((!snd_mixer_selem_is_active(elem)) ||
354             (!snd_mixer_selem_has_playback_volume(elem)))
355           continue;
356
357         snd_mixer_selem_get_id(elem, sid);
358         name = snd_mixer_selem_id_get_name(sid);
359         if (name)
360           channels = eina_list_append(channels, eina_stringshare_add(name));
361      }
362
363    return channels;
364 }
365
366 void
367 e_mixer_system_free_channels_names(Eina_List *channels_names)
368 {
369    const char *channel;
370
371    EINA_LIST_FREE(channels_names, channel)
372      eina_stringshare_del(channel);
373 }
374
375 const char *
376 e_mixer_system_get_default_channel_name(E_Mixer_System *self)
377 {
378    snd_mixer_elem_t *elem;
379    snd_mixer_selem_id_t *sid;
380
381    if (!self)
382      return NULL;
383
384    snd_mixer_selem_id_alloca(&sid);
385
386    elem = snd_mixer_first_elem(self);
387    for (; elem; elem = snd_mixer_elem_next(elem))
388      {
389         const char *name;
390         if ((!snd_mixer_selem_is_active(elem)) ||
391             (!snd_mixer_selem_has_playback_volume(elem)))
392           continue;
393
394         snd_mixer_selem_get_id(elem, sid);
395         name = snd_mixer_selem_id_get_name(sid);
396         if (name)
397           return eina_stringshare_add(name);
398      }
399
400    return NULL;
401 }
402
403 E_Mixer_Channel *
404 e_mixer_system_get_channel_by_name(E_Mixer_System *self,
405                                    const char     *name)
406 {
407    snd_mixer_elem_t *elem;
408    snd_mixer_selem_id_t *sid;
409
410    if ((!self) || (!name))
411      return NULL;
412
413    snd_mixer_selem_id_alloca(&sid);
414
415    elem = snd_mixer_first_elem(self);
416    for (; elem; elem = snd_mixer_elem_next(elem))
417      {
418         const char *n;
419         if ((!snd_mixer_selem_is_active(elem)) ||
420             (!snd_mixer_selem_has_playback_volume(elem)))
421           continue;
422
423         snd_mixer_selem_get_id(elem, sid);
424         n = snd_mixer_selem_id_get_name(sid);
425         if (n && (strcmp(n, name) == 0))
426           return elem;
427      }
428
429    return NULL;
430 }
431
432 void
433 e_mixer_system_channel_del(E_Mixer_Channel *channel __UNUSED__)
434 {
435 }
436
437 const char *
438 e_mixer_system_get_channel_name(E_Mixer_System  *self,
439                                 E_Mixer_Channel *channel)
440 {
441    snd_mixer_selem_id_t *sid;
442    const char *name;
443
444    if ((!self) || (!channel))
445      return NULL;
446
447    snd_mixer_selem_id_alloca(&sid);
448    snd_mixer_selem_get_id(channel, sid);
449    name = eina_stringshare_add(snd_mixer_selem_id_get_name(sid));
450
451    return name;
452 }
453
454 int
455 e_mixer_system_get_volume(E_Mixer_System  *self,
456                           E_Mixer_Channel *channel,
457                           int             *left,
458                           int             *right)
459 {
460    long lvol, rvol, range, min, max;
461
462    if ((!self) || (!channel) || (!left) || (!right))
463      return 0;
464
465    snd_mixer_handle_events(self);
466    snd_mixer_selem_get_playback_volume_range(channel, &min, &max);
467    range = max - min;
468    if (range < 1)
469      return 0;
470
471    if (snd_mixer_selem_has_playback_channel(channel, 0))
472      snd_mixer_selem_get_playback_volume(channel, 0, &lvol);
473    else
474      lvol = min;
475
476    if (snd_mixer_selem_has_playback_channel(channel, 1))
477      snd_mixer_selem_get_playback_volume(channel, 1, &rvol);
478    else
479      rvol = min;
480
481    if (snd_mixer_selem_is_playback_mono(channel) ||
482        snd_mixer_selem_has_playback_volume_joined(channel))
483      rvol = lvol;
484
485    *left = rint((double)(lvol - min) * 100 / (double)range);
486    *right = rint((double)(rvol - min) * 100 / (double)range);
487
488    return 1;
489 }
490
491 int
492 e_mixer_system_set_volume(E_Mixer_System  *self,
493                           E_Mixer_Channel *channel,
494                           int              left,
495                           int              right)
496 {
497    long range, min, max, div;
498    int mode;
499
500    if ((!self) || (!channel))
501      return 0;
502
503    snd_mixer_handle_events(self);
504    snd_mixer_selem_get_playback_volume_range(channel, &min, &max);
505    div = 100 + min;
506    if (div == 0)
507      {
508         div = 1; /* no zero-division */
509         min++;
510      }
511
512    range = max - min;
513    if (range < 1)
514      return 0;
515
516    mode = 0;
517    if (left >= 0)
518      {
519         left = (((range * left) + (range / 2)) / div) - min;
520         mode |= 1;
521      }
522
523    if (right >= 0)
524      {
525         right = (((range * right) + (range / 2)) / div) - min;
526         mode |= 2;
527      }
528
529    if (mode & 1)
530      snd_mixer_selem_set_playback_volume(channel, 0, left);
531
532    if ((!snd_mixer_selem_is_playback_mono(channel)) &&
533        (!snd_mixer_selem_has_playback_volume_joined(channel)) &&
534        (mode & 2))
535      {
536         if (snd_mixer_selem_has_playback_channel(channel, 1))
537           snd_mixer_selem_set_playback_volume(channel, 1, right);
538      }
539
540    return 1;
541 }
542
543 int
544 e_mixer_system_can_mute(E_Mixer_System  *self,
545                         E_Mixer_Channel *channel)
546 {
547    if ((!self) || (!channel))
548      return 0;
549
550    snd_mixer_handle_events(self);
551    return snd_mixer_selem_has_playback_switch(channel) ||
552           snd_mixer_selem_has_playback_switch_joined(channel);
553 }
554
555 int
556 e_mixer_system_get_mute(E_Mixer_System  *self,
557                         E_Mixer_Channel *channel,
558                         int             *mute)
559 {
560    if ((!self) || (!channel) || (!mute))
561      return 0;
562
563    snd_mixer_handle_events(self);
564    if (snd_mixer_selem_has_playback_switch(channel) ||
565        snd_mixer_selem_has_playback_switch_joined(channel))
566      {
567         int m;
568
569         /* XXX: not checking for return, always returns 0 even if it worked.
570          * alsamixer also don't check it. Bug?
571          */
572         snd_mixer_selem_get_playback_switch(channel, 0, &m);
573         *mute = !m;
574      }
575    else
576      *mute = 0;
577
578    return 1;
579 }
580
581 int
582 e_mixer_system_set_mute(E_Mixer_System  *self,
583                         E_Mixer_Channel *channel,
584                         int              mute)
585 {
586    if ((!self) || (!channel))
587      return 0;
588
589    snd_mixer_handle_events(self);
590    if (snd_mixer_selem_has_playback_switch(channel) ||
591        snd_mixer_selem_has_playback_switch_joined(channel))
592      return snd_mixer_selem_set_playback_switch_all(channel, !mute);
593    else
594      return 0;
595 }
596
597 int
598 e_mixer_system_get_state(E_Mixer_System        *self,
599                          E_Mixer_Channel       *channel,
600                          E_Mixer_Channel_State *state)
601 {
602    int r;
603
604    if (!state)
605      return 0;
606
607    r = e_mixer_system_get_mute(self, channel, &state->mute);
608    r &= e_mixer_system_get_volume(self, channel, &state->left, &state->right);
609    return r;
610 }
611
612 int
613 e_mixer_system_set_state(E_Mixer_System              *self,
614                          E_Mixer_Channel             *channel,
615                          const E_Mixer_Channel_State *state)
616 {
617    int r;
618
619    if (!state)
620      return 0;
621
622    r = e_mixer_system_set_mute(self, channel, state->mute);
623    r &= e_mixer_system_set_volume(self, channel, state->left, state->right);
624    return r;
625 }
626
627 int
628 e_mixer_system_has_capture(E_Mixer_System  *self,
629                            E_Mixer_Channel *channel)
630 {
631    if ((!self) || (!channel))
632      return 0;
633
634    return snd_mixer_selem_has_capture_switch(channel);
635 }
636