aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Bultel <thierry.bultel@iot.bzh>2018-12-04 23:11:20 +0100
committerThierry Bultel <thierry.bultel@iot.bzh>2018-12-18 12:12:33 +0100
commit23cda61198d29010f5a86116b88b8b48eea5e797 (patch)
tree0f36dbc9452f31c34d39b72d0e3e10d5afaaf71e
parent6f898395879f5b3846d3eef5a16c4d5bff9aa171 (diff)
Add support for bluetooth telephony
This adds support for bluetooth telephony. A big rework in the softmixer internals has been mandatory, in order to support dynamic streams creation and deletions. Bluetooth telephony relies on the recent evolutions of bluez-alsa, the most important one being the support of HFP over Ofono. The softmixer opens PCM ioplugs provided by bluez-alsa. Bluetooth SCO needs 2 streams, one for listening and the other for talking. These streams are created upon requests sent by the hal-manager. The hal manager subscribes for bluez-alsa events and request the list of availalble transports. For each "attach" transaction verb, the softmixer maintains a list of the all created objects (sources, sinks, zones, ramps, streams, and more) Additionnally, it creates a new verb when the attach succeeds, that verb is typically something like "sco_XX:XX:XX:XX:XX:XX", and the only supported action at the present time is {"action":"remove"}, that performs all the cleanup of the registered objects. Change-Id: I1b119e6c079e60daf771e63c083a1ef33a39f379 Signed-off-by: Thierry Bultel <thierry.bultel@iot.bzh>
-rw-r--r--plugins/alsa/alsa-api-loop.c239
-rw-r--r--plugins/alsa/alsa-api-mixer.c342
-rw-r--r--plugins/alsa/alsa-api-pcm.c357
-rw-r--r--plugins/alsa/alsa-api-ramp.c86
-rw-r--r--plugins/alsa/alsa-api-sink.c149
-rw-r--r--plugins/alsa/alsa-api-source.c68
-rw-r--r--plugins/alsa/alsa-api-streams.c279
-rw-r--r--plugins/alsa/alsa-api-zones.c207
-rw-r--r--plugins/alsa/alsa-bluez.c71
-rw-r--r--plugins/alsa/alsa-bluez.h29
-rw-r--r--plugins/alsa/alsa-core-ctl.c196
-rw-r--r--plugins/alsa/alsa-core-pcm.c300
-rw-r--r--plugins/alsa/alsa-effect-ramp.c36
-rw-r--r--plugins/alsa/alsa-plug-dmix.c16
-rw-r--r--plugins/alsa/alsa-plug-rate.c26
-rw-r--r--plugins/alsa/alsa-plug-route.c213
-rw-r--r--plugins/alsa/alsa-plug-vol.c23
-rw-r--r--plugins/alsa/alsa-ringbuf.c6
-rw-r--r--plugins/alsa/alsa-softmixer.h142
-rw-r--r--plugins/alsa/alsa-transaction.c111
-rw-r--r--plugins/alsa/alsa-transaction.h37
-rw-r--r--plugins/alsa/alsa-utils-bypath.c148
-rw-r--r--plugins/alsa/alsa-utils-dump.c17
-rw-r--r--plugins/alsa/ringbuf.c2
24 files changed, 2070 insertions, 1030 deletions
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
index 61bdc99..55ab8a8 100644
--- a/plugins/alsa/alsa-api-loop.c
+++ b/plugins/alsa/alsa-api-loop.c
@@ -24,31 +24,42 @@
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
+ AlsaSndLoopT * _loop;
if (targetUid) {
- for (int idx = 0; mixer->loops[idx]; idx++) {
- for (int jdx = 0; jdx < mixer->loops[idx]->scount; jdx++) {
- if (mixer->loops[idx]->subdevs[jdx]->uid && !strcasecmp(mixer->loops[idx]->subdevs[jdx]->uid, targetUid)) {
- *loop = mixer->loops[idx];
- return mixer->loops[idx]->subdevs[jdx];
- }
- }
- }
+ cds_list_for_each_entry(_loop, &mixer->loops.list, list) {
+ AlsaLoopSubdevT * subdev;
+ cds_list_for_each_entry(subdev, &_loop->subdevs.list, list) {
+ if (subdev->uid && !strcmp(subdev->uid, targetUid)) {
+ *loop = _loop;
+ return subdev;
+ }
+ }
+ }
} else {
- for (int idx = 0; mixer->loops[idx]; idx++) {
- for (int jdx = 0; mixer->loops[idx]->subdevs[jdx]; jdx++) {
- if (!mixer->loops[idx]->subdevs[jdx]->uid) {
- mixer->loops[idx]->subdevs[jdx]->uid = streamUid;
- *loop = mixer->loops[idx];
- return mixer->loops[idx]->subdevs[jdx];
- }
- }
- }
+ cds_list_for_each_entry(_loop, &mixer->loops.list, list) {
+ AlsaLoopSubdevT * subdev;
+ cds_list_for_each_entry(subdev, &_loop->subdevs.list, list) {
+ if (!subdev->uid) {
+ subdev->uid = streamUid;
+ *loop = _loop;
+ return subdev;
+ }
+ }
+ }
}
return NULL;
}
+
STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *subdevJ) {
- AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaPcmCtlT));
+
+ AFB_ApiDebug(mixer->api, "%s", __func__);
+
+ AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaLoopSubdevT));
+ if (subdev == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
int error = wrap_json_unpack(subdevJ, "{s?s, si,si !}"
, "uid", &subdev->uid
@@ -56,12 +67,20 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop,
, "numid", &subdev->numid
);
if (error) {
- AFB_ApiError(mixer->api, "ProcessOneSubdev: loop=%s missing (uid|subdev|numid) error=%s json=%s", loop->uid, wrap_json_get_error_string(error),json_object_get_string(subdevJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api,
+ "%s: loop=%s missing (uid|subdev|numid) error=%s json=%s",
+ __func__, loop->uid, wrap_json_get_error_string(error),json_object_get_string(subdevJ));
+ goto fail_subdev;
}
// subdev with no UID are dynamically attached
- if (subdev->uid) subdev->uid = strdup(subdev->uid);
+ if (subdev->uid) {
+ subdev->uid = strdup(subdev->uid);
+ if (subdev->uid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_subdev;
+ }
+ }
// create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard
AlsaDevInfoT loopSubdev;
@@ -73,25 +92,61 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop,
loopSubdev.subdev = subdev->index;
// assert we may open this loopback subdev in capture mode
- AlsaPcmCtlT *pcmInfo = AlsaByPathOpenPcm(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE);
- if (!pcmInfo) goto OnErrorExit;
+ 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
- snd_pcm_close(pcmInfo->handle);
- free(pcmInfo);
+ AlsaPcmCtlDelete(mixer, pcmCtl);
+
+ AFB_ApiDebug(mixer->api, "%s DONE", __func__);
return subdev;
-OnErrorExit:
+fail_subdev_uid:
+ free((char*)subdev->uid);
+fail_subdev:
+ free(subdev);
+fail:
return NULL;
}
+static void freeSubdev(AlsaLoopSubdevT * subdev) {
+ if (subdev->uid)
+ free((char*)subdev->uid);
+ free(subdev);
+}
+
+
STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
- AlsaSndLoopT *loop = calloc(1, sizeof (AlsaSndLoopT));
+
json_object *subdevsJ = NULL, *devicesJ = NULL;
int error;
- loop->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
+ AFB_ApiNotice(mixer->api, "%s, uid %s", __func__, uid);
+
+ AlsaSndLoopT *loop = calloc(1, sizeof (AlsaSndLoopT));
+ if (loop == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ CDS_INIT_LIST_HEAD(&loop->list);
+ CDS_INIT_LIST_HEAD(&loop->subdevs.list);
+
+ AlsaSndCtlT * sndctl = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
+ if (sndctl == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_loop;
+ }
+
+ CDS_INIT_LIST_HEAD(&sndctl->registryList);
+
+ loop->sndcard = sndctl;
+
error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,so,so !}"
, "uid", &loop->uid
, "path", &loop->sndcard->cid.devpath
@@ -102,7 +157,7 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje
if (error || !loop->uid || !subdevsJ || (!loop->sndcard->cid.devpath && !loop->sndcard->cid.cardid)) {
AFB_ApiNotice(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 OnErrorExit;
+ goto fail_snd_card;
}
// try to open sound card control interface
@@ -110,10 +165,10 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje
if (!loop->sndcard->ctl) {
AFB_ApiError(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 OnErrorExit;
+ goto fail_snd_card;
}
- // Default devices is payback=0 capture=1
+ // Default devices is playback=0 capture=1
if (!devicesJ) {
loop->playback = 0;
loop->capture = 1;
@@ -122,87 +177,141 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje
if (error) {
AFB_ApiNotice(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 OnErrorExit;
+ goto fail_snd_card_ctl;
}
}
+ AlsaLoopSubdevT * subdev;
+
switch (json_object_get_type(subdevsJ)) {
case json_type_object:
- loop->scount = 1;
- loop->subdevs = calloc(2, sizeof (void*));
- loop->subdevs[0] = ProcessOneSubdev(mixer, loop, subdevsJ);
- if (!loop->subdevs[0]) goto OnErrorExit;
+ 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:
- loop->scount = (int) json_object_array_length(subdevsJ);
- loop->subdevs = calloc(loop->scount + 1, sizeof (void*));
- for (int idx = 0; idx < loop->scount; idx++) {
+ 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);
- loop->subdevs[idx] = ProcessOneSubdev(mixer, loop, subdevJ);
- if (!loop->subdevs[idx]) goto OnErrorExit;
+ subdev = ProcessOneSubdev(mixer, loop, subdevJ);
+ if (!subdev) {
+ goto fail_loop_subdev;
+ }
+ loop->nbSubdevs++;
+ cds_list_add_tail(&subdev->list, &loop->subdevs.list);
}
break;
+ }
default:
AFB_ApiError(mixer->api, "%s mixer=%s hal=%s Loop=%s invalid subdevs= %s",
__func__, mixer->uid, uid, loop->uid, json_object_get_string(subdevsJ));
- goto OnErrorExit;
+ goto fail_snd_card_ctl;
}
- // we may have to register up to 3 control per subdevice (vol, pause, actif)
- loop->sndcard->registry = calloc(loop->scount * SMIXER_SUBDS_CTLS + 1, sizeof (RegistryEntryPcmT));
- loop->sndcard->rcount = loop->scount*SMIXER_SUBDS_CTLS;
+ AFB_ApiNotice(mixer->api, "%s, uid %s DONE", __func__, uid);
return loop;
-OnErrorExit:
+fail_loop_subdev: {
+ AFB_ApiDebug(mixer->api, "%s cleanup", __func__);
+ AlsaLoopSubdevT * subdev, *tmp;
+ cds_list_for_each_entry_safe(subdev, tmp, &loop->subdevs.list, list) {
+ cds_list_del(&subdev->list);
+ freeSubdev(subdev);
+ }
+}
+
+fail_snd_card_ctl:
+ snd_ctl_close(loop->sndcard->ctl);
+fail_snd_card:
+ free(loop->sndcard);
+fail_loop:
+ free(loop);
+fail:
return NULL;
}
-PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+static void loopDestroy(SoftMixerT * mixer, void* arg) {
+ AlsaSndLoopT * loop = (AlsaSndLoopT*) arg;
+ AFB_ApiDebug(mixer->api, "%s... %s not implemented", __func__, loop->uid);
+ return;
- int index;
- for (index = 0; index < mixer->max.loops; index++) {
- if (!mixer->loops[index]) break;
- }
+ if (loop->sndcard) {
+ if (loop->sndcard->ctl) {
+ snd_ctl_close(loop->sndcard->ctl);
+ free(loop->sndcard->ctl);
+ }
+ free(loop->sndcard);
+ }
+
+ AlsaLoopSubdevT * subdev, *tmp;
+ cds_list_for_each_entry_safe(subdev, tmp, &loop->subdevs.list, list) {
+ cds_list_del(&subdev->list);
+ freeSubdev(subdev);
+ }
+ mixer->nbLoops--;
+ free(loop);
+
+}
+
+static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+ AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ);
+ if (!newLoop) {
+ goto fail;
+ }
+
+ mixer->nbLoops++;
+ cds_list_add_tail(&newLoop->list, &mixer->loops.list);
+ AlsaMixerTransactionObjectAdd(mixer->transaction, newLoop, loopDestroy);
- if (index == mixer->max.loops) {
+fail:
+ return newLoop;
+}
+
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) {
+
+ AlsaSndLoopT * newLoop = NULL;
+
+ if (mixer->nbLoops >= mixer->max.loops) {
AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops);
- goto OnErrorExit;
+ goto fail;
}
switch (json_object_get_type(argsJ)) {
size_t count;
case json_type_object:
- mixer->loops[index] = AttachOneLoop(mixer, uid, argsJ);
- if (!mixer->loops[index]) {
- goto OnErrorExit;
+ newLoop = loopCreate(mixer, uid, argsJ);
+ if (!newLoop) {
+ goto fail;
}
break;
case json_type_array:
count = json_object_array_length(argsJ);
- if (count > (mixer->max.loops - index)) {
+ if (count > (mixer->max.loops - mixer->nbLoops)) {
AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops);
- goto OnErrorExit;
-
+ goto fail_loop;
}
for (int idx = 0; idx < count; idx++) {
json_object *loopJ = json_object_array_get_idx(argsJ, idx);
- mixer->loops[index + idx] = AttachOneLoop(mixer, uid, loopJ);
- if (!mixer->loops[index + idx]) {
- goto OnErrorExit;
+ newLoop = loopCreate(mixer, uid, loopJ);
+ if (newLoop == NULL) {
+ goto fail_loop;
}
}
break;
default:
AFB_IfReqFailF(mixer, request, "bad-loop", "mixer=%s hal=%s loops invalid argsJ= %s", mixer->uid, uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
return 0;
-OnErrorExit:
+fail_loop:
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index 171046b..072026e 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -19,58 +19,15 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
-#include "alsa-bluez.h"
#include <string.h>
#include <pthread.h>
-extern Lua2cWrapperT Lua2cWrap;
static void MixerRemoveVerb(AFB_ReqT request) {
- SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
- int error;
-
- for (int idx = 0; mixer->streams[idx]->uid; idx++) {
- AlsaStreamAudioT *stream = mixer->streams[idx];
- AlsaPcmCopyHandleT * copy = stream->copy;
-
- AFB_ApiNotice(mixer->api, "cleaning mixer=%s stream=%s", mixer->uid, stream->uid);
-
- error = pthread_cancel(stream->copy->rthread);
- if (error) {
- AFB_ReqFailF(request, "internal-error", "Fail to kill audio-stream threads mixer=%s", mixer->uid);
- goto OnErrorExit;
- }
-
- error = afb_api_del_verb(mixer->api, stream->uid, (void**)&mixer);
- if (error) {
- AFB_ReqFailF(request, "internal-error", "Mixer=%s fail to remove verb=%s error=%s", mixer->uid, stream->uid, strerror(error));
- goto OnErrorExit;
- }
-
- // free audio-stream dynamic structures
- snd_pcm_close(copy->pcmIn->handle);
- snd_pcm_close(copy->pcmOut->handle);
- if (copy->evtsrc) sd_event_source_unref(copy->evtsrc);
- if (copy->sdLoop) sd_event_unref(copy->sdLoop);
- }
-
- // // (Fulup to be Done) registry is attached to source
- // if (mixer->sources) ApiSourcFree (mixer);
- // if (mixer->sinks) ApiSinkFree (mixer);
- // if (mixer->loops) ApiLoopFree (mixer);
- // if (mixer->ramps) ApiRampFree (mixer);
- // if (mixer->zones) ApiZoneFree (mixer);
-
- // finally free mixer handle
- free(mixer);
- AFB_ReqSuccess(request, NULL, "Fulup: delete might not clean everything properly");
-
+ AFB_ReqFailF(request, "internal-error", "Function not implemented");
return;
-
-OnErrorExit:
- AFB_ReqFail(request, "internal-error", "fail to delete mixer");
}
STATIC json_object *MixerInfoOneStream(AlsaStreamAudioT *stream, int verbose) {
@@ -100,8 +57,7 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ,
const char * key;
json_object *valueJ;
json_object *responseJ = NULL;
- AlsaStreamAudioT **streams = mixer->streams;
-
+ AlsaStreamAudioT * stream;
switch (json_object_get_type(streamsJ)) {
@@ -109,17 +65,18 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ,
case json_type_boolean:
// list every existing stream
responseJ = json_object_new_array();
- for (int idx = 0; streams[idx]; idx++) {
- valueJ = MixerInfoOneStream(streams[idx], verbose);
+ cds_list_for_each_entry(stream, &mixer->streams.list, list) {
+ valueJ = MixerInfoOneStream(stream, verbose);
json_object_array_add(responseJ, valueJ);
}
break;
case json_type_string:
key = json_object_get_string(streamsJ);
- for (int idx = 0; streams[idx]; idx++) {
- if (strcasecmp(streams[idx]->uid, key)) continue;
- responseJ = MixerInfoOneStream(streams[idx], verbose);
+ cds_list_for_each_entry(stream, &mixer->streams.list, list) {
+ if (strcasecmp(stream->uid, key))
+ continue;
+ responseJ = MixerInfoOneStream(stream, verbose);
break;
}
break;
@@ -132,9 +89,10 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ,
__func__, json_object_get_string(streamsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error));
goto OnErrorExit;
}
- for (int idx = 0; streams[idx]; idx++) {
- if (strcasecmp(streams[idx]->uid, key)) continue;
- responseJ = MixerInfoOneStream(streams[idx], verbose);
+ cds_list_for_each_entry(stream, &mixer->streams.list, list) {
+ if (strcasecmp(stream->uid, key))
+ continue;
+ responseJ = MixerInfoOneStream(stream, verbose);
break;
}
break;
@@ -147,8 +105,8 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ,
valueJ = MixerInfoStreams(mixer, streamJ, verbose);
if (!valueJ) {
AFB_ApiError(mixer->api,
- "%s: fail to find stream=%s",
- __func__, json_object_get_string(streamsJ));
+ "%s: failed to find stream=%s",
+ __func__, json_object_get_string(streamJ));
goto OnErrorExit;
}
json_object_array_add(responseJ, valueJ);
@@ -156,7 +114,7 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ,
break;
default:
- AFB_ApiError(mixer->api, "MixerInfoStreams: unsupported json type streamsJ=%s", json_object_get_string(streamsJ));
+ AFB_ApiError(mixer->api, "%s: unsupported json type streamsJ=%s", __func__, json_object_get_string(streamsJ));
goto OnErrorExit;
}
@@ -187,7 +145,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v
const char * key;
json_object *valueJ;
json_object *responseJ = NULL;
- AlsaVolRampT **ramps = mixer->ramps;
+ AlsaVolRampT * ramp;
switch (json_object_get_type(rampsJ)) {
@@ -195,18 +153,18 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v
case json_type_boolean:
// list every existing ramp
responseJ = json_object_new_array();
- for (int idx = 0; ramps[idx]; idx++) {
+ cds_list_for_each_entry(ramp, &mixer->ramps.list, list) {
- valueJ = MixerInfoOneRamp(ramps[idx], verbose);
+ valueJ = MixerInfoOneRamp(ramp, verbose);
json_object_array_add(responseJ, valueJ);
}
break;
case json_type_string:
key = json_object_get_string(rampsJ);
- for (int idx = 0; ramps[idx]; idx++) {
- if (strcasecmp(ramps[idx]->uid, key)) continue;
- responseJ = MixerInfoOneRamp(ramps[idx], verbose);
+ cds_list_for_each_entry(ramp, &mixer->ramps.list, list) {
+ if (strcasecmp(ramp->uid, key)) continue;
+ responseJ = MixerInfoOneRamp(ramp, verbose);
break;
}
break;
@@ -214,12 +172,13 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v
case json_type_object:
error = wrap_json_unpack(rampsJ, "{ss}", "uid", &key);
if (error) {
- AFB_ApiError(mixer->api, "MixerInfoRamps: missing 'uid' request rampJ=%s error=%s position=%d", json_object_get_string(rampsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error));
+ AFB_ApiError(mixer->api, "%s: missing 'uid' request rampJ=%s error=%s position=%d",
+ __func__, json_object_get_string(rampsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error));
goto OnErrorExit;
}
- for (int idx = 0; ramps[idx]; idx++) {
- if (strcasecmp(ramps[idx]->uid, key)) continue;
- responseJ = MixerInfoOneRamp(ramps[idx], verbose);
+ cds_list_for_each_entry(ramp, &mixer->ramps.list, list) {
+ if (strcasecmp(ramp->uid, key)) continue;
+ responseJ = MixerInfoOneRamp(ramp, verbose);
break;
}
break;
@@ -231,7 +190,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v
valueJ = MixerInfoRamps(mixer, rampJ, verbose);
if (!valueJ) {
- AFB_ApiError(mixer->api, "MixerInfoRamps: fail to find ramp=%s", json_object_get_string(rampsJ));
+ AFB_ApiError(mixer->api, "%s: fail to find ramp=%s", __func__, json_object_get_string(rampsJ));
goto OnErrorExit;
}
json_object_array_add(responseJ, valueJ);
@@ -239,7 +198,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v
break;
default:
- AFB_ApiError(mixer->api, "MixerInfoRamps: unsupported json type rampsJ=%s", json_object_get_string(rampsJ));
+ AFB_ApiError(mixer->api, "%s: unsupported json type rampsJ=%s", __func__, json_object_get_string(rampsJ));
goto OnErrorExit;
}
@@ -252,30 +211,34 @@ OnErrorExit:
STATIC json_object *MixerInfoOneZone(AlsaSndZoneT *zone, int verbose) {
json_object *responseJ;
+ AlsaPcmChannelT * channel;
+
if (!verbose) {
wrap_json_pack(&responseJ, "{ss}", "uid", zone->uid);
} else {
json_object *responseJ = json_object_new_object();
- if (zone->sinks) {
+ if (zone->nbSinks > 0) {
json_object *sinksJ = json_object_new_array();
- for (int jdx = 0; zone->sinks[jdx]; jdx++) {
+
+ cds_list_for_each_entry(channel, &zone->sinks.list, list) {
json_object *channelJ;
wrap_json_pack(&channelJ, "{ss,si}"
- , "uid", zone->sinks[jdx]->uid
- , "port", zone->sinks[jdx]->port
+ , "uid", channel->uid
+ , "port", channel->port
);
json_object_array_add(sinksJ, channelJ);
}
json_object_object_add(responseJ, "sinks", sinksJ);
}
- if (zone->sources) {
+ if (zone->nbSources > 0) {
json_object *sourcesJ = json_object_new_array();
- for (int jdx = 0; zone->sources[jdx]; jdx++) {
+
+ cds_list_for_each_entry(channel, &zone->sources.list, list) {
json_object *channelJ;
wrap_json_pack(&channelJ, "{ss,si}"
- , "uid", zone->sources[jdx]->uid
- , "port", zone->sources[jdx]->port
+ , "uid", channel->uid
+ , "port", channel->port
);
json_object_array_add(sourcesJ, channelJ);
}
@@ -300,7 +263,8 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v
const char * key;
json_object *valueJ;
json_object *responseJ = NULL;
- AlsaSndZoneT **zones = mixer->zones;
+
+ AlsaSndZoneT *zone;
switch (json_object_get_type(zonesJ)) {
@@ -308,18 +272,18 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v
case json_type_boolean:
// list every existing zone
responseJ = json_object_new_array();
- for (int idx = 0; zones[idx]; idx++) {
-
- valueJ = MixerInfoOneZone(zones[idx], verbose);
+ cds_list_for_each_entry(zone, &mixer->zones.list, list) {
+ valueJ = MixerInfoOneZone(zone, verbose);
json_object_array_add(responseJ, valueJ);
}
break;
case json_type_string:
key = json_object_get_string(zonesJ);
- for (int idx = 0; zones[idx]; idx++) {
- if (strcasecmp(zones[idx]->uid, key)) continue;
- responseJ = MixerInfoOneZone(zones[idx], verbose);
+ cds_list_for_each_entry(zone, &mixer->zones.list, list) {
+ if (strcasecmp(zone->uid, key))
+ continue;
+ responseJ = MixerInfoOneZone(zone, verbose);
break;
}
break;
@@ -332,9 +296,9 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v
__func__ ,json_object_get_string(zonesJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error));
goto OnErrorExit;
}
- for (int idx = 0; zones[idx]; idx++) {
- if (strcasecmp(zones[idx]->uid, key)) continue;
- responseJ = MixerInfoOneZone(zones[idx], verbose);
+ cds_list_for_each_entry(zone, &mixer->zones.list, list) {
+ if (strcasecmp(zone->uid, key)) continue;
+ responseJ = MixerInfoOneZone(zone, verbose);
break;
}
break;
@@ -346,7 +310,7 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v
valueJ = MixerInfoZones(mixer, zoneJ, verbose);
if (!valueJ) {
- AFB_ApiError(mixer->api, "MixerInfoZones: fail to find zone=%s", json_object_get_string(zonesJ));
+ AFB_ApiError(mixer->api, "%s: fail to find zone=%s", __func__, json_object_get_string(zonesJ));
goto OnErrorExit;
}
json_object_array_add(responseJ, valueJ);
@@ -354,11 +318,11 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v
break;
default:
- AFB_ApiError(mixer->api, "MixerInfoZones: unsupported json type zonesJ=%s", json_object_get_string(zonesJ));
+ AFB_ApiError(mixer->api, "%s: unsupported json type zonesJ=%s", __func__, json_object_get_string(zonesJ));
goto OnErrorExit;
}
- AFB_ApiNotice(mixer->api, "MixerInfoZones: response=%s", json_object_get_string(responseJ));
+ AFB_ApiNotice(mixer->api, "%s: response=%s", __func__, json_object_get_string(responseJ));
return (responseJ);
OnErrorExit:
@@ -384,7 +348,7 @@ STATIC json_object *MixerInfoOnePcm(AlsaSndPcmT *pcm, int verbose) {
wrap_json_pack(&alsaJ, "{ss,ss,so}"
, "volume", pcm->volume
, "mute", pcm->mute
- , "ccount", pcm->ccount
+ , "ccount", pcm->nbChannels
);
wrap_json_pack(&responseJ, "{ss,ss,so,so}"
, "uid", pcm->uid
@@ -401,18 +365,18 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm
const char * key;
json_object *valueJ;
json_object *responseJ = NULL;
- AlsaSndPcmT **pcms;
+ AlsaSndPcmT * pcms = NULL, *pcm;
switch (direction) {
case SND_PCM_STREAM_PLAYBACK:
- pcms = mixer->sinks;
+ pcms = &mixer->sinks;
break;
case SND_PCM_STREAM_CAPTURE:
- pcms = mixer->sources;
+ pcms = &mixer->sources;
break;
default:
- AFB_ApiError(mixer->api, "MixerInfoPcms: invalid Direction should be SND_PCM_STREAM_PLAYBACK|SND_PCM_STREAM_capture");
+ AFB_ApiError(mixer->api, "%s: invalid Direction should be SND_PCM_STREAM_PLAYBACK|SND_PCM_STREAM_capture", __func__);
goto OnErrorExit;
}
@@ -422,18 +386,18 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm
case json_type_boolean:
// list every existing pcm
responseJ = json_object_new_array();
- for (int idx = 0; pcms[idx]; idx++) {
-
- valueJ = MixerInfoOnePcm(pcms[idx], verbose);
+ cds_list_for_each_entry(pcm, &pcms->list, list) {
+ valueJ = MixerInfoOnePcm(pcm, verbose);
json_object_array_add(responseJ, valueJ);
}
break;
case json_type_string:
key = json_object_get_string(pcmsJ);
- for (int idx = 0; pcms[idx]; idx++) {
- if (strcasecmp(pcms[idx]->uid, key)) continue;
- responseJ = MixerInfoOnePcm(pcms[idx], verbose);
+ cds_list_for_each_entry(pcm, &pcms->list, list) {
+ if (strcasecmp(pcm->uid, key))
+ continue;
+ responseJ = MixerInfoOnePcm(pcm, verbose);
break;
}
break;
@@ -446,9 +410,10 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm
__func__, json_object_get_string(pcmsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error));
goto OnErrorExit;
}
- for (int idx = 0; pcms[idx]; idx++) {
- if (strcasecmp(pcms[idx]->uid, key)) continue;
- responseJ = MixerInfoOnePcm(pcms[idx], verbose);
+ cds_list_for_each_entry(pcm, &pcms->list, list) {
+ if (strcasecmp(pcm->uid, key))
+ continue;
+ responseJ = MixerInfoOnePcm(pcm, verbose);
break;
}
break;
@@ -547,6 +512,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
json_object *argsJ = afb_req_json(request);
json_object *responseJ = json_object_new_object();
int error;
+ AlsaMixerTransaction * transaction = NULL;
error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?o,s?o,s?o,s?o,s?o,s?o !}"
, "uid", &uid
@@ -562,35 +528,43 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (error) {
AFB_ReqFailF(request,
"invalid-syntax",
- "mixer=%s missing 'uid|ramps|playbacks|captures|zones|streams' error=%s args=%s",
- mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
+ "%s mixer=%s missing 'uid|ramps|playbacks|captures|zones|streams' error=%s args=%s",
+ __func__, mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto fail;
+ }
+
+ transaction = AlsaMixerTransactionNew(mixer, uid);
+ if (transaction == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
}
+ mixer->transaction = transaction;
+
AFB_ApiInfo(mixer->api, "%s set LOOPS", __func__);
if (loopsJ) {
error = ApiLoopAttach(mixer, request, uid, loopsJ);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
}
- AFB_ApiInfo(mixer->api, "%s set PLAYBACK", __func__);
+ AFB_ApiInfo(mixer->api, "%s set PLAYBACKS", __func__);
if (playbacksJ) {
error = ApiSinkAttach(mixer, request, uid, playbacksJ);
- if (error) goto OnErrorExit;
+ if (error) goto fail_loop;
json_object *resultJ = MixerInfoPcms(mixer, playbacksJ, SND_PCM_STREAM_PLAYBACK, 0);
json_object_object_add(responseJ, "playbacks", resultJ);
}
- AFB_ApiInfo(mixer->api, "%s set CAPTURE", __func__);
+ AFB_ApiInfo(mixer->api, "%s set CAPTURES", __func__);
if (capturesJ) {
error = ApiSourceAttach(mixer, request, uid, capturesJ);
if (error) {
AFB_ApiError(mixer->api,"%s: source attach failed", __func__);
- goto OnErrorExit;
+ goto fail_sink;
}
json_object *resultJ = MixerInfoPcms(mixer, capturesJ, SND_PCM_STREAM_CAPTURE, 0);
@@ -601,7 +575,8 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (zonesJ) {
error = ApiZoneAttach(mixer, request, uid, zonesJ);
- if (error) goto OnErrorExit;
+ if (error)
+ goto fail_source;
json_object *resultJ = MixerInfoZones(mixer, zonesJ, 0);
json_object_object_add(responseJ, "zone", resultJ);
@@ -611,7 +586,8 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (rampsJ) {
error = ApiRampAttach(mixer, request, uid, rampsJ);
- if (error) goto OnErrorExit;
+ if (error)
+ goto fail_zone;
json_object *resultJ = MixerInfoRamps(mixer, rampsJ, 0);
json_object_object_add(responseJ, "ramps", resultJ);
@@ -621,71 +597,54 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (streamsJ) {
error = ApiStreamAttach(mixer, request, uid, prefix, streamsJ);
- if (error) goto OnErrorExit;
+ if (error)
+ goto fail_ramp;
json_object *resultJ = MixerInfoStreams(mixer, streamsJ, 0);
json_object_object_add(responseJ, "streams", resultJ);
}
+ error = afb_api_add_verb(mixer->api, uid, "Post Attach API", AlsaMixerTransactionVerbCB, transaction, NULL, 0, 0);
+ if (error) {
+ AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to register post attach Verb ",
+ __func__, mixer->uid, uid);
+ goto fail_stream;
+ }
+
AFB_ApiNotice(mixer->api, "%s responseJ=%s", __func__, json_object_get_string(responseJ));
AFB_ReqSuccess(request, responseJ, NULL);
AFB_ApiInfo(mixer->api,"%s DONE", __func__);
return;
-OnErrorExit:
+fail_stream:
+ // TODO remove created streams
+fail_ramp:
+ // TODO remove created ramps
+fail_zone:
+ // TODO remove created zone
+fail_source:
+ // TODO remove created sources
+fail_sink:
+ // TODO remove created sinks
+fail_loop:
+ // TODO remove created loops
+fail:
+
+ if (mixer->transaction)
+ free(mixer->transaction);
+
AFB_ApiError(mixer->api,"%s FAILED", __func__);
return;
}
-static void MixerBluezAlsaDevVerb(AFB_ReqT request) {
- SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
- char * interface = NULL, *device = NULL, *profile = NULL;
- json_object *argsJ = afb_req_json(request);
- int error;
-
- if (json_object_is_type(argsJ,json_type_null)) {
- goto parsed;
- }
-
- error = wrap_json_unpack(argsJ, "{ss,ss,ss !}"
- , "interface", &interface
- , "device", &device
- , "profile", &profile
- );
-
- if (error) {
- AFB_ReqFailF(request,
- "invalid-syntax",
- "mixer=%s missing 'interface|device|profile' error=%s args=%s",
- mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
- }
-
-parsed:
- AFB_ApiNotice(mixer->api, "%s: interface %s, device %s, profile %s\n", __func__, interface, device, profile);
- error = alsa_bluez_set_remote_device(interface, device, profile);
- if (error) {
- AFB_ReqFailF(request,
- "runtime error",
- "Unable to set device , err %d", error);
- goto OnErrorExit;
- }
-
- AFB_ReqSuccess(request, NULL, NULL);
-
-OnErrorExit:
- return;
-}
-
// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT
STATIC AFB_ApiVerbs CtrlApiVerbs[] = {
/* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */
{ .verb = "attach", .callback = MixerAttachVerb, .info = "attach resources to mixer"},
{ .verb = "remove", .callback = MixerRemoveVerb, .info = "remove existing mixer streams, zones, ..."},
{ .verb = "info", .callback = MixerInfoVerb, .info = "list existing mixer streams, zones, ..."},
- { .verb = "bluezalsa_dev", .callback = MixerBluezAlsaDevVerb, .info = "set bluez alsa device"},
{ .verb = NULL} /* marker for end of the array */
};
@@ -704,6 +663,8 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) {
json_object *playbackJ = NULL, *captureJ = NULL, *zonesJ = NULL, *streamsJ = NULL, *rampsJ = NULL, *loopsJ = NULL;
const char* uid = source->uid, *prefix = NULL;
+ AlsaMixerTransaction * transaction = NULL;
+
int error;
error = wrap_json_unpack(argsJ, "{s?s, s?o,s?o,s?o,s?o,s?o,s?o !}"
@@ -716,10 +677,17 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) {
, "streams", &streamsJ
);
if (error) {
- AFB_ApiError(mixer->api, "MixerAttachVerb: invalid-syntax mixer=%s error=%s args=%s", mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ AFB_ApiError(mixer->api, "%s: invalid-syntax mixer=%s error=%s args=%s",
+ __func__, mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
goto OnErrorExit;
}
+ transaction = AlsaMixerTransactionNew(mixer, uid);
+ if (transaction == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
if (playbackJ) {
error = ApiSinkAttach(mixer, NULL, uid, playbackJ);
if (error) goto OnErrorExit;
@@ -759,6 +727,10 @@ OnErrorExit:
CTLP_CAPI(MixerCreate, source, argsJ, responseJ) {
SoftMixerT *mixer = calloc(1, sizeof (SoftMixerT));
+ if (mixer == NULL) {
+ SOFTMIXER_NOMEM(source->api);
+ goto fail;
+ }
source->context = mixer;
int error;
@@ -770,8 +742,8 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) {
mixer->max.ramps = SMIXER_DEFLT_RAMPS;
if (json_object_get_type(argsJ) != json_type_object) {
- AFB_ApiError(source->api, "_mixer_new_: invalid object type= %s", json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiError(source->api, "%s: invalid object type= %s", __func__, json_object_get_string(argsJ));
+ goto fail;
}
error = wrap_json_unpack(argsJ, "{ss,s?s,s?i,s?i,s?i,s?i,s?i,s?i !}"
@@ -785,31 +757,57 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) {
, "max_ramp", &mixer->max.ramps
);
if (error) {
- AFB_ApiNotice(source->api, "_mixer_new_ missing 'uid|max_loop|max_sink|max_source|max_zone|max_stream|max_ramp' error=%s mixer=%s", wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiNotice(source->api,
+ "%s missing 'uid|max_loop|max_sink|max_source|max_zone|max_stream|max_ramp' error=%s mixer=%s",
+ __func__, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto fail_mixer;
}
// make sure string do not get deleted
mixer->uid = strdup(mixer->uid);
- if (mixer->info)mixer->info = strdup(mixer->info);
-
- mixer->loops = calloc(mixer->max.loops + 1, sizeof (void*));
- mixer->sinks = calloc(mixer->max.sinks + 1, sizeof (void*));
- mixer->sources = calloc(mixer->max.sources + 1, sizeof (void*));
- mixer->zones = calloc(mixer->max.zones + 1, sizeof (void*));
- mixer->streams = calloc(mixer->max.streams + 1, sizeof (void*));
- mixer->ramps = calloc(mixer->max.ramps + 1, sizeof (void*));
+ if (mixer->uid == NULL) {
+ SOFTMIXER_NOMEM(source->api);
+ goto fail_mixer;
+ }
+ if (mixer->info) {
+ mixer->info = strdup(mixer->info);
+ if (mixer->info == NULL) {
+ SOFTMIXER_NOMEM(source->api);
+ goto fail_uid;
+ }
+ }
+
+ mixer->nbLoops = 0;
+ mixer->nbSinks = 0;
+ mixer->nbSources = 0;
+ mixer->nbZones = 0;
+ mixer->nbStreams = 0;
+ mixer->nbZones = 0;
+
+ CDS_INIT_LIST_HEAD(&mixer->loops.list);
+ CDS_INIT_LIST_HEAD(&mixer->sinks.list);
+ CDS_INIT_LIST_HEAD(&mixer->sources.list);
+ CDS_INIT_LIST_HEAD(&mixer->zones.list);
+ CDS_INIT_LIST_HEAD(&mixer->streams.list);
+ CDS_INIT_LIST_HEAD(&mixer->ramps.list);
mixer->sdLoop = AFB_GetEventLoop(source->api);
mixer->api = source->api;
afb_api_set_userdata(source->api, mixer);
error = LoadStaticVerbs(mixer, CtrlApiVerbs);
- if (error) goto OnErrorExit;
+ if (error)
+ goto fail_info;
return 0;
-OnErrorExit:
+fail_info:
+ free((char*)mixer->info);
+fail_uid:
+ free((char*)mixer->uid);
+fail_mixer:
+ free(mixer);
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c
index 1bba75c..cc2817d 100644
--- a/plugins/alsa/alsa-api-pcm.c
+++ b/plugins/alsa/alsa-api-pcm.c
@@ -51,17 +51,31 @@ typedef struct {
AlsaSndPcmT* pcm;
} apiVerbHandleT;
-STATIC AlsaPcmChannelT *ProcessOneChannel(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
+STATIC AlsaPcmChannelT * ProcessOneChannel(SoftMixerT *mixer, const char *uid, json_object *argsJ) {
AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT));
+ if (channel == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
int error = wrap_json_unpack(argsJ, "{ss,si !}", "uid", &channel->uid, "port", &channel->port);
- if (error) goto OnErrorExit;
+ if (error) {
+ AFB_ApiError(mixer->api, "%s: sndcard=%s channel: missing (uid||port) error=%s json=%s",
+ __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto fail_channel;
+ }
+
+ CDS_INIT_LIST_HEAD(&channel->list);
channel->uid = strdup(channel->uid);
+ if (channel->uid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_channel;
+ }
return channel;
-OnErrorExit:
- AFB_ApiError(mixer->api, "ProcessOneChannel: sndcard=%s channel: missing (uid||port) error=%s json=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+fail_channel:
free(channel);
+fail:
return NULL;
}
@@ -81,37 +95,45 @@ STATIC int PcmAttachOneCtl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, json_object
AFB_ApiError(mixer->api,
"%s: cardid=%s channel: missing (numid|name|value) error=%s json=%s",
__func__, sndcard->cid.name, wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
if (numid > 0) {
elemId = AlsaCtlGetNumidElemId(mixer, sndcard, numid);
if (!elemId) {
- AFB_ApiError(mixer->api, "PcmAttachOneCtl sndard=%s fail to find control numid=%d", sndcard->cid.cardid, numid);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s sndard=%s fail to find control numid=%d",
+ __func__, sndcard->cid.cardid, numid);
+ goto fail;
}
} else {
elemId = AlsaCtlGetNameElemId(mixer, sndcard, name);
if (!elemId) {
- AFB_ApiError(mixer->api, "PcmAttachOneCtl sndard=%s fail to find control name=%s", sndcard->cid.cardid, name);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s sndard=%s fail to find control name=%s",
+ __func__, sndcard->cid.cardid, name);
+ goto fail;
}
}
snd_ctl_elem_info_alloca(&elemInfo);
snd_ctl_elem_info_set_id(elemInfo, elemId);
control->name = strdup(snd_ctl_elem_info_get_name(elemInfo));
+ if (!control->name) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_elemId;
+ }
control->numid = snd_ctl_elem_info_get_numid(elemInfo);
if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) {
- AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not loadable", sndcard->cid.cardid, control->numid, control->name);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not loadable",
+ __func__, sndcard->cid.cardid, control->numid, control->name);
+ goto fail_control_name;
}
if (!snd_ctl_elem_info_is_writable(elemInfo)) {
- AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable",
+ __func__, sndcard->cid.cardid, control->numid, control->name);
+ goto fail_control_name;
}
control->count = snd_ctl_elem_info_get_count(elemInfo);
@@ -132,22 +154,25 @@ STATIC int PcmAttachOneCtl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, json_object
break;
default:
- AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' invalid/unsupported type=%d",
- sndcard->cid.cardid, control->numid, control->name, snd_ctl_elem_info_get_type(elemInfo));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' invalid/unsupported type=%d",
+ __func__, sndcard->cid.cardid, control->numid, control->name, snd_ctl_elem_info_get_type(elemInfo));
+ goto fail_control_name;
}
if (error) {
- AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable",
+ __func__, sndcard->cid.cardid, control->numid, control->name);
+ goto fail_control_name;
}
free(elemId);
-
return 0;
-OnErrorExit:
- if (elemId)free(elemId);
+fail_control_name:
+ free((char*)control->name);
+fail_elemId:
+ free(elemId);
+fail:
return -1;
}
@@ -161,7 +186,7 @@ STATIC int PcmSetControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaSndControl
elemId = AlsaCtlGetNumidElemId(mixer, sndcard, control->numid);
if (!elemId) {
- AFB_ApiError(mixer->api, "PcmSetControl sndard=%s fail to find control numid=%d", sndcard->cid.cardid, control->numid);
+ AFB_ApiError(mixer->api, "%s sndard=%s fail to find control numid=%d", __func__, sndcard->cid.cardid, control->numid);
goto OnErrorExit;
}
@@ -169,18 +194,18 @@ STATIC int PcmSetControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaSndControl
snd_ctl_elem_info_set_id(elemInfo, elemId);
if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) {
- AFB_ApiError(mixer->api, "PcmSetControl: sndard=%s numid=%d name='%s' not loadable", sndcard->cid.cardid, control->numid, control->name);
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not loadable", __func__, sndcard->cid.cardid, control->numid, control->name);
goto OnErrorExit;
}
if (!snd_ctl_elem_info_is_writable(elemInfo)) {
- AFB_ApiError(mixer->api, "PcmSetControl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name);
+ AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable", __func__, sndcard->cid.cardid, control->numid, control->name);
goto OnErrorExit;
}
error = CtlElemIdGetLong(mixer, sndcard, elemId, &curval);
if (error) {
- AFB_ApiError(mixer->api, "PcmSetControl sndard=%s fail to read control numid=%d", sndcard->cid.cardid, control->numid);
+ AFB_ApiError(mixer->api, "%s sndcard=%s fail to read control numid=%d", __func__, sndcard->cid.cardid, control->numid);
goto OnErrorExit;
}
@@ -347,9 +372,20 @@ OnErrorExit:
return;
}
+PUBLIC void ApiPcmDelParams(SoftMixerT* mixer, AlsaPcmHwInfoT* params) {
+ AFB_ApiDebug(mixer->api, "%s... free params formatS %s", __func__, params->formatS);
+ free((char*)params->formatS);
+ free(params);
+}
+
PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object * paramsJ) {
- AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT));
+
const char *format = NULL, *access = NULL;
+ AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT));
+ if (params == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
// some default values
params->rate = ALSA_DEFAULT_PCM_RATE;
@@ -364,17 +400,26 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json
"format", &format,
"access", &access);
if (error) {
- AFB_ApiError(mixer->api, "ApiPcmSetParams: sndcard=%s invalid params=%s", uid, json_object_get_string(paramsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: sndcard=%s invalid params=%s",
+ __func__, uid, json_object_get_string(paramsJ));
+ goto fail_params;
}
}
if (!format) {
params->format = SND_PCM_FORMAT_S16_LE;
- params->formatS = "S16_LE";
+ params->formatS = strdup("S16_LE");
+ if (params->formatS == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_params;
+ }
goto check_access;
}
params->formatS = strdup(format);
+ if (params->formatS == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_params;
+ }
#define FORMAT_CHECK(arg) if (!strcmp(format,#arg)) { params->format = SND_PCM_FORMAT_##arg; goto check_access; }
FORMAT_CHECK(S16_LE);
@@ -394,11 +439,11 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json
FORMAT_CHECK(FLOAT_LE);
FORMAT_CHECK(FLOAT_BE);
- AFB_ApiError(mixer->api, "ApiPcmSetParams:%s(params) unsupported format 'S16_LE|S32_L|...' format=%s", uid, format);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: %s(params) unsupported format 'S16_LE|S32_L|...' format=%s", __func__, uid, format);
+ goto fail_params;
check_access:
- AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s format set to SND_PCM_FORMAT_%s", uid, params->formatS);
+ AFB_ApiNotice(mixer->api, "%s: %s format set to SND_PCM_FORMAT_%s", __func__, uid, params->formatS);
#define ACCESS_CHECK(arg) if (!strcmp(access,#arg)) { params->access = SND_PCM_ACCESS_##arg; goto success;}
@@ -413,25 +458,99 @@ check_access:
ACCESS_CHECK(RW_INTERLEAVED);
ACCESS_CHECK(RW_NONINTERLEAVED);
- AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", uid, access);
- goto OnErrorExit;
+ AFB_ApiNotice(mixer->api,
+ "%s:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s",
+ __func__,uid, access);
+ goto fail_params;
success:
- AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s access set to %s", uid, access);
+ AFB_ApiNotice(mixer->api, "%s:%s access set to %s", __func__, uid, access);
return params;
-OnErrorExit:
+fail_params:
free(params);
+fail:
return NULL;
}
-PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object * argsJ) {
+
+static void pcmChannelsDestroy(SoftMixerT * mixer, AlsaSndPcmT * pcm) {
+ AFB_ApiDebug(mixer->api, "%s: pcm %s", __func__, pcm->uid);
+
+ AlsaPcmChannelT * channel, *tmp;
+ cds_list_for_each_entry_safe(channel, tmp, &pcm->channels.list, list) {
+ cds_list_del(&channel->list);
+
+ free((char *)channel->uid);
+ free(channel);
+ }
+ AFB_ApiDebug(mixer->api, "%s: pcm %s DONE", __func__, pcm->uid);
+}
+
+PUBLIC void ApiPcmDelete(SoftMixerT * mixer, AlsaSndPcmT * pcm) {
+
+ AFB_ApiDebug(mixer->api, "%s: pcm %s", __func__, pcm->uid);
+
+ free(pcm->apiVerbHandle);
+ AlsaSndCtlT * card = pcm->sndcard;
+ if (card->cid.pcmplug_params)
+ free((char*)card->cid.pcmplug_params);
+
+ pcmChannelsDestroy(mixer, pcm);
+
+ snd_ctl_close(card->ctl);
+ free(card);
+ free ((char*)pcm->mute.name);
+ free ((char*)pcm->volume.name);
+
+ cds_list_del(&pcm->list);
+
+ if (pcm->verb &&
+ (strcmp(pcm->verb, SOFTMIXER_VERB_NONE)!=0)) {
+ int error = afb_api_del_verb(mixer->api, pcm->verb, (void**)&pcm->apiVerbHandle);
+ if (error) {
+ AFB_ApiError(mixer->api, "Failed to remove verb %s", pcm->verb);
+ }
+ }
+
+ free((char*)pcm->uid);
+ free(pcm);
+ AFB_ApiDebug(mixer->api, "%s: done", __func__);
+}
+
+PUBLIC AlsaSndPcmT * ApiPcmNew(SoftMixerT* mixer) {
AlsaSndPcmT *pcm = calloc(1, sizeof (AlsaSndPcmT));
+ if (pcm == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ CDS_INIT_LIST_HEAD(&pcm->list);
+ CDS_INIT_LIST_HEAD(&pcm->channels.list);
+fail:
+ return pcm;
+}
+
+PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object * argsJ) {
+
json_object *sourceJ = NULL, *paramsJ = NULL, *sinkJ = NULL, *targetJ = NULL;
char *apiVerb = NULL, *apiInfo = NULL;
+ apiVerbHandleT *handle = NULL;
int error;
+ AlsaSndPcmT *pcm = ApiPcmNew(mixer);
+ if (pcm == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
pcm->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
+ if (pcm->sndcard == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_pcm;
+ }
+ CDS_INIT_LIST_HEAD(&pcm->sndcard->registryList);
+
error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o !}"
, "uid", &pcm->uid
, "pcmplug_params", &pcm->sndcard->cid.pcmplug_params
@@ -444,42 +563,48 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
, "params", &paramsJ
);
if (error) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s",
+ __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto fail_pcm_sndcard;
}
+ if (pcm->sndcard->cid.pcmplug_params)
+ pcm->isPcmPlug = true;
+ else
+ pcm->isPcmPlug = false;
+
// try to open sound card control interface
pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard);
if (!pcm->sndcard->ctl) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s", uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s",
+ __func__, uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid);
+ goto fail_pcm_sndcard;
}
// check sndcard accepts params
pcm->sndcard->params = ApiPcmSetParams(mixer, pcm->uid, paramsJ);
if (!pcm->sndcard->params) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to set params sndcard uid=%s params=%s", uid, pcm->uid, json_object_get_string(paramsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: hal=%s Fail to set params sndcard uid=%s params=%s",
+ __func__, uid, pcm->uid, json_object_get_string(paramsJ));
+ goto fail_pcm_sndcard_ctl;
}
if (direction == SND_PCM_STREAM_PLAYBACK) {
if (!sinkJ) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_PLAYBACK require sinks args=%s", uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: hal=%s SND_PCM_STREAM_PLAYBACK require sinks args=%s",
+ __func__, uid, json_object_get_string(argsJ));
+ goto fail_pcm_sndcard_ctl;
}
targetJ = sinkJ;
}
if (direction == SND_PCM_STREAM_CAPTURE) {
if (!sourceJ) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_CAPTURE require sources args=%s", uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: hal=%s SND_PCM_STREAM_CAPTURE require sources args=%s",
+ __func__, uid, json_object_get_string(argsJ));
+ goto fail_pcm_sndcard_ctl;
}
targetJ = sourceJ;
-
- // we may have to register SMIXER_SUBDS_CTLS per subdev (Fulup ToBeDone when sndcard get multiple device/subdev)
- pcm->sndcard->registry = calloc(SMIXER_SUBDS_CTLS + 1, sizeof (RegistryEntryPcmT));
- pcm->sndcard->rcount = SMIXER_SUBDS_CTLS;
}
json_object *channelsJ = NULL, *controlsJ = NULL;
@@ -488,30 +613,43 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
, "controls", &controlsJ
);
if (error) {
- AFB_ApiNotice(mixer->api, "ApiPcmAttachOne: hal=%s pcms missing channels|[controls] error=%s paybacks=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
- goto OnErrorExit;
+ AFB_ApiNotice(mixer->api, "%s: hal=%s pcms missing channels|[controls] error=%s (%s)",
+ __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto fail_pcm_sndcard_ctl;
}
+
+ AlsaPcmChannelT * channel = NULL;
+
if (channelsJ) {
switch (json_object_get_type(channelsJ)) {
case json_type_object:
- pcm->ccount = 1;
- pcm->channels = calloc(2, sizeof (void*));
- pcm->channels[0] = ProcessOneChannel(mixer, pcm->uid, channelsJ);
- if (!pcm->channels[0]) goto OnErrorExit;
+ channel = ProcessOneChannel(mixer, pcm->uid, channelsJ);
+ if (channel == NULL) {
+ goto fail_pcm_sndcard_ctl;
+ }
+
+ pcm->nbChannels++;
+ cds_list_add_tail(&channel->list, &pcm->channels.list);
+
break;
- case json_type_array:
- pcm->ccount = (int) json_object_array_length(channelsJ);
- pcm->channels = calloc(pcm->ccount + 1, sizeof (void*));
- for (int idx = 0; idx < pcm->ccount; idx++) {
+ case json_type_array: {
+ int nbChannels = (int) json_object_array_length(channelsJ);
+
+ for (int idx = 0; idx < nbChannels; idx++) {
json_object *channelJ = json_object_array_get_idx(channelsJ, idx);
- pcm->channels[idx] = ProcessOneChannel(mixer, pcm->uid, channelJ);
- if (!pcm->channels[idx]) goto OnErrorExit;
+ channel = ProcessOneChannel(mixer, pcm->uid, channelJ);
+ if (!channel)
+ goto fail_pcm_channels;
+
+ pcm->nbChannels++;
+ cds_list_add_tail(&channel->list, &pcm->channels.list);
}
break;
+ }
default:
- AFB_ApiError(mixer->api, "ApiPcmAttachOne:%s invalid pcm=%s", pcm->uid, json_object_get_string(channelsJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s:%s invalid pcm=%s", pcm->uid, __func__, json_object_get_string(channelsJ));
+ goto fail_pcm_sndcard_ctl;
}
}
@@ -525,50 +663,101 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
AFB_ApiNotice(mixer->api,
"%s: source missing [volume]|[mute] error=%s control=%s",
__func__, wrap_json_get_error_string(error), json_object_get_string(controlsJ));
- goto OnErrorExit;
+ goto fail_pcm_channels;
}
- if (volJ) error += PcmAttachOneCtl(mixer, pcm->sndcard, volJ, &pcm->volume);
- if (muteJ) error += PcmAttachOneCtl(mixer, pcm->sndcard, muteJ, &pcm->mute);
- if (error) goto OnErrorExit;
+ if (volJ) {
+ error = PcmAttachOneCtl(mixer, pcm->sndcard, volJ, &pcm->volume);
+ if (error)
+ goto fail_pcm_channels;
+ }
+ if (muteJ) {
+ error = PcmAttachOneCtl(mixer, pcm->sndcard, muteJ, &pcm->mute);
+ if (error)
+ goto fail_pcm_channels;
+ }
// create master control for this sink
if (direction == SND_PCM_STREAM_PLAYBACK) {
- if (asprintf(&apiVerb, "%s:playback", pcm->uid) == -1)
- goto OnErrorExit;
- if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1)
- goto OnErrorExit;
+ if (asprintf(&apiVerb, "%s:playback", pcm->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_pcm_channels;
+ }
+ if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_verb_name;
+ }
+
} else {
- if (asprintf(&apiVerb, "%s:capture", pcm->uid) == -1)
- goto OnErrorExit;
- if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1)
- goto OnErrorExit;
+ if (asprintf(&apiVerb, "%s:capture", pcm->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_pcm_channels;
+ }
+ if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_verb_name;
+ }
}
apiVerbHandleT *handle = calloc(1, sizeof (apiVerbHandleT));
+ if (handle == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_api_info;
+ }
+
handle->uid = uid;
handle->pcm = pcm;
handle->mixer = mixer;
pcm->verb=apiVerb;
+ pcm->apiVerbHandle = handle;
+
error = afb_api_add_verb(mixer->api, apiVerb, apiInfo, ApiPcmVerbCB, handle, NULL, 0, 0);
if (error) {
- AFB_ApiError(mixer->api, "ApiPcmAttachOne mixer=%s verb=%s fail to Register Master control ", mixer->uid, apiVerb);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to Register Master control ",
+ __func__, mixer->uid, apiVerb);
+ goto fail_handle;
}
} else {
/* no controls -> put dummy verb */
- pcm->verb = "none";
+ pcm->verb = SOFTMIXER_VERB_NONE;
}
// free useless resource and secure others
pcm->uid = strdup(pcm->uid);
+ if (pcm->uid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_handle;
+ }
+ const char ** ppcmplug = &(pcm->sndcard->cid.pcmplug_params);
+ if (*ppcmplug) {
+ *ppcmplug = strdup(*ppcmplug);
+ if (*ppcmplug == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_pcm_uid;
+ }
+ }
return pcm;
-OnErrorExit:
- free(pcm);
- free(apiVerb);
- free(apiInfo);
+fail_pcm_uid:
+ free((char*)pcm->uid);
+fail_handle:
+ free(handle);
+fail_api_info:
+ free(apiInfo);
+fail_verb_name:
+ free(apiVerb);
+fail_pcm_channels:
+ pcmChannelsDestroy(mixer, pcm);
+fail_pcm_sndcard_ctl:
+ free(pcm->sndcard->ctl);
+fail_pcm_sndcard:
+ free(pcm->sndcard);
+fail_pcm:
+ free ((char*)pcm->mute.name);
+ free ((char*)pcm->volume.name);
+ free(pcm);
+fail:
return NULL;
}
diff --git a/plugins/alsa/alsa-api-ramp.c b/plugins/alsa/alsa-api-ramp.c
index 21ec165..877e281 100644
--- a/plugins/alsa/alsa-api-ramp.c
+++ b/plugins/alsa/alsa-api-ramp.c
@@ -28,20 +28,22 @@
PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid) {
AlsaVolRampT *ramp = NULL;
-
- // Loop on every Register zone pcm and extract (cardid) from (uid)
- for (int idx = 0; mixer->ramps[idx]->uid != NULL; idx++) {
- if (!strcasecmp(mixer->ramps[idx]->uid, uid)) {
- ramp = mixer->ramps[idx];
- return ramp;
- }
+ cds_list_for_each_entry(ramp, &mixer->ramps.list, list) {
+ if (!strcasecmp(ramp->uid, uid))
+ return ramp;
}
return NULL;
}
STATIC AlsaVolRampT *AttachOneRamp(SoftMixerT *mixer, const char *uid, json_object *rampJ) {
- const char*rampUid;
+ const char * rampUid;
AlsaVolRampT *ramp = calloc(1, sizeof (AlsaVolRampT));
+ if (ramp == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ CDS_INIT_LIST_HEAD(&ramp->list);
int error = wrap_json_unpack(rampJ, "{ss,si,si,si !}"
, "uid", &rampUid
@@ -50,65 +52,87 @@ STATIC AlsaVolRampT *AttachOneRamp(SoftMixerT *mixer, const char *uid, json_obje
, "down", &ramp->stepDown
);
if (error) {
- AFB_ApiError(mixer->api, "AttachOneRamp mixer=%s hal=%s error=%s json=%s", mixer->uid, uid, wrap_json_get_error_string(error), json_object_get_string(rampJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api,
+ "%s mixer=%s hal=%s error=%s json=%s",
+ __func__, mixer->uid, uid, wrap_json_get_error_string(error), json_object_get_string(rampJ));
+ goto fail_ramp;
}
ramp->delay = ramp->delay * 1000; // move from ms to us
ramp->uid = strdup(rampUid);
+ if (ramp->uid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_ramp;
+ }
+
return ramp;
-OnErrorExit:
+fail_ramp:
free(ramp);
+fail:
return NULL;
}
-PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object *argsJ) {
- int index;
- for (index = 0; index < mixer->max.ramps; index++) {
- if (!mixer->ramps[index]) break;
- }
+static void rampDestroy(SoftMixerT * mixer, void * arg) {
+ AlsaVolRampT * ramp = (AlsaVolRampT*) arg;
+ AFB_ApiDebug(mixer->api, "%s... %s not implemented", __func__, ramp->uid);
+}
+
+static AlsaVolRampT * rampCreate(SoftMixerT * mixer, const char * uid, json_object *argsJ) {
+ AlsaVolRampT * newRamp = AttachOneRamp(mixer, uid, argsJ);
+ if (!newRamp) {
+ goto fail;
+ }
+ mixer->nbRamps++;
+ cds_list_add_tail(&newRamp->list, &mixer->ramps.list);
+ AlsaMixerTransactionObjectAdd(mixer->transaction, newRamp, rampDestroy);
+fail:
+ return newRamp;
+}
- if (index == mixer->max.ramps) {
- AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max ramp=%d", mixer->uid, uid, mixer->max.ramps);
- goto OnErrorExit;
+PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object *argsJ) {
+
+ if (mixer->nbRamps >= mixer->max.ramps) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max ramp=%d", mixer->uid, uid, mixer->max.ramps);
+ goto fail;
}
+ AlsaVolRampT * newRamp = NULL;
+
switch (json_object_get_type(argsJ)) {
long count;
case json_type_object:
- mixer->ramps[index] = AttachOneRamp(mixer, uid, argsJ);
- if (!mixer->ramps[index]) {
+ newRamp = rampCreate(mixer, uid, argsJ);
+ if (!newRamp) {
AFB_ReqFailF(request, "bad-ramp", "mixer=%s hal=%s invalid ramp= %s", mixer->uid, uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
break;
case json_type_array:
count = json_object_array_length(argsJ);
- if (count > (mixer->max.ramps - index)) {
+ if (count > (mixer->max.ramps - mixer->nbRamps)) {
AFB_ReqFailF(request, "too-small", "mixer=%s hal=%s max ramp=%d", mixer->uid, uid, mixer->max.ramps);
- goto OnErrorExit;
-
+ goto fail;
}
for (int idx = 0; idx < count; idx++) {
json_object *streamAudioJ = json_object_array_get_idx(argsJ, idx);
- mixer->ramps[index + idx] = AttachOneRamp(mixer, uid, streamAudioJ);
- if (!mixer->ramps[index + idx]) {
- AFB_ReqFailF(request, "bad-ramp", "mixer=%s hal=%s invalid ramp= %s", mixer->uid, uid, json_object_get_string(streamAudioJ));
- goto OnErrorExit;
+ newRamp = rampCreate(mixer, uid, streamAudioJ);
+ if (!newRamp) {
+ AFB_ReqFailF(request, "bad-ramp", "mixer=%s hal=%s invalid ramp= %s", mixer->uid, uid, json_object_get_string(argsJ));
+ goto fail;
}
}
break;
default:
AFB_ReqFailF(request, "invalid-syntax", "mixer=%s hal=%s ramps invalid argsJ= %s", mixer->uid, uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
return 0;
-OnErrorExit:
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-sink.c b/plugins/alsa/alsa-api-sink.c
index 96437f5..b3d17ac 100644
--- a/plugins/alsa/alsa-api-sink.c
+++ b/plugins/alsa/alsa-api-sink.c
@@ -24,106 +24,133 @@ PUBLIC AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *tar
// try to attach a zone as stream playback sink
AlsaSndZoneT *zone = ApiZoneGetByUid(mixer, target);
- if (zone && zone->sinks) {
- // use 1st channel to find attached sound card.
- const char *channel = zone->sinks[0]->uid;
+ if (!zone) {
+ AFB_ApiError(mixer->api, "%s: Unable to find zone %s", __func__, target);
+ goto fail;
+ }
+ if (zone->nbSinks == 0) {
+ AFB_ApiError(mixer->api, "%s: Zone %s has no sinks", __func__, target);
+ goto fail;
+ }
- // search for channel uid into mixer sinks and derive sound card
- for (int idx = 0; mixer->sinks[idx]; idx++) {
- for (int jdx = 0; jdx < mixer->sinks[idx]->ccount; jdx++) {
- if (mixer->sinks[idx]->channels[jdx]->uid && !strcasecmp(channel, mixer->sinks[idx]->channels[jdx]->uid)) {
- return mixer->sinks[idx]->sndcard->params;
- }
- }
- }
+ // use 1st channel to find attached sound card.
+ AlsaPcmChannelT * pcmChannel = cds_list_first_entry(&zone->sinks.list, AlsaPcmChannelT , list);
+ const char *channel = pcmChannel->uid;
+
+ AFB_ApiDebug(mixer->api, "%s: first channel uid is %s, zone has %d sinks", __func__, channel, zone->nbSinks);
+
+ AlsaSndPcmT * sink = NULL;
+
+ cds_list_for_each_entry(sink, &mixer->sinks.list, list) {
+ AlsaPcmChannelT * pcmChannel;
+ cds_list_for_each_entry(pcmChannel, &sink->channels.list, list) {
+ if (pcmChannel->uid && !strcasecmp(channel, pcmChannel->uid)) {
+ AFB_ApiDebug(mixer->api, "%s: found %s, return", __func__, pcmChannel->uid);
+ return sink->sndcard->params;
+ }
+ }
}
+
+fail:
return NULL;
}
PUBLIC AlsaSndPcmT *ApiSinkGetByUid(SoftMixerT *mixer, const char *target) {
// if no attached zone found, then try direct sink attachment
- for (int idx = 0; mixer->sinks[idx]; idx++) {
- if (mixer->sinks[idx]->uid && !strcasecmp(mixer->sinks[idx]->uid, target)) {
- return mixer->sinks[idx];
- }
- }
+ AlsaSndPcmT * sink;
+ cds_list_for_each_entry(sink, &mixer->sinks.list, list) {
+ if (sink->uid && !strcasecmp(sink->uid, target))
+ return sink;
+ }
return NULL;
}
-PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+static void sinkDestroy(SoftMixerT* mixer, void * arg) {
+ AlsaSndPcmT * sink = (AlsaSndPcmT*) arg;
+ AFB_ApiDebug(mixer->api, "%s... %s", __func__, sink->uid);
+ ApiPcmDelete(mixer, sink);
+ mixer->nbSinks--;
+}
- int index;
+AlsaSndPcmT * sinkCreate(SoftMixerT * mixer, AFB_ReqT request ,const char * uid, json_object * argsJ) {
+ AlsaPcmCtlT* dmixConfig = NULL;
char *dmixUid = NULL;
-
- for (index = 0; index < mixer->max.sinks; index++) {
- if (!mixer->sinks[index]) break;
+ AlsaSndPcmT * newSink = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ);
+ if (!newSink) {
+ goto fail;
+ }
+ /* DMIX only supports REAL hardware */
+ if (!newSink->isPcmPlug) {
+ // move from hardware to DMIX attach to sndcard
+ if (asprintf(&dmixUid, "dmix-%s", newSink->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_pcm;
+ }
+ dmixConfig = AlsaCreateDmix(mixer, dmixUid, newSink, 0);
+ if (!dmixConfig) {
+ AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, newSink->uid);
+ goto fail_dmix;
+ }
}
- if (index == mixer->max.sinks) {
+ mixer->nbSinks++;
+ cds_list_add_tail(&newSink->list, &mixer->sinks.list);
+ AlsaMixerTransactionObjectAdd(mixer->transaction, newSink, sinkDestroy);
+
+ return newSink;
+fail:
+fail_pcm:
+ // TODO delete PCM
+fail_dmix:
+ free(dmixUid);
+ return NULL;
+}
+
+
+PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) {
+
+ if (mixer->nbSinks >= mixer->max.sinks) {
AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks);
- goto OnErrorExit;
+ goto fail;
}
switch (json_object_get_type(argsJ)) {
- long count;
-
- AlsaPcmCtlT* dmixConfig;
+ long count;
- case json_type_object:
- mixer->sinks[index] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ);
- if (!mixer->sinks[index]) {
+ case json_type_object: {
+ AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, argsJ);
+ if (!newSink) {
AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
-
- // move from hardware to DMIX attach to sndcard
- if (asprintf(&dmixUid, "dmix-%s", mixer->sinks[index]->uid) == -1)
- goto OnErrorExit;
-
- dmixConfig = AlsaCreateDmix(mixer, dmixUid, mixer->sinks[index], 0);
- if (!dmixConfig) {
- AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, mixer->sinks[index]->uid);
- goto OnErrorExit;
- }
-
break;
-
- case json_type_array:
+ }
+ case json_type_array: {
count = json_object_array_length(argsJ);
- if (count > (mixer->max.sinks - index)) {
+ if (count > (mixer->max.sinks - mixer->nbSinks)) {
AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks);
- goto OnErrorExit;
+ goto fail;
}
for (int idx = 0; idx < count; idx++) {
json_object *sinkJ = json_object_array_get_idx(argsJ, idx);
- AlsaSndPcmT * pcm = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, sinkJ);
- if (!pcm) {
+ AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, sinkJ);
+ if (!newSink) {
AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(sinkJ));
- goto OnErrorExit;
+ goto fail;
}
- // move from hardware to DMIX attach to sndcard
- if (asprintf(&dmixUid, "dmix-%s", pcm->uid) == -1)
- goto OnErrorExit;
-
- dmixConfig = AlsaCreateDmix(mixer, dmixUid, pcm, 0);
- if (!dmixConfig) {
- AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, pcm->uid);
- goto OnErrorExit;
- }
- mixer->sinks[index + idx] = pcm;
}
break;
+ }
default:
AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sinks invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
return 0;
-OnErrorExit:
- free(dmixUid);
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-source.c b/plugins/alsa/alsa-api-source.c
index 4f356b2..2b6c8b7 100644
--- a/plugins/alsa/alsa-api-source.c
+++ b/plugins/alsa/alsa-api-source.c
@@ -24,61 +24,83 @@
PUBLIC AlsaSndCtlT *ApiSourceFindSubdev(SoftMixerT *mixer, const char *target) {
// search for subdev into every registered source
- for (int idx = 0; mixer->sources[idx]; idx++) {
- if (mixer->sources[idx]->uid && !strcasecmp(mixer->sources[idx]->uid, target)) {
- return mixer->sources[idx]->sndcard;
- }
- }
+
+ AlsaSndPcmT * source;
+ cds_list_for_each_entry(source, &mixer->sources.list, list) {
+ if (source->uid && !strcmp(source->uid, target))
+ return source->sndcard;
+ }
+
return NULL;
}
-PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+static void sourceDestroy(SoftMixerT* mixer, void * arg) {
+ AlsaSndPcmT * source = (AlsaSndPcmT*) arg;
+ AFB_ApiDebug(mixer->api, "%s... %s", __func__, source->uid);
+ ApiPcmDelete(mixer, source);
+ mixer->nbSources--;
+}
- int index;
- for (index = 0; index < mixer->max.sources; index++) {
- if (!mixer->sources[index]) break;
- }
+static AlsaSndPcmT * sourceCreate(SoftMixerT* mixer, const char * uid, json_object * argsJ) {
+ AlsaSndPcmT * newSource = NULL;
+ newSource = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_CAPTURE, argsJ);
+ if (!newSource) {
+ goto fail;
+ }
+
+ mixer->nbSources++;
+ cds_list_add(&newSource->list, &mixer->sources.list);
+ AlsaMixerTransactionObjectAdd(mixer->transaction, newSource, sourceDestroy);
- if (index == mixer->max.sources) {
+fail:
+ return newSource;
+}
+
+PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) {
+
+ if (mixer->nbSources >= mixer->max.sources) {
AFB_ReqFailF(request, "too-small", "mixer=%s max source=%d", mixer->uid, mixer->max.sources);
- goto OnErrorExit;
+ goto fail;
}
+ AlsaSndPcmT * newSource = NULL;
+
switch (json_object_get_type(argsJ)) {
long count;
case json_type_object:
- mixer->sources[index] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_CAPTURE, argsJ);
- if (!mixer->sources[index]) {
+ newSource = sourceCreate(mixer, uid, argsJ);
+ if (!newSource) {
AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid source= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
+
break;
case json_type_array:
count = json_object_array_length(argsJ);
- if (count > (mixer->max.sources - index)) {
+ if (count > (mixer->max.sources - mixer->nbSources)) {
AFB_ReqFailF(request, "too-small", "mixer=%s max source=%d", mixer->uid, mixer->max.sources);
- goto OnErrorExit;
-
+ goto fail;
}
for (int idx = 0; idx < count; idx++) {
json_object *sourceJ = json_object_array_get_idx(argsJ, idx);
- mixer->sources[index + idx] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_CAPTURE, sourceJ);
- if (!mixer->sources[index + idx]) {
+ newSource = sourceCreate(mixer, uid, sourceJ);
+ if (!newSource) {
AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid source= %s", mixer->uid, json_object_get_string(sourceJ));
- goto OnErrorExit;
+ goto fail;
}
}
+
break;
default:
AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sources invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
return 0;
-OnErrorExit:
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index 9a5c5c2..d9d9667 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -19,7 +19,6 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
-#include "alsa-bluez.h"
#include <string.h>
#include <stdbool.h>
@@ -192,7 +191,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
AlsaSndCtlT *captureCard;
AlsaDevInfoT *captureDev = alloca(sizeof (AlsaDevInfoT));
AlsaLoopSubdevT *loopDev;
- AlsaSndZoneT *zone;
+ AlsaSndZoneT *zone = NULL;
char *volSlaveId = NULL;
char *playbackName = NULL;
char *runName = NULL;
@@ -200,7 +199,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
int pauseNumid = 0;
int volNumid = 0;
- AFB_ApiInfo(mixer->api,
+ AFB_ApiDebug(mixer->api,
"%s, stream %s %s, source %s, sink %s, mute %d",
__func__,uid, stream->uid, stream->source, stream->sink, stream->mute);
@@ -215,13 +214,13 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
captureDev->pcmplug_params = NULL;
captureCard = loop->sndcard;
- AFB_ApiInfo(mixer->api,
+ AFB_ApiDebug(mixer->api,
"%s: found loopdev %d,%d",
__func__, loop->capture, loopDev->index);
} else {
- // if capture UID is not present in loop search on sources
- AFB_ApiInfo(mixer->api,"%s: %s not found in loop, look in sources", __func__, uid);
+ // if capture UID is not present in loop search on known sources
+ AFB_ApiDebug(mixer->api,"%s: %s not found in loop, look in sources", __func__, uid);
AlsaSndCtlT *sourceDev = ApiSourceFindSubdev(mixer, stream->source);
if (sourceDev) {
@@ -232,7 +231,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
captureDev->subdev = sourceDev->cid.subdev;
captureDev->pcmplug_params = sourceDev->cid.pcmplug_params;
captureCard = sourceDev;
- AFB_ApiInfo(mixer->api, "%s found capture %s", __func__, uid);
+ AFB_ApiDebug(mixer->api, "%s found capture for %s (plug %s, card %s)",
+ __func__, uid, captureDev->pcmplug_params, sourceDev->cid.cardid);
} else {
AFB_ApiError(mixer->api,
"%s: mixer=%s stream=%s not found in loops/sources",
@@ -242,20 +242,26 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
}
// check PCM is valid and get its full name
- AlsaPcmCtlT *capturePcm = AlsaByPathOpenPcm(mixer, captureDev, SND_PCM_STREAM_CAPTURE);
- if (!capturePcm) goto OnErrorExit;
+ AlsaPcmCtlT *capturePcm = AlsaByPathOpenPcmCtl(mixer, captureDev, SND_PCM_STREAM_CAPTURE);
+ if (!capturePcm) {
+ AFB_ApiError(mixer->api,"%s: Unable to open the capture PCM !", __func__);
+ goto OnErrorExit;
+ }
+ capturePcm->closeAtDeletion = true;
capturePcm->mute = stream->mute;
- AFB_ApiInfo(mixer->api,"%s: PCM opened !", __func__);
+ AFB_ApiDebug(mixer->api,"%s: PCM opened !", __func__);
// Registry capturePcm PCM for active/pause event
if (loopDev && loopDev->numid) {
+ AFB_ApiDebug(mixer->api, "%s: REGISTER active/pause", __func__);
error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_RUN, loopDev->numid);
- if (error) goto OnErrorExit;
+ if (error)
+ goto OnErrorExit;
}
- if (mixer->zones[0]) {
+ if (stream->sink) {
// if zones exist then retrieve zone pcmid and channel count
zone = ApiZoneGetByUid(mixer, stream->sink);
if (!zone) {
@@ -266,11 +272,13 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
}
// route PCM should have been create during zones attach phase.
- if (asprintf(&volSlaveId, "route-%s", zone->uid) == -1)
+ if (asprintf(&volSlaveId, "route-%s", zone->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
- } else {
- AlsaSndPcmT *playback = ApiSinkGetByUid(mixer, stream->sink);
+ } else if (stream->playback) {
+ AlsaSndPcmT *playback = ApiSinkGetByUid(mixer, stream->playback);
if (!playback) {
AFB_ApiError(mixer->api,
"%s: mixer=%s stream=%s fail to find sink playback='%s'",
@@ -278,26 +286,42 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
- // retrieve channel count from route and push it to stream
- if (asprintf(&volSlaveId, "dmix-%s", playback->uid) == -1)
- goto OnErrorExit;
-
+ /* there is no dmix on pcmplugs */
+ if (playback->isPcmPlug) {
+ if (asprintf(&volSlaveId, "%s", playback->sndcard->cid.pcmplug_params) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
+ else {
+ if (asprintf(&volSlaveId, "dmix-%s", playback->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
+
+ AFB_ApiError(mixer->api, "%s: Allocate a fake ZONE", __func__);
+
// create a fake zone for rate converter selection
zone=alloca(sizeof(AlsaSndZoneT));
zone->uid= playback->uid;
zone->params = playback->sndcard->params;
- zone->ccount = playback->ccount;
+ zone->ccount = playback->nbChannels;
+
}
+
// retrieve channel count from route and push it to stream
stream->params->channels = zone->ccount;
// create mute control and Registry it as pause/resume ctl)
- if (asprintf(&runName, "pause-%s", stream->uid) == -1)
+ if (asprintf(&runName, "pause-%s", stream->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
- AFB_ApiInfo(mixer->api,"%s: create mute control...", __func__);
+ AFB_ApiDebug(mixer->api,"%s: create mute control...", __func__);
pauseNumid = AlsaCtlCreateControl(mixer, captureCard, runName, 1, 0, 1, 1, stream->mute);
if (pauseNumid <= 0) {
@@ -305,7 +329,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
- AFB_ApiInfo(mixer->api,"%s: register mute control...", __func__);
+ AFB_ApiDebug(mixer->api,"%s: register mute control...", __func__);
// Registry stop/play as a pause/resume control
error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_PAUSE, pauseNumid);
@@ -314,10 +338,12 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
- if (asprintf(&volName, "vol-%s", stream->uid) == -1)
+ if (asprintf(&volName, "vol-%s", stream->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
- AFB_ApiInfo(mixer->api,"%s: create softvol", __func__);
+ AFB_ApiDebug(mixer->api,"%s: create softvol", __func__);
// create stream and delay pcm opening until vol control is created
streamPcm = AlsaCreateSoftvol(mixer, stream, volSlaveId, captureCard, volName, VOL_CONTROL_MAX, 0);
@@ -326,7 +352,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
- AFB_ApiInfo(mixer->api,"%s: create softvol control", __func__);
+ AFB_ApiDebug(mixer->api,"%s: create softvol control", __func__);
// create volume control before softvol pcm is opened
volNumid = AlsaCtlCreateControl(mixer,
@@ -355,9 +381,11 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
zone->params->format);
char *rateName;
- if (asprintf(&rateName, "rate-%s", stream->uid) == -1)
+ if (asprintf(&rateName, "rate-%s", stream->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
- streamPcm = AlsaCreateRate(mixer, rateName, streamPcm, zone->params, 0);
+ }
+ streamPcm = AlsaCreateRate(mixer, stream, rateName, streamPcm, zone->params, 0);
if (!streamPcm) {
AFB_ApiError(mixer->api, "%s: fail to create rate converter", __func__);
goto OnErrorExit;
@@ -368,7 +396,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
playbackName = (char*) streamPcm->cid.cardid;
}
- AFB_ApiInfo(mixer->api, "%s: Opening PCM PLAYBACK name %s", __func__, playbackName);
+ AFB_ApiDebug(mixer->api, "%s: Opening PCM PLAYBACK name %s", __func__, playbackName);
// everything is now ready to open playback pcm in BLOCKING mode this time
error = snd_pcm_open(&streamPcm->handle, playbackName, SND_PCM_STREAM_PLAYBACK, 0 /* will block*/ );
@@ -379,13 +407,15 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
+ streamPcm->closeAtDeletion = true;
+
// start stream pcm copy (at this both capturePcm & sink pcm should be open, we use output params to configure both in+outPCM)
- error = AlsaPcmCopy(mixer, stream, capturePcm, streamPcm, stream->params);
+ error = AlsaPcmCopyStart(mixer, stream, capturePcm, streamPcm, stream->params);
if (error) {
AFB_ApiError(mixer->api, "%s: Failed to launch copy", __func__);
goto OnErrorExit;
}
-
+ AFB_ApiDebug(mixer->api, "%s: register VOL ctrl", __func__);
error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_IGNORE, volNumid);
if (error) {
AFB_ApiError(mixer->api, "%s: register control on capture", __func__);
@@ -396,7 +426,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
if (loopDev && loopDev->numid) {
// retrieve active/pause control and set PCM status accordingly
error = AlsaCtlNumidGetLong(mixer, captureCard, loopDev->numid, &value);
- if (error) goto OnErrorExit;
+ if (error)
+ goto OnErrorExit;
// toggle pause/resume (should be done after pcm_start)
if ((error = snd_pcm_pause(capturePcm->handle, !value)) < 0) {
@@ -405,21 +436,33 @@ 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)
- goto OnErrorExit;
+ if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, loop->playback, capturePcm->cid.subdev) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
} else {
- if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, captureDev->device, captureDev->subdev) == -1)
+ if (asprintf((char**) &stream->source, "hw:%d,%d,%d", captureDev->cardidx, captureDev->device, captureDev->subdev) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
}
// create a dedicated verb for this stream
apiHandleT *apiHandle = calloc(1, sizeof (apiHandleT));
+ if (apiHandle == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
apiHandle->mixer = mixer;
apiHandle->stream = stream;
apiHandle->sndcard = captureCard;
apiHandle->pcm = capturePcm->handle;
+ // keep a reference for future cleanup
+ stream->verbApiHandle = apiHandle;
+
// replace stream volume/mute values with corresponding ctl control
stream->volume = volNumid;
stream->mute = pauseNumid;
@@ -437,7 +480,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
//AlsaDumpPcmInfo(source, "\n\nPcm_config\n-----------\n", streamPcm->handle);
AFB_ApiNotice(mixer->api,
- "%s: mixer=%s stream=%s done",
+ "%s: mixer=%s stream=%s CREATED",
__func__, mixer->uid, stream->uid);
return 0;
@@ -450,34 +493,52 @@ OnErrorExit:
}
STATIC AlsaStreamAudioT * AttachOneStream(SoftMixerT *mixer, const char *uid, const char *prefix, json_object * streamJ) {
- AlsaStreamAudioT *stream = calloc(1, sizeof (AlsaStreamAudioT));
int error;
json_object *paramsJ = NULL;
+ AlsaStreamAudioT *stream = calloc(1, sizeof (AlsaStreamAudioT));
+ if (stream == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
// Make sure default runs
stream->volume = ALSA_DEFAULT_PCM_VOLUME;
stream->mute = 0;
stream->info = NULL;
+ stream->delayms = SMIXER_DEFLT_DELAYMS;
- error = wrap_json_unpack(streamJ, "{ss,s?s,s?s,ss,s?s,s?i,s?b,s?o,s?s !}"
+ error = wrap_json_unpack(streamJ, "{ss,s?s,s?s,s?s,s?s,s?s,s?i,s?b,s?o,s?s,s?i !}"
, "uid", &stream->uid
, "verb", &stream->verb
, "info", &stream->info
, "zone", &stream->sink
+ , "playback", &stream->playback
, "source", &stream->source
, "volume", &stream->volume
, "mute", &stream->mute
, "params", &paramsJ
, "ramp", &stream->ramp
+ , "delayms", &stream->delayms
);
if (error) {
- AFB_ApiNotice(mixer->api,
+ AFB_ApiError(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 (!stream->sink && !stream->playback) {
+ AFB_ApiError(mixer->api, "%s: A stream must have a zone or a playback", __func__);
+ goto OnErrorExit;
+ }
+
+ if (stream->sink && stream->playback) {
+ AFB_ApiError(mixer->api, "%s: both a playback and a zone cannot be defined at the same time", __func__);
+ goto OnErrorExit;
+ }
+
stream->params = ApiPcmSetParams(mixer, stream->uid, paramsJ);
if (!stream->params) {
AFB_ApiError(mixer->api,
@@ -488,22 +549,54 @@ STATIC AlsaStreamAudioT * AttachOneStream(SoftMixerT *mixer, const char *uid, co
// make sure remain valid even when json object is removed
stream->uid = strdup(stream->uid);
- if (stream->sink)stream->sink = strdup(stream->sink);
- if (stream->source)stream->source = strdup(stream->source);
+ if (stream->uid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ if (stream->sink) {
+ stream->sink = strdup(stream->sink);
+ if (stream->sink == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
+ if (stream->source) {
+ stream->source = strdup(stream->source);
+ if (stream->source == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
+ if (stream->playback) {
+ stream->playback = strdup(stream->playback);
+ if (stream->playback == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
// Prefix verb with uid|prefix
if (prefix) {
if (stream->verb) {
- if (asprintf((char**) &stream->verb, "%s#%s", prefix, stream->verb) == -1)
+ if (asprintf((char**) &stream->verb, "%s#%s", prefix, stream->verb) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
}
else {
- if (asprintf((char**) &stream->verb, "%s#%s", prefix, stream->uid) == -1)
+ if (asprintf((char**) &stream->verb, "%s#%s", prefix, stream->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
}
} else {
- if (!stream->verb)
+ if (!stream->verb) {
stream->verb = strdup(stream->uid);
+ if (stream->verb == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+ }
}
// implement stream PCM with corresponding thread and controls
@@ -512,7 +605,6 @@ STATIC AlsaStreamAudioT * AttachOneStream(SoftMixerT *mixer, const char *uid, co
AFB_ApiError(mixer->api, "%s: failed to create stream", __func__);
goto OnErrorExit;
}
-
return stream;
OnErrorExit:
@@ -521,55 +613,108 @@ OnErrorExit:
return NULL;
}
-PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, const char *prefix, json_object * argsJ) {
+static void streamDestroy(SoftMixerT * mixer, void * arg) {
+ AlsaStreamAudioT * stream = (AlsaStreamAudioT*) arg;
+ int error;
+ AFB_ApiDebug(mixer->api, "%s... %s", __func__, stream->uid);
- AFB_ApiInfo(mixer->api, "%s: %s prefix %s", __func__, uid, prefix);
-
- if (!mixer->loops) {
- AFB_ApiError(mixer->api, "%s: mixer=%s No Loop found [should Registry snd_loop first]", __func__, mixer->uid);
- goto OnErrorExit;
+ error = afb_api_del_verb(mixer->api, stream->uid, (void**)stream->verbApiHandle);
+ if (error) {
+ AFB_ApiDebug(mixer->api, "%s: failed to remove verb %s", __func__, stream->uid);
}
- int index;
- for (index = 0; index < mixer->max.streams; index++) {
- if (!mixer->streams[index]) break;
+ AlsaPcmCopyStop(mixer, stream->copy);
+
+ if (stream->softvolConfig) {
+ AFB_ApiDebug(mixer->api, "%s... %s delete softvol config", __func__, stream->uid);
+ snd_config_delete(stream->softvolConfig);
+ snd_config_update();
+ stream->softvolConfig = NULL;
+ }
+
+ if (stream->rateConfig) {
+ AFB_ApiDebug(mixer->api, "%s... %s delete rate config", __func__, stream->uid);
+ snd_config_delete(stream->rateConfig);
+ snd_config_update();
+ stream->rateConfig = NULL;
+ }
+
+ free((char*)stream->uid);
+ free((char*)stream->playback);
+ free((char*)stream->ramp);
+ free((char*)stream->sink);
+ free((char*)stream->source);
+ free((char*)stream->verb);
+
+ ApiPcmDelParams(mixer, stream->params);
+
+ mixer->nbStreams--;
+ cds_list_del(&stream->list);
+
+ free(stream);
+ AFB_ApiDebug(mixer->api, "%s... DONE !", __func__);
+}
+
+static AlsaStreamAudioT * streamCreate(SoftMixerT * mixer, const char * uid, const char * prefix, json_object * argsJ) {
+ AlsaStreamAudioT * newStream = AttachOneStream(mixer, uid, prefix, argsJ);
+ if (!newStream) {
+ goto fail;
+ }
+ mixer->nbStreams++;
+ cds_list_add_tail(&newStream->list, &mixer->streams.list);
+
+ AlsaMixerTransactionObjectAdd(mixer->transaction, newStream, streamDestroy);
+fail:
+ return newStream;
+}
+
+PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, const char *prefix, json_object * argsJ) {
+
+ AFB_ApiDebug(mixer->api, "%s: %s prefix %s", __func__, uid, prefix);
+
+ AlsaStreamAudioT * newStream = NULL;
+
+ if (mixer->nbLoops == 0) {
+ AFB_ApiError(mixer->api, "%s: mixer=%s No Loop found [should Registry snd_loop first]", __func__, mixer->uid);
+ goto fail;
}
- if (index == mixer->max.streams) {
- AFB_ReqFailF(request, "too-small", "mixer=%s max stream=%d", mixer->uid, mixer->max.streams);
- goto OnErrorExit;
+ if (mixer->nbStreams >= mixer->max.streams) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max stream=%d", mixer->uid, mixer->max.streams);
+ goto fail;
}
switch (json_object_get_type(argsJ)) {
long count;
case json_type_object:
- mixer->streams[index] = AttachOneStream(mixer, uid, prefix, argsJ);
- if (!mixer->streams[index]) {
+ newStream = streamCreate(mixer, uid, prefix, argsJ);
+ if (!newStream) {
AFB_ReqFailF(request, "bad-stream", "mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
+
break;
case json_type_array:
count = json_object_array_length(argsJ);
- if (count > (mixer->max.streams - index)) {
+ if (count > (mixer->max.streams - mixer->nbStreams)) {
AFB_ReqFailF(request,
"too-small",
"mixer=%s max stream=%d", mixer->uid, mixer->max.streams);
- goto OnErrorExit;
+ goto fail;
}
for (int idx = 0; idx < count; idx++) {
json_object *streamJ = json_object_array_get_idx(argsJ, idx);
- mixer->streams[index + idx] = AttachOneStream(mixer, uid, prefix, streamJ);
- if (!mixer->streams[index + idx]) {
+ newStream = streamCreate(mixer, uid, prefix, streamJ);
+ if (!newStream) {
AFB_ReqFailF(request,
"bad-stream",
"%s: mixer=%s invalid stream= %s",
__func__, mixer->uid, json_object_get_string(streamJ));
- goto OnErrorExit;
+ goto fail;
}
}
break;
@@ -577,12 +722,12 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid,
AFB_ReqFailF(request,
"invalid-syntax",
"mixer=%s streams invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ));
- goto OnErrorExit;
+ goto fail;
}
return 0;
-OnErrorExit:
+fail:
AFB_ApiError(mixer->api, "%s FAILED\n", __func__);
return -1;
}
diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c
index c8ab107..299037f 100644
--- a/plugins/alsa/alsa-api-zones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -25,166 +25,242 @@
extern Lua2cWrapperT Lua2cWrap;
PUBLIC AlsaSndZoneT *ApiZoneGetByUid(SoftMixerT *mixer, const char *target) {
+ AlsaSndZoneT * zone = NULL;
- assert(mixer->zones[0]);
+ if (mixer->nbZones == 0) {
+ AFB_ApiError(mixer->api, "%s mixer=%s does not have any zone", __func__, mixer->uid);
+ goto fail;
+ }
// search for subdev into every registered loop
- for (int idx = 0; mixer->zones[idx]; idx++) {
- if (mixer->zones[idx]->uid && !strcasecmp(mixer->zones[idx]->uid, target)) {
- return mixer->zones[idx];
- }
+ cds_list_for_each_entry(zone, &mixer->zones.list, list) {
+ if (zone->uid && !strcasecmp(zone->uid, target)) {
+ return zone;
+ }
}
- AFB_ApiError(mixer->api, "ApiZoneGetByUid mixer=%s fail to find zone=%s", mixer->uid, target);
+fail:
+ AFB_ApiError(mixer->api, "%s mixer=%s fail to find zone=%s", __func__, mixer->uid, target);
return NULL;
}
STATIC AlsaPcmChannelT* ProcessOneChannel(SoftMixerT *mixer, const char* uid, json_object *channelJ) {
+
AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT));
+ if (!channel) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ CDS_INIT_LIST_HEAD(&channel->list);
+
+ channel->volume = -1;
- int error = wrap_json_unpack(channelJ, "{ss,si !}"
+ int error = wrap_json_unpack(channelJ, "{ss,si,s?f !}"
, "target", &channel->uid
, "channel", &channel->port
+ , "volume", &channel->volume
);
- if (error) goto OnErrorExit;
+ if (error)
+ goto fail;
channel->uid = strdup(channel->uid);
+ if (!channel->uid) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_channel;
+ }
+
+ AFB_ApiDebug(mixer->api, "%s: uid %s, channel uid %s, port %d, volume %f",
+ __func__, uid, channel->uid, channel->port, channel->volume);
+
return channel;
-OnErrorExit:
- AFB_ApiError(mixer->api, "ProcessOneChannel: zone=%s channel: missing (target|channel) json=%s", uid, json_object_get_string(channelJ));
+fail_channel:
+ free(channel);
+fail:
+ AFB_ApiError(mixer->api, "%s: zone=%s channel: missing (target|channel) json=%s", __func__, uid, json_object_get_string(channelJ));
return NULL;
}
-STATIC AlsaSndZoneT *AttacheOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) {
- AlsaSndZoneT *zone = calloc(1, sizeof (AlsaSndZoneT));
+STATIC AlsaSndZoneT *AttachOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) {
+
+ AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid);
+
json_object *sinkJ = NULL, *sourceJ = NULL;
size_t count;
int error;
+ AlsaSndZoneT *zone = calloc(1, sizeof (AlsaSndZoneT));
+ if (!zone) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ CDS_INIT_LIST_HEAD(&zone->list);
+ CDS_INIT_LIST_HEAD(&zone->sinks.list);
+ CDS_INIT_LIST_HEAD(&zone->sources.list);
+
error = wrap_json_unpack(zoneJ, "{ss,s?o,s?o !}"
, "uid", &zone->uid
, "sink", &sinkJ
, "source", &sourceJ
);
if (error || (!sinkJ && sourceJ)) {
- AFB_ApiNotice(mixer->api, "AttacheOneZone missing 'uid|sink|source' error=%s zone=%s", wrap_json_get_error_string(error), json_object_get_string(zoneJ));
- goto OnErrorExit;
+ AFB_ApiNotice(mixer->api, "%s missing 'uid|sink|source' error=%s zone=%s", __func__, wrap_json_get_error_string(error), json_object_get_string(zoneJ));
+ goto fail_zone;
}
// make sure remain valid even when json object is removed
zone->uid = strdup(zone->uid);
+ if (!zone->uid) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_zone;
+ }
+
+ AlsaPcmChannelT * channel = NULL;
if (sinkJ) {
switch (json_object_get_type(sinkJ)) {
case json_type_object:
- zone->sinks = calloc(2, sizeof (void*));
- zone->sinks[0] = ProcessOneChannel(mixer, zone->uid, sinkJ);
- if (!zone->sinks[0]) goto OnErrorExit;
+
+ channel = ProcessOneChannel(mixer, zone->uid, sinkJ);
+ if (!channel)
+ goto fail_uid;
+
+ zone->nbSinks++;
+ cds_list_add_tail(&channel->list, &zone->sinks.list);
break;
case json_type_array:
count = json_object_array_length(sinkJ);
- zone->sinks = calloc(count + 1, sizeof (void*));
+
for (int idx = 0; idx < count; idx++) {
json_object *subdevJ = json_object_array_get_idx(sinkJ, idx);
- zone->sinks[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ);
- if (error) goto OnErrorExit;
+
+ channel = ProcessOneChannel(mixer, zone->uid, subdevJ);
+ if (!channel)
+ goto fail_uid;
+
+ zone->nbSinks++;
+ cds_list_add_tail(&channel->list, &zone->sinks.list);
+
}
break;
default:
- AFB_ApiError(mixer->api, "AttacheOneZone: Mixer=%s Hal=%s zone=%s invalid mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sinkJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: Mixer=%s Hal=%s zone=%s invalid mapping=%s", __func__, mixer->uid, uid, zone->uid, json_object_get_string(sinkJ));
+ goto fail;
}
}
if (sourceJ) {
+
switch (json_object_get_type(sourceJ)) {
case json_type_object:
- zone->sources = calloc(2, sizeof (void*));
- zone->sources[0] = ProcessOneChannel(mixer, zone->uid, sourceJ);
- if (!zone->sources[0]) goto OnErrorExit;
+ channel = ProcessOneChannel(mixer, zone->uid, sourceJ);
+ if (!channel)
+ goto fail_uid;
+ zone->nbSources++;
+ cds_list_add(&channel->list, &zone->sources.list);
break;
case json_type_array:
count = json_object_array_length(sourceJ);
- zone->sources = calloc(count + 1, sizeof (void*));
+
for (int idx = 0; idx < count; idx++) {
json_object *subdevJ = json_object_array_get_idx(sourceJ, idx);
- zone->sources[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ);
- if (error) goto OnErrorExit;
+ channel = ProcessOneChannel(mixer, zone->uid, subdevJ);
+ if (!zone)
+ goto fail_uid;
+
+ zone->nbSources++;
+ cds_list_add(&channel->list, &zone->sources.list);
}
break;
default:
- AFB_ApiError(mixer->api, "AttacheOneZone:Mixer=%s Hal=%s zone=%s mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sourceJ));
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: Mixer=%s Hal=%s zone=%s mapping=%s",
+ __func__, mixer->uid, uid, zone->uid, json_object_get_string(sourceJ));
+ goto fail;
}
}
+ AFB_ApiDebug(mixer->api, "%s uid %s DONE", __func__, uid);
return zone;
-OnErrorExit:
+fail_uid:
+ free((char*)zone->uid);
+fail_zone:
+ free(zone);
+fail:
return NULL;
}
-PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) {
+static void zoneDestroy(SoftMixerT* mixer, void * arg) {
+ AlsaSndZoneT * zone = (AlsaSndZoneT*) arg;
- int index;
+ if (zone->routeConfig) {
+ AFB_ApiDebug(mixer->api, "%s... %s delete route config", __func__, zone->uid);
+ snd_config_delete(zone->routeConfig);
+ snd_config_update();
+ zone->routeConfig = NULL;
+ }
+}
- for (index = 0; index < mixer->max.zones; index++) {
- if (!mixer->zones[index]) break;
- }
+PUBLIC AlsaSndZoneT * zoneCreate(SoftMixerT* mixer, const char * uid, json_object * argsJ) {
+ AlsaSndZoneT * zone = AttachOneZone(mixer, uid, argsJ);
+ if (!zone)
+ goto fail;
+
+ // must be set now; AlsaCreateRoute needs it !
+ mixer->nbZones++;
+ cds_list_add_tail(&zone->list, &mixer->zones.list);
+ AlsaMixerTransactionObjectAdd(mixer->transaction, zone, zoneDestroy);
- if (index == mixer->max.zones) {
- AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones);
- goto OnErrorExit;
+ AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0);
+ if (!routeConfig) {
+ AFB_ApiError(mixer->api,
+ "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route",
+ __func__, mixer->uid, uid, zone->uid);
+ goto fail;
}
+fail:
+ return zone;
+}
+
+PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) {
+
+ AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid);
+
+ if (mixer->nbZones >= mixer->max.zones) {
+ AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones);
+ goto OnErrorExit;
+ }
+
switch (json_object_get_type(argsJ)) {
- long count;
+ long count;
case json_type_object: {
- AlsaSndZoneT * zone = AttacheOneZone(mixer, uid, argsJ);
- if (!zone) {
+ AlsaSndZoneT * newZone = zoneCreate(mixer, uid, argsJ);
+ if (!newZone) {
AFB_ReqFailF(request, "bad-zone", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
-
- // must be set now; AlsaCreateRoute needs it !
- mixer->zones[index] = zone;
- AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0);
- if (!routeConfig) {
- AFB_ApiError(mixer->api,
- "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route",
- __func__, mixer->uid, uid, zone->uid);
- goto OnErrorExit;
- }
break;
}
case json_type_array:
count = json_object_array_length(argsJ);
- if (count > (mixer->max.zones - index)) {
+ if (count > (mixer->max.zones - mixer->nbZones)) {
AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones);
goto OnErrorExit;
}
for (int idx = 0; idx < count; idx++) {
json_object *zoneJ = json_object_array_get_idx(argsJ, idx);
- AlsaSndZoneT * zone = AttacheOneZone(mixer, uid, zoneJ);
- if (!zone) {
+ AlsaSndZoneT * newZone = zoneCreate(mixer, uid, zoneJ);
+ if (!newZone) {
AFB_ReqFailF(request, "bad-zone", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(zoneJ));
goto OnErrorExit;
}
-
- mixer->zones[index + idx] = zone;
- AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0);
- if (!routeConfig) {
- AFB_ApiError(mixer->api,
- "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route",
- __func__, mixer->uid, uid, zone->uid);
- goto OnErrorExit;
- }
-
}
break;
default:
@@ -192,6 +268,7 @@ PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, j
goto OnErrorExit;
}
+ AFB_ApiDebug(mixer->api, "%s uid %s DONE", __func__, uid);
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-bluez.c b/plugins/alsa/alsa-bluez.c
deleted file mode 100644
index 080c3c8..0000000
--- a/plugins/alsa/alsa-bluez.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright(C) 2018 "IoT.bzh"
- * Author Thierry Bultel <thierry.bultel@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.
- *
- *
- */
-
-
-#include "alsa-bluez.h"
-
-#include <stdbool.h>
-#include <dlfcn.h>
-
-#define ALSA_BLUEZ_PROXY_LIB "/usr/lib/alsa-lib/libasound_module_pcm_bluealsa_proxy.so"
-#define ALSA_BLUEZ_PROXY_SETDEVICE "bluealsa_proxy_set_remote_device"
-
-typedef int (*bluealsa_set_remote_device_ptr) (const char * interface, const char * device, const char * profile);
-
-static bluealsa_set_remote_device_ptr bluealsa_proxy_set_remote_device = NULL;
-
-void alsa_bluez_init() {
- static bool initialized = false;
- if (initialized)
- goto failed;
-
-#if SND_LIB_VERSION >= (1<<16|1<<8|6)
- char errbuf[256];
- void * dl = snd_dlopen(ALSA_BLUEZ_PROXY_LIB, RTLD_NOW, errbuf, 256);
-#else
- void * dl = snd_dlopen(ALSA_BLUEZ_PROXY_LIB, RTLD_NOW);
-#endif
- if (!dl) {
- printf("Failed to open bluealsa proxy plugin\n");
- goto failed;
- }
-
- void * func = snd_dlsym(dl, ALSA_BLUEZ_PROXY_SETDEVICE, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));
- if (!func) {
- printf("Unable to find %s symbol\n", ALSA_BLUEZ_PROXY_SETDEVICE);
- goto failed;
- }
-
- bluealsa_proxy_set_remote_device = func;
- initialized = true;
-
-failed:
- return;
-}
-
-int alsa_bluez_set_remote_device(const char * interface, const char * device, const char * profile) {
- if (!bluealsa_proxy_set_remote_device)
- return -1;
-
- return bluealsa_proxy_set_remote_device(interface,device,profile);
-}
-
-
diff --git a/plugins/alsa/alsa-bluez.h b/plugins/alsa/alsa-bluez.h
deleted file mode 100644
index c288d38..0000000
--- a/plugins/alsa/alsa-bluez.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright(C) 2018 "IoT.bzh"
- * Author Thierry Bultel <thierry.bultel@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.
- *
- */
-
-#ifndef __INC_ALSA_BLUEZ_H
-#define __INC_ALSA_BLUEZ_H
-
-#include <alsa/asoundlib.h>
-
-extern void alsa_bluez_init();
-extern int alsa_bluez_set_remote_device(const char * interface, const char * device, const char * profile);
-
-#endif /* __INC_ALSA_BLUEZ_H */
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index 7a0c476..505be1e 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -25,12 +25,11 @@ for the specific language governing permissions and
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
-#include "alsa-bluez.h"
#include <pthread.h>
#include <sys/syscall.h>
-typedef struct {
+typedef struct SubscribeHandleT_ {
SoftMixerT *mixer;
sd_event_source* evtsrc;
const char* uid;
@@ -151,24 +150,6 @@ OnErrorExit:
return NULL;
}
-PUBLIC snd_ctl_t *AlsaCtlOpenCtl(SoftMixerT *mixer, const char *cardid) {
- int error;
- snd_ctl_t *ctl;
-
- if (!cardid) goto OnErrorExit;
-
- if ((error = snd_ctl_open(&ctl, cardid, SND_CTL_READONLY)) < 0) {
- cardid = "Not Defined";
- goto OnErrorExit;
- }
-
- return ctl;
-
-OnErrorExit:
- AFB_ApiError(mixer->api, "AlsaCtlOpenCtl: fail to find sndcard by id= %s", cardid);
- return NULL;
-}
-
STATIC void CtlElemIdDisplay(SoftMixerT *mixer, snd_ctl_elem_info_t *elemInfo, snd_ctl_elem_value_t *elemData) {
@@ -275,7 +256,7 @@ PUBLIC int CtlElemIdSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_ele
OnErrorExit:
numid = snd_ctl_elem_info_get_numid(elemInfo);
name = snd_ctl_elem_info_get_name(elemInfo);
- AFB_ApiError(mixer->api, "CtlElemIdSetInt: numid=%d name=%s not writable", numid, name);
+ AFB_ApiError(mixer->api, "%s: numid=%d name=%s not writable", __func__, numid, name);
return -1;
}
@@ -290,26 +271,42 @@ PUBLIC snd_ctl_card_info_t *AlsaCtlGetCardInfo(SoftMixerT *mixer, const char *ca
/* "bluealsa" is the name of the control external plugin
* (https://www.alsa-project.org/alsa-doc/alsa-lib/ctl_external_plugins.html)
*/
+
if (strstr(cardid, "bluealsa")) {
- cardid="bluealsa";
- alsa_bluez_init();
+ cardid = strdup("bluealsa");
+ if (cardid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
}
AFB_ApiNotice(mixer->api, "Opening card control '%s'", cardid);
if ((error = snd_ctl_open(&ctl, cardid, SND_CTL_READONLY)) < 0) {
cardid = "Not Defined";
- goto OnErrorExit;
+ goto fail;
}
snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof());
+ if (cardInfo == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_ctl;
+ }
+
if ((error = snd_ctl_card_info(ctl, cardInfo)) < 0) {
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s fail to find sndcard by id= %s", __func__, cardid);
+ goto fail_card_info;
}
+
+ snd_ctl_close(ctl);
return cardInfo;
-OnErrorExit:
- AFB_ApiError(mixer->api, "AlsaCtlGetInfo: fail to find sndcard by id= %s", cardid);
+fail_card_info:
+ free(cardInfo);
+fail_ctl:
+ snd_ctl_close(ctl);
+fail:
+ AFB_ApiError(mixer->api, "%s: fail to find sndcard by id= %s", __func__, cardid);
return NULL;
}
@@ -495,50 +492,76 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
snd_ctl_event_t *eventId;
snd_ctl_elem_id_t *elemId;
long value;
- int index;
+
+ AFB_ApiDebug(mixer->api, "%s event on card %s", __func__, sndcard->cid.cardid);
if ((revents & EPOLLHUP) != 0) {
AFB_ApiNotice(mixer->api, "%s hanghup [card:%s disconnected]", __func__, sHandle->uid);
goto OnSuccessExit;
}
- if ((revents & EPOLLIN) == 0) goto OnSuccessExit;
+ if ((revents & EPOLLIN) == 0) {
+ AFB_ApiNotice(mixer->api, "%s: no events", __func__);
+ goto OnSuccessExit;
+ }
// initialise event structure on stack
snd_ctl_event_alloca(&eventId);
snd_ctl_elem_id_alloca(&elemId);
error = snd_ctl_read(sndcard->ctl, eventId);
- if (error < 0) goto OnErrorExit;
+ if (error < 0) {
+ AFB_ApiDebug(mixer->api, "%s: failed to read control", __func__);
+ goto OnErrorExit;
+ }
// we only process sndctrl element
- if (snd_ctl_event_get_type(eventId) != SND_CTL_EVENT_ELEM) goto OnSuccessExit;
+ if (snd_ctl_event_get_type(eventId) != SND_CTL_EVENT_ELEM) {
+ AFB_ApiDebug(mixer->api, "%s: bad event type", __func__);
+ goto OnSuccessExit;
+ }
// we only process value changed events
unsigned int eventMask = snd_ctl_event_elem_get_mask(eventId);
- if (!(eventMask & SND_CTL_EVENT_MASK_VALUE)) goto OnSuccessExit;
+ if (!(eventMask & SND_CTL_EVENT_MASK_VALUE)) {
+ goto OnSuccessExit;
+ }
// extract element from event and get value
snd_ctl_event_elem_get_id(eventId, elemId);
error = CtlElemIdGetLong(mixer, sHandle->sndcard, elemId, &value);
- if (error) goto OnErrorExit;
+ if (error) {
+ AFB_ApiDebug(mixer->api, "%s: failed to get elem id", __func__);
+ goto OnErrorExit;
+ }
// get numdid and name from elemId
snd_ctl_elem_info_t *elemInfo;
snd_ctl_elem_info_alloca(&elemInfo);
snd_ctl_elem_info_set_id(elemInfo, elemId);
- if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) goto OnErrorExit;
+ if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) {
+ AFB_ApiDebug(mixer->api, "%s: failed to get elem info", __func__);
+ goto OnErrorExit;
+ }
int numid = snd_ctl_elem_info_get_numid(elemInfo);
+
+ AFB_ApiDebug(mixer->api, "%s: event on elem id %d", __func__, numid);
+
const char *name= snd_ctl_elem_info_get_name(elemInfo);
- for (index = 0; sndcard->registry[index]; index++) {
- RegistryEntryPcmT * reg = sndcard->registry[index];
- snd_pcm_t * pcm = reg->pcm->handle;
- if (reg->numid == numid) {
+ RegistryEntryPcmT * registry = NULL;
+
+ bool found = false;
+
+ cds_list_for_each_entry(registry, &sndcard->registryList, cardListEntry) {
+ snd_pcm_t * pcm = registry->pcm->handle;
+
+ if (registry->numid == numid) {
+ found = true;
int ret;
- switch (reg->type) {
+ switch (registry->type) {
case FONTEND_NUMID_RUN:
- AlsaPcmCopyMuteSignal(mixer, reg->pcm, !value);
+ AlsaPcmCopyMuteSignal(mixer, registry->pcm, !value);
ret = snd_pcm_pause(pcm, (int) (!value));
AFB_ApiNotice(mixer->api, "%s:%s numid=%d name=%s active=%ld ret %d",
__func__, sHandle->uid, numid, name, value, ret);
@@ -548,8 +571,8 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
break;
case FONTEND_NUMID_PAUSE:
- AlsaPcmCopyMuteSignal(mixer, reg->pcm, value);
- ret = snd_pcm_pause(reg->pcm->handle, (int) value);
+ AlsaPcmCopyMuteSignal(mixer, registry->pcm, value);
+ ret = snd_pcm_pause(registry->pcm->handle, (int) value);
AFB_ApiNotice(mixer->api, "%s:%s numid=%d name=%s pause=%ld ret %d",
__func__, sHandle->uid, numid, name, value, ret);
if (ret < 0) {
@@ -558,53 +581,49 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
break;
case FONTEND_NUMID_IGNORE:
default:
- AFB_ApiInfo(mixer->api,
+ AFB_ApiDebug(mixer->api,
"%s:%s numid=%d name=%s ignored=%ld",
__func__, sHandle->uid, numid, name, value);
}
break;
}
}
- if (index == sndcard->rcount) {
- AFB_ApiNotice(mixer->api, "%s:%s numid=%d (unknown)", __func__, sHandle->uid, numid);
+ if (!found) {
+ AFB_ApiNotice(mixer->api, "%s:%s numid=%d is unknown", __func__, sHandle->uid, numid);
}
OnSuccessExit:
return 0;
OnErrorExit:
- AFB_ApiInfo(mixer->api, "%s: ignored unsupported event", __func__);
+ AFB_ApiDebug(mixer->api, "%s: ignored unsupported event", __func__);
return 0;
}
-PUBLIC snd_ctl_t* AlsaCrlFromPcm(SoftMixerT *mixer, snd_pcm_t *pcm) {
- char buffer[32];
- int error;
- snd_ctl_t *ctl;
- snd_pcm_info_t *pcmInfo;
-
- snd_pcm_info_alloca(&pcmInfo);
- if ((error = snd_pcm_info(pcm, pcmInfo)) < 0) goto OnErrorExit;
- int pcmCard = snd_pcm_info_get_card(pcmInfo);
- snprintf(buffer, sizeof (buffer), "hw:%i", pcmCard);
- if ((error = snd_ctl_open(&ctl, buffer, SND_CTL_READONLY)) < 0) goto OnErrorExit;
+PUBLIC int AlsaCtlUnSubscribe(SoftMixerT *mixer, AlsaSndCtlT *sndcard) {
+ SubscribeHandleT * handle = sndcard->eventSubscribeHandle;
- return ctl;
-
-OnErrorExit:
- return NULL;
+ sd_event_source_unref(handle->evtsrc);
+ free(handle);
+ return 0;
}
PUBLIC int AlsaCtlSubscribe(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *sndcard) {
int error;
char string [32];
struct pollfd pfds;
- SubscribeHandleT *handle = malloc(sizeof (SubscribeHandleT));
+
+ SubscribeHandleT *handle = calloc(1, sizeof (SubscribeHandleT));
+ if (handle == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
handle->mixer = mixer;
handle->sndcard = sndcard;
handle->uid = uid;
+ sndcard->eventSubscribeHandle = handle;
// subscribe for sndctl events attached to cardid
if ((error = snd_ctl_subscribe_events(handle->sndcard->ctl, 1)) < 0) {
@@ -636,36 +655,63 @@ OnErrorExit:
return -1;
}
-PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid) {
- int index;
- AFB_ApiInfo(mixer->api,"%s: registered ID %d.", __func__, numid);
+PUBLIC void AlsaCtlUnregister(SoftMixerT* mixer, void * arg) {
- for (index = 0; index < sndcard->rcount; index++) {
- if (!sndcard->registry[index]) break;
- }
+ RegistryEntryPcmT * registryEntry = (RegistryEntryPcmT*) arg;
- if (index == sndcard->rcount) {
+ AFB_ApiDebug(mixer->api,"%s: card %s : Unregistering control ID %d (%ld in registry).",
+ __func__, registryEntry->sndcard->cid.cardid, registryEntry->numid, registryEntry->sndcard->nbRegistry);
+ AlsaSndCtlT * sndcard = registryEntry->sndcard;
+
+ cds_list_del(&registryEntry->cardListEntry);
+ sndcard->nbRegistry--;
+
+ if (sndcard->nbRegistry <= 0)
+ AlsaCtlUnSubscribe(mixer, sndcard);
+
+ free(registryEntry);
+ AFB_ApiDebug(mixer->api,"%s: DONE", __func__);
+}
+
+
+PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid) {
+
+ AFB_ApiDebug(mixer->api,"%s: registering control ID %d to %s", __func__, numid, sndcard->cid.cardid);
+
+ if (sndcard->nbRegistry >= SMIXER_SUBDS_CTLS ) {
AFB_ApiError(mixer->api,
- "%s cardid=%s cardname=%s to many audio stream max=%ld",
- __func__, sndcard->cid.cardid, sndcard->cid.longname, sndcard->rcount);
- goto OnErrorExit;
+ "%s cardid='%s' cardname='%s' too many registered controls max=%ld",
+ __func__, sndcard->cid.cardid, sndcard->cid.longname, sndcard->nbRegistry);
+ goto fail;
}
// If 1st registration then register to card event
- if (index == 0) {
+ if (sndcard->nbRegistry == 0) {
AlsaCtlSubscribe(mixer, sndcard->cid.cardid, sndcard);
}
// store PCM in order to pause/resume depending on event
RegistryEntryPcmT *entry = calloc(1, sizeof (RegistryEntryPcmT));
- sndcard->registry[index] = entry;
+ if (entry == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+ CDS_INIT_LIST_HEAD(&entry->cardListEntry);
+
entry->pcm = pcmdev;
entry->numid = numid;
entry->type = type;
+ entry->sndcard = sndcard;
+
+ sndcard->nbRegistry++;
+
+ AlsaMixerTransactionObjectAdd(mixer->transaction, entry, AlsaCtlUnregister);
+
+ cds_list_add_tail(&entry->cardListEntry, &sndcard->registryList);
return 0;
-OnErrorExit:
+fail:
return -1;
}
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index 25872ae..a40f4bf 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -34,6 +34,16 @@ for the specific language governing permissions and
static int xrun(snd_pcm_t * pcm, int error);
static int suspend(snd_pcm_t * pcm, int error);
+typedef enum {
+ PCM_COPY_MUTE,
+ PCM_COPY_UNMUTE,
+ PCM_COPY_END,
+ PCM_COPY_LAST // Do not put anything after
+} PcmCopyEventType;
+
+typedef struct {
+ PcmCopyEventType eventType;
+} PcmCopyEvent;
STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
int pcmSampleSize;
@@ -81,12 +91,13 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
snd_pcm_access_t access;
AlsaPcmHwInfoT * opts = pcm->params;
+ const char * card = pcm->cid.cardid;
const char * modeS = mode==SND_PCM_STREAM_PLAYBACK?"PLAYBACK":"CAPTURE";
- AFB_ApiInfo(mixer->api,
- "%s: mixer info %s uid %s , pcm %s, mode %s",
- __func__, mixer->info, mixer->uid, pcm->cid.cardid, modeS);
+ AFB_ApiDebug(mixer->api,
+ "%s: mixer info %s uid %s, pcm %s, mode %s",
+ __func__, mixer->info, mixer->uid, card, modeS);
// retrieve hardware config from PCM
snd_pcm_hw_params_alloca(&pxmHwParams);
@@ -96,7 +107,7 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
goto OnErrorExit;
}
- AFB_ApiDebug(mixer->api, "PARAMS before:\n");
+ AFB_ApiDebug(mixer->api, "(%s): PARAMS before:", card);
AlsaDumpPcmParams(mixer, pxmHwParams);
if (!opts->access)
@@ -106,8 +117,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
error = snd_pcm_hw_params_set_access(pcm->handle, pxmHwParams, opts->access);
if (error) {
AFB_ApiError(mixer->api,
- "%s set_access failed (ignore this error): mixer=%s cardid=%s access=%d Fail current=%d mode error=%s",
- __func__, mixer->uid, pcm->cid.cardid, opts->access, access, snd_strerror(error));
+ "%s (%s) set_access failed (ignore this error): mixer=%s access=%d Fail current=%d mode error=%s",
+ __func__, card, mixer->uid, opts->access, access, snd_strerror(error));
//Fulup goto OnErrorExit;
};
@@ -115,8 +126,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
snd_pcm_hw_params_get_format(pxmHwParams, &format);
if ((error = snd_pcm_hw_params_set_format(pcm->handle, pxmHwParams, opts->format)) < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Set_Format=%s (%d) FAILED current=%d error=%s",
- __func__, mixer->uid, pcm->cid.cardid, opts->formatS, opts->format, format, snd_strerror(error));
+ "%s (%s) mixer=%s Set_Format=%s (%d) FAILED current=%d error=%s",
+ __func__, card, mixer->uid, opts->formatS, opts->format, format, snd_strerror(error));
AlsaDumpFormats(mixer, pcm->handle);
goto OnErrorExit;
}
@@ -124,22 +135,22 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
if (opts->rate > 0 ) {
- AFB_ApiInfo(mixer->api," %s: set rate to %d", __func__, opts->rate);
+ AFB_ApiDebug(mixer->api,"%s (%s): set rate to %d", __func__, card, opts->rate);
unsigned int pcmRate = opts->rate;
/* Attempt to set the rate. Failing on a capture dev is acceptable */
error = snd_pcm_hw_params_set_rate_near(pcm->handle, pxmHwParams, &opts->rate, 0);
if ( mode == SND_PCM_STREAM_PLAYBACK && error < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s FailSet_Rate=%d error=%s",
- __func__, mixer->uid, pcm->cid.cardid, opts->rate, snd_strerror(error));
+ "%s (%s): mixer=%s FailSet_Rate=%d error=%s",
+ __func__, card, mixer->uid, opts->rate, snd_strerror(error));
goto OnErrorExit;
}
// check we got requested rate
if (mode == SND_PCM_STREAM_PLAYBACK && opts->rate != pcmRate) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Set_Rate Fail ask=%dHz get=%dHz",
- __func__, mixer->uid, pcm->cid.cardid,pcmRate, opts->rate);
+ "%s (%s): mixer=%s Set_Rate Fail ask=%dHz get=%dHz",
+ __func__, card, mixer->uid, pcmRate, opts->rate);
goto OnErrorExit;
}
}
@@ -147,8 +158,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
if (opts->channels) {
if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Set_Channels=%d Fail error=%s",
- __func__, mixer->uid, pcm->cid.cardid, opts->channels, snd_strerror(error));
+ "%s (%s): mixer=%s Set_Channels=%d Fail error=%s",
+ __func__, card, mixer->uid, opts->channels, snd_strerror(error));
goto OnErrorExit;
};
}
@@ -165,7 +176,7 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
error = snd_pcm_hw_params_get_buffer_time_max(pxmHwParams, &buffer_time, 0);
- AFB_ApiInfo(mixer->api, "HW_BUFFER_TIME MAX is %d\n", buffer_time);
+ AFB_ApiDebug(mixer->api, "(%s) HW_BUFFER_TIME MAX is %d", card, buffer_time);
if (buffer_time > 500000)
buffer_time = 500000;
@@ -178,45 +189,45 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
}
if (period_time > 0) {
- AFB_ApiInfo(mixer->api, "SET PERIOD TIME to %d", period_time);
+ AFB_ApiDebug(mixer->api, "(%s) SET PERIOD TIME to %d", card, period_time);
error = snd_pcm_hw_params_set_period_time_near(pcm->handle, pxmHwParams, &period_time, 0);
}
else {
- AFB_ApiInfo(mixer->api, "SET PERIOD SIZE...");
+ AFB_ApiDebug(mixer->api, "(%s) SET PERIOD SIZE...", card);
error = snd_pcm_hw_params_set_period_size_near(pcm->handle, pxmHwParams, &period_frames, 0);
}
if (error < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail to set period in hwparams error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s Fail to set period in hwparams error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
}
if (buffer_time > 0) {
- AFB_ApiInfo(mixer->api, "SET BUFFER TIME to %d", buffer_time);
+ AFB_ApiDebug(mixer->api, "(%s) SET BUFFER TIME to %d", card, buffer_time);
error = snd_pcm_hw_params_set_buffer_time_near(pcm->handle, pxmHwParams, &buffer_time, 0);
} else {
- AFB_ApiInfo(mixer->api, "SET BUFFER SIZE...");
+ AFB_ApiDebug(mixer->api, "(%s) SET BUFFER SIZE to %ld...", card, buffer_frames);
error = snd_pcm_hw_params_set_buffer_size_near(pcm->handle, pxmHwParams, &buffer_frames);
}
if (error < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail to set buffer in hwparams error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s Fail to set buffer in hwparams error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
}
// store selected values
if ((error = snd_pcm_hw_params(pcm->handle, pxmHwParams)) < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail to apply hwparams error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s Fail to apply hwparams error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
}
- AFB_ApiDebug(mixer->api, "PARAMS after:\n");
+ AFB_ApiDebug(mixer->api, "(%s) PARAMS after:", card);
AlsaDumpPcmParams(mixer, pxmHwParams);
// check we effective hw params after optional format change
@@ -224,13 +235,13 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
snd_pcm_hw_params_get_format(pxmHwParams, &opts->format);
snd_pcm_hw_params_get_rate(pxmHwParams, &opts->rate, 0);
- AFB_ApiInfo(mixer->api, "rate is %d", opts->rate);
+ AFB_ApiDebug(mixer->api, "(%s) rate is %d", card, opts->rate);
opts->sampleSize = AlsaPeriodSize(opts->format);
if (opts->sampleSize == 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail unsupported format format=%d",
- __func__, mixer->uid, pcm->cid.cardid, opts->format);
+ "%s (%s): mixer=%s Fail unsupported format format=%d",
+ __func__, card, mixer->uid, opts->format);
goto OnErrorExit;
}
@@ -240,8 +251,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
snd_pcm_hw_params_get_buffer_size(pxmHwParams, &buffer_size);
if (chunk_size == buffer_size) {
AFB_ApiError(mixer->api,
- "Can't use period equal to buffer size (%lu == %lu)",
- chunk_size, buffer_size);
+ "(%s) Can't use period equal to buffer size (%lu == %lu)",
+ card, chunk_size, buffer_size);
goto OnErrorExit;
}
@@ -263,8 +274,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, n)) < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail set_buffersize error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s Fail set_buffersize error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
};
@@ -285,39 +296,39 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
if (start_threshold > n/2)
start_threshold = n/2;
- AFB_ApiInfo(mixer->api, "CALCULATED START THRESHOLD: %ld", start_threshold);
+ AFB_ApiDebug(mixer->api, "(%s) CALCULATED START THRESHOLD: %ld", card, start_threshold);
if (mode == SND_PCM_STREAM_PLAYBACK) {
start_threshold = 1;
}
- AFB_ApiInfo(mixer->api, "%s: Set start threshold to %ld", modeS, start_threshold);
+ AFB_ApiDebug(mixer->api, "(%s) %s: Set start threshold to %ld", card, modeS, start_threshold);
error = snd_pcm_sw_params_set_start_threshold(pcm->handle, pxmSwParams, start_threshold);
if (error < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s failed set start_threshold, error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s failed set start_threshold, error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
}
// push software params into PCM
if ((error = snd_pcm_sw_params(pcm->handle, pxmSwParams)) < 0) {
AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s Fail to push SW params error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
+ "%s (%s): mixer=%s Fail to push SW params error=%s",
+ __func__, card, mixer->uid, snd_strerror(error));
goto OnErrorExit;
};
AFB_ApiNotice(mixer->api,
- "%s: mixer=%s cardid=%s Done channels=%d rate=%d format=%d access=%d ... done !",
- __func__, mixer->uid, pcm->cid.cardid, opts->channels, opts->rate, opts->format, opts->access);
+ "%s (%s): mixer=%s Done channels=%d rate=%d format=%d access=%d ... DONE !",
+ __func__, card, mixer->uid, opts->channels, opts->rate, opts->format, opts->access);
return 0;
OnErrorExit:
return -1;
}
-STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandle) {
+STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) {
char string[32];
snd_pcm_sframes_t availIn;
@@ -327,19 +338,6 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
int err;
- // PCM has was closed
- if ((pfd->revents & POLLHUP) != 0) {
- AFB_ApiNotice(pcmCopyHandle->api,
- "%s PCM=%s hanghup/disconnected",
- __func__, ALSA_PCM_UID(pcmIn, string));
- goto ExitOnSuccess;
- }
-
- // ignore any non input events. This is not supposed to happen ever
- if ((pfd->revents & EPOLLIN) == 0) {
- goto ExitOnSuccess;
- }
-
// do we have waiting frames ?
availIn = snd_pcm_avail_update(pcmIn);
if (availIn <= 0) {
@@ -465,16 +463,20 @@ static void *readThreadEntry(void *handle) {
AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle;
pcmCopyHandle->tid = (int) syscall(SYS_gettid);
+ int ix;
AFB_ApiNotice(pcmCopyHandle->api,
"%s :%s/%d Started, muted=%d",
__func__, pcmCopyHandle->info, pcmCopyHandle->tid, pcmCopyHandle->pcmIn->mute);
- struct pollfd * mutePfd = &pcmCopyHandle->pollFds[0];
- struct pollfd * framePfd = &pcmCopyHandle->pollFds[1];
+ struct pollfd * eventFd = &pcmCopyHandle->pollFds[0];
+ struct pollfd * framePfds = &pcmCopyHandle->pollFds[1];
- mutePfd->events = POLLIN | POLLHUP;
- framePfd->events = POLLIN | POLLHUP;
+ eventFd->events = POLLIN | POLLHUP;
+
+ for (ix = 0; ix <pcmCopyHandle->nbPcmFds-1; ix++) {
+ framePfds[ix].events = POLLIN | POLLHUP;
+ }
bool muted = pcmCopyHandle->pcmIn->mute;
@@ -492,48 +494,60 @@ static void *readThreadEntry(void *handle) {
if (err == 0) {
/* timeout */
- AFB_ApiDebug(pcmCopyHandle->api, "%s(%s) alive, mute %d", __func__, pcmCopyHandle->pcmIn->cid.cardid, muted );
+// AFB_ApiDebug(pcmCopyHandle->api, "%s(%s) alive, mute %d", __func__, pcmCopyHandle->pcmIn->cid.cardid, muted );
continue;
}
- // handle the un/mute order
- if ((mutePfd->revents & EPOLLIN) != 0) {
- bool mute;
+ // handle the incoming events/mute order
+ if ((eventFd->revents & EPOLLIN) != 0) {
+ PcmCopyEvent event;
- size_t ret = read(mutePfd->fd, &mute, sizeof(mute));
+ size_t ret = read(eventFd->fd, &event, sizeof(event));
if (ret <= 0)
continue;
- if (mute == muted)
- continue;
-
- muted = mute;
-
- if (muted) {
- readSuspend(pcmCopyHandle);
- } else {
- readResume(pcmCopyHandle);
+ switch (event.eventType) {
+ case PCM_COPY_MUTE:
+ if (!muted) {
+ readSuspend(pcmCopyHandle);
+ muted = true;
+ }
+ break;
+ case PCM_COPY_UNMUTE:
+ if (muted) {
+ readResume(pcmCopyHandle);
+ muted = false;
+ };
+ break;
+ case PCM_COPY_END:
+ AFB_ApiDebug(pcmCopyHandle->api, "%s -> EXIT", __func__);
+ goto done;
+ break;
+ case PCM_COPY_LAST:
+ default:
+ AFB_ApiError(pcmCopyHandle->api, "%s: Unexpected event 0x%x", __func__, event.eventType);
+ break;
}
continue;
}
unsigned short revents;
- int ret = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, &pcmCopyHandle->pollFds[1], 1, &revents);
+ int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFds, &revents);
- if (ret == -ENODEV) {
+ if (res == -ENODEV) {
sleep(1);
continue;
}
- if (framePfd->revents & POLLHUP) {
+ if (revents & POLLHUP) {
AFB_ApiNotice(pcmCopyHandle->api, "Frame POLLHUP");
continue;
}
- AlsaPcmReadCB(&pcmCopyHandle->pollFds[1], pcmCopyHandle);
+ AlsaPcmReadCB(pcmCopyHandle);
}
-
+done:
pthread_exit(0);
return NULL;
}
@@ -554,6 +568,8 @@ static void *writeThreadEntry(void *handle) {
snd_pcm_status(pcmOut, pcmOutStatus);
pcmOutSize = snd_pcm_status_get_avail_max(pcmOutStatus);
+ const char * cardid = pcmCopyHandle->pcmOut->cid.cardid;
+
/* This threshold is the expected space available in the hw output buffer
* The aim is to wait to have a significant amount of space, in order to
* avoid to write to the device too often, or take a very small amount of
@@ -566,17 +582,23 @@ static void *writeThreadEntry(void *handle) {
sem_wait(&pcmCopyHandle->sem);
while (true) {
+
+ if (pcmCopyHandle->ending) {
+ AFB_ApiDebug(pcmCopyHandle->api, "%s: EXIT", __func__);
+ goto done;
+ }
+
snd_pcm_sframes_t used, nbWritten;
snd_pcm_sframes_t availOut = snd_pcm_avail(pcmOut);
if (availOut < 0) {
if (availOut == -EPIPE) {
- AFB_ApiDebug(pcmCopyHandle->api, "write update EPIPE");
+ AFB_ApiDebug(pcmCopyHandle->api, "%s: write update EPIPE", cardid);
xrun(pcmOut, (int)availOut);
continue;
}
if (availOut == -ESTRPIPE) {
- AFB_ApiDebug(pcmCopyHandle->api, "write update ESTRPIPE");
+ AFB_ApiDebug(pcmCopyHandle->api, "%s: write update ESTRPIPE", cardid);
suspend(pcmOut, (int)availOut);
continue;
}
@@ -606,7 +628,8 @@ static void *writeThreadEntry(void *handle) {
if (nbWritten <= 0) {
if (nbWritten == -EPIPE) {
int err = xrun(pcmOut, (int)nbWritten);
- AFB_ApiDebug(pcmCopyHandle->api, "XXX write EPIPE (%d), recov %d", ++pcmCopyHandle->write_err_count , err);
+ AFB_ApiDebug(pcmCopyHandle->api, "XXX %s write EPIPE (%d), recov %d",
+ pcmCopyHandle->pcmOut->cid.cardid, ++pcmCopyHandle->write_err_count , err);
continue;
} else if (nbWritten == -ESTRPIPE) {
@@ -620,30 +643,68 @@ static void *writeThreadEntry(void *handle) {
}
}
-
+done:
pthread_exit(0);
return NULL;
}
PUBLIC int AlsaPcmCopyMuteSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn, bool mute) {
- ssize_t ret = write(pcmIn->muteFd, &mute, sizeof(mute));
+
+ PcmCopyEvent event;
+ event.eventType = mute?PCM_COPY_MUTE:PCM_COPY_UNMUTE;
+ ssize_t ret = write(pcmIn->eventFd, &event, sizeof(event));
+ (void) ret;
+ return 0;
+}
+
+static int AlsaPcmCopyEndSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn) {
+ PcmCopyEvent event;
+ event.eventType = PCM_COPY_END;
+ ssize_t ret = write(pcmIn->eventFd, &event, sizeof(event));
(void) ret;
return 0;
}
+static int AlsaPcmCopyEnd(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) {
+
+ handle->ending = true;
+ // wake up the reader through the eventfd
+ AlsaPcmCopyEndSignal(mixer, handle->pcmIn);
+ // wake up the writer through the eventfd
+ sem_post(&handle->sem);
+
+ return 0;
+}
+
+PUBLIC int AlsaPcmCopyStop(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) {
-PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) {
+ AFB_ApiDebug(mixer->api, "%s: Stopping copy threads", __func__);
+
+ AlsaPcmCopyEnd(mixer, handle);
+
+ if (pthread_join(handle->wthread, NULL) != 0)
+ AFB_ApiDebug(mixer->api, "%s: Failed to join write thread", __func__);
+
+ if (pthread_join(handle->rthread, NULL) != 0)
+ AFB_ApiDebug(mixer->api, "%s: Failed to join read thread", __func__);
+
+ AFB_ApiDebug(mixer->api, "%s: Copy threads are STOPPED", __func__);
+
+ return 0;
+
+}
+
+PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) {
char string[32];
int error;
+ AlsaPcmCopyHandleT *cHandle=NULL;
// Fulup need to check https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html
AlsaDumpPcmInfo(mixer,"PcmIn",pcmIn->handle);
AlsaDumpPcmInfo(mixer,"PcmOut",pcmOut->handle);
- AFB_ApiInfo(mixer->api, "%s: Configure CAPTURE PCM", __func__);
-
/* remember configuration of capture */
pcmIn->params = (AlsaPcmHwInfoT*)malloc(sizeof(AlsaPcmHwInfoT));
memcpy(pcmIn->params, opts, sizeof(AlsaPcmHwInfoT));
@@ -654,7 +715,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
pcmIn->mixer = mixer;
pcmOut->mixer = mixer;
- AFB_ApiInfo(mixer->api, "%s: Configure CAPTURE PCM", __func__);
+ AFB_ApiDebug(mixer->api, "%s: Configure CAPTURE PCM", __func__);
// prepare PCM for capture and replay
error = AlsaPcmConf(mixer, pcmIn, SND_PCM_STREAM_CAPTURE);
@@ -663,7 +724,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
goto OnErrorExit;
}
- AFB_ApiInfo(mixer->api, "%s: Configure PLAYBACK PCM", __func__);
+ AFB_ApiDebug(mixer->api, "%s: Configure PLAYBACK PCM", __func__);
// input and output should match
error = AlsaPcmConf(mixer, pcmOut, SND_PCM_STREAM_PLAYBACK);
@@ -696,7 +757,11 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
goto OnErrorExit;
};
- AlsaPcmCopyHandleT *cHandle= calloc(1, sizeof(AlsaPcmCopyHandleT));
+ cHandle = calloc(1, sizeof(AlsaPcmCopyHandleT));
+ if (cHandle == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
cHandle->info = "pcmCpy";
cHandle->pcmIn = pcmIn;
@@ -705,17 +770,21 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
cHandle->channels = opts->channels;
cHandle->frame_size = (snd_pcm_format_physical_width(opts->format) / 8) * opts->channels;
+ AFB_ApiDebug(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size);
+ AFB_ApiDebug(mixer->api, "%s: Buffer delay is %d ms", __func__, stream->delayms);
- AFB_ApiInfo(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size);
-
- snd_pcm_uframes_t nbFrames = 2 * opts->rate; // Exactly 2 second of buffer
+ snd_pcm_uframes_t nbFrames = (stream->delayms * opts->rate)/1000;
cHandle->rbuf = alsa_ringbuf_new(nbFrames, cHandle->frame_size);
+ if (!cHandle->rbuf) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
cHandle->read_err_count = 0;
cHandle->write_err_count = 0;
- AFB_ApiInfo(mixer->api, "%s Copy buffer nbframes is %zu", __func__, nbFrames);
+ AFB_ApiDebug(mixer->api, "%s Copy buffer: nbframes is %zu", __func__, nbFrames);
// get FD poll descriptor for capture PCM
int pcmInCount = snd_pcm_poll_descriptors_count(pcmIn->handle);
@@ -724,17 +793,16 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
"%s: Fail pcmIn=%s get fds count error=%s",
__func__, ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error));
goto OnErrorExit;
- };
+ }
- if (pcmInCount > 1) {
- AFB_ApiError(mixer->api,
- "%s: Fail, pcmIn=%s; having more than one FD on capture PCM is not supported (here, %d)",
- __func__, ALSA_PCM_UID(pcmOut->handle, string) , pcmInCount);
+ cHandle->nbPcmFds = pcmInCount+1;
+ cHandle->pollFds = (struct pollfd *) malloc((cHandle->nbPcmFds)*sizeof(struct pollfd));
+ if (cHandle->pollFds == NULL){
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
}
- struct pollfd pcmInFd;
- if ((error = snd_pcm_poll_descriptors(pcmIn->handle, &pcmInFd, 1)) < 0) {
+ if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFds+1, pcmInCount)) < 0) {
AFB_ApiError(mixer->api,
"%s: Fail pcmIn=%s get pollfds error=%s",
__func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
@@ -742,27 +810,22 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
};
// create the mute pipe
- int pMuteFd[2];
- error = pipe(pMuteFd);
+ int eventFdPipe[2];
+ error = pipe(eventFdPipe);
if (error < 0) {
AFB_ApiError(mixer->api,
"Unable to create the mute signaling pipe");
goto OnErrorExit;
}
- struct pollfd mutePFd;
+ struct pollfd * eventPollFd = &cHandle->pollFds[0];
// read end
- mutePFd.fd = pMuteFd[0];
- mutePFd.events = POLLIN;
- mutePFd.revents = 0;
+ eventPollFd->fd = eventFdPipe[0];
+ eventPollFd->events = POLLIN;
+ eventPollFd->revents = 0;
// write end
- pcmIn->muteFd = pMuteFd[1];
-
- cHandle->pollFds[0] = mutePFd;
- cHandle->pollFds[1] = pcmInFd;
-
- cHandle->nbPcmFds = pcmInCount+1;
+ pcmIn->eventFd = eventFdPipe[1];
error = sem_init(&cHandle->sem, 0 , 0);
if (error < 0) {
@@ -818,11 +881,22 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
__func__, ALSA_PCM_UID(pcmOut->handle, string), strerror(error));
}
+ stream->copy = cHandle;
+
return 0;
OnErrorExit:
AFB_ApiError(mixer->api, "%s: - pcmIn=%s" , __func__, ALSA_PCM_UID(pcmIn->handle, string));
AFB_ApiError(mixer->api, "%s: - pcmOut=%s", __func__, ALSA_PCM_UID(pcmOut->handle, string));
+
+ if (cHandle &&cHandle->pollFds) {
+ free (cHandle->pollFds);
+ cHandle->pollFds = NULL;
+ }
+
+ if (cHandle)
+ free(cHandle);
+
return -1;
}
diff --git a/plugins/alsa/alsa-effect-ramp.c b/plugins/alsa/alsa-effect-ramp.c
index 312bdfa..3111d20 100644
--- a/plugins/alsa/alsa-effect-ramp.c
+++ b/plugins/alsa/alsa-effect-ramp.c
@@ -84,7 +84,7 @@ PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamA
long curvol, newvol;
const char *uid, *volS;
json_object *volJ;
- int error, index;
+ int error;
uint64_t usec;
int count = 0;
@@ -93,7 +93,9 @@ PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamA
, "volume", &volJ
);
if (error) {
- AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-json should {uid:ramp, vol:[+,-,=]value} ramp=%s", mixer->uid, stream->uid, json_object_get_string(rampJ));
+ AFB_ApiError(mixer->api,
+ "%s: mixer=%s stream=%s invalid-json should {uid:ramp, vol:[+,-,=]value} ramp=%s",
+ __func__, mixer->uid, stream->uid, json_object_get_string(rampJ));
goto OnErrorExit;
}
@@ -125,7 +127,9 @@ PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamA
}
if (count != 1) {
- AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-numeric expect {uid:%s, vol:[+,-,=]value} get vol:%s", mixer->uid, stream->uid, uid, json_object_get_string(volJ));
+ AFB_ApiError(mixer->api,
+ "%s: mixer=%s stream=%s invalid-numeric expect {uid:%s, vol:[+,-,=]value} get vol:%s",
+ __func__, mixer->uid, stream->uid, uid, json_object_get_string(volJ));
goto OnErrorExit;
}
break;
@@ -134,35 +138,47 @@ PUBLIC int AlsaVolRampApply(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaStreamA
break;
default:
- AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s invalid-type expect {uid:%s, vol:[+,-,=]value} get vol:%s", mixer->uid, stream->uid, uid, json_object_get_string(volJ));
+ AFB_ApiError(mixer->api,
+ "%s :mixer=%s stream=%s invalid-type expect {uid:%s, vol:[+,-,=]value} get vol:%s",
+ __func__, mixer->uid, stream->uid, uid, json_object_get_string(volJ));
goto OnErrorExit;
}
error = AlsaCtlNumidGetLong(mixer, sndcard, stream->volume, &curvol);
if (error) {
- AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s ramp=%s Fail to get volume from numid=%d", mixer->uid, stream->uid, uid, stream->volume);
+ AFB_ApiError(mixer->api,
+ "%s: mixer=%s stream=%s ramp=%s Fail to get volume from numid=%d",
+ __func__, mixer->uid, stream->uid, uid, stream->volume);
goto OnErrorExit;
}
// search for ramp uid in mixer
- for (index=0; index<= mixer->max.ramps; index++) {
- if (!strcasecmp(mixer->ramps[index]->uid, uid)) {
+ AlsaVolRampT * ramp;
+ bool found = false;
+ cds_list_for_each_entry(ramp, &mixer->ramps.list, list) {
+ if (ramp->uid && !strcasecmp(ramp->uid, uid)) {
break;
}
}
- if (index == mixer->max.ramps) {
- AFB_ApiError(mixer->api, "AlsaVolRampApply:mixer=%s stream=%s ramp=%s does not exit", mixer->uid, stream->uid, uid);
+ if (!found) {
+ AFB_ApiError(mixer->api,
+ "%s: mixer=%s stream=%s ramp=%s does not exit",
+ __func__, mixer->uid, stream->uid, uid);
goto OnErrorExit;
}
VolRampHandleT *rHandle = calloc(1, sizeof (VolRampHandleT));
+ if (rHandle == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
rHandle->uid = stream->uid;
rHandle->numid = stream->volume;
rHandle->sndcard = sndcard;
rHandle->mixer = mixer;
- rHandle->ramp = mixer->ramps[index];
+ rHandle->ramp = ramp;
rHandle->target = newvol;
rHandle->current = curvol;
rHandle->sdLoop = mixer->sdLoop;
diff --git a/plugins/alsa/alsa-plug-dmix.c b/plugins/alsa/alsa-plug-dmix.c
index 3c6d62e..fe9819b 100644
--- a/plugins/alsa/alsa-plug-dmix.c
+++ b/plugins/alsa/alsa-plug-dmix.c
@@ -28,14 +28,20 @@ ALSA_PLUG_PROTO(dmix);
PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open) {
snd_config_t *dmixConfig = NULL, *slaveConfig = NULL, *elemConfig = NULL, *pcmConfig=NULL;
- AlsaPcmCtlT *pcmPlug= calloc(1,sizeof(AlsaPcmCtlT));
AlsaSndCtlT *sndSlave=pcmSlave->sndcard;
+
+ AlsaPcmCtlT *pcmPlug = AlsaPcmCtlNew(mixer, pcmName);
+ if (pcmPlug == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
pcmPlug->cid.cardid=pcmName;
char * fullPcmName = NULL;
int error=0;
- AFB_ApiInfo(mixer->api, "%s: %s, slave %s, cardid %s (dev %d, subdev %d)\n",
+ AFB_ApiDebug(mixer->api, "%s: %s, slave %s, cardid %s (dev %d, subdev %d)",
__func__,
pcmName,
pcmSlave->uid,
@@ -46,9 +52,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS
error = asprintf(&fullPcmName,"%s,%d,%d", sndSlave->cid.cardid, sndSlave->cid.device, sndSlave->cid.subdev);
if (error == -1) {
- AFB_ApiError(mixer->api,
- "%s: Insufficient memory",
- __func__);
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
}
@@ -92,7 +96,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS
* Trying to set another value later leads to silent failure
* */
- error += snd_config_imake_integer(&elemConfig, "channels", pcmSlave->ccount);
+ error += snd_config_imake_integer(&elemConfig, "channels", pcmSlave->nbChannels);
if (error) goto OnErrorExit;
error += snd_config_add(slaveConfig, elemConfig);
if (error) goto OnErrorExit;
diff --git a/plugins/alsa/alsa-plug-rate.c b/plugins/alsa/alsa-plug-rate.c
index 085ad78..e9b1f0d 100644
--- a/plugins/alsa/alsa-plug-rate.c
+++ b/plugins/alsa/alsa-plug-rate.c
@@ -23,10 +23,18 @@
ALSA_PLUG_PROTO(rate);
-PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open) {
+PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open) {
snd_config_t *rateConfig, *slaveConfig, *elemConfig, *pcmConfig;
- AlsaPcmCtlT *pcmPlug = calloc(1, sizeof (AlsaPcmCtlT));
+
+ AFB_ApiDebug(mixer->api,"%s : %s", __func__, pcmName);
+
+ AlsaPcmCtlT *pcmPlug = AlsaPcmCtlNew(mixer, pcmName);
+ if (!pcmPlug) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
pcmPlug->cid.cardid = pcmName;
int error = 0;
@@ -59,24 +67,28 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaP
error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, rateConfig);
if (error) {
- AFB_ApiError(mixer->api, "AlsaCreateRate: fail to add configRATE=%s", pcmPlug->cid.cardid);
+ AFB_ApiError(mixer->api, "%s: fail to add config RATE=%s", __func__, pcmPlug->cid.cardid);
goto OnErrorExit;
}
+ stream->rateConfig = rateConfig;
+
if (open) error = _snd_pcm_rate_open(&pcmPlug->handle, pcmPlug->cid.cardid, snd_config, rateConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(mixer->api, "AlsaCreateRate: fail to create Rate=%s Slave=%s Error=%s", pcmPlug->cid.cardid, pcmSlave->cid.cardid, snd_strerror(error));
+ AFB_ApiError(mixer->api,
+ "%s: fail to create Rate=%s Slave=%s Error=%s",
+ __func__, pcmPlug->cid.cardid, pcmSlave->cid.cardid, snd_strerror(error));
goto OnErrorExit;
}
// Debug config & pcm
//AlsaDumpCtlConfig(mixer, "plug-rate", pcmConfig, 1);
- //AlsaDumpCtlConfig (mixer, "plug-rate", rateConfig, 1);
- AFB_ApiNotice(mixer->api, "AlsaCreateRate: %s done", pcmPlug->cid.cardid);
+ AlsaDumpCtlConfig (mixer, "plug-rate", rateConfig, 1);
+ AFB_ApiNotice(mixer->api, "%s: %s done", __func__, pcmPlug->cid.cardid);
return pcmPlug;
OnErrorExit:
AlsaDumpCtlConfig(mixer, "plug-rate", rateConfig, 1);
- AFB_ApiNotice(mixer->api, "AlsaCreateRate: OnErrorExit");
+ AFB_ApiNotice(mixer->api, "%s: OnErrorExit", __func__);
return NULL;
}
diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c
index 2699701..b0dd560 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -26,37 +26,42 @@ ALSA_PLUG_PROTO(route);
typedef struct {
const char *uid;
const char *cardid;
+ const char * pcmplug_params;
int cardidx;
int ccount;
int port;
+ bool isPcmPlug;
} ChannelCardPortT;
STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT *response) {
+
+ AlsaSndPcmT * pcm = NULL;
bool found = false;
// search for channel within all sound card sink (channel port target is computed by order)
- for (int idx = 0; mixer->sinks[idx]; idx++) {
- int jdx;
- AlsaSndPcmT * pcm = mixer->sinks[idx];
- AlsaPcmChannelT **channels = pcm->channels;
+ cds_list_for_each_entry(pcm, &mixer->sinks.list, list) {
- if (!channels) {
+ if (pcm->nbChannels == 0) {
AFB_ApiError(mixer->api,
"%s: No Sink card=%s [should declare channels]",
__func__, pcm->uid);
goto OnErrorExit;
}
- for (jdx = 0; jdx < pcm->ccount; jdx++) {
- if (!strcasecmp(channels[jdx]->uid, uid)) {
- response->port = channels[jdx]->port;
- response->uid = pcm->uid;
- response->ccount = pcm->ccount;
- response->cardid = pcm->sndcard->cid.cardid;
- response->cardidx = pcm->sndcard->cid.cardidx;
- found = true;
- break;
- }
+ AlsaPcmChannelT * channel = NULL;
+ cds_list_for_each_entry(channel, &pcm->channels.list, list) {
+
+ if (channel->uid && !strcasecmp(channel->uid, uid)) {
+ response->port = channel->port;
+ response->uid = pcm->uid;
+ response->ccount = pcm->nbChannels;
+ response->cardid = pcm->sndcard->cid.cardid;
+ response->cardidx = pcm->sndcard->cid.cardidx;
+ response->pcmplug_params = pcm->sndcard->cid.pcmplug_params;
+ response->isPcmPlug= pcm->isPcmPlug;
+ found = true;
+ break;
+ }
}
}
@@ -75,78 +80,140 @@ OnErrorExit:
PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open) {
snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
- int scount=0, error = 0;
- ChannelCardPortT slave, channel;
- AlsaPcmCtlT *pcmRoute = calloc(1, sizeof (AlsaPcmCtlT));
+ int error = 0;
+ ChannelCardPortT slave, channelCardPort;
+ AlsaPcmCtlT *pcmRoute = NULL;
+
char *cardid = NULL;
- char *dmixUid = NULL;
+ char *slaveUid = NULL;
- if (!mixer->sinks) {
- AFB_ApiError(mixer->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No sink found [should Registry sound card first]", mixer->uid, zone->uid);
- goto OnErrorExit;
+ AFB_ApiDebug(mixer->api, "%s (zone %s)", __func__, zone->uid);
+
+ if (mixer->nbSinks == 0) {
+ AFB_ApiError(mixer->api, "%s: mixer=%s zone(%s)(zone) No sink found [should Registry sound card first]", __func__, mixer->uid, zone->uid);
+ goto fail;
}
- if (asprintf(&cardid, "route-%s", zone->uid) == -1)
- goto OnErrorExit;
+ if (asprintf(&cardid, "route-%s", zone->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ pcmRoute = AlsaPcmCtlNew(mixer, cardid);
+ if (!pcmRoute) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
pcmRoute->cid.cardid = (const char *) cardid;
pcmRoute->params = ApiSinkGetParamsByZone(mixer, zone->uid);
- zone->params=pcmRoute->params;
+ if (pcmRoute->params == NULL) {
+ AFB_ApiError(mixer->api, "%s: Failed to retrieve zone parameters", __func__);
+ goto fail_nodump;
+ }
+
+ zone->params = pcmRoute->params;
// use 1st zone channel to retrieve sound card name + channel count.
- error = CardChannelByUid(mixer, zone->sinks[0]->uid, &slave);
+
+ AlsaPcmChannelT * channel = cds_list_first_entry(&zone->sinks.list, AlsaPcmChannelT, list);
+ error = CardChannelByUid(mixer, channel->uid, &slave);
if (error) {
- AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[0]->uid);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s:zone(%s) fail to find channel=%s", __func__, zone->uid, channel->uid);
+ goto fail;
}
- // move from hardware to DMIX attach to sndcard
- if (asprintf(&dmixUid, "dmix-%s", slave.uid) == -1)
- goto OnErrorExit;
+ // DMIX only supports real hardware as a slave
+
+ if (!slave.isPcmPlug) {
+ // move from hardware to DMIX attach to sndcard
+ if (asprintf(&slaveUid, "dmix-%s", slave.uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+ } else {
+ slaveUid = strdup(slave.pcmplug_params);
+ if (!slaveUid) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+ }
// temporary store to unable multiple channel to route to the same port
- snd_config_t **cports = alloca(slave.ccount * sizeof (void*));
- memset(cports, 0, slave.ccount * sizeof (void*));
+
+ snd_config_t **cports = alloca(zone->nbSinks * sizeof (void*));
+ memset(cports, 0, zone->nbSinks * sizeof (void*));
int zcount = 0;
// We create 1st ttable to retrieve sndcard slave and channel count
(void)snd_config_make_compound(&tableConfig, "ttable", 0);
- for (scount = 0; zone->sinks[scount] != NULL; scount++) {
+ cds_list_for_each_entry(channel, &zone->sinks.list, list) {
+
+ AFB_ApiDebug(mixer->api, "%s: zone sink channel %s", __func__, channel->uid);
- error = CardChannelByUid(mixer, zone->sinks[scount]->uid, &channel);
+ error = CardChannelByUid(mixer, channel->uid, &channelCardPort);
if (error) {
- AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[scount]->uid);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: zone(%s) fail to find channel=%s", __func__, zone->uid, channel->uid);
+ goto fail;
}
- if (slave.cardidx != channel.cardidx) {
- AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) cannot span over multiple sound card %s != %s ", zone->uid, slave.cardid, channel.cardid);
- goto OnErrorExit;
+ if (slave.cardidx != channelCardPort.cardidx) {
+ AFB_ApiError(mixer->api,
+ "%s: zone(%s) cannot span over multiple sound card %s != %s ",
+ __func__, zone->uid, slave.cardid, channelCardPort.cardid);
+ goto fail;
}
- int port = zone->sinks[scount]->port;
- int target = channel.port;
+ int target = channelCardPort.port;
+ int port = channel->port;
+
double volume = 1.0; // currently only support 100%
- // if channel entry does not exit into ttable create it now
- if (!cports[port]) {
- zcount++;
+ // if channel entry does not exist into ttable create it now
+ if (!cports[port]) {
+
char channelS[4]; // 999 channel should be more than enough
snprintf(channelS, sizeof (channelS), "%d", port);
- error += snd_config_make_compound(&cports[port], channelS, 0);
- error += snd_config_add(tableConfig, cports[port]);
+
+ error = snd_config_make_compound(&cports[port], channelS, 0);
+ if (error) {
+ AFB_ApiError(mixer->api, "%s: make_compound failed , err %s", __func__, snd_strerror(error));
+ goto fail;
+ }
+
+ error = snd_config_add(tableConfig, cports[port]);
+ if (error) {
+ AFB_ApiError(mixer->api, "%s: add compound to table failed, err %s", __func__, snd_strerror(error));
+ goto fail;
+ }
}
+ zcount++;
+
// ttable require target port as a table and volume as a value
char targetS[4];
snprintf(targetS, sizeof (targetS), "%d", target);
- error += snd_config_imake_real(&elemConfig, targetS, volume);
- error += snd_config_add(cports[port], elemConfig);
- if (error) goto OnErrorExit;
+
+ if (channel->volume != -1)
+ volume = channel->volume;
+ else
+ volume = 1.0;
+
+ error = snd_config_imake_real(&elemConfig, targetS, volume);
+ if (error) {
+ AFB_ApiError(mixer->api, "%s: Failed to interpret volume real value, err %s", __func__, snd_strerror(error));
+ goto fail;
+ }
+
+ error = snd_config_add(cports[port], elemConfig);
+ if (error) {
+ AFB_ApiError(mixer->api, "%s Failed to add ttable entry:err=%s", __func__, snd_strerror(error));
+ goto fail;
+ }
+
}
- if (error) goto OnErrorExit;
// update zone with route channel count and sndcard params
pcmRoute->ccount = zcount;
@@ -155,36 +222,36 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
// refresh global alsalib config and create PCM top config
snd_config_update();
error += snd_config_top(&routeConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_set_id(routeConfig, cardid);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_imake_string(&elemConfig, "type", "route");
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_add(routeConfig, elemConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_make_compound(&slaveConfig, "slave", 0);
- if (error) goto OnErrorExit;
- error += snd_config_imake_string(&elemConfig, "pcm", dmixUid);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
+ error += snd_config_imake_string(&elemConfig, "pcm", slaveUid);
+ if (error) goto fail;
error += snd_config_add(slaveConfig, elemConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_imake_integer(&elemConfig, "channels", slave.ccount);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_add(slaveConfig, elemConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_add(routeConfig, slaveConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
error += snd_config_add(routeConfig, tableConfig);
- if (error) goto OnErrorExit;
+ if (error) goto fail;
if (open) error = _snd_pcm_route_open(&pcmRoute->handle, pcmRoute->cid.cardid, snd_config, routeConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
AFB_ApiError(mixer->api,
"%s: zone(%s) fail to create Plug=%s error=%s",
__func__, zone->uid, pcmRoute->cid.cardid, snd_strerror(error));
- goto OnErrorExit;
+ goto fail;
}
snd_config_update();
@@ -194,20 +261,28 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
AFB_ApiError(mixer->api,
"%s: %s fail to add config route=%s error=%s",
__func__, zone->uid, pcmRoute->cid.cardid, snd_strerror(error));
- goto OnErrorExit;
+ goto fail;
}
+ zone->routeConfig = routeConfig;
+
// Debug config & pcm
AFB_ApiNotice(mixer->api, "%s: zone(%s) DONE", __func__, zone->uid);
AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1);
+
return pcmRoute;
-OnErrorExit:
+fail:
+// AFB_ApiError(mixer->api, "%s ERROR, DUMPING PCM...", __func__);
+// AlsaDumpCtlConfig(mixer, "plug-pcm", snd_config, 1);
+ AFB_ApiError(mixer->api, "%s ERROR, DUMPING ROUTE...", __func__);
+ AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1);
+
+fail_nodump:
free(pcmRoute);
free(cardid);
- free(dmixUid);
- AlsaDumpCtlConfig(mixer, "plug-pcm", snd_config, 1);
- AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1);
+ free(slaveUid);
+
AFB_ApiNotice(mixer->api, "%s: zone(%s) FAIL", __func__, zone->uid);
return NULL;
}
diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c
index ce4627a..935e141 100644
--- a/plugins/alsa/alsa-plug-vol.c
+++ b/plugins/alsa/alsa-plug-vol.c
@@ -22,16 +22,27 @@
ALSA_PLUG_PROTO(softvol); // stream uses softvol plugin
+PUBLIC void AlsaDeleteSoftvol(SoftMixerT *mixer, AlsaPcmCtlT * ctl) {
+
+}
+
PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, char* slaveid, AlsaSndCtlT *sndcard, char* ctlName, int max, int open) {
snd_config_t *streamConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig;
- AlsaPcmCtlT *pcmVol= calloc(1,sizeof(AlsaPcmCtlT));
+ char *cardid = NULL;
int error = 0;
-
- AFB_ApiInfo(mixer->api, "%s create SOFTVOL on %s", __func__, slaveid);
+ AlsaPcmCtlT *pcmVol = NULL;
- char *cardid = NULL;
- if (asprintf(&cardid, "softvol-%s", stream->uid) == -1)
+ AFB_ApiDebug(mixer->api, "%s create SOFTVOL on %s", __func__, slaveid);
+ if (asprintf(&cardid, "softvol-%s", stream->uid) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
+ }
+
+ pcmVol = AlsaPcmCtlNew(mixer, cardid);
+ if (pcmVol == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
pcmVol->cid.cardid = (const char *) cardid;
@@ -82,6 +93,8 @@ PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *strea
goto OnErrorExit;
}
+ stream->softvolConfig = streamConfig;
+
// Debug config & pcm
//AlsaDumpCtlConfig (mixer, "plug-config", pcmConfig, 1);
AlsaDumpCtlConfig(mixer, "plug-softvol", streamConfig, 1);
diff --git a/plugins/alsa/alsa-ringbuf.c b/plugins/alsa/alsa-ringbuf.c
index 476222c..2dd1d0d 100644
--- a/plugins/alsa/alsa-ringbuf.c
+++ b/plugins/alsa/alsa-ringbuf.c
@@ -3,8 +3,14 @@
alsa_ringbuf_t * alsa_ringbuf_new(snd_pcm_uframes_t capacity, size_t frameSize) {
alsa_ringbuf_t * rb = malloc(sizeof(alsa_ringbuf_t));
+ if (!rb)
+ goto fail;
+
rb->rbuf = ringbuf_new(capacity*frameSize);
+ if (!rb->rbuf)
+ goto fail;
rb->frameSize = frameSize;
+fail:
return rb;
}
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index e7218bd..f901680 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -21,7 +21,6 @@
#ifndef _ALSA_SOFTMIXER_
#define _ALSA_SOFTMIXER_
-//#include <afb/afb-binding.h>
#include <json-c/json_object.h>
#include <stdlib.h>
#include <stdio.h>
@@ -31,11 +30,15 @@
#include <systemd/sd-event.h>
#include <semaphore.h>
+#include <urcu/list.h>
+
#include "ctl-plugin.h"
#include "wrap-json.h"
#include "alsa-ringbuf.h"
+#include "alsa-transaction.h"
+
#ifndef PUBLIC
#define PUBLIC
#endif
@@ -51,7 +54,7 @@
#define ALSA_CARDID_MAX_LEN 64
-#define SMIXER_SUBDS_CTLS 3
+#define SMIXER_SUBDS_CTLS 16
#define SMIXER_DEFLT_LOOPS 4
#define SMIXER_DEFLT_SINKS 8
#define SMIXER_DEFLT_SOURCES 32
@@ -59,9 +62,16 @@
#define SMIXER_DEFLT_STREAMS 32
#define SMIXER_DEFLT_RAMPS 8
+#define SMIXER_DEFLT_DELAYMS 1000
+
+#define SOFTMIXER_VERB_NONE "none"
+
#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)
+#define SOFTMIXER_NOMEM(api) \
+ AFB_ApiError((api), "%s: Insufficient memory", __func__)
+
// auto switch from Log to API request depending on request presence.
#define AFB_IfReqFailF(mixer, request, status, format, ...) \
if (request) AFB_ReqFailF(request, status, format, __VA_ARGS__); \
@@ -101,16 +111,19 @@ typedef struct {
} AlsaPcmHwInfoT;
typedef struct {
+ const char * name;
int ccount;
bool mute;
- int muteFd;
+ int eventFd;
AlsaDevInfoT cid;
snd_pcm_t *handle;
AlsaPcmHwInfoT *params;
- void * mixer;
+ struct SoftMixerT_ * mixer;
snd_pcm_uframes_t avail_min;
+
+ bool closeAtDeletion; // intermediate pcms in the pcm chain must not be closed, else it make libasound abort()
} AlsaPcmCtlT;
typedef struct {
@@ -138,12 +151,13 @@ typedef struct {
char* info;
int nbPcmFds;
- struct pollfd pollFds[2];
+ struct pollfd * pollFds;
sem_t sem;
pthread_mutex_t mutex;
int saveFd;
+ bool ending;
} AlsaPcmCopyHandleT;
@@ -156,71 +170,88 @@ typedef struct {
long step;
} AlsaSndControlT;
+
typedef struct {
const char*uid;
int port;
+ double volume;
+ struct cds_list_head list;
} AlsaPcmChannelT;
-
typedef struct {
const char *uid;
int delay; // delay between volset in us
int stepDown; // linear %
int stepUp; // linear %
+ struct cds_list_head list;
} AlsaVolRampT;
-
-
-
typedef struct {
int numid;
RegistryNumidT type;
AlsaPcmCtlT *pcm;
+ struct cds_list_head cardListEntry; /* node to list in AlsaSndCtlT */
+ struct AlsaSndCtlT_ *sndcard; /* ref to owner */
} RegistryEntryPcmT;
-typedef struct {
- long rcount;
+
+typedef struct AlsaSndCtlT_ {
AlsaDevInfoT cid;
snd_ctl_t *ctl;
AlsaPcmHwInfoT *params;
- RegistryEntryPcmT **registry;
+ long nbRegistry;
+ struct cds_list_head registryList;
+ struct SubscribeHandleT_ * eventSubscribeHandle;
} AlsaSndCtlT;
typedef struct {
const char *uid;
- AlsaPcmChannelT **sources;
- AlsaPcmChannelT **sinks;
+ unsigned int nbSources;
+ AlsaPcmChannelT sources;
+ unsigned int nbSinks;
+ AlsaPcmChannelT sinks;
int ccount;
AlsaPcmHwInfoT *params;
+ struct cds_list_head list;
+ snd_config_t * routeConfig;
} AlsaSndZoneT;
typedef struct {
const char *uid;
const char *verb;
- unsigned int ccount;
AlsaSndCtlT *sndcard;
AlsaSndControlT volume;
AlsaSndControlT mute;
- AlsaPcmChannelT **channels;
+ unsigned int nbChannels;
+ AlsaPcmChannelT channels;
snd_pcm_stream_t direction;
+ struct cds_list_head list;
+ bool isPcmPlug;
+ void * apiVerbHandle;
} AlsaSndPcmT;
typedef struct {
const char*uid;
int index;
int numid;
+ struct cds_list_head list;
} AlsaLoopSubdevT;
-typedef struct {
+
+struct SoftMixerT_;
+
+typedef struct AlsaSndLoopT {
const char *uid;
+ struct SoftMixerT_ * mixer; /* owner */
int playback;
int capture;
- long scount;
AlsaSndCtlT *sndcard;
- AlsaLoopSubdevT **subdevs;
+ int nbSubdevs;
+ AlsaLoopSubdevT subdevs;
+ struct cds_list_head list;
} AlsaSndLoopT;
typedef struct {
@@ -228,15 +259,22 @@ typedef struct {
const char *verb;
const char *info;
const char *sink;
+ const char *playback;
const char *source;
const char *ramp;
int volume;
int mute;
+ unsigned int delayms;
AlsaPcmHwInfoT *params;
AlsaPcmCopyHandleT *copy;
+ struct cds_list_head list; /* link to the global list*/
+ AlsaPcmCtlT * softvol;
+ snd_config_t * softvolConfig;
+ snd_config_t * rateConfig;
+ void * verbApiHandle;
} AlsaStreamAudioT;
-typedef struct {
+typedef struct SoftMixerT_{
const char *uid;
const char *info;
AFB_ApiT api;
@@ -250,17 +288,27 @@ typedef struct {
unsigned int streams;
unsigned int ramps;
} max;
- AlsaSndLoopT **loops;
- AlsaSndPcmT **sinks;
- AlsaSndPcmT **sources;
- AlsaSndZoneT **zones;
- AlsaStreamAudioT **streams;
- AlsaVolRampT **ramps;
+ unsigned int nbLoops;
+ AlsaSndLoopT loops;
+ unsigned int nbSinks;
+ AlsaSndPcmT sinks;
+ unsigned int nbSources;
+ AlsaSndPcmT sources;
+ unsigned int nbZones;
+ AlsaSndZoneT zones;
+ unsigned int nbStreams;
+ AlsaStreamAudioT streams;
+ unsigned int nbRamps;
+ AlsaVolRampT ramps;
+
+ AlsaMixerTransaction * transaction;
} SoftMixerT;
// alsa-utils-bypath.c
PUBLIC snd_ctl_card_info_t *AlsaByPathInfo(SoftMixerT *mixer, const char *devpath);
-PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmId, snd_pcm_stream_t direction);
+PUBLIC AlsaPcmCtlT * AlsaPcmCtlNew(SoftMixerT*, const char*);
+PUBLIC void AlsaPcmCtlDelete(SoftMixerT *mixer, void *);
+PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcmCtl(SoftMixerT *mixer, AlsaDevInfoT *pcmId, snd_pcm_stream_t direction);
PUBLIC snd_ctl_t *AlsaByPathOpenCtl(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *dev);
// alsa-utils-dump.c
@@ -279,7 +327,7 @@ PUBLIC void AlsaDumpCtlConfig(SoftMixerT *mixer, const char* info, snd_config_t
// alsa-core-ctl.c
PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNumidElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid) ;
PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName) ;
-PUBLIC snd_ctl_t *AlsaCtlOpenCtl(SoftMixerT *mixer, const char *cardid) ;
+
PUBLIC int CtlElemIdGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long *value) ;
PUBLIC int CtlElemIdSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long value) ;
PUBLIC snd_ctl_card_info_t *AlsaCtlGetCardInfo(SoftMixerT *mixer, const char *cardid) ;
@@ -288,37 +336,55 @@ PUBLIC int AlsaCtlNumidGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numi
PUBLIC int AlsaCtlNameSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long value) ;
PUBLIC int AlsaCtlNameGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long* value) ;
PUBLIC int AlsaCtlCreateControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, char* ctlName, int ctlCount, int ctlMin, int ctlMax, int ctlStep, long value) ;
-PUBLIC snd_ctl_t* AlsaCrlFromPcm(SoftMixerT *mixer, snd_pcm_t *pcm) ;
+
PUBLIC int AlsaCtlSubscribe(SoftMixerT *mixer, const char *uid, AlsaSndCtlT *sndcard) ;
+
+PUBLIC void AlsaCtlUnregister(SoftMixerT* mixer, void *);
PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid);
// alsa-core-pcm.c
PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode);
-PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
+PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
+PUBLIC int AlsaPcmCopyStop(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle);
// alsa-plug-*.c _snd_pcm_PLUGIN_open_ see macro ALSA_PLUG_PROTO(plugin)
-PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *streamAudio, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
PUBLIC int AlsaPcmCopyMuteSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn, bool mute);
+
PUBLIC AlsaPcmCtlT* AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, char *slaveid, AlsaSndCtlT *sndcard, char* ctlName, int max, int open);
PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open);
-PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open);
+PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT *stream, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open);
PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open);
// alsa-api-*
+
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *, json_object * argsJ);
+PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char *, json_object * argsJ);
+PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *, json_object * argsJ);
+PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, const char *prefix, json_object * argsJ);
+PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *, json_object * argsJ);
+PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char *, json_object *argsJ);
+
+// helper used for attach verb,and also by the streams API for fake zones
+PUBLIC AlsaSndZoneT * zoneCreate(SoftMixerT* mixer, const char * uid, json_object * argsJ);
+
PUBLIC AlsaLoopSubdevT *ApiLoopFindSubdev(SoftMixerT *mixer, const char *streamUid, const char *targetUid, AlsaSndLoopT **loop);
-PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
+
PUBLIC AlsaPcmHwInfoT *ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object *paramsJ);
+PUBLIC void ApiPcmDelParams(SoftMixerT*, AlsaPcmHwInfoT*);
+
PUBLIC AlsaSndPcmT *ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object *argsJ);
+PUBLIC AlsaSndPcmT * ApiPcmNew(SoftMixerT* mixer);
+PUBLIC void ApiPcmDelete(SoftMixerT * mixer, AlsaSndPcmT * pcm);
+
PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid);
-PUBLIC int ApiRampAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object *argsJ);
+
PUBLIC AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *target);
-PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
+
PUBLIC AlsaSndPcmT *ApiSinkGetByUid(SoftMixerT *mixer, const char *target);
PUBLIC AlsaSndCtlT *ApiSourceFindSubdev(SoftMixerT *mixer, const char *target);
-PUBLIC int ApiSourceAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
-PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, const char *prefix, json_object * argsJ);
+
+
PUBLIC AlsaSndZoneT *ApiZoneGetByUid(SoftMixerT *mixer, const char *target);
-PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ);
// alsa-effect-ramp.c
PUBLIC AlsaVolRampT *ApiRampGetByUid(SoftMixerT *mixer, const char *uid);
diff --git a/plugins/alsa/alsa-transaction.c b/plugins/alsa/alsa-transaction.c
new file mode 100644
index 0000000..cb4d81b
--- /dev/null
+++ b/plugins/alsa/alsa-transaction.c
@@ -0,0 +1,111 @@
+#include <malloc.h>
+#include <string.h>
+
+#include "alsa-softmixer.h"
+#include "alsa-transaction.h"
+#include "wrap-json.h"
+
+AlsaMixerTransaction * AlsaMixerTransactionNew(struct SoftMixerT_ * mixer, const char * uid) {
+ AlsaMixerTransaction * newList = (AlsaMixerTransaction *) malloc(sizeof(AlsaMixerTransaction));
+ if (newList == NULL)
+ goto fail;
+
+ CDS_INIT_LIST_HEAD(&newList->list);
+ newList->uid = strdup(uid);
+ if (newList->uid == NULL) {
+ goto fail_list;
+ }
+
+ newList->mixer = mixer;
+
+ return newList;
+
+fail_list:
+ free(newList);
+fail:
+ return NULL;
+}
+
+void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction* list) {
+ free(list);
+}
+
+bool AlsaMixerTransactionObjectAdd(AlsaMixerTransaction* list, void* object, AlsaTransactionDestructor destructor) {
+ bool ret = false;
+ AlsaMixerTransactionDataItem * newItem = NULL;
+ if (!list)
+ goto fail;
+
+ newItem = (AlsaMixerTransactionDataItem *) malloc(sizeof(AlsaMixerTransactionDataItem));
+ if (newItem == NULL)
+ goto fail;
+
+ CDS_INIT_LIST_HEAD(&newItem->list_entry);
+ newItem->object = object;
+ newItem->destructor = destructor;
+ cds_list_add(&newItem->list_entry, &list->list);
+
+ ret = true;
+fail:
+ return ret;
+
+}
+
+void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) {
+ AlsaMixerTransactionDataItem * item, *sav;
+
+ cds_list_for_each_entry_safe(item, sav, &transaction->list, list_entry) {
+ if (item->destructor)
+ item->destructor(transaction->mixer, item->object);
+
+ cds_list_del(&item->list_entry);
+ free(item);
+ }
+}
+
+void AlsaMixerTransactionVerbCB(AFB_ReqT request) {
+ json_object *responseJ = NULL;
+ AlsaMixerTransaction *transaction = (AlsaMixerTransaction*) afb_req_get_vcbdata(request);
+ json_object *argsJ = afb_req_json(request);
+ int error;
+ char * action = NULL;
+ const char * uid;
+
+ error = wrap_json_unpack(argsJ, "{ss!}",
+ "action", &action);
+
+ if (error) {
+ AFB_ReqFailF(request, "missing action", "%s: missing 'action' field: %s", transaction->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ uid = strdup(transaction->uid);
+ if (!uid) {
+ SOFTMIXER_NOMEM(transaction->mixer->api);
+ goto OnErrorExit;
+ }
+
+ if (strcmp(action, "remove") == 0) {
+ AlsaMixerTransactionDoCleanup(transaction);
+ error = afb_api_del_verb(transaction->mixer->api, transaction->uid, (void**)transaction);
+ if (error) {
+ AFB_ReqFail(request, "verb deletion" , "verb was not removed");
+ goto OnErrorExit;
+ }
+
+ } else {
+ AFB_ReqFailF(request, "unsupported action", "%s: unsupported action %s (supported ones are ['remove']", transaction->uid, action);
+ goto OnErrorExit;
+ }
+
+ responseJ=json_object_new_object();
+ json_object_object_add(responseJ, "result", json_object_new_string("OK"));
+ AFB_ReqSuccess(request, responseJ, uid);
+
+ free((char*)uid);
+ return;
+
+OnErrorExit:
+ return;
+
+}
diff --git a/plugins/alsa/alsa-transaction.h b/plugins/alsa/alsa-transaction.h
new file mode 100644
index 0000000..a0af0ae
--- /dev/null
+++ b/plugins/alsa/alsa-transaction.h
@@ -0,0 +1,37 @@
+#ifndef __INC_ALSA_TRANSACTION_H
+#define __INC_ALSA_TRANSACTION_H
+
+#include <urcu/list.h>
+#include <stdbool.h>
+
+#include "afb-definitions.h"
+
+struct SoftMixerT_;
+
+typedef void (*AlsaTransactionDestructor) (struct SoftMixerT_ *, void*);
+
+typedef struct {
+ const char * uid;
+ struct SoftMixerT_* mixer;
+ struct cds_list_head list;
+} AlsaMixerTransaction;
+
+typedef struct {
+ struct cds_list_head list_entry;
+ void * object;
+ AlsaTransactionDestructor destructor;
+} AlsaMixerTransactionDataItem;
+
+extern AlsaMixerTransaction * AlsaMixerTransactionNew(struct SoftMixerT_ * api, const char*);
+
+extern void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction*);
+
+extern bool AlsaMixerTransactionObjectAdd(AlsaMixerTransaction*, void * object, AlsaTransactionDestructor destructor);
+
+extern void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction*);
+
+extern bool AlsaMixerTransactionVerbCreate(AlsaMixerTransaction*);
+
+extern void AlsaMixerTransactionVerbCB(AFB_ReqT request);
+
+#endif /* __INC_ALSA_TRANSACTION_H */
diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c
index 2427e9f..9816256 100644
--- a/plugins/alsa/alsa-utils-bypath.c
+++ b/plugins/alsa/alsa-utils-bypath.c
@@ -27,7 +27,6 @@
#include <sys/ioctl.h>
#include "alsa-softmixer.h"
-#include "alsa-bluez.h"
// extracted IOCTLs from <alsa/asoundlib.h>
#define _IOR_HACKED(type,nr,size) _IOC(_IOC_READ,(type),(nr),size)
@@ -38,52 +37,119 @@
PUBLIC snd_ctl_card_info_t *AlsaByPathInfo(SoftMixerT *mixer, const char *devpath) {
int open_dev;
- snd_ctl_card_info_t *cardInfo = malloc(snd_ctl_card_info_sizeof());
-
- if (!devpath) goto OnErrorExit;
+ snd_ctl_card_info_t *cardInfo = (snd_ctl_card_info_t *) malloc(snd_ctl_card_info_sizeof());
+ if (!devpath) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
open_dev = open(devpath, O_RDONLY);
- if (open_dev < 0) goto OnErrorExit;
+ if (open_dev < 0) {
+ AFB_ApiError(mixer->api, "%s: Unable to open %s", __func__, devpath);
+ goto fail_cardinfo;
+ }
int rc = ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO(snd_ctl_card_info_sizeof()), cardInfo);
if (rc < 0) {
- close(open_dev);
- goto OnErrorExit;
+ AFB_ApiError(mixer->api, "%s: ioctl on %s failed", __func__, devpath);
+ goto fail_dev;
}
close(open_dev);
return cardInfo;
-OnErrorExit:
- AFB_ApiError(mixer->api, "AlsaCardInfoByPath: fail to find sndcard by path= %s", devpath);
+
+fail_dev:
+ close(open_dev);
+fail_cardinfo:
+ free(cardInfo);
+fail:
return NULL;
}
-PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmDev, snd_pcm_stream_t direction) {
+PUBLIC AlsaPcmCtlT * AlsaPcmCtlNew(SoftMixerT* mixer, const char * name) {
+
+ AFB_ApiDebug(mixer->api, "%s: NEW %s",__func__, name);
+
+ AlsaPcmCtlT *pcmCtl = calloc(1, sizeof (AlsaPcmCtlT));
+ if (pcmCtl == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
+
+ pcmCtl->name = name;
+ pcmCtl->closeAtDeletion = false;
+
+ AlsaMixerTransactionObjectAdd(mixer->transaction, pcmCtl, AlsaPcmCtlDelete);
+
+fail:
+ return pcmCtl;
+}
+
+PUBLIC void AlsaPcmCtlDelete(SoftMixerT* mixer, void * arg) {
+ int error;
+
+ AlsaPcmCtlT * pcmCtl = (AlsaPcmCtlT *) arg;
+
+ if (!pcmCtl->closeAtDeletion)
+ goto done;
+
+ AFB_ApiDebug(mixer->api, "%s: Closing %s",__func__, pcmCtl->cid.cardid);
+ error = snd_pcm_close(pcmCtl->handle);
+ if (error) {
+ // It's safe to ignore this error. In case of ioplug PCM, the device may have already disappeared
+ AFB_ApiDebug(mixer->api,
+ "%s: failed to close capture PCM %s: err=%s", __func__, pcmCtl->cid.cardid, snd_strerror(error));
+ }
+done:
+ free((char*) pcmCtl->cid.cardid);
+ free(pcmCtl);
+}
+
+PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcmCtl(SoftMixerT *mixer, AlsaDevInfoT *pcmDev, snd_pcm_stream_t direction) {
int error;
- AlsaPcmCtlT *pcmCtl = calloc(1, sizeof (AlsaPcmCtlT));
char *cardid = NULL;
+ AlsaPcmCtlT *pcmCtl = NULL;
+
if (pcmDev->pcmplug_params) {
- pcmDev->cardid = pcmDev->pcmplug_params;
+ pcmDev->cardid = strdup(pcmDev->pcmplug_params);
+ if (pcmDev->cardid == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
}
if (!pcmDev->cardid) {
if (pcmDev->subdev) {
- if (asprintf(&cardid, "hw:%i,%i,%i", pcmDev->cardidx, pcmDev->device, pcmDev->subdev) == -1)
- goto OnErrorExit;
+ if (asprintf(&cardid, "hw:%i,%i,%i", pcmDev->cardidx, pcmDev->device, pcmDev->subdev) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
}
else if (pcmDev->device) {
- if (asprintf(&cardid, "hw:%i,%i", pcmDev->cardidx, pcmDev->device) == -1)
- goto OnErrorExit;
+ if (asprintf(&cardid, "hw:%i,%i", pcmDev->cardidx, pcmDev->device) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
}
else {
- if (asprintf(&cardid, "hw:%i", pcmDev->cardidx) == -1)
- goto OnErrorExit;
+ if (asprintf(&cardid, "hw:%i", pcmDev->cardidx) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
}
+
pcmDev->cardid = (const char*)cardid;
}
+ pcmCtl = AlsaPcmCtlNew(mixer, pcmDev->cardid);
+ if (pcmCtl == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_cardid;
+ }
+
+
// inherit CID from pcmDev
pcmCtl->cid.cardid = pcmDev->cardid;
pcmCtl->cid.cardidx = pcmDev->cardidx;
@@ -92,21 +158,23 @@ PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmDev, s
pcmCtl->cid.name=NULL;
pcmCtl->cid.longname=NULL;
- AFB_ApiInfo(mixer->api,
+ AFB_ApiDebug(mixer->api,
"%s OPEN PCM '%s', direction %s",
__func__, pcmDev->cardid, direction==SND_PCM_STREAM_PLAYBACK?"playback":"capture");
error = snd_pcm_open(&pcmCtl->handle, pcmCtl->cid.cardid, direction, SND_PCM_NONBLOCK);
if (error < 0) {
AFB_ApiError(mixer->api, "%s: fail openpcm cardid=%s error=%s", __func__, pcmCtl->cid.cardid, snd_strerror(error));
- goto OnErrorExit;
+ goto fail_pcmctl;
}
return pcmCtl;
-OnErrorExit:
+fail_pcmctl:
free(pcmCtl);
- free(cardid);
+fail_cardid:
+ free((char*)pcmDev->cardid);
+fail:
return NULL;
}
@@ -117,43 +185,63 @@ PUBLIC snd_ctl_t *AlsaByPathOpenCtl(SoftMixerT *mixer, const char *uid, AlsaSndC
// get card info from /dev/snd/xxx if not use hw:x,x,x
snd_ctl_card_info_t *cardInfo = NULL;
+ AFB_ApiDebug(mixer->api, "%s: devpath %s, cardid %s, plug %s", __func__, dev->cid.devpath, dev->cid.cardid, dev->cid.pcmplug_params);
+
if (dev->cid.devpath)
cardInfo = AlsaByPathInfo(mixer, dev->cid.devpath);
else if (dev->cid.cardid)
cardInfo = AlsaCtlGetCardInfo(mixer, dev->cid.cardid);
- else if (dev->cid.pcmplug_params)
+ else if (dev->cid.pcmplug_params) {
+ AFB_ApiDebug(mixer->api, "Get card info from plug params %s", dev->cid.pcmplug_params);
cardInfo = AlsaCtlGetCardInfo(mixer, dev->cid.pcmplug_params);
+ }
if (!cardInfo) {
AFB_ApiError(mixer->api,
"%s: uid=%s fail to find sndcard by path=%s id=%s",
__func__, uid, dev->cid.devpath, dev->cid.cardid);
- goto OnErrorExit;
+ goto fail;
}
// extract useful info from cardInfo handle
dev->cid.devpath = NULL;
dev->cid.cardidx = snd_ctl_card_info_get_card(cardInfo);
dev->cid.name = strdup(snd_ctl_card_info_get_name(cardInfo));
+ if (dev->cid.name == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail;
+ }
dev->cid.longname = strdup(snd_ctl_card_info_get_longname(cardInfo));
+ if (dev->cid.longname == NULL) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_cid_name;
+ }
// build a valid name and open sndcard
- if (asprintf((char**) &dev->cid.cardid, "hw:%i", dev->cid.cardidx) == -1)
- goto OnErrorExit;
+ if (asprintf((char**) &dev->cid.cardid, "hw:%i", dev->cid.cardidx) == -1) {
+ SOFTMIXER_NOMEM(mixer->api);
+ goto fail_cid_longname;
+ }
if ((err = snd_ctl_open(&handle, dev->cid.cardid, 0)) < 0) {
AFB_ApiError(mixer->api,
"%s uid=%s sndcard open fail cardid=%s longname=%s error=%s",
__func__, uid, dev->cid.cardid, dev->cid.longname, snd_strerror(err));
- goto OnErrorExit;
+ goto fail_cardid;
}
AFB_ApiNotice(mixer->api,
- "%s: uid=%s cardid=%s cardname=%s",
- __func__, uid, dev->cid.cardid, dev->cid.longname);
+ "%s: uid=%s cardid=%s cardname=%s pcmplug %s",
+ __func__, uid, dev->cid.cardid, dev->cid.longname, dev->cid.pcmplug_params);
free(cardInfo);
return handle;
-OnErrorExit:
+fail_cid_longname:
+ free((char*)dev->cid.longname);
+fail_cid_name:
+ free((char*)dev->cid.name);
+fail_cardid:
+ free((char*)dev->cid.cardid);
+fail:
return NULL;
}
diff --git a/plugins/alsa/alsa-utils-dump.c b/plugins/alsa/alsa-utils-dump.c
index b6c011d..fba89a4 100644
--- a/plugins/alsa/alsa-utils-dump.c
+++ b/plugins/alsa/alsa-utils-dump.c
@@ -109,7 +109,7 @@ PUBLIC void AlsaDumpCtlSubdev(SoftMixerT *mixer, snd_ctl_t *handle) {
while (1) {
if (snd_ctl_pcm_next_device(handle, &dev) < 0) {
- AFB_ApiError(mixer->api, "AlsaDumpCard: fail to open subdev card id=%s name=%s", cardId, cardName);
+ AFB_ApiError(mixer->api, "%s: fail to open subdev card id=%s name=%s", __func__, cardId, cardName);
goto OnErrorExit;
}
@@ -119,12 +119,13 @@ PUBLIC void AlsaDumpCtlSubdev(SoftMixerT *mixer, snd_ctl_t *handle) {
// ignore empty device slot
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
- AFB_ApiError(mixer->api, "control digital audio info (%s): %s", cardName, snd_strerror(err));
+ AFB_ApiError(mixer->api, "%s: control digital audio info (%s): %s", __func__, cardName, snd_strerror(err));
continue;
}
- AFB_ApiDebug(mixer->api, "AlsaDumpCard card %d: %s [%s], device %d: %s [%s]",
- cardIndex, cardId, cardName, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo));
+ AFB_ApiDebug(mixer->api,
+ "%s card %d: %s [%s], device %d: %s [%s]",
+ __func__, cardIndex, cardId, cardName, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo));
// loop on subdevices
subdevCount = snd_pcm_info_get_subdevices_count(pcminfo);
@@ -133,12 +134,12 @@ PUBLIC void AlsaDumpCtlSubdev(SoftMixerT *mixer, snd_ctl_t *handle) {
for (unsigned int idx = 0; idx < subdevCount; idx++) {
snd_pcm_info_set_subdevice(pcminfo, idx);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
- AFB_ApiError(mixer->api, "AlsaDumpCard: control digital audio playback info %i: %s", cardIndex, snd_strerror(err));
+ AFB_ApiError(mixer->api, "%s: control digital audio playback info %i: %s", __func__, cardIndex, snd_strerror(err));
} else {
- AFB_ApiDebug(mixer->api, "AlsaDumpCard: -- Subdevice #%d: %s", idx, snd_pcm_info_get_subdevice_name(pcminfo));
+ AFB_ApiDebug(mixer->api, "%s: -- Subdevice #%d: %s", __func__, idx, snd_pcm_info_get_subdevice_name(pcminfo));
}
}
- AFB_ApiDebug(mixer->api, "AlsaDumpCard => subdevice count=%d avaliable=%d", subdevCount, subdevAvail);
+ AFB_ApiDebug(mixer->api, "%s => subdevice count=%d avaliable=%d", __func__, subdevCount, subdevAvail);
}
return;
@@ -153,7 +154,7 @@ PUBLIC void AlsaDumpPcmParams(SoftMixerT *mixer, snd_pcm_hw_params_t *pcmHwParam
snd_output_buffer_open(&output);
snd_pcm_hw_params_dump(pcmHwParams, output);
snd_output_buffer_string(output, &buffer);
- AFB_ApiDebug(mixer->api, "%s:\n%s",__func__, buffer);
+ AFB_ApiDebug(mixer->api, "%s:\n--------------\n%s\n-------------",__func__, buffer);
snd_output_close(output);
}
diff --git a/plugins/alsa/ringbuf.c b/plugins/alsa/ringbuf.c
index b18ddb6..7d0923f 100644
--- a/plugins/alsa/ringbuf.c
+++ b/plugins/alsa/ringbuf.c
@@ -46,7 +46,7 @@ ringbuf_new(size_t capacity)
/* One byte is used for detecting the full condition. */
rb->size = capacity + 1;
- rb->buf = malloc(rb->size);
+ rb->buf = calloc(1, rb->size);
if (rb->buf)
ringbuf_reset(rb);
else {