summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/alsa/alsa-api-loop.c13
-rw-r--r--plugins/alsa/alsa-api-mixer.c45
-rw-r--r--plugins/alsa/alsa-api-pcm.c62
-rw-r--r--plugins/alsa/alsa-api-streams.c45
-rw-r--r--plugins/alsa/alsa-api-zones.c9
-rw-r--r--plugins/alsa/alsa-core-pcm.c262
-rw-r--r--plugins/alsa/alsa-plug-dmix.c12
-rw-r--r--plugins/alsa/alsa-plug-rate.c12
-rw-r--r--plugins/alsa/alsa-plug-route.c38
-rw-r--r--plugins/alsa/alsa-plug-vol.c10
-rw-r--r--plugins/alsa/alsa-softmixer.h29
-rw-r--r--plugins/alsa/alsa-transaction.c30
-rw-r--r--plugins/alsa/alsa-transaction.h4
-rw-r--r--plugins/alsa/alsa-utils-bypath.c3
-rw-r--r--plugins/alsa/alsa-utils-dump.c1
15 files changed, 403 insertions, 172 deletions
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
index 7e852a8..a4b19f5 100644
--- a/plugins/alsa/alsa-api-loop.c
+++ b/plugins/alsa/alsa-api-loop.c
@@ -117,7 +117,7 @@ STATIC int CheckOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, AlsaLoopSubdevT
pcmCtl->closeAtDeletion = true;
// free PCM as we only open loop to assert it's a valid capture device
- AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl);
+ AlsaMixerTransactionObjectDelete(mixer->transaction, pcmCtl, false);
AlsaPcmCtlDelete(mixer, pcmCtl);
return 0;
@@ -445,10 +445,12 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid,
AlsaSndLoopT * newLoop = NULL;
+ AFB_API_INFO(mixer->api, "%s: %s", __func__, json_object_get_string(argsJ));
+
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);
+ AFB_IfReqFailF(mixer, request, "too many loops", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops);
goto fail;
- }
+ }
switch (json_object_get_type(argsJ)) {
size_t count;
@@ -464,14 +466,14 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid,
count = json_object_array_length(argsJ);
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 fail_loop;
+ goto fail;
}
for (int idx = 0; idx < count; idx++) {
json_object *loopJ = json_object_array_get_idx(argsJ, idx);
newLoop = loopCreate(mixer, uid, loopJ, streamsJ);
if (newLoop == NULL) {
- goto fail_loop;
+ goto fail;
}
}
break;
@@ -482,7 +484,6 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid,
return 0;
-fail_loop:
fail:
return -1;
}
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index 328f02e..15dae20 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -32,7 +32,7 @@ static json_object *LoopsJ = NULL;
static void MixerExit() {
SoftMixerT *mixer, *tmp;
- printf("%s !\n", __func__);
+ printf("-------------------------- %s ------------------------!\n", __func__);
cds_list_for_each_entry_safe(mixer, tmp, &mixerList, list) {
// remove this mixer from the global mixer list
@@ -41,13 +41,12 @@ static void MixerExit() {
AlsaMixerTransaction * transaction, * tmp_trans;
cds_list_for_each_entry_safe(transaction, tmp_trans, &mixer->transactionList, transaction_node) {
- cds_list_del(&transaction->transaction_node);
- AlsaMixerTransactionDoCleanup(transaction);
AlsaMixerTransactionDelete(transaction);
}
+ AFB_API_INFO(mixer->api, "Mixer %s terminated", mixer->uid);
MixerDelete(mixer);
}
- printf("%s DONE ! Bye !\n", __func__);
+ printf("------------------------- %s DONE ! Bye ! --------------\n", __func__);
}
CTLP_ONLOAD(plugin, callbacks){
@@ -582,7 +581,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (playbacksJ) {
error = ApiSinkAttach(mixer, request, uid, playbacksJ);
- if (error) goto fail_loop;
+ if (error) goto fail;
json_object *resultJ = MixerInfoPcms(mixer, playbacksJ, SND_PCM_STREAM_PLAYBACK, 0);
json_object_object_add(responseJ, "playbacks", resultJ);
@@ -594,7 +593,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
error = ApiSourceAttach(mixer, request, uid, capturesJ);
if (error) {
AFB_ApiError(mixer->api,"%s: source attach failed", __func__);
- goto fail_sink;
+ goto fail;
}
json_object *resultJ = MixerInfoPcms(mixer, capturesJ, SND_PCM_STREAM_CAPTURE, 0);
@@ -606,7 +605,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (zonesJ) {
error = ApiZoneAttach(mixer, request, uid, zonesJ);
if (error)
- goto fail_source;
+ goto fail;
json_object *resultJ = MixerInfoZones(mixer, zonesJ, 0);
json_object_object_add(responseJ, "zone", resultJ);
@@ -619,7 +618,11 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
AFB_ApiInfo(mixer->api, "%s set LOOPS/AVIRT", __func__);
error = ApiLoopAttach(mixer, request, uid,
((loopsJ) ? loopsJ : LoopsJ), streamsJ);
- if (error) goto fail_loop;
+ if (error)
+ goto fail;
+ // Now, forget the saved LoopsJ
+ if (LoopsJ)
+ LoopsJ = NULL;
}
AFB_ApiInfo(mixer->api, "%s set RAMPS", __func__);
@@ -627,7 +630,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (rampsJ) {
error = ApiRampAttach(mixer, request, uid, rampsJ);
if (error)
- goto fail_zone;
+ goto fail;
json_object *resultJ = MixerInfoRamps(mixer, rampsJ, 0);
json_object_object_add(responseJ, "ramps", resultJ);
@@ -638,7 +641,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (streamsJ) {
error = ApiStreamAttach(mixer, request, uid, prefix, streamsJ);
if (error)
- goto fail_ramp;
+ goto fail;
json_object *resultJ = MixerInfoStreams(mixer, streamsJ, 0);
json_object_object_add(responseJ, "streams", resultJ);
@@ -648,7 +651,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
if (error) {
AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to register post attach Verb ",
__func__, mixer->uid, uid);
- goto fail_stream;
+ goto fail;
}
AFB_ApiNotice(mixer->api, "%s responseJ=%s", __func__, json_object_get_string(responseJ));
@@ -657,22 +660,12 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
AFB_ApiInfo(mixer->api,"%s DONE", __func__);
return;
-fail_stream:
- // TODO remove created streams
-fail_ramp:
- // TODO remove created ramps
-fail_zone:
- // TODO remove created zone
-fail_loop:
- // TODO remove created loops
-fail_source:
- // TODO remove created sources
-fail_sink:
- // TODO remove created sinks
fail:
if (mixer->transaction)
- free(mixer->transaction);
+ AlsaMixerTransactionDelete(mixer->transaction);
+
+ mixer->transaction = NULL;
AFB_ApiError(mixer->api,"%s FAILED", __func__);
return;
@@ -771,6 +764,10 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) {
return 0;
OnErrorExit:
+ if (mixer->transaction)
+ AlsaMixerTransactionDelete(mixer->transaction);
+
+ mixer->transaction = NULL;
return -1;
}
diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c
index ce74982..464a725 100644
--- a/plugins/alsa/alsa-api-pcm.c
+++ b/plugins/alsa/alsa-api-pcm.c
@@ -384,14 +384,17 @@ PUBLIC void ApiPcmDelParams(SoftMixerT* mixer, AlsaPcmHwInfoT* params) {
}
PUBLIC void ApiPcmParamsShow(SoftMixerT * mixer, const char *msg, const AlsaPcmHwInfoT * params) {
- AFB_ApiInfo(mixer->api, "%s PARAMS: rate=%d, format=%d, formatString=%s",
- msg, params->rate, params->format, params->formatString);
+ AFB_ApiInfo(mixer->api, "%s PARAMS: rate=%d, format=%d, formatString=%s, channels %d",
+ msg, params->rate, params->format, params->formatString, params->channels);
}
PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object * paramsJ) {
const char *format = NULL, *access = NULL;
+
+ AFB_API_NOTICE(mixer->api, "%s for %s: params are %s", __func__, uid, json_object_get_string(paramsJ));
+
AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT));
if (params == NULL) {
SOFTMIXER_NOMEM(mixer->api);
@@ -405,10 +408,11 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json
if (paramsJ) {
int error =
- wrap_json_unpack(paramsJ, "{s?i,s?s,s?s !}",
+ wrap_json_unpack(paramsJ, "{s?i,s?s,s?s,s?i !}",
"rate", &params->rate,
"format", &format,
- "access", &access);
+ "access", &access,
+ "channels", &params->channels);
if (error) {
AFB_ApiError(mixer->api, "%s: sndcard=%s invalid params=%s",
__func__, uid, json_object_get_string(paramsJ));
@@ -416,6 +420,9 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json
}
}
+ AFB_API_NOTICE(mixer->api, "%s:%s rate set to %d", __func__, uid, params->rate);
+ AFB_API_NOTICE(mixer->api, "%s:%s channels set to %d", __func__, uid, params->channels);
+
// default format
if (!format) {
params->format = SND_PCM_FORMAT_S16_LE;
@@ -502,7 +509,9 @@ PUBLIC void ApiPcmDelete(SoftMixerT * mixer, AlsaSndPcmT * pcm) {
pcmChannelsDestroy(mixer, pcm);
- snd_ctl_close(card->ctl);
+ if (card->ctl)
+ snd_ctl_close(card->ctl);
+
free(card);
free ((char*)pcm->mute.name);
free ((char*)pcm->volume.name);
@@ -536,7 +545,7 @@ fail:
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;
+ json_object *sourceJ = NULL, *paramsJ = NULL, *sinkJ = NULL, *targetJ = NULL, *quirksJ = NULL;
char *apiVerb = NULL, *apiInfo = NULL;
apiVerbHandleT *handle = NULL;
int error;
@@ -554,7 +563,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_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 !}"
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o,s?o,s?b !}"
, "uid", &pcm->uid
, "pcmplug_params", &pcm->sndcard->cid.pcmplug_params
, "path", &pcm->sndcard->cid.devpath
@@ -564,9 +573,11 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
, "sink", &sinkJ
, "source", &sourceJ
, "params", &paramsJ
+ , "quirks", &quirksJ
+ , "optional", &pcm->optional
);
if (error) {
- AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s",
+ AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params|quirks|optional' error=%s args=%s",
__func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
goto fail_pcm_sndcard;
}
@@ -576,11 +587,21 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
else
pcm->isPcmPlug = false;
+ pcm->sndcard->optional = pcm->optional;
+
// try to open sound card control interface
pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard);
if (!pcm->sndcard->ctl) {
+ if (pcm->optional) {
+
+ AFB_API_INFO(mixer->api, "%s: hal=%s Fail to open OPTIONAL sndcard uid=%s devpath=%s cardid=%s",
+ __func__, uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid);
+ goto done;
+ }
+
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;
}
@@ -658,6 +679,29 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
pcm->sndcard->params->channels = pcm->nbChannels;
+ if (quirksJ) {
+ int nbQuirks = (int) json_object_array_length(quirksJ);
+
+ for (int idx = 0; idx < nbQuirks; idx++) {
+ json_object * quirkJ = json_object_array_get_idx(quirksJ, idx);
+
+ if (!json_object_is_type(quirkJ, json_type_string)) {
+ AFB_API_ERROR(mixer->api, "%s: hal=%s quirk must be of type string, arg=%s",
+ __func__, uid, json_object_get_string(quirkJ));
+ goto fail_pcm_channels;
+ }
+ const char * quirk = json_object_get_string(quirkJ);
+
+#define QUIRK_CHECK(quirkS, quirk) \
+ if (!strcmp(quirkS, #quirk)) { \
+ pcm->quirks |= quirk; \
+ AFB_API_INFO(mixer->api, "%s: PCM=%s has quirk %s",__func__, uid, #quirk);\
+ }
+
+ QUIRK_CHECK(quirk, QUIRK_BOGUS_POLL_REVENTS_DEMANGLING);
+ }
+ }
+
if (controlsJ) {
json_object *volJ = NULL, *muteJ = NULL;
error = wrap_json_unpack(controlsJ, "{s?o,s?o !}"
@@ -741,7 +785,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
goto fail_pcm_uid;
}
}
-
+done:
return pcm;
fail_pcm_uid:
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index f7149cc..e3d7bf9 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -263,7 +263,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
} else {
// 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);
+ AFB_ApiDebug(mixer->api,"%s: %s not found in loop, look in sources", __func__, stream->source);
AlsaSndCtlT *sourceDev = ApiSourceFindSubdev(mixer, stream->source);
if (sourceDev) {
@@ -278,12 +278,20 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
__func__, uid, captureDev->pcmplug_params, sourceDev->cid.cardid);
} else {
AFB_ApiError(mixer->api,
- "%s: mixer=%s stream=%s not found in loops/sources",
- __func__, mixer->uid, stream->uid);
+ "%s: mixer=%s stream=%s: %s not found in loops/sources",
+ __func__, mixer->uid, stream->uid, stream->source);
goto OnErrorExit;
}
}
+ stream->optional = captureCard->optional;
+
+ if (!captureCard->ctl && captureCard->optional) {
+ stream->noHwDetected = true;
+ AFB_API_INFO(mixer->api,"%s: no detected capture device", __func__);
+ goto done;
+ }
+
// check PCM is valid and get its full name
AlsaPcmCtlT *capturePcm = AlsaByPathOpenPcmCtl(mixer, captureDev, SND_PCM_STREAM_CAPTURE);
if (!capturePcm) {
@@ -417,7 +425,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
if ((zone->params->rate != stream->params->rate) ||
(zone->params->format != stream->params->format)) {
AFB_ApiNotice(mixer->api,
- "%s: Instanciate a RATE CONVERTER (stream [rate %d,%s(%d),%d channels], zone [rate %d,%s(%d), %d channels])",
+ "%s: Instantiate a RATE CONVERTER (stream [rate %d,%s(%d),%d channels], zone [rate %d,%s(%d), %d channels])",
__func__,
stream->params->rate,
stream->params->formatString,
@@ -445,8 +453,9 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
}
streamPcm->isPcmPlug = zone->isPcmPlug;
+ streamPcm->quirks = zone->quirks;
- AFB_ApiDebug(mixer->api, "%s: Opening PCM PLAYBACK name %s", __func__, playbackName);
+ AFB_ApiDebug(mixer->api, "%s: Opening PCM PLAYBACK name %s (quirks=0x%x)", __func__, playbackName, streamPcm->quirks);
// 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*/ );
@@ -531,7 +540,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
AFB_ApiNotice(mixer->api,
"%s: mixer=%s stream=%s CREATED",
__func__, mixer->uid, stream->uid);
-
+done:
return 0;
OnErrorExit:
@@ -667,6 +676,9 @@ static void streamDestroy(SoftMixerT * mixer, void * arg) {
int error = 0;
AFB_ApiDebug(mixer->api, "%s... %s", __func__, stream->uid);
+ if (stream->noHwDetected)
+ goto freemem;
+
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);
@@ -674,19 +686,7 @@ static void streamDestroy(SoftMixerT * mixer, void * arg) {
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;
- }
+freemem:
free((char*)stream->uid);
free((char*)stream->playback);
@@ -743,6 +743,8 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid
AFB_ReqFailF(request, "bad-stream", "mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(argsJ));
goto fail;
}
+ if (newStream->noHwDetected)
+ AlsaMixerTransactionObjectDelete(mixer->transaction, newStream, true);
break;
@@ -765,7 +767,10 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid
"%s: mixer=%s invalid stream= %s",
__func__, mixer->uid, json_object_get_string(streamJ));
goto fail;
- }
+ }
+ if (newStream->noHwDetected)
+ AlsaMixerTransactionObjectDelete(mixer->transaction, newStream, true);
+
}
break;
default:
diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c
index bf3bece..83f6e3a 100644
--- a/plugins/alsa/alsa-api-zones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -81,6 +81,7 @@ fail:
return NULL;
}
+
STATIC AlsaSndZoneT *AttachOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) {
AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid);
@@ -199,13 +200,6 @@ static void zoneDestroy(SoftMixerT* mixer, void * arg) {
AFB_ApiDebug(mixer->api, "%s... %s (%d sinks, %d sources)", __func__, zone->uid, zone->nbSinks, zone->nbSources);
- 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;
- }
-
AlsaPcmChannelT * channel, *tmp;
cds_list_for_each_entry_safe(channel, tmp, &zone->sinks.list, list) {
@@ -241,6 +235,7 @@ PUBLIC AlsaSndZoneT * zoneCreate(SoftMixerT* mixer, const char * uid, json_objec
}
zone->isPcmPlug = routeConfig->isPcmPlug;
+ zone->quirks = routeConfig->quirks;
fail:
return zone;
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index a45d463..0e8f86f 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -136,7 +136,22 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
}
}
- if (opts->rate > 0 ) {
+ unsigned int * channels = &opts->channels;
+
+ if (*channels) {
+ AFB_API_DEBUG(mixer->api, "%s: Attempt to set %d channels", __func__, *channels);
+ if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, *channels)) < 0) {
+ AFB_API_ERROR(mixer->api,
+ "%s (%s): mixer=%s Set_Channels=%d Fail error=%s",
+ __func__, card, mixer->uid, *channels, snd_strerror(error));
+
+ AlsaDumpPcmParams(mixer, pxmHwParams);
+ goto OnErrorExit;
+ };
+ AFB_API_DEBUG(mixer->api, "%s: CHANNELS SET TO %d", __func__, *channels);
+ }
+
+ if (opts->rate > 0 ) {
AFB_ApiDebug(mixer->api,"%s (%s): set rate to %d", __func__, card, opts->rate);
unsigned int pcmRate = opts->rate;
@@ -158,18 +173,10 @@ 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 (%s): mixer=%s Set_Channels=%d Fail error=%s",
- __func__, card, mixer->uid, opts->channels, snd_strerror(error));
- goto OnErrorExit;
- };
- }
/* The following code, that
* 1) sets period time/size; buffer time/size hardware params
- * 2) sets start and stop threashold in software params
+ * 2) sets start and stop threshold in software params
* ... is taken as such from 'aplay' in alsa-utils */
unsigned buffer_time = 0;
@@ -275,6 +282,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
// available_min is the minimum number of frames available to read,
// when the call to poll returns. Assume this is the same for playback (not checked that)
+ AFB_API_DEBUG(mixer->api, "(%s) try AVAIL_MIN: %ld", card, n);
+
if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, n)) < 0) {
AFB_ApiError(mixer->api,
"%s (%s): mixer=%s Fail set_buffersize error=%s",
@@ -283,11 +292,12 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
};
snd_pcm_sw_params_get_avail_min(pxmSwParams, &pcm->avail_min);
+ AFB_API_DEBUG(mixer->api, "(%s) AVAIL_MIN set to : %ld", card, pcm->avail_min);
int start_delay = 0;
snd_pcm_uframes_t start_threshold;
- /* round up to closest transfer boundary */
+ /* round up to closest transfer boundary. Start delay is in microseconds */
n = buffer_size;
if (start_delay <= 0) {
start_threshold = n + (size_t)((double)rate * start_delay / 1000000);
@@ -340,13 +350,14 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) {
snd_pcm_uframes_t bufSize = alsa_ringbuf_buffer_size(rbuf);
int err;
+ const char * uid = ALSA_PCM_UID(pcmIn, string);
// do we have waiting frames ?
availIn = snd_pcm_avail_update(pcmIn);
if (availIn <= 0) {
if (availIn == -EPIPE) {
int ret = xrun(pcmIn, (int)availIn);
- AFB_ApiDebug(pcmCopyHandle->api, "XXX read EPIPE (recov=%d) {%s}!", ret, ALSA_PCM_UID(pcmIn, string));
+ AFB_ApiDebug(pcmCopyHandle->api, "XXX read EPIPE (recov=%d) {%s}!", ret, uid);
// For some (undocumented...) reason, a start is mandatory.
snd_pcm_start(pcmIn);
@@ -376,14 +387,13 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) {
pthread_mutex_unlock(&pcmCopyHandle->mutex);
nbRead = snd_pcm_readi(pcmIn, buf, remain);
-
if (nbRead == 0) {
break;
}
if (nbRead < 0) {
if (nbRead== -EPIPE) {
err = xrun(pcmIn, (int)nbRead);
- AFB_ApiDebug(pcmCopyHandle->api, "read EPIPE (%d), recov %d", ++pcmCopyHandle->read_err_count, err);
+ AFB_ApiDebug(pcmCopyHandle->api, "read EPIPE (%d), recov %d {%s}", ++pcmCopyHandle->read_err_count, err, uid);
goto ExitOnSuccess;
} else if (nbRead== -ESTRPIPE) {
AFB_ApiDebug(pcmCopyHandle->api, "read ESTRPIPE");
@@ -391,6 +401,7 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) {
goto ExitOnSuccess;
nbRead = 0;
} else {
+ AFB_API_DEBUG(pcmCopyHandle->api, "read error: %ld (%s) {%s}", -nbRead, strerror((int)-nbRead), uid);
goto ExitOnSuccess;
}
}
@@ -422,7 +433,6 @@ ExitOnSuccess:
static int xrun( snd_pcm_t * pcm, int error)
{
int err;
-
if ((err = snd_pcm_recover(pcm, error, 1)) < 0) {
return err;
}
@@ -445,8 +455,8 @@ static int suspend( snd_pcm_t * pcm, int error)
static void readSuspend(AlsaPcmCopyHandleT * pcmCopyHandle) {
// will be deaf
- pcmCopyHandle->saveFd = pcmCopyHandle->pollFds[1].fd;
- pcmCopyHandle->pollFds[1].fd = -1;
+ pcmCopyHandle->saveFd = pcmCopyHandle->pollFdsIn[1].fd;
+ pcmCopyHandle->pollFdsIn[1].fd = -1;
AFB_ApiNotice(pcmCopyHandle->api, "capture muted");
}
@@ -454,7 +464,7 @@ static void readSuspend(AlsaPcmCopyHandleT * pcmCopyHandle) {
static void readResume(AlsaPcmCopyHandleT * pcmCopyHandle) {
// undeaf it
- pcmCopyHandle->pollFds[1].fd = pcmCopyHandle->saveFd;
+ pcmCopyHandle->pollFdsIn[1].fd = pcmCopyHandle->saveFd;
snd_pcm_prepare(pcmCopyHandle->pcmIn->handle);
snd_pcm_start(pcmCopyHandle->pcmIn->handle);
AFB_ApiNotice(pcmCopyHandle->api, "capture unmuted");
@@ -466,32 +476,26 @@ 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 * eventFd = &pcmCopyHandle->pollFds[0];
- struct pollfd * framePfds = &pcmCopyHandle->pollFds[1];
+ struct pollfd * eventFd = &pcmCopyHandle->pollFdsIn[0];
+ struct pollfd * framePfds = &pcmCopyHandle->pollFdsIn[1];
- eventFd->events = POLLIN | POLLHUP;
-
- for (ix = 0; ix <pcmCopyHandle->nbPcmFds-1; ix++) {
- framePfds[ix].events = POLLIN | POLLHUP;
- }
+ eventFd->events = POLLIN;
bool muted = pcmCopyHandle->pcmIn->mute;
if (muted)
readSuspend(pcmCopyHandle);
-
/* loop until end */
for (;;) {
- int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->nbPcmFds, LOOP_TIMEOUT_MSEC);
- if (err < 0) {
+ int err = poll(pcmCopyHandle->pollFdsIn, pcmCopyHandle->nbPcmFdsIn, LOOP_TIMEOUT_MSEC);
+ if (err < 0) {
AFB_ApiError(pcmCopyHandle->api, "%s: poll err %s", __func__, strerror(errno));
continue;
}
@@ -503,7 +507,7 @@ static void *readThreadEntry(void *handle) {
}
// handle the incoming events/mute order
- if ((eventFd->revents & EPOLLIN) != 0) {
+ if ((eventFd->revents & POLLIN) != 0) {
PcmCopyEvent event;
size_t ret = read(eventFd->fd, &event, sizeof(event));
@@ -537,8 +541,7 @@ static void *readThreadEntry(void *handle) {
unsigned short revents = 0;
- int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFds, &revents);
-
+ int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFdsIn-1, &revents);
if (res == -ENODEV) {
sleep(1);
continue;
@@ -549,6 +552,11 @@ static void *readThreadEntry(void *handle) {
continue;
}
+ if (!(revents & POLLIN)) {
+ AFB_API_DEBUG(pcmCopyHandle->api, "no POLLIN. try again !");
+ continue;
+ }
+
AlsaPcmReadCB(pcmCopyHandle);
}
done:
@@ -564,23 +572,14 @@ static void *writeThreadEntry(void *handle) {
alsa_ringbuf_t * rbuf = pcmCopyHandle->rbuf;
- snd_pcm_status_t *pcmOutStatus;
- snd_pcm_uframes_t pcmOutSize;
-
- snd_pcm_sframes_t threshold;
-
- snd_pcm_status_alloca(&pcmOutStatus);
- 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
- * frames from the ring buffer. So basically, this saves some CPU load */
+ struct pollfd * eventFd = &pcmCopyHandle->pollFdsOut[0];
+ struct pollfd * framePfds = &pcmCopyHandle->pollFdsOut[1];
- threshold = pcmOutSize / 3;
+ eventFd->events = POLLIN;
+
+ const char * name = pcmCopyHandle->pcmOut->name;
for (;;) {
@@ -593,6 +592,62 @@ static void *writeThreadEntry(void *handle) {
goto done;
}
+ int err = poll(pcmCopyHandle->pollFdsOut, pcmCopyHandle->nbPcmFdsOut, LOOP_TIMEOUT_MSEC);
+ if (err < 0) {
+ AFB_API_ERROR(pcmCopyHandle->api, "%s: %s poll out err %s", __func__, name, strerror(errno));
+ continue;
+ }
+
+ // timeout
+ if (err == 0)
+ continue;
+
+ // handle the incoming events/mute order
+ if ((eventFd->revents & POLLIN) != 0) {
+ PcmCopyEvent event;
+
+ size_t ret = read(eventFd->fd, &event, sizeof(event));
+ if (ret <= 0)
+ continue;
+
+ switch (event.eventType) {
+ case PCM_COPY_MUTE:
+ case PCM_COPY_UNMUTE:
+ break;
+ case PCM_COPY_END:
+ AFB_API_DEBUG(pcmCopyHandle->api, "%s ending -> EXIT", __func__);
+ goto done;
+ break;
+ case PCM_COPY_LAST:
+ default:
+ AFB_API_ERROR(pcmCopyHandle->api, "%s: Unexpected event 0x%x", __func__, event.eventType);
+ break;
+ }
+ continue;
+ }
+
+ unsigned short revents = 0;
+
+ int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmOut->handle, framePfds, pcmCopyHandle->nbPcmFdsOut-1, &revents);
+ if (res == -ENODEV) {
+ sleep(1);
+ continue;
+ }
+
+ if (revents & POLLHUP) {
+ AFB_API_NOTICE(pcmCopyHandle->api, "Frame POLLHUP");
+ continue;
+ }
+
+ // not ready ... wait again
+ if ((revents & POLLOUT) == 0) {
+ // When that quirk is set, continuing to poll again would block too much time
+ // and led to a EPIPE
+ // Instead, we ignore the "non ready state" and perform the write directly
+ if ((pcmCopyHandle->pcmOut->quirks & QUIRK_BOGUS_POLL_REVENTS_DEMANGLING) == 0)
+ continue;
+ }
+
snd_pcm_sframes_t used, nbWritten;
snd_pcm_sframes_t availOut = snd_pcm_avail(pcmOut);
@@ -609,15 +664,9 @@ static void *writeThreadEntry(void *handle) {
}
}
- // no space for output
- if (availOut <= threshold) {
- usleep(500);
- continue;
- }
-
pthread_mutex_lock(&pcmCopyHandle->mutex);
used = alsa_ringbuf_frames_used(rbuf);
- if (used <= 0) {
+ if (used <= 0) { /* no more frames*/
pthread_mutex_unlock(&pcmCopyHandle->mutex);
break; // will wait again
}
@@ -640,8 +689,11 @@ static void *writeThreadEntry(void *handle) {
} else if (nbWritten == -ESTRPIPE) {
AFB_ApiDebug(pcmCopyHandle->api, "XXX write ESTRPIPE");
break;
+ } else if (nbWritten == 0) {
+ AFB_API_DEBUG(pcmCopyHandle->api, "%s: nothing was written", __func__);
+ break;
}
- AFB_ApiDebug(pcmCopyHandle->api, "Unhandled error %s", strerror(errno));
+ AFB_ApiDebug(pcmCopyHandle->api, "%s: Unhandled error %s", __func__, strerror((int)-nbWritten));
break;
}
@@ -676,8 +728,11 @@ 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 wait semaphore
sem_post(&handle->sem);
+ // and make in exit from poll
+ AlsaPcmCopyEndSignal(mixer, handle->pcmOut);
return 0;
}
@@ -700,7 +755,8 @@ PUBLIC int AlsaPcmCopyStop(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) {
alsa_ringbuf_free(handle->rbuf);
- free(handle->pollFds);
+ free(handle->pollFdsIn);
+ free(handle->pollFdsOut);
free(handle);
return 0;
@@ -791,7 +847,7 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm
cHandle->stream = stream;
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: Frame size is %zu (%d channels)", __func__, cHandle->frame_size, opts->channels);
AFB_ApiDebug(mixer->api, "%s: Buffer delay is %d ms", __func__, stream->delayms);
snd_pcm_uframes_t nbFrames = (stream->delayms * opts->rate)/1000;
@@ -807,6 +863,29 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm
AFB_ApiDebug(mixer->api, "%s Copy buffer: nbframes is %zu", __func__, nbFrames);
+ // get FD poll descriptor for playback PCM
+ int pcmOutCount = snd_pcm_poll_descriptors_count(pcmOut->handle);
+ if (pcmOutCount <= 0) {
+ AFB_API_ERROR(mixer->api,
+ "%s: Fail pcmOut=%s get fds count error=%s",
+ __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
+ goto OnErrorExit;
+ }
+
+ cHandle->nbPcmFdsOut = pcmOutCount+1;
+ cHandle->pollFdsOut = (struct pollfd *) malloc((cHandle->nbPcmFdsOut)*sizeof(struct pollfd));
+ if (cHandle->pollFdsOut == NULL){
+ SOFTMIXER_NOMEM(mixer->api);
+ goto OnErrorExit;
+ }
+
+ if ((error = snd_pcm_poll_descriptors(pcmOut->handle, cHandle->pollFdsOut+1, pcmOutCount)) < 0) {
+ AFB_API_ERROR(mixer->api,
+ "%s: Fail pcmOut=%s get pollfds error=%s",
+ __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
+ goto OnErrorExit;
+ };
+
// get FD poll descriptor for capture PCM
int pcmInCount = snd_pcm_poll_descriptors_count(pcmIn->handle);
if (pcmInCount <= 0) {
@@ -816,43 +895,61 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm
goto OnErrorExit;
}
- cHandle->nbPcmFds = pcmInCount+1;
- cHandle->pollFds = (struct pollfd *) malloc((cHandle->nbPcmFds)*sizeof(struct pollfd));
- if (cHandle->pollFds == NULL){
+ cHandle->nbPcmFdsIn = pcmInCount+1;
+ cHandle->pollFdsIn = (struct pollfd *) malloc((cHandle->nbPcmFdsIn)*sizeof(struct pollfd));
+ if (cHandle->pollFdsIn == NULL){
SOFTMIXER_NOMEM(mixer->api);
goto OnErrorExit;
}
- if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFds+1, pcmInCount)) < 0) {
+ if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFdsIn+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));
goto OnErrorExit;
};
- // create the mute pipe
- int eventFdPipe[2];
- error = pipe(eventFdPipe);
+ // create the event pipe for capture
+ int eventFdPipeIn[2];
+ error = pipe(eventFdPipeIn);
if (error < 0) {
AFB_ApiError(mixer->api,
- "Unable to create the mute signaling pipe");
+ "Unable to create the mute signaling pipe for capture");
goto OnErrorExit;
}
- struct pollfd * eventPollFd = &cHandle->pollFds[0];
+ struct pollfd * eventPollFdIn = &cHandle->pollFdsIn[0];
// read end
- eventPollFd->fd = eventFdPipe[0];
- eventPollFd->events = POLLIN;
- eventPollFd->revents = 0;
+ eventPollFdIn->fd = eventFdPipeIn[0];
+ eventPollFdIn->events = POLLIN;
+ eventPollFdIn->revents = 0;
// write end
- pcmIn->eventFd = eventFdPipe[1];
+ pcmIn->eventFd = eventFdPipeIn[1];
+
+ // create the event pipe for playback
+ int eventFdPipeOut[2];
+ error = pipe(eventFdPipeOut);
+ if (error < 0) {
+ AFB_API_ERROR(mixer->api,
+ "Unable to create the signaling pipe for playback");
+ goto OnErrorExit;
+ }
+
+ struct pollfd * eventPollFdOut = &cHandle->pollFdsOut[0];
+ // read end
+ eventPollFdOut->fd = eventFdPipeOut[0];
+ eventPollFdOut->events = POLLIN;
+ eventPollFdOut->revents = 0;
+
+ // write end
+ pcmOut->eventFd = eventFdPipeOut[1];
error = sem_init(&cHandle->sem, 0 , 0);
if (error < 0) {
AFB_ApiError(mixer->api,
- "%s Fail initialize loop semaphore pcmIn=%s err=%d",
- __func__, ALSA_PCM_UID(pcmIn->handle, string), error);
+ "%s Fail to initialize the loop semaphore pcmIn=%s err=%d",
+ __func__, ALSA_PCM_UID(pcmIn->handle, string), error);
goto OnErrorExit;
}
@@ -910,11 +1007,28 @@ 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 &&cHandle->pollFdsIn) {
+ free (cHandle->pollFdsIn);
+ cHandle->pollFdsIn = NULL;
+ }
+
+ if (cHandle && cHandle->pollFdsOut) {
+ free (cHandle->pollFdsOut);
+ cHandle->pollFdsOut = NULL;
}
+ if (eventFdPipeIn[0])
+ close(eventFdPipeIn[0]);
+
+ if (eventFdPipeIn[1])
+ close(eventFdPipeIn[1]);
+
+ if (eventFdPipeOut[0])
+ close(eventFdPipeOut[0]);
+
+ if (eventFdPipeOut[1])
+ close(eventFdPipeOut[1]);
+
if (cHandle)
free(cHandle);
diff --git a/plugins/alsa/alsa-plug-dmix.c b/plugins/alsa/alsa-plug-dmix.c
index 9aaf866..bb58a29 100644
--- a/plugins/alsa/alsa-plug-dmix.c
+++ b/plugins/alsa/alsa-plug-dmix.c
@@ -24,6 +24,13 @@ static int uniqueIpcIndex = 1024;
ALSA_PLUG_PROTO(dmix);
+static void dmixConfigClean(SoftMixerT *mixer, void * arg) {
+ snd_config_t * dmixConfig = arg;
+ AFB_API_DEBUG(mixer->api, "%s... ", __func__);
+ snd_config_delete(dmixConfig);
+ snd_config_update();
+}
+
PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open) {
@@ -92,7 +99,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS
if (error) goto OnErrorExit;
}
- /* It is critical to set the right number of channels ... know.
+ /* It is critical to set the right number of channels ... now.
* Trying to set another value later leads to silent failure
* */
@@ -121,6 +128,9 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS
goto OnErrorExit;
}
+ pcmPlug->private_data = dmixConfig;
+ pcmPlug->private_data_clean = dmixConfigClean;
+
// Debug config & pcm
AlsaDumpCtlConfig(mixer, "plug-dmix", dmixConfig, 1);
AFB_ApiNotice(mixer->api, "%s: %s done", __func__, pcmPlug->cid.cardid);
diff --git a/plugins/alsa/alsa-plug-rate.c b/plugins/alsa/alsa-plug-rate.c
index d4a3068..ea96da2 100644
--- a/plugins/alsa/alsa-plug-rate.c
+++ b/plugins/alsa/alsa-plug-rate.c
@@ -23,6 +23,13 @@
ALSA_PLUG_PROTO(rate);
+static void rateConfigClean(SoftMixerT *mixer, void * arg) {
+ snd_config_t * rateConfig = arg;
+ AFB_API_DEBUG(mixer->api, "%s... rate config", __func__);
+ snd_config_delete(rateConfig);
+ snd_config_update();
+}
+
PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open) {
snd_config_t *rateConfig, *slaveConfig, *elemConfig, *pcmConfig;
@@ -71,8 +78,6 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream,
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,
@@ -81,6 +86,9 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream,
goto OnErrorExit;
}
+ pcmPlug->private_data = rateConfig;
+ pcmPlug->private_data_clean = rateConfigClean;
+
// Debug config & pcm
//AlsaDumpCtlConfig(mixer, "plug-rate", pcmConfig, 1);
AlsaDumpCtlConfig (mixer, "plug-rate", rateConfig, 1);
diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c
index a192e03..021ef44 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -23,6 +23,14 @@
ALSA_PLUG_PROTO(route);
+static void routeConfigClean(SoftMixerT *mixer, void * arg) {
+ snd_config_t * routeConfig = arg;
+ AFB_API_DEBUG(mixer->api, "%s... route config", __func__);
+ snd_config_delete(routeConfig);
+ snd_config_update();
+}
+
+
typedef struct {
const char *uid;
const char *cardid;
@@ -31,6 +39,7 @@ typedef struct {
int ccount;
int port;
bool isPcmPlug;
+ unsigned int quirks;
} ChannelCardPortT;
STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT *response) {
@@ -59,6 +68,7 @@ STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT
response->cardidx = pcm->sndcard->cid.cardidx;
response->pcmplug_params = pcm->sndcard->cid.pcmplug_params;
response->isPcmPlug= pcm->isPcmPlug;
+ response->quirks = pcm->quirks;
found = true;
break;
}
@@ -79,7 +89,7 @@ OnErrorExit:
}
PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open) {
- snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
+ snd_config_t *routeConfig = NULL, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
int error = 0;
ChannelCardPortT slave, channelCardPort;
AlsaPcmCtlT *pcmRoute = NULL;
@@ -151,15 +161,21 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
pcmRoute->isPcmPlug = true;
}
+ pcmRoute->quirks = slave.quirks;
+
// temporary store to enable multiple channel to route to the same port
snd_config_t **cports = alloca(zone->nbSinks * sizeof (void*));
memset(cports, 0, zone->nbSinks * sizeof (void*));
- int zcount = 0;
+ int portCount = 0;
// We create 1st ttable to retrieve sndcard slave and channel count
(void)snd_config_make_compound(&tableConfig, "ttable", 0);
+ // used physical ports
+ bool targets[16];
+ memset(targets, 0, sizeof(targets));
+
cds_list_for_each_entry(channel, &zone->sinks.list, list) {
AFB_ApiDebug(mixer->api, "%s: zone->sink channel %s ", __func__, channel->uid);
@@ -180,12 +196,17 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
int target = channelCardPort.port;
int port = channel->port;
+ if (!targets[target]) {
+ zone->physicalChannelCount++;
+ targets[target] = true;
+ }
+
double volume = 1.0; // currently only support 100%
// if channel entry does not exist into ttable create it now
if (!cports[port]) {
- char channelS[4]; // 999 channel should be more than enough
+ char channelS[4]; // 999 channels should be more than enough
snprintf(channelS, sizeof (channelS), "%d", port);
error = snd_config_make_compound(&cports[port], channelS, 0);
@@ -201,7 +222,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
}
}
- zcount++;
+ portCount++;
// ttable require target port as a table and volume as a value
char targetS[4];
@@ -227,8 +248,10 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
}
// update zone with route channel count and sndcard params
- pcmRoute->ccount = zcount;
- zone->ccount=zcount;
+ pcmRoute->ccount = portCount;
+ zone->ccount=portCount;
+
+ AFB_API_DEBUG(mixer->api, "%s: ZONE has %d ports and %d hardware channels", __func__, zone->ccount, zone->physicalChannelCount);
// refresh global alsalib config and create PCM top config
snd_config_update();
@@ -275,7 +298,8 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o
goto fail;
}
- zone->routeConfig = routeConfig;
+ pcmRoute->private_data = routeConfig;
+ pcmRoute->private_data_clean = routeConfigClean;
// Debug config & pcm
AFB_ApiNotice(mixer->api, "%s: zone(%s) DONE", __func__, zone->uid);
diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c
index 09025c4..ade29ac 100644
--- a/plugins/alsa/alsa-plug-vol.c
+++ b/plugins/alsa/alsa-plug-vol.c
@@ -22,8 +22,13 @@
ALSA_PLUG_PROTO(softvol); // stream uses softvol plugin
-PUBLIC void AlsaDeleteSoftvol(SoftMixerT *mixer, AlsaPcmCtlT * ctl) {
+
+static void softVolConfigClean(SoftMixerT *mixer, void * arg) {
+ snd_config_t * softvolConfig = arg;
+ AFB_API_DEBUG(mixer->api, "%s... softvol config", __func__);
+ snd_config_delete(softvolConfig);
+ snd_config_update();
}
PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, char* slaveid, AlsaSndCtlT *sndcard, char* ctlName, int max, int open) {
@@ -95,7 +100,8 @@ PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *strea
goto OnErrorExit;
}
- stream->softvolConfig = streamConfig;
+ pcmVol->private_data = streamConfig;
+ pcmVol->private_data_clean = softVolConfigClean;
// Debug config & pcm
//AlsaDumpCtlConfig (mixer, "plug-config", pcmConfig, 1);
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index 2e7874d..ea3e201 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -128,6 +128,10 @@ typedef struct {
bool closeAtDeletion; // intermediate pcms in the pcm chain must not be closed, else it make libasound abort()
bool isPcmPlug;
+
+ void * private_data;
+ void (*private_data_clean) (struct SoftMixerT_ *, void *);
+ unsigned int quirks;
} AlsaPcmCtlT;
typedef struct {
@@ -154,8 +158,11 @@ typedef struct {
int tid;
char* info;
- int nbPcmFds;
- struct pollfd * pollFds;
+ int nbPcmFdsIn;
+ struct pollfd * pollFdsIn;
+
+ int nbPcmFdsOut;
+ struct pollfd * pollFdsOut;
sem_t sem;
pthread_mutex_t mutex;
@@ -210,6 +217,7 @@ typedef struct AlsaSndCtlT_ {
long nbRegistry;
struct cds_list_head registryList;
struct SubscribeHandleT_ * eventSubscribeHandle;
+ bool optional;
} AlsaSndCtlT;
@@ -220,12 +228,20 @@ typedef struct {
unsigned int nbSinks;
AlsaPcmChannelT sinks;
int ccount;
+ int physicalChannelCount;
AlsaPcmHwInfoT *params;
struct cds_list_head list;
- snd_config_t * routeConfig;
+
bool isPcmPlug;
+ unsigned int quirks;
} AlsaSndZoneT;
+
+/* This is a list of known sound card (hardware or driver) specific bugs */
+
+/* - do not trust the result of snd_pcm_poll_descriptors_revents */
+#define QUIRK_BOGUS_POLL_REVENTS_DEMANGLING (1<<0)
+
typedef struct {
const char *uid;
const char *verb;
@@ -238,6 +254,8 @@ typedef struct {
struct cds_list_head list;
bool isPcmPlug;
void * apiVerbHandle;
+ unsigned int quirks;
+ bool optional;
} AlsaSndPcmT;
typedef struct {
@@ -277,9 +295,10 @@ typedef struct AlsaStreamAudioT_ {
AlsaPcmCopyHandleT *copy;
struct cds_list_head list; /* link to the global list*/
AlsaPcmCtlT * softvol;
- snd_config_t * softvolConfig;
- snd_config_t * rateConfig;
+
void * verbApiHandle;
+ bool optional;
+ bool noHwDetected;
} AlsaStreamAudioT;
typedef struct SoftMixerT_{
diff --git a/plugins/alsa/alsa-transaction.c b/plugins/alsa/alsa-transaction.c
index dcfd028..a630bdc 100644
--- a/plugins/alsa/alsa-transaction.c
+++ b/plugins/alsa/alsa-transaction.c
@@ -6,16 +6,19 @@
#include "wrap-json.h"
-void AlsaMixerTransactionDelete(AlsaMixerTransaction * transaction) {
+static void AlsaMixerTransactionFree(AlsaMixerTransaction * transaction) {
free((char*)transaction->uid);
free(transaction);
}
AlsaMixerTransaction * AlsaMixerTransactionNew(struct SoftMixerT_ * mixer, const char * uid) {
+
AlsaMixerTransaction * transaction = (AlsaMixerTransaction *) malloc(sizeof(AlsaMixerTransaction));
if (transaction == NULL)
goto fail;
+ AFB_API_INFO(mixer->api, "-------- NEW TRANSACTION %s --------", uid);
+
CDS_INIT_LIST_HEAD(&transaction->item_list);
transaction->uid = strdup(uid);
if (transaction->uid == NULL) {
@@ -33,14 +36,15 @@ fail:
return NULL;
}
-void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction* list) {
- free(list);
-}
-void AlsaMixerTransactionObjectForget(AlsaMixerTransaction* list, void * object) {
+void AlsaMixerTransactionObjectDelete(AlsaMixerTransaction* transaction, void * object, bool destructor) {
AlsaMixerTransactionDataItem *item, *tmp;
- cds_list_for_each_entry_safe(item, tmp, &list->item_list, list_entry)
+ cds_list_for_each_entry_safe(item, tmp, &transaction->item_list, list_entry)
if (item->object == object) {
+
+ if (destructor && item->destructor)
+ item->destructor(transaction->mixer, item->object);
+
cds_list_del(&item->list_entry);
free(item);
}
@@ -88,7 +92,7 @@ fail:
}
-void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) {
+static void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) {
AlsaMixerTransactionDataItem * item, *sav;
AFB_ApiInfo(transaction->mixer->api, "%s for transaction %s", __func__, transaction->uid);
@@ -127,11 +131,6 @@ void AlsaMixerTransactionVerbCB(AFB_ReqT request) {
}
if (strcmp(action, "remove") == 0) {
- AlsaMixerTransactionDoCleanup(transaction);
-
- // remove this transaction for the list of mixer
- cds_list_del(&transaction->transaction_node);
-
error = afb_api_del_verb(transaction->mixer->api, transaction->uid, (void**)NULL);
if (error) {
AFB_ReqFail(request, "verb deletion" , "verb was not removed");
@@ -154,3 +153,10 @@ fail:
return;
}
+
+void AlsaMixerTransactionDelete(AlsaMixerTransaction * transaction) {
+ AlsaMixerTransactionDoCleanup(transaction);
+ // remove this transaction for the list of mixer
+ cds_list_del(&transaction->transaction_node);
+ AlsaMixerTransactionFree(transaction);
+}
diff --git a/plugins/alsa/alsa-transaction.h b/plugins/alsa/alsa-transaction.h
index ba45ae7..9080518 100644
--- a/plugins/alsa/alsa-transaction.h
+++ b/plugins/alsa/alsa-transaction.h
@@ -32,9 +32,7 @@ extern void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction*);
extern bool AlsaMixerTransactionObjectAdd(AlsaMixerTransaction*, void * object, AlsaTransactionDestructor destructor);
extern bool AlsaMixerTransactionObjectAddTail(AlsaMixerTransaction* list, void* object, AlsaTransactionDestructor destructor);
-extern void AlsaMixerTransactionObjectForget(AlsaMixerTransaction* list, void * object);
-
-extern void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction*);
+extern void AlsaMixerTransactionObjectDelete(AlsaMixerTransaction* list, void * object, bool destructor);
extern bool AlsaMixerTransactionVerbCreate(AlsaMixerTransaction*);
diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c
index 3a8afb7..674b4d6 100644
--- a/plugins/alsa/alsa-utils-bypath.c
+++ b/plugins/alsa/alsa-utils-bypath.c
@@ -93,6 +93,9 @@ PUBLIC void AlsaPcmCtlDelete(SoftMixerT* mixer, void * arg) {
AFB_ApiDebug(mixer->api, "%s of %s (plug: %d)", __func__, pcmCtl->name, pcmCtl->isPcmPlug);
+ if (pcmCtl->private_data_clean)
+ pcmCtl->private_data_clean(mixer, pcmCtl->private_data);
+
if (!pcmCtl->closeAtDeletion)
goto done;
diff --git a/plugins/alsa/alsa-utils-dump.c b/plugins/alsa/alsa-utils-dump.c
index 79059ee..4bcc294 100644
--- a/plugins/alsa/alsa-utils-dump.c
+++ b/plugins/alsa/alsa-utils-dump.c
@@ -153,6 +153,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_putc(output,'\0');
snd_output_buffer_string(output, &buffer);
AFB_ApiDebug(mixer->api, "%s:\n--------------\n%s\n-------------",__func__, buffer);
snd_output_close(output);