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