diff options
Diffstat (limited to 'plugins/alsa')
-rw-r--r-- | plugins/alsa/alsa-api-loop.c | 13 | ||||
-rw-r--r-- | plugins/alsa/alsa-api-mixer.c | 45 | ||||
-rw-r--r-- | plugins/alsa/alsa-api-pcm.c | 62 | ||||
-rw-r--r-- | plugins/alsa/alsa-api-streams.c | 45 | ||||
-rw-r--r-- | plugins/alsa/alsa-api-zones.c | 9 | ||||
-rw-r--r-- | plugins/alsa/alsa-core-pcm.c | 262 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-dmix.c | 12 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-rate.c | 12 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-route.c | 38 | ||||
-rw-r--r-- | plugins/alsa/alsa-plug-vol.c | 10 | ||||
-rw-r--r-- | plugins/alsa/alsa-softmixer.h | 29 | ||||
-rw-r--r-- | plugins/alsa/alsa-transaction.c | 30 | ||||
-rw-r--r-- | plugins/alsa/alsa-transaction.h | 4 | ||||
-rw-r--r-- | plugins/alsa/alsa-utils-bypath.c | 3 | ||||
-rw-r--r-- | plugins/alsa/alsa-utils-dump.c | 1 |
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", ¶ms->rate, "format", &format, - "access", &access); + "access", &access, + "channels", ¶ms->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", ¶msJ + , "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); |