summaryrefslogtreecommitdiffstats
path: root/plugins/alsa
diff options
context:
space:
mode:
authorFulup Ar Foll <fulup@iot.bzh>2018-05-13 22:07:22 +0200
committerFulup Ar Foll <fulup@iot.bzh>2018-05-13 22:07:22 +0200
commit6f13e28ba698a2b0145acbb926b79cd569a31f44 (patch)
tree061e2daace484aea73200ab85db39b3fafeb95e4 /plugins/alsa
parent0eb15da6365910ba3f290e3254719fd412ae0155 (diff)
First testable version.
Mixing with volume and mute per audio role works.
Diffstat (limited to 'plugins/alsa')
-rw-r--r--plugins/alsa/alsa-api-sndcards.c118
-rw-r--r--plugins/alsa/alsa-api-sndloops.c80
-rw-r--r--plugins/alsa/alsa-api-sndstreams.c166
-rw-r--r--plugins/alsa/alsa-api-sndzones.c3
-rw-r--r--plugins/alsa/alsa-core-ctl.c336
-rw-r--r--plugins/alsa/alsa-core-pcm.c29
-rw-r--r--plugins/alsa/alsa-plug-dmix.c4
-rw-r--r--plugins/alsa/alsa-plug-multi.c4
-rw-r--r--plugins/alsa/alsa-plug-rate.c81
-rw-r--r--plugins/alsa/alsa-plug-route.c4
-rw-r--r--plugins/alsa/alsa-plug-stream.c19
-rw-r--r--plugins/alsa/alsa-softmixer.c82
-rw-r--r--plugins/alsa/alsa-softmixer.h27
-rw-r--r--plugins/alsa/alsa-utils-bypath.c1
14 files changed, 637 insertions, 317 deletions
diff --git a/plugins/alsa/alsa-api-sndcards.c b/plugins/alsa/alsa-api-sndcards.c
index e98a294..daa996a 100644
--- a/plugins/alsa/alsa-api-sndcards.c
+++ b/plugins/alsa/alsa-api-sndcards.c
@@ -25,77 +25,121 @@ extern Lua2cWrapperT Lua2cWrap;
STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannels *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);
+ 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));
+ AFB_ApiError(source->api, "ProcessOneChannel: sndcard=%s channel: missing (uid||port) json=%s", uid, json_object_get_string(channelJ));
return -1;
}
-STATIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params) {
-
- int error = wrap_json_unpack(paramsJ, "{s?i,s?i !}", "rate", &params->rate, "channels", &params->channels);
+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:
- AFB_ApiError(source->api, "ProcessSndParams: sndcard=%s params: missing (rate|channel) params=%s", uid, json_object_get_string(paramsJ));
return -1;
}
STATIC int ProcessOneSndCard(CtlSourceT *source, json_object *sndcardJ, AlsaPcmInfoT *snd) {
- json_object *sinkJ, *paramsJ=NULL;
+ 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);
+ 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;
+
+ 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);
+ 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;
+ goto OnErrorExit;
} else {
- snd_pcm_close(dmixPcm->handle);
- snd->cardid=dmixPcm->cardid;
+ snd->cardid = dmixPcm->cardid;
}
switch (json_object_get_type(sinkJ)) {
case json_type_object:
- snd->ccount=1;
- snd->channels = calloc(snd->ccount+1, sizeof (AlsaPcmChannels));
+ snd->ccount = 1;
+ snd->channels = calloc(snd->ccount + 1, sizeof (AlsaPcmChannels));
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 (AlsaPcmChannels));
+ snd->ccount = (int) json_object_array_length(sinkJ);
+ snd->channels = calloc(snd->ccount + 1, sizeof (AlsaPcmChannels));
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]);
@@ -115,13 +159,13 @@ OnErrorExit:
CTLP_LUA2C(snd_cards, source, argsJ, responseJ) {
AlsaPcmInfoT *sndcards;
-
+
int error;
size_t count;
switch (json_object_get_type(argsJ)) {
case json_type_object:
- count= 1;
+ count = 1;
sndcards = calloc(count + 1, sizeof (AlsaPcmInfoT));
error = ProcessOneSndCard(source, argsJ, &sndcards[0]);
if (error) goto OnErrorExit;
@@ -141,23 +185,21 @@ CTLP_LUA2C(snd_cards, source, argsJ, responseJ) {
}
// register Sound card and multi when needed
- Softmixer->sndcardCtl= sndcards;
-
+ Softmixer->sndcardCtl = sndcards;
+
if (count == 1) {
-
+
// only one sound card we multi would be useless
Softmixer->multiPcm = &sndcards[0];
-
+
} else {
AlsaPcmInfoT *pcmMulti;
-
+
// instantiate an alsa multi plugin
- pcmMulti = AlsaCreateMulti(source, "PcmMulti");
+ pcmMulti = AlsaCreateMulti(source, "PcmMulti", 0);
if (!pcmMulti) goto OnErrorExit;
- // Close Multi and save into globak handle for further use
- snd_pcm_close(pcmMulti->handle);
- Softmixer->multiPcm= pcmMulti;
+ Softmixer->multiPcm = pcmMulti;
}
return 0;
diff --git a/plugins/alsa/alsa-api-sndloops.c b/plugins/alsa/alsa-api-sndloops.c
index 5599ae0..be12c11 100644
--- a/plugins/alsa/alsa-api-sndloops.c
+++ b/plugins/alsa/alsa-api-sndloops.c
@@ -24,23 +24,34 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-STATIC int ProcessOneSubdev(CtlSourceT *source, AlsaSndLoopT *loop, json_object *subdevJ, AlsaPcmInfoT *subdev) {
-
- int error = wrap_json_unpack(subdevJ, "{si,si !}", "subdev", &subdev->subdev, "numid", &subdev->numid);
+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);
+ 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->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->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) {
@@ -55,27 +66,43 @@ OnErrorExit:
}
STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *loop) {
- json_object *subdevsJ=NULL, *devicesJ=NULL;
+ json_object *subdevsJ = NULL, *devicesJ = NULL, *paramsJ = NULL;
int error;
- error = wrap_json_unpack(loopJ, "{ss,s?s,s?s,s?i,s?o,so}", "uid",&loop->uid, "devpath",&loop->devpath, "cardid",&loop->cardid
- , "cardidx",&loop->cardidx, "devices",&devicesJ, "subdevs",&subdevsJ);
+ error = wrap_json_unpack(loopJ, "{ss,s?s,s?s,s?i,s?o,so,s?o !}", "uid", &loop->uid, "devpath", &loop->devpath, "cardid", &loop->cardid
+ , "cardidx", &loop->cardidx, "devices", &devicesJ, "subdevs", &subdevsJ, "params", &paramsJ);
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;
+ }
+
// make sure useful information will not be removed
- loop->uid=strdup(loop->uid);
- if (loop->cardid) loop->cardid=strdup(loop->cardid);
- if (loop->devpath) loop->cardid=strdup(loop->devpath);
-
+ loop->uid = strdup(loop->uid);
+ if (loop->cardid) loop->cardid = strdup(loop->cardid);
+ if (loop->devpath) loop->cardid = strdup(loop->devpath);
+
// Default devices is payback=0 capture=1
if (!devicesJ) {
- loop->playback=0;
- loop->capture=1;
+ loop->playback = 0;
+ loop->capture = 1;
} else {
- error = wrap_json_unpack(devicesJ, "{si,si}", "capture",&loop->capture, "playback", &loop->playback);
+ 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;
@@ -85,24 +112,24 @@ STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *
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, &loop->subdevs[0]);
+ 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));
+ 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, &loop->subdevs[idx]);
+ error = ProcessOneSubdev(source, loop, subdevJ, loopDefParams, &loop->subdevs[idx]);
if (error) goto OnErrorExit;
}
break;
default:
- AFB_ApiError(source->api, "L2C:ProcessOneLoop=%s invalid subdevs= %s", loop->uid, json_object_get_string(subdevsJ));
+ AFB_ApiError(source->api, "L2C:ProcessOneLoop=%s invalid subdevs= %s", loop->uid, json_object_get_string(subdevsJ));
goto OnErrorExit;
}
-
+
return 0;
OnErrorExit:
@@ -111,7 +138,7 @@ OnErrorExit:
CTLP_LUA2C(snd_loops, source, argsJ, responseJ) {
int error;
- AlsaSndLoopT *sndLoop = calloc (1, sizeof(AlsaSndLoopT));
+ AlsaSndLoopT *sndLoop = calloc(1, sizeof (AlsaSndLoopT));
if (json_object_get_type(argsJ) != json_type_object) {
AFB_ApiError(source->api, "L2C:sndloops: invalid object type= %s", json_object_get_string(argsJ));
@@ -127,7 +154,6 @@ CTLP_LUA2C(snd_loops, source, argsJ, responseJ) {
// register routed into global softmixer handle
Softmixer->loopCtl = sndLoop;
-
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-api-sndstreams.c b/plugins/alsa/alsa-api-sndstreams.c
index 1ee0029..d02d47e 100644
--- a/plugins/alsa/alsa-api-sndstreams.c
+++ b/plugins/alsa/alsa-api-sndstreams.c
@@ -26,39 +26,32 @@ extern Lua2cWrapperT Lua2cWrap;
STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaSndStreamT *stream) {
int error;
- const char*format=NULL;
+ json_object *paramsJ = NULL;
- error = wrap_json_unpack(streamJ, "{ss,ss,s?i,s?b,s?i,s?i,s?i !}", "uid", &stream->uid, "zone", &stream->zone, "volume", &stream->volume, "mute", stream->mute
- , "rate", &stream->params.rate, "format", &format, "access", &stream->params.access);
+ // Make sure default runs
+ stream->volume = ALSA_DEFAULT_PCM_VOLUME;
+ stream->mute = 0;
+
+ error = wrap_json_unpack(streamJ, "{ss,ss,s?i,s?b,s?o !}", "uid", &stream->uid, "zone", &stream->zone, "volume", &stream->volume
+ , "mute", stream->mute, "params", &paramsJ);
+ if (error) {
+ AFB_ApiNotice(source->api, "ProcessOneStream missing 'uid|zone|volume|rate|mute|params' stream=%s", json_object_get_string(streamJ));
+ goto OnErrorExit;
+ }
+
+ if (paramsJ) error = ProcessSndParams(source, stream->uid, paramsJ, &stream->params);
if (error) {
- AFB_ApiNotice(source->api, "ProcessOneStream missing 'uid|zone|volume|rate|mute' stream=%s", json_object_get_string(streamJ));
+ AFB_ApiError(source->api, "ProcessOneSndCard: sndcard=%s invalid params=%s", 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;
}
- if (!format) stream->params.format=SND_PCM_FORMAT_UNKNOWN;
- else if (!strcasecmp (format, "S16_LE")) stream->params.format=SND_PCM_FORMAT_S16_LE;
- else if (!strcasecmp (format, "S16_BE")) stream->params.format=SND_PCM_FORMAT_S16_BE;
- else if (!strcasecmp (format, "U16_LE")) stream->params.format=SND_PCM_FORMAT_U16_LE;
- else if (!strcasecmp (format, "U16_BE")) stream->params.format=SND_PCM_FORMAT_U16_BE;
- else if (!strcasecmp (format, "S32_LE")) stream->params.format=SND_PCM_FORMAT_S32_LE;
- else if (!strcasecmp (format, "S32_BE")) stream->params.format=SND_PCM_FORMAT_S32_BE;
- else if (!strcasecmp (format, "U32_LE")) stream->params.format=SND_PCM_FORMAT_U32_LE;
- else if (!strcasecmp (format, "U32_BE")) stream->params.format=SND_PCM_FORMAT_U32_BE;
- else if (!strcasecmp (format, "S24_LE")) stream->params.format=SND_PCM_FORMAT_S24_LE;
- else if (!strcasecmp (format, "S24_BE")) stream->params.format=SND_PCM_FORMAT_S24_BE;
- else if (!strcasecmp (format, "U24_LE")) stream->params.format=SND_PCM_FORMAT_U24_LE;
- else if (!strcasecmp (format, "U24_BE")) stream->params.format=SND_PCM_FORMAT_U24_BE;
- else if (!strcasecmp (format, "S8")) stream->params.format=SND_PCM_FORMAT_S8;
- else if (!strcasecmp (format, "U8")) stream->params.format=SND_PCM_FORMAT_U8;
- else if (!strcasecmp (format, "FLOAT_LE")) stream->params.format=SND_PCM_FORMAT_FLOAT_LE;
- else if (!strcasecmp (format, "FLOAT_BE")) stream->params.format=SND_PCM_FORMAT_FLOAT_LE;
- else {
- AFB_ApiNotice(source->api, "ProcessOneStream unsupported format 'uid|zone|volume|rate|mute' stream=%s", json_object_get_string(streamJ));
- goto OnErrorExit;
- }
-
- if(!stream->params.rate) stream->params.rate=ALSA_DEFAULT_PCM_RATE;
-
// make sure remain valid even when json object is removed
stream->uid = strdup(stream->uid);
stream->zone = strdup(stream->zone);
@@ -72,6 +65,7 @@ OnErrorExit:
CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
AlsaSndStreamT *sndStream;
int error;
+ long value;
size_t count;
// assert static/global softmixer handle get requited info
@@ -111,13 +105,13 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// return stream data to application as a json array
- *responseJ = json_object_new_array();
-
+ *responseJ = json_object_new_array();
+
for (int idx = 0; sndStream[idx].uid != NULL; idx++) {
- json_object *streamJ;
-
+ json_object *streamJ, *paramsJ;
+
// Search for a free loop capture device
- AFB_ApiNotice(source->api, "L2C:sndstreams stream=%s Start", (char*)sndStream[idx].uid);
+ AFB_ApiNotice(source->api, "L2C:sndstreams stream=%s Start", (char*) sndStream[idx].uid);
ctlLoop->scount--;
if (ctlLoop->scount < 0) {
AFB_ApiError(source->api, "L2C:sndstreams no more subdev avaliable in loopback=%s", ctlLoop->uid);
@@ -126,43 +120,107 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// 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);
+ playbackDev->device = ctlLoop->capture;
+ AlsaPcmInfoT *captureDev = AlsaByPathOpenPcm(source, playbackDev, SND_PCM_STREAM_CAPTURE);
if (!captureDev) goto OnErrorExit;
-
- AlsaPcmInfoT *streamPcm = AlsaCreateStream(source, &sndStream[idx], captureDev);
+
+ // configure with default loopback subdev params
+ error = AlsaPcmConf(source, captureDev, &playbackDev->params);
+ if (error) goto OnErrorExit;
+
+ // Register capture PCM for active/pause event
+ if (captureDev->numid) {
+ error = AlsaCtlRegister(source, captureDev, 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, "AlsaCtlRegister [pcm=%s] fail attache sndcard", captureDev->cardid);
+ goto OnErrorExit;
+ }
+
+ // create mute control and register it as pause/resume ctl)
+ char runName[ALSA_CARDID_MAX_LEN];
+ snprintf(runName, sizeof (runName), "run-%s", sndStream[idx].uid);
+
+ // create a single boolean value control for pause/resume
+ int runNumid = AlsaCtlCreateControl(source, ctlDev, playbackDev, runName, 1, 0, 1, 1, !sndStream[idx].mute);
+ if (runNumid <= 0) goto OnErrorExit;
+
+ // register mute/unmute as a pause/resume control
+ error = AlsaCtlRegister(source, captureDev, runNumid);
+ 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", sndStream[idx].uid);
+ AlsaPcmInfoT *streamPcm = AlsaCreateStream(source, &sndStream[idx], captureDev, volName, 0);
if (!streamPcm) {
- AFB_ApiError(source->api, "L2C:sndstreams fail to create stream=%s", (char*) sndStream[idx].uid);
+ AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to create stream", sndStream[idx].uid);
+ goto OnErrorExit;
+ }
+
+ // create volume control before softvol pcm is opened
+ int volNumid = AlsaCtlCreateControl(source, ctlDev, playbackDev, volName, streamPcm->params.channels, 0, 100, 1, sndStream[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", sndStream[idx].uid);
+ // AlsaPcmInfoT *ratePcm= AlsaCreateRate(source, rateName, streamPcm, 1);
+ // if (!ratePcm) {
+ // AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to create rate converter", sndStream[idx].uid);
+ // goto OnErrorExit;
+ // }
+
+ // everything is not ready to open capture pcm
+ error = snd_pcm_open(&streamPcm->handle, sndStream[idx].uid, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (error) {
+ AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to open capture", sndStream[idx].uid);
goto OnErrorExit;
}
-
+
// capture stream inherit channel from targeted zone
captureDev->ccount = streamPcm->ccount;
- sndStream[idx].params.channels=streamPcm->ccount;
-
- // start stream pcm copy
- error = AlsaPcmCopy(source, captureDev, streamPcm, &sndStream[idx].params);
+ streamPcm->params.channels = streamPcm->ccount;
+
+ // start stream pcm copy (at this both capture & sink pcm should be open)
+ error = AlsaPcmCopy(source, captureDev, streamPcm, &streamPcm->params);
if (error) goto OnErrorExit;
- // Registration to event should be done after pcm_start
- if (captureDev->numid) {
- error = AlsaCtlRegister(source, captureDev, captureDev->numid);
- 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_ApiError(source->api, "L2C:sndstreams [capture=%s] fail to pause error=%s", captureDev->cardid, snd_strerror(error));
+ goto OnErrorExit;
}
-
+
// prepare response for application
- playbackDev->device= ctlLoop->playback;
+ playbackDev->device = ctlLoop->playback;
error = AlsaByPathDevid(source, playbackDev);
- wrap_json_pack(&streamJ, "{ss ss si}", "uid", sndStream[idx].uid, "alsa", playbackDev->cardid, "numid", captureDev->numid);
- json_object_array_add(*responseJ,streamJ);
-
+
+ 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", runNumid, "params", paramsJ);
+ error += json_object_array_add(*responseJ, streamJ);
+ if (error) {
+ AFB_ApiError(source->api, "L2C:sndstreams:%s(stream) fail to prepare response", captureDev->cardid);
+ goto OnErrorExit;
+ }
+
+ snd_ctl_close(ctlDev);
// Debug Alsa Config
//AlsaDumpElemConfig (source, "\n\nAlsa_Config\n------------\n", "pcm");
//AlsaDumpPcmInfo(source, "\n\nPcm_config\n-----------\n", streamPcm->handle);
- AFB_ApiNotice(source->api, "L2C:sndstreams stream=%s OK\n", (char*) sndStream[idx].uid);
+ AFB_ApiNotice(source->api, "L2C:sndstreams:%s(stream) OK reponse=%s\n", streamPcm->uid, json_object_get_string(streamJ));
}
return 0;
diff --git a/plugins/alsa/alsa-api-sndzones.c b/plugins/alsa/alsa-api-sndzones.c
index 11d77c2..7ca61ad 100644
--- a/plugins/alsa/alsa-api-sndzones.c
+++ b/plugins/alsa/alsa-api-sndzones.c
@@ -128,12 +128,11 @@ CTLP_LUA2C(snd_zones, source, argsJ, responseJ) {
// instantiate one route PCM per zone with multi plugin as slave
for (int idx = 0; sndZone[idx].uid != NULL; idx++) {
- Softmixer->zonePcms[idx] = AlsaCreateRoute(source, &sndZone[idx]);
+ Softmixer->zonePcms[idx] = AlsaCreateRoute(source, &sndZone[idx], 0);
if (!Softmixer->zonePcms[idx]) {
AFB_ApiNotice(source->api, "L2C:sndzones fail to create route zone=%s", sndZone[idx].uid);
goto OnErrorExit;
}
- snd_pcm_close(Softmixer->zonePcms[idx]->handle);
}
// do not need this handle anymore
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index 38ce8cc..79bf0c8 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -46,11 +46,13 @@ typedef struct {
typedef struct {
SubStreamT stream[MAX_AUDIO_STREAMS + 1];
int count;
+ snd_ctl_t *ctlDev;
} AudioStreamHandleT;
static AudioStreamHandleT AudioStreamHandle;
-STATIC snd_ctl_elem_id_t *AlsaCtlGetElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) {
+
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) {
char string[32];
int error;
int index;
@@ -60,18 +62,18 @@ STATIC snd_ctl_elem_id_t *AlsaCtlGetElemId(CtlSourceT *source, snd_ctl_t* ctlDev
snd_ctl_elem_list_alloca(&ctlList);
if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
goto OnErrorExit;
}
if ((error = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) {
- AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve count", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail retrieve count", ALSA_CTL_UID(ctlDev, string));
goto OnErrorExit;
}
// Fulup: do not understand why snd_ctl_elem_list should be call twice to get a valid ctlCount
if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlElemIdGetInt [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
goto OnErrorExit;
}
@@ -87,7 +89,7 @@ STATIC snd_ctl_elem_id_t *AlsaCtlGetElemId(CtlSourceT *source, snd_ctl_t* ctlDev
}
if (index == ctlCount) {
- AFB_ApiError(source->api, "AlsaCtlRegister [%s] fail get numid=%i count", ALSA_CTL_UID(ctlDev, string), numid);
+ AFB_ApiError(source->api, "AlsaCtlGetNumidElemId [%s] fail get numid=%i count", ALSA_CTL_UID(ctlDev, string), numid);
goto OnErrorExit;
}
@@ -100,6 +102,55 @@ OnErrorExit:
return NULL;
}
+PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(CtlSourceT *source, snd_ctl_t* ctlDev, const char *ctlName) {
+ char string[32];
+ int error;
+ int index;
+ snd_ctl_elem_list_t *ctlList = NULL;
+ snd_ctl_elem_id_t *elemId;
+
+ snd_ctl_elem_list_alloca(&ctlList);
+
+ if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
+ AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ goto OnErrorExit;
+ }
+
+ if ((error = snd_ctl_elem_list_alloc_space(ctlList, snd_ctl_elem_list_get_count(ctlList))) < 0) {
+ AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail retrieve count", ALSA_CTL_UID(ctlDev, string));
+ goto OnErrorExit;
+ }
+
+ // Fulup: do not understand why snd_ctl_elem_list should be call twice to get a valid ctlCount
+ if ((error = snd_ctl_elem_list(ctlDev, ctlList)) < 0) {
+ AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail retrieve controls", ALSA_CTL_UID(ctlDev, string));
+ goto OnErrorExit;
+ }
+
+ // loop on control to find the right one
+ int ctlCount = snd_ctl_elem_list_get_used(ctlList);
+ for (index = 0; index < ctlCount; index++) {
+
+ if (!strcasecmp(ctlName, snd_ctl_elem_list_get_name(ctlList, index))) {
+ snd_ctl_elem_id_malloc(&elemId);
+ snd_ctl_elem_list_get_id(ctlList, index, elemId);
+ break;
+ }
+ }
+
+ if (index == ctlCount) {
+ AFB_ApiError(source->api, "AlsaCtlGetNameElemId [%s] fail get ctl name=%s", ALSA_CTL_UID(ctlDev, string), ctlName);
+ goto OnErrorExit;
+ }
+
+ // clear ctl list and return elemid
+ snd_ctl_elem_list_clear(ctlList);
+ return elemId;
+
+OnErrorExit:
+ if (ctlList) snd_ctl_elem_list_clear(ctlList);
+ return NULL;
+}
PUBLIC snd_ctl_t *AlsaCtlOpenCtl(CtlSourceT *source, const char *cardid) {
int error;
@@ -135,7 +186,50 @@ OnErrorExit:
return -1;
}
-STATIC int CtlElemIdGetInt(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long *value) {
+STATIC void CtlElemIdDisplay(AFB_ApiT api, 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);
+ const char* name = snd_ctl_elem_info_get_name(elemInfo);
+ snd_ctl_elem_type_t elemType = snd_ctl_elem_info_get_type(elemInfo);
+
+
+ if (!elemData) {
+ AFB_ApiWarning(api, "CtlElemIdDisplay: numid=%d name=%s value=unreadable", numid, name);
+ } else
+ for (int idx = 0; idx < count; idx++) {
+ long valueL;
+
+ switch (elemType) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ valueL = snd_ctl_elem_value_get_boolean(elemData, idx);
+ AFB_ApiWarning(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);
+ 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);
+ 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);
+ 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);
+ break;
+ case SND_CTL_ELEM_TYPE_IEC958:
+ default:
+ AFB_ApiWarning(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) {
int error;
snd_ctl_elem_value_t *elemData;
snd_ctl_elem_info_t *elemInfo;
@@ -146,58 +240,68 @@ STATIC int CtlElemIdGetInt(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *e
if (!snd_ctl_elem_info_is_readable(elemInfo)) goto OnErrorExit;
// as we have static rate/channel we should have only one boolean as value
- snd_ctl_elem_type_t elemType = snd_ctl_elem_info_get_type(elemInfo);
- int count = snd_ctl_elem_info_get_count(elemInfo);
- if (count != 1) goto OnErrorExit;
snd_ctl_elem_value_alloca(&elemData);
snd_ctl_elem_value_set_id(elemData, elemId);
error = snd_ctl_elem_read(ctlDev, elemData);
- if (error) goto OnSuccessExit;
+ 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 = snd_ctl_elem_value_get_integer(elemData, 0);
+ *value = (int) snd_ctl_elem_value_get_integer(elemData, 0);
-OnSuccessExit:
return 0;
OnErrorExit:
+ CtlElemIdDisplay(api, elemInfo, elemData);
+ return -1;
+}
- AFB_ApiWarning(api, "CtlSubscribeEventCB: ignored unsupported event Numid=%i", snd_ctl_elem_info_get_numid(elemInfo));
- for (int idx = 0; idx < count; idx++) {
- long valueL;
-
- switch (elemType) {
- case SND_CTL_ELEM_TYPE_BOOLEAN:
- valueL = snd_ctl_elem_value_get_boolean(elemData, idx);
- AFB_ApiNotice(api, "CtlElemIdGetBool: value=%ld", valueL);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER:
- valueL = snd_ctl_elem_value_get_integer(elemData, idx);
- AFB_ApiNotice(api, "CtlElemIdGetInt: value=%ld", valueL);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER64:
- valueL = snd_ctl_elem_value_get_integer64(elemData, idx);
- AFB_ApiNotice(api, "CtlElemIdGetInt64: value=%ld", valueL);
- break;
- case SND_CTL_ELEM_TYPE_ENUMERATED:
- valueL = snd_ctl_elem_value_get_enumerated(elemData, idx);
- AFB_ApiNotice(api, "CtlElemIdGetEnum: value=%ld", valueL);
- break;
- case SND_CTL_ELEM_TYPE_BYTES:
- valueL = snd_ctl_elem_value_get_byte(elemData, idx);
- AFB_ApiNotice(api, "CtlElemIdGetByte: value=%ld", valueL);
- break;
- case SND_CTL_ELEM_TYPE_IEC958:
- default:
- AFB_ApiNotice(api, "CtlElemIdGetInt: Unsupported type=%d", elemType);
- break;
- }
+PUBLIC int CtlElemIdSetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, long value) {
+ snd_ctl_elem_value_t *elemData;
+ snd_ctl_elem_info_t *elemInfo;
+ const char* name;
+ int error, numid;
+
+ 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_writable(elemInfo)) goto OnErrorExit;
+
+ int count = snd_ctl_elem_info_get_count(elemInfo);
+ if (count == 0) goto OnErrorExit;
+
+ snd_ctl_elem_value_alloca(&elemData);
+ snd_ctl_elem_value_set_id(elemData, elemId);
+ error = snd_ctl_elem_read(ctlDev, elemData);
+ if (error) goto OnErrorExit;
+
+
+ for (int index = 0; index < count; index++) {
+ snd_ctl_elem_value_set_integer(elemData, index, value);
}
+
+ error = snd_ctl_elem_write(ctlDev, elemData);
+ if (error) goto OnErrorExit;
+
+ return 0;
+
+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);
return -1;
}
// Clone of AlsaLib snd_card_load2 static function
+
PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(CtlSourceT *source, const char *cardid) {
int error;
snd_ctl_t *ctlDev;
@@ -220,15 +324,34 @@ OnErrorExit:
return NULL;
}
-PUBLIC int AlsaCtlGetNumidValueI(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value) {
-
- snd_ctl_elem_id_t *elemId = AlsaCtlGetElemId(source, ctlDev, numid);
+PUBLIC int AlsaCtlNumidSetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long value) {
+
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
+ if (!elemId) {
+ AFB_ApiError(source->api, "AlsaCtlNumidSetLong [sndcard=%s] fail to find numid=%d", snd_ctl_name(ctlDev), numid);
+ goto OnErrorExit;
+ }
+
+ int error = CtlElemIdSetLong(source->api, ctlDev, elemId, value);
+ if (error) {
+ AFB_ApiError(source->api, "AlsaCtlNumidSetLong [sndcard=%s] fail to set numid=%d value=%ld", snd_ctl_name(ctlDev), numid, value);
+ goto OnErrorExit;
+ }
+
+ return 0;
+OnErrorExit:
+ return -1;
+}
+
+PUBLIC int AlsaCtlNumidGetLong(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value) {
+
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
if (!elemId) {
AFB_ApiError(source->api, "AlsaCtlGetNumValueI [sndcard=%s] fail to find numid=%d", snd_ctl_name(ctlDev), numid);
goto OnErrorExit;
}
- int error = CtlElemIdGetInt(source->api, ctlDev, elemId, value);
+ int error = CtlElemIdGetLong(source->api, ctlDev, elemId, value);
if (error) {
AFB_ApiError(source->api, "AlsaCtlGetNumValueI [sndcard=%s] fail to get numid=%d value", snd_ctl_name(ctlDev), numid);
goto OnErrorExit;
@@ -239,6 +362,79 @@ 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) {
+ snd_ctl_elem_type_t ctlType;
+ snd_ctl_elem_info_t *elemInfo;
+ int error;
+
+ 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);
+
+ // softvol plugin is bugged and can only 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, 0);
+ snd_ctl_elem_info_set_subdevice(elemInfo, 0);
+
+ // only two types implemented
+ if (ctlMin == 0 && ctlMax == 1) ctlType = SND_CTL_ELEM_TYPE_BOOLEAN;
+ else ctlType = SND_CTL_ELEM_TYPE_INTEGER;
+
+ switch (ctlType) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ error = snd_ctl_add_boolean_elem_set(ctlDev, 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);
+ if (error) goto OnErrorExit;
+ break;
+
+ default:
+ AFB_ApiError(source->api, "AlsaCtlMakeControl:%s(subdev) fail to create %s(control)", subdev->uid, ctlName);
+ goto OnErrorExit;
+ }
+
+ // retrieve newly created control numid
+ int numid = snd_ctl_elem_info_get_numid(elemInfo);
+ return numid;
+
+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) {
+ int numid = -1;
+
+ // if control does not exist then create
+ snd_ctl_elem_id_t *elemId = AlsaCtlGetNameElemId(source, ctlDev, 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);
+ if (numid <= 0) {
+ AFB_ApiError(source->api, "AlsaCtlCreateControl [sndcard=%s] fail to create ctlName=%s", snd_ctl_name(ctlDev), ctlName);
+ goto OnErrorExit;
+ }
+
+ elemId = AlsaCtlGetNumidElemId(source, ctlDev, numid);
+ }
+
+ int error = CtlElemIdSetLong(source->api, ctlDev, elemId, value);
+ if (error) {
+ AFB_ApiError(source->api, "AlsaCtlCreateControl [sndcard=%s] fail to set ctlName=%s Numid=%d", snd_ctl_name(ctlDev), ctlName, numid);
+ goto OnErrorExit;
+ }
+
+ 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;
@@ -270,7 +466,7 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
// extract element from event and get value
snd_ctl_event_elem_get_id(eventId, elemId);
- error = CtlElemIdGetInt(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value);
+ error = CtlElemIdGetLong(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &value);
if (error) goto OnErrorExit;
error = CtlElemIdGetNumid(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &numid);
@@ -286,15 +482,15 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
}
if (idx == AudioStreamHandle.count) {
char cardName[32];
- ALSA_CTL_UID(subscribeHandle->ctlDev,cardName);
- AFB_ApiWarning(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d card=%s numid=%d (ignored)", subscribeHandle->info, subscribeHandle->tid, cardName, numid);
+ ALSA_CTL_UID(subscribeHandle->ctlDev, cardName);
+ AFB_ApiWarning(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d card=%s numid=%d (ignored)", subscribeHandle->info, subscribeHandle->tid, cardName, numid);
}
-
+
OnSuccessExit:
return 0;
OnErrorExit:
- AFB_ApiWarning(subscribeHandle->api, "CtlSubscribeEventCB: ignored unsupported event");
+ AFB_ApiInfo(subscribeHandle->api, "CtlSubscribeEventCB: ignored unsupported event");
return 0;
}
@@ -340,7 +536,6 @@ OnErrorExit:
return NULL;
}
-
PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) {
int error;
char string [32];
@@ -389,43 +584,32 @@ OnErrorExit:
}
PUBLIC int AlsaCtlRegister(CtlSourceT *source, AlsaPcmInfoT *pcm, int numid) {
- long value;
- int error;
- // NumID are attached to sndcard retrieve ctldev from PCM
- snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, pcm->handle);
- if (!ctlDev) {
- AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", pcm->cardid);
+
+ int count = AudioStreamHandle.count;
+ if (count > MAX_AUDIO_STREAMS) {
+ AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] to many audio stream max=%d", pcm->cardid, MAX_AUDIO_STREAMS);
goto OnErrorExit;
}
- // This is the first registration let's subscrive to event
- if (AudioStreamHandle.count == 0) {
+ // If 1st registration then open a dev control channel to recieve events
+ if (!AudioStreamHandle.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);
}
- error = AlsaCtlGetNumidValueI(source, ctlDev, numid, &value);
- if (error) goto OnErrorExit;
-
- AFB_ApiNotice(source->api, "AlsaCtlRegister [pcm=%s] numid=%d value=%ld", pcm->cardid, numid, value);
-
// store PCM in order to pause/resume depending on event
- int count=AudioStreamHandle.count;
AudioStreamHandle.stream[count].pcm = pcm;
AudioStreamHandle.stream[count].numid = numid;
-
- // we only need to keep ctldev open for initial registration
- if (AudioStreamHandle.count++ > 0) snd_ctl_close(ctlDev);
-
- // toggle pause/resume (should be done after pcm_start)
- if ((error = snd_pcm_pause(pcm->handle, !value)) < 0) {
- AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail to pause", pcm->cardid);
- goto OnErrorExit;
- }
+ AudioStreamHandle.count++;
return 0;
OnErrorExit:
-
return -1;
}
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index f143eb6..bc676ef 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -29,9 +29,6 @@ for the specific language governing permissions and
#include <sys/syscall.h>
-
-#define BUFFER_FRAME_COUNT 1024
-
typedef struct {
snd_pcm_t *pcmIn;
snd_pcm_t *pcmOut;
@@ -238,7 +235,7 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u
// In/Out frames transfer through buffer copy
framesOut = snd_pcm_writei(pcmCopyHandle->pcmOut, pcmCopyHandle->buffer, framesIn);
if (framesOut < 0 || framesOut != framesIn) {
- AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmOut=%s UNDERUN/SUSPEND frameOut=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), framesOut);
+ AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PcmOut=%s UNDERUN frame=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), (framesIn - framesOut));
goto ExitOnSuccess;
}
@@ -283,28 +280,26 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
struct pollfd *pcmInFds;
int error;
- // prepare PCM for capture and replay
- error = AlsaPcmConf(source, pcmIn, opts);
+ // input and output should match
+ error = AlsaPcmConf(source, pcmOut, 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));
+ 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));
goto OnErrorExit;
};
-
- error = AlsaPcmConf(source, pcmOut, opts);
+ // prepare PCM for capture and replay
+ error = AlsaPcmConf(source, pcmIn, opts);
if (error) goto OnErrorExit;
// Prepare PCM for usage
- if ((error = snd_pcm_prepare(pcmOut->handle)) < 0) {
- AFB_ApiError(source->api, "AlsaPcmCopy: Fail to start PCM=%s error=%s", ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
+ if ((error = snd_pcm_start(pcmIn->handle)) < 0) {
+ AFB_ApiError(source->api, "AlsaPcmCopy: Fail to prepare PCM=%s error=%s", ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error));
goto OnErrorExit;
};
-
-
AlsaPcmCopyHandleT *pcmCopyHandle = malloc(sizeof (AlsaPcmCopyHandleT));
pcmCopyHandle->info = "pcmCpy";
pcmCopyHandle->pcmIn = pcmIn->handle;
@@ -312,7 +307,7 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
pcmCopyHandle->api = source->api;
pcmCopyHandle->channels = opts->channels;
pcmCopyHandle->frameSize = opts->channels * opts->sampleSize;
- pcmCopyHandle->frameCount = BUFFER_FRAME_COUNT;
+ pcmCopyHandle->frameCount = ALSA_BUFFER_FRAMES_COUNT;
pcmCopyHandle->buffer = malloc(pcmCopyHandle->frameCount * pcmCopyHandle->frameSize);
// get FD poll descriptor for capture PCM
@@ -350,8 +345,8 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
return 0;
OnErrorExit:
- AFB_ApiError(source->api, "AlsaPcmCopy: Fail \n - pcmIn=%s \n - pcmOut=%s", ALSA_PCM_UID(pcmIn->handle, string), ALSA_PCM_UID(pcmOut->handle, string));
-
+ 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));
return -1;
}
diff --git a/plugins/alsa/alsa-plug-dmix.c b/plugins/alsa/alsa-plug-dmix.c
index 22b5117..b06d5f6 100644
--- a/plugins/alsa/alsa-plug-dmix.c
+++ b/plugins/alsa/alsa-plug-dmix.c
@@ -25,7 +25,7 @@ static int uniqueIpcIndex = 1024;
ALSA_PLUG_PROTO(dmix);
-PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave) {
+PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open) {
snd_config_t *dmixConfig, *slaveConfig, *elemConfig, *pcmConfig;
AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
@@ -57,7 +57,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, Als
error += snd_config_add(dmixConfig, slaveConfig);
if (error) goto OnErrorExit;
- 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->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));
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-plug-multi.c b/plugins/alsa/alsa-plug-multi.c
index 6deca96..3e82e1e 100644
--- a/plugins/alsa/alsa-plug-multi.c
+++ b/plugins/alsa/alsa-plug-multi.c
@@ -22,7 +22,7 @@
ALSA_PLUG_PROTO(multi);
-PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid) {
+PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid, int open) {
snd_config_t *multiConfig, *elemConfig, *slavesConfig, *slaveConfig, *bindingsConfig, *bindingConfig, *pcmConfig;
int error = 0, channelIdx=0;
@@ -79,7 +79,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid) {
// update top config to access previous plugin PCM
snd_config_update();
- error = _snd_pcm_multi_open(&pcmPlug->handle, pcmUid, snd_config, multiConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ 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;
diff --git a/plugins/alsa/alsa-plug-rate.c b/plugins/alsa/alsa-plug-rate.c
new file mode 100644
index 0000000..a9b9e23
--- /dev/null
+++ b/plugins/alsa/alsa-plug-rate.c
@@ -0,0 +1,81 @@
+/*
+ * 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(rate);
+
+
+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;
+
+ 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_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_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_add(pcmConfig, rateConfig);
+ if (error) {
+ AFB_ApiError(source->api, "AlsaCreateRate: fail to add configRATE=%s", pcmPlug->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 (error) {
+ AFB_ApiError(source->api, "AlsaCreateRate: fail to create Rate=%s Slave=%s Error=%s", pcmPlug->cardid, pcmSlave->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);
+ return pcmPlug;
+
+OnErrorExit:
+ AlsaDumpCtlConfig(source, "plug-rate", rateConfig, 1);
+ AFB_ApiNotice(source->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 2c8f3cb..8fa5de8 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -47,7 +47,7 @@ OnErrorExit:
return -1;
}
-PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone) {
+PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int open) {
snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
int error = 0;
@@ -115,7 +115,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone) {
// update top config to access previous plugin PCM
snd_config_update();
- 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(&pcmPlug->handle, zone->uid, 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));
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-plug-stream.c b/plugins/alsa/alsa-plug-stream.c
index e0ef69a..ffb7010 100644
--- a/plugins/alsa/alsa-plug-stream.c
+++ b/plugins/alsa/alsa-plug-stream.c
@@ -36,11 +36,11 @@ STATIC AlsaPcmInfoT* SlaveZoneByUid(CtlSourceT *source, AlsaPcmInfoT **pcmZones
return NULL;
}
-PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl) {
+PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int open) {
snd_config_t *streamConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig;
int error = 0;
- AlsaPcmInfoT *pcmPlug= malloc(sizeof(AlsaPcmInfoT));
+ AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
// assert static/global softmixer handle get requited info
AlsaSndLoopT *ctlLoop = Softmixer->loopCtl;
@@ -57,16 +57,19 @@ PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream
}
// search for target zone uid
- pcmPlug->uid= stream->uid;
- pcmPlug->cardid= stream->uid;
AlsaPcmInfoT *pcmSlave= SlaveZoneByUid (source, pcmZones, stream->zone);
if (!pcmSlave || !pcmSlave->uid) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to find Zone=%s", pcmPlug->uid, stream->zone);
+ AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to find Zone=%s", stream->uid, stream->zone);
goto OnErrorExit;
}
// 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();
@@ -74,6 +77,8 @@ PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream
error += snd_config_set_id (streamConfig, pcmPlug->cardid);
error += snd_config_imake_string(&elemConfig, "type", "softvol");
error += snd_config_add(streamConfig, elemConfig);
+ error += snd_config_imake_integer(&elemConfig, "resolution", 99); // use 0-100%
+ error += snd_config_add(streamConfig, elemConfig);
if (error) goto OnErrorExit;
// add slave leaf
@@ -85,7 +90,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream
// add control leaf
error += snd_config_make_compound(&controlConfig, "control", 0);
- error += snd_config_imake_string(&elemConfig, "name", stream->uid);
+ 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_add(controlConfig, elemConfig);
@@ -95,7 +100,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream
// update top config to access previous plugin PCM
snd_config_update();
- 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(&pcmPlug->handle, stream->uid, snd_config, streamConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
if (error) {
AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to create Plug=%s Slave=%s error=%s", stream->uid, pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-softmixer.c b/plugins/alsa/alsa-softmixer.c
index bd9ee5e..23a01dd 100644
--- a/plugins/alsa/alsa-softmixer.c
+++ b/plugins/alsa/alsa-softmixer.c
@@ -33,85 +33,3 @@ CTLP_ONLOAD(plugin, callbacks) {
return NULL;
}
-CTLP_LUA2C(AlsaDmix, source, argsJ, responseJ) {
- json_object* subscribeArgsJ = NULL;
-
- int error = 0;
- wrap_json_pack(&subscribeArgsJ, "{ss}", "value", "location");
- AFB_ApiNotice(source->api, "lua2c router with %s", json_object_to_json_string_ext(subscribeArgsJ, JSON_C_TO_STRING_PRETTY));
-
- return error;
-}
-
-
-
-CTLP_LUA2C(AlsaRouter, source, argsJ, responseJ) {
- json_object *sndInJ, *sndOutJ, *paramsJ = NULL;
- AlsaPcmInfoT *sndIn, *sndOut;
- int error;
-
- // make sndIn/Out a pointer to get cleaner code
- sndIn = calloc(1, sizeof (AlsaPcmInfoT));
- sndOut = calloc(1, sizeof (AlsaPcmInfoT));
-
- // set pcm options to defaults
- AlsaPcmHwInfoT *pcmOpts;
- pcmOpts = calloc(1, sizeof (AlsaPcmHwInfoT));
- pcmOpts->format = SND_PCM_FORMAT_UNKNOWN;
- pcmOpts->access = SND_PCM_ACCESS_RW_INTERLEAVED;
-
- error = wrap_json_unpack(argsJ, "{s:o,s:o,s?o}", "devin", &sndInJ, "devout", &sndOutJ, "params", &paramsJ);
- if (error) {
- AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter ARGS missing devIn|devOut args=%s", json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
- error = wrap_json_unpack(sndInJ, "{s?s,s?s,s?i,s?i,s?i}", "path", &sndIn->devpath, "id", &sndIn->cardid, "numid", &sndIn->numid, "dev", &sndIn->device, "sub", &sndIn->subdev);
- if (error || (!sndIn->devpath && !sndIn->cardid)) {
- AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-IN missing 'path|id|dev|sub|numid' devin=%s", json_object_get_string(sndInJ));
- goto OnErrorExit;
- }
-
- error = wrap_json_unpack(sndOutJ, "{s?s,s?s,s?i,s?i, s?i}", "path", &sndOut->devpath, "id", &sndOut->cardid, "numid", &sndOut->numid, "dev", &sndOut->device, "sub", &sndOut->subdev);
- if (error || (!sndOut->devpath && !sndOut->cardid)) {
- AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter DEV-OUT missing 'path|id|dev|sub' devout=%s", json_object_get_string(sndOutJ));
- goto OnErrorExit;
- }
-
- if (paramsJ) if ((error = wrap_json_unpack(paramsJ, "{s?i, s?i, s?i, s?i}", "format", &pcmOpts->format, "access", &pcmOpts->access, "rate", &pcmOpts->rate, "channels", &pcmOpts->channels)) != 0) {
- AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter PARAMS missing 'format|access|rate|channels' params=%s", json_object_get_string(paramsJ));
- goto OnErrorExit;
- }
-
- AFB_ApiNotice(source->api, "--lua2c-- AlsaRouter devin=%s devout=%s rate=%d channel=%d", sndIn->devpath, sndOut->devpath, pcmOpts->rate, pcmOpts->channels);
-
- // Check sndOut Exist and build a valid cardid config
- error = AlsaByPathDevid(source, sndOut);
- if (error) goto OnErrorExit;
-
- // open capture PCM
- AlsaPcmInfoT *pcmIn = AlsaByPathOpenPcm(source, sndIn, SND_PCM_STREAM_CAPTURE);
- if (!pcmIn) goto OnErrorExit;
-
- AlsaPcmInfoT *pcmDmix = AlsaCreateDmix(source, "DmixPlugPcm", sndOut);
- if (!pcmDmix) goto OnErrorExit;
-
- //AlsaPcmInfoT *pcmVol = AlsaCreateVol(source, "SoftVol", sndIn, pcmDmix);
- //if (!pcmVol) goto OnErrorExit;
-
- //error = AlsaPcmCopy(source, pcmIn, pcmVol, pcmOpts);
- //if (error) goto OnErrorExit;
-
- // Registration to event should be done after pcm_start
- if (sndIn->numid) {
- error = AlsaCtlRegister(source, pcmIn, sndIn->numid);
- if (error) goto OnErrorExit;
- }
-
- return 0;
-
-OnErrorExit:
- AFB_ApiNotice(source->api, "--lua2c-- ERROR AlsaRouter sndIn=%s sndOut=%s rate=%d channel=%d", sndIn->devpath, sndOut->devpath, pcmOpts->rate, pcmOpts->channels);
- return -1;
-}
-
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index db499c3..3018402 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -33,9 +33,11 @@
#include <alsa/asoundlib.h>
#define MAINLOOP_WATCHDOG 30000
-#define MAX_AUDIO_STREAMS 8
+#define MAX_AUDIO_STREAMS 8*2
#define ALSA_DEFAULT_PCM_RATE 48000
-#define ALSA_CARDID_MAX_LEN 32
+#define ALSA_DEFAULT_PCM_VOLUME 80
+#define ALSA_BUFFER_FRAMES_COUNT 1024
+#define ALSA_CARDID_MAX_LEN 64
#define ALSA_PLUG_PROTO(plugin) \
@@ -110,11 +112,15 @@ typedef struct {
extern SoftMixerHandleT *Softmixer;
+// 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);
+// alsa-api-*.c
+PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params);
+
// 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);
@@ -130,18 +136,25 @@ PUBLIC char *AlsaDumpCtlUid(snd_ctl_t *ctlHandle, char *buffer, size_t len);
// 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);
PUBLIC int AlsaCtlRegister(CtlSourceT *source, AlsaPcmInfoT *pcm, int numid);
-PUBLIC int AlsaCtlGetNumidValueI(CtlSourceT *source, snd_ctl_t* ctlDev, int numid, long* value);
+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 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);
// alsa-core-pcm.c
PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts);
PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *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);
-PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmName);
-PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *pcmControl);
-PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone);
+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* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int open);
+PUBLIC AlsaPcmInfoT* AlsaCreateRate(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open);
#endif \ No newline at end of file
diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c
index 7ceb66f..a857ab1 100644
--- a/plugins/alsa/alsa-utils-bypath.c
+++ b/plugins/alsa/alsa-utils-bypath.c
@@ -74,7 +74,6 @@ PUBLIC int AlsaByPathDevid(CtlSourceT *source, AlsaPcmInfoT *dev) {
dev->cardid=malloc(ALSA_CARDID_MAX_LEN);
snprintf((char*)dev->cardid, ALSA_CARDID_MAX_LEN, "hw:%i", dev->cardidx);
cardInfo = AlsaCtlGetInfo(source, dev->cardid);
- cardInfo = AlsaCtlGetInfo(source, dev->cardid);
}
if (!cardInfo) {