aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/alsa
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/alsa')
-rw-r--r--plugins/alsa/alsa-api-backend.c10
-rw-r--r--plugins/alsa/alsa-api-frontend.c121
-rw-r--r--plugins/alsa/alsa-api-mixer.c8
-rw-r--r--plugins/alsa/alsa-api-streams.c184
-rw-r--r--plugins/alsa/alsa-api-zones.c10
-rw-r--r--plugins/alsa/alsa-core-ctl.c61
-rw-r--r--plugins/alsa/alsa-core-pcm.c32
-rw-r--r--plugins/alsa/alsa-effect-ramp.c144
-rw-r--r--plugins/alsa/alsa-plug-multi.c4
-rw-r--r--plugins/alsa/alsa-plug-route.c4
-rw-r--r--plugins/alsa/alsa-plug-vol.c10
-rw-r--r--plugins/alsa/alsa-softmixer.c4
-rw-r--r--plugins/alsa/alsa-softmixer.h73
13 files changed, 473 insertions, 192 deletions
diff --git a/plugins/alsa/alsa-api-backend.c b/plugins/alsa/alsa-api-backend.c
index 948d30d..85d9566 100644
--- a/plugins/alsa/alsa-api-backend.c
+++ b/plugins/alsa/alsa-api-backend.c
@@ -23,7 +23,9 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannels *channel) {
+
+
+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);
@@ -141,13 +143,13 @@ STATIC int ProcessOneSndCard(CtlSourceT *source, json_object *sndcardJ, AlsaPcmI
switch (json_object_get_type(sinkJ)) {
case json_type_object:
snd->ccount = 1;
- snd->channels = calloc(snd->ccount + 1, sizeof (AlsaPcmChannels));
+ 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 (AlsaPcmChannels));
+ 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]);
@@ -204,7 +206,7 @@ PUBLIC int SndBackend(CtlSourceT *source, json_object *argsJ) {
mixerHandle->multiPcm = &mixerHandle->backend[0];
} else {
-
+
// instantiate an alsa multi plugin
mixerHandle->multiPcm = AlsaCreateMulti(source, "PcmMulti", 0);
if (!mixerHandle->multiPcm) goto OnErrorExit;
diff --git a/plugins/alsa/alsa-api-frontend.c b/plugins/alsa/alsa-api-frontend.c
index 91ce63f..d39c92e 100644
--- a/plugins/alsa/alsa-api-frontend.c
+++ b/plugins/alsa/alsa-api-frontend.c
@@ -24,9 +24,29 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-STATIC int ProcessOneSubdev(CtlSourceT *source, AlsaSndLoopT *loop, json_object *subdevJ, AlsaPcmHwInfoT *loopDefParams,AlsaPcmInfoT *subdev) {
+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));
@@ -41,7 +61,7 @@ STATIC int ProcessOneSubdev(CtlSourceT *source, AlsaSndLoopT *loop, json_object
}
} else {
// use global loop params definition as default
- memcpy (&subdev->params, loopDefParams, sizeof(AlsaPcmHwInfoT));
+ memcpy(&subdev->params, loopDefParams, sizeof (AlsaPcmHwInfoT));
}
// create a fake uid and complete subdev info from loop handle
char subuid[30];
@@ -66,17 +86,25 @@ OnErrorExit:
}
STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *loop) {
- json_object *subdevsJ = NULL, *devicesJ = NULL, *paramsJ = NULL;
+ 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 !}", "uid", &loop->uid, "devpath", &loop->devpath, "cardid", &loop->cardid
- , "cardidx", &loop->cardidx, "devices", &devicesJ, "subdevs", &subdevsJ, "params", &paramsJ);
+ 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));
+ AlsaPcmHwInfoT *loopDefParams = alloca(sizeof (AlsaPcmHwInfoT));
if (paramsJ) {
error = ProcessSndParams(source, loop->uid, paramsJ, loopDefParams);
if (error) {
@@ -85,11 +113,11 @@ STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *
}
} 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;
+ 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
@@ -104,11 +132,36 @@ STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *
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->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) {
@@ -149,37 +202,37 @@ OnErrorExit:
return -1;
}
-PUBLIC int SndFrontend (CtlSourceT *source, json_object *argsJ) {
+PUBLIC int SndFrontend(CtlSourceT *source, json_object *argsJ) {
SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
int error;
-
- assert (mixerHandle);
-
- if (mixerHandle->loop) {
+
+ assert(mixerHandle);
+
+ if (mixerHandle->frontend) {
AFB_ApiError(source->api, "SndFrontend: mixer=%s SndFrontend already declared %s", mixerHandle->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto OnErrorExit;
}
-
- mixerHandle->loop= calloc(1, sizeof (AlsaSndLoopT));
-
+
+ mixerHandle->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);
+ json_type type = json_object_get_type(argsJ);
if (type == json_type_array) {
- size_t count= json_object_array_length(argsJ);
+ 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", mixerHandle->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto OnErrorExit;
}
- argsJ= json_object_array_get_idx(argsJ,0);
- }
-
- type= json_object_get_type(argsJ);
+ 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", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
- error = ProcessOneLoop(source, argsJ, mixerHandle->loop);
+ error = ProcessOneLoop(source, argsJ, mixerHandle->frontend);
if (error) {
AFB_ApiError(source->api, "SndFrontend: mixer=%s invalid object= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index 31854b0..db5f129 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -79,7 +79,7 @@ static void MixerApiVerbCB(AFB_ReqT request) {
if (streams) {
streamsJ = json_object_new_array();
- AlsaSndStreamT *streams = mixerHandle->streams;
+ AlsaLoopStreamT *streams = mixerHandle->streams;
for (int idx = 0; streams[idx].uid; idx++) {
if (quiet) {
json_object_array_add(streamsJ, json_object_new_string(streams[idx].uid));
@@ -126,7 +126,7 @@ static void MixerApiVerbCB(AFB_ReqT request) {
}
if (streamsJ) {
- error = SndStreams(source, streamsJ, &responseJ);
+ error = LoopStreams(source, streamsJ, &responseJ);
if (error) goto OnErrorExit;
}
@@ -168,7 +168,7 @@ CTLP_LUA2C(_mixer_new_, source, argsJ, responseJ) {
// create mixer verb within API.
error = afb_dynapi_add_verb(source->api, mixerHandle->uid, mixerHandle->info, MixerApiVerbCB, mixerHandle, NULL, 0);
if (error) {
- AFB_ApiError(source->api, "_mixer_new_ mixer=%s fail to register API verb", mixerHandle->uid);
+ AFB_ApiError(source->api, "_mixer_new_ mixer=%s fail to Registry API verb", mixerHandle->uid);
return -1;
}
@@ -191,7 +191,7 @@ CTLP_LUA2C(_mixer_new_, source, argsJ, responseJ) {
}
if (streamsJ) {
- error = SndStreams(source, streamsJ, responseJ);
+ error = LoopStreams(source, streamsJ, responseJ);
if (error) goto OnErrorExit;
}
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index d101a5a..56d3cf8 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -31,16 +31,30 @@ extern Lua2cWrapperT Lua2cWrap;
typedef struct {
const char* verb;
- AlsaSndStreamT *streams;
+ AlsaLoopStreamT *stream;
SoftMixerHandleT *mixer;
+ AlsaVolRampT *ramp;
} apiHandleT;
-static void StreamApiVerbCB(AFB_ReqT request) {
+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) {
int error, doClose = 0, doQuiet = 0, doToggle = 0, doMute = -1;
long mute, volume;
- json_object *doVolume, *responseJ, *argsJ = afb_request_json(request);
+ 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;
+ snd_ctl_t *ctlDev = NULL;
CtlSourceT *source = alloca(sizeof (CtlSourceT));
source->uid = handle->verb;
@@ -48,21 +62,22 @@ static void StreamApiVerbCB(AFB_ReqT request) {
source->request = NULL;
source->context = NULL;
- error = wrap_json_unpack(argsJ, "{s?b s?b,s?b,s?b,s?o !}"
+ error = wrap_json_unpack(argsJ, "{s?b s?b,s?b,s?b,s?o,s?o !}"
, "quiet", &doQuiet
, "close", &doClose
, "mute", &doMute
, "toggle", &doToggle
- , "volume", &doVolume
+ , "volume", &volumeJ
+ , "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->loop->cardid);
+ ctlDev = AlsaCtlOpenCtl(source, handle->mixer->frontend->cardid);
if (!ctlDev) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to open sndcard=%s", handle->mixer->loop->cardid);
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to open sndcard=%s", handle->mixer->frontend->cardid);
goto OnErrorExit;
}
@@ -72,24 +87,24 @@ static void StreamApiVerbCB(AFB_ReqT request) {
}
if (doToggle) {
- error += AlsaCtlNumidGetLong(source, ctlDev, handle->streams->mute, &mute);
- error += AlsaCtlNumidSetLong(source, ctlDev, handle->streams->mute, !mute);
+ error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->mute, &mute);
+ error += AlsaCtlNumidSetLong(source, ctlDev, handle->stream->mute, !mute);
}
- if (doVolume) {
+ if (volumeJ) {
long curvol, newvol;
const char*volString;
- error = AlsaCtlNumidGetLong(source, ctlDev, handle->streams->volume, &curvol);
+ error = AlsaCtlNumidGetLong(source, ctlDev, handle->stream->volume, &curvol);
if (error) {
- AFB_ReqFailF(request, "invalid-numid", "Fail to set volume numid=%d value=%ld", handle->streams->volume, volume);
+ AFB_ReqFailF(request, "invalid-numid", "Fail to set volume numid=%d value=%ld", handle->stream->volume, volume);
goto OnErrorExit;
}
- switch (json_object_get_type(doVolume)) {
+ switch (json_object_get_type(volumeJ)) {
case json_type_string:
- volString = json_object_get_string(doVolume);
+ volString = json_object_get_string(volumeJ);
switch (volString[0]) {
case '+':
sscanf(&volString[1], "%ld", &newvol);
@@ -101,30 +116,38 @@ static void StreamApiVerbCB(AFB_ReqT request) {
newvol = curvol - newvol;
break;
default:
- AFB_ReqFailF(request, "not-integer", "relative volume should start by '+|-' value=%s", json_object_get_string(doVolume));
+ AFB_ReqFailF(request, "not-integer", "relative volume should start by '+|-' value=%s", json_object_get_string(volumeJ));
goto OnErrorExit;
}
break;
case json_type_int:
- newvol = json_object_get_int(doVolume);
+ newvol = json_object_get_int(volumeJ);
break;
default:
- AFB_ReqFailF(request, "not-integer", "volume should be string or integer value=%s", json_object_get_string(doVolume));
+ AFB_ReqFailF(request, "not-integer", "volume should be string or integer value=%s", json_object_get_string(volumeJ));
goto OnErrorExit;
}
- error = AlsaCtlNumidSetLong(source, ctlDev, handle->streams->volume, newvol);
+ error = AlsaCtlNumidSetLong(source, ctlDev, 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 (error) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%ld", handle->streams->volume, newvol);
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volram numid=%d value=%s", handle->stream->volume, json_object_get_string(rampJ));
goto OnErrorExit;
}
}
if (doMute != -1) {
- error = AlsaCtlNumidSetLong(source, ctlDev, handle->streams->mute, !mute);
+ error = AlsaCtlNumidSetLong(source, ctlDev, handle->stream->mute, !mute);
if (error) {
- AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%d", handle->streams->volume, !mute);
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%d", handle->stream->volume, !mute);
goto OnErrorExit;
}
}
@@ -132,8 +155,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->streams->volume, &volume);
- error += AlsaCtlNumidGetLong(source, ctlDev, handle->streams->mute, &mute);
+ error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->volume, &volume);
+ error += AlsaCtlNumidGetLong(source, ctlDev, handle->stream->mute, &mute);
if (error) {
AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to get stream numids volume=%ld mute=%ld", volume, mute);
goto OnErrorExit;
@@ -151,7 +174,7 @@ OnErrorExit:
}
-STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaSndStreamT *stream) {
+STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaLoopStreamT *stream) {
int error;
json_object *paramsJ = NULL;
@@ -160,13 +183,15 @@ STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaSndStr
stream->mute = 0;
stream->info = NULL;
- error = wrap_json_unpack(streamJ, "{ss,s?s,ss,s?i,s?b,s?o !}"
+ error = wrap_json_unpack(streamJ, "{ss,s?s,ss,s?i,s?b,s?o,s?s !}"
, "uid", &stream->uid
, "info", &stream->info
, "zone", &stream->zone
, "volume", &stream->volume
, "mute", stream->mute
- , "params", &paramsJ);
+ , "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));
goto OnErrorExit;
@@ -195,9 +220,9 @@ OnErrorExit:
return -1;
}
-PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ) {
+PUBLIC int LoopStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ) {
SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
- AlsaSndStreamT *sndStream;
+ AlsaLoopStreamT *loopStream;
int error;
long value;
size_t count;
@@ -205,37 +230,37 @@ PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **resp
assert(mixerHandle);
// assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = mixerHandle->loop;
+ AlsaSndLoopT *ctlLoop = mixerHandle->frontend;
if (!ctlLoop) {
- AFB_ApiError(source->api, "SndStreams: mixer=%s No Loop found [should register snd_loop first]", mixerHandle->uid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s No Loop found [should Registry snd_loop first]", mixerHandle->uid);
goto OnErrorExit;
}
switch (json_object_get_type(argsJ)) {
case json_type_object:
count = 1;
- sndStream = calloc(count + 1, sizeof (AlsaSndStreamT));
- error = ProcessOneStream(source, argsJ, &sndStream[0]);
+ loopStream = calloc(count + 1, sizeof (AlsaLoopStreamT));
+ error = ProcessOneStream(source, argsJ, &loopStream[0]);
if (error) {
- AFB_ApiError(source->api, "SndStreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
break;
case json_type_array:
count = json_object_array_length(argsJ);
- sndStream = calloc(count + 1, sizeof (AlsaSndStreamT));
+ loopStream = calloc(count + 1, sizeof (AlsaLoopStreamT));
for (int idx = 0; idx < count; idx++) {
- json_object *sndStreamJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneStream(source, sndStreamJ, &sndStream[idx]);
+ json_object *loopStreamJ = json_object_array_get_idx(argsJ, idx);
+ error = ProcessOneStream(source, loopStreamJ, &loopStream[idx]);
if (error) {
- AFB_ApiError(source->api, "sndstreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(sndStreamJ));
+ AFB_ApiError(source->api, "loopstreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(loopStreamJ));
goto OnErrorExit;
}
}
break;
default:
- AFB_ApiError(source->api, "SndStreams: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
@@ -243,14 +268,14 @@ PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **resp
// return stream data to application as a json array
*responseJ = json_object_new_array();
- for (int idx = 0; sndStream[idx].uid != NULL; idx++) {
+ for (int idx = 0; loopStream[idx].uid != NULL; idx++) {
json_object *streamJ, *paramsJ;
// Search for a free loop capture device
- AFB_ApiNotice(source->api, "SndStreams: mixer=%s stream=%s Start", mixerHandle->uid, (char*) sndStream[idx].uid);
+ AFB_ApiNotice(source->api, "LoopStreams: mixer=%s stream=%s Start", mixerHandle->uid, (char*) loopStream[idx].uid);
ctlLoop->scount--;
if (ctlLoop->scount < 0) {
- AFB_ApiError(source->api, "SndStreams: mixer=%s stream=%s no more subdev avaliable in loopback=%s", mixerHandle->uid, sndStream[idx].uid, ctlLoop->uid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s stream=%s no more subdev avaliable in loopback=%s", mixerHandle->uid, loopStream[idx].uid, ctlLoop->uid);
goto OnErrorExit;
}
@@ -266,58 +291,58 @@ PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **resp
error = AlsaPcmConf(source, captureDev, &playbackDev->params);
if (error) goto OnErrorExit;
- // Register capture PCM for active/pause event
+ // Registry capture PCM for active/pause event
if (captureDev->numid) {
- error = AlsaCtlRegister(source, captureDev, captureDev->numid);
+ error = AlsaCtlRegister(source, mixerHandle, 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, "SndStreams: mixer=%s [pcm=%s] fail attache sndcard", mixerHandle->uid, captureDev->cardid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s [pcm=%s] fail attache sndcard", mixerHandle->uid, captureDev->cardid);
goto OnErrorExit;
}
- // create mute control and register it as pause/resume ctl)
+ // create mute control and Registry it as pause/resume ctl)
char runName[ALSA_CARDID_MAX_LEN];
- snprintf(runName, sizeof (runName), "run-%s", sndStream[idx].uid);
+ snprintf(runName, sizeof (runName), "run-%s", loopStream[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;
+ int pauseNumid = AlsaCtlCreateControl(source, ctlDev, playbackDev, runName, 1, 0, 1, 1, loopStream[idx].mute);
+ if (pauseNumid <= 0) goto OnErrorExit;
- // register mute/unmute as a pause/resume control
- error = AlsaCtlRegister(source, captureDev, runNumid);
+ // Registry mute/unmute as a pause/resume control
+ error = AlsaCtlRegister(source, mixerHandle, 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", sndStream[idx].uid);
- AlsaPcmInfoT *streamPcm = AlsaCreateSoftvol(source, &sndStream[idx], captureDev, volName, VOL_CONTROL_MAX, 0);
+ 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, "SndStreams: mixer=%s%s(pcm) fail to create stream", mixerHandle->uid, sndStream[idx].uid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to create stream", mixerHandle->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, sndStream[idx].volume);
+ 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", sndStream[idx].uid);
+ // snprintf(rateName, sizeof (rateName), "rate-%s", loopStream[idx].uid);
// AlsaPcmInfoT *ratePcm= AlsaCreateRate(source, rateName, streamPcm, 1);
// if (!ratePcm) {
- // AFB_ApiError(source->api, "SndStreams: mixer=%s%s(pcm) fail to create rate converter", sndStream[idx].uid);
+ // 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, sndStream[idx].uid, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ error = snd_pcm_open(&streamPcm->handle, loopStream[idx].uid, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "SndStreams: mixer=%s%s(pcm) fail to open capture", mixerHandle->uid, sndStream[idx].uid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s%s(pcm) fail to open capture", mixerHandle->uid, loopStream[idx].uid);
goto OnErrorExit;
}
@@ -325,18 +350,20 @@ PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **resp
captureDev->ccount = streamPcm->ccount;
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);
+ // 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, mixerHandle, 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_ApiError(source->api, "SndStreams: mixer=%s [capture=%s] fail to pause error=%s", mixerHandle->uid, captureDev->cardid, snd_strerror(error));
- goto OnErrorExit;
+ AFB_ApiWarning(source->api, "LoopStreams: mixer=%s [capture=%s] fail to pause error=%s", mixerHandle->uid, captureDev->cardid, snd_strerror(error));
}
// prepare response for application
@@ -344,45 +371,54 @@ PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **resp
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", runNumid, "params", paramsJ);
+ 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, "SndStreams: mixer=%s stream=%s fail to prepare response", mixerHandle->uid, captureDev->cardid);
+ AFB_ApiError(source->api, "LoopStreams: mixer=%s stream=%s fail to prepare response", mixerHandle->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", mixerHandle->uid, sndStream[idx].uid);
+ error = snprintf(apiVerb, sizeof (apiVerb), "%s/%s", mixerHandle->uid, loopStream[idx].uid);
if (error == sizeof (apiVerb)) {
- AFB_ApiError(source->api, "SndStreams mixer=%s fail to register Stream API too long %s/%s", mixerHandle->uid, mixerHandle->uid, sndStream[idx].uid);
+ AFB_ApiError(source->api, "LoopStreams mixer=%s fail to Registry Stream API too long %s/%s", mixerHandle->uid, mixerHandle->uid, loopStream[idx].uid);
return -1;
}
+ // 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", mixerHandle->uid, loopStream[idx].uid, loopStream->ramp);
+ goto OnErrorExit;
+ }
+ }
+
apiHandle->mixer = mixerHandle;
- apiHandle->streams = &sndStream[idx];
+ apiHandle->stream= &loopStream[idx];
apiHandle->verb = strdup(apiVerb);
- error = afb_dynapi_add_verb(source->api, apiHandle->verb, sndStream[idx].info, StreamApiVerbCB, apiHandle, NULL, 0);
+ error = afb_dynapi_add_verb(source->api, apiHandle->verb, loopStream[idx].info, StreamApiVerbCB, apiHandle, NULL, 0);
if (error) {
- AFB_ApiError(source->api, "SndStreams mixer=%s fail to register API verb=%s", mixerHandle->uid, apiHandle->verb);
+ AFB_ApiError(source->api, "LoopStreams mixer=%s fail to Registry API verb=%s", mixerHandle->uid, apiHandle->verb);
return -1;
}
// free temporary resources
snd_ctl_close(ctlDev);
- sndStream[idx].volume = volNumid;
- sndStream[idx].mute = runNumid;
+ 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, "SndStreams: mixer=%s stream=%s OK reponse=%s\n", mixerHandle->uid, streamPcm->uid, json_object_get_string(streamJ));
+ AFB_ApiNotice(source->api, "LoopStreams: mixer=%s stream=%s OK reponse=%s\n", mixerHandle->uid, streamPcm->uid, json_object_get_string(streamJ));
}
// save handle for further use
- mixerHandle->streams = sndStream;
+ mixerHandle->streams = loopStream;
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c
index de834df..8f383df 100644
--- a/plugins/alsa/alsa-api-zones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -24,7 +24,7 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
-STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannels *channel) {
+STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannelT *channel) {
const char*channelUid;
int error = wrap_json_unpack(channelJ, "{ss,si,s?i !}", "target", &channelUid, "channel", &channel->port);
@@ -70,13 +70,13 @@ STATIC int ProcessOneZone(CtlSourceT *source, json_object *zoneJ, AlsaSndZoneT *
switch (json_object_get_type(mappingJ)) {
case json_type_object:
count = 1;
- zone->channels = calloc(count + 1, sizeof (AlsaPcmChannels));
+ 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 (AlsaPcmChannels));
+ 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]);
@@ -103,7 +103,7 @@ PUBLIC int SndZones(CtlSourceT *source, json_object *argsJ) {
assert(mixerHandle);
if (mixerHandle->routes) {
- AFB_ApiError(source->api, "SndZones: mixer=%s Zones already registered %s", mixerHandle->uid, json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndZones: mixer=%s Zones already Registryed %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
@@ -135,7 +135,7 @@ PUBLIC int SndZones(CtlSourceT *source, json_object *argsJ) {
goto OnErrorExit;
}
- // register routed into global softmixer handle
+ // Registry routed into global softmixer handle
mixerHandle->routes= calloc(count + 1, sizeof (AlsaPcmInfoT*));
// instantiate one route PCM per zone with multi plugin as slave
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index 1749679..4ba871a 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -36,21 +36,9 @@ typedef struct {
char* info;
snd_ctl_t *ctlDev;
sd_event *sdLoop;
+ RegistryHandleT *registry;
} SubscribeHandleT;
-typedef struct {
- AlsaPcmInfoT *pcm;
- int numid;
-} SubStreamT;
-
-typedef struct {
- SubStreamT stream[MAX_AUDIO_STREAMS + 1];
- int count;
- snd_ctl_t *ctlDev;
-} AudioStreamHandleT;
-
-static AudioStreamHandleT AudioStreamHandle;
-
PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(CtlSourceT *source, snd_ctl_t* ctlDev, int numid) {
char string[32];
@@ -513,18 +501,30 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
error = CtlElemIdGetNumid(subscribeHandle->api, subscribeHandle->ctlDev, elemId, &numid);
if (error) goto OnErrorExit;
- for (idx = 0; idx < AudioStreamHandle.count; idx++) {
- if (AudioStreamHandle.stream[idx].numid == numid) {
- const char *pcmName = AudioStreamHandle.stream[idx].pcm->cardid;
- snd_pcm_pause(AudioStreamHandle.stream[idx].pcm->handle, !value);
- AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d pcm=%s pause=%d numid=%d", subscribeHandle->info, subscribeHandle->tid, pcmName, !value, numid);
+ 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);
+ 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);
+ 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);
+ }
break;
}
}
- if (idx == AudioStreamHandle.count) {
+ 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 (ignored)", subscribeHandle->info, subscribeHandle->tid, cardName, numid);
+ AFB_ApiNotice(subscribeHandle->api, "CtlSubscribeEventCB:%s/%d card=%s numid=%d (unknown)", subscribeHandle->info, subscribeHandle->tid, cardName, numid);
}
OnSuccessExit:
@@ -577,7 +577,7 @@ OnErrorExit:
return NULL;
}
-PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) {
+PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev, RegistryHandleT *registry) {
int error;
char string [32];
struct pollfd pfds;
@@ -586,6 +586,7 @@ PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) {
subscribeHandle->api = source->api;
subscribeHandle->ctlDev = ctlDev;
subscribeHandle->info = "ctlEvt";
+ subscribeHandle->registry= registry;
// subscribe for sndctl events attached to cardid
if ((error = snd_ctl_subscribe_events(ctlDev, 1)) < 0) {
@@ -606,7 +607,7 @@ PUBLIC int AlsaCtlSubscribe(CtlSourceT *source, snd_ctl_t * ctlDev) {
goto OnErrorExit;
}
- // register sound event to binder main loop
+ // 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;
@@ -624,30 +625,32 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlRegister(CtlSourceT *source, AlsaPcmInfoT *pcm, int numid) {
+PUBLIC int AlsaCtlRegister(CtlSourceT *source, SoftMixerHandleT *mixer, AlsaPcmInfoT *pcm, RegistryNumidT type, int numid) {
+ RegistryHandleT *registry= mixer->frontend->registry;
- int count = AudioStreamHandle.count;
+ 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);
goto OnErrorExit;
}
// If 1st registration then open a dev control channel to recieve events
- if (!AudioStreamHandle.ctlDev) {
+ 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);
+ AlsaCtlSubscribe(source, ctlDev, registry);
}
// store PCM in order to pause/resume depending on event
- AudioStreamHandle.stream[count].pcm = pcm;
- AudioStreamHandle.stream[count].numid = numid;
- AudioStreamHandle.count++;
+ registry->stream[count].pcm = pcm;
+ registry->stream[count].numid = numid;
+ registry->stream[count].type = type;
+ registry->count++;
return 0;
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index bc676ef..ad8e842 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -27,22 +27,7 @@ for the specific language governing permissions and
#include "alsa-softmixer.h"
#include <pthread.h>
#include <sys/syscall.h>
-
-
-typedef struct {
- snd_pcm_t *pcmIn;
- snd_pcm_t *pcmOut;
- AFB_ApiT api;
- sd_event_source* evtsrc;
- void* buffer;
- size_t frameSize;
- unsigned int frameCount;
- unsigned int channels;
- sd_event *sdLoop;
- pthread_t thread;
- int tid;
- char* info;
-} AlsaPcmCopyHandleT;
+#include <sched.h>
STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
int pcmSampleSize;
@@ -243,7 +228,7 @@ STATIC int AlsaPcmReadCB(sd_event_source* src, int fd, uint32_t revents, void* u
AFB_ApiNotice(pcmCopyHandle->api, "AlsaPcmReadCB PCM=%s Loosing frames=%ld", ALSA_PCM_UID(pcmCopyHandle->pcmOut, string), (framesIn - framesOut));
goto ExitOnSuccess;
}
-
+
return 0;
// Cannot handle error in callback
@@ -275,7 +260,7 @@ static void *LoopInThread(void *handle) {
pthread_exit(0);
}
-PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT * opts) {
+PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaLoopStreamT *loopStream, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT * opts) {
char string[32];
struct pollfd *pcmInFds;
int error;
@@ -300,7 +285,7 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
goto OnErrorExit;
};
- AlsaPcmCopyHandleT *pcmCopyHandle = malloc(sizeof (AlsaPcmCopyHandleT));
+ AlsaPcmCopyHandleT *pcmCopyHandle = &loopStream->copy;
pcmCopyHandle->info = "pcmCpy";
pcmCopyHandle->pcmIn = pcmIn->handle;
pcmCopyHandle->pcmOut = pcmOut->handle;
@@ -309,6 +294,7 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
pcmCopyHandle->frameSize = opts->channels * opts->sampleSize;
pcmCopyHandle->frameCount = ALSA_BUFFER_FRAMES_COUNT;
pcmCopyHandle->buffer = malloc(pcmCopyHandle->frameCount * pcmCopyHandle->frameSize);
+
// get FD poll descriptor for capture PCM
int pcmInCount = snd_pcm_poll_descriptors_count(pcmCopyHandle->pcmIn);
@@ -341,6 +327,14 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
AFB_ApiError(source->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);
+ 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));
+ }
return 0;
diff --git a/plugins/alsa/alsa-effect-ramp.c b/plugins/alsa/alsa-effect-ramp.c
new file mode 100644
index 0000000..c861d0e
--- /dev/null
+++ b/plugins/alsa/alsa-effect-ramp.c
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ *
+ * reference :
+ * https://github.com/zonque/simple-alsa-loop/blob/master/loop.c
+ * https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html#a31
+ *
+ */
+
+#define _GNU_SOURCE // needed for vasprintf
+
+#include "alsa-softmixer.h"
+#include <pthread.h>
+#include <sys/syscall.h>
+
+typedef struct {
+ const char *uid;
+ long current;
+ long target;
+ int numid;
+ snd_ctl_t *ctlDev;
+ AlsaVolRampT *params;
+ sd_event_source *evtsrc;
+ CtlSourceT *source;
+ sd_event *sdLoop;
+} VolRampHandleT;
+
+STATIC int VolRampTimerCB(sd_event_source* source, uint64_t timer, void* handle) {
+ VolRampHandleT *rampHandle = (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;
+ }
+
+ // RampUp
+ if (rampHandle->current < rampHandle->target) {
+ rampHandle->current = rampHandle->current + rampHandle->params->stepUp;
+ if (rampHandle->current > rampHandle->target) rampHandle->current = rampHandle->target;
+ }
+
+ error = AlsaCtlNumidSetLong(rampHandle->source, rampHandle->ctlDev, rampHandle->numid, rampHandle->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 {
+ // 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);
+ }
+
+ return 0;
+
+OnErrorExit:
+ AFB_ApiWarning(rampHandle->source->api, "VolRampTimerCB stream=%s numid=%d value=%ld", rampHandle->uid, rampHandle->numid, rampHandle->current);
+ sd_event_source_unref(source); // abandon volRamp
+ return -1;
+}
+
+PUBLIC int AlsaVolRampApply(CtlSourceT *source, AlsaSndLoopT *frontend, AlsaLoopStreamT *stream, AlsaVolRampT *ramp, json_object *volumeJ) {
+ long curvol, newvol;
+ const char*volString;
+ int error;
+ uint64_t usec;
+
+ snd_ctl_t *ctlDev =AlsaCtlOpenCtl(source, frontend->cardid);
+ if (!ctlDev) goto OnErrorExit;
+
+ error = AlsaCtlNumidGetLong(source, ctlDev, stream->volume, &curvol);
+ if (error) {
+ AFB_ApiError(source->api,"AlsaVolRampApply:%s(stream) Fail to get volume numid=%d", stream->uid, stream->volume);
+ goto OnErrorExit;
+ }
+
+ switch (json_object_get_type(volumeJ)) {
+ case json_type_string:
+ volString = json_object_get_string(volumeJ);
+ switch (volString[0]) {
+ case '+':
+ sscanf(&volString[1], "%ld", &newvol);
+ newvol = curvol + newvol;
+ break;
+
+ case '-':
+ sscanf(&volString[1], "%ld", &newvol);
+ newvol = curvol - 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;
+ }
+ break;
+ case json_type_int:
+ newvol = json_object_get_int(volumeJ);
+ break;
+ default:
+ AFB_ApiError(source->api,"AlsaVolRampApply:%s(stream) volume should be string or integer value=%s", stream->uid, json_object_get_string(volumeJ));
+ 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;
+
+ // 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);
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+}
diff --git a/plugins/alsa/alsa-plug-multi.c b/plugins/alsa/alsa-plug-multi.c
index 0dd417d..c28508c 100644
--- a/plugins/alsa/alsa-plug-multi.c
+++ b/plugins/alsa/alsa-plug-multi.c
@@ -34,7 +34,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid, int
AlsaPcmInfoT* pcmSlaves=mixerHandle->backend;
if (!pcmSlaves) {
- AFB_ApiError(source->api, "AlsaCreateMulti: No Sound Card find [should register snd_cards first]");
+ AFB_ApiError(source->api, "AlsaCreateMulti: No Sound Card find [should Registry snd_cards first]");
goto OnErrorExit;
}
@@ -52,7 +52,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid, int
// loop on sound card to include into multi
for (int idx = 0; pcmSlaves[idx].uid != NULL; idx++) {
AlsaPcmInfoT* sndcard=&pcmSlaves[idx];
- AlsaPcmChannels *channels = sndcard->channels;
+ AlsaPcmChannelT *channels = sndcard->channels;
for (channelIdx=0; channels[channelIdx].uid != NULL; channelIdx++) {
char idxS[4]; // 999 channel should be more than enough
diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c
index d2ca223..f254c68 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -28,7 +28,7 @@ STATIC int CardChannelByUid(CtlSourceT *source, AlsaPcmInfoT *pcmBackend, const
// 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++) {
- AlsaPcmChannels *channels = pcmBackend[cardIdx].channels;
+ AlsaPcmChannelT *channels = pcmBackend[cardIdx].channels;
if (!channels) {
AFB_ApiError(source->api, "CardChannelByUid: No Backend card=%s [should declare channels]", pcmBackend[cardIdx].uid);
goto OnErrorExit;
@@ -60,7 +60,7 @@ PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int
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 register snd_cards first]"
+ AFB_ApiError(source->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No Sound Card Ctl find [should Registry snd_cards first]"
, mixerHandle->uid, zone->uid);
goto OnErrorExit;
}
diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c
index 09bc51a..cb764ef 100644
--- a/plugins/alsa/alsa-plug-vol.c
+++ b/plugins/alsa/alsa-plug-vol.c
@@ -25,7 +25,7 @@ ALSA_PLUG_PROTO(softvol); // stream uses solftvol plugin
STATIC AlsaPcmInfoT* SlaveZoneByUid(CtlSourceT *source, AlsaPcmInfoT **pcmZones, const char *uid) {
AlsaPcmInfoT *slaveZone= NULL;
- // Loop on every registered zone pcm and extract (cardid) from (uid)
+ // 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];
@@ -36,7 +36,7 @@ STATIC AlsaPcmInfoT* SlaveZoneByUid(CtlSourceT *source, AlsaPcmInfoT **pcmZones
return NULL;
}
-PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open) {
+PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaLoopStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open) {
SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
snd_config_t *streamConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig;
int error = 0;
@@ -45,16 +45,16 @@ PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaSndStreamT *strea
assert (mixerHandle);
// assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = mixerHandle->loop;
+ AlsaSndLoopT *ctlLoop = mixerHandle->frontend;
if (!ctlLoop) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Loop found [should register snd_loop first]",stream->uid);
+ 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;
if (!pcmZones) {
- AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Zone found [should register snd_zones first]", stream->uid);
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Zone found [should Registry snd_zones first]", stream->uid);
goto OnErrorExit;
}
diff --git a/plugins/alsa/alsa-softmixer.c b/plugins/alsa/alsa-softmixer.c
index 4528087..b414e20 100644
--- a/plugins/alsa/alsa-softmixer.c
+++ b/plugins/alsa/alsa-softmixer.c
@@ -22,11 +22,11 @@
// Force Lua2cWrapper inclusion within already existing plugin
-CTLP_LUA_REGISTER("alsa-softmixer")
+CTLP_LUA_Registry("alsa-softmixer")
// Call at initialisation time
CTLP_ONLOAD(plugin, callbacks) {
- AFB_ApiDebug(plugin->api, "SoftMixer Plugin Registered: uid='%s' 'info='%s'", plugin->uid, plugin->info);
+ AFB_ApiDebug(plugin->api, "SoftMixer Plugin Registryed: uid='%s' 'info='%s'", plugin->uid, plugin->info);
return NULL;
}
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index f5a82f0..a8d7af6 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -48,6 +48,7 @@
#define ALSA_CARDID_MAX_LEN 64
+
#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)
@@ -58,12 +59,39 @@
#define STATIC static
#endif
-// alsa-utils-bypath.c
+
+typedef enum {
+ FONTEND_NUMID_IGNORE,
+ FONTEND_NUMID_PAUSE,
+ FONTEND_NUMID_RUN
+} RegistryNumidT;
+
+typedef struct {
+ snd_pcm_t *pcmIn;
+ snd_pcm_t *pcmOut;
+ AFB_ApiT api;
+ sd_event_source* evtsrc;
+ void* buffer;
+ size_t frameSize;
+ unsigned int frameCount;
+ unsigned int channels;
+ sd_event *sdLoop;
+ pthread_t thread;
+ int tid;
+ char* info;
+} AlsaPcmCopyHandleT;
+
+typedef struct {
+ const char *uid;
+ int delay; // delay between volset in us
+ int stepDown; // linear %
+ int stepUp; // linear %
+} AlsaVolRampT;
typedef struct {
const char*uid;
int port;
-} AlsaPcmChannels;
+} AlsaPcmChannelT;
typedef struct {
unsigned int rate;
@@ -83,17 +111,31 @@ typedef struct {
int numid;
int ccount;
snd_pcm_t *handle;
- AlsaPcmChannels *channels;
+ AlsaPcmChannelT *channels;
AlsaPcmHwInfoT params;
} AlsaPcmInfoT;
typedef struct {
const char *uid;
snd_pcm_stream_t type;
- AlsaPcmChannels *channels;
+ AlsaPcmChannelT *channels;
AlsaPcmInfoT *pcm;
} AlsaSndZoneT;
+
+typedef struct {
+ AlsaPcmInfoT *pcm;
+ int numid;
+ RegistryNumidT type;
+} RegistryStreamT;
+
+typedef struct {
+ RegistryStreamT stream[MAX_AUDIO_STREAMS + 1];
+ int count;
+ snd_ctl_t *ctlDev;
+} RegistryHandleT;
+
+
typedef struct {
const char *uid;
const char *devpath;
@@ -103,26 +145,30 @@ typedef struct {
int capture;
int scount;
AlsaPcmInfoT *subdevs;
+ AlsaVolRampT *ramps;
+ RegistryHandleT *registry;
} AlsaSndLoopT;
typedef struct {
const char *uid;
const char *info;
const char *zone;
+ const char *ramp;
int volume;
int mute;
AlsaPcmInfoT *pcm;
AlsaPcmHwInfoT params;
-} AlsaSndStreamT;
+ AlsaPcmCopyHandleT copy;
+} AlsaLoopStreamT;
typedef struct {
const char *uid;
const char *info;
- AlsaSndLoopT *loop;
+ AlsaSndLoopT *frontend;
AlsaPcmInfoT *backend;
AlsaPcmInfoT *multiPcm;
AlsaPcmInfoT **routes;
- AlsaSndStreamT *streams;
+ AlsaLoopStreamT *streams;
} SoftMixerHandleT;
// alsa-utils-bypath.c
@@ -147,8 +193,8 @@ PUBLIC char *AlsaDumpCtlUid(snd_ctl_t *ctlHandle, char *buffer, size_t len);
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 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);
@@ -160,13 +206,13 @@ PUBLIC int CtlElemIdGetLong(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *
// alsa-core-pcm.c
PUBLIC int AlsaPcmConf(CtlSourceT *source, AlsaPcmInfoT *pcm, AlsaPcmHwInfoT *opts);
-PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pcmOut, AlsaPcmHwInfoT *opts);
+PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaLoopStreamT *loopStream, 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, 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, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, 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);
// alsa-api-*
@@ -174,6 +220,9 @@ PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *pa
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 SndStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ);
+PUBLIC int LoopStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ);
+
+// alsa-effect-ramp.c
+PUBLIC int AlsaVolRampApply(CtlSourceT *source, AlsaSndLoopT *frontend, AlsaLoopStreamT *stream, AlsaVolRampT *ramp, json_object *volumeJ);
#endif \ No newline at end of file