diff options
author | Jari Vetoniemi <mailroxas@gmail.com> | 2018-10-31 08:16:29 +0200 |
---|---|---|
committer | Jari Vetoniemi <mailroxas@gmail.com> | 2018-10-31 08:16:29 +0200 |
commit | c7e59084ac09028af7fd81c9fc40f9c55fb30857 (patch) | |
tree | 0704ac0e10805442e93b8b1eb66bb3eb77d16a20 /libasound.c | |
parent | 484412b8704c147b7f343a19fefae44757e186b1 (diff) |
make alsamixer show controls
Diffstat (limited to 'libasound.c')
-rw-r--r-- | libasound.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/libasound.c b/libasound.c index c5662df..c39f4b6 100644 --- a/libasound.c +++ b/libasound.c @@ -15,6 +15,7 @@ #include <poll.h> #include "dsp/dsp.h" +#include "sysex.h" #define WARN1(x) do { warn("asound: %s %s", __func__, x); } while (0) #define WARN(x, ...) do { warn("asound: %s " x, __func__, ##__VA_ARGS__); } while (0) @@ -1036,4 +1037,328 @@ snd_pcm_get_chmap(snd_pcm_t *pcm) return map; } +struct _snd_ctl { + char noop; +}; + +int +snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode) +{ + if (!(*ctl = calloc(1, sizeof(**ctl)))) { + WARNX1("calloc"); + return -1; + } + + return 0; +} + +int +snd_ctl_close(snd_ctl_t *ctl) +{ + free(ctl); + return 0; +} + +struct _snd_ctl_card_info { + const char *name; +}; + +size_t +snd_ctl_card_info_sizeof(void) +{ + return sizeof(snd_ctl_card_info_t); +} + +int +snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr) +{ + if (!(*ptr = calloc(1, sizeof(**ptr)))) + return -1; + + return 0; +} + +void +snd_ctl_card_info_free(snd_ctl_card_info_t *obj) +{ + free(obj); +} + +int +snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) +{ + info->name = "sndio"; + return 0; +} + +const char* +snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj) +{ + return obj->name; +} + +const char* +snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj) +{ + return obj->name; +} + +struct _snd_mixer_elem { + snd_mixer_elem_t *next; + char name[SYSEX_NAMELEN]; + unsigned int index, vol; +}; + +struct _snd_mixer { + struct mio_hdl *hdl; + snd_mixer_elem_t *controls; +}; + +#define MIXER_MAX_CHANNELS 16 + +static bool +onsysex(unsigned char *buf, unsigned len, snd_mixer_elem_t controls[MIXER_MAX_CHANNELS + 1]) +{ + if (len < SYSEX_SIZE(empty)) + return false; + + union { + struct sysex x; + unsigned char buf[sizeof(struct sysex)]; + } u; + + memcpy(u.buf, buf, len); + + if (u.x.type == SYSEX_TYPE_RT && u.x.id0 == SYSEX_CONTROL && u.x.id1 == SYSEX_MASTER) { + if (len == SYSEX_SIZE(master)) { + controls[0].vol = u.x.u.master.coarse; + snprintf(controls[0].name, sizeof(controls[0].name), "master"); + } + return false; + } + + if (u.x.type != SYSEX_TYPE_EDU || u.x.id0 != SYSEX_AUCAT) + return false; + + switch (u.x.id1) { + case SYSEX_AUCAT_MIXINFO: { + unsigned cn = u.x.u.mixinfo.chan; + if (cn >= MIXER_MAX_CHANNELS || !memchr(u.x.u.mixinfo.name, '\0', SYSEX_NAMELEN)) + return false; + + snprintf(controls[cn + 1].name, sizeof(controls[cn + 1].name), "%s", u.x.u.mixinfo.name); + break; + } + + case SYSEX_AUCAT_DUMPEND: + return true; + } + + return false; +} + +static void +oncommon(unsigned char *buf, unsigned len, snd_mixer_elem_t controls[MIXER_MAX_CHANNELS + 1]) +{ +#define MIDI_CMDMASK 0xf0 +#define MIDI_CTL 0xb0 +#define MIDI_CTLVOL 7 + if ((buf[0] & MIDI_CMDMASK) != MIDI_CTL || buf[1] != MIDI_CTLVOL) + return; + +#define MIDI_CHANMASK 0x0f + unsigned cn = (buf[0] & MIDI_CHANMASK); + if (cn >= MIXER_MAX_CHANNELS) + return; + + controls[cn + 1].vol = buf[2]; +} + +static void +get_controls(snd_mixer_t *mixer) +{ + const unsigned char dumpreq[] = { + SYSEX_START, + SYSEX_TYPE_EDU, + 0, + SYSEX_AUCAT, + SYSEX_AUCAT_DUMPREQ, + SYSEX_END, + }; + + static snd_mixer_elem_t controls[MIXER_MAX_CHANNELS + 1]; + memset(controls, 0, sizeof(controls)); + + unsigned char buf[0x100]; + mio_write(mixer->hdl, dumpreq, sizeof(dumpreq)); + for (int ready = 0; !ready;) { + size_t size; + if (!(size = mio_read(mixer->hdl, buf, sizeof(buf)))) { + WARNX1("mio_read failed"); + return; + } + + static unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 }; + static unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 }; + + unsigned char mmsg[0x100]; + unsigned int mst = 0, midx = 0, mlen = 0; + for (unsigned char *p = buf; size > 0; --size, ++p) { + if (*p >= 0xf8) { + /* clock events */ + } else if (*p >= 0xf0) { + if (mst == SYSEX_START && *p == SYSEX_END && midx < sizeof(mmsg)) { + mmsg[midx++] = *p; + ready = onsysex(mmsg, midx, controls); + continue; + } + + mmsg[0] = *p; + mlen = common_len[*p & 7]; + mst = *p; + midx = 1; + } else if (*p >= 0x80) { + mmsg[0] = *p; + mlen = voice_len[(*p >> 4) & 7]; + mst = *p; + midx = 1; + } else if (mst) { + if (midx == sizeof(mmsg)) + continue; + + if (midx == 0) + mmsg[midx++] = mst; + + mmsg[midx++] = *p; + + if (midx == mlen) { + oncommon(mmsg, midx, controls); + midx = 0; + } + } + } + } + + snd_mixer_elem_t **tail = &mixer->controls; + for (size_t i = 0; i < ARRAY_SIZE(controls); ++i) { + if (!controls[i].name[0]) + continue; + + *tail = &controls[i]; + tail = &(*tail)->next; + } +} + +int +snd_mixer_open(snd_mixer_t **mixer, int mode) +{ + if (!(*mixer = calloc(1, sizeof(**mixer)))) { + WARNX1("calloc"); + return -1; + } + + if (!((*mixer)->hdl = mio_open("snd/0", MIO_OUT | MIO_IN, 0))) { + WARNX1("mio_open failed"); + goto fail; + } + + get_controls(*mixer); + return 0; + +fail: + free(*mixer); + return -1; +} + +int +snd_mixer_close(snd_mixer_t *mixer) +{ + mio_close(mixer->hdl); + free(mixer); + return 0; +} + +struct _snd_mixer_selem_id { + char noop; +}; + +int +snd_mixer_selem_id_malloc(snd_mixer_selem_id_t **ptr) +{ + if (!(*ptr = calloc(1, sizeof(**ptr)))) + return -1; + + return 0; +} + +void +snd_mixer_selem_id_free(snd_mixer_selem_id_t *obj) +{ + free(obj); +} + +snd_mixer_elem_t* +snd_mixer_first_elem(snd_mixer_t *mixer) +{ + return mixer->controls; +} + +snd_mixer_elem_t* +snd_mixer_elem_next(snd_mixer_elem_t *elem) +{ + return elem->next; +} + +const char* +snd_mixer_selem_get_name(snd_mixer_elem_t *elem) +{ + return elem->name; +} + +int +snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) +{ + if (value) *value = ((float)elem->vol / (float)0x7f) * 100; + return 0; +} + +int +snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value) +{ + if (value) *value = (long)elem->vol - 0x7f; + return 0; +} + +int +snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem, long *min, long *max) +{ + if (max) *max = 0; + if (min) *min = -127; + return 0; +} + +int +snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem) +{ + return true; +} + +int +snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem) +{ + return true; +} + +int +snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem) +{ + return true; +} + +int +snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem) +{ + return true; +} + #include "symversioning-hell.h" |