summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/alsa/alsa-api-backend.c220
-rw-r--r--plugins/alsa/alsa-api-frontend.c245
-rw-r--r--plugins/alsa/alsa-api-loop.c205
-rw-r--r--plugins/alsa/alsa-api-mixer.c359
-rw-r--r--plugins/alsa/alsa-api-pcm.c304
-rw-r--r--plugins/alsa/alsa-api-ramp.c114
-rw-r--r--plugins/alsa/alsa-api-sink.c111
-rw-r--r--plugins/alsa/alsa-api-source.c84
-rw-r--r--plugins/alsa/alsa-api-streams.c475
-rw-r--r--plugins/alsa/alsa-api-zones.c196
-rw-r--r--plugins/alsa/alsa-core-ctl.c346
-rw-r--r--plugins/alsa/alsa-core-pcm.c103
-rw-r--r--plugins/alsa/alsa-effect-ramp.c144
-rw-r--r--plugins/alsa/alsa-plug-dmix.c35
-rw-r--r--plugins/alsa/alsa-plug-multi.c106
-rw-r--r--plugins/alsa/alsa-plug-rate.c51
-rw-r--r--plugins/alsa/alsa-plug-route.c175
-rw-r--r--plugins/alsa/alsa-plug-vol.c83
-rw-r--r--plugins/alsa/alsa-softmixer.h222
-rw-r--r--plugins/alsa/alsa-utils-bypath.c104
-rw-r--r--plugins/alsa/alsa-utils-dump.c70
21 files changed, 2069 insertions, 1683 deletions
diff --git a/plugins/alsa/alsa-api-backend.c b/plugins/alsa/alsa-api-backend.c
deleted file mode 100644
index 85d9566..0000000
--- a/plugins/alsa/alsa-api-backend.c
+++ /dev/null
@@ -1,220 +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"
-
-// Fulup need to be cleanup with new controller version
-extern Lua2cWrapperT Lua2cWrap;
-
-
-
-STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannelT *channel) {
- const char*channelUid;
-
- int error = wrap_json_unpack(channelJ, "{ss,si !}", "uid", &channelUid, "port", &channel->port);
- if (error) goto OnErrorExit;
-
- channel->uid = strdup(channelUid);
- return 0;
-
-OnErrorExit:
- AFB_ApiError(source->api, "ProcessOneChannel: sndcard=%s channel: missing (uid||port) json=%s", uid, json_object_get_string(channelJ));
- return -1;
-}
-
-PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params) {
- const char *format = NULL, *access = NULL;
-
- // some default values
- params->rate = ALSA_DEFAULT_PCM_RATE;
- params->channels = 2;
- params->sampleSize = 0;
-
- int error = wrap_json_unpack(paramsJ, "{s?i,s?i, s?s, s?s !}", "rate", &params->rate, "channels", &params->channels, "format", &format, "access", &access);
- if (error) goto OnErrorExit;
-
- if (!format) params->format = SND_PCM_FORMAT_S16_LE;
- else if (!strcasecmp(format, "S16_LE")) params->format = SND_PCM_FORMAT_S16_LE;
- else if (!strcasecmp(format, "S16_BE")) params->format = SND_PCM_FORMAT_S16_BE;
- else if (!strcasecmp(format, "U16_LE")) params->format = SND_PCM_FORMAT_U16_LE;
- else if (!strcasecmp(format, "U16_BE")) params->format = SND_PCM_FORMAT_U16_BE;
- else if (!strcasecmp(format, "S32_LE")) params->format = SND_PCM_FORMAT_S32_LE;
- else if (!strcasecmp(format, "S32_BE")) params->format = SND_PCM_FORMAT_S32_BE;
- else if (!strcasecmp(format, "U32_LE")) params->format = SND_PCM_FORMAT_U32_LE;
- else if (!strcasecmp(format, "U32_BE")) params->format = SND_PCM_FORMAT_U32_BE;
- else if (!strcasecmp(format, "S24_LE")) params->format = SND_PCM_FORMAT_S24_LE;
- else if (!strcasecmp(format, "S24_BE")) params->format = SND_PCM_FORMAT_S24_BE;
- else if (!strcasecmp(format, "U24_LE")) params->format = SND_PCM_FORMAT_U24_LE;
- else if (!strcasecmp(format, "U24_BE")) params->format = SND_PCM_FORMAT_U24_BE;
- else if (!strcasecmp(format, "S8")) params->format = SND_PCM_FORMAT_S8;
- else if (!strcasecmp(format, "U8")) params->format = SND_PCM_FORMAT_U8;
- else if (!strcasecmp(format, "FLOAT_LE")) params->format = SND_PCM_FORMAT_FLOAT_LE;
- else if (!strcasecmp(format, "FLOAT_BE")) params->format = SND_PCM_FORMAT_FLOAT_LE;
- else {
- AFB_ApiNotice(source->api, "ProcessSndParams:%s(params) unsupported format 'S16_LE|S32_L|...' format=%s", uid, format);
- goto OnErrorExit;
- }
-
- if (!access) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
- else if (!strcasecmp(access, "MMAP_INTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- else if (!strcasecmp(access, "MMAP_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
- else if (!strcasecmp(access, "MMAP_COMPLEX")) params->access = SND_PCM_ACCESS_MMAP_COMPLEX;
- else if (!strcasecmp(access, "RW_INTERLEAVED")) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
- else if (!strcasecmp(access, "RW_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
-
- else {
- AFB_ApiNotice(source->api, "ProcessSndParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", uid, access);
- goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- return -1;
-}
-
-STATIC int ProcessOneSndCard(CtlSourceT *source, json_object *sndcardJ, AlsaPcmInfoT *snd) {
- json_object *sinkJ = NULL, *paramsJ = NULL;
- int error;
-
- error = wrap_json_unpack(sndcardJ, "{ss,s?s,s?s,s?i,s?i,s?i,so,s?o !}"
- , "uid", &snd->uid
- , "devpath", &snd->devpath
- , "cardid", &snd->cardid
- , "cardidx", &snd->cardidx
- , "device", &snd->device
- , "subdev", &snd->subdev
- , "sink", &sinkJ
- , "params", &paramsJ
- );
- if (error || !snd->uid || !sinkJ || (!snd->devpath && !snd->cardid && snd->cardidx)) {
- AFB_ApiNotice(source->api, "ProcessOneSndCard missing 'uid|path|cardid|cardidx|channels|device|subdev|numid|params' devin=%s", json_object_get_string(sndcardJ));
- goto OnErrorExit;
- }
-
- if (paramsJ) {
- error = ProcessSndParams(source, snd->uid, paramsJ, &snd->params);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneSndCard: sndcard=%s invalid params=%s", snd->uid, json_object_get_string(paramsJ));
- goto OnErrorExit;
- }
- } else {
- snd->params.rate = ALSA_DEFAULT_PCM_RATE;
- snd->params.access = SND_PCM_ACCESS_RW_INTERLEAVED;
- snd->params.format = SND_PCM_FORMAT_S16_LE;
- snd->params.channels = 2;
- }
-
- // check snd card is accessible
- error = AlsaByPathDevid(source, snd);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneSndCard: sndcard=%s not found config=%s", snd->uid, json_object_get_string(sndcardJ));
- goto OnErrorExit;
- }
-
- // protect each sndcard with a dmix plugin to enable audio-stream mixing
- char dmixUid[100];
- snprintf(dmixUid, sizeof (dmixUid), "Dmix-%s", snd->uid);
- AlsaPcmInfoT *dmixPcm = AlsaCreateDmix(source, dmixUid, snd, 0);
- if (!dmixPcm) {
- AFB_ApiError(source->api, "ProcessOneSndCard: sndcard=%s fail to attach dmix plugin", snd->uid);
- goto OnErrorExit;
- } else {
- snd->cardid = dmixPcm->cardid;
- }
-
- switch (json_object_get_type(sinkJ)) {
- case json_type_object:
- snd->ccount = 1;
- snd->channels = calloc(snd->ccount + 1, sizeof (AlsaPcmChannelT));
- error = ProcessOneChannel(source, snd->uid, sndcardJ, &snd->channels[0]);
- if (error) goto OnErrorExit;
- break;
- case json_type_array:
- snd->ccount = (int) json_object_array_length(sinkJ);
- snd->channels = calloc(snd->ccount + 1, sizeof (AlsaPcmChannelT));
- for (int idx = 0; idx < snd->ccount; idx++) {
- json_object *channelJ = json_object_array_get_idx(sinkJ, idx);
- error = ProcessOneChannel(source, snd->uid, channelJ, &snd->channels[idx]);
- if (error) goto OnErrorExit;
- }
- break;
- default:
- AFB_ApiError(source->api, "ProcessOneSndCard:%s invalid sink=%s", snd->uid, json_object_get_string(sinkJ));
- goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- return -1;
-}
-
-PUBLIC int SndBackend(CtlSourceT *source, json_object *argsJ) {
- SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
- int error;
- size_t count;
-
- assert(mixerHandle);
-
- if (mixerHandle->backend) {
- AFB_ApiError(source->api, "SndBackend: mixer=%s backend already declared %s", mixerHandle->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- switch (json_object_get_type(argsJ)) {
- case json_type_object:
- count = 1;
- mixerHandle->backend = calloc(count + 1, sizeof (AlsaPcmInfoT));
- error = ProcessOneSndCard(source, argsJ, &mixerHandle->backend[0]);
- if (error) goto OnErrorExit;
- break;
- case json_type_array:
- count = json_object_array_length(argsJ);
- mixerHandle->backend = calloc(count + 1, sizeof (AlsaPcmInfoT));
- for (int idx = 0; idx < count; idx++) {
- json_object *sndcardJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneSndCard(source, sndcardJ, &mixerHandle->backend[idx]);
- if (error) goto OnErrorExit;
- }
- break;
- default:
- AFB_ApiError(source->api, "SndBackend: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
-
- if (count == 1) {
- // only one sound card we multi would be useless
- mixerHandle->multiPcm = &mixerHandle->backend[0];
-
- } else {
-
- // instantiate an alsa multi plugin
- mixerHandle->multiPcm = AlsaCreateMulti(source, "PcmMulti", 0);
- if (!mixerHandle->multiPcm) goto OnErrorExit;
-
- }
- return 0;
-
-OnErrorExit:
- AFB_ApiNotice(source->api, "SndBackend mixer=%s fail to process: %s", mixerHandle->uid, json_object_get_string(argsJ));
- return -1;
-}
diff --git a/plugins/alsa/alsa-api-frontend.c b/plugins/alsa/alsa-api-frontend.c
deleted file mode 100644
index 11b8a7e..0000000
--- a/plugins/alsa/alsa-api-frontend.c
+++ /dev/null
@@ -1,245 +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"
-#include <string.h>
-
-// Fulup need to be cleanup with new controller version
-extern Lua2cWrapperT Lua2cWrap;
-
-STATIC int ProcessOneRamp(CtlSourceT *source, const char* uid, json_object *rampJ, AlsaVolRampT *ramp) {
- const char*rampUid;
-
- int error = wrap_json_unpack(rampJ, "{ss,si,si,si !}"
- , "uid", &rampUid
- , "delay", &ramp->delay
- , "up", &ramp->stepUp
- , "down", &ramp->stepDown
- );
- if (error) goto OnErrorExit;
-
- ramp->delay=ramp->delay*100; // move from ms to us
- ramp->uid = strdup(rampUid);
- return 0;
-
-OnErrorExit:
- AFB_ApiError(source->api, "ProcessOneRamp: sndcard=%s ramps: missing (uid||delay|up|down) json=%s", uid, json_object_get_string(rampJ));
- return -1;
-}
-
-STATIC int ProcessOneSubdev(CtlSourceT *source, AlsaSndLoopT *loop, json_object *subdevJ, AlsaPcmHwInfoT *loopDefParams, AlsaPcmInfoT *subdev) {
- json_object *paramsJ = NULL;
-
- int error = wrap_json_unpack(subdevJ, "{si,si,s?o !}", "subdev", &subdev->subdev, "numid", &subdev->numid, "params", &paramsJ);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneSubdev: loop=%s missing (uid|subdev|numid) json=%s", loop->uid, json_object_get_string(subdevJ));
- goto OnErrorExit;
- }
-
- if (paramsJ) {
- error = ProcessSndParams(source, loop->uid, paramsJ, loopDefParams);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneLoop: sndcard=%s invalid params=%s", loop->uid, json_object_get_string(paramsJ));
- goto OnErrorExit;
- }
- } else {
- // use global loop params definition as default
- memcpy(&subdev->params, loopDefParams, sizeof (AlsaPcmHwInfoT));
- }
- // create a fake uid and complete subdev info from loop handle
- char subuid[30];
- snprintf(subuid, sizeof (subuid), "loop:/%i/%i", subdev->subdev, subdev->numid);
- subdev->uid = strdup(subuid);
- subdev->device = loop->capture; // Fulup: with alsaloop softmixer only use capture device (playback is used by applications)
- subdev->devpath = loop->devpath;
- subdev->cardid = NULL; // force AlsaByPathDevId to rebuild a new one for each subdev
- subdev->cardidx = loop->cardidx;
-
- // check if card exist
- error = AlsaByPathDevid(source, subdev);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneSubdev: loop=%s fail to open subdev=%s", loop->uid, json_object_get_string(subdevJ));
- goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- return -1;
-}
-
-STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *loop) {
- json_object *subdevsJ = NULL, *devicesJ = NULL, *paramsJ = NULL, *rampsJ = NULL;
- int error;
-
- error = wrap_json_unpack(loopJ, "{ss,s?s,s?s,s?i,s?o,so,s?o,s?o !}"
- , "uid", &loop->uid
- , "devpath", &loop->devpath
- , "cardid", &loop->cardid
- , "cardidx", &loop->cardidx
- , "devices", &devicesJ
- , "subdevs", &subdevsJ
- , "params", &paramsJ
- , "ramps", &rampsJ
- );
- if (error || !loop->uid || !subdevsJ || (!loop->devpath && !loop->cardid && loop->cardidx)) {
- AFB_ApiNotice(source->api, "ProcessOneLoop missing 'uid|devpath|cardid|cardidx|devices|subdevs' loop=%s", json_object_get_string(loopJ));
- goto OnErrorExit;
- }
-
- AlsaPcmHwInfoT *loopDefParams = alloca(sizeof (AlsaPcmHwInfoT));
- if (paramsJ) {
- error = ProcessSndParams(source, loop->uid, paramsJ, loopDefParams);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneLoop: sndcard=%s invalid params=%s", loop->uid, json_object_get_string(paramsJ));
- goto OnErrorExit;
- }
- } else {
- loopDefParams->rate = ALSA_DEFAULT_PCM_RATE;
- loopDefParams->rate = ALSA_DEFAULT_PCM_RATE;
- loopDefParams->access = SND_PCM_ACCESS_RW_INTERLEAVED;
- loopDefParams->format = SND_PCM_FORMAT_S16_LE;
- loopDefParams->channels = 2;
- loopDefParams->sampleSize = 0;
- }
-
- // Fake a sound card to check if loop is a valid Alsa snd driver
- AlsaPcmInfoT sndLoop;
- sndLoop.uid = loop->uid;
- sndLoop.devpath = loop->devpath;
- sndLoop.cardid = loop->cardid;
- sndLoop.device = 0;
- sndLoop.subdev = 0;
- error = AlsaByPathDevid(source, &sndLoop);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneLoop: loop=%s not found config=%s", loop->uid, json_object_get_string(loopJ));
- goto OnErrorExit;
- }
- loop->uid = sndLoop.uid;
- loop->devpath = sndLoop.devpath;
- loop->cardid = sndLoop.cardid;
- loop->cardidx = sndLoop.cardidx;
- loop->registry= calloc (1,sizeof(RegistryHandleT));
-
- // process volume ramps
- if (rampsJ) {
- int rcount;
- switch (json_object_get_type(rampsJ)) {
- case json_type_object:
- rcount = 1;
- loop->ramps = calloc(rcount+1, sizeof (AlsaVolRampT));
- error = ProcessOneRamp(source, loop->uid, rampsJ, &loop->ramps[0]);
- if (error) goto OnErrorExit;
- break;
- case json_type_array:
- rcount = (int) json_object_array_length(rampsJ);
- loop->ramps = calloc(rcount+1, sizeof (AlsaVolRampT));
- for (int idx = 0; idx < rcount; idx++) {
- json_object *rampJ = json_object_array_get_idx(rampsJ, idx);
- error = ProcessOneRamp(source, loop->uid, rampJ, &loop->ramps[idx]);
- if (error) goto OnErrorExit;
- }
- break;
- default:
- AFB_ApiError(source->api, "ProcessOneLoop:%s invalid ramps=%s", loop->uid, json_object_get_string(rampsJ));
- goto OnErrorExit;
- }
- }
-
- // Default devices is payback=0 capture=1
- if (!devicesJ) {
- loop->playback = 0;
- loop->capture = 1;
- } else {
- error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback);
- if (error) {
- AFB_ApiNotice(source->api, "ProcessOneLoop=%s missing 'capture|playback' devices=%s", loop->uid, json_object_get_string(devicesJ));
- goto OnErrorExit;
- }
- }
-
- switch (json_object_get_type(subdevsJ)) {
- case json_type_object:
- loop->scount = 1;
- loop->subdevs = calloc(loop->scount + 1, sizeof (AlsaPcmInfoT));
- error = ProcessOneSubdev(source, loop, subdevsJ, loopDefParams, &loop->subdevs[0]);
- if (error) goto OnErrorExit;
- break;
- case json_type_array:
- loop->scount = (int) json_object_array_length(subdevsJ);
- loop->subdevs = calloc(loop->scount + 1, sizeof (AlsaPcmInfoT));
- for (int idx = 0; idx < loop->scount; idx++) {
- json_object *subdevJ = json_object_array_get_idx(subdevsJ, idx);
- error = ProcessOneSubdev(source, loop, subdevJ, loopDefParams, &loop->subdevs[idx]);
- if (error) goto OnErrorExit;
- }
- break;
- default:
- AFB_ApiError(source->api, "ProcessOneLoop=%s invalid subdevs= %s", loop->uid, json_object_get_string(subdevsJ));
- goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- return -1;
-}
-
-PUBLIC int SndFrontend(CtlSourceT *source, json_object *argsJ) {
- SoftMixerHandleT *mixer = (SoftMixerHandleT*) source->context;
- int error;
-
- assert(mixer);
-
- if (mixer->frontend) {
- AFB_ApiError(source->api, "SndFrontend: mixer=%s SndFrontend already declared %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- mixer->frontend = calloc(1, sizeof (AlsaSndLoopT));
-
- // or syntax purpose array is accepted but frontend should have a single driver entry
- json_type type = json_object_get_type(argsJ);
- if (type == json_type_array) {
- size_t count = json_object_array_length(argsJ);
- if (count != 1) {
- AFB_ApiError(source->api, "SndFrontend: mixer=%s frontend only support on input driver args=%s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
- argsJ = json_object_array_get_idx(argsJ, 0);
- }
-
- type = json_object_get_type(argsJ);
- if (type != json_type_object) {
- AFB_ApiError(source->api, "SndFrontend: mixer=%s invalid object type= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- error = ProcessOneLoop(source, argsJ, mixer->frontend);
- if (error) {
- AFB_ApiError(source->api, "SndFrontend: mixer=%s invalid object= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- return -1;
-} \ No newline at end of file
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
new file mode 100644
index 0000000..9a746ea
--- /dev/null
+++ b/plugins/alsa/alsa-api-loop.c
@@ -0,0 +1,205 @@
+/*
+ * 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"
+#include <string.h>
+
+PUBLIC AlsaLoopSubdevT *ApiLoopFindSubdev(SoftMixerT *mixer, const char *streamUid, const char *targetUid, AlsaSndLoopT **loop) {
+
+ // Either allocate a free loop subdev or search for a specific targetUid when specified
+ if (targetUid) {
+ for (int idx = 0; mixer->loops[idx]; idx++) {
+ for (int jdx = 0; jdx < mixer->loops[idx]->scount; jdx++) {
+ if (mixer->loops[idx]->subdevs[jdx]->uid && !strcasecmp(mixer->loops[idx]->subdevs[jdx]->uid, targetUid)) {
+ *loop = mixer->loops[idx];
+ return mixer->loops[idx]->subdevs[jdx];
+ }
+ }
+ }
+ } else {
+ for (int idx = 0; mixer->loops[idx]; idx++) {
+ for (int jdx = 0; mixer->loops[idx]->subdevs[jdx]; jdx++) {
+ if (!mixer->loops[idx]->subdevs[jdx]->uid) {
+ mixer->loops[idx]->subdevs[jdx]->uid = streamUid;
+ *loop = mixer->loops[idx];
+ return mixer->loops[idx]->subdevs[jdx];
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *subdevJ) {
+ AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaPcmCtlT));
+
+ int error = wrap_json_unpack(subdevJ, "{s?s, si,si,s?o !}"
+ , "uid", &subdev->uid
+ , "subdev", &subdev->index
+ , "numid", &subdev->numid
+ );
+ if (error) {
+ AFB_ApiError(mixer->api, "ProcessOneSubdev: loop=%s missing (uid|subdev|numid) error=%s json=%s", loop->uid, wrap_json_get_error_string(error),json_object_get_string(subdevJ));
+ goto OnErrorExit;
+ }
+
+ // subdev with no UID are dynamically attached
+ if (subdev->uid) subdev->uid = strdup(subdev->uid);
+
+ // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard
+ AlsaDevInfoT loopSubdev;
+ loopSubdev.devpath=NULL;
+ loopSubdev.cardid=NULL;
+ loopSubdev.cardidx = loop->sndcard->cid.cardidx;
+ loopSubdev.device = loop->capture;
+ loopSubdev.subdev = subdev->index;
+
+ // assert we may open this loopback subdev in capture mode
+ AlsaPcmCtlT *pcmInfo = AlsaByPathOpenPcm(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE);
+ if (!pcmInfo) goto OnErrorExit;
+
+ // free PCM as we only open loop to assert it's a valid capture device
+ snd_pcm_close(pcmInfo->handle);
+ free(pcmInfo);
+
+ return subdev;
+
+OnErrorExit:
+ return NULL;
+}
+
+STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+ AlsaSndLoopT *loop = calloc(1, sizeof (AlsaSndLoopT));
+ json_object *subdevsJ = NULL, *devicesJ = NULL;
+ int error;
+
+ loop->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,so,so !}"
+ , "uid", &loop->uid
+ , "path", &loop->sndcard->cid.devpath
+ , "cardid", &loop->sndcard->cid.cardid
+ , "devices", &devicesJ
+ , "subdevs", &subdevsJ
+ );
+ if (error || !loop->uid || !subdevsJ || (!loop->sndcard->cid.devpath && !loop->sndcard->cid.cardid)) {
+ AFB_ApiNotice(mixer->api, "AttachOneLoop mixer=%s hal=%s missing 'uid|path|cardid|devices|subdevs' error=%s args=%s", mixer->uid, uid, wrap_json_get_error_string(error),json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ // try to open sound card control interface
+ loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard);
+ if (!loop->sndcard->ctl) {
+ AFB_ApiError(mixer->api, "AttachOneLoop mixer=%s hal=%s Fail open sndcard loop=%s devpath=%s cardid=%s (please check 'modprobe snd_aloop')", mixer->uid, uid, loop->uid, loop->sndcard->cid.devpath, loop->sndcard->cid.cardid);
+ goto OnErrorExit;
+ }
+
+ // Default devices is payback=0 capture=1
+ if (!devicesJ) {
+ loop->playback = 0;
+ loop->capture = 1;
+ } else {
+ error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback);
+ if (error) {
+ AFB_ApiNotice(mixer->api, "AttachOneLoop mixer=%s hal=%s Loop=%s missing 'capture|playback' error=%s devices=%s", mixer->uid, uid, loop->uid, wrap_json_get_error_string(error),json_object_get_string(devicesJ));
+ goto OnErrorExit;
+ }
+ }
+
+ switch (json_object_get_type(subdevsJ)) {
+ case json_type_object:
+ loop->scount = 1;
+ loop->subdevs = calloc(2, sizeof (void*));
+ loop->subdevs[0] = ProcessOneSubdev(mixer, loop, subdevsJ);
+ if (!loop->subdevs[0]) goto OnErrorExit;
+ break;
+ case json_type_array:
+ loop->scount = (int) json_object_array_length(subdevsJ);
+ loop->subdevs = calloc(loop->scount + 1, sizeof (void*));
+ for (int idx = 0; idx < loop->scount; idx++) {
+ json_object *subdevJ = json_object_array_get_idx(subdevsJ, idx);
+ loop->subdevs[idx] = ProcessOneSubdev(mixer, loop, subdevJ);
+ if (!loop->subdevs[idx]) goto OnErrorExit;
+ }
+ break;
+ default:
+ AFB_ApiError(mixer->api, "AttachOneLoop mixer=%s hal=%s Loop=%s invalid subdevs= %s", mixer->uid, uid, loop->uid, json_object_get_string(subdevsJ));
+ goto OnErrorExit;
+ }
+
+ // we may have to register up to 3 control per subdevice (vol, pause, actif)
+ loop->sndcard->registry = calloc(loop->scount * SMIXER_SUBDS_CTLS + 1, sizeof (RegistryEntryPcmT));
+ loop->sndcard->rcount = loop->scount*SMIXER_SUBDS_CTLS;
+
+ return loop;
+
+OnErrorExit:
+ return NULL;
+}
+
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+
+ int index;
+ for (index = 0; index < mixer->max.loops; index++) {
+ if (!mixer->loops[index]) break;
+ }
+
+ if (index == mixer->max.loops) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max loop=%d argsJ= %s", mixer->uid, uid, mixer->max.loops, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ switch (json_object_get_type(argsJ)) {
+ size_t count;
+
+ case json_type_object:
+ mixer->loops[index] = AttachOneLoop(mixer, uid, argsJ);
+ if (!mixer->loops[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s invalid loop= %s", mixer->uid, uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ break;
+
+ case json_type_array:
+ count = json_object_array_length(argsJ);
+ if (count > (mixer->max.loops - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max loop=%d argsJ= %s", mixer->uid, uid, mixer->max.loops, json_object_get_string(argsJ));
+ goto OnErrorExit;
+
+ }
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *loopJ = json_object_array_get_idx(argsJ, idx);
+ mixer->loops[index + idx] = AttachOneLoop(mixer, uid, loopJ);
+ if (!mixer->loops[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s invalid loop= %s", mixer->uid, uid, json_object_get_string(loopJ));
+ goto OnErrorExit;
+ }
+ }
+ break;
+ default:
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s loops invalid argsJ= %s", mixer->uid, uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+} \ No newline at end of file
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index b910b9f..300dd6a 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -23,186 +23,271 @@
#include <pthread.h>
-// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-// API
-
-static void MixerApiVerbCB(AFB_ReqT request) {
- json_object *valueJ, *backendJ = NULL, *frontendJ = NULL, *zonesJ = NULL, *streamsJ = NULL, *listJ = NULL;
- // retrieve action handle from request and execute the request
- json_object *argsJ = afb_request_json(request);
- json_object *responseJ = json_object_new_object();
-
- SoftMixerHandleT *mixer = (SoftMixerHandleT*) afb_request_get_vcbdata(request);
+static void MixerRemoveVerb(AFB_ReqT request) {
+ SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
int error;
- int delete = 0;
-
- CtlSourceT *source = alloca(sizeof (CtlSourceT));
- source->uid = mixer->uid;
- source->api = request->dynapi;
- source->request = request;
- source->context = mixer;
-
- error = wrap_json_unpack(argsJ, "{s?b,s?o,s?o,s?o,s?o,s?o !}"
- , "delete", &delete
- , "list", &listJ
- , "backend", &backendJ
- , "frontend", &frontendJ
- , "zones", &zonesJ
- , "streams", &streamsJ
- );
- if (error) {
- AFB_ReqFailF(request, "invalid-syntax", "request missing 'uid|list|backend|frontend|zones|streams' mixer=%s", json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- // Free attached resources and free mixer
- if (delete) {
- for (int idx; mixer->streams[idx].uid; idx++) {
- AlsaLoopStreamT *stream = &mixer->streams[idx];
-
- AFB_ApiNotice(source->api, "cleaning mixer=%s stream=%s", mixer->uid, stream->uid);
-
- error = pthread_cancel(stream->copy.thread);
- if (error) {
- AFB_ReqFailF(request, "internal-error", "Fail to kill audio-stream threads mixer=%s", mixer->uid);
- goto OnErrorExit;
- }
-
- char apiStreamVerb[128];
- error = snprintf(apiStreamVerb, sizeof (apiStreamVerb), "%s/%s", mixer->uid, stream->uid);
- if (error == sizeof (apiStreamVerb)) {
- AFB_ApiError(source->api, "LoopStreams mixer=%s fail to Registry Stream API too long %s/%s", mixer->uid, mixer->uid, stream->uid);
- goto OnErrorExit;
- }
- error = afb_dynapi_sub_verb(source->api, apiStreamVerb);
- if (error) {
- AFB_ApiError(source->api, "fail to Clean API verb=%s", apiStreamVerb);
- goto OnErrorExit;
- }
+ for (int idx; mixer->streams[idx]->uid; idx++) {
+ AlsaStreamAudioT *stream = mixer->streams[idx];
- // free audio-stream dynamic structures
- snd_pcm_close(mixer->streams[idx].copy.pcmIn);
- snd_pcm_close(mixer->streams[idx].copy.pcmOut);
- if (stream->copy.evtsrc) sd_event_source_unref(stream->copy.evtsrc);
- if (stream->copy.sdLoop) sd_event_unref(stream->copy.sdLoop);
+ AFB_ApiNotice(mixer->api, "cleaning mixer=%s stream=%s", mixer->uid, stream->uid);
+ error = pthread_cancel(stream->copy->thread);
+ if (error) {
+ AFB_ReqFailF(request, "internal-error", "Fail to kill audio-stream threads mixer=%s", mixer->uid);
+ goto OnErrorExit;
}
- // registry is attached to frontend
- if (mixer->frontend->registry)free(mixer->frontend->registry);
-
- error = afb_dynapi_sub_verb(source->api, mixer->uid);
+ error = afb_dynapi_sub_verb(mixer->api, stream->uid);
if (error) {
- AFB_ApiError(source->api, "fail to Clean API verb=%s", mixer->uid);
+ AFB_ReqFailF(request, "internal-error", "Mixer=%s fail to remove verb=%s error=%s", mixer->uid, stream->uid, strerror(error));
goto OnErrorExit;
}
- // finally free mixer handle
- free(mixer);
- responseJ = json_object_new_string("Fulup: delete might not clean everything properly");
- goto OnSuccessExit;
+ // free audio-stream dynamic structures
+ snd_pcm_close(mixer->streams[idx]->copy->pcmIn);
+ snd_pcm_close(mixer->streams[idx]->copy->pcmOut);
+ if (stream->copy->evtsrc) sd_event_source_unref(stream->copy->evtsrc);
+ if (stream->copy->sdLoop) sd_event_unref(stream->copy->sdLoop);
}
- if (listJ) {
- int streams = 0, quiet = 0, backend = 0, frontend = 0, zones = 0;
+ // // (Fulup to be Done) registry is attached to source
+ // if (mixer->sources) ApiSourcFree (mixer);
+ // if (mixer->sinks) ApiSinkFree (mixer);
+ // if (mixer->loops) ApiLoopFree (mixer);
+ // if (mixer->ramps) ApiRampFree (mixer);
+ // if (mixer->zones) ApiZoneFree (mixer);
+
+ // finally free mixer handle
+ free(mixer);
+ AFB_ReqSucess(request, NULL, "Fulup: delete might not clean everything properly");
+
+ return;
+
+OnErrorExit:
+ AFB_ReqFail(request, "internal-error", "fail to delete mixer");
+}
- error = wrap_json_unpack(listJ, "{s?b,s?b,s?b,s?b,s?b !}"
+STATIC void MixerInfoVerb(AFB_ReqT request) {
+
+ SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
+ json_object *argsJ = afb_request_json(request);
+ int error, stream = 0, quiet = 0, backend = 0, source = 0, zones = 0;
+
+ if (json_object_get_type(argsJ) == json_type_null) {
+ stream = 1;
+ } else {
+ error = wrap_json_unpack(argsJ, "{s?b,s?b,s?b,s?b,s?b !}"
, "quiet", &quiet
- , "streams", &streams
+ , "stream", &stream
, "backend", &backend
- , "frontend", &frontend
+ , "source", &source
, "zones", &zones
);
if (error) {
- AFB_ReqFailF(request, "invalid-syntax", "list missing 'uid|backend|frontend|zones|streams' list=%s", json_object_get_string(listJ));
+ AFB_ReqFailF(request, "invalid-syntax", "list missing 'quiet|stream|backend|source' argsJ=%s", json_object_get_string(argsJ));
goto OnErrorExit;
}
+ }
+ json_object *responseJ = json_object_new_object();
- if (streams) {
- streamsJ = json_object_new_array();
-
- AlsaLoopStreamT *streams = mixer->streams;
- for (int idx = 0; streams[idx].uid; idx++) {
- if (quiet) {
- json_object_array_add(streamsJ, json_object_new_string(streams[idx].uid));
- } else {
- json_object *numidJ;
- wrap_json_pack(&numidJ, "{si,si}"
- , "volume", streams[idx].volume
- , "mute", streams[idx].mute
- );
- wrap_json_pack(&valueJ, "{ss,so}"
- , "uid", streams[idx].uid
- , "numid", numidJ
- );
- json_object_array_add(streamsJ, valueJ);
- AFB_ApiWarning(request->dynapi, "stream=%s", json_object_get_string(streamsJ));
- }
-
+ if (stream) {
+ json_object *streamsJ = json_object_new_array();
+ json_object *valueJ;
+
+ AlsaStreamAudioT **streams = mixer->streams;
+ for (int idx = 0; streams[idx]->uid; idx++) {
+ if (quiet) {
+ json_object_array_add(streamsJ, json_object_new_string(streams[idx]->uid));
+ } else {
+ json_object *numidJ;
+ wrap_json_pack(&numidJ, "{si,si}"
+ , "volume", streams[idx]->volume
+ , "mute", streams[idx]->mute
+ );
+ wrap_json_pack(&valueJ, "{ss,so}"
+ , "uid", streams[idx]->uid
+ , "numid", numidJ
+ );
+ json_object_array_add(streamsJ, valueJ);
+ AFB_ApiWarning(request->dynapi, "stream=%s", json_object_get_string(streamsJ));
}
- json_object_object_add(responseJ, "streams", streamsJ);
- }
- if (backend || frontend || zones) {
- AFB_ReqFailF(request, "not implemented", "(Fulup) list action Still To Be Done");
- goto OnErrorExit;
}
+ json_object_object_add(responseJ, "streams", streamsJ);
+ }
+
+ if (backend || source || zones) {
+ AFB_ReqFailF(request, "not implemented", "(Fulup) list action Still To Be Done");
+ goto OnErrorExit;
+ }
+
+ AFB_ReqSucess(request, responseJ, NULL);
+ return;
+
+OnErrorExit:
+ AFB_ReqFail(request, "internal-error", "fail to get mixer info");
+
+}
+
+STATIC void MixerAttachVerb(AFB_ReqT request) {
+ SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
+ const char *uid=NULL;
+ json_object *playbackJ = NULL, *captureJ = NULL, *zonesJ = NULL, *streamsJ = NULL, *rampsJ = NULL, *loopsJ = NULL;
+ json_object *argsJ = afb_request_json(request);
+ json_object *responseJ;
+ int error;
+
+ error = wrap_json_unpack(argsJ, "{ss,s?o,s?o,s?o,s?o,s?o,s?o !}"
+ , "uid", &uid
+ , "ramps", &rampsJ
+ , "playbacks", &playbackJ
+ , "captures", &captureJ
+ , "loops", &loopsJ
+ , "zones", &zonesJ
+ , "streams", &streamsJ
+ );
+ if (error) {
+ AFB_ApiError(mixer->api, "MixerAttachVerb: invalid-syntax mixer=%s error=%s args=%s", mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s missing 'uid|ramps|playbacks|captures|zones|streams' error=%s args=%s", mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
- AFB_ReqSucess(request, responseJ, NULL);
- return;
+ if (playbackJ) {
+ error = ApiSinkAttach(mixer, request, uid, playbackJ);
+ if (error) goto OnErrorExit;
}
- if (backendJ) {
- error = SndBackend(source, backendJ);
+ if (captureJ) {
+ error = ApiSourceAttach(mixer, request, uid, captureJ);
if (error) goto OnErrorExit;
}
- if (frontendJ) {
- error = SndFrontend(source, frontendJ);
+ if (loopsJ) {
+ error = ApiLoopAttach(mixer, request, uid, loopsJ);
if (error) goto OnErrorExit;
}
if (zonesJ) {
- error = SndZones(source, zonesJ);
+ error = ApiZoneAttach(mixer, request, uid, zonesJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (rampsJ) {
+ error = ApiRampAttach(mixer, request, uid, rampsJ);
if (error) goto OnErrorExit;
}
if (streamsJ) {
- error = LoopStreams(source, streamsJ, &responseJ);
+ error = ApiStreamAttach(mixer, request, uid, streamsJ, &responseJ);
if (error) goto OnErrorExit;
}
-OnSuccessExit:
- AFB_ReqSucess(request, responseJ, mixer->uid);
+ AFB_ReqNotice(request, "**** mixer=%s response=%s", json_object_get_string(responseJ), mixer->uid);
+ AFB_ReqSucess(request, NULL, mixer->uid);
return;
OnErrorExit:
return;
}
+STATIC void MixerPingVerb(AFB_ReqT request) {
+ static int count = 0;
+ count++;
+ AFB_ReqNotice(request, "Controller:ping count=%d", count);
+ AFB_ReqSucess(request, json_object_new_int(count), NULL);
+
+ return;
+}
+
+STATIC void MixerEventCB(AFB_ApiT api, const char *evtLabel, struct json_object *eventJ) {
+
+ SoftMixerT *mixer = (SoftMixerT*) afb_dynapi_get_userdata(api);
+ assert(mixer);
+
+ AFB_ApiNotice(api, "Mixer=%s Received event=%s, eventJ=%s", mixer->uid, evtLabel, json_object_get_string(eventJ));
+}
+
+// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT
+STATIC AFB_ApiVerbs CtrlApiVerbs[] = {
+ /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */
+ { .verb = "ping", .callback = MixerPingVerb, .info = "ping count test"},
+ { .verb = "attach", .callback = MixerAttachVerb, .info = "attach resources to mixer"},
+ { .verb = "remove", .callback = MixerRemoveVerb, .info = "remove existing mixer streams, zones, ..."},
+ { .verb = "info", .callback = MixerInfoVerb, .info = "list existing mixer streams, zones, ..."},
+ { .verb = NULL} /* marker for end of the array */
+};
+
+STATIC int LoadStaticVerbs(SoftMixerT *mixer, AFB_ApiVerbs *verbs) {
+ int errcount = 0;
+
+ for (int idx = 0; verbs[idx].verb; idx++) {
+ errcount += afb_dynapi_add_verb(mixer->api, CtrlApiVerbs[idx].verb, CtrlApiVerbs[idx].info, CtrlApiVerbs[idx].callback, (void*) mixer, CtrlApiVerbs[idx].auth, 0);
+ }
+
+ return errcount;
+};
+
+STATIC int MixerInitCB(AFB_ApiT api) {
+
+ SoftMixerT *mixer = (SoftMixerT*) afb_dynapi_get_userdata(api);
+ assert(mixer);
+
+ // attach AFB mainloop to mixer
+ mixer->sdLoop = AFB_GetEventLoop(api);
+
+
+ AFB_ApiNotice(api, "MixerInitCB API=%s activated info=%s", mixer->uid, mixer->info);
+
+ return 0;
+}
+
+STATIC int MixerApiCB(void* handle, AFB_ApiT api) {
+ SoftMixerT *mixer = (SoftMixerT*) handle;
+
+ mixer->api= api;
+ afb_dynapi_set_userdata(api, mixer);
+ afb_dynapi_on_event(api, MixerEventCB);
+ afb_dynapi_on_init(api, MixerInitCB);
+
+ int error = LoadStaticVerbs(mixer, CtrlApiVerbs);
+ if (error) goto OnErrorExit;
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+}
+
CTLP_LUA2C(_mixer_new_, source, argsJ, responseJ) {
- SoftMixerHandleT *mixer = calloc(1, sizeof (SoftMixerHandleT));
- json_object *backendJ = NULL, *frontendJ = NULL, *zonesJ = NULL, *streamsJ = NULL;
+ SoftMixerT *mixer = calloc(1, sizeof (SoftMixerT));
int error;
- assert(source->api);
+ mixer->max.loops = SMIXER_DEFLT_RAMPS;
+ mixer->max.sinks = SMIXER_DEFLT_SINKS;
+ mixer->max.sources = SMIXER_DEFLT_SOURCES;
+ mixer->max.zones = SMIXER_DEFLT_ZONES;
+ mixer->max.streams = SMIXER_DEFLT_STREAMS;
+ mixer->max.ramps = SMIXER_DEFLT_RAMPS;
if (json_object_get_type(argsJ) != json_type_object) {
AFB_ApiError(source->api, "_mixer_new_: invalid object type= %s", json_object_get_string(argsJ));
goto OnErrorExit;
}
- error = wrap_json_unpack(argsJ, "{ss,s?s,s?o,s?o,s?o,s?o !}"
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?i,s?i,s?i,s?i,s?i,s?i !}"
, "uid", &mixer->uid
, "info", &mixer->info
- , "backend", &backendJ
- , "frontend", &frontendJ
- , "zones", &zonesJ
- , "streams", &streamsJ);
+ , "max_loop", &mixer->max.loops
+ , "max_sink", &mixer->max.sinks
+ , "max_source", &mixer->max.sources
+ , "max_zone", &mixer->max.zones
+ , "max_stream", &mixer->max.streams
+ , "max_ramp", &mixer->max.ramps
+ );
if (error) {
- AFB_ApiNotice(source->api, "_mixer_new_ missing 'uid|backend|frontend|zones|streams' mixer=%s", json_object_get_string(argsJ));
+ AFB_ApiNotice(source->api, "_mixer_new_ missing 'uid|max_loop|max_sink|max_source|max_zone|max_stream|max_ramp' error=%s mixer=%s", wrap_json_get_error_string(error),json_object_get_string(argsJ));
goto OnErrorExit;
}
@@ -210,34 +295,18 @@ CTLP_LUA2C(_mixer_new_, source, argsJ, responseJ) {
mixer->uid = strdup(mixer->uid);
if (mixer->info)mixer->info = strdup(mixer->info);
+ mixer->loops = calloc(mixer->max.loops+1, sizeof (AlsaSndLoopT));
+ mixer->sinks = calloc(mixer->max.sinks+1, sizeof (AlsaSndPcmT));
+ mixer->sources = calloc(mixer->max.sources+1, sizeof (AlsaSndPcmT));
+ mixer->zones = calloc(mixer->max.zones+1, sizeof (AlsaSndZoneT));
+ mixer->streams = calloc(mixer->max.streams+1, sizeof (AlsaStreamAudioT));
+ mixer->ramps = calloc(mixer->max.ramps+1, sizeof (AlsaVolRampT));
+
// create mixer verb within API.
- error = afb_dynapi_add_verb(source->api, mixer->uid, mixer->info, MixerApiVerbCB, mixer, NULL, 0);
+ error = afb_dynapi_new_api(source->api, mixer->uid, mixer->info, !MAINLOOP_CONCURENCY, MixerApiCB, mixer);
if (error) {
AFB_ApiError(source->api, "_mixer_new_ mixer=%s fail to Registry API verb", mixer->uid);
- return -1;
- }
-
- // make sure sub command get access to mixer handle
- source->context = mixer;
-
- if (backendJ) {
- error = SndBackend(source, backendJ);
- if (error) goto OnErrorExit;
- }
-
- if (frontendJ) {
- error = SndFrontend(source, frontendJ);
- if (error) goto OnErrorExit;
- }
-
- if (zonesJ) {
- error = SndZones(source, zonesJ);
- if (error) goto OnErrorExit;
- }
-
- if (streamsJ) {
- error = LoopStreams(source, streamsJ, responseJ);
- if (error) goto OnErrorExit;
+ goto OnErrorExit;
}
return 0;
diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c
new file mode 100644
index 0000000..228b7a7
--- /dev/null
+++ b/plugins/alsa/alsa-api-pcm.c
@@ -0,0 +1,304 @@
+/*
+ * 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"
+#include <math.h>
+
+// move from vol % to absolute value
+#define CONVERT_RANGE(val, min, max) ceil((val) * ((max) - (min)) * 0.01 + (min))
+#define CONVERT_VOLUME(val, min, max) (int) CONVERT_RANGE ((double)val, (double)min, (double)max)
+
+
+STATIC AlsaPcmChannelT *ProcessOneChannel(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+ AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT));
+ int error = wrap_json_unpack(argsJ, "{ss,si !}", "uid", &channel->uid, "port", &channel->port);
+ if (error) goto OnErrorExit;
+
+ channel->uid = strdup(channel->uid);
+ return channel;
+
+OnErrorExit:
+ AFB_ApiError(mixer->api, "ProcessOneChannel: sndcard=%s channel: missing (uid||port) error=%s json=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ free(channel);
+ return NULL;
+}
+
+STATIC int ProcessOneControl(SoftMixerT *mixer, AlsaSndPcmT* pcm, json_object *argsJ, AlsaSndControlT *control) {
+ snd_ctl_elem_id_t* elemId = NULL;
+ snd_ctl_elem_info_t *elemInfo;
+ int numid = 0;
+ long value = ALSA_DEFAULT_PCM_VOLUME;
+ const char *name;
+
+
+ int error = wrap_json_unpack(argsJ, "{s?i,s?s,s?i !}"
+ , "numid", &numid
+ , "name", &name
+ , "value", &value
+ );
+ if (error || (!numid && !name)) {
+ AFB_ApiError(mixer->api, "ProcessOneControl: sndcard=%s channel: missing (numid|name|value) error=%s json=%s", pcm->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ if (numid > 0) {
+ elemId = AlsaCtlGetNumidElemId(mixer, pcm->sndcard, numid);
+ if (!elemId) {
+ AFB_ApiError(mixer->api, "ProcessOneControl sndard=%s fail to find control numid=%d", pcm->sndcard->cid.cardid, numid);
+ goto OnErrorExit;
+ }
+
+ } else {
+ elemId = AlsaCtlGetNameElemId(mixer, pcm->sndcard, name);
+ if (!elemId) {
+ AFB_ApiError(mixer->api, "ProcessOneControl sndard=%s fail to find control name=%s", pcm->sndcard->cid.cardid, name);
+ goto OnErrorExit;
+ }
+ }
+
+ snd_ctl_elem_info_alloca(&elemInfo);
+ snd_ctl_elem_info_set_id(elemInfo, elemId);
+ control->name = strdup(snd_ctl_elem_info_get_name(elemInfo));
+ control->numid = snd_ctl_elem_info_get_numid(elemInfo);
+
+ if (snd_ctl_elem_info(pcm->sndcard->ctl, elemInfo) < 0) {
+ AFB_ApiError(mixer->api, "ProcessOneControl: sndard=%s numid=%d name='%s' not loadable", pcm->sndcard->cid.cardid, control->numid, control->name);
+ goto OnErrorExit;
+ }
+
+ if (!snd_ctl_elem_info_is_writable(elemInfo)) {
+ AFB_ApiError(mixer->api, "ProcessOneControl: sndard=%s numid=%d name='%s' not writable", pcm->sndcard->cid.cardid, control->numid, control->name);
+ goto OnErrorExit;
+ }
+
+ control->count = snd_ctl_elem_info_get_count(elemInfo);
+ switch (snd_ctl_elem_info_get_type(elemInfo)) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ control->min = 0;
+ control->max = 1;
+ control->step = 0;
+ error = CtlElemIdSetLong(mixer, pcm->sndcard, elemId, value);
+ break;
+
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ control->min = snd_ctl_elem_info_get_min(elemInfo);
+ control->max = snd_ctl_elem_info_get_max(elemInfo);
+ control->step = snd_ctl_elem_info_get_step(elemInfo);
+ error = CtlElemIdSetLong(mixer, pcm->sndcard, elemId, (int) CONVERT_VOLUME(value, control->min, control->max));
+ break;
+
+ default:
+ AFB_ApiError(mixer->api, "ProcessOneControl: sndard=%s numid=%d name='%s' invalid/unsupported type=%d", pcm->sndcard->cid.cardid, control->numid, control->name, snd_ctl_elem_info_get_type(elemInfo));
+ goto OnErrorExit;
+ }
+
+ if (error) {
+ AFB_ApiError(mixer->api, "ProcessOneControl: sndard=%s numid=%d name='%s' not writable", pcm->sndcard->cid.cardid, control->numid, control->name);
+ goto OnErrorExit;
+ }
+
+ free(elemId);
+
+ return 0;
+
+OnErrorExit:
+ if (elemId)free(elemId);
+ return -1;
+}
+
+PUBLIC AlsaPcmHwInfoT *ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object *paramsJ) {
+ AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT));
+ const char *format = NULL, *access = NULL;
+
+ // some default values
+ params->rate = ALSA_DEFAULT_PCM_RATE;
+ params->channels = 2;
+ params->sampleSize = 0;
+
+ if (paramsJ) {
+ int error = wrap_json_unpack(paramsJ, "{s?i,s?i, s?s, s?s !}", "rate", &params->rate, "channels", &params->channels, "format", &format, "access", &access);
+ if (error) {
+ AFB_ApiError(mixer->api, "ApiPcmSetParams: sndcard=%s invalid params=%s", uid, json_object_get_string(paramsJ));
+ goto OnErrorExit;
+ }
+ }
+
+ if (!format) {
+ params->format = SND_PCM_FORMAT_S16_LE;
+ params->formatS = "S16_LE";
+ } else {
+ params->formatS = strdup(format);
+ if (!strcasecmp(format, "S16_LE")) params->format = SND_PCM_FORMAT_S16_LE;
+ else if (!strcasecmp(format, "S16_BE")) params->format = SND_PCM_FORMAT_S16_BE;
+ else if (!strcasecmp(format, "U16_LE")) params->format = SND_PCM_FORMAT_U16_LE;
+ else if (!strcasecmp(format, "U16_BE")) params->format = SND_PCM_FORMAT_U16_BE;
+ else if (!strcasecmp(format, "S32_LE")) params->format = SND_PCM_FORMAT_S32_LE;
+ else if (!strcasecmp(format, "S32_BE")) params->format = SND_PCM_FORMAT_S32_BE;
+ else if (!strcasecmp(format, "U32_LE")) params->format = SND_PCM_FORMAT_U32_LE;
+ else if (!strcasecmp(format, "U32_BE")) params->format = SND_PCM_FORMAT_U32_BE;
+ else if (!strcasecmp(format, "S24_LE")) params->format = SND_PCM_FORMAT_S24_LE;
+ else if (!strcasecmp(format, "S24_BE")) params->format = SND_PCM_FORMAT_S24_BE;
+ else if (!strcasecmp(format, "U24_LE")) params->format = SND_PCM_FORMAT_U24_LE;
+ else if (!strcasecmp(format, "U24_BE")) params->format = SND_PCM_FORMAT_U24_BE;
+ else if (!strcasecmp(format, "S8")) params->format = SND_PCM_FORMAT_S8;
+ else if (!strcasecmp(format, "U8")) params->format = SND_PCM_FORMAT_U8;
+ else if (!strcasecmp(format, "FLOAT_LE")) params->format = SND_PCM_FORMAT_FLOAT_LE;
+ else if (!strcasecmp(format, "FLOAT_BE")) params->format = SND_PCM_FORMAT_FLOAT_LE;
+ else {
+ AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s(params) unsupported format 'S16_LE|S32_L|...' format=%s", uid, format);
+ goto OnErrorExit;
+ }
+ }
+
+
+ if (!access) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ else if (!strcasecmp(access, "MMAP_INTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ else if (!strcasecmp(access, "MMAP_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ else if (!strcasecmp(access, "MMAP_COMPLEX")) params->access = SND_PCM_ACCESS_MMAP_COMPLEX;
+ else if (!strcasecmp(access, "RW_INTERLEAVED")) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ else if (!strcasecmp(access, "RW_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
+
+ else {
+ AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", uid, access);
+ goto OnErrorExit;
+ }
+
+ return params;
+
+OnErrorExit:
+ free(params);
+ return NULL;
+}
+
+PUBLIC AlsaSndPcmT *ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object *argsJ) {
+ AlsaSndPcmT *pcm = calloc(1, sizeof (AlsaSndPcmT));
+ json_object *sourceJ = NULL, *paramsJ = NULL, *sinkJ = NULL, *targetJ;
+ int error;
+
+ pcm->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?i,s?i,s?o,s?o,s?o !}"
+ , "uid", &pcm->uid
+ , "path", &pcm->sndcard->cid.devpath
+ , "cardid", &pcm->sndcard->cid.cardid
+ , "device", &pcm->sndcard->cid.device
+ , "subdev", &pcm->sndcard->cid.subdev
+ , "sink", &sinkJ
+ , "source", &sourceJ
+ , "params", &paramsJ
+ );
+ if (error) {
+ AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ // try to open sound card control interface
+ pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard);
+ if (!pcm->sndcard->ctl) {
+ AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s", uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid);
+ goto OnErrorExit;
+ }
+
+ // check sndcard accepts params
+ pcm->sndcard->params = ApiPcmSetParams(mixer, pcm->uid, paramsJ);
+ if (!pcm->sndcard->params) {
+ AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to set params sndcard uid=%s params=%s", uid, pcm->uid, json_object_get_string(paramsJ));
+ goto OnErrorExit;
+ }
+
+ if (direction == SND_PCM_STREAM_PLAYBACK) {
+ if (!sinkJ) {
+ AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_PLAYBACK require sinks args=%s", uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ targetJ = sinkJ;
+ }
+
+ if (direction == SND_PCM_STREAM_CAPTURE) {
+ if (!sourceJ) {
+ AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_CAPTURE require sources args=%s", uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ targetJ = sourceJ;
+
+ // we may have to register SMIXER_SUBDS_CTLS per subdev (Fulup ToBeDone when sndcard get multiple device/subdev)
+ pcm->sndcard->registry = calloc(SMIXER_SUBDS_CTLS+1, sizeof (RegistryEntryPcmT));
+ pcm->sndcard->rcount = SMIXER_SUBDS_CTLS;
+ }
+
+ json_object *channelsJ = NULL, *controlsJ = NULL;
+ error = wrap_json_unpack(targetJ, "{so,s?o !}"
+ , "channels", &channelsJ
+ , "controls", &controlsJ
+ );
+ if (error) {
+ AFB_ApiNotice(mixer->api, "ApiPcmAttachOne: hal=%s pcms missing channels|[controls] error=%s paybacks=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ if (channelsJ) {
+ switch (json_object_get_type(channelsJ)) {
+
+ case json_type_object:
+ pcm->ccount = 1;
+ pcm->channels = calloc(2, sizeof (void*));
+ pcm->channels[0] = ProcessOneChannel(mixer, pcm->uid, channelsJ);
+ if (!pcm->channels[0]) goto OnErrorExit;
+ break;
+ case json_type_array:
+ pcm->ccount = (int) json_object_array_length(channelsJ);
+ pcm->channels = calloc(pcm->ccount + 1, sizeof (void*));
+ for (int idx = 0; idx < pcm->ccount; idx++) {
+ json_object *channelJ = json_object_array_get_idx(channelsJ, idx);
+ pcm->channels[idx] = ProcessOneChannel(mixer, pcm->uid, channelJ);
+ if (!pcm->channels[idx]) goto OnErrorExit;
+ }
+ break;
+ default:
+ AFB_ApiError(mixer->api, "ProcessPcmControls:%s invalid pcm=%s", pcm->uid, json_object_get_string(channelsJ));
+ goto OnErrorExit;
+ }
+ }
+
+ if (controlsJ) {
+ json_object *volJ = NULL, *muteJ = NULL;
+ error = wrap_json_unpack(controlsJ, "{s?o,s?o !}"
+ , "volume", &volJ
+ , "mute", &muteJ
+ );
+ if (error) {
+ AFB_ApiNotice(mixer->api, "ProcessPcmControls: source missing [volume]|[mute] error=%s control=%s", wrap_json_get_error_string(error), json_object_get_string(controlsJ));
+ goto OnErrorExit;
+ }
+
+ if (volJ) error += ProcessOneControl(mixer, pcm, volJ, &pcm->volume);
+ if (muteJ) error += ProcessOneControl(mixer, pcm, muteJ, &pcm->mute);
+ if (error) goto OnErrorExit;
+ }
+
+ // free useless resource and secure others
+ pcm->uid = strdup(pcm->uid);
+
+ return pcm;
+
+OnErrorExit:
+ free(pcm);
+ return NULL;
+}
+
diff --git a/plugins/alsa/alsa-api-ramp.c b/plugins/alsa/alsa-api-ramp.c
new file mode 100644
index 0000000..002aec0
--- /dev/null
+++ b/plugins/alsa/alsa-api-ramp.c
@@ -0,0 +1,114 @@
+/*
+ * 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"
+#include <string.h>
+
+// Set stream volume control in %
+#define VOL_CONTROL_MAX 100
+#define VOL_CONTROL_MIN 0
+#define VOL_CONTROL_STEP 1
+
+PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid) {
+ AlsaVolRampT *ramp = NULL;
+
+ // Loop on every Register zone pcm and extract (cardid) from (uid)
+ for (int idx = 0; mixer->ramps[idx]->uid != NULL; idx++) {
+ if (!strcasecmp(mixer->ramps[idx]->uid, uid)) {
+ ramp = mixer->ramps[idx];
+ return ramp;
+ }
+ }
+ return NULL;
+}
+
+STATIC AlsaVolRampT *AttachOneRamp(SoftMixerT *mixer, const char *uid, json_object *rampJ) {
+ const char*rampUid;
+ AlsaVolRampT *ramp = calloc(1, sizeof (AlsaVolRampT));
+
+ int error = wrap_json_unpack(rampJ, "{ss,si,si,si !}"
+ , "uid", &rampUid
+ , "delay", &ramp->delay
+ , "up", &ramp->stepUp
+ , "down", &ramp->stepDown
+ );
+ if (error) {
+ AFB_ApiError(mixer->api, "AttachOneRamp mixer=%s hal=%s error=%s json=%s", mixer->uid, uid, wrap_json_get_error_string(error), json_object_get_string(rampJ));
+ goto OnErrorExit;
+ }
+
+ ramp->delay = ramp->delay * 100; // move from ms to us
+ ramp->uid = strdup(rampUid);
+ return ramp;
+
+OnErrorExit:
+ free(ramp);
+ return NULL;
+}
+
+PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object *argsJ) {
+ int index;
+
+ for (index = 0; index < mixer->max.ramps; index++) {
+ if (!mixer->ramps[index]) break;
+ }
+
+ if (index == mixer->max.ramps) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max ramp=%d argsJ= %s", mixer->uid, uid, mixer->max.ramps, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ switch (json_object_get_type(argsJ)) {
+ long count;
+
+ case json_type_object:
+ mixer->ramps[index] = AttachOneRamp(mixer, uid, argsJ);
+ if (!mixer->ramps[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s invalid ramp= %s", mixer->uid, uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ break;
+
+ case json_type_array:
+ count = json_object_array_length(argsJ);
+ if (count > (mixer->max.ramps - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max ramp=%d argsJ= %s", mixer->uid, uid, mixer->max.ramps, json_object_get_string(argsJ));
+ goto OnErrorExit;
+
+ }
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *streamAudioJ = json_object_array_get_idx(argsJ, idx);
+ mixer->ramps[index + idx] = AttachOneRamp(mixer, uid, streamAudioJ);
+ if (!mixer->ramps[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s invalid ramp= %s", mixer->uid, uid, json_object_get_string(streamAudioJ));
+ goto OnErrorExit;
+ }
+ }
+ break;
+ default:
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s ramps invalid argsJ= %s", mixer->uid, uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ return 0;
+
+OnErrorExit:
+ return -1;
+}
diff --git a/plugins/alsa/alsa-api-sink.c b/plugins/alsa/alsa-api-sink.c
new file mode 100644
index 0000000..ab32276
--- /dev/null
+++ b/plugins/alsa/alsa-api-sink.c
@@ -0,0 +1,111 @@
+/*
+ * 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 AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *target) {
+
+ AlsaSndZoneT *zone = ApiZoneGetByUid(mixer, target);
+ if (!zone || !zone->sinks) return NULL;
+
+ // use 1st channel to find attached sound card.
+ const char *channel = zone->sinks[0]->uid;
+
+ // search for channel uid into mixer sinks
+ for (int idx = 0; mixer->sinks[idx]; idx++) {
+ for (int jdx = 0; jdx < mixer->sinks[idx]->ccount; jdx++) {
+ if (mixer->sinks[idx]->channels[jdx]->uid && !strcasecmp(channel, mixer->sinks[idx]->channels[jdx]->uid)) {
+ return mixer->sinks[idx]->sndcard->params;
+ }
+ }
+ }
+ return NULL;
+}
+
+PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+
+ int index;
+ for (index = 0; index < mixer->max.sinks; index++) {
+ if (!mixer->sinks[index]) break;
+ }
+
+ if (index == mixer->max.sinks) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d argsJ= %s", mixer->uid, mixer->max.sinks, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ switch (json_object_get_type(argsJ)) {
+ long count;
+ char *dmixUid;
+ AlsaPcmCtlT* dmixConfig;
+
+ case json_type_object:
+ mixer->sinks[index] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ);
+ if (!mixer->sinks[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ // move from hardware to DMIX attach to sndcard
+ (void) asprintf(&dmixUid, "dmix-%s", mixer->sinks[index]->uid);
+
+ dmixConfig = AlsaCreateDmix(mixer, dmixUid, mixer->sinks[index], 0);
+ if (!dmixConfig) {
+ AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, mixer->sinks[index]->uid);
+ goto OnErrorExit;
+ }
+
+ break;
+
+ case json_type_array:
+ count = json_object_array_length(argsJ);
+ if (count > (mixer->max.sinks - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d argsJ= %s", mixer->uid, mixer->max.sinks, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *sinkJ = json_object_array_get_idx(argsJ, idx);
+ mixer->sinks[index + idx] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, sinkJ);
+ if (!mixer->sinks[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(sinkJ));
+ goto OnErrorExit;
+ }
+
+ // move from hardware to DMIX attach to sndcard
+ (void) asprintf(&dmixUid, "dmix-%s", mixer->sinks[index]->uid);
+
+ dmixConfig = AlsaCreateDmix(mixer, dmixUid, mixer->sinks[index], 0);
+ if (!dmixConfig) {
+ AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, mixer->sinks[index]->uid);
+ goto OnErrorExit;
+ }
+ }
+ break;
+ default:
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sinks invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+} \ No newline at end of file
diff --git a/plugins/alsa/alsa-api-source.c b/plugins/alsa/alsa-api-source.c
new file mode 100644
index 0000000..6315a14
--- /dev/null
+++ b/plugins/alsa/alsa-api-source.c
@@ -0,0 +1,84 @@
+/*
+ * 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"
+#include <string.h>
+
+PUBLIC AlsaSndCtlT *ApiSourceFindSubdev(SoftMixerT *mixer, const char *target) {
+
+ // search for subdev into every registered source
+ for (int idx = 0; mixer->sources[idx]; idx++) {
+ if (mixer->sources[idx]->uid && !strcasecmp(mixer->sources[idx]->uid, target)) {
+ return mixer->sources[idx]->sndcard;
+ }
+ }
+ return NULL;
+}
+
+PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+
+ int index;
+ for (index = 0; index < mixer->max.sources; index++) {
+ if (!mixer->sources[index]) break;
+ }
+
+ if (index == mixer->max.sources) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max source=%d argsJ= %s", mixer->uid, mixer->max.sources, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ switch (json_object_get_type(argsJ)) {
+ long count;
+
+ case json_type_object:
+ mixer->sources[index] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_CAPTURE, argsJ);
+ if (!mixer->sources[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid source= %s", mixer->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ break;
+
+ case json_type_array:
+ count = json_object_array_length(argsJ);
+ if (count > (mixer->max.sources - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max source=%d argsJ= %s", mixer->uid, mixer->max.sources, json_object_get_string(argsJ));
+ goto OnErrorExit;
+
+ }
+
+ for (int idx = 0; idx < count; idx++) {
+ json_object *sourceJ = json_object_array_get_idx(argsJ, idx);
+ mixer->sources[index + idx] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_CAPTURE, sourceJ);
+ if (!mixer->sources[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid source= %s", mixer->uid, json_object_get_string(sourceJ));
+ goto OnErrorExit;
+ }
+ }
+ break;
+ default:
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sources invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+} \ No newline at end of file
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index 3923048..f531ed5 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -30,37 +30,22 @@
extern Lua2cWrapperT Lua2cWrap;
typedef struct {
- const char* verb;
- AlsaLoopStreamT *stream;
- SoftMixerHandleT *mixer;
+ AlsaStreamAudioT *stream;
+ SoftMixerT *mixer;
AlsaVolRampT *ramp;
+ AlsaSndCtlT *sndcard;
+ snd_pcm_t *pcm;
} apiHandleT;
-STATIC AlsaVolRampT* RampGetByUid(CtlSourceT *source, AlsaVolRampT *ramps, const char *uid) {
- AlsaVolRampT *ramp = NULL;
-
- // Loop on every Registryed zone pcm and extract (cardid) from (uid)
- for (int idx = 0; ramps[idx].uid != NULL; idx++) {
- if (!strcasecmp(ramps[idx].uid, uid)) {
- ramp = &ramps[idx];
- return ramp;
- }
- }
- return NULL;
-}
-
STATIC void StreamApiVerbCB(AFB_ReqT request) {
+ apiHandleT *handle = (apiHandleT*) afb_request_get_vcbdata(request);
int error, doClose = 0, doQuiet = 0, doToggle = 0, doMute = -1;
long mute, volume;
json_object *responseJ, *volumeJ = NULL, *rampJ = NULL, *argsJ = afb_request_json(request);
- apiHandleT *handle = (apiHandleT*) afb_request_get_vcbdata(request);
- snd_ctl_t *ctlDev = NULL;
- CtlSourceT *source = alloca(sizeof (CtlSourceT));
- source->uid = handle->verb;
- source->api = request->dynapi;
- source->request = NULL;
- source->context = NULL;
+ SoftMixerT *mixer = handle->mixer;
+ AlsaSndCtlT *sndcard = handle->sndcard;
+ assert(mixer && sndcard);
error = wrap_json_unpack(argsJ, "{s?b s?b,s?b,s?b,s?o,s?o !}"
, "quiet", &doQuiet
@@ -71,31 +56,25 @@ STATIC void StreamApiVerbCB(AFB_ReqT request) {
, "ramp", &rampJ
);
if (error) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "Missing 'close|mute|volume|quiet' args=%s", json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- ctlDev = AlsaCtlOpenCtl(source, handle->mixer->frontend->cardid);
- if (!ctlDev) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to open sndcard=%s", handle->mixer->frontend->cardid);
+ AFB_ReqFailF(request, "syntax-error", "Missing 'close|mute|volume|quiet' args=%s", json_object_get_string(argsJ));
goto OnErrorExit;
}
if (doClose) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "(Fulup) Close action still to be done mixer=%s", json_object_get_string(argsJ));
+ AFB_ReqFailF(request, "internal-error", "(Fulup) Close action still to be done mixer=%s stream=%s", mixer->uid, handle->stream->uid);
goto OnErrorExit;
}
if (doToggle) {
- error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->mute, &mute);
- error += AlsaCtlNumidSetLong(source, ctlDev, handle->stream->mute, !mute);
+ error += AlsaCtlNumidGetLong(mixer, sndcard, handle->stream->mute, &mute);
+ error += AlsaCtlNumidSetLong(mixer, sndcard, handle->stream->mute, !mute);
}
if (volumeJ) {
long curvol, newvol;
const char*volString;
- error = AlsaCtlNumidGetLong(source, ctlDev, handle->stream->volume, &curvol);
+ error = AlsaCtlNumidGetLong(mixer, handle->sndcard, handle->stream->volume, &curvol);
if (error) {
AFB_ReqFailF(request, "invalid-numid", "Fail to set volume numid=%d value=%ld", handle->stream->volume, volume);
goto OnErrorExit;
@@ -129,15 +108,15 @@ STATIC void StreamApiVerbCB(AFB_ReqT request) {
}
- error = AlsaCtlNumidSetLong(source, ctlDev, handle->stream->volume, newvol);
+ error = AlsaCtlNumidSetLong(mixer, handle->sndcard, handle->stream->volume, newvol);
if (error) {
AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%ld", handle->stream->volume, newvol);
goto OnErrorExit;
}
}
- if (rampJ) {
- error = AlsaVolRampApply(source, handle->mixer->frontend, handle->stream, handle->ramp, rampJ);
+ if (rampJ) {
+ error = AlsaVolRampApply(mixer, handle->sndcard, handle->stream, rampJ);
if (error) {
AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volram numid=%d value=%s", handle->stream->volume, json_object_get_string(rampJ));
goto OnErrorExit;
@@ -145,7 +124,7 @@ STATIC void StreamApiVerbCB(AFB_ReqT request) {
}
if (doMute != -1) {
- error = AlsaCtlNumidSetLong(source, ctlDev, handle->stream->mute, !mute);
+ error = AlsaCtlNumidSetLong(mixer, handle->sndcard, handle->stream->mute, !mute);
if (error) {
AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%d", handle->stream->volume, !mute);
goto OnErrorExit;
@@ -155,8 +134,8 @@ STATIC void StreamApiVerbCB(AFB_ReqT request) {
// if not in quiet mode return effective selected control values
if (doQuiet) responseJ = NULL;
else {
- error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->volume, &volume);
- error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->mute, &mute);
+ error += AlsaCtlNumidGetLong(mixer, handle->sndcard, handle->stream->volume, &volume);
+ error += AlsaCtlNumidGetLong(mixer, handle->sndcard, handle->stream->mute, &mute);
if (error) {
AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to get stream numids volume=%ld mute=%ld", volume, mute);
goto OnErrorExit;
@@ -164,17 +143,188 @@ STATIC void StreamApiVerbCB(AFB_ReqT request) {
wrap_json_pack(&responseJ, "{si,sb}", "volume", volume, "mute", !mute);
}
- snd_ctl_close(ctlDev);
- AFB_ReqSucess(request, responseJ, handle->verb);
+ AFB_ReqSucess(request, responseJ, NULL);
return;
OnErrorExit:
- if (ctlDev) snd_ctl_close(ctlDev);
return;
+}
+
+PUBLIC json_object *CreateOneStream(SoftMixerT *mixer, AlsaStreamAudioT *stream) {
+ int error;
+ long value;
+ AlsaSndLoopT *loop=NULL;
+ AlsaPcmCtlT *streamPcm;
+ AlsaSndCtlT *captureCard;
+ AlsaDevInfoT *captureDev = alloca(sizeof (AlsaDevInfoT));
+ AlsaLoopSubdevT *loopDev;
+ char * captureName;
+
+ loopDev = ApiLoopFindSubdev(mixer, stream->uid, stream->source, &loop);
+ if (loopDev) {
+ // create a valid PCM reference and try to open it.
+ captureDev->devpath = NULL;
+ captureDev->cardid = NULL;
+ captureDev->cardidx = loop->sndcard->cid.cardidx;
+ captureDev->device = loop->capture;
+ captureDev->subdev = loopDev->index;
+ captureCard = loop->sndcard;
+ } else {
+ // if capture UID is not present in loop search on sources
+ AlsaSndCtlT *sourceDev = ApiSourceFindSubdev(mixer, stream->source);
+ if (sourceDev) {
+ captureDev->devpath = NULL;
+ captureDev->cardid = NULL;
+ captureDev->cardidx = sourceDev->cid.cardidx;
+ captureDev->device = sourceDev->cid.device;
+ captureDev->subdev = sourceDev->cid.subdev;
+ captureCard = sourceDev;
+ } else {
+ AFB_ApiError(mixer->api, "CreateOneStream: mixer=%s stream=%s not found in loops/sources", mixer->uid, stream->uid);
+ goto OnErrorExit;
+ }
+ }
+ // check PCM is valid and get its full name
+ AlsaPcmCtlT *capturePcm = AlsaByPathOpenPcm(mixer, captureDev, SND_PCM_STREAM_CAPTURE);
+ if (!capturePcm) goto OnErrorExit;
+
+ // Registry capturePcm PCM for active/pause event
+ if (loopDev && loopDev->numid) {
+ error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_RUN, loopDev->numid);
+ if (error) goto OnErrorExit;
+ }
+
+ AlsaSndZoneT *zone = ApiZoneGetByUid(mixer, stream->sink);
+ if (!zone) {
+ AFB_ApiError(mixer->api, "CreateOneStream: mixer=%s stream=%s fail to find sink zone='%s'", mixer->uid, stream->uid, stream->sink);
+ goto OnErrorExit;
+ }
+
+ // retrieve channel count from route and push it to stream
+ stream->params->channels = zone->ccount;
+
+ // create mute control and Registry it as pause/resume ctl)
+ char *runName;
+ (void) asprintf(&runName, "pause-%s", stream->uid);
+ int pauseNumid = AlsaCtlCreateControl(mixer, captureCard, runName, 1, 0, 1, 1, stream->mute);
+ if (pauseNumid <= 0) goto OnErrorExit;
+
+ // Registry stop/play as a pause/resume control
+ error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_PAUSE, pauseNumid);
+ if (error) goto OnErrorExit;
+
+
+ char *volName;
+ (void) asprintf(&volName, "vol-%s", stream->uid);
+
+ // create stream and delay pcm opening until vol control is created
+ streamPcm = AlsaCreateSoftvol(mixer, stream, zone, captureCard, volName, VOL_CONTROL_MAX, 0);
+ if (!streamPcm) {
+ AFB_ApiError(mixer->api, "CreateOneStream: mixer=%s stream=%s fail to create stream", mixer->uid, stream->uid);
+ goto OnErrorExit;
+ }
+
+ // create volume control before softvol pcm is opened
+ int volNumid = AlsaCtlCreateControl(mixer, captureCard, volName, stream->params->channels, VOL_CONTROL_MIN, VOL_CONTROL_MAX, VOL_CONTROL_STEP, stream->volume);
+ if (volNumid <= 0) goto OnErrorExit;
+
+ if ((zone->params->rate != stream->params->rate) || (zone->params->format != stream->params->format)) {
+ char *rateName;
+ (void) asprintf(&rateName, "rate-%s", stream->uid);
+ streamPcm = AlsaCreateRate(mixer, rateName, streamPcm, zone->params, 0);
+ if (!streamPcm) {
+ AFB_ApiError(mixer->api, "StreamsAttach: mixer=%s stream=%s fail to create rate converter", mixer->uid, stream->uid);
+ goto OnErrorExit;
+ }
+ captureName=rateName;
+ } else {
+ captureName= (char*)streamPcm->cid.cardid;
+ }
+
+ // everything is not ready to open playback pcm
+ error = snd_pcm_open(&streamPcm->handle, captureName, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (error) {
+ AFB_ApiError(mixer->api, "CreateOneStream: mixer=%s stream=%s fail to open capturePcm=%s error=%s", mixer->uid, stream->uid, streamPcm->cid.cardid, snd_strerror(error));
+ goto OnErrorExit;
+ }
+
+ // start stream pcm copy (at this both capturePcm & sink pcm should be open, we use output params to configure both in+outPCM)
+ error = AlsaPcmCopy(mixer, stream, capturePcm, streamPcm, stream->params);
+ if (error) goto OnErrorExit;
+
+ error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_IGNORE, volNumid);
+ if (error) goto OnErrorExit;
+
+ // when using loopdev check if subdev is active or not to prevent thread from reading empty packet
+ if (loopDev && loopDev->numid) {
+ // retrieve active/pause control and set PCM status accordingly
+ error = AlsaCtlNumidGetLong(mixer, captureCard, loopDev->numid, &value);
+ if (error) goto OnErrorExit;
+
+ // toggle pause/resume (should be done after pcm_start)
+ if ((error = snd_pcm_pause(capturePcm->handle, !value)) < 0) {
+ AFB_ApiWarning(mixer->api, "CreateOneStream: mixer=%s [capturePcm=%s] fail to pause error=%s", mixer->uid, captureDev->cardid, snd_strerror(error));
+ }
+ }
+
+ // prepare response for application
+ json_object *paramsJ, *streamJ;
+ char *appCardId = NULL;
+
+ // return alsa URI only when loopback is used
+ if (loop) {
+ (void) asprintf(&appCardId, "hw:%d,%d,%d", captureDev->cardidx, loop->playback, capturePcm->cid.subdev);
+ } else {
+ appCardId="";
+ }
+
+ error += wrap_json_pack(&paramsJ, "{si si si si}"
+ , "rate", stream->params->rate
+ , "channels", stream->params->channels
+ , "format", stream->params->format
+ , "access", stream->params->access
+ );
+ error += wrap_json_pack(&streamJ, "{ss ss si si so}", "uid"
+ , stream->uid, "alsa"
+ , appCardId
+ , "volid", volNumid
+ , "runid", pauseNumid
+ , "params", paramsJ
+ );
+ if (error) {
+ AFB_ApiError(mixer->api, "CreateOneStream: mixer=%s stream=%s fail to prepare response", mixer->uid, stream->uid);
+ goto OnErrorExit;
+ }
+
+ // create a dedicated verb for this stream
+ apiHandleT *apiHandle = calloc(1, sizeof (apiHandleT));
+
+ apiHandle->mixer = mixer;
+ apiHandle->stream = stream;
+ apiHandle->sndcard =captureCard;
+ apiHandle->pcm = capturePcm->handle;
+
+ error = afb_dynapi_add_verb(mixer->api, stream->uid, stream->info, StreamApiVerbCB, apiHandle, NULL, 0);
+ if (error) {
+ AFB_ApiError(mixer->api, "CreateOneStream mixer=%s fail to Register API verb stream=%s", mixer->uid, stream->uid);
+ goto OnErrorExit;
+ }
+
+ // Debug Alsa Config
+ //AlsaDumpElemConfig (source, "\n\nAlsa_Config\n------------\n", "pcm");
+ //AlsaDumpPcmInfo(source, "\n\nPcm_config\n-----------\n", streamPcm->handle);
+
+ AFB_ApiNotice(mixer->api, "CreateOneStream: mixer=%s stream=%s OK reponse=%s\n", mixer->uid, stream->uid, json_object_get_string(streamJ));
+
+ return streamJ;
+
+OnErrorExit:
+ return NULL;
}
-STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaLoopStreamT *stream) {
+STATIC AlsaStreamAudioT * AttachOneStream(SoftMixerT *mixer, const char *uid, json_object *streamJ, json_object **responseJ) {
+ AlsaStreamAudioT *stream = calloc(1, sizeof (AlsaStreamAudioT));
int error;
json_object *paramsJ = NULL;
@@ -183,242 +333,93 @@ STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaLoopSt
stream->mute = 0;
stream->info = NULL;
- error = wrap_json_unpack(streamJ, "{ss,s?s,ss,s?i,s?b,s?o,s?s !}"
+ error = wrap_json_unpack(streamJ, "{ss,s?s,ss,s?s,s?i,s?b,s?o,s?s !}"
, "uid", &stream->uid
, "info", &stream->info
- , "zone", &stream->zone
+ , "zone", &stream->sink
+ , "source", &stream->source
, "volume", &stream->volume
, "mute", stream->mute
, "params", &paramsJ
, "ramp", &stream->ramp
);
if (error) {
- AFB_ApiNotice(source->api, "ProcessOneStream missing 'uid|[info]|zone|[volume]|[mute]|[params]' stream=%s", json_object_get_string(streamJ));
+ AFB_ApiNotice(mixer->api, "ProcessOneStream hal=%s missing 'uid|[info]|zone|source||[volume]|[mute]|[params]' error=%s stream=%s", uid, wrap_json_get_error_string(error), json_object_get_string(streamJ));
goto OnErrorExit;
}
- if (paramsJ) error = ProcessSndParams(source, stream->uid, paramsJ, &stream->params);
- if (error) {
- AFB_ApiError(source->api, "ProcessOneSndCard: sndcard=%s invalid params=%s", stream->uid, json_object_get_string(paramsJ));
+ stream->params = ApiPcmSetParams(mixer, stream->uid, paramsJ);
+ if (!stream->params) {
+ AFB_ApiError(mixer->api, "ProcessOneSndCard: hal=%s stream=%s invalid params=%s", uid, stream->uid, json_object_get_string(paramsJ));
goto OnErrorExit;
- } else {
- stream->params.rate = ALSA_DEFAULT_PCM_RATE;
- stream->params.rate = ALSA_DEFAULT_PCM_RATE;
- stream->params.access = SND_PCM_ACCESS_RW_INTERLEAVED;
- stream->params.format = SND_PCM_FORMAT_S16_LE;
- stream->params.channels = 2;
- stream->params.sampleSize = 0;
}
// make sure remain valid even when json object is removed
stream->uid = strdup(stream->uid);
- stream->zone = strdup(stream->zone);
+ if (stream->sink)stream->sink = strdup(stream->sink);
+ if (stream->source)stream->source = strdup(stream->source);
- return 0;
+ // implement stream PCM with corresponding thread and controls
+ *responseJ = CreateOneStream(mixer, stream);
+
+ return stream;
OnErrorExit:
- return -1;
+ return NULL;
}
-PUBLIC int LoopStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ) {
- SoftMixerHandleT *mixer = (SoftMixerHandleT*) source->context;
- AlsaLoopStreamT *loopStream;
- int error;
- long value;
- size_t count;
+PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ, json_object **responseJ) {
+
+ if (!mixer->loops) {
+ AFB_ApiError(mixer->api, "StreamsAttach: mixer=%s No Loop found [should Registry snd_loop first]", mixer->uid);
+ goto OnErrorExit;
+ }
- assert(mixer);
+ int index;
+ for (index = 0; index < mixer->max.streams; index++) {
+ if (!mixer->streams[index]) break;
+ }
- // assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = mixer->frontend;
- if (!ctlLoop) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s No Loop found [should Registry snd_loop first]", mixer->uid);
+ if (index == mixer->max.streams) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max stream=%d argsJ= %s", mixer->uid, mixer->max.streams, json_object_get_string(argsJ));
goto OnErrorExit;
}
switch (json_object_get_type(argsJ)) {
+ long count;
+
case json_type_object:
- count = 1;
- loopStream = calloc(count + 1, sizeof (AlsaLoopStreamT));
- error = ProcessOneStream(source, argsJ, &loopStream[0]);
- if (error) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(argsJ));
+ mixer->streams[index] = AttachOneStream(mixer, uid, argsJ, responseJ);
+ if (!mixer->streams[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
break;
case json_type_array:
+ *responseJ = json_object_new_array();
+
count = json_object_array_length(argsJ);
- loopStream = calloc(count + 1, sizeof (AlsaLoopStreamT));
+ if (count > (mixer->max.streams - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max stream=%d argsJ= %s", mixer->uid, mixer->max.streams, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
for (int idx = 0; idx < count; idx++) {
- json_object *loopStreamJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneStream(source, loopStreamJ, &loopStream[idx]);
- if (error) {
- AFB_ApiError(source->api, "loopstreams: mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(loopStreamJ));
+ json_object *streamJ = json_object_array_get_idx(argsJ, idx);
+ mixer->streams[index + idx] = AttachOneStream(mixer, uid, streamJ, &streamJ);
+ if (!mixer->streams[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(streamJ));
goto OnErrorExit;
}
+ (void) json_object_array_add(*responseJ, streamJ);
}
break;
default:
- AFB_ApiError(source->api, "LoopStreams: mixer=%s invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
-
- // return stream data to application as a json array
- *responseJ = json_object_new_array();
-
- for (int idx = 0; loopStream[idx].uid != NULL; idx++) {
- json_object *streamJ, *paramsJ;
-
- // Search for a free loop capture device
- AFB_ApiNotice(source->api, "LoopStreams: mixer=%s stream=%s Start", mixer->uid, (char*) loopStream[idx].uid);
- ctlLoop->scount--;
- if (ctlLoop->scount < 0) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s stream=%s no more subdev avaliable in loopback=%s", mixer->uid, loopStream[idx].uid, ctlLoop->uid);
- goto OnErrorExit;
- }
-
- // Retrieve subdev loop device and open corresponding pcm
- AlsaPcmInfoT *playbackDev = &ctlLoop->subdevs[ctlLoop->scount];
-
- // capture use the same card/subdev as playback with a different device
- playbackDev->device = ctlLoop->capture;
- AlsaPcmInfoT *captureDev = AlsaByPathOpenPcm(source, playbackDev, SND_PCM_STREAM_CAPTURE);
- if (!captureDev) goto OnErrorExit;
-
- // configure with default loopback subdev params
- error = AlsaPcmConf(source, captureDev, &playbackDev->params);
- if (error) goto OnErrorExit;
-
- // Registry capture PCM for active/pause event
- if (captureDev->numid) {
- error = AlsaCtlRegister(source, mixer, captureDev, FONTEND_NUMID_RUN, captureDev->numid);
- if (error) goto OnErrorExit;
- }
-
- // Try to create/setup volume control.
- snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, captureDev->handle);
- if (!ctlDev) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s [pcm=%s] fail attache sndcard", mixer->uid, captureDev->cardid);
- goto OnErrorExit;
- }
-
- // create mute control and Registry it as pause/resume ctl)
- char runName[ALSA_CARDID_MAX_LEN];
- snprintf(runName, sizeof (runName), "run-%s", loopStream[idx].uid);
-
- // create a single boolean value control for pause/resume
- int pauseNumid = AlsaCtlCreateControl(source, ctlDev, playbackDev, runName, 1, 0, 1, 1, loopStream[idx].mute);
- if (pauseNumid <= 0) goto OnErrorExit;
-
- // Registry mute/unmute as a pause/resume control
- error = AlsaCtlRegister(source, mixer, captureDev, FONTEND_NUMID_PAUSE, pauseNumid);
- if (error) goto OnErrorExit;
-
- // create stream and delay pcm openning until vol control is created
- char volName[ALSA_CARDID_MAX_LEN];
- snprintf(volName, sizeof (volName), "vol-%s", loopStream[idx].uid);
- AlsaPcmInfoT *streamPcm = AlsaCreateSoftvol(source, &loopStream[idx], captureDev, volName, VOL_CONTROL_MAX, 0);
- if (!streamPcm) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to create stream", mixer->uid, loopStream[idx].uid);
- goto OnErrorExit;
- }
-
- // create volume control before softvol pcm is opened
- int volNumid = AlsaCtlCreateControl(source, ctlDev, playbackDev, volName, streamPcm->params.channels, VOL_CONTROL_MIN, VOL_CONTROL_MAX, VOL_CONTROL_STEP, loopStream[idx].volume);
- if (volNumid <= 0) goto OnErrorExit;
-
- // **** Fulup (would need some help to get automatic rate converter to work).
- // // add a rate converter plugin to match stream params config
- // char rateName[ALSA_CARDID_MAX_LEN];
- // snprintf(rateName, sizeof (rateName), "rate-%s", loopStream[idx].uid);
- // AlsaPcmInfoT *ratePcm= AlsaCreateRate(source, rateName, streamPcm, 1);
- // if (!ratePcm) {
- // AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to create rate converter", loopStream[idx].uid);
- // goto OnErrorExit;
- // }
-
- // everything is not ready to open capture pcm
- error = snd_pcm_open(&streamPcm->handle, loopStream[idx].uid, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- if (error) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to open capture", mixer->uid, loopStream[idx].uid);
- goto OnErrorExit;
- }
-
- // capture stream inherit channel from targeted zone
- captureDev->ccount = streamPcm->ccount;
- streamPcm->params.channels = streamPcm->ccount;
-
- // start stream pcm copy (at this both capture & sink pcm should be open, we use output params to configure both in+outPCM)
- error = AlsaPcmCopy(source, &loopStream[idx], captureDev, streamPcm, &streamPcm->params);
- if (error) goto OnErrorExit;
-
- error = AlsaCtlRegister(source, mixer, captureDev, FONTEND_NUMID_IGNORE, volNumid);
- if (error) goto OnErrorExit;
-
- // retrieve active/pause control and set PCM status accordingly
- error = AlsaCtlNumidGetLong(source, ctlDev, captureDev->numid, &value);
- if (error) goto OnErrorExit;
-
- // toggle pause/resume (should be done after pcm_start)
- if ((error = snd_pcm_pause(captureDev->handle, !value)) < 0) {
- AFB_ApiWarning(source->api, "LoopStreams: mixer=%s [capture=%s] fail to pause error=%s", mixer->uid, captureDev->cardid, snd_strerror(error));
- }
-
- // prepare response for application
- playbackDev->device = ctlLoop->playback;
- error = AlsaByPathDevid(source, playbackDev);
-
- error += wrap_json_pack(&paramsJ, "{si si si si}", "rate", streamPcm->params.rate, "channels", streamPcm->params.channels, "format", streamPcm->params.format, "access", streamPcm->params.access);
- error += wrap_json_pack(&streamJ, "{ss ss si si so}", "uid", streamPcm->uid, "alsa", playbackDev->cardid, "volid", volNumid, "runid", pauseNumid, "params", paramsJ);
- error += json_object_array_add(*responseJ, streamJ);
- if (error) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s stream=%s fail to prepare response", mixer->uid, captureDev->cardid);
- goto OnErrorExit;
- }
-
- // create a dedicated verb for this stream compose of mixeruid/streamuid
- apiHandleT *apiHandle = calloc(1, sizeof (apiHandleT));
- char apiVerb[128];
- error = snprintf(apiVerb, sizeof (apiVerb), "%s/%s", mixer->uid, loopStream[idx].uid);
- if (error == sizeof (apiVerb)) {
- AFB_ApiError(source->api, "LoopStreams mixer=%s fail to Registry Stream API too long %s/%s", mixer->uid, mixer->uid, loopStream[idx].uid);
- goto OnErrorExit;
- }
-
- // if set get stream attached volramp
- if (loopStream->ramp) {
- apiHandle->ramp = RampGetByUid(source, ctlLoop->ramps, loopStream->ramp);
- if (!apiHandle->ramp) {
- AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to find ramp=%s", mixer->uid, loopStream[idx].uid, loopStream->ramp);
- goto OnErrorExit;
- }
- }
-
- apiHandle->mixer = mixer;
- apiHandle->stream= &loopStream[idx];
- apiHandle->verb = strdup(apiVerb);
- error = afb_dynapi_add_verb(source->api, apiHandle->verb, loopStream[idx].info, StreamApiVerbCB, apiHandle, NULL, 0);
- if (error) {
- AFB_ApiError(source->api, "LoopStreams mixer=%s fail to Registry API verb=%s", mixer->uid, apiHandle->verb);
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s streams invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
goto OnErrorExit;
- }
-
- // free temporary resources
- snd_ctl_close(ctlDev);
- loopStream[idx].volume = volNumid;
- loopStream[idx].mute = pauseNumid;
-
- // Debug Alsa Config
- //AlsaDumpElemConfig (source, "\n\nAlsa_Config\n------------\n", "pcm");
- //AlsaDumpPcmInfo(source, "\n\nPcm_config\n-----------\n", streamPcm->handle);
-
- AFB_ApiNotice(source->api, "LoopStreams: mixer=%s stream=%s OK reponse=%s\n", mixer->uid, streamPcm->uid, json_object_get_string(streamJ));
}
- // save handle for further use
- mixer->streams = loopStream;
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c
index 8f383df..32af733 100644
--- a/plugins/alsa/alsa-api-zones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -16,7 +16,7 @@
*
*/
-#define _GNU_SOURCE // needed for vasprintf
+#define _GNU_MIXER // needed for vasprintf
#include "alsa-softmixer.h"
#include <string.h>
@@ -24,133 +24,169 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannelT *channel) {
- const char*channelUid;
+PUBLIC AlsaSndZoneT *ApiZoneGetByUid(SoftMixerT *mixer, const char *target) {
- int error = wrap_json_unpack(channelJ, "{ss,si,s?i !}", "target", &channelUid, "channel", &channel->port);
+ assert(mixer->zones[0]);
+
+ // search for subdev into every registered loop
+ for (int idx = 0; mixer->zones[idx]; idx++) {
+ if (mixer->zones[idx]->uid && !strcasecmp(mixer->zones[idx]->uid, target)) {
+ return mixer->zones[idx];
+ }
+ }
+ AFB_ApiError(mixer->api, "ApiZoneGetByUid mixer=%s fail to find zone=%s", mixer->uid, target);
+ return NULL;
+}
+
+STATIC AlsaPcmChannelT* ProcessOneChannel(SoftMixerT *mixer, const char* uid, json_object *channelJ) {
+ AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT));
+
+ int error = wrap_json_unpack(channelJ, "{ss,si !}"
+ , "target", &channel->uid
+ , "channel", &channel->port
+ );
if (error) goto OnErrorExit;
- channel->uid = strdup(channelUid);
- return 0;
+ channel->uid = strdup(channel->uid);
+ return channel;
OnErrorExit:
- AFB_ApiError(source->api, "ProcessOneChannel: zone=%s channel: missing (target||channel) json=%s", uid, json_object_get_string(channelJ));
- return -1;
+ AFB_ApiError(mixer->api, "ProcessOneChannel: zone=%s channel: missing (target|channel) json=%s", uid, json_object_get_string(channelJ));
+ return NULL;
}
-STATIC int ProcessOneZone(CtlSourceT *source, json_object *zoneJ, AlsaSndZoneT *zone) {
- json_object *mappingJ;
+STATIC AlsaSndZoneT *AttacheOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) {
+ AlsaSndZoneT *zone = calloc(1, sizeof (AlsaSndZoneT));
+ json_object *sinkJ = NULL, *sourceJ = NULL;
size_t count;
- const char* streamType;
int error;
- error = wrap_json_unpack(zoneJ, "{ss,s?s,so !}"
+ error = wrap_json_unpack(zoneJ, "{ss,s?o,s?o !}"
, "uid", &zone->uid
- , "type", &streamType
- , "mapping", &mappingJ
+ , "sink", &sinkJ
+ , "source", &sourceJ
);
- if (error) {
- AFB_ApiNotice(source->api, "ProcessOneZone missing 'uid|type|mapping' zone=%s", json_object_get_string(zoneJ));
+ if (error || (!sinkJ && sourceJ)) {
+ AFB_ApiNotice(mixer->api, "AttacheOneZone missing 'uid|sink|source' error=%s zone=%s", wrap_json_get_error_string(error), json_object_get_string(zoneJ));
goto OnErrorExit;
}
- if (!streamType) zone->type = SND_PCM_STREAM_PLAYBACK;
- else {
- if (!strcasecmp(streamType, "capture")) zone->type = SND_PCM_STREAM_CAPTURE;
- else if (!strcasecmp(streamType, "playback")) zone->type = SND_PCM_STREAM_PLAYBACK;
- else {
- AFB_ApiError(source->api, "ProcessOneZone:%s invalid stream type !(playback||capture) json=%s", zone->uid, json_object_get_string(zoneJ));
- goto OnErrorExit;
- }
- }
-
// make sure remain valid even when json object is removed
zone->uid = strdup(zone->uid);
- switch (json_object_get_type(mappingJ)) {
- case json_type_object:
- count = 1;
- zone->channels = calloc(count + 1, sizeof (AlsaPcmChannelT));
- error = ProcessOneChannel(source, zone->uid, mappingJ, &zone->channels[0]);
- if (error) goto OnErrorExit;
- break;
- case json_type_array:
- count = json_object_array_length(mappingJ);
- zone->channels = calloc(count + 1, sizeof (AlsaPcmChannelT));
- for (int idx = 0; idx < count; idx++) {
- json_object *channelJ = json_object_array_get_idx(mappingJ, idx);
- error = ProcessOneChannel(source, zone->uid, channelJ, &zone->channels[idx]);
- if (error) goto OnErrorExit;
- }
- break;
- default:
- AFB_ApiError(source->api, "ProcessOneZone:%s invalid mapping=%s", zone->uid, json_object_get_string(mappingJ));
- goto OnErrorExit;
+ if (sinkJ) {
+
+ switch (json_object_get_type(sinkJ)) {
+ case json_type_object:
+ zone->sinks = calloc(2, sizeof (void*));
+ zone->sinks[0] = ProcessOneChannel(mixer, zone->uid, sinkJ);
+ if (!zone->sinks[0]) goto OnErrorExit;
+
+ break;
+ case json_type_array:
+ count = json_object_array_length(sinkJ);
+ zone->sinks = calloc(count + 1, sizeof (void*));
+ for (int idx = 0; idx < count; idx++) {
+ json_object *subdevJ = json_object_array_get_idx(sinkJ, idx);
+ zone->sinks[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ);
+ if (error) goto OnErrorExit;
+ }
+ break;
+ default:
+ AFB_ApiError(mixer->api, "AttacheOneZone: Mixer=%s Hal=%s zone=%s invalid mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sinkJ));
+ goto OnErrorExit;
+ }
+
}
- return 0;
+ if (sourceJ) {
+ switch (json_object_get_type(sourceJ)) {
+ case json_type_object:
+ zone->sources = calloc(2, sizeof (void*));
+ zone->sources[0] = ProcessOneChannel(mixer, zone->uid, sourceJ);
+ if (!zone->sources[0]) goto OnErrorExit;
+ break;
+ case json_type_array:
+ count = json_object_array_length(sourceJ);
+ zone->sources = calloc(count + 1, sizeof (void*));
+ for (int idx = 0; idx < count; idx++) {
+ json_object *subdevJ = json_object_array_get_idx(sourceJ, idx);
+ zone->sources[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ);
+ if (error) goto OnErrorExit;
+ }
+ break;
+ default:
+ AFB_ApiError(mixer->api, "AttacheOneZone:Mixer=%s Hal=%s zone=%s mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sourceJ));
+ goto OnErrorExit;
+ }
+ }
+
+ return zone;
OnErrorExit:
- return -1;
+ return NULL;
}
-PUBLIC int SndZones(CtlSourceT *source, json_object *argsJ) {
- SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
- AlsaSndZoneT *zones=NULL;
- int error;
- size_t count;
+PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
- assert(mixerHandle);
+ int index;
+ for (index = 0; index < mixer->max.zones; index++) {
+ if (!mixer->zones[index]) break;
+ }
- if (mixerHandle->routes) {
- AFB_ApiError(source->api, "SndZones: mixer=%s Zones already Registryed %s", mixerHandle->uid, json_object_get_string(argsJ));
+ if (index == mixer->max.zones) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d argsJ= %s", mixer->uid, mixer->max.zones, json_object_get_string(argsJ));
goto OnErrorExit;
}
switch (json_object_get_type(argsJ)) {
+ long count;
+
case json_type_object:
- count = 1;
- zones = calloc(count + 1, sizeof (AlsaSndZoneT));
- error = ProcessOneZone(source, argsJ, &zones[0]);
- if (error) {
- AFB_ApiError(source->api, "SndZones: mixer=%s invalid zone= %s", mixerHandle->uid, json_object_get_string(argsJ));
+ mixer->zones[index] = AttacheOneZone(mixer, uid, argsJ);
+ if (!mixer->zones[index]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
+
+ AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, mixer->zones[index], 0);
+ if (!routeConfig) {
+ AFB_ApiError(mixer->api, "AttacheOneZone: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route", mixer->uid, uid, mixer->zones[index]->uid);
+ goto OnErrorExit;
+ }
+
break;
case json_type_array:
count = json_object_array_length(argsJ);
- zones = calloc(count + 1, sizeof (AlsaSndZoneT));
+ if (count > (mixer->max.zones - count)) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d argsJ= %s", mixer->uid, mixer->max.zones, json_object_get_string(argsJ));
+ goto OnErrorExit;
+
+ }
+
for (int idx = 0; idx < count; idx++) {
- json_object *sndZoneJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneZone(source, sndZoneJ, &zones[idx]);
- if (error) {
- AFB_ApiError(source->api, "SndZones: mixer=%s invalid zone= %s", mixerHandle->uid, json_object_get_string(sndZoneJ));
+ json_object *zoneJ = json_object_array_get_idx(argsJ, idx);
+ mixer->zones[index + idx] = AttacheOneZone(mixer, uid, zoneJ);
+ if (!mixer->zones[index + idx]) {
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(zoneJ));
+ goto OnErrorExit;
+ }
+
+ AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, mixer->zones[idx], 0);
+ if (!routeConfig) {
+ AFB_ApiError(mixer->api, "AttacheOneZone: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route", mixer->uid, uid, mixer->zones[idx]->uid);
goto OnErrorExit;
}
}
break;
default:
- AFB_ApiError(source->api, "SndZones: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
+ AFB_ReqFailF(request, "invalid-syntax", "mixer=%s zones invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
- // Registry routed into global softmixer handle
- mixerHandle->routes= calloc(count + 1, sizeof (AlsaPcmInfoT*));
-
- // instantiate one route PCM per zone with multi plugin as slave
- for (int idx = 0; zones[idx].uid != NULL; idx++) {
- mixerHandle->routes[idx] = AlsaCreateRoute(source, &zones[idx], 0);
- if (!mixerHandle->routes[idx]) {
- AFB_ApiNotice(source->api, "SndZones: mixer=%s fail to create route zone=%s", mixerHandle->uid, zones[idx].uid);
- goto OnErrorExit;
- }
- }
-
- free (zones);
return 0;
OnErrorExit:
- if (zones) free(zones);
return -1;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index 4ba871a..0ef11e1 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -29,18 +29,14 @@ for the specific language governing permissions and
#include <sys/syscall.h>
typedef struct {
- AFB_ApiT api;
+ SoftMixerT *mixer;
sd_event_source* evtsrc;
- pthread_t thread;
- int tid;
- char* info;
- snd_ctl_t *ctlDev;
+ const char* uid;
+ AlsaSndCtlT *sndcard;
sd_event *sdLoop;
- RegistryHandleT *registry;
} SubscribeHandleT;
-
-PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) {
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid) {
char string[32];
int error;
int index;
@@ -49,19 +45,19 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(CtlSourceT *source, snd_ctl_t* c
snd_ctl_elem_list_alloca(&ctlList);
- if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ if ((error = snd_ctl_elem_list(sndcard->ctl, ctlList)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(sndcard->ctl, string));
goto OnErrorExit;
}
if ((error = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) {
- AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail retrieve count", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(mixer->api, "AlsaCtlGetNumidElemId [%s] fail retrieve count", ALSA_CTL_UID(sndcard->ctl, 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, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ if ((error = snd_ctl_elem_list(sndcard->ctl, ctlList)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(sndcard->ctl, string));
goto OnErrorExit;
}
@@ -77,7 +73,7 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(CtlSourceT *source, snd_ctl_t* c
}
if (index == ctlCount) {
- AFB_ApiNotice(source->api, "AlsaCtlGetNumidElemId [%s] fail get numid=%i count", ALSA_CTL_UID(ctlDev, string), numid);
+ AFB_ApiNotice(mixer->api, "AlsaCtlGetNumidElemId [%s] fail get numid=%i count", ALSA_CTL_UID(sndcard->ctl, string), numid);
goto OnErrorExit;
}
@@ -90,8 +86,7 @@ OnErrorExit:
return NULL;
}
-PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName) {
- char string[32];
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName) {
int error;
int index;
snd_ctl_elem_list_t *ctlList = NULL;
@@ -99,19 +94,19 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ct
snd_ctl_elem_list_alloca(&ctlList);
- if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ if ((error = snd_ctl_elem_list(sndcard->ctl, ctlList)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlGetNameElemId cardid='%s' cardname='%s' fail retrieve controls", sndcard->cid.cardid, sndcard->cid.name);
goto OnErrorExit;
}
if ((error = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) {
- AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail retrieve count", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(mixer->api, "AlsaCtlGetNameElemId cardid='%s' cardname='%s' fail retrieve count", sndcard->cid.cardid, sndcard->cid.name);
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, "AlsaCtlGetNameElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ if ((error = snd_ctl_elem_list(sndcard->ctl, ctlList)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlGetNameElemId cardid='%s' cardname='%s' fail retrieve controls", sndcard->cid.cardid, sndcard->cid.name);
goto OnErrorExit;
}
@@ -119,7 +114,7 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ct
int ctlCount = snd_ctl_elem_list_get_used(ctlList);
for (index = 0; index < ctlCount; index++) {
- if (!strcasecmp(ctlName, snd_ctl_elem_list_get_name(ctlList, index))) {
+ if (strcasestr(snd_ctl_elem_list_get_name(ctlList, index), ctlName)) {
snd_ctl_elem_id_malloc(&elemId);
snd_ctl_elem_list_get_id(ctlList, index, elemId);
break;
@@ -127,7 +122,7 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ct
}
if (index == ctlCount) {
- AFB_ApiNotice(source->api, "AlsaCtlGetNameElemId [%s] ctl not found name=%s", ALSA_CTL_UID(ctlDev, string), ctlName);
+ AFB_ApiNotice(mixer->api, "AlsaCtlGetNameElemId cardid='%s' cardname='%s' ctl not found name=%s", sndcard->cid.cardid, sndcard->cid.name, ctlName);
goto OnErrorExit;
}
@@ -140,42 +135,26 @@ OnErrorExit:
return NULL;
}
-PUBLIC snd_ctl_t *AlsaCtlOpenCtl(CtlSourceT *source, const char *cardid) {
+PUBLIC snd_ctl_t *AlsaCtlOpenCtl(SoftMixerT *mixer, const char *cardid) {
int error;
- snd_ctl_t *ctlDev;
+ snd_ctl_t *ctl;
if (!cardid) goto OnErrorExit;
- if ((error = snd_ctl_open(&ctlDev, cardid, SND_CTL_READONLY)) < 0) {
+ if ((error = snd_ctl_open(&ctl, cardid, SND_CTL_READONLY)) < 0) {
cardid = "Not Defined";
goto OnErrorExit;
}
- return ctlDev;
+ return ctl;
OnErrorExit:
- AFB_ApiError(source->api, "AlsaCtlOpenCtl: fail to find sndcard by id= %s", cardid);
+ AFB_ApiError(mixer->api, "AlsaCtlOpenCtl: fail to find sndcard by id= %s", cardid);
return NULL;
}
-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 void CtlElemIdDisplay(AFB_ApiT api, snd_ctl_elem_info_t *elemInfo, snd_ctl_elem_value_t *elemData) {
+STATIC void CtlElemIdDisplay(SoftMixerT *mixer, snd_ctl_elem_info_t *elemInfo, snd_ctl_elem_value_t *elemData) {
int numid = snd_ctl_elem_info_get_numid(elemInfo);
int count = snd_ctl_elem_info_get_count(elemInfo);
@@ -184,7 +163,7 @@ STATIC void CtlElemIdDisplay(AFB_ApiT api, snd_ctl_elem_info_t *elemInfo, snd_ct
if (!elemData) {
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=unreadable", numid, name);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=unreadable", numid, name);
} else
for (int idx = 0; idx < count; idx++) {
long valueL;
@@ -192,67 +171,63 @@ STATIC void CtlElemIdDisplay(AFB_ApiT api, snd_ctl_elem_info_t *elemInfo, snd_ct
switch (elemType) {
case SND_CTL_ELEM_TYPE_BOOLEAN:
valueL = snd_ctl_elem_value_get_boolean(elemData, idx);
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
break;
case SND_CTL_ELEM_TYPE_INTEGER:
valueL = snd_ctl_elem_value_get_integer(elemData, idx);
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
break;
case SND_CTL_ELEM_TYPE_INTEGER64:
valueL = snd_ctl_elem_value_get_integer64(elemData, idx);
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
break;
case SND_CTL_ELEM_TYPE_ENUMERATED:
valueL = snd_ctl_elem_value_get_enumerated(elemData, idx);
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
break;
case SND_CTL_ELEM_TYPE_BYTES:
valueL = snd_ctl_elem_value_get_byte(elemData, idx);
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s value=%ld", numid, name, valueL);
break;
case SND_CTL_ELEM_TYPE_IEC958:
default:
- AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s Unsupported type=%d", numid, name, elemType);
+ AFB_ApiWarning(mixer->api, "CtlElemIdDisplay: numid=%d name=%s Unsupported type=%d", numid, name, elemType);
break;
}
}
}
-PUBLIC int CtlElemIdGetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long *value) {
+PUBLIC int CtlElemIdGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, 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(sndcard->ctl, 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_value_alloca(&elemData);
snd_ctl_elem_value_set_id(elemData, elemId);
- error = snd_ctl_elem_read(ctlDev, elemData);
+ error = snd_ctl_elem_read(sndcard->ctl, elemData);
if (error) {
elemData = NULL;
goto OnErrorExit;
}
- // warning multi channel are always view as grouped
- //int count = snd_ctl_elem_info_get_count(elemInfo);
- //if (count != 1) goto OnErrorExit;
-
// value=1 when active and 0 when not active
*value = (int) snd_ctl_elem_value_get_integer(elemData, 0);
return 0;
OnErrorExit:
- CtlElemIdDisplay(api, elemInfo, elemData);
+ CtlElemIdDisplay(mixer, elemInfo, elemData);
return -1;
}
-PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long value) {
+PUBLIC int CtlElemIdSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long value) {
snd_ctl_elem_value_t *elemData;
snd_ctl_elem_info_t *elemInfo;
const char* name;
@@ -260,7 +235,7 @@ PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *
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(sndcard->ctl, elemInfo) < 0) goto OnErrorExit;
if (!snd_ctl_elem_info_is_writable(elemInfo)) goto OnErrorExit;
@@ -269,15 +244,14 @@ PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *
snd_ctl_elem_value_alloca(&elemData);
snd_ctl_elem_value_set_id(elemData, elemId);
- error = snd_ctl_elem_read(ctlDev, elemData);
+ error = snd_ctl_elem_read(sndcard->ctl, elemData);
if (error) goto OnErrorExit;
-
for (int index = 0; index < count; index++) {
snd_ctl_elem_value_set_integer(elemData, index, value);
}
- error = snd_ctl_elem_write(ctlDev, elemData);
+ error = snd_ctl_elem_write(sndcard->ctl, elemData);
if (error) goto OnErrorExit;
return 0;
@@ -285,45 +259,43 @@ PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *
OnErrorExit:
numid = snd_ctl_elem_info_get_numid(elemInfo);
name = snd_ctl_elem_info_get_name(elemInfo);
- AFB_ApiError(api, "CtlElemIdSetInt: numid=%d name=%s not writable", numid, name);
+ AFB_ApiError(mixer->api, "CtlElemIdSetInt: numid=%d name=%s not writable", numid, name);
return -1;
}
// Clone of AlsaLib snd_card_load2 static function
-PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(CtlSourceT *source, const char *cardid) {
+PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(SoftMixerT *mixer, const char *cardid) {
int error;
- snd_ctl_t *ctlDev;
-
- if (cardid) goto OnErrorExit;
+ snd_ctl_t *ctl;
- if ((error = snd_ctl_open(&ctlDev, cardid, SND_CTL_READONLY)) < 0) {
+ if ((error = snd_ctl_open(&ctl, cardid, SND_CTL_READONLY)) < 0) {
cardid = "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) {
+ if ((error = snd_ctl_card_info(ctl, cardInfo)) < 0) {
goto OnErrorExit;
}
return cardInfo;
OnErrorExit:
- AFB_ApiError(source->api, "AlsaCtlGetInfo: fail to find sndcard by id= %s", cardid);
+ AFB_ApiError(mixer->api, "AlsaCtlGetInfo: fail to find sndcard by id= %s", cardid);
return NULL;
}
-PUBLIC int AlsaCtlNumidSetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long value) {
+PUBLIC int AlsaCtlNumidSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long value) {
- snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(mixer, sndcard, numid);
if (!elemId) {
- AFB_ApiError(source->api, "AlsaCtlNumidSetLong [sndcard=%s] fail to find numid=%d", snd_ctl_name(ctlDev), numid);
+ AFB_ApiError(mixer->api, "AlsaCtlNumidSetLong cardid=%s cardname=%s fail to find numid=%d", sndcard->cid.cardid, sndcard->cid.longname, numid);
goto OnErrorExit;
}
- int error = CtlElemIdSetLong(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdSetLong(mixer, sndcard, elemId, value);
if (error) {
- AFB_ApiError(source->api, "AlsaCtlNumidSetLong [sndcard=%s] fail to set numid=%d value=%ld", snd_ctl_name(ctlDev), numid, value);
+ AFB_ApiError(mixer->api, "AlsaCtlNumidSetLong cardid=%s cardname=%s fail to set numid=%d value=%ld", sndcard->cid.cardid, sndcard->cid.longname, numid, value);
goto OnErrorExit;
}
@@ -332,17 +304,17 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlNumidGetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value) {
+PUBLIC int AlsaCtlNumidGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long* value) {
- snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(mixer, sndcard, numid);
if (!elemId) {
- AFB_ApiError(source->api, "AlsaCtlNumidGetLong [sndcard=%s] fail to find numid=%d", snd_ctl_name(ctlDev), numid);
+ AFB_ApiError(mixer->api, "AlsaCtlNumidGetLong cardid=%s cardname=%s fail to find numid=%d", sndcard->cid.cardid, sndcard->cid.longname, numid);
goto OnErrorExit;
}
- int error = CtlElemIdGetLong(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdGetLong(mixer, sndcard, elemId, value);
if (error) {
- AFB_ApiError(source->api, "AlsaCtlNumidGetLong [sndcard=%s] fail to get numid=%d value", snd_ctl_name(ctlDev), numid);
+ AFB_ApiError(mixer->api, "AlsaCtlNumidGetLong cardid=%s cardname=%s fail to get numid=%d value", sndcard->cid.cardid, sndcard->cid.longname, numid);
goto OnErrorExit;
}
@@ -351,17 +323,17 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlNameSetLong(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName, long value) {
+PUBLIC int AlsaCtlNameSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long value) {
- snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(source, ctlDev, ctlName);
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(mixer, sndcard, ctlName);
if (!elemId) {
- AFB_ApiError(source->api, "AlsaCtlNameSetLong [sndcard=%s] fail to find crlName=%s", snd_ctl_name(ctlDev), ctlName);
+ AFB_ApiError(mixer->api, "AlsaCtlNameSetLong cardid=%s cardname=%s fail to find crlName=%s", sndcard->cid.cardid, sndcard->cid.longname, ctlName);
goto OnErrorExit;
}
- int error = CtlElemIdSetLong(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdSetLong(mixer, sndcard, elemId, value);
if (error) {
- AFB_ApiError(source->api, "AlsaCtlNameSetLong [sndcard=%s] fail to set crlName=%s value=%ld", snd_ctl_name(ctlDev), ctlName, value);
+ AFB_ApiError(mixer->api, "AlsaCtlNameSetLong cardid=%s cardname=%s fail to set crlName=%s value=%ld", sndcard->cid.cardid, sndcard->cid.longname, ctlName, value);
goto OnErrorExit;
}
@@ -370,18 +342,17 @@ OnErrorExit:
return -1;
}
+PUBLIC int AlsaCtlNameGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long* value) {
-PUBLIC int AlsaCtlNameGetLong(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName, long* value) {
-
- snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(source, ctlDev, ctlName);
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(mixer, sndcard, ctlName);
if (!elemId) {
- AFB_ApiError(source->api, "AlsaCtlNameGetLong [sndcard=%s] fail to find crlName=%s", snd_ctl_name(ctlDev), ctlName);
+ AFB_ApiError(mixer->api, "AlsaCtlNameGetLong cardid=%s cardname=%s fail to find crlName=%s", sndcard->cid.cardid, sndcard->cid.longname, ctlName);
goto OnErrorExit;
}
- int error = CtlElemIdGetLong(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdGetLong(mixer, sndcard, elemId, value);
if (error) {
- AFB_ApiError(source->api, "AlsaCtlNameGetLong [sndcard=%s] fail to get crlName=%s value", snd_ctl_name(ctlDev), ctlName);
+ AFB_ApiError(mixer->api, "AlsaCtlNameGetLong cardid=%s cardname=%s fail to get crlName=%s value", sndcard->cid.cardid, sndcard->cid.longname, ctlName);
goto OnErrorExit;
}
@@ -390,7 +361,7 @@ OnErrorExit:
return -1;
}
-STATIC int AlsaCtlMakeControl(CtlSourceT *source, snd_ctl_t* ctlDev, AlsaPcmInfoT *subdev, const char *ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep) {
+STATIC int AlsaCtlMakeControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep) {
snd_ctl_elem_type_t ctlType;
snd_ctl_elem_info_t *elemInfo;
int error;
@@ -398,11 +369,11 @@ STATIC int AlsaCtlMakeControl(CtlSourceT *source, snd_ctl_t* ctlDev, AlsaPcmInfo
snd_ctl_elem_info_alloca(&elemInfo);
if (ctlName) snd_ctl_elem_info_set_name(elemInfo, ctlName);
snd_ctl_elem_info_set_interface(elemInfo, SND_CTL_ELEM_IFACE_MIXER);
- snd_ctl_elem_info(ctlDev, elemInfo);
-
+ snd_ctl_elem_info(sndcard->ctl, elemInfo);
+
// map volume to sndcard device+subdev=0
- snd_ctl_elem_info_set_device(elemInfo, subdev->device);
- snd_ctl_elem_info_set_subdevice(elemInfo, subdev->subdev);
+ snd_ctl_elem_info_set_device(elemInfo, sndcard->cid.device);
+ snd_ctl_elem_info_set_subdevice(elemInfo, sndcard->cid.subdev);
snd_ctl_elem_info_set_device(elemInfo, 0);
snd_ctl_elem_info_set_subdevice(elemInfo, 0);
@@ -412,17 +383,17 @@ STATIC int AlsaCtlMakeControl(CtlSourceT *source, snd_ctl_t* ctlDev, AlsaPcmInfo
switch (ctlType) {
case SND_CTL_ELEM_TYPE_BOOLEAN:
- error = snd_ctl_add_boolean_elem_set(ctlDev, elemInfo, 1, ctlCount);
+ error = snd_ctl_add_boolean_elem_set(sndcard->ctl, elemInfo, 1, ctlCount);
if (error) goto OnErrorExit;
break;
case SND_CTL_ELEM_TYPE_INTEGER:
- error = snd_ctl_add_integer_elem_set(ctlDev, elemInfo, 1, ctlCount, ctlMin, ctlMax, ctlStep);
+ error = snd_ctl_add_integer_elem_set(sndcard->ctl, elemInfo, 1, ctlCount, ctlMin, ctlMax, ctlStep);
if (error) goto OnErrorExit;
break;
default:
- AFB_ApiError(source->api, "AlsaCtlMakeControl:%s(subdev) fail to create %s(control)", subdev->uid, ctlName);
+ AFB_ApiError(mixer->api, "AlsaCtlMakeControl: mixer=%s cardid=%s cardname=%s fail to create %s(control)", mixer->uid, sndcard->cid.cardid, sndcard->cid.longname, ctlName);
goto OnErrorExit;
}
@@ -434,46 +405,48 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlCreateControl(CtlSourceT *source, snd_ctl_t* ctlDev, AlsaPcmInfoT *subdevs, char* ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep, long value) {
+PUBLIC int AlsaCtlCreateControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, char* ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep, long value) {
int numid = -1;
// if control does not exist then create
- snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(source, ctlDev, ctlName);
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(mixer, sndcard, ctlName);
if (elemId) {
numid = snd_ctl_elem_id_get_numid(elemId);
} else {
// create or get numid control when already exist
- numid = AlsaCtlMakeControl(source, ctlDev, subdevs, ctlName, ctlCount, ctlMin, ctlMax, ctlStep);
+ numid = AlsaCtlMakeControl(mixer, sndcard, ctlName, ctlCount, ctlMin, ctlMax, ctlStep);
if (numid <= 0) {
- AFB_ApiError(source->api, "AlsaCtlCreateControl [sndcard=%s] fail to create ctlName=%s", snd_ctl_name(ctlDev), ctlName);
+ AFB_ApiError(mixer->api, "AlsaCtlCreateControl cardid=%s cardname=%s fail to create ctlName=%s", sndcard->cid.cardid, sndcard->cid.longname, ctlName);
goto OnErrorExit;
}
- elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
+ elemId = AlsaCtlGetNumidElemId(mixer, sndcard, numid);
}
- int error = CtlElemIdSetLong(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdSetLong(mixer, sndcard, elemId, value);
if (error) {
- AFB_ApiError(source->api, "AlsaCtlCreateControl [sndcard=%s] fail to set ctlName=%s Numid=%d", snd_ctl_name(ctlDev), ctlName, numid);
+ AFB_ApiError(mixer->api, "AlsaCtlCreateControl cardid=%s cardname=%s fail to set ctlName=%s Numid=%d", sndcard->cid.cardid, sndcard->cid.longname, ctlName, numid);
goto OnErrorExit;
}
- AFB_ApiNotice(source->api, "AlsaCtlCreateControl [sndcard=%s] ctl create name=%s numid=%d value=%ld", snd_ctl_name(ctlDev), ctlName, numid, value);
+ AFB_ApiNotice(mixer->api, "AlsaCtlCreateControl cardid=%s cardname=%s ctl create name=%s numid=%d value=%ld", sndcard->cid.cardid, sndcard->cid.longname, ctlName, numid, value);
return numid;
OnErrorExit:
return -1;
}
STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData) {
- int error, numid;
- SubscribeHandleT *subscribeHandle = (SubscribeHandleT*) userData;
+ int error;
+ SubscribeHandleT *sHandle = (SubscribeHandleT*) userData;
+ AlsaSndCtlT *sndcard = sHandle->sndcard;
+ SoftMixerT *mixer = sHandle->mixer;
snd_ctl_event_t *eventId;
snd_ctl_elem_id_t *elemId;
long value;
- int idx;
+ int index;
if ((revents & EPOLLHUP) != 0) {
- AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB hanghup [card:%s disconnected]", subscribeHandle->info);
+ AFB_ApiNotice(mixer->api, "CtlSubscribeEventCB hanghup [card:%s disconnected]", sHandle->uid);
goto OnSuccessExit;
}
@@ -483,7 +456,7 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
snd_ctl_event_alloca(&eventId);
snd_ctl_elem_id_alloca(&elemId);
- error = snd_ctl_read(subscribeHandle->ctlDev, eventId);
+ error = snd_ctl_read(sndcard->ctl, eventId);
if (error < 0) goto OnErrorExit;
// we only process sndctrl element
@@ -495,73 +468,52 @@ 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 = CtlElemIdGetLong(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value);
+ error = CtlElemIdGetLong(mixer, sHandle->sndcard, elemId, &value);
if (error) goto OnErrorExit;
+
+ // get numdid and name from elemId
+ 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(sndcard->ctl, elemInfo) < 0) goto OnErrorExit;
+ int numid = snd_ctl_elem_info_get_numid(elemInfo);
+ const char *name= snd_ctl_elem_info_get_name(elemInfo);
- error = CtlElemIdGetNumid(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &numid);
- if (error) goto OnErrorExit;
+ for (index = 0; sndcard->registry[index]; index++) {
+ if (sndcard->registry[index]->numid == numid) {
- for (idx = 0; idx < subscribeHandle->registry->count; idx++) {
- if (subscribeHandle->registry->stream[idx].numid == numid) {
- const char *pcmName = subscribeHandle->registry->stream[idx].pcm->cardid;
-
- switch (subscribeHandle->registry->stream[idx].type) {
- case FONTEND_NUMID_RUN:
- snd_pcm_pause(subscribeHandle->registry->stream[idx].pcm->handle, (int)(!value));
- AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d pcm=%s numid=%d active=%ld", subscribeHandle->info, subscribeHandle->tid, pcmName, numid, value);
+ switch (sndcard->registry[index]->type) {
+ case FONTEND_NUMID_RUN:
+ (void) snd_pcm_pause(sndcard->registry[index]->pcm->handle, (int) (!value));
+ AFB_ApiNotice(mixer->api, "CtlSubscribeEventCB:%s numid=%d name=%s active=%ld", sHandle->uid, numid, name, value);
break;
- case FONTEND_NUMID_PAUSE:
- AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d pcm=%s numid=%d pause=%ld", subscribeHandle->info, subscribeHandle->tid, pcmName, numid, value);
- snd_pcm_pause(subscribeHandle->registry->stream[idx].pcm->handle, (int)value);
+ case FONTEND_NUMID_PAUSE:
+ AFB_ApiNotice(mixer->api, "CtlSubscribeEventCB:%s numid=%d name=%s pause=%ld", sHandle->uid, numid, name, value);
+ (void) snd_pcm_pause(sndcard->registry[index]->pcm->handle, (int) value);
break;
case FONTEND_NUMID_IGNORE:
- default:
- AFB_ApiInfo(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d pcm=%s numid=%d ignored=%ld", subscribeHandle->info, subscribeHandle->tid, pcmName, numid, value);
+ default:
+ AFB_ApiInfo(mixer->api, "CtlSubscribeEventCB:%s numid=%d name=%s ignored=%ld", sHandle->uid, numid, name, value);
}
break;
}
}
- if (idx == subscribeHandle->registry->count) {
- char cardName[32];
- ALSA_CTL_UID(subscribeHandle->ctlDev, cardName);
- AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d card=%s numid=%d (unknown)", subscribeHandle->info, subscribeHandle->tid, cardName, numid);
+ if (index == sndcard->rcount) {
+ AFB_ApiNotice(mixer->api, "CtlSubscribeEventCB:%s numid=%d (unknown)", sHandle->uid, numid);
}
OnSuccessExit:
return 0;
OnErrorExit:
- AFB_ApiInfo(subscribeHandle->api, "CtlSubscribeEventCB: ignored unsupported event");
+ AFB_ApiInfo(mixer->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_ApiInfo(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) {
+PUBLIC snd_ctl_t* AlsaCrlFromPcm(SoftMixerT *mixer, snd_pcm_t *pcm) {
char buffer[32];
int error;
- snd_ctl_t *ctlDev;
+ snd_ctl_t *ctl;
snd_pcm_info_t *pcmInfo;
snd_pcm_info_alloca(&pcmInfo);
@@ -569,53 +521,40 @@ PUBLIC snd_ctl_t* AlsaCrlFromPcm(CtlSourceT *source, snd_pcm_t *pcm) {
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;
+ if ((error = snd_ctl_open(&ctl, buffer, SND_CTL_READONLY)) < 0) goto OnErrorExit;
- return ctlDev;
+ return ctl;
OnErrorExit:
return NULL;
}
-PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev, RegistryHandleT *registry) {
+PUBLIC int AlsaCtlSubscribe(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *sndcard) {
int error;
char string [32];
struct pollfd pfds;
- SubscribeHandleT *subscribeHandle = malloc(sizeof (SubscribeHandleT));
+ SubscribeHandleT *handle = malloc(sizeof (SubscribeHandleT));
- subscribeHandle->api = source->api;
- subscribeHandle->ctlDev = ctlDev;
- subscribeHandle->info = "ctlEvt";
- subscribeHandle->registry= registry;
+ handle->mixer = mixer;
+ handle->sndcard = sndcard;
+ handle->uid = uid;
// subscribe for sndctl events attached to cardid
- if ((error = snd_ctl_subscribe_events(ctlDev, 1)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlSubscribe: fail sndcard=%s to subscribe events", ALSA_CTL_UID(ctlDev, string));
+ if ((error = snd_ctl_subscribe_events(handle->sndcard->ctl, 1)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlSubscribe: fail sndcard=%s to subscribe events", ALSA_CTL_UID(handle->sndcard->ctl, string));
goto OnErrorExit;
}
// get pollfd attach to this sound board
- int count = snd_ctl_poll_descriptors(ctlDev, &pfds, 1);
+ int count = snd_ctl_poll_descriptors(handle->sndcard->ctl, &pfds, 1);
if (count != 1) {
- 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", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(mixer->api, "AlsaCtlSubscribe: fail sndcard=%s get poll descriptors", ALSA_CTL_UID(handle->sndcard->ctl, string));
goto OnErrorExit;
}
// Registry 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", 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", ALSA_CTL_UID(ctlDev, string), error);
+ if ((error = sd_event_add_io(mixer->sdLoop, &handle->evtsrc, pfds.fd, EPOLLIN, CtlSubscribeEventCB, handle)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaCtlSubscribe: Fail sndcard=%s adding mainloop", ALSA_CTL_UID(handle->sndcard->ctl, string));
goto OnErrorExit;
}
@@ -625,32 +564,29 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlRegister(CtlSourceT *source, SoftMixerHandleT *mixer, AlsaPcmInfoT *pcm, RegistryNumidT type, int numid) {
+PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid) {
+ int index;
- RegistryHandleT *registry= mixer->frontend->registry;
+ for (index = 0; index < sndcard->rcount; index++) {
+ if (!sndcard->registry[index]) break;
+ }
- int count = registry->count;
- if (count > MAX_AUDIO_STREAMS) {
- AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] to many audio stream max=%d", pcm->cardid, MAX_AUDIO_STREAMS);
+ if (index == sndcard->rcount) {
+ AFB_ApiError(mixer->api, "AlsaCtlRegister cardid=%s cardname=%s to many audio stream max=%ld", sndcard->cid.cardid, sndcard->cid.longname, sndcard->rcount);
goto OnErrorExit;
}
- // If 1st registration then open a dev control channel to recieve events
- if (!registry->ctlDev) {
- snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, pcm->handle);
- if (!ctlDev) {
- AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", pcm->cardid);
- goto OnErrorExit;
- }
-
- AlsaCtlSubscribe(source, ctlDev, registry);
+ // If 1st registration then register to card event
+ if (index == 0) {
+ AlsaCtlSubscribe(mixer, sndcard->cid.cardid, sndcard);
}
// store PCM in order to pause/resume depending on event
- registry->stream[count].pcm = pcm;
- registry->stream[count].numid = numid;
- registry->stream[count].type = type;
- registry->count++;
+ RegistryEntryPcmT *entry = calloc(1, sizeof (RegistryEntryPcmT));
+ sndcard->registry[index] = entry;
+ entry->pcm = pcmdev;
+ entry->numid = numid;
+ entry->type = type;
return 0;
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index ad8e842..b69d8c6 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -60,27 +60,30 @@ STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
return pcmSampleSize;
}
-PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts) {
- char string[32];
+PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, AlsaPcmHwInfoT *opts) {
int error;
snd_pcm_hw_params_t *pxmHwParams;
snd_pcm_sw_params_t *pxmSwParams;
+ snd_pcm_format_t format;
+ snd_pcm_access_t access;
// retrieve hadware config from PCM
snd_pcm_hw_params_alloca(&pxmHwParams);
snd_pcm_hw_params_any(pcm->handle, pxmHwParams);
if (!opts->access) opts->access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ snd_pcm_hw_params_get_access(pxmHwParams, &access);
error = snd_pcm_hw_params_set_access(pcm->handle, pxmHwParams, opts->access);
if (error) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s Set_Interleave=%d mode error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), opts->access, snd_strerror(error));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Interleave=%d Fail current=%d mode error=%s", mixer->uid, pcm->cid.cardid, opts->access, access, snd_strerror(error));
+//Fulup goto OnErrorExit;
};
if (opts->format != SND_PCM_FORMAT_UNKNOWN) {
+ snd_pcm_hw_params_get_format(pxmHwParams, &format);
if ((error = snd_pcm_hw_params_set_format(pcm->handle, pxmHwParams, opts->format)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s Set_Format=%d error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), opts->format, snd_strerror(error));
- AlsaDumpFormats(source, pcm->handle);
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Format=%d Fail current=%d error=%s", mixer->uid, pcm->cid.cardid, opts->format, format, snd_strerror(error));
+ AlsaDumpFormats(mixer, pcm->handle);
goto OnErrorExit;
}
}
@@ -88,27 +91,27 @@ PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *op
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:%s Fail PCM=%s Set_Rate=%d error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), opts->rate, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s FailSet_Rate=%d error=%s", mixer->uid, pcm->cid.cardid, opts->rate, snd_strerror(error));
goto OnErrorExit;
}
// check we got requested rate
if (opts->rate != pcmRate) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s Set_Rate ask=%dHz get=%dHz", pcm->uid, ALSA_PCM_UID(pcm->handle, string), pcmRate, opts->rate);
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Rate Fail ask=%dHz get=%dHz", mixer->uid, pcm->cid.cardid,pcmRate, opts->rate);
goto OnErrorExit;
}
}
if (opts->channels) {
if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s Set_Channels=%d error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), opts->channels, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Channels=%d Fail error=%s",mixer->uid, pcm->cid.cardid, opts->channels, snd_strerror(error));
goto OnErrorExit;
};
}
// store selected values
if ((error = snd_pcm_hw_params(pcm->handle, pxmHwParams)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s apply hwparams error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Fail apply hwparams error=%s", mixer->uid, pcm->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
@@ -118,7 +121,7 @@ PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *op
snd_pcm_hw_params_get_rate(pxmHwParams, &opts->rate, 0);
opts->sampleSize = AlsaPeriodSize(opts->format);
if (opts->sampleSize == 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail PCM=%s unsupported format format=%d", pcm->uid, ALSA_PCM_UID(pcm->handle, string), opts->format);
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Fail unsupported format format=%d", mixer->uid, pcm->cid.cardid, opts->format);
goto OnErrorExit;
}
@@ -127,17 +130,17 @@ PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *op
snd_pcm_sw_params_current(pcm->handle, pxmSwParams);
if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, 16)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail to PCM=%s set_buffersize error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Fail set_buffersize error=%s", mixer->uid, pcm->cid.cardid, snd_strerror(error));
goto OnErrorExit;
};
// push software params into PCM
if ((error = snd_pcm_sw_params(pcm->handle, pxmSwParams)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmConf:%s Fail to push software=%s params error=%s", pcm->uid, ALSA_PCM_UID(pcm->handle, string), snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Fail to push params error=%s", mixer->uid, pcm->cid.cardid, snd_strerror(error));
goto OnErrorExit;
};
- AFB_ApiNotice(source->api, "AlsaPcmConf:%s PCM=%s channels=%d rate=%d format=%d access=%d done", pcm->uid, ALSA_PCM_UID(pcm->handle,string), opts->channels, opts->rate, opts->format, opts->access);
+ AFB_ApiNotice(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Done channels=%d rate=%d format=%d access=%d done", mixer->uid, pcm->cid.cardid, opts->channels, opts->rate, opts->format, opts->access);
return 0;
OnErrorExit:
@@ -211,16 +214,19 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u
}
// effectively read pcmIn and push frame to pcmOut
- framesIn = snd_pcm_readi(pcmCopyHandle->pcmIn, pcmCopyHandle->buffer, availIn);
+ framesIn = snd_pcm_readi(pcmCopyHandle->pcmIn, pcmCopyHandle->buffer, availOut);
if (framesIn < 0 || framesIn != availIn) {
AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmIn=%s UNDERUN frame=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmIn, string), framesIn);
+ snd_pcm_prepare(pcmCopyHandle->pcmIn);
goto ExitOnSuccess;
}
// In/Out frames transfer through buffer copy
- framesOut = snd_pcm_writei(pcmCopyHandle->pcmOut, pcmCopyHandle->buffer, framesIn);
+ //framesOut = snd_pcm_writei(pcmCopyHandle->pcmOut, pcmCopyHandle->buffer, framesIn);
+ framesOut = snd_pcm_mmap_writei (pcmCopyHandle->pcmOut, pcmCopyHandle->buffer, framesIn);
if (framesOut < 0 || framesOut != framesIn) {
AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmOut=%s UNDERUN frame=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), (framesIn - framesOut));
+ snd_pcm_prepare(pcmCopyHandle->pcmOut);
goto ExitOnSuccess;
}
@@ -260,87 +266,92 @@ static void *LoopInThread(void *handle) {
pthread_exit(0);
}
-PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaLoopStreamT *loopStream, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT * opts) {
+PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) {
char string[32];
struct pollfd *pcmInFds;
int error;
+
+ // Fulup need to check https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html
+
+ AlsaDumpPcmInfo(mixer,"PcmIn",pcmIn->handle);
+ AlsaDumpPcmInfo(mixer,"PcmOut",pcmOut->handle);
+ // prepare PCM for capture and replay
+ error = AlsaPcmConf(mixer, pcmIn, opts);
+ if (error) goto OnErrorExit;
+
// input and output should match
- error = AlsaPcmConf(source, pcmOut, opts);
+ error = AlsaPcmConf(mixer, pcmOut, opts);
if (error) goto OnErrorExit;
// Prepare PCM for usage
if ((error = snd_pcm_prepare(pcmOut->handle)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
goto OnErrorExit;
};
- // prepare PCM for capture and replay
- error = AlsaPcmConf(source, pcmIn, opts);
- if (error) goto OnErrorExit;
-
// Prepare PCM for usage
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));
+ AFB_ApiError(mixer->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error));
goto OnErrorExit;
};
- AlsaPcmCopyHandleT *pcmCopyHandle = &loopStream->copy;
- pcmCopyHandle->info = "pcmCpy";
- pcmCopyHandle->pcmIn = pcmIn->handle;
- pcmCopyHandle->pcmOut = pcmOut->handle;
- pcmCopyHandle->api = source->api;
- pcmCopyHandle->channels = opts->channels;
- pcmCopyHandle->frameSize = opts->channels * opts->sampleSize;
- pcmCopyHandle->frameCount = ALSA_BUFFER_FRAMES_COUNT;
- pcmCopyHandle->buffer = malloc(pcmCopyHandle->frameCount * pcmCopyHandle->frameSize);
+ AlsaPcmCopyHandleT *cHandle= calloc(1, sizeof(AlsaPcmCopyHandleT));
+ cHandle = cHandle;
+ cHandle->info = "pcmCpy";
+ cHandle->pcmIn = pcmIn->handle;
+ cHandle->pcmOut = pcmOut->handle;
+ cHandle->api = mixer->api;
+ cHandle->channels = opts->channels;
+ cHandle->frameSize = opts->channels * opts->sampleSize;
+ cHandle->frameCount = ALSA_BUFFER_FRAMES_COUNT;
+ cHandle->buffer = malloc(cHandle->frameCount * cHandle->frameSize);
-
// get FD poll descriptor for capture PCM
- int pcmInCount = snd_pcm_poll_descriptors_count(pcmCopyHandle->pcmIn);
+ int pcmInCount = snd_pcm_poll_descriptors_count(cHandle->pcmIn);
if (pcmInCount <= 0) {
- AFB_ApiError(source->api, "AlsaPcmCopy: Fail pcmIn=%s get fds count error=%s", ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error));
+ AFB_ApiError(mixer->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->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));
+ AFB_ApiError(mixer->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) {
+ if ((error = sd_event_new(&cHandle->sdLoop)) < 0) {
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", ALSA_PCM_UID(pcmIn->handle, string), error);
+ if ((error = sd_event_add_io(cHandle->sdLoop, &cHandle->evtsrc, pcmInFds[idx].fd, EPOLLIN, AlsaPcmReadCB, cHandle)) < 0) {
+ AFB_ApiError(mixer->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", ALSA_PCM_UID(pcmIn->handle, string), error);
+ if ((error = pthread_create(&cHandle->thread, NULL, &LoopInThread, cHandle)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaPcmCopy: Fail create waiting thread pcmIn=%s err=%d", ALSA_PCM_UID(pcmIn->handle, string), error);
goto OnErrorExit;
}
// request a higher priority for each audio stream thread
struct sched_param params;
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
- error= pthread_setschedparam(pcmCopyHandle->thread, SCHED_FIFO, &params);
+ error= pthread_setschedparam(cHandle->thread, SCHED_FIFO, &params);
if (error) {
- AFB_ApiWarning(source->api, "AlsaPcmCopy: Fail create increase stream thread priority pcmIn=%s err=%s", ALSA_PCM_UID(pcmIn->handle, string), strerror(error));
+ AFB_ApiWarning(mixer->api, "AlsaPcmCopy: Fail create increase stream thread priority pcmIn=%s err=%s", ALSA_PCM_UID(pcmIn->handle, string), strerror(error));
}
return 0;
OnErrorExit:
- AFB_ApiError(source->api, "AlsaPcmCopy: - pcmIn=%s" , ALSA_PCM_UID(pcmIn->handle, string));
- AFB_ApiError(source->api, "AlsaPcmCopy: - pcmOut=%s", ALSA_PCM_UID(pcmOut->handle, string));
+ AFB_ApiError(mixer->api, "AlsaPcmCopy: - pcmIn=%s" , ALSA_PCM_UID(pcmIn->handle, string));
+ AFB_ApiError(mixer->api, "AlsaPcmCopy: - pcmOut=%s", ALSA_PCM_UID(pcmOut->handle, string));
return -1;
}
diff --git a/plugins/alsa/alsa-effect-ramp.c b/plugins/alsa/alsa-effect-ramp.c
index c861d0e..86bdd24 100644
--- a/plugins/alsa/alsa-effect-ramp.c
+++ b/plugins/alsa/alsa-effect-ramp.c
@@ -24,118 +24,152 @@ for the specific language governing permissions and
#define _GNU_SOURCE // needed for vasprintf
-#include "alsa-softmixer.h"
#include <pthread.h>
#include <sys/syscall.h>
+#include "alsa-softmixer.h"
+
typedef struct {
const char *uid;
- long current;
- long target;
+ long current;
+ long target;
int numid;
- snd_ctl_t *ctlDev;
- AlsaVolRampT *params;
- sd_event_source *evtsrc;
- CtlSourceT *source;
+ AlsaSndCtlT *sndcard;
+ AlsaVolRampT *ramp;
+ sd_event_source *evtsrc;
+ SoftMixerT *mixer;
sd_event *sdLoop;
} VolRampHandleT;
STATIC int VolRampTimerCB(sd_event_source* source, uint64_t timer, void* handle) {
- VolRampHandleT *rampHandle = (VolRampHandleT*) handle;
+ VolRampHandleT *rHandle = (VolRampHandleT*)handle;
int error;
uint64_t usec;
// RampDown
- if (rampHandle->current > rampHandle->target) {
- rampHandle->current = rampHandle->current - rampHandle->params->stepDown;
- if (rampHandle->current < rampHandle->target) rampHandle->current = rampHandle->target;
+ if (rHandle->current > rHandle->target) {
+ rHandle->current = rHandle->current - rHandle->ramp->stepDown;
+ if (rHandle->current < rHandle->target) rHandle->current = rHandle->target;
}
// RampUp
- if (rampHandle->current < rampHandle->target) {
- rampHandle->current = rampHandle->current + rampHandle->params->stepUp;
- if (rampHandle->current > rampHandle->target) rampHandle->current = rampHandle->target;
+ if (rHandle->current < rHandle->target) {
+ rHandle->current = rHandle->current + rHandle->ramp->stepUp;
+ if (rHandle->current > rHandle->target) rHandle->current = rHandle->target;
}
-
- error = AlsaCtlNumidSetLong(rampHandle->source, rampHandle->ctlDev, rampHandle->numid, rampHandle->current);
+
+ error = AlsaCtlNumidSetLong(rHandle->mixer, rHandle->sndcard, rHandle->numid, rHandle->current);
if (error) goto OnErrorExit;
// we reach target stop volram event
- if (rampHandle->current == rampHandle->target) {
- sd_event_source_unref(rampHandle->evtsrc);
- snd_ctl_close (rampHandle->ctlDev);
- free (rampHandle);
- }
- else {
+ if (rHandle->current == rHandle->target) {
+ sd_event_source_unref(rHandle->evtsrc);
+ free(rHandle);
+ } else {
// otherwise validate timer for a new run
- sd_event_now(rampHandle->sdLoop, CLOCK_MONOTONIC, &usec);
- sd_event_source_set_enabled(rampHandle->evtsrc, SD_EVENT_ONESHOT);
- error = sd_event_source_set_time(rampHandle->evtsrc, usec + rampHandle->params->delay);
+ sd_event_now(rHandle->sdLoop, CLOCK_MONOTONIC, &usec);
+ sd_event_source_set_enabled(rHandle->evtsrc, SD_EVENT_ONESHOT);
+ error = sd_event_source_set_time(rHandle->evtsrc, usec + rHandle->ramp->delay);
}
return 0;
OnErrorExit:
- AFB_ApiWarning(rampHandle->source->api, "VolRampTimerCB stream=%s numid=%d value=%ld", rampHandle->uid, rampHandle->numid, rampHandle->current);
+ AFB_ApiWarning(rHandle->mixer->api, "VolRampTimerCB stream=%s numid=%d value=%ld", rHandle->uid, rHandle->numid, rHandle->current);
sd_event_source_unref(source); // abandon volRamp
return -1;
}
-PUBLIC int AlsaVolRampApply(CtlSourceT *source, AlsaSndLoopT *frontend, AlsaLoopStreamT *stream, AlsaVolRampT *ramp, json_object *volumeJ) {
+PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamAudioT *stream, json_object *rampJ) {
long curvol, newvol;
- const char*volString;
- int error;
+ const char *uid, *volS;
+ json_object *volJ;
+ int error, index;
uint64_t usec;
-
- snd_ctl_t *ctlDev =AlsaCtlOpenCtl(source, frontend->cardid);
- if (!ctlDev) goto OnErrorExit;
-
- error = AlsaCtlNumidGetLong(source, ctlDev, stream->volume, &curvol);
+
+ error = wrap_json_unpack(rampJ, "{ss so !}"
+ , "uid", &uid
+ , "vol", &volJ
+ );
if (error) {
- AFB_ApiError(source->api,"AlsaVolRampApply:%s(stream) Fail to get volume numid=%d", stream->uid, stream->volume);
+ AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-json should {uid:ramp, vol:[+,-,=]value} ramp=%s", mixer->uid, stream->uid, json_object_get_string(rampJ));
goto OnErrorExit;
}
- switch (json_object_get_type(volumeJ)) {
+ switch (json_object_get_type(volJ)) {
+ int count;
+
case json_type_string:
- volString = json_object_get_string(volumeJ);
- switch (volString[0]) {
+ volS = json_object_get_string(volJ);
+
+ switch (volS[0]) {
case '+':
- sscanf(&volString[1], "%ld", &newvol);
+ count= sscanf(&volS[1], "%ld", &newvol);
newvol = curvol + newvol;
break;
case '-':
- sscanf(&volString[1], "%ld", &newvol);
+ count= sscanf(&volS[1], "%ld", &newvol);
newvol = curvol - newvol;
break;
+
+ case '=':
+ count= sscanf(&volS[1], "%ld", &newvol);
+ break;
+
default:
- AFB_ApiError(source->api,"AlsaVolRampApply:%s(stream) relative volume should start by '+|-' value=%s", stream->uid, json_object_get_string(volumeJ));
- goto OnErrorExit;
+ // hope for int as a string and force it as relative
+ sscanf(&volS[0], "%ld", &newvol);
+ if (newvol < 0) newvol = curvol - newvol;
+ else newvol = curvol + newvol;
+ }
+
+ if (count != 1) {
+ AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-numeric expect {uid:%s, vol:[+,-,=]value} get vol:%s", mixer->uid, stream->uid, uid, json_object_get_string(volJ));
+ goto OnErrorExit;
}
break;
case json_type_int:
- newvol = json_object_get_int(volumeJ);
+ newvol = json_object_get_int(volJ);
break;
+
default:
- AFB_ApiError(source->api,"AlsaVolRampApply:%s(stream) volume should be string or integer value=%s", stream->uid, json_object_get_string(volumeJ));
+ AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-type expect {uid:%s, vol:[+,-,=]value} get vol:%s", mixer->uid, stream->uid, uid, json_object_get_string(volJ));
goto OnErrorExit;
}
- VolRampHandleT *rampHandle = calloc(1, sizeof (VolRampHandleT));
- rampHandle->uid= stream->uid;
- rampHandle->numid= stream->volume;
- rampHandle->ctlDev= ctlDev;
- rampHandle->source = source;
- rampHandle->params = ramp;
- rampHandle->target = newvol;
- rampHandle->current= curvol;
- rampHandle->sdLoop= stream->copy.sdLoop;
+ error = AlsaCtlNumidGetLong(mixer, sndcard, stream->volume, &curvol);
+ if (error) {
+ AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s ramp=%s Fail to get volume from numid=%d", mixer->uid, stream->uid, uid, stream->volume);
+ goto OnErrorExit;
+ }
+ // search for ramp uid in mixer
+ for (index=0; index<= mixer->max.ramps; index++) {
+ if (!strcasecmp(mixer->ramps[index]->uid, uid)) {
+ break;
+ }
+ }
+
+ if (index == mixer->max.ramps) {
+ AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s ramp=%s does not exit", mixer->uid, stream->uid, uid);
+ goto OnErrorExit;
+ }
+
+ VolRampHandleT *rHandle = calloc(1, sizeof (VolRampHandleT));
+ rHandle->uid = stream->uid;
+ rHandle->numid = stream->volume;
+ rHandle->sndcard = sndcard;
+ rHandle->mixer = mixer;
+ rHandle->ramp = mixer->ramps[index];
+ rHandle->target = newvol;
+ rHandle->current = curvol;
+ rHandle->sdLoop = mixer->sdLoop;
+
// set a timer with ~250us accuracy
- sd_event_now(rampHandle->sdLoop, CLOCK_MONOTONIC, &usec);
- (void)sd_event_add_time(rampHandle->sdLoop, &rampHandle->evtsrc, CLOCK_MONOTONIC, usec+100, 250, VolRampTimerCB, rampHandle);
+ sd_event_now(rHandle->sdLoop, CLOCK_MONOTONIC, &usec);
+ (void) sd_event_add_time(rHandle->sdLoop, &rHandle->evtsrc, CLOCK_MONOTONIC, usec + 100, 250, VolRampTimerCB, rHandle);
return 0;
diff --git a/plugins/alsa/alsa-plug-dmix.c b/plugins/alsa/alsa-plug-dmix.c
index b06d5f6..a9f6b7e 100644
--- a/plugins/alsa/alsa-plug-dmix.c
+++ b/plugins/alsa/alsa-plug-dmix.c
@@ -25,19 +25,16 @@ static int uniqueIpcIndex = 1024;
ALSA_PLUG_PROTO(dmix);
-PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open) {
+PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open) {
snd_config_t *dmixConfig, *slaveConfig, *elemConfig, *pcmConfig;
- AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
- pcmPlug->uid= strdup(pcmName);
- pcmPlug->cardid=pcmPlug->uid;
-
+ AlsaPcmCtlT *pcmPlug= calloc(1,sizeof(AlsaPcmCtlT));
+ AlsaSndCtlT *sndSlave=pcmSlave->sndcard;
+ pcmPlug->cid.cardid=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->cardid);
+ error += snd_config_set_id (dmixConfig, pcmPlug->cid.cardid);
error += snd_config_imake_string(&elemConfig, "type", "dmix");
error += snd_config_add(dmixConfig, elemConfig);
error += snd_config_imake_integer(&elemConfig, "ipc_key", uniqueIpcIndex++);
@@ -45,10 +42,10 @@ PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, Als
if (error) goto OnErrorExit;
error += snd_config_make_compound(&slaveConfig, "slave", 0);
- error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->cardid);
- if (pcmSlave->params.rate) {
+ error += snd_config_imake_string(&elemConfig, "pcm", sndSlave->cid.cardid);
+ if (sndSlave->params->rate) {
error += snd_config_add(slaveConfig, elemConfig);
- error += snd_config_imake_integer(&elemConfig, "rate", pcmSlave->params.rate);
+ error += snd_config_imake_integer(&elemConfig, "rate", sndSlave->params->rate);
}
error += snd_config_add(slaveConfig, elemConfig);
if (error) goto OnErrorExit;
@@ -57,26 +54,28 @@ PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, Als
error += snd_config_add(dmixConfig, slaveConfig);
if (error) goto OnErrorExit;
- if (open) error = _snd_pcm_dmix_open(&pcmPlug->handle, pcmPlug->cardid, snd_config, dmixConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
+ if (open) error = _snd_pcm_dmix_open(&pcmPlug->handle, pcmPlug->cid.cardid, snd_config, dmixConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateDmix: fail to create Dmix=%s Slave=%s Error=%s", pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaCreateDmix: fail to create Dmix=%s Slave=%s Error=%s", sndSlave->cid.cardid, sndSlave->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
+ snd_config_update();
error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, dmixConfig);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateDmix: fail to add configDMIX=%s", pcmPlug->cardid);
+ AFB_ApiError(mixer->api, "AlsaCreateDmix: fail to add configDMIX=%s", pcmPlug->cid.cardid);
goto OnErrorExit;
}
// Debug config & pcm
- //AlsaDumpCtlConfig (source, "plug-dmix", dmixConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateDmix: %s done\n", pcmPlug->cardid);
+ //AlsaDumpCtlConfig (mixer, "plug-dmix", dmixConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateDmix: %s done\n", pcmPlug->cid.cardid);
return pcmPlug;
OnErrorExit:
- AlsaDumpCtlConfig(source, "plug-dmix", dmixConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateDmix: OnErrorExit\n");
+ AlsaDumpCtlConfig(mixer, "plug-pcm", pcmConfig, 1);
+ AlsaDumpCtlConfig(mixer, "plug-dmix", dmixConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateDmix: OnErrorExit\n");
return NULL;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-plug-multi.c b/plugins/alsa/alsa-plug-multi.c
deleted file mode 100644
index c28508c..0000000
--- a/plugins/alsa/alsa-plug-multi.c
+++ /dev/null
@@ -1,106 +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"
-
-ALSA_PLUG_PROTO(multi);
-
-PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid, int open) {
- SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
- snd_config_t *multiConfig, *elemConfig, *slavesConfig, *slaveConfig, *bindingsConfig, *bindingConfig, *pcmConfig;
- int error = 0, channelIdx=0;
- AlsaPcmInfoT *pcmPlug = calloc(1, sizeof (AlsaPcmInfoT));
- pcmPlug->uid = pcmUid;
- pcmPlug->cardid = pcmUid;
-
- assert(mixerHandle);
-
- AlsaPcmInfoT* pcmSlaves=mixerHandle->backend;
- if (!pcmSlaves) {
- AFB_ApiError(source->api, "AlsaCreateMulti: No Sound Card find [should Registry snd_cards first]");
- goto OnErrorExit;
- }
-
- // refresh global alsalib config and create PCM top config
- snd_config_update();
- error += snd_config_top(&multiConfig);
- error += snd_config_set_id (multiConfig, pcmPlug->cardid);
- error += snd_config_imake_string(&elemConfig, "type", "multi");
- error += snd_config_add(multiConfig, elemConfig);
- if (error) goto OnErrorExit;
-
- error += snd_config_make_compound(&slavesConfig, "slaves", 0);
- error += snd_config_make_compound(&bindingsConfig, "bindings", 0);
-
- // loop on sound card to include into multi
- for (int idx = 0; pcmSlaves[idx].uid != NULL; idx++) {
- AlsaPcmInfoT* sndcard=&pcmSlaves[idx];
- AlsaPcmChannelT *channels = sndcard->channels;
-
- for (channelIdx=0; channels[channelIdx].uid != NULL; channelIdx++) {
- char idxS[4]; // 999 channel should be more than enough
- snprintf (idxS, sizeof(idxS), "%d", pcmPlug->ccount++);
- // multi does not support to name channels
- error += snd_config_make_compound(&bindingConfig,idxS, 0);
- error += snd_config_imake_string(&elemConfig, "slave", sndcard->uid);
- error += snd_config_add(bindingConfig, elemConfig);
- error += snd_config_imake_integer(&elemConfig,"channel", channelIdx);
- error += snd_config_add(bindingConfig, elemConfig);
- error += snd_config_add(bindingsConfig, bindingConfig);
- }
-
- error += snd_config_make_compound(&slaveConfig, sndcard->uid, 0);
- error += snd_config_imake_string(&elemConfig, "pcm", sndcard->cardid);
- error += snd_config_add(slaveConfig, elemConfig);
- error += snd_config_imake_integer(&elemConfig, "channels", channelIdx);
- error += snd_config_add(slaveConfig, elemConfig);
- error += snd_config_add(slavesConfig, slaveConfig);
- }
-
- error += snd_config_add(multiConfig, slavesConfig);
- error += snd_config_add(multiConfig, bindingsConfig);
- if (error) goto OnErrorExit;
-
- // update top config to access previous plugin PCM
- snd_config_update();
-
- if (open) error = _snd_pcm_multi_open(&pcmPlug->handle, pcmUid, snd_config, multiConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
- if (error) {
- AFB_ApiError(source->api, "AlsaCreateMulti: fail to create Plug=%s Error=%s", pcmPlug->cardid, snd_strerror(error));
- goto OnErrorExit;
- }
-
- error += snd_config_search(snd_config, "pcm", &pcmConfig);
- error += snd_config_add(pcmConfig, multiConfig);
- if (error) {
- AFB_ApiError(source->api, "AlsaCreateDmix: fail to add configDMIX=%s", pcmPlug->cardid);
- goto OnErrorExit;
- }
-
- // Debug config & pcm
- //AlsaDumpCtlConfig(source, "plug-multi", multiConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateMulti: %s done\n", pcmPlug->cardid);
- return pcmPlug;
-
-OnErrorExit:
- AlsaDumpCtlConfig(source, "plug-multi", multiConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateMulti: OnErrorExit\n");
- return NULL;
-} \ No newline at end of file
diff --git a/plugins/alsa/alsa-plug-rate.c b/plugins/alsa/alsa-plug-rate.c
index a9b9e23..4c83c7f 100644
--- a/plugins/alsa/alsa-plug-rate.c
+++ b/plugins/alsa/alsa-plug-rate.c
@@ -23,59 +23,60 @@
ALSA_PLUG_PROTO(rate);
+PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open) {
-PUBLIC AlsaPcmInfoT* AlsaCreateRate(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open) {
-
snd_config_t *rateConfig, *slaveConfig, *elemConfig, *pcmConfig;
- AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
- pcmPlug->uid= strdup(pcmName);
- pcmPlug->cardid=pcmPlug->uid;
+ AlsaPcmCtlT *pcmPlug = calloc(1, sizeof (AlsaPcmCtlT));
+ pcmPlug->cid.cardid = pcmName;
- int error=0;
+ int error = 0;
// refresh global alsalib config and create PCM top config
snd_config_update();
error += snd_config_top(&rateConfig);
- error += snd_config_set_id (rateConfig, pcmPlug->cardid);
- error += snd_config_imake_string(&elemConfig, "type", "plug");
+ error += snd_config_set_id(rateConfig, pcmPlug->cid.cardid);
+ error += snd_config_imake_string(&elemConfig, "type", "rate");
error += snd_config_add(rateConfig, elemConfig);
if (error) goto OnErrorExit;
error += snd_config_make_compound(&slaveConfig, "slave", 0);
- error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->cardid);
- if (pcmSlave->params.rate) {
+ error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->cid.cardid);
+ error += snd_config_add(slaveConfig, elemConfig);
+ if (params->rate) {
+ error += snd_config_imake_integer(&elemConfig, "rate", params->rate);
+ error += snd_config_add(slaveConfig, elemConfig);
+ }
+ if (params->format) {
+ error += snd_config_imake_string(&elemConfig, "format", params->formatS);
error += snd_config_add(slaveConfig, elemConfig);
- // *** error += snd_config_imake_integer(&elemConfig, "rate", pcmSlave->params.rate);
- error += snd_config_imake_integer(&elemConfig, "rate", 48000);
}
- error += snd_config_add(slaveConfig, elemConfig);
if (error) goto OnErrorExit;
// add leaf into config
error += snd_config_add(rateConfig, slaveConfig);
if (error) goto OnErrorExit;
-
- error += snd_config_search(snd_config, "pcm", &pcmConfig);
+
+ error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, rateConfig);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateRate: fail to add configRATE=%s", pcmPlug->cardid);
+ AFB_ApiError(mixer->api, "AlsaCreateRate: fail to add configRATE=%s", pcmPlug->cid.cardid);
goto OnErrorExit;
}
-
- if (open) error = _snd_pcm_rate_open(&pcmPlug->handle, pcmPlug->cardid, snd_config, rateConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
+
+ if (open) error = _snd_pcm_rate_open(&pcmPlug->handle, pcmPlug->cid.cardid, snd_config, rateConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateRate: fail to create Rate=%s Slave=%s Error=%s", pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaCreateRate: fail to create Rate=%s Slave=%s Error=%s", pcmPlug->cid.cardid, pcmSlave->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
-
+
// Debug config & pcm
- AlsaDumpCtlConfig (source, "plug-rate", pcmConfig, 1);
- //AlsaDumpCtlConfig (source, "plug-rate", rateConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateRate: %s done\n", pcmPlug->cardid);
+ AlsaDumpCtlConfig(mixer, "plug-rate", pcmConfig, 1);
+ //AlsaDumpCtlConfig (mixer, "plug-rate", rateConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateRate: %s done\n", pcmPlug->cid.cardid);
return pcmPlug;
OnErrorExit:
- AlsaDumpCtlConfig(source, "plug-rate", rateConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateRate: OnErrorExit\n");
+ AlsaDumpCtlConfig(mixer, "plug-rate", rateConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateRate: OnErrorExit\n");
return NULL;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c
index f254c68..ac3aa1c 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -22,122 +22,163 @@
ALSA_PLUG_PROTO(route);
-STATIC int CardChannelByUid(CtlSourceT *source, AlsaPcmInfoT *pcmBackend, const char *uid) {
- int channelIdx = -1;
+typedef struct {
+ const char *uid;
+ const char *cardid;
+ int cardidx;
+ int ccount;
+ int port;
+} ChannelCardPortT;
- // search for channel within all sound card backend (channel port target is computed by order)
- int targetIdx=0;
- for (int cardIdx = 0; pcmBackend[cardIdx].uid != NULL; cardIdx++) {
- AlsaPcmChannelT *channels = pcmBackend[cardIdx].channels;
+STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT *response) {
+
+ // search for channel within all sound card sink (channel port target is computed by order)
+ for (int idx = 0; mixer->sinks[idx]; idx++) {
+ int jdx;
+
+ AlsaPcmChannelT **channels = mixer->sinks[idx]->channels;
if (!channels) {
- AFB_ApiError(source->api, "CardChannelByUid: No Backend card=%s [should declare channels]", pcmBackend[cardIdx].uid);
+ AFB_ApiError(mixer->api, "CardChannelByUid: No Sink card=%s [should declare channels]", mixer->sinks[idx]->uid);
goto OnErrorExit;
}
- for (int idx = 0; channels[idx].uid != NULL; idx++) {
- if (!strcmp(channels[idx].uid, uid)) return targetIdx;
- targetIdx++;
+ for (jdx = 0; jdx < mixer->sinks[idx]->ccount; jdx++) {
+ if (!strcasecmp(channels[jdx]->uid, uid)) {
+ response->port = channels[jdx]->port;
+ response->uid = mixer->sinks[idx]->uid;
+ response->ccount = mixer->sinks[idx]->ccount;
+ response->cardid = mixer->sinks[idx]->sndcard->cid.cardid;
+ response->cardidx = mixer->sinks[idx]->sndcard->cid.cardidx;
+ break;
+ }
+ }
+
+ if (jdx == mixer->sinks[idx]->ccount) {
+ AFB_ApiError(mixer->api, "CardChannelByUid: No Channel with uid=%s [should declare channels]", uid);
+ goto OnErrorExit;
}
}
- // this is OnErrorExit
- return channelIdx;
+ return 0;
OnErrorExit:
return -1;
}
-PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int open) {
- SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
+PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open) {
snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
- int error = 0;
- AlsaPcmInfoT *pcmPlug = calloc(1, sizeof (AlsaPcmInfoT));
- pcmPlug->uid = zone->uid;
- pcmPlug->cardid = zone->uid;
-
- assert(mixerHandle);
+ int scount=0, error = 0;
+ ChannelCardPortT slave, channel;
+ AlsaPcmCtlT *pcmRoute = calloc(1, sizeof (AlsaPcmCtlT));
- AlsaPcmInfoT *pcmBackend = mixerHandle->backend;
- AlsaPcmInfoT* pcmSlave=mixerHandle->multiPcm;
- if (!pcmBackend || !pcmSlave) {
- AFB_ApiError(source->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No Sound Card Ctl find [should Registry snd_cards first]"
- , mixerHandle->uid, zone->uid);
+ if (!mixer->sinks) {
+ AFB_ApiError(mixer->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No sink found [should Registry sound card first]", mixer->uid, zone->uid);
goto OnErrorExit;
}
- // refresh global alsalib config and create PCM top config
- snd_config_update();
- error += snd_config_top(&routeConfig);
- error += snd_config_set_id(routeConfig, pcmPlug->cardid);
- error += snd_config_imake_string(&elemConfig, "type", "route");
- error += snd_config_add(routeConfig, elemConfig);
- error += snd_config_make_compound(&slaveConfig, "slave", 0);
- error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->cardid);
- error += snd_config_add(slaveConfig, elemConfig);
- error += snd_config_imake_integer(&elemConfig, "channels", pcmSlave->ccount);
- error += snd_config_add(slaveConfig, elemConfig);
- error += snd_config_add(routeConfig, slaveConfig);
- error += snd_config_make_compound(&tableConfig, "ttable", 0);
- error += snd_config_add(routeConfig, tableConfig);
- if (error) goto OnErrorExit;
+ char *cardid;
+ (void) asprintf(&cardid, "route-%s", zone->uid);
+ pcmRoute->cid.cardid = (const char *) cardid;
+
+ pcmRoute->params = ApiSinkGetParamsByZone(mixer, zone->uid);
+ zone->params=pcmRoute->params;
+
+ // use 1st zone channel to retrieve sound card name + channel count.
+ error = CardChannelByUid(mixer, zone->sinks[0]->uid, &slave);
+ if (error) {
+ AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[0]->uid);
+ goto OnErrorExit;
+ }
+
+ // move from hardware to DMIX attach to sndcard
+ char *dmixUid;
+ (void) asprintf(&dmixUid, "dmix-%s", slave.uid);
+
+ // temporary store to unable multiple channel to route to the same port
+ snd_config_t **cports = alloca(slave.ccount * sizeof (void*));
+ memset(cports, 0, slave.ccount * sizeof (void*));
+ int zcount = 0;
- // tempry store to unable multiple channel to route to the same port
- snd_config_t **cports;
- cports = alloca(sizeof (snd_config_t*)*(pcmSlave->ccount + 1));
- memset(cports, 0, (sizeof (snd_config_t*)*(pcmSlave->ccount + 1)));
+ // We create 1st ttable to retrieve sndcard slave and channel count
+ (void)snd_config_make_compound(&tableConfig, "ttable", 0);
- // loop on sound card to include into multi
- for (int idx = 0; zone->channels[idx].uid != NULL; idx++) {
+ for (scount = 0; zone->sinks[scount] != NULL; scount++) {
+
+ error = CardChannelByUid(mixer, zone->sinks[scount]->uid, &channel);
+ if (error) {
+ AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[scount]->uid);
+ goto OnErrorExit;
+ }
- int target = CardChannelByUid(source, pcmBackend, zone->channels[idx].uid);
- if (target < 0) {
- AFB_ApiError(source->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->channels[idx].uid);
+ if (slave.cardidx != channel.cardidx) {
+ AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) cannot span over multiple sound card %s != %s ", zone->uid, slave.cardid, channel.cardid);
goto OnErrorExit;
}
-
- int channel = zone->channels[idx].port;
+
+ int port = zone->sinks[scount]->port;
+ int target = channel.port;
double volume = 1.0; // currently only support 100%
// if channel entry does not exit into ttable create it now
- if (!cports[channel]) {
- pcmPlug->ccount++;
+ if (!cports[port]) {
+ zcount++;
char channelS[4]; // 999 channel should be more than enough
- snprintf(channelS, sizeof (channelS), "%d", channel);
- error += snd_config_make_compound(&cports[channel], channelS, 0);
- error += snd_config_add(tableConfig, cports[channel]);
+ snprintf(channelS, sizeof (channelS), "%d", port);
+ error += snd_config_make_compound(&cports[port], channelS, 0);
+ error += snd_config_add(tableConfig, cports[port]);
}
// ttable require target port as a table and volume as a value
char targetS[4];
snprintf(targetS, sizeof (targetS), "%d", target);
error += snd_config_imake_real(&elemConfig, targetS, volume);
- error += snd_config_add(cports[channel], elemConfig);
+ error += snd_config_add(cports[port], elemConfig);
if (error) goto OnErrorExit;
}
+ if (error) goto OnErrorExit;
- // update top config to access previous plugin PCM
+ // update zone with route channel count and sndcard params
+ pcmRoute->ccount = zcount;
+ zone->ccount=zcount;
+
+ // refresh global alsalib config and create PCM top config
snd_config_update();
+ error += snd_config_top(&routeConfig);
+ error += snd_config_set_id(routeConfig, cardid);
+ error += snd_config_imake_string(&elemConfig, "type", "route");
+ error += snd_config_add(routeConfig, elemConfig);
+ error += snd_config_make_compound(&slaveConfig, "slave", 0);
+ error += snd_config_imake_string(&elemConfig, "pcm", dmixUid);
+ error += snd_config_add(slaveConfig, elemConfig);
+// error += snd_config_imake_integer(&elemConfig, "channels", scount);
+// error += snd_config_add(slaveConfig, elemConfig);
+ error += snd_config_add(routeConfig, slaveConfig);
+ error += snd_config_add(routeConfig, tableConfig);
+ if (error) goto OnErrorExit;
- if (open) error = _snd_pcm_route_open(&pcmPlug->handle, zone->uid, snd_config, routeConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (open) error = _snd_pcm_route_open(&pcmRoute->handle, pcmRoute->cid.cardid, snd_config, routeConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateRoute:zone(%s) fail to create Plug=%s error=%s", zone->uid, pcmPlug->cardid, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to create Plug=%s error=%s", zone->uid, pcmRoute->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
+ snd_config_update();
error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, routeConfig);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateDmix:%s fail to add configDMIX=%s", zone->uid, pcmPlug->cardid);
+ AFB_ApiError(mixer->api, "AlsaCreateDmix:%s fail to add config route=%s error=%s", zone->uid, pcmRoute->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
// Debug config & pcm
- //AlsaDumpCtlConfig(source, "plug-route", routeConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateRoute:zone(%s) done\n", zone->uid);
- return pcmPlug;
+ AFB_ApiNotice(mixer->api, "AlsaCreateRoute:zone(%s) done", zone->uid);
+ AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1);
+ return pcmRoute;
OnErrorExit:
- AlsaDumpCtlConfig(source, "plug-route", routeConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateRoute:zone(%s) OnErrorExit\n", zone->uid);
+ free(pcmRoute);
+ AlsaDumpCtlConfig(mixer, "plug-pcm", snd_config, 1);
+ AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateRoute:zone(%s) OnErrorExit\n", zone->uid);
return NULL;
-} \ No newline at end of file
+}
diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c
index cb764ef..f16b0a9 100644
--- a/plugins/alsa/alsa-plug-vol.c
+++ b/plugins/alsa/alsa-plug-vol.c
@@ -22,61 +22,30 @@
ALSA_PLUG_PROTO(softvol); // stream uses solftvol plugin
-STATIC AlsaPcmInfoT* SlaveZoneByUid(CtlSourceT *source, AlsaPcmInfoT **pcmZones, const char *uid) {
- AlsaPcmInfoT *slaveZone= NULL;
-
- // Loop on every Registryed zone pcm and extract (cardid) from (uid)
- for (int idx = 0; pcmZones[idx] != NULL; idx++) {
- if (!strcasecmp (pcmZones[idx]->uid, uid)) {
- slaveZone= pcmZones[idx];
- return slaveZone;
- }
- }
-
- return NULL;
-}
-
-PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaLoopStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open) {
- SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
+PUBLIC AlsaPcmCtlT* AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaSndZoneT *zone, AlsaSndCtlT *sndcard, char* ctlName, int max, int open) {
snd_config_t *streamConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig;
+ AlsaPcmCtlT *pcmVol= calloc(1,sizeof(AlsaPcmCtlT));
int error = 0;
- AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
-
- assert (mixerHandle);
-
- // assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = mixerHandle->frontend;
- if (!ctlLoop) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Loop found [should Registry snd_loop first]",stream->uid);
- goto OnErrorExit;
- }
// assert static/global softmixer handle get requited info
- AlsaPcmInfoT **pcmZones = mixerHandle->routes;
+ AlsaSndZoneT **pcmZones = mixer->zones;
if (!pcmZones) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Zone found [should Registry snd_zones first]", stream->uid);
+ AFB_ApiError(mixer->api, "AlsaCreateSoftvol:%s(stream) No Zone found [should Registry zones first]", stream->uid);
goto OnErrorExit;
}
+
+ char *cardid;
+ (void) asprintf(&cardid, "softvol-%s", stream->uid);
+ pcmVol->cid.cardid = (const char *) cardid;
- // search for target zone uid
- AlsaPcmInfoT *pcmSlave= SlaveZoneByUid (source, pcmZones, stream->zone);
- if (!pcmSlave || !pcmSlave->uid) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to find Zone=%s", stream->uid, stream->zone);
- goto OnErrorExit;
- }
+ char *slaveid;
+ (void)asprintf(&slaveid, "route-%s", zone->uid);
+ // Fulup debug (void) asprintf(&slaveid, "dmix-%s", "8CH-USB");
- // stream inherit from zone channel count
- pcmPlug->uid= strdup(stream->uid);
- pcmPlug->cardid= pcmPlug->uid;
- pcmPlug->devpath=NULL;
- pcmPlug->ccount= pcmSlave->ccount;
- memcpy (&pcmPlug->params, &stream->params, sizeof(AlsaPcmHwInfoT));
- pcmPlug->params.channels= pcmSlave->ccount;
-
// refresh global alsalib config and create PCM top config
snd_config_update();
error += snd_config_top(&streamConfig);
- error += snd_config_set_id (streamConfig, pcmPlug->cardid);
+ error += snd_config_set_id (streamConfig, pcmVol->cid.cardid);
error += snd_config_imake_string(&elemConfig, "type", "softvol");
error += snd_config_add(streamConfig, elemConfig);
error += snd_config_imake_integer(&elemConfig, "resolution", max+1);
@@ -85,7 +54,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaLoopStreamT *stre
// add slave leaf
error += snd_config_make_compound(&slaveConfig, "slave", 0);
- error += snd_config_imake_string(&elemConfig, "pcm", pcmSlave->cardid);
+ error += snd_config_imake_string(&elemConfig, "pcm", slaveid);
error += snd_config_add(slaveConfig, elemConfig);
error += snd_config_add(streamConfig, slaveConfig);
if (error) goto OnErrorExit;
@@ -94,34 +63,36 @@ PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaLoopStreamT *stre
error += snd_config_make_compound(&controlConfig, "control", 0);
error += snd_config_imake_string(&elemConfig, "name", ctlName);
error += snd_config_add(controlConfig, elemConfig);
- error += snd_config_imake_integer(&elemConfig, "card", ctlControl->cardidx);
+ error += snd_config_imake_integer(&elemConfig, "card", sndcard->cid.cardidx);
error += snd_config_add(controlConfig, elemConfig);
error += snd_config_add(streamConfig, controlConfig);
if (error) goto OnErrorExit;
- // update top config to access previous plugin PCM
- snd_config_update();
-
- if (open) error = _snd_pcm_softvol_open(&pcmPlug->handle, stream->uid, snd_config, streamConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
+ if (open) error = _snd_pcm_softvol_open(&pcmVol->handle, stream->uid, snd_config, streamConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to create Plug=%s Slave=%s error=%s", stream->uid, pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaCreateSoftvol:%s(stream) fail to create Plug=%s Slave=%s error=%s", stream->uid, pcmVol->cid.cardid, sndcard->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
+ // update top config to access previous plugin PCM
+ snd_config_update();
+
error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, streamConfig);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to add config", stream->uid);
+ AFB_ApiError(mixer->api, "AlsaCreateSoftvol:%s(stream) fail to add config error=%s", stream->uid, snd_strerror(error));
goto OnErrorExit;
}
// Debug config & pcm
- //AlsaDumpCtlConfig (source, "plug-stream", streamConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateSoftvol:%s(stream) done\n", stream->uid);
- return pcmPlug;
+ //AlsaDumpCtlConfig (mixer, "plug-config", pcmConfig, 1);
+ //AlsaDumpCtlConfig(mixer, "plug-softvol", streamConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateSoftvol:%s(stream) done\n", stream->uid);
+ return pcmVol;
OnErrorExit:
- AlsaDumpCtlConfig(source, "plug-stream", streamConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateSoftvol:%s(stream) OnErrorExit\n", stream->uid);
+ AlsaDumpCtlConfig (mixer, "plug-config", pcmConfig, 1);
+ AlsaDumpCtlConfig(mixer, "plug-softvol", streamConfig, 1);
+ AFB_ApiNotice(mixer->api, "AlsaCreateSoftvol:%s(stream) OnErrorExit\n", stream->uid);
return NULL;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index a8d7af6..f8d94e4 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -40,14 +40,21 @@
#define STATIC static
#endif
+#define MAINLOOP_CONCURENCY 0
#define MAINLOOP_WATCHDOG 30000
-#define MAX_AUDIO_STREAMS 8*2
#define ALSA_DEFAULT_PCM_RATE 48000
#define ALSA_DEFAULT_PCM_VOLUME 80
#define ALSA_BUFFER_FRAMES_COUNT 1024
#define ALSA_CARDID_MAX_LEN 64
+#define SMIXER_SUBDS_CTLS 3
+#define SMIXER_DEFLT_LOOPS 4
+#define SMIXER_DEFLT_SINKS 8
+#define SMIXER_DEFLT_SOURCES 32
+#define SMIXER_DEFLT_ZONES 32
+#define SMIXER_DEFLT_STREAMS 32
+#define SMIXER_DEFLT_RAMPS 8
#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)
@@ -59,7 +66,6 @@
#define STATIC static
#endif
-
typedef enum {
FONTEND_NUMID_IGNORE,
FONTEND_NUMID_PAUSE,
@@ -82,11 +88,13 @@ typedef struct {
} AlsaPcmCopyHandleT;
typedef struct {
- const char *uid;
- int delay; // delay between volset in us
- int stepDown; // linear %
- int stepUp; // linear %
-} AlsaVolRampT;
+ const char*name;
+ int numid;
+ int count;
+ long min;
+ long max;
+ long step;
+} AlsaSndControlT;
typedef struct {
const char*uid;
@@ -96,6 +104,7 @@ typedef struct {
typedef struct {
unsigned int rate;
unsigned int channels;
+ const char *formatS;
snd_pcm_format_t format;
snd_pcm_access_t access;
size_t sampleSize;
@@ -103,126 +112,175 @@ typedef struct {
typedef struct {
const char *uid;
+ int delay; // delay between volset in us
+ int stepDown; // linear %
+ int stepUp; // linear %
+} AlsaVolRampT;
+
+typedef struct {
+ int cardidx;
const char *devpath;
const char *cardid;
- int cardidx;
+ const char *name;
+ const char *longname;
int device;
int subdev;
- int numid;
+} AlsaDevInfoT;
+
+typedef struct {
int ccount;
+ AlsaDevInfoT cid;
snd_pcm_t *handle;
- AlsaPcmChannelT *channels;
- AlsaPcmHwInfoT params;
-} AlsaPcmInfoT;
+ AlsaPcmHwInfoT *params;
+} AlsaPcmCtlT;
typedef struct {
- const char *uid;
- snd_pcm_stream_t type;
- AlsaPcmChannelT *channels;
- AlsaPcmInfoT *pcm;
-} AlsaSndZoneT;
+ int numid;
+ RegistryNumidT type;
+ AlsaPcmCtlT *pcm;
+} RegistryEntryPcmT;
+
+typedef struct {
+ long rcount;
+ AlsaDevInfoT cid;
+ snd_ctl_t *ctl;
+ AlsaPcmHwInfoT *params;
+ RegistryEntryPcmT **registry;
+} AlsaSndCtlT;
typedef struct {
- AlsaPcmInfoT *pcm;
- int numid;
- RegistryNumidT type;
-} RegistryStreamT;
+ const char *uid;
+ AlsaPcmChannelT **sources;
+ AlsaPcmChannelT **sinks;
+ int ccount;
+ AlsaPcmHwInfoT *params;
+} AlsaSndZoneT;
typedef struct {
- RegistryStreamT stream[MAX_AUDIO_STREAMS + 1];
- int count;
- snd_ctl_t *ctlDev;
-} RegistryHandleT;
+ const char *uid;
+ unsigned int ccount;
+ AlsaSndCtlT *sndcard;
+ AlsaSndControlT volume;
+ AlsaSndControlT mute;
+ AlsaPcmChannelT **channels;
+ snd_pcm_stream_t direction;
+} AlsaSndPcmT;
+typedef struct {
+ const char*uid;
+ int index;
+ int numid;
+} AlsaLoopSubdevT;
typedef struct {
const char *uid;
- const char *devpath;
- const char *cardid;
- int cardidx;
int playback;
int capture;
- int scount;
- AlsaPcmInfoT *subdevs;
- AlsaVolRampT *ramps;
- RegistryHandleT *registry;
+ long scount;
+ AlsaSndCtlT *sndcard;
+ AlsaLoopSubdevT **subdevs;
} AlsaSndLoopT;
-typedef struct {
+typedef struct {
const char *uid;
const char *info;
- const char *zone;
+ const char *sink;
+ const char *source;
const char *ramp;
int volume;
int mute;
- AlsaPcmInfoT *pcm;
- AlsaPcmHwInfoT params;
- AlsaPcmCopyHandleT copy;
-} AlsaLoopStreamT;
+ AlsaPcmHwInfoT *params;
+ AlsaPcmCopyHandleT *copy;
+} AlsaStreamAudioT;
typedef struct {
const char *uid;
const char *info;
- AlsaSndLoopT *frontend;
- AlsaPcmInfoT *backend;
- AlsaPcmInfoT *multiPcm;
- AlsaPcmInfoT **routes;
- AlsaLoopStreamT *streams;
-} SoftMixerHandleT;
+ AFB_ApiT api;
+ sd_event *sdLoop;
+
+ struct {
+ unsigned int loops;
+ unsigned int sinks;
+ unsigned int sources;
+ unsigned int zones;
+ unsigned int streams;
+ unsigned int ramps;
+ } max;
+ AlsaSndLoopT **loops;
+ AlsaSndPcmT **sinks;
+ AlsaSndPcmT **sources;
+ AlsaSndZoneT **zones;
+ AlsaStreamAudioT **streams;
+ AlsaVolRampT **ramps;
+} SoftMixerT;
// alsa-utils-bypath.c
-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);
+PUBLIC snd_ctl_card_info_t *AlsaByPathInfo(SoftMixerT *mixer, const char *devpath);
+PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmId, snd_pcm_stream_t direction);
+PUBLIC snd_ctl_t *AlsaByPathOpenCtl(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *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 AlsaDumpElemConfig(CtlSourceT *source, const char* info, const char* elem);
-PUBLIC void AlsaDumpPcmInfo(CtlSourceT *source, const char* info, snd_pcm_t *pcm);
-PUBLIC void AlsaDumpPcmParams(CtlSourceT *source, snd_pcm_hw_params_t *pcmHwParams);
-PUBLIC void AlsaDumpCtlConfig(CtlSourceT *source, const char* info, 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))
+PUBLIC json_object *AlsaDumpObjF(const char *format, ...);
+PUBLIC char *AlsaDumpPcmUid(snd_pcm_t *pcmHandle, char *buffer, size_t len);
+PUBLIC char *AlsaDumpCtlUid(snd_ctl_t *ctlHandle, char *buffer, size_t len);
+PUBLIC void AlsaDumpFormats(SoftMixerT *mixer, snd_pcm_t *pcmHandle);
+PUBLIC void AlsaDumpCtlSubdev(SoftMixerT *mixer, snd_ctl_t *handle);
+PUBLIC void AlsaDumpPcmParams(SoftMixerT *mixer, snd_pcm_hw_params_t *pcmHwParams);
+PUBLIC void AlsaDumpPcmInfo(SoftMixerT *mixer, const char* info, snd_pcm_t *pcm);
+PUBLIC void AlsaDumpElemConfig(SoftMixerT *mixer, const char* info, const char* elem);
+PUBLIC void AlsaDumpCtlConfig(SoftMixerT *mixer, const char* info, snd_config_t *config, int indent);
// alsa-core-ctl.c
-PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(CtlSourceT *source, const char *cardid);
-PUBLIC snd_ctl_t *AlsaCtlOpenCtl(CtlSourceT *source, const char *cardid);
-PUBLIC snd_ctl_t* AlsaCrlFromPcm(CtlSourceT *source, snd_pcm_t *pcm);
-PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev, RegistryHandleT *registry);
-PUBLIC int AlsaCtlRegister(CtlSourceT *source, SoftMixerHandleT *mixer, AlsaPcmInfoT *pcm, RegistryNumidT type, int numid);
-PUBLIC int AlsaCtlNumidGetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value);
-PUBLIC int AlsaCtlNumidSetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long value);
-PUBLIC int AlsaCtlNameGetLong(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName, long* value);
-PUBLIC int AlsaCtlNameSetLong(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName, long value);
-PUBLIC int AlsaCtlCreateControl(CtlSourceT *source, snd_ctl_t* ctlDev, AlsaPcmInfoT *subdevs, char* name, int ctlCount, int ctlMin, int ctlMax, int ctlStep, long value);
-PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName);
-PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long value);
-PUBLIC int CtlElemIdGetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long *value);
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid) ;
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName) ;
+PUBLIC snd_ctl_t *AlsaCtlOpenCtl(SoftMixerT *mixer, const char *cardid) ;
+PUBLIC int CtlElemIdGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long *value) ;
+PUBLIC int CtlElemIdSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long value) ;
+PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(SoftMixerT *mixer, const char *cardid) ;
+PUBLIC int AlsaCtlNumidSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long value) ;
+PUBLIC int AlsaCtlNumidGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long* value) ;
+PUBLIC int AlsaCtlNameSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long value) ;
+PUBLIC int AlsaCtlNameGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long* value) ;
+PUBLIC int AlsaCtlCreateControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, char* ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep, long value) ;
+PUBLIC snd_ctl_t* AlsaCrlFromPcm(SoftMixerT *mixer, snd_pcm_t *pcm) ;
+PUBLIC int AlsaCtlSubscribe(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *sndcard) ;
+PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid);
// alsa-core-pcm.c
-PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts);
-PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaLoopStreamT *loopStream, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT * opts);
+PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, AlsaPcmHwInfoT *opts);
+PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
+
// alsa-plug-*.c _snd_pcm_PLUGIN_open_ see macro ALSA_PLUG_PROTO(plugin)
-PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open);
-PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmName, int open);
-PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int open);
-PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaLoopStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open);
-PUBLIC AlsaPcmInfoT* AlsaCreateRate(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open);
+PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, AlsaPcmHwInfoT *opts);
+PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *streamAudio, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
+PUBLIC AlsaPcmCtlT* AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaSndZoneT *zone, AlsaSndCtlT *sndcard, char* ctlName, int max, int open);
+PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open);
+PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open);
+PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open);
// alsa-api-*
-PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params);
-PUBLIC int SndFrontend (CtlSourceT *source, json_object *argsJ);
-PUBLIC int SndBackend (CtlSourceT *source, json_object *argsJ);
-PUBLIC int SndZones (CtlSourceT *source, json_object *argsJ);
-PUBLIC int LoopStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ);
+PUBLIC AlsaLoopSubdevT *ApiLoopFindSubdev(SoftMixerT *mixer, const char *streamUid, const char *targetUid, AlsaSndLoopT **loop);
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
+PUBLIC AlsaPcmHwInfoT *ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object *paramsJ);
+PUBLIC AlsaSndPcmT *ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object *argsJ);
+PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid);
+PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object *argsJ);
+PUBLIC AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *target);
+PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
+PUBLIC AlsaSndCtlT *ApiSourceFindSubdev(SoftMixerT *mixer, const char *target);
+PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
+PUBLIC json_object *CreateOneStream(SoftMixerT *mixer, AlsaStreamAudioT *stream);
+PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ, json_object **responseJ);
+PUBLIC AlsaSndZoneT *ApiZoneGetByUid(SoftMixerT *mixer, const char *target);
+PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
// alsa-effect-ramp.c
-PUBLIC int AlsaVolRampApply(CtlSourceT *source, AlsaSndLoopT *frontend, AlsaLoopStreamT *stream, AlsaVolRampT *ramp, json_object *volumeJ);
+PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid);
+PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamAudioT *stream, json_object *rampJ);
#endif \ No newline at end of file
diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c
index a857ab1..30e528f 100644
--- a/plugins/alsa/alsa-utils-bypath.c
+++ b/plugins/alsa/alsa-utils-bypath.c
@@ -35,7 +35,7 @@
// 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(SoftMixerT *mixer, const char *devpath) {
int open_dev;
snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof());
@@ -54,104 +54,72 @@ PUBLIC snd_ctl_card_info_t *AlsaByPathInfo(CtlSourceT *source, const char *devpa
return cardInfo;
OnErrorExit:
- AFB_ApiError(source->api, "AlsaCardInfoByPath: fail to find sndcard by path= %s", devpath);
+ AFB_ApiError(mixer->api, "AlsaCardInfoByPath: fail to find sndcard by path= %s", devpath);
return NULL;
}
-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;
- if (dev->devpath) {
- cardInfo = AlsaByPathInfo(source, dev->devpath);
- dev->cardid=NULL;
- }
- else if (dev->cardid) {
- dev->cardid= strdup(dev->cardid);
- cardInfo = AlsaCtlGetInfo(source, dev->cardid);
- }
- else {
- dev->cardid=malloc(ALSA_CARDID_MAX_LEN);
- snprintf((char*)dev->cardid, ALSA_CARDID_MAX_LEN, "hw:%i", dev->cardidx);
- cardInfo = AlsaCtlGetInfo(source, dev->cardid);
- }
-
- if (!cardInfo) {
- AFB_ApiWarning(source->api, "AlsaByPathOpenPcm: fail to find sndcard by path=%s id=%s", dev->devpath, dev->cardid);
- goto OnErrorExit;
- }
-
- // extract useful info from cardInfo handle
- dev->cardidx = snd_ctl_card_info_get_card(cardInfo);
-
- // if not provided build a valid PCM cardid
- if (!dev->cardid) {
- dev->cardid=malloc(ALSA_CARDID_MAX_LEN);
- if (dev->subdev) snprintf((char*)dev->cardid, ALSA_CARDID_MAX_LEN, "hw:%i,%i,%i", dev->cardidx, dev->device, dev->subdev);
- else if (dev->device) snprintf((char*)dev->cardid, ALSA_CARDID_MAX_LEN, "hw:%i,%i", dev->cardidx, dev->device);
- else snprintf((char*)dev->cardid, ALSA_CARDID_MAX_LEN, "hw:%i", dev->cardidx);
+PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmDev, snd_pcm_stream_t direction) {
+ int error;
+ AlsaPcmCtlT *pcmCtl = calloc(1, sizeof (AlsaPcmCtlT));
+
+ if (!pcmDev->cardid) {
+ char *cardid;
+ if (pcmDev->subdev) (void)asprintf(&cardid, "hw:%i,%i,%i", pcmDev->cardidx, pcmDev->device, pcmDev->subdev);
+ else if (pcmDev->device) (void) asprintf(&cardid, "hw:%i,%i", pcmDev->cardidx, pcmDev->device);
+ else (void) asprintf(&cardid, "hw:%i", pcmDev->cardidx);
+ pcmDev->cardid= (const char*)cardid;
}
-
- // make sure UID will cannot be removed
- dev->uid= strdup(dev->uid);
- return 0;
-OnErrorExit:
- return -1;
-}
+ // inherit CID fropm pcmDev
+ pcmCtl->cid.cardid = pcmDev->cardid;
+ pcmCtl->cid.cardidx = pcmDev->cardidx;
+ pcmCtl->cid.device = pcmDev->device;
+ pcmCtl->cid.subdev = pcmDev->subdev;
+ pcmCtl->cid.name=NULL;
+ pcmCtl->cid.longname=NULL;
-PUBLIC AlsaPcmInfoT* AlsaByPathOpenPcm(CtlSourceT *source, AlsaPcmInfoT *dev, snd_pcm_stream_t direction) {
- int error;
-
- // duplicate dev structure to allow caller to free dev
- AlsaPcmInfoT* pcm=malloc(sizeof(AlsaPcmInfoT));
- memcpy (pcm, dev, sizeof(AlsaPcmInfoT));
-
-
- error = AlsaByPathDevid(source, pcm);
- if (error) goto OnErrorExit;
-
- error = snd_pcm_open(&pcm->handle, pcm->cardid, direction, SND_PCM_NONBLOCK);
+ error = snd_pcm_open(&pcmCtl->handle, pcmCtl->cid.cardid, direction, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaByPathOpenPcm: fail openpcm (cardid=%s idxdev=%i subdev=%d): %s"
- , pcm->cardid, pcm->device, pcm->subdev, snd_strerror(error));
+ AFB_ApiError(mixer->api, "AlsaByPathOpenPcm: fail openpcm cardid=%s error=%s", pcmCtl->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
- return (pcm);
+ return pcmCtl;
OnErrorExit:
+ free(pcmCtl);
return NULL;
}
-PUBLIC snd_ctl_t *AlsaByPathOpenCtl(CtlSourceT *source, AlsaPcmInfoT *dev) {
+PUBLIC snd_ctl_t *AlsaByPathOpenCtl(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *dev) {
int err;
- char cardid[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;
- if (dev->devpath) cardInfo = AlsaByPathInfo(source, dev->devpath);
- else if (dev->cardid) cardInfo = AlsaCtlGetInfo(source, dev->cardid);
+ if (dev->cid.devpath) cardInfo = AlsaByPathInfo(mixer, dev->cid.devpath);
+ else if (dev->cid.cardid) cardInfo = AlsaCtlGetInfo(mixer, dev->cid.cardid);
if (!cardInfo) {
- AFB_ApiError(source->api, "AlsaByPathOpenCtl: fail to find sndcard by path=%s id=%s", dev->devpath, dev->cardid);
+ AFB_ApiError(mixer->api, "AlsaByPathOpenCtl: uid=%s fail to find sndcard by path=%s id=%s", uid, dev->cid.devpath, dev->cid.cardid);
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);
- const char *cardName = snd_ctl_card_info_get_name(cardInfo);
+ dev->cid.devpath = NULL;
+ dev->cid.cardidx = snd_ctl_card_info_get_card(cardInfo);
+ dev->cid.name = strdup(snd_ctl_card_info_get_name(cardInfo));
+ dev->cid.longname = strdup(snd_ctl_card_info_get_longname(cardInfo));
// build a valid name and open sndcard
- snprintf(cardid, sizeof (cardid), "hw:%i", cardIndex);
- if ((err = snd_ctl_open(&handle, cardid, 0)) < 0) {
- AFB_ApiError(source->api, "control open (hw:%d -> %s): %s", cardIndex, cardName, snd_strerror(err));
+ (void) asprintf((char**) &dev->cid.cardid, "hw:%i", dev->cid.cardidx);
+
+ if ((err = snd_ctl_open(&handle, dev->cid.cardid, 0)) < 0) {
+ AFB_ApiError(mixer->api, "AlsaByPathOpenCtl uid=%s sndcard open fail cardid=%s longname=%s error=%s", uid, dev->cid.cardid, dev->cid.longname, snd_strerror(err));
goto OnErrorExit;
}
- AFB_ApiNotice(source->api, "AlsaCtlOpenByPath: sndcard hw:%d id=%s name=%s", cardIndex, cardId, cardName);
+ AFB_ApiNotice(mixer->api, "AlsaCtlOpenByPath: uid=%s cardid=%s cardname=%s", uid, dev->cid.cardid, dev->cid.longname);
free(cardInfo);
return handle;
diff --git a/plugins/alsa/alsa-utils-dump.c b/plugins/alsa/alsa-utils-dump.c
index 9ff07d8..3359a39 100644
--- a/plugins/alsa/alsa-utils-dump.c
+++ b/plugins/alsa/alsa-utils-dump.c
@@ -18,8 +18,22 @@
#define _GNU_SOURCE // needed for vasprintf
-
#include "alsa-softmixer.h"
+#include <stdarg.h>
+
+PUBLIC json_object *AlsaDumpObjF(const char *format, ...) {
+ assert (format);
+ va_list args;
+
+ char *result;
+ va_start(args, format);
+ int error= vasprintf(&result, format, args);
+ va_end(args);
+
+ if (error < 0) return NULL;
+
+ return (json_object_new_string(result));
+}
PUBLIC char *AlsaDumpPcmUid(snd_pcm_t *pcmHandle, char *buffer, size_t len) {
snd_pcm_info_t *pcmInfo;
@@ -56,7 +70,7 @@ OnErrorExit:
return NULL;
}
-PUBLIC void AlsaDumpFormats(CtlSourceT *source, snd_pcm_t *pcmHandle) {
+PUBLIC void AlsaDumpFormats(SoftMixerT *mixer, snd_pcm_t *pcmHandle) {
char string[32];
snd_pcm_format_t format;
snd_pcm_hw_params_t *pxmHwParams;
@@ -65,15 +79,15 @@ 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", ALSA_PCM_UID(pcmHandle, string));
+ AFB_ApiNotice(mixer->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));
+ AFB_ApiNotice(mixer->api, "- %s", snd_pcm_format_name(format));
}
}
}
-PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle) {
+PUBLIC void AlsaDumpCtlSubdev(SoftMixerT *mixer, snd_ctl_t *handle) {
snd_ctl_card_info_t *cardInfo;
int err;
int dev = -1;
@@ -91,7 +105,7 @@ PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle) {
while (1) {
if (snd_ctl_pcm_next_device(handle, &dev) < 0) {
- AFB_ApiError(source->api, "AlsaDumpCard: fail to open subdev card id=%s name=%s", cardId, cardName);
+ AFB_ApiError(mixer->api, "AlsaDumpCard: fail to open subdev card id=%s name=%s", cardId, cardName);
goto OnErrorExit;
}
@@ -101,11 +115,11 @@ PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle) {
// ignore empty device slot
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
- AFB_ApiError(source->api, "control digital audio info (%s): %s", cardName, snd_strerror(err));
+ AFB_ApiError(mixer->api, "control digital audio info (%s): %s", cardName, snd_strerror(err));
continue;
}
- AFB_ApiNotice(source->api, "AlsaDumpCard card %d: %s [%s], device %d: %s [%s]",
+ AFB_ApiNotice(mixer->api, "AlsaDumpCard card %d: %s [%s], device %d: %s [%s]",
cardIndex, cardId, cardName, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo));
// loop on subdevices
@@ -115,12 +129,12 @@ PUBLIC void AlsaDumpCtlSubdev(CtlSourceT *source, snd_ctl_t *handle) {
for (unsigned int idx = 0; idx < subdevCount; idx++) {
snd_pcm_info_set_subdevice(pcminfo, idx);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
- AFB_ApiError(source->api, "AlsaDumpCard: control digital audio playback info %i: %s", cardIndex, snd_strerror(err));
+ AFB_ApiError(mixer->api, "AlsaDumpCard: control digital audio playback info %i: %s", cardIndex, snd_strerror(err));
} else {
- AFB_ApiNotice(source->api, "AlsaDumpCard: -- Subdevice #%d: %s", idx, snd_pcm_info_get_subdevice_name(pcminfo));
+ AFB_ApiNotice(mixer->api, "AlsaDumpCard: -- Subdevice #%d: %s", idx, snd_pcm_info_get_subdevice_name(pcminfo));
}
}
- AFB_ApiNotice(source->api, "AlsaDumpCard => subdevice count=%d avaliable=%d", subdevCount, subdevAvail);
+ AFB_ApiNotice(mixer->api, "AlsaDumpCard => subdevice count=%d avaliable=%d", subdevCount, subdevAvail);
}
return;
@@ -128,18 +142,18 @@ OnErrorExit:
return;
}
-PUBLIC void AlsaDumpPcmParams(CtlSourceT *source, snd_pcm_hw_params_t *pcmHwParams) {
+PUBLIC void AlsaDumpPcmParams(SoftMixerT *mixer, snd_pcm_hw_params_t *pcmHwParams) {
snd_output_t *output;
char *buffer;
snd_output_buffer_open(&output);
snd_pcm_hw_params_dump(pcmHwParams, output);
snd_output_buffer_string(output, &buffer);
- AFB_ApiNotice(source->api, "AlsaPCMDump: %s", buffer);
+ AFB_ApiNotice(mixer->api, "AlsaPCMDump: %s", buffer);
snd_output_close(output);
}
-PUBLIC void AlsaDumpPcmInfo(CtlSourceT *source, const char* info, snd_pcm_t *pcm) {
+PUBLIC void AlsaDumpPcmInfo(SoftMixerT *mixer, const char* info, snd_pcm_t *pcm) {
snd_output_t *out;
char *buffer;
@@ -150,18 +164,18 @@ PUBLIC void AlsaDumpPcmInfo(CtlSourceT *source, const char* info, snd_pcm_t *pcm
snd_pcm_dump(pcm, out);
snd_output_buffer_string(out, &buffer);
- AFB_ApiNotice(source->api, "AlsaPCMDump: %s", buffer);
+ AFB_ApiNotice(mixer->api, "AlsaPCMDump: %s", buffer);
snd_output_close(out);
}
-PUBLIC void AlsaDumpElemConfig(CtlSourceT *source, const char* info, const char* elem) {
- snd_config_update();
- snd_config_t *pcmConfig;
- snd_config_search(snd_config, elem, &pcmConfig);
- AlsaDumpCtlConfig(source, info, pcmConfig,1);
+PUBLIC void AlsaDumpElemConfig(SoftMixerT *mixer, const char* info, const char* elem) {
+ snd_config_update();
+ snd_config_t *pcmConfig;
+ snd_config_search(snd_config, elem, &pcmConfig);
+ AlsaDumpCtlConfig(mixer, info, pcmConfig, 1);
}
-PUBLIC void AlsaDumpCtlConfig(CtlSourceT *source, const char* info, snd_config_t *config, int indent) {
+PUBLIC void AlsaDumpCtlConfig(SoftMixerT *mixer, const char* info, snd_config_t *config, int indent) {
snd_config_iterator_t it, next;
// hugly hack to get minimalist indentation
@@ -184,28 +198,28 @@ PUBLIC void AlsaDumpCtlConfig(CtlSourceT *source, const char* info, snd_config_t
case SND_CONFIG_TYPE_INTEGER:
snd_config_get_integer(node, &valueI);
- AFB_ApiNotice(source->api, "%s: %s %s: %d (int)", info, pretty, key, (int) valueI);
+ AFB_ApiNotice(mixer->api, "%s: %s %s: %d (int)", info, pretty, key, (int) valueI);
break;
case SND_CONFIG_TYPE_REAL:
snd_config_get_real(node, &valueD);
- AFB_ApiNotice(source->api, "%s: %s %s: %.2f (float)", info, pretty, key, valueD);
+ AFB_ApiNotice(mixer->api, "%s: %s %s: %.2f (float)", info, pretty, key, valueD);
break;
case SND_CONFIG_TYPE_STRING:
snd_config_get_string(node, &valueS);
- AFB_ApiNotice(source->api, "%s: %s %s: %s (str)", info, pretty, key, valueS);
+ AFB_ApiNotice(mixer->api, "%s: %s %s: %s (str)", info, pretty, key, valueS);
break;
case SND_CONFIG_TYPE_COMPOUND:
- AFB_ApiNotice(source->api, "%s: %s %s { ", info, pretty, key);
- AlsaDumpCtlConfig(source, info, node, indent + 2);
- AFB_ApiNotice(source->api, "%s: %s } ", info, pretty);
+ AFB_ApiNotice(mixer->api, "%s: %s %s { ", info, pretty, key);
+ AlsaDumpCtlConfig(mixer, info, node, indent + 2);
+ AFB_ApiNotice(mixer->api, "%s: %s } ", info, pretty);
break;
default:
snd_config_get_string(node, &valueS);
- AFB_ApiNotice(source->api, "%s: %s: key=%s unknown=%s", info, pretty, key, valueS);
+ AFB_ApiNotice(mixer->api, "%s: %s: key=%s unknown=%s", info, pretty, key, valueS);
break;
}
}