From 1dd1509a02fee564ff87f80c2f29055d7aad889c Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Fri, 11 May 2018 01:18:43 +0200 Subject: Initial version with softvol control and DMIX --- plugins/alsa/alsa-ctl.c | 399 ------------------------------------------------ 1 file changed, 399 deletions(-) delete mode 100644 plugins/alsa/alsa-ctl.c (limited to 'plugins/alsa/alsa-ctl.c') diff --git a/plugins/alsa/alsa-ctl.c b/plugins/alsa/alsa-ctl.c deleted file mode 100644 index b469e94..0000000 --- a/plugins/alsa/alsa-ctl.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright(C) 2018 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http : //www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License - -for the specific language governing permissions and - * limitations under the License. - * - * reference : - * https://github.com/zonque/simple-alsa-loop/blob/master/loop.c - * https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html#a31 - * - */ - -#define _GNU_SOURCE // needed for vasprintf - -#include "alsa-softmixer.h" -#include -#include - -typedef struct { - AFB_ApiT api; - sd_event_source* evtsrc; - pthread_t thread; - int tid; - char* info; - snd_ctl_t *ctlDev; - sd_event *sdLoop; -} SubscribeHandleT; - -typedef struct { - snd_pcm_t *pcm[MAX_AUDIO_STREAMS+1]; - int numid[MAX_AUDIO_STREAMS+1]; - int count; -} AudioStreamHandleT; - -static AudioStreamHandleT AudioStreamHandle; - -// Clone of AlsaLib snd_card_load2 static function - -snd_ctl_card_info_t *AlsaCtlGetInfo(CtlSourceT *source, const char *devid) { - int error; - snd_ctl_t *ctlDev; - - if (devid) goto OnErrorExit; - - if ((error = snd_ctl_open(&ctlDev, devid, SND_CTL_READONLY)) < 0) { - devid = "Not Defined"; - goto OnErrorExit; - } - - snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof()); - if ((error = snd_ctl_card_info(ctlDev, cardInfo)) < 0) { - goto OnErrorExit; - } - return cardInfo; - -OnErrorExit: - AFB_ApiError(source->api, "AlsaCtlGetInfo: fail to find sndcard by id= %s", devid); - return NULL; -} - -PUBLIC snd_ctl_t *AlsaCtlOpenCtl(CtlSourceT *source, const char *devid) { - int error; - snd_ctl_t *ctlDev; - - if (devid) goto OnErrorExit; - - if ((error = snd_ctl_open(&ctlDev, devid, SND_CTL_READONLY)) < 0) { - devid = "Not Defined"; - goto OnErrorExit; - } - - return ctlDev; - -OnErrorExit: - AFB_ApiError(source->api, "AlsaCtlOpenCtl: fail to find sndcard by id= %s", devid); - return NULL; -} - -STATIC int CtlElemIdGetInt (AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long *value) { - int error; - snd_ctl_elem_value_t *elemData; - snd_ctl_elem_info_t *elemInfo; - - snd_ctl_elem_info_alloca(&elemInfo); - snd_ctl_elem_info_set_id(elemInfo, elemId); - if (snd_ctl_elem_info(ctlDev, elemInfo) < 0) goto OnErrorExit; - if (!snd_ctl_elem_info_is_readable(elemInfo)) goto OnErrorExit; - - // as we have static rate/channel we should have only one boolean as value - snd_ctl_elem_type_t elemType = snd_ctl_elem_info_get_type(elemInfo); - int count = snd_ctl_elem_info_get_count(elemInfo); - if (count != 1) goto OnErrorExit; - - snd_ctl_elem_value_alloca(&elemData); - snd_ctl_elem_value_set_id(elemData, elemId); - error = snd_ctl_elem_read(ctlDev, elemData); - if (error) goto OnSuccessExit; - - // value=1 when active and 0 when not active - *value = snd_ctl_elem_value_get_integer(elemData, 0); - -OnSuccessExit: - return 0; - -OnErrorExit: - - AFB_ApiWarning(api, "CtlSubscribeEventCB: ignored unsupported event Numid=%i", snd_ctl_elem_info_get_numid(elemInfo)); - for (int idx = 0; idx < count; idx++) { - long valueL; - - switch (elemType) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - valueL = snd_ctl_elem_value_get_boolean(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetBool: value=%ld", valueL); - break; - case SND_CTL_ELEM_TYPE_INTEGER: - valueL = snd_ctl_elem_value_get_integer(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetInt: value=%ld", valueL); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - valueL = snd_ctl_elem_value_get_integer64(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetInt64: value=%ld", valueL); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - valueL = snd_ctl_elem_value_get_enumerated(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetEnum: value=%ld", valueL); - break; - case SND_CTL_ELEM_TYPE_BYTES: - valueL = snd_ctl_elem_value_get_byte(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetByte: value=%ld", valueL); - break; - case SND_CTL_ELEM_TYPE_IEC958: - default: - AFB_ApiNotice(api, "CtlElemIdGetInt: Unsupported type=%d", elemType); - break; - } - } - return -1; -} - -STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) { - int error; - SubscribeHandleT *subscribeHandle = (SubscribeHandleT*) userData; - snd_ctl_event_t *eventId; - snd_ctl_elem_id_t *elemId; - snd_ctl_elem_info_t *elemInfo; - long value; - - if ((revents & EPOLLHUP) != 0) { - AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB hanghup [card:%s disconnected]", subscribeHandle->info); - goto OnSuccessExit; - } - - if ((revents & EPOLLIN) == 0) goto OnSuccessExit; - - // initialise event structure on stack - snd_ctl_event_alloca(&eventId); - snd_ctl_elem_id_alloca(&elemId); - - error = snd_ctl_read(subscribeHandle->ctlDev, eventId); - if (error < 0) goto OnErrorExit; - - // we only process sndctrl element - if (snd_ctl_event_get_type(eventId) != SND_CTL_EVENT_ELEM) goto OnSuccessExit; - - // we only process value changed events - unsigned int eventMask = snd_ctl_event_elem_get_mask(eventId); - if (!(eventMask & SND_CTL_EVENT_MASK_VALUE)) goto OnSuccessExit; - - // extract element from event and get value - snd_ctl_event_elem_get_id(eventId, elemId); - error= CtlElemIdGetInt (subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value); - if (error) goto OnErrorExit; - - // Fulup we have to reload info as elemId from event does not hold numid - snd_ctl_elem_info_alloca(&elemInfo); - snd_ctl_elem_info_set_id(elemInfo, elemId); - if (snd_ctl_elem_info(subscribeHandle->ctlDev, elemInfo) < 0) goto OnErrorExit; - int numid= snd_ctl_elem_info_get_numid(elemInfo); - - - for (int idx=0; idx < AudioStreamHandle.count; idx++) { - if (AudioStreamHandle.numid[idx] == numid) { - const char *pcmName = snd_pcm_name(AudioStreamHandle.pcm[idx]); - AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d pcm=%s pause=%d numid=%d", subscribeHandle->info, subscribeHandle->tid, pcmName, !value, numid); - snd_pcm_pause(AudioStreamHandle.pcm[idx], !value); - break; - } - } - -OnSuccessExit: - return 0; - -OnErrorExit: - AFB_ApiWarning(subscribeHandle->api, "CtlSubscribeEventCB: ignored unsupported event"); - return 0; -} - -static void *LoopInThread(void *handle) { - SubscribeHandleT *subscribeHandle = (SubscribeHandleT*) handle; - int count = 0; - int watchdog = MAINLOOP_WATCHDOG * 1000; - subscribeHandle->tid = (int) syscall(SYS_gettid); - - AFB_ApiNotice(subscribeHandle->api, "LoopInThread:%s/%d Started", subscribeHandle->info, subscribeHandle->tid); - - /* loop until end */ - for (;;) { - int res = sd_event_run(subscribeHandle->sdLoop, watchdog); - if (res == 0) { - AFB_ApiNotice(subscribeHandle->api, "LoopInThread:%s/%d Idle count=%d", subscribeHandle->info, subscribeHandle->tid, count++); - continue; - } - if (res < 0) { - AFB_ApiError(subscribeHandle->api, "LoopInThread:%s/%d ERROR=%i Exit errno=%s", subscribeHandle->info, subscribeHandle->tid, res, strerror(res)); - break; - } - } - pthread_exit(0); -} - -PUBLIC snd_ctl_t* AlsaCrlFromPcm(CtlSourceT *source, snd_pcm_t *pcm) { - char buffer[32]; - int error; - snd_ctl_t *ctlDev; - snd_pcm_info_t *pcmInfo; - - snd_pcm_info_alloca(&pcmInfo); - if ((error = snd_pcm_info(pcm, pcmInfo)) < 0) goto OnErrorExit; - - int pcmCard = snd_pcm_info_get_card(pcmInfo); - snprintf(buffer, sizeof(buffer), "hw:%i", pcmCard); - if ((error = snd_ctl_open(&ctlDev, buffer, SND_CTL_READONLY)) < 0) goto OnErrorExit; - - return ctlDev; - -OnErrorExit: - return NULL; -} - -PUBLIC snd_ctl_elem_id_t * AlsaCtlGetElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) { - char string[32]; - int error; - int index; - snd_ctl_elem_list_t *ctlList = NULL; - snd_ctl_elem_id_t *elemId; - - snd_ctl_elem_list_alloca(&ctlList); - - if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) { - AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve controls", AlsaCtlUID (ctlDev, string)); - goto OnErrorExit; - } - - if ((error = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) { - AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve count", AlsaCtlUID (ctlDev, string)); - goto OnErrorExit; - } - - // Fulup: do not understand why snd_ctl_elem_list should be call twice to get a valid ctlCount - if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) { - AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve controls", AlsaCtlUID (ctlDev, string)); - goto OnErrorExit; - } - - // loop on control to find the right one - int ctlCount = snd_ctl_elem_list_get_used(ctlList); - for (index = 0; index < ctlCount; index++) { - - if (numid == snd_ctl_elem_list_get_numid(ctlList, index)) { - snd_ctl_elem_id_malloc(&elemId); - snd_ctl_elem_list_get_id(ctlList, index, elemId); - break; - } - } - - if (index == ctlCount) { - AFB_ApiError(source->api, "AlsaCtlRegister [%s] fail get numid=%i count", AlsaCtlUID (ctlDev, string), numid); - goto OnErrorExit; - } - - // clear ctl list and return elemid - snd_ctl_elem_list_clear(ctlList); - return elemId; - -OnErrorExit: - if (ctlList) snd_ctl_elem_list_clear(ctlList); - return NULL; -} - - -PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) { - int error; - char string [32]; - struct pollfd pfds; - SubscribeHandleT *subscribeHandle = malloc(sizeof (SubscribeHandleT)); - - subscribeHandle->api = source->api; - subscribeHandle->ctlDev = ctlDev; - subscribeHandle->info = "ctlEvt"; - - // subscribe for sndctl events attached to devid - if ((error = snd_ctl_subscribe_events(ctlDev, 1)) < 0) { - AFB_ApiError(source->api, "AlsaCtlSubscribe: fail sndcard=%s to subscribe events", AlsaCtlUID(ctlDev, string)); - goto OnErrorExit; - } - - // get pollfd attach to this sound board - int count = snd_ctl_poll_descriptors(ctlDev, &pfds, 1); - if (count != 1) { - AFB_ApiError(source->api, "AlsaCtlSubscribe: fail sndcard=%s get poll descriptors", AlsaCtlUID(ctlDev, string)); - goto OnErrorExit; - } - - // add poll descriptor to AGL systemd mainloop - if ((error = sd_event_new(&subscribeHandle->sdLoop)) < 0) { - fprintf(stderr, "AlsaCtlSubscribe: fail sndcard=%s creating a new loop", AlsaCtlUID(ctlDev, string)); - goto OnErrorExit; - } - - // register sound event to binder main loop - if ((error = sd_event_add_io(subscribeHandle->sdLoop, &subscribeHandle->evtsrc, pfds.fd, EPOLLIN, CtlSubscribeEventCB, subscribeHandle)) < 0) { - AFB_ApiError(source->api, "AlsaCtlSubscribe: Fail sndcard=%s adding mainloop", AlsaCtlUID(ctlDev, string)); - goto OnErrorExit; - } - - // start a thread with a mainloop to monitor Audio-Agent - if ((error = pthread_create(&subscribeHandle->thread, NULL, &LoopInThread, subscribeHandle)) < 0) { - AFB_ApiError(source->api, "AlsaCtlSubscribe: Fail sndcard=%s create waiting thread err=%d", AlsaCtlUID(ctlDev, string), error); - goto OnErrorExit; - } - - return 0; - -OnErrorExit: - return -1; -} - -PUBLIC int AlsaCtlRegister(CtlSourceT *source, snd_pcm_t *pcm, int numid) { - long value; - int error; - - // NumID are attached to sndcard retrieve ctldev from PCM - snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, pcm); - if (!ctlDev) { - AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", snd_pcm_name(pcm)); - goto OnErrorExit; - } - - // This is the first registration let's subscrive to event - if (AudioStreamHandle.count == 0) { - AlsaCtlSubscribe (source, ctlDev); - } - - snd_ctl_elem_id_t *elemId= AlsaCtlGetElemId(source, ctlDev, numid); - if (!elemId) { - AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail to find numid=%d", snd_pcm_name(pcm), numid); - goto OnErrorExit; - } - - if ((error = CtlElemIdGetInt (source->api, ctlDev, elemId, &value)) != 0) { - AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail to get numid=%d value", snd_pcm_name(pcm), numid); - goto OnErrorExit; - } - - AFB_ApiNotice(source->api, "AlsaCtlRegister [pcm=%s] numid=%d value=%ld", snd_pcm_name(pcm), numid, value); - - // store PCM in order to pause/resume depending on event - AudioStreamHandle.pcm[AudioStreamHandle.count] = pcm; - AudioStreamHandle.numid[AudioStreamHandle.count] = numid; - AudioStreamHandle.count++; - - // we only need to keep ctldev open for initial registration - if (AudioStreamHandle.count >1) snd_ctl_close(ctlDev); - - // toggle pause/resume (should be done after pcm_start) - if ((error = snd_pcm_pause(pcm, !value)) < 0) { - AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail to pause", snd_pcm_name(pcm)); - goto OnErrorExit; - } - - return 0; - -OnErrorExit: - - return -1; -} -- cgit 1.2.3-korg