diff options
author | Fulup Ar Foll <fulup@iot.bzh> | 2018-05-11 01:18:43 +0200 |
---|---|---|
committer | Fulup Ar Foll <fulup@iot.bzh> | 2018-05-11 01:18:43 +0200 |
commit | 1dd1509a02fee564ff87f80c2f29055d7aad889c (patch) | |
tree | 031a0a9e912fa3f9ab94a268ad6058d774e6f8dd /plugins/alsa | |
parent | e904b7da51297b0417df31ab79568c3f1243fb64 (diff) |
Initial version with softvol control and DMIX
Diffstat (limited to 'plugins/alsa')
-rw-r--r-- | plugins/alsa/alsa-capture.c | 80 | ||||
-rw-r--r-- | plugins/alsa/alsa-core-ctl.c (renamed from plugins/alsa/alsa-ctl.c) | 264 | ||||
-rw-r--r-- | plugins/alsa/alsa-core-pcm.c (renamed from plugins/alsa/alsa-pcm.c) | 183 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-dmix.c (renamed from plugins/alsa/alsa-dmix.c) | 54 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-vol.c | 77 | ||||
-rw-r--r-- | plugins/alsa/alsa-softmixer.c | 95 | ||||
-rw-r--r-- | plugins/alsa/alsa-softmixer.h | 84 | ||||
-rw-r--r-- | plugins/alsa/alsa-utils-bypath.c | 75 | ||||
-rw-r--r-- | plugins/alsa/alsa-utils-dump.c | 2 |
9 files changed, 472 insertions, 442 deletions
diff --git a/plugins/alsa/alsa-capture.c b/plugins/alsa/alsa-capture.c deleted file mode 100644 index 992f731..0000000 --- a/plugins/alsa/alsa-capture.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 "IoT.bzh" - * Author Fulup Ar Foll <fulup@iot.bzh> - * - * 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. - * - */ - -#define _GNU_SOURCE // needed for vasprintf - -#include "alsa-softmixer.h" - -PUBLIC snd_pcm_t* AlsaCreateCapture(CtlSourceT *source, const char* sndDevPath, unsigned int deviceIdx, unsigned int subpcmNamex, unsigned int channelCount) { - char pcmName[32]; - int error; - snd_pcm_t *pcmHandle; - snd_pcm_hw_params_t *pcmParams; - - AFB_ApiNotice(source->api, "AlsaCreateCapture: start "); - - // get card info from /dev/snd/xxx - snd_ctl_card_info_t *cardInfo = AlsaByPathInfo(source, sndDevPath); - - if (!cardInfo) { - AFB_ApiError(source->api,"AlsaCreateCapture: fail to find sndcard by path= %s", sndDevPath); - goto OnErrorExit; - } - - // extract useful info from cardInfo handle - int cardIdx = snd_ctl_card_info_get_card(cardInfo); - const char *cardName = snd_ctl_card_info_get_name(cardInfo); - - - // build a valid name and open sndcard - snprintf(pcmName, sizeof (pcmName), "hw:%i,%i,%i", cardIdx, deviceIdx, subpcmNamex); - error= snd_pcm_open(&pcmHandle, pcmName, SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC); - if (error) { - AFB_ApiError(source->api,"AlsaCreateCapture: fail openpcm (hw:%d -> %s pcmNamex=%i subpcmNamex=%d): %s" - , cardIdx, cardName, deviceIdx, subpcmNamex, snd_strerror(error)); - goto OnErrorExit; - } - - // set default to param object - snd_pcm_hw_params_alloca(&pcmParams); - snd_pcm_hw_params_any(pcmHandle, pcmParams); - - - error= snd_pcm_hw_params_set_channels (pcmHandle, pcmParams, channelCount); - if (error) { - AFB_ApiError(source->api,"lsaCreateCapture: fail set channel count (hw:%d -> %s pcmNamex=%i subpcmNamex=%d channelCount=%d): %s" - , cardIdx, cardName, deviceIdx, subpcmNamex, channelCount, snd_strerror(error)); - goto OnErrorExit; - } - - // push selected params to PCM - error= snd_pcm_hw_params(pcmHandle, pcmParams); - if (error) { - AFB_ApiError(source->api,"lsaCreateCapture: fail pushing params (hw:%d -> %s pcmNamex=%i subpcmNamex=%d): %s" - , cardIdx, cardName, deviceIdx, subpcmNamex, snd_strerror(error)); - goto OnErrorExit; - } - - AFB_ApiNotice(source->api, "AlsaCreateCapture: done"); - free (cardInfo); - return NULL; - -OnErrorExit: - AFB_ApiNotice(source->api, "AlsaCreateCapture: OnErrorExit"); - return NULL; -}
\ No newline at end of file diff --git a/plugins/alsa/alsa-ctl.c b/plugins/alsa/alsa-core-ctl.c index b469e94..5944fb8 100644 --- a/plugins/alsa/alsa-ctl.c +++ b/plugins/alsa/alsa-core-ctl.c @@ -39,37 +39,68 @@ typedef struct { } SubscribeHandleT; typedef struct { - snd_pcm_t *pcm[MAX_AUDIO_STREAMS+1]; - int numid[MAX_AUDIO_STREAMS+1]; + AlsaPcmInfoT *pcm; + int numid; +} SubStreamT; + +typedef struct { + SubStreamT stream[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) { +STATIC snd_ctl_elem_id_t *AlsaCtlGetElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) { + char string[32]; int error; - snd_ctl_t *ctlDev; + int index; + snd_ctl_elem_list_t *ctlList = NULL; + snd_ctl_elem_id_t *elemId; - if (devid) goto OnErrorExit; + snd_ctl_elem_list_alloca(&ctlList); - if ((error = snd_ctl_open(&ctlDev, devid, SND_CTL_READONLY)) < 0) { - devid = "Not Defined"; + if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) { + AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string)); goto OnErrorExit; } - snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof()); - if ((error = snd_ctl_card_info(ctlDev, cardInfo)) < 0) { + 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", ALSA_CTL_UID(ctlDev, string)); goto OnErrorExit; } - return cardInfo; + + // 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", ALSA_CTL_UID(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", ALSA_CTL_UID(ctlDev, string), numid); + goto OnErrorExit; + } + + // clear ctl list and return elemid + snd_ctl_elem_list_clear(ctlList); + return elemId; OnErrorExit: - AFB_ApiError(source->api, "AlsaCtlGetInfo: fail to find sndcard by id= %s", devid); + if (ctlList) snd_ctl_elem_list_clear(ctlList); return NULL; } + PUBLIC snd_ctl_t *AlsaCtlOpenCtl(CtlSourceT *source, const char *devid) { int error; snd_ctl_t *ctlDev; @@ -88,7 +119,23 @@ OnErrorExit: return NULL; } -STATIC int CtlElemIdGetInt (AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long *value) { +STATIC int CtlElemIdGetNumid(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, int *numid) { + 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; + + *numid = snd_ctl_elem_info_get_numid(elemInfo); + + return 0; + +OnErrorExit: + return -1; +} + +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; @@ -115,7 +162,7 @@ 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; @@ -123,23 +170,23 @@ OnErrorExit: switch (elemType) { case SND_CTL_ELEM_TYPE_BOOLEAN: valueL = snd_ctl_elem_value_get_boolean(elemData, idx); - AFB_ApiNotice(api, "CtlElemIdGetBool: value=%ld", valueL); + 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); + 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); + 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); + 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); + AFB_ApiNotice(api, "CtlElemIdGetByte: value=%ld", valueL); break; case SND_CTL_ELEM_TYPE_IEC958: default: @@ -150,12 +197,53 @@ OnErrorExit: return -1; } -STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) { +// Clone of AlsaLib snd_card_load2 static function +PUBLIC 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 int AlsaCtlGetNumidValueI(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value) { + + snd_ctl_elem_id_t *elemId = AlsaCtlGetElemId(source, ctlDev, numid); + if (!elemId) { + AFB_ApiError(source->api, "AlsaCtlGetNumValueI [sndcard=%s] fail to find numid=%d", snd_ctl_name(ctlDev), numid); + goto OnErrorExit; + } + + int error = CtlElemIdGetInt(source->api, ctlDev, elemId, value); + if (error) { + AFB_ApiError(source->api, "AlsaCtlGetNumValueI [sndcard=%s] fail to get numid=%d value", snd_ctl_name(ctlDev), numid); + goto OnErrorExit; + } + + return 0; +OnErrorExit: + return -1; +} + +STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) { + int error, numid; 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) { @@ -181,21 +269,17 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v // extract element from event and get value snd_ctl_event_elem_get_id(eventId, elemId); - error= CtlElemIdGetInt (subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value); + error = CtlElemIdGetInt(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value); + if (error) goto OnErrorExit; + + error = CtlElemIdGetNumid(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &numid); 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]); + for (int idx = 0; idx < AudioStreamHandle.count; idx++) { + if (AudioStreamHandle.stream[idx].numid == numid) { + const char *pcmName = AudioStreamHandle.stream[idx].pcm->devid; + snd_pcm_pause(AudioStreamHandle.stream[idx].pcm->handle, !value); 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; } } @@ -220,7 +304,7 @@ static void *LoopInThread(void *handle) { 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++); + AFB_ApiInfo(subscribeHandle->api, "LoopInThread:%s/%d Idle count=%d", subscribeHandle->info, subscribeHandle->tid, count++); continue; } if (res < 0) { @@ -241,7 +325,7 @@ PUBLIC snd_ctl_t* AlsaCrlFromPcm(CtlSourceT *source, snd_pcm_t *pcm) { 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); + snprintf(buffer, sizeof (buffer), "hw:%i", pcmCard); if ((error = snd_ctl_open(&ctlDev, buffer, SND_CTL_READONLY)) < 0) goto OnErrorExit; return ctlDev; @@ -250,56 +334,6 @@ 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; @@ -313,32 +347,32 @@ PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) { // 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)); + AFB_ApiError(source->api, "AlsaCtlSubscribe: fail sndcard=%s to subscribe events", ALSA_CTL_UID(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)); + AFB_ApiError(source->api, "AlsaCtlSubscribe: fail sndcard=%s get poll descriptors", ALSA_CTL_UID(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)); + fprintf(stderr, "AlsaCtlSubscribe: fail sndcard=%s creating a new loop", ALSA_CTL_UID(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)); + AFB_ApiError(source->api, "AlsaCtlSubscribe: Fail sndcard=%s adding mainloop", ALSA_CTL_UID(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); + AFB_ApiError(source->api, "AlsaCtlSubscribe: Fail sndcard=%s create waiting thread err=%d", ALSA_CTL_UID(ctlDev, string), error); goto OnErrorExit; } @@ -348,49 +382,41 @@ OnErrorExit: return -1; } -PUBLIC int AlsaCtlRegister(CtlSourceT *source, snd_pcm_t *pcm, int numid) { +PUBLIC int AlsaCtlRegister(CtlSourceT *source, AlsaPcmInfoT *pcm, int numid) { long value; int error; - + // NumID are attached to sndcard retrieve ctldev from PCM - snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, pcm); + snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, pcm->handle); if (!ctlDev) { - AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", snd_pcm_name(pcm)); + AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", pcm->devid); 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; + AlsaCtlSubscribe(source, ctlDev); } - 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); + error = AlsaCtlGetNumidValueI(source, ctlDev, numid, &value); + if (error) goto OnErrorExit; + + AFB_ApiNotice(source->api, "AlsaCtlRegister [pcm=%s] numid=%d value=%ld", pcm->devid, numid, value); // store PCM in order to pause/resume depending on event - AudioStreamHandle.pcm[AudioStreamHandle.count] = pcm; - AudioStreamHandle.numid[AudioStreamHandle.count] = numid; - AudioStreamHandle.count++; - + int count=AudioStreamHandle.count; + AudioStreamHandle.stream[count].pcm = pcm; + AudioStreamHandle.stream[count].numid = numid; + // we only need to keep ctldev open for initial registration - if (AudioStreamHandle.count >1) snd_ctl_close(ctlDev); - + if (AudioStreamHandle.count++ > 0) 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; + if ((error = snd_pcm_pause(pcm->handle, !value)) < 0) { + AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail to pause", pcm->devid); + goto OnErrorExit; } - + return 0; OnErrorExit: diff --git a/plugins/alsa/alsa-pcm.c b/plugins/alsa/alsa-core-pcm.c index f342888..a08dfca 100644 --- a/plugins/alsa/alsa-pcm.c +++ b/plugins/alsa/alsa-core-pcm.c @@ -32,7 +32,6 @@ for the specific language governing permissions and #define BUFFER_FRAME_COUNT 1024 - typedef struct { snd_pcm_t *pcmIn; snd_pcm_t *pcmOut; @@ -40,7 +39,7 @@ typedef struct { sd_event_source* evtsrc; void* buffer; size_t frameSize; - unsigned int frameCount; + unsigned int frameCount; unsigned int channels; sd_event *sdLoop; pthread_t thread; @@ -48,11 +47,6 @@ typedef struct { char* info; } AlsaPcmCopyHandleT; -static int defaultPcmRate = 48000; -static int defaultPcmChannels = 2; -static snd_pcm_format_t defaultPcmFormat = SND_PCM_FORMAT_S16_LE; -static snd_pcm_access_t defaultPcmAccess = SND_PCM_ACCESS_RW_INTERLEAVED; - STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) { int pcmSampleSize; @@ -84,8 +78,7 @@ STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) { return pcmSampleSize; } - -PUBLIC int AlsaPcmConf(CtlSourceT *source, snd_pcm_t *pcmHandle, snd_pcm_format_t pcmFormat, unsigned int pcmRate, unsigned int pcmChannels, AlsaPcmHwInfoT *pcmHwInfo) { +PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts) { char string[32]; int error; snd_pcm_hw_params_t *pxmHwParams; @@ -93,75 +86,76 @@ PUBLIC int AlsaPcmConf(CtlSourceT *source, snd_pcm_t *pcmHandle, snd_pcm_format_ // retrieve hadware config from PCM snd_pcm_hw_params_alloca(&pxmHwParams); - snd_pcm_hw_params_any(pcmHandle, pxmHwParams); + snd_pcm_hw_params_any(pcm->handle, pxmHwParams); - error = snd_pcm_hw_params_set_access(pcmHandle, pxmHwParams, defaultPcmAccess); + if (!opts->access) opts->access = SND_PCM_ACCESS_RW_INTERLEAVED; + error = snd_pcm_hw_params_set_access(pcm->handle, pxmHwParams, opts->access); if (error) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Interleave=%d mode error=%s", AlsaPcmUID(pcmHandle, string), defaultPcmAccess, snd_strerror(error)); + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Interleave=%d mode error=%s", ALSA_PCM_UID(pcm->handle, string), opts->access, snd_strerror(error)); goto OnErrorExit; }; - if (pcmFormat == ALSA_PCM_DEFAULT_FORMAT) pcmFormat = defaultPcmFormat; - if (pcmFormat != SND_PCM_FORMAT_UNKNOWN) { - if ((error = snd_pcm_hw_params_set_format(pcmHandle, pxmHwParams, pcmFormat)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Format=%d error=%s", AlsaPcmUID(pcmHandle, string), pcmFormat, snd_strerror(error)); - AlsaDumpFormats(source, pcmHandle); + if (opts->format != SND_PCM_FORMAT_UNKNOWN) { + if ((error = snd_pcm_hw_params_set_format(pcm->handle, pxmHwParams, opts->format)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Format=%d error=%s", ALSA_PCM_UID(pcm->handle, string), opts->format, snd_strerror(error)); + AlsaDumpFormats(source, pcm->handle); goto OnErrorExit; } } - if (pcmRate == ALSA_PCM_DEFAULT_RATE) pcmRate = defaultPcmRate; - pcmHwInfo->rate = pcmRate; - if ((error = snd_pcm_hw_params_set_rate_near(pcmHandle, pxmHwParams, &pcmHwInfo->rate, 0)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Rate=%d error=%s", AlsaPcmUID(pcmHandle, string), pcmRate, snd_strerror(error)); - goto OnErrorExit; - } + if (opts->rate > 0) { + unsigned int pcmRate = opts->rate; + if ((error = snd_pcm_hw_params_set_rate_near(pcm->handle, pxmHwParams, &opts->rate, 0)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Rate=%d error=%s", ALSA_PCM_UID(pcm->handle, string), opts->rate, snd_strerror(error)); + goto OnErrorExit; + } - // check we got requested rate - if (pcmHwInfo->rate != pcmRate) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Rate ask=%dHz get=%dHz", AlsaPcmUID(pcmHandle, string), pcmRate, pcmHwInfo->rate); - goto OnErrorExit; + // check we got requested rate + if (opts->rate != pcmRate) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Rate ask=%dHz get=%dHz", ALSA_PCM_UID(pcm->handle, string), pcmRate, opts->rate); + goto OnErrorExit; + } } - if (pcmChannels == ALSA_PCM_DEFAULT_CHANNELS) pcmChannels = defaultPcmChannels; - if ((error = snd_pcm_hw_params_set_channels(pcmHandle, pxmHwParams, pcmChannels)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Channels=%d current=%d mode error=%s", AlsaPcmUID(pcmHandle, string), pcmChannels, pcmHwInfo->channels, snd_strerror(error)); - goto OnErrorExit; - }; + if (opts->channels) { + if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s Set_Channels=%d error=%s", ALSA_PCM_UID(pcm->handle, string), opts->channels, snd_strerror(error)); + goto OnErrorExit; + }; + } // store selected values - if ((error = snd_pcm_hw_params(pcmHandle, pxmHwParams)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s apply hwparams error=%s", AlsaPcmUID(pcmHandle, string), snd_strerror(error)); + if ((error = snd_pcm_hw_params(pcm->handle, pxmHwParams)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s apply hwparams error=%s", ALSA_PCM_UID(pcm->handle, string), snd_strerror(error)); goto OnErrorExit; } // check we effective hw params after optional format change - snd_pcm_hw_params_get_channels(pxmHwParams, &pcmHwInfo->channels); - snd_pcm_hw_params_get_format(pxmHwParams, &pcmHwInfo->format); - snd_pcm_hw_params_get_rate(pxmHwParams, &pcmHwInfo->rate, 0); - pcmHwInfo->sampleSize = AlsaPeriodSize(pcmHwInfo->format); - if (pcmHwInfo->sampleSize == 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s unsupported format format=%d", AlsaPcmUID(pcmHandle, string), pcmFormat); + snd_pcm_hw_params_get_channels(pxmHwParams, &opts->channels); + snd_pcm_hw_params_get_format(pxmHwParams, &opts->format); + snd_pcm_hw_params_get_rate(pxmHwParams, &opts->rate, 0); + opts->sampleSize = AlsaPeriodSize(opts->format); + if (opts->sampleSize == 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail PCM=%s unsupported format format=%d", ALSA_PCM_UID(pcm->handle, string), opts->format); goto OnErrorExit; } // retrieve software config from PCM snd_pcm_sw_params_alloca(&pxmSwParams); - snd_pcm_sw_params_current(pcmHandle, pxmSwParams); + snd_pcm_sw_params_current(pcm->handle, pxmSwParams); - if ((error = snd_pcm_sw_params_set_avail_min(pcmHandle, pxmSwParams, 16)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail to PCM=%s set_buffersize error=%s", AlsaPcmUID(pcmHandle, string), snd_strerror(error)); + if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, 16)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail to PCM=%s set_buffersize error=%s", ALSA_PCM_UID(pcm->handle, string), snd_strerror(error)); goto OnErrorExit; }; // push software params into PCM - if ((error = snd_pcm_sw_params(pcmHandle, pxmSwParams)) < 0) { - AFB_ApiError(source->api, "AlsaPcmConf: Fail to push software=%s params error=%s", AlsaPcmUID(pcmHandle, string), snd_strerror(error)); + if ((error = snd_pcm_sw_params(pcm->handle, pxmSwParams)) < 0) { + AFB_ApiError(source->api, "AlsaPcmConf: Fail to push software=%s params error=%s", ALSA_PCM_UID(pcm->handle, string), snd_strerror(error)); goto OnErrorExit; }; - - AFB_ApiNotice(source->api, "AlsaPcmConf: PCM=%s done", AlsaPcmUID(pcmHandle, string)); + AFB_ApiNotice(source->api, "AlsaPcmConf: PCM=%s channels=%d rate=%d format=%d access=%d done", ALSA_PCM_UID(pcm->handle,string), opts->channels, opts->rate, opts->format, opts->access); return 0; OnErrorExit: @@ -174,10 +168,9 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u snd_pcm_sframes_t framesIn, framesOut, availIn, availOut; AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) userData; - // PCM has was closed if ((revents & EPOLLHUP) != 0) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s hanghup/disconnected", AlsaPcmUID(pcmCopyHandle->pcmIn, string)); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s hanghup/disconnected", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string)); goto ExitOnSuccess; } @@ -186,26 +179,30 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u goto ExitOnSuccess; } - // Fulup this should be optimised to limit CPU usage when idle + // retrieve PCM state snd_pcm_state_t pcmState = snd_pcm_state(pcmCopyHandle->pcmIn); + + // When pause flush remaining frame and wait if (pcmState == SND_PCM_STATE_PAUSED) { - sleep(1); + framesIn = snd_pcm_readi(pcmCopyHandle->pcmIn, pcmCopyHandle->buffer, pcmCopyHandle->frameCount); + AFB_ApiInfo(pcmCopyHandle->api, "AlsaPcmReadCB: paused frame:%ld ignored", framesIn); + goto ExitOnSuccess; } - + // When XRNS append try to restore PCM if (pcmState == SND_PCM_STATE_XRUN) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s XRUN", AlsaPcmUID(pcmCopyHandle->pcmIn, string)); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s XRUN", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string)); snd_pcm_prepare(pcmCopyHandle->pcmIn); } - + // when PCM suspending loop until ready to go if (pcmState == SND_PCM_STATE_SUSPENDED) { while (1) { if ((error = snd_pcm_resume(pcmCopyHandle->pcmIn)) < 0) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s SUSPENDED fail to resume", AlsaPcmUID(pcmCopyHandle->pcmIn, string)); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s SUSPENDED fail to resume", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string)); sleep(1); // Fulup should be replace with corresponding AFB_timer } else { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s SUSPENDED success to resume", AlsaPcmUID(pcmCopyHandle->pcmIn, string)); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s SUSPENDED success to resume", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string)); } } } @@ -228,31 +225,28 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u // we get too many data ignore some if (availIn > pcmCopyHandle->frameCount) { - AFB_ApiInfo(pcmCopyHandle->api, "AlsaPcmReadCB PcmIn=%s XRUN lost=%ld", AlsaPcmUID(pcmCopyHandle->pcmIn, string), availIn - pcmCopyHandle->frameCount); availIn = pcmCopyHandle->frameCount; } // effectively read pcmIn and push frame to pcmOut framesIn = snd_pcm_readi(pcmCopyHandle->pcmIn, pcmCopyHandle->buffer, availIn); if (framesIn < 0 || framesIn != availIn) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmIn=%s UNDERUN frame=%ld", AlsaPcmUID(pcmCopyHandle->pcmIn, string), framesIn); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmIn=%s UNDERUN frame=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string), framesIn); goto ExitOnSuccess; } // In/Out frames transfer through buffer copy framesOut = snd_pcm_writei(pcmCopyHandle->pcmOut, pcmCopyHandle->buffer, framesIn); if (framesOut < 0 || framesOut != framesIn) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmOut=%s UNDERUN/SUSPEND frameOut=%ld", AlsaPcmUID(pcmCopyHandle->pcmOut, string), framesOut); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmOut=%s UNDERUN/SUSPEND frameOut=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), framesOut); goto ExitOnSuccess; } if (framesIn != framesOut) { - AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s Loosing frames=%ld", AlsaPcmUID(pcmCopyHandle->pcmOut, string), (framesIn - framesOut)); + AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s Loosing frames=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), (framesIn - framesOut)); goto ExitOnSuccess; } - // fprintf(stderr, "."); - return 0; // Cannot handle error in callback @@ -273,107 +267,90 @@ static void *LoopInThread(void *handle) { for (;;) { int res = sd_event_run(pcmCopyHandle->sdLoop, watchdog); if (res == 0) { - AFB_ApiNotice(pcmCopyHandle->api, "LoopInThread:%s/%d Idle count=%d", pcmCopyHandle->info, pcmCopyHandle->tid, count++); + AFB_ApiInfo(pcmCopyHandle->api, "LoopInThread:%s/%d Idle count=%d", pcmCopyHandle->info, pcmCopyHandle->tid, count++); continue; } if (res < 0) { - AFB_ApiError(pcmCopyHandle->api,"LoopInThread:%s/%d ERROR=%i Exit errno=%s.\n", pcmCopyHandle->info, pcmCopyHandle->tid, res, strerror(res)); + AFB_ApiError(pcmCopyHandle->api, "LoopInThread:%s/%d ERROR=%i Exit errno=%s.\n", pcmCopyHandle->info, pcmCopyHandle->tid, res, strerror(res)); break; } } pthread_exit(0); } -PUBLIC int AlsaPcmCopy(CtlSourceT *source, snd_pcm_t *pcmIn, snd_pcm_t *pcmOut, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int rate, unsigned int channels) { +PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT * opts) { char string[32]; - struct pollfd *pcmInFds; + struct pollfd *pcmInFds; int error; - AlsaPcmHwInfoT infoIn, infoOut; - - - if (format == ALSA_PCM_DEFAULT_FORMAT) format = defaultPcmFormat; - if (rate == ALSA_PCM_DEFAULT_RATE) rate = defaultPcmRate; - if (access == ALSA_PCM_DEFAULT_ACCESS) access = defaultPcmAccess; - if (channels == ALSA_PCM_DEFAULT_CHANNELS) channels = defaultPcmChannels; // prepare PCM for capture and replay - error = AlsaPcmConf(source, pcmIn, format, rate, channels, &infoIn); + error = AlsaPcmConf(source, pcmIn, opts); if (error) goto OnErrorExit; // Prepare PCM for usage - if ((error = snd_pcm_start(pcmIn)) < 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", AlsaPcmUID(pcmIn, string), snd_strerror(error)); + if ((error = snd_pcm_start(pcmIn->handle)) < 0) { + AFB_ApiError(source->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error)); goto OnErrorExit; }; - - error = AlsaPcmConf(source, pcmOut, infoIn.format, infoIn.rate, infoIn.channels, &infoOut); + + error = AlsaPcmConf(source, pcmOut, opts); if (error) goto OnErrorExit; // Prepare PCM for usage - if ((error = snd_pcm_prepare(pcmOut)) < 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail to start PCM=%s error=%s", AlsaPcmUID(pcmOut, string), snd_strerror(error)); + if ((error = snd_pcm_prepare(pcmOut->handle)) < 0) { + AFB_ApiError(source->api, "AlsaPcmCopy: Fail to start PCM=%s error=%s", ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); goto OnErrorExit; }; + - if (infoIn.format != infoOut.format) { - AFB_ApiError(source->api, "AlsaPcmCopy: pcmIn=%s pcmOut=%s format mismatch in=%d out=%d" - , AlsaPcmUID(pcmIn, string), AlsaPcmUID(pcmOut, string), infoIn.format, infoOut.format); - goto OnErrorExit; - } - - if (infoIn.channels != infoOut.channels) { - AFB_ApiError(source->api, "AlsaPcmCopy: pcmIn=%s pcmOut=%s channel count mismatch in=%d out=%d" - , AlsaPcmUID(pcmIn, string), AlsaPcmUID(pcmOut, string), infoIn.channels, infoOut.channels); - goto OnErrorExit; - } AlsaPcmCopyHandleT *pcmCopyHandle = malloc(sizeof (AlsaPcmCopyHandleT)); pcmCopyHandle->info = "pcmCpy"; - pcmCopyHandle->pcmIn = pcmIn; - pcmCopyHandle->pcmOut = pcmOut; + pcmCopyHandle->pcmIn = pcmIn->handle; + pcmCopyHandle->pcmOut = pcmOut->handle; pcmCopyHandle->api = source->api; - pcmCopyHandle->channels = infoIn.channels; - pcmCopyHandle->frameSize = infoIn.channels * infoIn.sampleSize; + pcmCopyHandle->channels = opts->channels; + pcmCopyHandle->frameSize = opts->channels * opts->sampleSize; pcmCopyHandle->frameCount = BUFFER_FRAME_COUNT; pcmCopyHandle->buffer = malloc(pcmCopyHandle->frameCount * pcmCopyHandle->frameSize); - + // get FD poll descriptor for capture PCM int pcmInCount = snd_pcm_poll_descriptors_count(pcmCopyHandle->pcmIn); if (pcmInCount <= 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s get fds count error=%s", AlsaPcmUID(pcmIn, string), snd_strerror(error)); + AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s get fds count error=%s", ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error)); goto OnErrorExit; }; pcmInFds = alloca(sizeof (*pcmInFds) * pcmInCount); - if ((error = snd_pcm_poll_descriptors(pcmIn, pcmInFds, pcmInCount)) < 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s get pollfds error=%s", AlsaPcmUID(pcmOut, string), snd_strerror(error)); + if ((error = snd_pcm_poll_descriptors(pcmIn->handle, pcmInFds, pcmInCount)) < 0) { + AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s get pollfds error=%s", ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); goto OnErrorExit; }; // add poll descriptor to AGL systemd mainloop if ((error = sd_event_new(&pcmCopyHandle->sdLoop)) < 0) { - fprintf(stderr, "LaunchCallRequest: fail pcmin=%s creating a new loop: %s\n", AlsaPcmUID(pcmOut, string), strerror(error)); + fprintf(stderr, "LaunchCallRequest: fail pcmin=%s creating a new loop: %s\n", ALSA_PCM_UID(pcmOut->handle, string), strerror(error)); goto OnErrorExit; } for (int idx = 0; idx < pcmInCount; idx++) { if ((error = sd_event_add_io(pcmCopyHandle->sdLoop, &pcmCopyHandle->evtsrc, pcmInFds[idx].fd, EPOLLIN, AlsaPcmReadCB, pcmCopyHandle)) < 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s sd_event_add_io err=%d", AlsaPcmUID(pcmIn, string), error); + AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s sd_event_add_io err=%d", ALSA_PCM_UID(pcmIn->handle, string), error); goto OnErrorExit; } } // start a thread with a mainloop to monitor Audio-Agent if ((error = pthread_create(&pcmCopyHandle->thread, NULL, &LoopInThread, pcmCopyHandle)) < 0) { - AFB_ApiError(source->api, "AlsaPcmCopy: Fail create waiting thread pcmIn=%s err=%d", AlsaPcmUID(pcmIn, string), error); + AFB_ApiError(source->api, "AlsaPcmCopy: Fail create waiting thread pcmIn=%s err=%d", ALSA_PCM_UID(pcmIn->handle, string), error); goto OnErrorExit; } return 0; OnErrorExit: - AFB_ApiError(source->api, "AlsaPcmCopy: Fail \n - pcmIn=%s \n - pcmOut=%s", AlsaPcmUID(pcmIn, string), AlsaPcmUID(pcmOut, string)); + AFB_ApiError(source->api, "AlsaPcmCopy: Fail \n - pcmIn=%s \n - pcmOut=%s", ALSA_PCM_UID(pcmIn->handle, string), ALSA_PCM_UID(pcmOut->handle, string)); return -1; } diff --git a/plugins/alsa/alsa-dmix.c b/plugins/alsa/alsa-plug-dmix.c index d527657..3d67410 100644 --- a/plugins/alsa/alsa-dmix.c +++ b/plugins/alsa/alsa-plug-dmix.c @@ -22,51 +22,53 @@ static int uniqueIpcIndex = 1024; +ALSA_PLUG_PROTO(dmix); -PUBLIC snd_pcm_t* AlsaCreateDmix(CtlSourceT *source, const char *dmixName, const char *slaveName) { - - AFB_ApiNotice(source->api, "AlsaCreateDmix: start "); - - int cardIndex= snd_ctl_card_info_get_card(AlsaByPathInfo (source, "/dev/snd/by-id/usb-Focusrite_Scarlett_18i8_USB_10004EE6-00")); - - AFB_ApiNotice(source->api, "AlsaCreateDmix: card index=%d ", cardIndex); - - snd_pcm_t *dmixPcm; - snd_config_t *dmixConfig, *slaveConfig, *elemConfig; - snd_pcm_stream_t streamPcm = SND_PCM_STREAM_PLAYBACK; - int error = 0, streamMode = SND_PCM_NONBLOCK; +PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave) { + + snd_config_t *dmixConfig, *slaveConfig, *elemConfig, *pcmConfig; + AlsaPcmInfoT *pcmPlug= malloc(sizeof(AlsaPcmInfoT)); + pcmPlug->devid= pcmName; + int error=0; + // refresh global alsalib config and create PCM top config + snd_config_update(); error += snd_config_top(&dmixConfig); + error += snd_config_set_id (dmixConfig, pcmPlug->devid); + error += snd_config_imake_string(&elemConfig, "type", "dmix"); + error += snd_config_add(dmixConfig, elemConfig); error += snd_config_imake_integer(&elemConfig, "ipc_key", uniqueIpcIndex++); error += snd_config_add(dmixConfig, elemConfig); if (error) goto OnErrorExit; error += snd_config_make_compound(&slaveConfig, "slave", 0); - error += snd_config_imake_string(&elemConfig, "pcm", slaveName); + error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->devid); error += snd_config_add(slaveConfig, elemConfig); if (error) goto OnErrorExit; // add leaf into config error += snd_config_add(dmixConfig, slaveConfig); if (error) goto OnErrorExit; + + error = _snd_pcm_dmix_open(&pcmPlug->handle, pcmPlug->devid, snd_config, dmixConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK); + if (error) { + AFB_ApiError(source->api, "AlsaCreateDmix: fail to create DMIX=%s SLAVE=%s", pcmPlug->devid, pcmSlave->devid); + goto OnErrorExit; + } - snd_config_update(); - //AlsaDumpConfig (source, snd_config, 1); - AlsaDumpCtlConfig (source, dmixConfig, 1); - - - error = _snd_pcm_dmix_open(&dmixPcm, dmixName, snd_config, dmixConfig, streamPcm , streamMode); + error += snd_config_search(snd_config, "pcm", &pcmConfig); + error += snd_config_add(pcmConfig, dmixConfig); if (error) { - AFB_ApiError(source->api, "AlsaCreateDmix: fail to create DMIX=%s SLAVE=%s", dmixName, slaveName); + AFB_ApiError(source->api, "AlsaCreateDmix: fail to add configDMIX=%s", pcmPlug->devid); goto OnErrorExit; } - - AlsaDumpPcmInfo(source, dmixPcm, "DmixPCM"); - - AFB_ApiNotice(source->api, "AlsaCreateDmix: done"); - - return dmixPcm; + + // Debug config & pcm + AlsaDumpCtlConfig (source, dmixConfig, 1); + //AlsaDumpPcmInfo(source, pcmPlug->handle, pcmPlug->devid); + AFB_ApiNotice(source->api, "AlsaCreateDmix: %s done", pcmPlug->devid); + return pcmPlug; OnErrorExit: AFB_ApiNotice(source->api, "AlsaCreateDmix: OnErrorExit"); diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c new file mode 100644 index 0000000..6de4ddf --- /dev/null +++ b/plugins/alsa/alsa-plug-vol.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Fulup Ar Foll <fulup@iot.bzh> + * + * 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. + * + */ + +#define _GNU_SOURCE // needed for vasprintf + +#include "alsa-softmixer.h" + +ALSA_PLUG_PROTO(softvol); + +PUBLIC AlsaPcmInfoT* AlsaCreateVol(CtlSourceT *source, const char *pcmName, AlsaPcmInfoT* ctlTarget, AlsaPcmInfoT* pcmSlave) { + + snd_config_t *volConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig; + int error = 0; + AlsaPcmInfoT *pcmPlug= malloc(sizeof(AlsaPcmInfoT)); + pcmPlug->devid= pcmName; + + // refresh global alsalib config and create PCM top config + snd_config_update(); + error += snd_config_top(&volConfig); + + // add slave leaf + error += snd_config_make_compound(&slaveConfig, "slave", 0); + error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->devid); + error += snd_config_add(slaveConfig, elemConfig); + error += snd_config_add(volConfig, slaveConfig); + if (error) goto OnErrorExit; + + // add control leaf + error += snd_config_make_compound(&controlConfig, "control", 0); + error += snd_config_imake_string(&elemConfig, "name", pcmName); + error += snd_config_add(controlConfig, elemConfig); + error += snd_config_imake_integer(&elemConfig, "card", ctlTarget->cardid); + error += snd_config_add(controlConfig, elemConfig); + error += snd_config_add(volConfig, controlConfig); + if (error) goto OnErrorExit; + + // update top config to access previous plugin PCM + snd_config_update(); + + error = _snd_pcm_softvol_open(&pcmPlug->handle, pcmName, snd_config, volConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK); + if (error) { + AFB_ApiError(source->api, "AlsaCreateVol: fail to create Plug=%s Slave=%s", pcmPlug->devid, pcmSlave->devid); + goto OnErrorExit; + } + + error += snd_config_search(snd_config, "pcm", &pcmConfig); + error += snd_config_add(pcmConfig, volConfig); + if (!error) { + AFB_ApiError(source->api, "AlsaCreateDmix: fail to add configDMIX=%s", pcmPlug->devid); + goto OnErrorExit; + } + + // Debug config & pcm + AlsaDumpCtlConfig (source, volConfig, 1); + //AlsaDumpPcmInfo(source, pcmPlug->handle, "pcmPlug->handle"); + AFB_ApiNotice(source->api, "AlsaCreateVol: %s done", pcmPlug->devid); + return pcmPlug; + +OnErrorExit: + AFB_ApiNotice(source->api, "AlsaCreateVol: OnErrorExit"); + return NULL; +}
\ No newline at end of file diff --git a/plugins/alsa/alsa-softmixer.c b/plugins/alsa/alsa-softmixer.c index 253a00b..9586fa3 100644 --- a/plugins/alsa/alsa-softmixer.c +++ b/plugins/alsa/alsa-softmixer.c @@ -29,71 +29,86 @@ CTLP_ONLOAD(plugin, callbacks) { return NULL; } +CTLP_LUA2C(AlsaDmix, source, argsJ, responseJ) { + json_object* subscribeArgsJ = NULL; + + int error = 0; + wrap_json_pack(&subscribeArgsJ, "{ss}", "value", "location"); + AFB_ApiNotice(source->api, "lua2c router with %s", json_object_to_json_string_ext(subscribeArgsJ, JSON_C_TO_STRING_PRETTY)); + + return error; +} + + CTLP_LUA2C(AlsaRouter, source, argsJ, responseJ) { - json_object *devInJ, *devOutJ, *paramsJ=NULL; - AlsaDevByPathT devIn, devOut; - int err; + json_object *sndInJ, *sndOutJ, *paramsJ=NULL; + AlsaPcmInfoT *sndIn, *sndOut; + int error; - // init default values - memset(&devIn,0,sizeof(AlsaDevByPathT)); - memset(&devOut,0,sizeof(AlsaDevByPathT)); - int rate=ALSA_PCM_DEFAULT_RATE; - int channels=ALSA_PCM_DEFAULT_CHANNELS; - snd_pcm_format_t format=ALSA_PCM_DEFAULT_FORMAT; - snd_pcm_access_t access=ALSA_PCM_DEFAULT_ACCESS; + // make sndIn/Out a pointer to get cleaner code + sndIn=calloc(1, sizeof(AlsaPcmInfoT)); + sndOut=calloc(1, sizeof(AlsaPcmInfoT)); + // set pcm options to defaults + AlsaPcmHwInfoT *pcmOpts; + pcmOpts=calloc(1, sizeof(AlsaPcmHwInfoT)); + pcmOpts->format=SND_PCM_FORMAT_UNKNOWN; + pcmOpts->access=SND_PCM_ACCESS_RW_INTERLEAVED; - err= wrap_json_unpack(argsJ, "{s:o,s:o,s?o}", "devin", &devInJ, "devout", &devOutJ, "params", ¶msJ); - if (err) { + error= wrap_json_unpack(argsJ, "{s:o,s:o,s?o}", "devin", &sndInJ, "devout", &sndOutJ, "params", ¶msJ); + if (error) { AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter ARGS missing devIn|devOut args=%s", json_object_get_string(argsJ)); goto OnErrorExit; } - AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter **** PARAMS missing 'format|access|rate|channels' params=%s", json_object_get_string(paramsJ)); - - err= wrap_json_unpack(devInJ, "{s?s,s?s,s?i,s?i}", "path", &devIn.devpath, "id",&devIn.devid,"numid",&devIn.numid,"dev", &devIn.device, "sub", &devIn.subdev); - if (err || (!devIn.devpath && !devIn.devid)) { - AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-IN missing 'path|id|dev|sub|numid' devin=%s", json_object_get_string(devInJ)); + error= wrap_json_unpack(sndInJ, "{s?s,s?s,s?i,s?i,s?i}", "path",&sndIn->devpath, "id",&sndIn->devid, "numid",&sndIn->numid, "dev",&sndIn->device, "sub",&sndIn->subdev); + if (error || (!sndIn->devpath && !sndIn->devid)) { + AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-IN missing 'path|id|dev|sub|numid' devin=%s", json_object_get_string(sndInJ)); goto OnErrorExit; } - err= wrap_json_unpack(devOutJ, "{s?s,s?s,s?i, s?i,s?i}", "path", &devOut.devpath, "id", &devOut.device, "sub", &devOut.subdev); - if (err || (!devOut.devpath && !devOut.devid)) { - AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-OUT missing 'path|id|dev|sub' devout=%s", json_object_get_string(devOutJ)); + error= wrap_json_unpack(sndOutJ, "{s?s,s?s,s?i,s?i, s?i}", "path",&sndOut->devpath, "id",&sndOut->devid, "numid",&sndOut->numid,"dev",&sndOut->device, "sub",&sndOut->subdev); + if (error || (!sndOut->devpath && !sndOut->devid)) { + AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-OUT missing 'path|id|dev|sub' devout=%s", json_object_get_string(sndOutJ)); goto OnErrorExit; } - if (paramsJ) if ((err= wrap_json_unpack(paramsJ, "{s?i, s?i, s?i, s?i}", "format", &format, "access", &access, "rate", &rate, "channels",&channels)) != 0) { + if (paramsJ) if ((error= wrap_json_unpack(paramsJ, "{s?i, s?i, s?i, s?i}", "format", &pcmOpts->format, "access", &pcmOpts->access, "rate", &pcmOpts->rate, "channels",&pcmOpts->channels)) != 0) { AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter PARAMS missing 'format|access|rate|channels' params=%s", json_object_get_string(paramsJ)); goto OnErrorExit; } - AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter devIn=%s devOut=%s rate=%d channel=%d", devIn.devpath, devOut.devpath, rate, channels); - - // open PCM and start frame copy from binder Mainloop - snd_pcm_t* pcmIn = AlsaByPathOpenPcm(source, &devIn, SND_PCM_STREAM_CAPTURE); - snd_pcm_t* pcmOut = AlsaByPathOpenPcm(source, &devOut, SND_PCM_STREAM_PLAYBACK); - err = AlsaPcmCopy(source, pcmIn, pcmOut, format, access, (unsigned int)rate, (unsigned int)channels); - if(err) goto OnErrorExit; + AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter devin=%s devout=%s rate=%d channel=%d", sndIn->devpath, sndOut->devpath, pcmOpts->rate, pcmOpts->channels); + + // Check sndOut Exist and build a valid devid config + error= AlsaByPathDevid (source, sndOut); + if (error) goto OnErrorExit; + + //AlsaPcmInfoT *pcmOut = AlsaByPathOpenPcm(source, sndOut, SND_PCM_STREAM_PLAYBACK); + + // open capture PCM + AlsaPcmInfoT *pcmIn = AlsaByPathOpenPcm(source, sndIn, SND_PCM_STREAM_CAPTURE); + if (!pcmIn) goto OnErrorExit; + + AlsaPcmInfoT *pcmDmix= AlsaCreateDmix(source, "DmixPlugPcm", sndOut); + if(!pcmDmix) goto OnErrorExit; + + AlsaPcmInfoT *pcmVol= AlsaCreateVol(source, "SoftVol", sndIn, pcmDmix); + if(!pcmVol) goto OnErrorExit; + + error = AlsaPcmCopy(source, pcmIn, pcmVol, pcmOpts); + if(error) goto OnErrorExit; // Registration to event should be done after pcm_start - if (devIn.numid) { - err= AlsaCtlRegister(source, pcmIn, devIn.numid); - if(err) goto OnErrorExit; + if (sndIn->numid) { + error= AlsaCtlRegister(source, pcmIn, sndIn->numid); + if(error) goto OnErrorExit; } return 0; OnErrorExit: + AFB_ApiNotice(source->api, "--lua2c-- ERROR AlsaRouter sndIn=%s sndOut=%s rate=%d channel=%d", sndIn->devpath, sndOut->devpath, pcmOpts->rate, pcmOpts->channels); return -1; } -CTLP_LUA2C(AlsaDmix, source, argsJ, responseJ) { - json_object* subscribeArgsJ = NULL; - - int err = 0; - wrap_json_pack(&subscribeArgsJ, "{ss}", "value", "location"); - AFB_ApiNotice(source->api, "lua2c router with %s", json_object_to_json_string_ext(subscribeArgsJ, JSON_C_TO_STRING_PRETTY)); - - return err; -} diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index c777f5d..17e7cf2 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -31,63 +31,61 @@ #include <alsa/asoundlib.h> -#define MAINLOOP_WATCHDOG 10000 +#define MAINLOOP_WATCHDOG 30000 #define MAX_AUDIO_STREAMS 8 -// Provide proto for LibASound low level API -int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name, - snd_config_t *root, snd_config_t *conf, - snd_pcm_stream_t stream, int mode); +#define ALSA_PLUG_PROTO(plugin) \ + int _snd_pcm_ ## plugin ## _open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode) -// alsa-ctl.c -PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo (CtlSourceT *source, const char *devid); -PUBLIC snd_ctl_t *AlsaCtlOpenCtl (CtlSourceT *source, const char *devid); -PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t *ctlDev); -PUBLIC int AlsaCtlRegister(CtlSourceT *source, snd_pcm_t *pcm, int numid); - -// alsa-tools-dump.c -PUBLIC void AlsaDumpFormats(CtlSourceT *source, snd_pcm_t *pcmHandle); -PUBLIC char *AlsaDumpPcmUid(snd_pcm_t *pcmHandle, char *buffer, size_t len); -PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle); -PUBLIC void AlsaDumpPcmInfo(CtlSourceT *source, snd_pcm_t *pcm, const char* info); -PUBLIC void AlsaDumpPcmParams(CtlSourceT *source, snd_pcm_hw_params_t *pcmHwParams); -PUBLIC void AlsaDumpCtlConfig(CtlSourceT *source, snd_config_t *config, int indent); -#define AlsaPcmUID(pcmHandle, buffer) AlsaDumpPcmUid(pcmHandle, buffer, sizeof(buffer)) -PUBLIC char *AlsaDumpCtlUid(snd_ctl_t *ctlHandle, char *buffer, size_t len); -#define AlsaCtlUID(ctlHandle, buffer) AlsaDumpCtlUid(ctlHandle, buffer, sizeof(buffer)) - -// alsa-tools-bypath.c +// alsa-utils-bypath.c typedef struct { - char *devpath; - char *devid; + const char *devpath; + const char *devid; int device; int subdev; int numid; -} AlsaDevByPathT; + int cardid; + snd_pcm_t *handle; +} AlsaPcmInfoT; -PUBLIC snd_ctl_card_info_t* AlsaByPathInfo (CtlSourceT *source, const char *control); -PUBLIC snd_pcm_t* AlsaByPathOpenPcm(CtlSourceT *source, AlsaDevByPathT *dev, snd_pcm_stream_t direction); -PUBLIC snd_ctl_t *AlsaByPathOpenCtl (CtlSourceT *source, AlsaDevByPathT *dev); -#define ALSA_PCM_DEFAULT_FORMAT (unsigned int)-99 -#define ALSA_PCM_DEFAULT_ACCESS (snd_pcm_access_t)-99 -#define ALSA_PCM_DEFAULT_RATE (snd_pcm_format_t)-99 -#define ALSA_PCM_DEFAULT_CHANNELS (unsigned int)-99 - -// alsa-pcm.c typedef struct { - snd_pcm_format_t format; + unsigned int rate; unsigned int channels; + snd_pcm_format_t format; + snd_pcm_access_t access; size_t sampleSize; - unsigned int rate; } AlsaPcmHwInfoT; -PUBLIC void AlsaPcmSetDefault (snd_pcm_format_t format, snd_pcm_access_t access, unsigned int rate, unsigned int channel); -PUBLIC int AlsaPcmConf(CtlSourceT *source, snd_pcm_t *pcmHandle, snd_pcm_format_t pcmFormat, unsigned int pcmRate, unsigned int pcmChannels, AlsaPcmHwInfoT *pcmHwInfo); -PUBLIC int AlsaPcmCopy(CtlSourceT *source, snd_pcm_t *pcmIn, snd_pcm_t *pcmOut, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int rate, unsigned int channel); +PUBLIC snd_ctl_card_info_t* AlsaByPathInfo (CtlSourceT *source, const char *control); +PUBLIC AlsaPcmInfoT* AlsaByPathOpenPcm(CtlSourceT *source, AlsaPcmInfoT *dev, snd_pcm_stream_t direction); +PUBLIC snd_ctl_t *AlsaByPathOpenCtl (CtlSourceT *source, AlsaPcmInfoT *dev); +PUBLIC int AlsaByPathDevid (CtlSourceT *source, AlsaPcmInfoT *dev); + +// alsa-utils-dump.c +PUBLIC void AlsaDumpFormats(CtlSourceT *source, snd_pcm_t *pcmHandle); +PUBLIC char *AlsaDumpPcmUid(snd_pcm_t *pcmHandle, char *buffer, size_t len); +PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle); +PUBLIC void AlsaDumpPcmInfo(CtlSourceT *source, snd_pcm_t *pcm, const char* info); +PUBLIC void AlsaDumpPcmParams(CtlSourceT *source, snd_pcm_hw_params_t *pcmHwParams); +PUBLIC void AlsaDumpCtlConfig(CtlSourceT *source, snd_config_t *config, int indent); +#define ALSA_PCM_UID(pcmHandle, buffer) AlsaDumpPcmUid(pcmHandle, buffer, sizeof(buffer)) +PUBLIC char *AlsaDumpCtlUid(snd_ctl_t *ctlHandle, char *buffer, size_t len); +#define ALSA_CTL_UID(ctlHandle, buffer) AlsaDumpCtlUid(ctlHandle, buffer, sizeof(buffer)) + +// alsa-core-ctl.c +PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo (CtlSourceT *source, const char *devid); +PUBLIC snd_ctl_t *AlsaCtlOpenCtl (CtlSourceT *source, const char *devid); +PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t *ctlDev); +PUBLIC int AlsaCtlRegister(CtlSourceT *source, AlsaPcmInfoT *pcm, int numid); +PUBLIC int AlsaCtlGetNumidValueI(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value); + + -// alse-dmix.c -PUBLIC snd_pcm_t* AlsaCreateDmix(CtlSourceT *source, const char *dmixName, const char *slaveName); -PUBLIC snd_pcm_t* AlsaCreateCapture(CtlSourceT *source, const char* sndDevPath, unsigned int deviceIdx, unsigned int subdevIdx, unsigned int channelCount); +PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts); +PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT *opts); +// _snd_pcm_PLUGIN_open_ see macro ALSA_PLUG_PROTO(plugin) +PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave); +PUBLIC AlsaPcmInfoT* AlsaCreateVol(CtlSourceT *source, const char *pcmName, AlsaPcmInfoT* ctlTarget, AlsaPcmInfoT* pcmSlave); #endif
\ No newline at end of file diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c index e4a8833..8fba1f0 100644 --- a/plugins/alsa/alsa-utils-bypath.c +++ b/plugins/alsa/alsa-utils-bypath.c @@ -34,78 +34,93 @@ // Clone of AlsaLib snd_card_load2 static function -PUBLIC snd_ctl_card_info_t *AlsaByPathInfo (CtlSourceT *source, const char *devpath) { + +PUBLIC snd_ctl_card_info_t *AlsaByPathInfo(CtlSourceT *source, const char *devpath) { int open_dev; snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof()); if (!devpath) goto OnErrorExit; - + open_dev = open(devpath, O_RDONLY); if (open_dev < 0) goto OnErrorExit; - + int rc = ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO(snd_ctl_card_info_sizeof()), cardInfo); if (rc < 0) { close(open_dev); goto OnErrorExit; } - + close(open_dev); return cardInfo; - + OnErrorExit: - AFB_ApiError(source->api,"AlsaCardInfoByPath: fail to find sndcard by path= %s", devpath); + AFB_ApiError(source->api, "AlsaCardInfoByPath: fail to find sndcard by path= %s", devpath); return NULL; } -PUBLIC snd_pcm_t* AlsaByPathOpenPcm(CtlSourceT *source, AlsaDevByPathT *dev, snd_pcm_stream_t direction) { - char pcmName[32]; - int error; - snd_pcm_t *pcmHandle; +PUBLIC int AlsaByPathDevid(CtlSourceT *source, AlsaPcmInfoT *dev) { // get card info from /dev/snd/xxx if not use hw:x,x,x - snd_ctl_card_info_t *cardInfo=NULL; + snd_ctl_card_info_t *cardInfo = NULL; if (dev->devpath) cardInfo = AlsaByPathInfo(source, dev->devpath); - else if (dev->devid) cardInfo = AlsaCtlGetInfo (source, dev->devid); + else if (dev->devid) cardInfo = AlsaCtlGetInfo(source, dev->devid); if (!cardInfo) { - AFB_ApiError(source->api, "AlsaByPathOpenPcm: fail to find sndcard by path=%s id=%s", dev->devpath, dev->devid); + AFB_ApiWarning(source->api, "AlsaByPathOpenPcm: fail to find sndcard by path=%s id=%s", dev->devpath, dev->devid); goto OnErrorExit; } // extract useful info from cardInfo handle - int cardIdx = snd_ctl_card_info_get_card(cardInfo); - const char *cardName = snd_ctl_card_info_get_name(cardInfo); + dev->cardid = snd_ctl_card_info_get_card(cardInfo); + + // if not provided build a valid PCM devid + if (!dev->devid) { + #define DEVID_MAX_LEN 32 + dev->devid=malloc(DEVID_MAX_LEN); + if (dev->subdev) snprintf((char*)dev->devid, DEVID_MAX_LEN, "hw:%i,%i,%i", dev->cardid, dev->device, dev->subdev); + else if (dev->device) snprintf((char*)dev->devid, DEVID_MAX_LEN, "hw:%i,%i", dev->cardid, dev->device); + else snprintf((char*)dev->devid, DEVID_MAX_LEN, "hw:%i", dev->cardid); + } + return 0; - // build a valid name and open sndcard - snprintf(pcmName, sizeof (pcmName), "hw:%i,%i,%i", cardIdx, dev->device, dev->subdev); - error = snd_pcm_open(&pcmHandle, pcmName, direction, SND_PCM_NONBLOCK); +OnErrorExit: + return -1; +} + +PUBLIC AlsaPcmInfoT* AlsaByPathOpenPcm(CtlSourceT *source, AlsaPcmInfoT *dev, snd_pcm_stream_t direction) { + int error; + + error = AlsaByPathDevid(source, dev); + if (error) goto OnErrorExit; + + error = snd_pcm_open(&dev->handle, dev->devid, direction, SND_PCM_NONBLOCK); if (error) { - AFB_ApiError(source->api, "AlsaByPathOpenPcm: fail openpcm (hw:%d -> %s name=%i subdev=%d): %s" - , cardIdx, cardName, dev->device, dev->subdev, snd_strerror(error)); + AFB_ApiError(source->api, "AlsaByPathOpenPcm: fail openpcm (devid=%s idxdev=%i subdev=%d): %s" + , dev->devid, dev->device, dev->subdev, snd_strerror(error)); goto OnErrorExit; } - - return (pcmHandle); + + return (dev); OnErrorExit: return NULL; } -PUBLIC snd_ctl_t *AlsaByPathOpenCtl (CtlSourceT *source, AlsaDevByPathT *dev) { +PUBLIC snd_ctl_t *AlsaByPathOpenCtl(CtlSourceT *source, AlsaPcmInfoT *dev) { int err; char devid[32]; snd_ctl_t *handle; - + // get card info from /dev/snd/xxx if not use hw:x,x,x - snd_ctl_card_info_t *cardInfo=NULL; + snd_ctl_card_info_t *cardInfo = NULL; if (dev->devpath) cardInfo = AlsaByPathInfo(source, dev->devpath); - else if (dev->devid) cardInfo = AlsaCtlGetInfo (source, dev->devid); + else if (dev->devid) cardInfo = AlsaCtlGetInfo(source, dev->devid); if (!cardInfo) { AFB_ApiError(source->api, "AlsaByPathOpenCtl: fail to find sndcard by path=%s id=%s", dev->devpath, dev->devid); goto OnErrorExit; } - + // extract useful info from cardInfo handle int cardIndex = snd_ctl_card_info_get_card(cardInfo); const char *cardId = snd_ctl_card_info_get_id(cardInfo); @@ -114,14 +129,14 @@ PUBLIC snd_ctl_t *AlsaByPathOpenCtl (CtlSourceT *source, AlsaDevByPathT *dev) { // build a valid name and open sndcard snprintf(devid, sizeof (devid), "hw:%i", cardIndex); if ((err = snd_ctl_open(&handle, devid, 0)) < 0) { - AFB_ApiError(source->api,"control open (hw:%d -> %s): %s", cardIndex, cardName, snd_strerror(err)); + AFB_ApiError(source->api, "control open (hw:%d -> %s): %s", cardIndex, cardName, snd_strerror(err)); goto OnErrorExit; } AFB_ApiNotice(source->api, "AlsaCtlOpenByPath: sndcard hw:%d id=%s name=%s", cardIndex, cardId, cardName); - free (cardInfo); + free(cardInfo); return handle; - + OnErrorExit: return NULL; } diff --git a/plugins/alsa/alsa-utils-dump.c b/plugins/alsa/alsa-utils-dump.c index 7cfa899..b3bc864 100644 --- a/plugins/alsa/alsa-utils-dump.c +++ b/plugins/alsa/alsa-utils-dump.c @@ -67,7 +67,7 @@ PUBLIC void AlsaDumpFormats(CtlSourceT *source, snd_pcm_t *pcmHandle) { snd_pcm_hw_params_alloca(&pxmHwParams); snd_pcm_hw_params_any(pcmHandle, pxmHwParams); - AFB_ApiNotice(source->api, "Available formats: PCM=%s", AlsaPcmUID(pcmHandle, string)); + AFB_ApiNotice(source->api, "Available formats: PCM=%s", ALSA_PCM_UID(pcmHandle, string)); for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) { if (snd_pcm_hw_params_test_format(pcmHandle, pxmHwParams, format) == 0) { AFB_ApiNotice(source->api, "- %s", snd_pcm_format_name(format)); |