#include #include #include "e_mod_system.h" #include "e.h" struct e_mixer_callback_desc { int (*func)(void *data, E_Mixer_System *self); void *data; E_Mixer_System *self; Ecore_Idler *idler; Eina_List *handlers; }; static int _mixer_callback_add(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data); static int _mixer_callback_del(E_Mixer_System *self, struct e_mixer_callback_desc *desc); static Eina_Bool _cb_dispatch(void *data) { struct e_mixer_callback_desc *desc; int r; desc = data; snd_mixer_handle_events(desc->self); r = desc->func(desc->data, desc->self); desc->idler = NULL; if (!r) _mixer_callback_del(desc->self, desc); /* desc is invalid then. */ return ECORE_CALLBACK_CANCEL; } static Eina_Bool _cb_fd_handler(void *data, Ecore_Fd_Handler *fd_handler) { struct e_mixer_callback_desc *desc; desc = data; if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) { desc->handlers = eina_list_remove(desc->handlers, fd_handler); if (!desc->handlers) { E_Mixer_System *s; int (*f)(void *, E_Mixer_System *); void *d; s = desc->self; f = desc->func; d = desc->data; _mixer_callback_del(s, desc); _mixer_callback_add(s, f, d); } return ECORE_CALLBACK_CANCEL; } if (!desc->idler) desc->idler = ecore_idler_add(_cb_dispatch, desc); return ECORE_CALLBACK_RENEW; } static int _mixer_callback_add(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data) { struct e_mixer_callback_desc *desc; struct pollfd *pfds; int len; len = snd_mixer_poll_descriptors_count(self); if (len <= 0) return 0; desc = malloc(sizeof(struct e_mixer_callback_desc)); if (!desc) return 0; desc->func = func; desc->data = data; desc->self = self; desc->idler = NULL; desc->handlers = NULL; pfds = alloca(len * sizeof(struct pollfd)); len = snd_mixer_poll_descriptors(self, pfds, len); if (len <= 0) { free(desc); return 0; } while (len > 0) { Ecore_Fd_Handler *fd_handler; len--; fd_handler = ecore_main_fd_handler_add( pfds[len].fd, ECORE_FD_READ, _cb_fd_handler, desc, NULL, NULL); desc->handlers = eina_list_prepend(desc->handlers, fd_handler); } snd_mixer_set_callback_private(self, desc); return 1; } static int _mixer_callback_del(E_Mixer_System *self, struct e_mixer_callback_desc *desc) { Ecore_Fd_Handler *handler; EINA_LIST_FREE(desc->handlers, handler) ecore_main_fd_handler_del(handler); snd_mixer_set_callback_private(self, NULL); free(desc); return 1; } static int _mixer_callback_replace(E_Mixer_System *self __UNUSED__, struct e_mixer_callback_desc *desc, int (*func)(void *data, E_Mixer_System *self), void *data) { desc->func = func; desc->data = data; return 1; } E_Mixer_System * e_mixer_system_new(const char *name) { snd_mixer_t *handle; int err; if (!name) return NULL; err = snd_mixer_open(&handle, 0); if (err < 0) goto error_open; err = snd_mixer_attach(handle, name); if (err < 0) goto error_load; err = snd_mixer_selem_register(handle, NULL, NULL); if (err < 0) goto error_load; err = snd_mixer_load(handle); if (err < 0) goto error_load; return handle; error_load: snd_mixer_close(handle); error_open: fprintf(stderr, "MIXER: Cannot get hardware info: %s\n", snd_strerror(err)); return NULL; } void e_mixer_system_del(E_Mixer_System *self) { struct e_mixer_callback_desc *desc; if (!self) return; desc = snd_mixer_get_callback_private(self); if (desc) _mixer_callback_del(self, desc); snd_mixer_close(self); } int e_mixer_system_callback_set(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data) { struct e_mixer_callback_desc *desc; if (!self) return 0; desc = snd_mixer_get_callback_private(self); if (!desc) { if (func) return _mixer_callback_add(self, func, data); return 1; } else { if (func) return _mixer_callback_replace(self, desc, func, data); else return _mixer_callback_del(self, desc); } } Eina_List * e_mixer_system_get_cards(void) { int err, card_num; Eina_List *cards; cards = NULL; card_num = -1; while (((err = snd_card_next(&card_num)) == 0) && (card_num >= 0)) { snd_ctl_t *control; char buf[256]; snprintf(buf, sizeof(buf), "hw:%d", card_num); if (snd_ctl_open(&control, buf, 0) < 0) break; snd_ctl_close(control); cards = eina_list_append(cards, eina_stringshare_add(buf)); } if (err < 0) fprintf(stderr, "MIXER: Cannot get available card number: %s\n", snd_strerror(err)); return cards; } void e_mixer_system_free_cards(Eina_List *cards) { const char *card; EINA_LIST_FREE(cards, card) eina_stringshare_del(card); } const char * e_mixer_system_get_default_card(void) { static const char buf[] = "hw:0"; snd_ctl_t *control; if (snd_ctl_open(&control, buf, 0) < 0) return NULL; snd_ctl_close(control); return eina_stringshare_add(buf); } const char * e_mixer_system_get_card_name(const char *card) { snd_ctl_card_info_t *hw_info; const char *name; snd_ctl_t *control; int err; if (!card) return NULL; snd_ctl_card_info_alloca(&hw_info); err = snd_ctl_open(&control, card, 0); if (err < 0) return NULL; err = snd_ctl_card_info(control, hw_info); if (err < 0) { fprintf(stderr, "MIXER: Cannot get hardware info: %s: %s\n", card, snd_strerror(err)); snd_ctl_close(control); return NULL; } snd_ctl_close(control); name = snd_ctl_card_info_get_name(hw_info); if (!name) { fprintf(stderr, "MIXER: Cannot get hardware name: %s\n", card); return NULL; } return eina_stringshare_add(name); } Eina_List * e_mixer_system_get_channels(E_Mixer_System *self) { Eina_List *channels; snd_mixer_elem_t *elem; if (!self) return NULL; channels = NULL; elem = snd_mixer_first_elem(self); for (; elem; elem = snd_mixer_elem_next(elem)) { if ((!snd_mixer_selem_is_active(elem)) || (!snd_mixer_selem_has_playback_volume(elem))) continue; channels = eina_list_append(channels, elem); } return channels; } void e_mixer_system_free_channels(Eina_List *channels) { eina_list_free(channels); } Eina_List * e_mixer_system_get_channels_names(E_Mixer_System *self) { Eina_List *channels; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; if (!self) return NULL; channels = NULL; snd_mixer_selem_id_alloca(&sid); elem = snd_mixer_first_elem(self); for (; elem; elem = snd_mixer_elem_next(elem)) { const char *name; if ((!snd_mixer_selem_is_active(elem)) || (!snd_mixer_selem_has_playback_volume(elem))) continue; snd_mixer_selem_get_id(elem, sid); name = snd_mixer_selem_id_get_name(sid); if (name) channels = eina_list_append(channels, eina_stringshare_add(name)); } return channels; } void e_mixer_system_free_channels_names(Eina_List *channels_names) { const char *channel; EINA_LIST_FREE(channels_names, channel) eina_stringshare_del(channel); } const char * e_mixer_system_get_default_channel_name(E_Mixer_System *self) { snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; if (!self) return NULL; snd_mixer_selem_id_alloca(&sid); elem = snd_mixer_first_elem(self); for (; elem; elem = snd_mixer_elem_next(elem)) { const char *name; if ((!snd_mixer_selem_is_active(elem)) || (!snd_mixer_selem_has_playback_volume(elem))) continue; snd_mixer_selem_get_id(elem, sid); name = snd_mixer_selem_id_get_name(sid); if (name) return eina_stringshare_add(name); } return NULL; } E_Mixer_Channel * e_mixer_system_get_channel_by_name(E_Mixer_System *self, const char *name) { snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; if ((!self) || (!name)) return NULL; snd_mixer_selem_id_alloca(&sid); elem = snd_mixer_first_elem(self); for (; elem; elem = snd_mixer_elem_next(elem)) { const char *n; if ((!snd_mixer_selem_is_active(elem)) || (!snd_mixer_selem_has_playback_volume(elem))) continue; snd_mixer_selem_get_id(elem, sid); n = snd_mixer_selem_id_get_name(sid); if (n && (strcmp(n, name) == 0)) return elem; } return NULL; } void e_mixer_system_channel_del(E_Mixer_Channel *channel __UNUSED__) { } const char * e_mixer_system_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel) { snd_mixer_selem_id_t *sid; const char *name; if ((!self) || (!channel)) return NULL; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_get_id(channel, sid); name = eina_stringshare_add(snd_mixer_selem_id_get_name(sid)); return name; } int e_mixer_system_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right) { long lvol, rvol, range, min, max; if ((!self) || (!channel) || (!left) || (!right)) return 0; snd_mixer_handle_events(self); snd_mixer_selem_get_playback_volume_range(channel, &min, &max); range = max - min; if (range < 1) return 0; if (snd_mixer_selem_has_playback_channel(channel, 0)) snd_mixer_selem_get_playback_volume(channel, 0, &lvol); else lvol = min; if (snd_mixer_selem_has_playback_channel(channel, 1)) snd_mixer_selem_get_playback_volume(channel, 1, &rvol); else rvol = min; if (snd_mixer_selem_is_playback_mono(channel) || snd_mixer_selem_has_playback_volume_joined(channel)) rvol = lvol; *left = rint((double)(lvol - min) * 100 / (double)range); *right = rint((double)(rvol - min) * 100 / (double)range); return 1; } int e_mixer_system_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right) { long range, min, max, divide; int mode; if ((!self) || (!channel)) return 0; snd_mixer_handle_events(self); snd_mixer_selem_get_playback_volume_range(channel, &min, &max); divide = 100 + min; if (divide == 0) { divide = 1; /* no zero-division */ min++; } range = max - min; if (range < 1) return 0; mode = 0; if (left >= 0) { left = (((range * left) + (range / 2)) / divide) - min; mode |= 1; } if (right >= 0) { right = (((range * right) + (range / 2)) / divide) - min; mode |= 2; } if (mode & 1) snd_mixer_selem_set_playback_volume(channel, 0, left); if ((!snd_mixer_selem_is_playback_mono(channel)) && (!snd_mixer_selem_has_playback_volume_joined(channel)) && (mode & 2)) { if (snd_mixer_selem_has_playback_channel(channel, 1)) snd_mixer_selem_set_playback_volume(channel, 1, right); } return 1; } int e_mixer_system_can_mute(E_Mixer_System *self, E_Mixer_Channel *channel) { if ((!self) || (!channel)) return 0; snd_mixer_handle_events(self); return snd_mixer_selem_has_playback_switch(channel) || snd_mixer_selem_has_playback_switch_joined(channel); } int e_mixer_system_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int *mute) { if ((!self) || (!channel) || (!mute)) return 0; snd_mixer_handle_events(self); if (snd_mixer_selem_has_playback_switch(channel) || snd_mixer_selem_has_playback_switch_joined(channel)) { int m; /* XXX: not checking for return, always returns 0 even if it worked. * alsamixer also don't check it. Bug? */ snd_mixer_selem_get_playback_switch(channel, 0, &m); *mute = !m; } else *mute = 0; return 1; } int e_mixer_system_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int mute) { if ((!self) || (!channel)) return 0; snd_mixer_handle_events(self); if (snd_mixer_selem_has_playback_switch(channel) || snd_mixer_selem_has_playback_switch_joined(channel)) return snd_mixer_selem_set_playback_switch_all(channel, !mute); else return 0; } int e_mixer_system_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state) { int r; if (!state) return 0; r = e_mixer_system_get_mute(self, channel, &state->mute); r &= e_mixer_system_get_volume(self, channel, &state->left, &state->right); return r; } int e_mixer_system_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state) { int r; if (!state) return 0; r = e_mixer_system_set_mute(self, channel, state->mute); r &= e_mixer_system_set_volume(self, channel, state->left, state->right); return r; } int e_mixer_system_has_capture(E_Mixer_System *self, E_Mixer_Channel *channel) { if ((!self) || (!channel)) return 0; return snd_mixer_selem_has_capture_switch(channel); }