summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/alsa/alsa-api-loop.c307
-rw-r--r--plugins/alsa/alsa-api-mixer.c33
-rw-r--r--plugins/alsa/alsa-api-streams.c23
-rw-r--r--plugins/alsa/alsa-softmixer.h9
4 files changed, 278 insertions, 94 deletions
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
index a123bb3..c0ea549 100644
--- a/plugins/alsa/alsa-api-loop.c
+++ b/plugins/alsa/alsa-api-loop.c
@@ -19,8 +19,50 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
+#include <avirt/avirt.h>
#include <string.h>
+#define UID_AVIRT_LOOP "AVIRT-Loopback"
+
+#define REGNUMID_0 51
+#define REGNUMID_1 57
+#define REGNUMID_2 63
+#define REGNUMID_3 69
+#define REGNUMID_4 75
+#define REGNUMID_5 81
+#define REGNUMID_6 87
+#define REGNUMID_7 93
+#define REGNUMID_8 99
+#define REGNUMID_9 105
+#define REGNUMID_10 111
+#define REGNUMID_11 117
+#define REGNUMID_12 123
+#define REGNUMID_13 129
+#define REGNUMID_14 135
+#define REGNUMID_15 141
+
+struct RegistryNumidMap {
+ int index;
+ int numid;
+} numidmap[] = {
+ { 0, REGNUMID_0 },
+ { 1, REGNUMID_1 },
+ { 2, REGNUMID_2 },
+ { 3, REGNUMID_3 },
+ { 4, REGNUMID_4 },
+ { 5, REGNUMID_5 },
+ { 6, REGNUMID_6 },
+ { 7, REGNUMID_7 },
+ { 8, REGNUMID_8 },
+ { 9, REGNUMID_9 },
+ { 10, REGNUMID_10 },
+ { 11, REGNUMID_11 },
+ { 12, REGNUMID_12 },
+ { 13, REGNUMID_13 },
+ { 14, REGNUMID_14 },
+ { 15, REGNUMID_15 },
+};
+
PUBLIC AlsaLoopSubdevT *ApiLoopFindSubdev(SoftMixerT *mixer, const char *streamUid, const char *targetUid, AlsaSndLoopT **loop) {
// Either allocate a free loop subdev or search for a specific targetUid when specified
@@ -55,6 +97,90 @@ fail:
return NULL;
}
+STATIC int CheckOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, AlsaLoopSubdevT *subdev) {
+ // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard
+ AlsaDevInfoT loopSubdev;
+ loopSubdev.devpath=NULL;
+ loopSubdev.cardid=NULL;
+ loopSubdev.pcmplug_params = NULL;
+ loopSubdev.cardidx = loop->sndcard->cid.cardidx;
+ loopSubdev.device = loop->capture;
+ loopSubdev.subdev = subdev->index;
+
+ // assert we may open this loopback subdev in capture mode
+ AlsaPcmCtlT *pcmCtl = AlsaByPathOpenPcmCtl(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE);
+ if (!pcmCtl) {
+ AFB_API_ERROR(mixer->api, "%s: Failed", __func__);
+ return -1;
+ }
+
+ pcmCtl->closeAtDeletion = true;
+
+ // free PCM as we only open loop to assert it's a valid capture device
+ AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl);
+ AlsaPcmCtlDelete(mixer, pcmCtl);
+
+ return 0;
+}
+
+STATIC AlsaLoopSubdevT *ProcessOneAvirtSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *streamJ, int index) {
+ char *uid, *zone_uid;
+ AlsaSndZoneT *zone;
+ AFB_API_INFO(mixer->api, "%s: %d", __func__, index);
+ int error;
+
+ uid = alloca(32);
+
+ error = wrap_json_unpack(streamJ, "{ss,s?s,s?s,ss,s?s,s?i,s?b,s?o,s?s !}"
+ , "uid", &uid
+ , "verb", NULL
+ , "info", NULL
+ , "zone", &zone_uid
+ , "source", NULL
+ , "volume", NULL
+ , "mute", NULL
+ , "params", NULL
+ , "ramp", NULL
+ );
+ if (error) {
+ AFB_API_NOTICE(mixer->api,
+ "%s: hal=%s missing 'uid|[info]|zone|source||[volume]|[mute]|[params]' error=%s stream=%s",
+ __func__, uid, wrap_json_get_error_string(error), json_object_get_string(streamJ));
+ goto OnErrorExit;
+ }
+
+ if (mixer->nbZones != 0) {
+ zone = ApiZoneGetByUid(mixer, zone_uid);
+ if (!zone)
+ goto OnErrorExit;
+ } else {
+ AFB_API_ERROR(mixer->api, "%s: No zones defined!", __func__);
+ goto OnErrorExit;
+ }
+
+ error = snd_avirt_stream_new(uid, zone->ccount,
+ SND_PCM_STREAM_PLAYBACK, "ap_loopback");
+ if (error < 0) {
+ AFB_API_ERROR(mixer->api,
+ "%s: mixer=%s stream=%s could not create AVIRT stream [errno=%d]",
+ __func__, mixer->uid, uid, error);
+ return NULL;
+ }
+
+ AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaPcmCtlT));
+ subdev->uid = NULL;
+ subdev->index = index;
+ subdev->numid = numidmap[index].index;
+
+ if (CheckOneSubdev(mixer, loop, subdev) < 0)
+ return NULL;
+
+ return subdev;
+
+OnErrorExit:
+ AFB_API_ERROR(mixer->api, "%s fail", __func__);
+ return NULL;
+}
STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *subdevJ) {
@@ -87,26 +213,8 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop,
}
}
- // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard
- AlsaDevInfoT loopSubdev;
- loopSubdev.devpath=NULL;
- loopSubdev.cardid=NULL;
- loopSubdev.pcmplug_params = NULL;
- loopSubdev.cardidx = loop->sndcard->cid.cardidx;
- loopSubdev.device = loop->capture;
- loopSubdev.subdev = subdev->index;
-
- // assert we may open this loopback subdev in capture mode
- AlsaPcmCtlT *pcmCtl = AlsaByPathOpenPcmCtl(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE);
- if (!pcmCtl) {
- goto fail_subdev_uid;
- }
-
- pcmCtl->closeAtDeletion = true;
-
- // free PCM as we only open loop to assert it's a valid capture device
- AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl);
- AlsaPcmCtlDelete(mixer, pcmCtl);
+ if (CheckOneSubdev(mixer, loop, subdev) < 0)
+ goto fail_subdev_uid;
AFB_API_DEBUG(mixer->api, "%s DONE", __func__);
@@ -126,7 +234,7 @@ static void freeSubdev(SoftMixerT* mixer, AlsaLoopSubdevT * subdev) {
}
-STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ, json_object *streamsJ) {
json_object *subdevsJ = NULL, *devicesJ = NULL;
int error;
@@ -152,67 +260,122 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje
loop->sndcard = sndctl;
- error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,so,so !}"
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?o,s?o !}"
, "uid", &loop->uid
, "path", &loop->sndcard->cid.devpath
, "cardid", &loop->sndcard->cid.cardid
, "devices", &devicesJ
, "subdevs", &subdevsJ
);
- if (error || !loop->uid || !subdevsJ || (!loop->sndcard->cid.devpath && !loop->sndcard->cid.cardid)) {
- AFB_API_NOTICE(mixer->api, "%s mixer=%s hal=%s missing 'uid|path|cardid|devices|subdevs' error=%s args=%s",
- __func__, mixer->uid, uid, wrap_json_get_error_string(error),json_object_get_string(argsJ));
- goto fail_snd_card;
+
+ if (loop->uid) {
+ if (!strcmp(loop->uid, UID_AVIRT_LOOP))
+ loop->avirt = true;
+ else
+ loop->avirt = false;
}
- // try to open sound card control interface
- loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard);
- if (!loop->sndcard->ctl) {
- AFB_API_ERROR(mixer->api, "%s mixer=%s hal=%s Fail open sndcard loop=%s devpath=%s cardid=%s (please check 'modprobe snd_aloop')",
- __func__, mixer->uid, uid, loop->uid, loop->sndcard->cid.devpath, loop->sndcard->cid.cardid);
+ if (error || !loop->uid || (!loop->avirt && !subdevsJ) || (!loop->sndcard->cid.devpath && !loop->sndcard->cid.cardid)) {
+ AFB_API_NOTICE(mixer->api, "%s mixer=%s hal=%s missing 'uid|path|cardid|devices|subdevs' error=%s args=%s",
+ __func__, mixer->uid, uid, wrap_json_get_error_string(error),json_object_get_string(argsJ));
goto fail_snd_card;
}
- // Default devices is playback=0 capture=1
- if (!devicesJ) {
- loop->playback = 0;
- loop->capture = 1;
- } else {
- error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback);
- if (error) {
- AFB_API_NOTICE(mixer->api, "%s mixer=%s hal=%s Loop=%s missing 'capture|playback' error=%s devices=%s",
- __func__, mixer->uid, uid, loop->uid, wrap_json_get_error_string(error),json_object_get_string(devicesJ));
- goto fail_snd_card_ctl;
+ if (!loop->avirt) {
+ // try to open sound card control interface
+ loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard);
+ if (!loop->sndcard->ctl) {
+ AFB_API_ERROR(mixer->api, "%s mixer=%s hal=%s Fail open sndcard loop=%s devpath=%s cardid=%s (please check 'modprobe snd_aloop')",
+ __func__, mixer->uid, uid, loop->uid, loop->sndcard->cid.devpath, loop->sndcard->cid.cardid);
+ goto fail_snd_card;
}
- }
- AlsaLoopSubdevT * subdev;
+ // Default devices is playback=0 capture=1
+ if (!devicesJ) {
+ loop->playback = 0;
+ loop->capture = 1;
+ } else {
+ error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback);
+ if (error) {
+ AFB_API_NOTICE(mixer->api, "%s mixer=%s hal=%s Loop=%s missing 'capture|playback' error=%s devices=%s",
+ __func__, mixer->uid, uid, loop->uid, wrap_json_get_error_string(error),json_object_get_string(devicesJ));
+ goto fail_snd_card_ctl;
+ }
+ }
- switch (json_object_get_type(subdevsJ)) {
- case json_type_object:
- subdev = ProcessOneSubdev(mixer, loop, subdevsJ);
- if (!subdev)
- goto fail_loop_subdev;
- loop->nbSubdevs++;
- cds_list_add_tail(&subdev->list, &loop->subdevs.list);
- break;
- case json_type_array: {
- int nbSubDevs = (int) json_object_array_length(subdevsJ);
- for (int idx = 0; idx < nbSubDevs; idx++) {
- json_object *subdevJ = json_object_array_get_idx(subdevsJ, idx);
- subdev = ProcessOneSubdev(mixer, loop, subdevJ);
- if (!subdev) {
- goto fail_loop_subdev;
- }
- loop->nbSubdevs++;
- cds_list_add_tail(&subdev->list, &loop->subdevs.list);
+ AlsaLoopSubdevT * subdev;
+
+ switch (json_object_get_type(subdevsJ)) {
+ case json_type_object:
+ subdev = ProcessOneSubdev(mixer, loop, subdevsJ);
+ if (!subdev)
+ goto fail_loop_subdev;
+ loop->nbSubdevs++;
+ cds_list_add_tail(&subdev->list, &loop->subdevs.list);
+ break;
+ case json_type_array: {
+ int nbSubDevs = (int) json_object_array_length(subdevsJ);
+ for (int idx = 0; idx < nbSubDevs; idx++) {
+ json_object *subdevJ = json_object_array_get_idx(subdevsJ, idx);
+ subdev = ProcessOneSubdev(mixer, loop, subdevJ);
+ if (!subdev) {
+ goto fail_loop_subdev;
+ }
+ loop->nbSubdevs++;
+ cds_list_add_tail(&subdev->list, &loop->subdevs.list);
+ }
+ break;
}
- break;
- }
- default:
- AFB_API_ERROR(mixer->api, "%s mixer=%s hal=%s Loop=%s invalid subdevs= %s",
- __func__, mixer->uid, uid, loop->uid, json_object_get_string(subdevsJ));
- goto fail_snd_card_ctl;
+ default:
+ AFB_API_ERROR(mixer->api, "%s mixer=%s hal=%s Loop=%s invalid subdevs= %s",
+ __func__, mixer->uid, uid, loop->uid, json_object_get_string(subdevsJ));
+ goto fail_snd_card_ctl;
+ }
+ } else { // loop->avirt == true
+ AFB_API_NOTICE(mixer->api, "nbStreams: %d max.streams: %d", mixer->nbStreams, mixer->max.streams);
+
+ if (mixer->nbStreams >= mixer->max.streams)
+ goto fail_snd_card_ctl;
+
+ AlsaLoopSubdevT * subdev;
+
+ // Create AVIRT streams
+ switch (json_object_get_type(streamsJ)) {
+ case json_type_object:
+ subdev = ProcessOneAvirtSubdev(mixer, loop, streamsJ, 0);
+ if (!subdev)
+ goto fail_loop_subdev;
+ loop->nbSubdevs++;
+ cds_list_add_tail(&subdev->list, &loop->subdevs.list);
+ break;
+ case json_type_array: {
+ int nbSubDevs = (int) json_object_array_length(streamsJ);
+ if (nbSubDevs >= mixer->max.streams)
+ goto fail_loop_subdev;
+ for (int idx = 0; idx < nbSubDevs; idx++) {
+ json_object *streamJ = json_object_array_get_idx(streamsJ, idx);
+ subdev = ProcessOneAvirtSubdev(mixer, loop, streamJ, idx);
+ if (!subdev) {
+ goto fail_loop_subdev;
+ }
+ loop->nbSubdevs++;
+ cds_list_add_tail(&subdev->list, &loop->subdevs.list);
+ }
+ break;
+ }
+ default:
+ goto fail_snd_card_ctl;
+ }
+
+ snd_avirt_card_seal();
+
+ // try to open sound card control interface
+ loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard);
+ if (!loop->sndcard->ctl) {
+ AFB_API_ERROR(mixer->api, "%s mixer=%s hal=%s Fail open sndcard loop=%s devpath=%s cardid=%s (please check 'modprobe snd_aloop')",
+ __func__, mixer->uid, uid, loop->uid, loop->sndcard->cid.devpath, loop->sndcard->cid.cardid);
+ goto fail_snd_card;
+ }
}
AFB_API_NOTICE(mixer->api, "%s, uid %s DONE", __func__, uid);
@@ -258,9 +421,9 @@ static void loopDestroy(SoftMixerT * mixer, void* arg) {
}
-static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ, json_object *streamsJ) {
- AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ);
+ AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ, streamsJ);
if (!newLoop) {
goto fail;
}
@@ -276,7 +439,7 @@ fail:
return newLoop;
}
-PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid, json_object * argsJ) {
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid, json_object * argsJ, json_object *streamsJ) {
AlsaSndLoopT * newLoop = NULL;
@@ -289,7 +452,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid,
size_t count;
case json_type_object:
- newLoop = loopCreate(mixer, uid, argsJ);
+ newLoop = loopCreate(mixer, uid, argsJ, streamsJ);
if (!newLoop) {
goto fail;
}
@@ -304,7 +467,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid,
for (int idx = 0; idx < count; idx++) {
json_object *loopJ = json_object_array_get_idx(argsJ, idx);
- newLoop = loopCreate(mixer, uid, loopJ);
+ newLoop = loopCreate(mixer, uid, loopJ, streamsJ);
if (newLoop == NULL) {
goto fail_loop;
}
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index e892b2f..ebb8c25 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -27,6 +27,9 @@ static struct cds_list_head mixerList;
static void MixerDelete(SoftMixerT * mixer);
+// AVIRT: temporary loops JSON
+static json_object *LoopsJ = NULL;
+
static void MixerExit() {
SoftMixerT *mixer, *tmp;
printf("%s !\n", __func__);
@@ -575,13 +578,6 @@ STATIC void MixerAttachVerb(afb_req_t request) {
mixer->transaction = transaction;
- AFB_API_INFO(mixer->api, "%s set LOOPS", __func__);
-
- if (loopsJ) {
- error = ApiLoopAttach(mixer, request, uid, loopsJ);
- if (error) goto fail;
- }
-
AFB_API_INFO(mixer->api, "%s set PLAYBACKS", __func__);
if (playbacksJ) {
@@ -616,6 +612,16 @@ STATIC void MixerAttachVerb(afb_req_t request) {
json_object_object_add(responseJ, "zone", resultJ);
}
+ // In AVIRT mode, we require both the loops and streams JSON objects to
+ // construct the loopbacks, so when the loops are set, but the streams
+ // are not, we need to save the loops until the streams are given to us
+ if (streamsJ && (loopsJ || LoopsJ)) {
+ AFB_API_INFO(mixer->api, "%s set LOOPS/AVIRT", __func__);
+ error = ApiLoopAttach(mixer, request, uid,
+ ((loopsJ) ? loopsJ : LoopsJ), streamsJ);
+ if (error) goto fail_loop;
+ }
+
AFB_API_INFO(mixer->api, "%s set RAMPS", __func__);
if (rampsJ) {
@@ -657,12 +663,12 @@ fail_ramp:
// TODO remove created ramps
fail_zone:
// TODO remove created zone
+fail_loop:
+ // TODO remove created loops
fail_source:
// TODO remove created sources
fail_sink:
// TODO remove created sinks
-fail_loop:
- // TODO remove created loops
fail:
if (mixer->transaction)
@@ -734,9 +740,14 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) {
if (error) goto OnErrorExit;
}
- if (loopsJ) {
- error = ApiLoopAttach(mixer, NULL, uid, loopsJ);
+ // In AVIRT mode, we require both the loops and streams JSON objects to
+ // construct the loopbacks, so when the loops are set, but the streams
+ // are not, we need to save the loops until the streams are given to us
+ if (loopsJ && streamsJ) {
+ error = ApiLoopAttach(mixer, NULL, uid, loopsJ, streamsJ);
if (error) goto OnErrorExit;
+ } else {
+ LoopsJ = loopsJ;
}
if (zonesJ) {
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index c1fb749..edf506e 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -19,6 +19,7 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
+#include <avirt/avirt.h>
#include <string.h>
#include <stdbool.h>
@@ -231,6 +232,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
char *volName = NULL;
int pauseNumid = 0;
int volNumid = 0;
+ int device, subdev;
AFB_API_DEBUG(mixer->api,
"NEW STREAM stream %s %s, source %s, sink %s, mute %d",
@@ -238,12 +240,20 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
loopDev = ApiLoopFindSubdev(mixer, stream->uid, stream->source, &loop);
if (loopDev) {
+ if (loop->avirt) {
+ device = loopDev->index;
+ subdev = 0;
+ } else { // loop->avirt == false
+ device = loop->capture;
+ subdev = loopDev->index;
+ }
+
// create a valid PCM reference and try to open it.
captureDev->devpath = NULL;
captureDev->cardid = NULL;
captureDev->cardidx = loop->sndcard->cid.cardidx;
- captureDev->device = loop->capture;
- captureDev->subdev = loopDev->index;
+ captureDev->device = device;
+ captureDev->subdev = subdev;
captureDev->pcmplug_params = NULL;
captureCard = loop->sndcard;
@@ -476,11 +486,10 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
}
if (loop) {
- if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, loop->playback, capturePcm->cid.subdev) == -1) {
- SOFTMIXER_NOMEM(mixer->api);
- goto OnErrorExit;
- }
-
+ int device = (loop->avirt) ? captureDev->device
+ : loop->playback;
+ if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, device, capturePcm->cid.subdev) == -1)
+ goto OnErrorExit;
} else {
if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, captureDev->device, captureDev->subdev) == -1) {
SOFTMIXER_NOMEM(mixer->api);
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index b6c0e5b..2fb9370 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -242,7 +242,7 @@ typedef struct {
typedef struct {
char * uid;
- int index;
+ int index; // AVIRT: parent PCM index (Since subdev idx is always 0)
int numid;
struct cds_list_head list;
} AlsaLoopSubdevT;
@@ -251,10 +251,11 @@ typedef struct {
struct SoftMixerT_;
typedef struct AlsaSndLoopT {
+ bool avirt; // AVIRT: Is this loop AVIRT?
const char *uid;
struct SoftMixerT_ * mixer; /* owner */
- int playback;
- int capture;
+ int playback; // AVIRT: UNUSED
+ int capture; // AVIRT: UNUSED
AlsaSndCtlT *sndcard;
int nbSubdevs;
AlsaLoopSubdevT subdevs;
@@ -368,7 +369,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS
// alsa-api-*
-PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char *, json_object * argsJ);
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char *, json_object * argsJ, json_object *streamsJ);
PUBLIC int ApiSourceAttach(SoftMixerT *mixer, afb_req_t request, const char *, json_object * argsJ);
PUBLIC int ApiSinkAttach(SoftMixerT *mixer, afb_req_t request, const char *, json_object * argsJ);
PUBLIC int ApiStreamAttach(SoftMixerT *mixer, afb_req_t request, const char * uid, const char *prefix, json_object * argsJ);