/* * Copyright (C) 2018 "IoT.bzh" * Author Fulup Ar Foll * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #define _GNU_SOURCE // needed for vasprintf #include "alsa-softmixer.h" PUBLIC AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *target) { // try to attach a zone as stream playback sink AlsaSndZoneT *zone = ApiZoneGetByUid(mixer, target); if (!zone) { AFB_ApiError(mixer->api, "%s: Unable to find zone %s", __func__, target); goto fail; } if (zone->nbSinks == 0) { AFB_ApiError(mixer->api, "%s: Zone %s has no sinks", __func__, target); goto fail; } // use 1st channel to find attached sound card. AlsaPcmChannelT * pcmChannel = cds_list_first_entry(&zone->sinks.list, AlsaPcmChannelT , list); const char *channel = pcmChannel->uid; AFB_ApiDebug(mixer->api, "%s: first channel uid is %s, zone has %d sinks", __func__, channel, zone->nbSinks); AlsaSndPcmT * sink = NULL; cds_list_for_each_entry(sink, &mixer->sinks.list, list) { AlsaPcmChannelT * pcmChannel; cds_list_for_each_entry(pcmChannel, &sink->channels.list, list) { if (pcmChannel->uid && !strcasecmp(channel, pcmChannel->uid)) { AFB_ApiDebug(mixer->api, "%s: found %s, return", __func__, pcmChannel->uid); return sink->sndcard->params; } } } fail: return NULL; } PUBLIC AlsaSndPcmT *ApiSinkGetByUid(SoftMixerT *mixer, const char *target) { // if no attached zone found, then try direct sink attachment AlsaSndPcmT * sink; cds_list_for_each_entry(sink, &mixer->sinks.list, list) { if (sink->uid && !strcasecmp(sink->uid, target)) return sink; } return NULL; } static void sinkDestroy(SoftMixerT* mixer, void * arg) { AlsaSndPcmT * sink = (AlsaSndPcmT*) arg; AFB_ApiDebug(mixer->api, "%s... %s", __func__, sink->uid); cds_list_del(&sink->list); mixer->nbSinks--; ApiPcmDelete(mixer, sink); } AlsaSndPcmT * sinkCreate(SoftMixerT * mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { AlsaPcmCtlT* dmixConfig = NULL; char *dmixUid = NULL; AlsaSndPcmT * newSink = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ); if (!newSink) { goto fail; } /* DMIX only supports REAL hardware */ if (!newSink->isPcmPlug) { // move from hardware to DMIX attach to sndcard if (asprintf(&dmixUid, "dmix-%s", newSink->uid) == -1) { SOFTMIXER_NOMEM(mixer->api); goto fail_pcm; } dmixConfig = AlsaCreateDmix(mixer, dmixUid, newSink, 0); if (!dmixConfig) { AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, newSink->uid); goto fail_dmix; } } mixer->nbSinks++; cds_list_add_tail(&newSink->list, &mixer->sinks.list); AlsaMixerTransactionObjectAdd(mixer->transaction, newSink, sinkDestroy); return newSink; fail: fail_pcm: // TODO delete PCM fail_dmix: free(dmixUid); return NULL; } PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { if (mixer->nbSinks >= mixer->max.sinks) { AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks); goto fail; } switch (json_object_get_type(argsJ)) { long count; case json_type_object: { AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, argsJ); if (!newSink) { AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(argsJ)); goto fail; } break; } case json_type_array: { count = json_object_array_length(argsJ); if (count > (mixer->max.sinks - mixer->nbSinks)) { AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks); goto fail; } for (int idx = 0; idx < count; idx++) { json_object *sinkJ = json_object_array_get_idx(argsJ, idx); AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, sinkJ); if (!newSink) { AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(sinkJ)); goto fail; } } break; } default: AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sinks invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ)); goto fail; } return 0; fail: return -1; }