From 94a12b24d1712573607b78cd78f47e998a0323d4 Mon Sep 17 00:00:00 2001 From: fulup Date: Sun, 30 Jul 2017 21:11:00 +0200 Subject: First Dev version with Volume Ramping --- ALSA-afb/Alsa-AddCtl.c | 138 ++++++++++++++++++++++++++++++++++++++++--------- ALSA-afb/Alsa-RegEvt.c | 47 ++++++++++------- ALSA-afb/Alsa-SetGet.c | 31 ++++++----- 3 files changed, 159 insertions(+), 57 deletions(-) (limited to 'ALSA-afb') diff --git a/ALSA-afb/Alsa-AddCtl.c b/ALSA-afb/Alsa-AddCtl.c index b7e3d90..7a68ab5 100644 --- a/ALSA-afb/Alsa-AddCtl.c +++ b/ALSA-afb/Alsa-AddCtl.c @@ -29,7 +29,7 @@ #include "Alsa-ApiHat.h" // Performs like a toggle switch for attenuation, because they're bool (ref:user-ctl-element-set.c) -static const unsigned int *allocate_bool_elem_set_tlv (void) { +static const unsigned int *allocate_bool_fake_tlv (void) { static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0); unsigned int *tlv= malloc(sizeof(range)); if (tlv == NULL) return NULL; @@ -37,8 +37,30 @@ static const unsigned int *allocate_bool_elem_set_tlv (void) { return tlv; } +static const unsigned int *allocate_int_dbscale_tlv (int min, int step, int mute) { + // SNDRV_CTL_TLVD_DECLARE_DB_SCALE(range, min, step, mute); + size_t tlvSize = sizeof(4*sizeof(int*)); + unsigned int *tlv= malloc(tlvSize); + tlv[0]=SNDRV_CTL_TLVT_DB_LINEAR; + tlv[1]=(int)tlvSize; + tlv[2]=min*100; + tlv[3] = ((step*100) & SNDRV_CTL_TLVD_DB_SCALE_MASK) | ((mute*100) ? SNDRV_CTL_TLVD_DB_SCALE_MUTE : 0); + return tlv; +} + +static const unsigned int *allocate_int_linear_tlv (int max, int min) { + // SNDRV_CTL_TLVD_DECLARE_DB_LINEAR (range, min, max); + size_t tlvSize = sizeof(4*sizeof(int*)); + unsigned int *tlv= malloc(tlvSize); + tlv[0]=SNDRV_CTL_TLVT_DB_SCALE; + tlv[1]=(int)tlvSize; + tlv[2]=-min*100; + tlv[3]=max*100; + return tlv; +} + STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_object *ctlJ, halQueryMode queryMode) { - int err, ctlNumid; + int err, ctlNumid, ctlValue; json_object *tmpJ; const char *ctlName; ctlRequestT ctlRequest; @@ -53,7 +75,7 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje json_object_object_get_ex (ctlJ, "name" , &tmpJ); ctlName = json_object_get_string(tmpJ); - json_object_object_get_ex (ctlJ, "numid" , &tmpJ); + json_object_object_get_ex (ctlJ, "ctl" , &tmpJ); ctlNumid = json_object_get_int(tmpJ); if (!ctlNumid && !ctlName) { @@ -70,17 +92,30 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje if (!err) { snd_ctl_elem_id_alloca(&elemId); snd_ctl_elem_info_get_id(elemInfo, elemId); - if (ctlNumid) goto OnSucessExit; // hardware control nothing todo + if (snd_ctl_elem_info_get_numid(elemInfo)) goto OnSucessExit; // hardware control nothing todo else { // user created kcontrol should be removable err = snd_ctl_elem_remove(ctlDev, elemId); - AFB_NOTICE ("ctlName=%s numid=%d fail to reset", snd_ctl_elem_info_get_name(elemInfo), snd_ctl_elem_info_get_numid(elemInfo)); - goto OnErrorExit; + if (err < 0) { + afb_req_fail_f (request, "ctl-reset-fail", "ctlName=%s numid=%d fail to reset", snd_ctl_elem_info_get_name(elemInfo), snd_ctl_elem_info_get_numid(elemInfo)); + goto OnErrorExit; + } else elemInfo=NULL; // we remove control elemInfo is not valid anymore } } + + // Control was deleted need to refresh elemInfo + if (!elemInfo) { + snd_ctl_elem_info_alloca(&elemInfo); + if (ctlName) snd_ctl_elem_info_set_name (elemInfo, ctlName); + snd_ctl_elem_info_set_interface (elemInfo, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_info(ctlDev, elemInfo); + } // default for json_object_get_int is zero json_object_object_get_ex (ctlJ, "min" , &tmpJ); ctlMin = json_object_get_int(tmpJ); + + json_object_object_get_ex (ctlJ, "value" , &tmpJ); + ctlValue = json_object_get_int(tmpJ); json_object_object_get_ex (ctlJ, "max" , &tmpJ); if (!tmpJ) ctlMax=1; @@ -91,7 +126,7 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje else ctlStep = json_object_get_int(tmpJ); json_object_object_get_ex (ctlJ, "count" , &tmpJ); - if (!tmpJ) ctlCount=2; + if (!tmpJ) ctlCount=1; else ctlCount = json_object_get_int(tmpJ); json_object_object_get_ex (ctlJ, "snddev" , &tmpJ); @@ -113,17 +148,17 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje switch (ctlType) { case SND_CTL_ELEM_TYPE_BOOLEAN: - err = snd_ctl_add_boolean_elem_set(ctlDev, elemInfo, 1, ctlCount); + err = snd_ctl_add_boolean_elem_set(ctlDev, elemInfo, ctlCount, ctlCount); if (err) { afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid boolean data", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); goto OnErrorExit; } - elemTlv = allocate_bool_elem_set_tlv(); + elemTlv = allocate_bool_fake_tlv(); // Provide FALSE as default value for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_boolean (elemValue, idx, 1); + snd_ctl_elem_value_set_boolean (elemValue, idx, ctlValue); } break; @@ -136,27 +171,79 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje // Provide 0 as default value for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_integer (elemValue, idx, 0); - } + snd_ctl_elem_value_set_integer (elemValue, idx, ctlValue); + } + + // Fulup needed to be tested with some dB expert !!! + json_object *dbscaleJ; + if (json_object_object_get_ex (ctlJ, "dbscale" , &dbscaleJ)) { + if (json_object_get_type(dbscaleJ) != json_type_object) { + afb_req_fail_f (request, "ctl-invalid-dbscale", "devid=%s crl=%s invalid json in integer control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; + + json_object_object_get_ex (ctlJ, "min" , &tmpJ); + int min = json_object_get_int(tmpJ); + if (min >= 0) { + afb_req_fail_f (request, "ctl-invalid-dbscale", "devid=%s crl=%s min should be a negative number", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; + } + + // default value 0=mute + json_object_object_get_ex (ctlJ, "max" , &tmpJ); + int max = json_object_get_int(tmpJ); + + // default value 1dB + json_object_object_get_ex (ctlJ, "step" , &tmpJ); + int step = json_object_get_int(tmpJ); + if (step <= 0) step=1; + + elemTlv = allocate_int_dbscale_tlv (min, max, step); + + } + } else { + // provide a fake linear TLV + elemTlv = allocate_int_linear_tlv (ctlMin, ctlMax); + } break; - case SND_CTL_ELEM_TYPE_INTEGER64: - err = snd_ctl_add_integer64_elem_set (ctlDev, elemInfo, 1, ctlCount, ctlMin, ctlMax, ctlStep); + + case SND_CTL_ELEM_TYPE_ENUMERATED: { + json_object *enumsJ; + json_object_object_get_ex (ctlJ, "enums" , &enumsJ); + if (json_object_get_type(enumsJ) != json_type_array) { + afb_req_fail_f (request, "ctl-missing-enums", "devid=%s crl=%s mandatory enum=xxx missing in enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + goto OnErrorExit; + } + + int length = json_object_array_length(enumsJ); + const char **enumlabels = malloc(length*sizeof(char*)); + + for (int jdx=0; jdx < length; jdx++) { + tmpJ = json_object_array_get_idx(enumsJ, jdx); + enumlabels[jdx] = json_object_get_string(tmpJ); + } + + err = snd_ctl_add_enumerated_elem_set (ctlDev, elemInfo, 1, ctlCount, length, enumlabels); if (err) { - afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid boolean data", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); + afb_req_fail_f (request, "ctl-invalid-bool", "devid=%s crl=%s invalid enumerated control", snd_ctl_name(ctlDev), json_object_get_string(ctlJ)); goto OnErrorExit; } - + // Provide 0 as default value for (int idx=0; idx < ctlCount; idx ++) { - snd_ctl_elem_value_set_integer64 (elemValue, idx, 0); + snd_ctl_elem_value_set_enumerated (elemValue, idx, ctlValue); } - break; + + elemTlv = allocate_bool_fake_tlv(); - case SND_CTL_ELEM_TYPE_ENUMERATED: + break; + } + + // Long Term Waiting ToDoList + case SND_CTL_ELEM_TYPE_INTEGER64: case SND_CTL_ELEM_TYPE_BYTES: default: - afb_req_fail_f (request, "ctl-invalid-type", "crl=%s invalid/unknown type", json_object_get_string(ctlJ)); + afb_req_fail_f (request, "ctl-invalid-type", "crl=%s unsupported/unknown element type", json_object_get_string(ctlJ)); goto OnErrorExit; } @@ -177,7 +264,7 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje afb_req_fail_f (request, "TLV-write-fail", "crl=%s numid=%d fail to write data error=%s", json_object_get_string(ctlJ), snd_ctl_elem_info_get_numid(elemInfo), snd_strerror(err)); goto OnErrorExit; } - } + } // return newly created as a JSON object OnSucessExit: @@ -225,9 +312,9 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { switch (json_object_get_type(ctlsJ)) { case json_type_object: - ctlsValues= addOneSndCtl(request, ctlDev, ctlsJ, queryMode); - - break; + ctlsValues= addOneSndCtl(request, ctlDev, ctlsJ, queryMode); + if (!ctlsValues) goto OnErrorExit; + break; case json_type_array: ctlsValues= json_object_new_array(); @@ -235,6 +322,7 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { json_object *ctlJ = json_object_array_get_idx (ctlsJ, idx); ctlValues= addOneSndCtl(request, ctlDev, ctlJ, queryMode) ; if (ctlValues) json_object_array_add (ctlsValues, ctlValues); + else goto OnErrorExit; } break; @@ -247,6 +335,6 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { afb_req_success(request, ctlsValues, NULL); OnErrorExit: - if (ctlDev) snd_ctl_close(ctlDev); + if (ctlDev) snd_ctl_close(ctlDev); return; } \ No newline at end of file diff --git a/ALSA-afb/Alsa-RegEvt.c b/ALSA-afb/Alsa-RegEvt.c index df6570a..add3944 100644 --- a/ALSA-afb/Alsa-RegEvt.c +++ b/ALSA-afb/Alsa-RegEvt.c @@ -71,7 +71,7 @@ OnErrorExit: // This routine is called when ALSA event are fired STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* userData) { - int err; + int err, ctlNumid; evtHandleT *evtHandle = (evtHandleT*)userData; snd_ctl_event_t *eventId; json_object *ctlEventJ; @@ -79,12 +79,12 @@ STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* int iface; int device; int subdev; - const char*devname; + const char*ctlName; ctlRequestT ctlRequest; snd_ctl_elem_id_t *elemId; if ((revents & EPOLLHUP) != 0) { - AFB_NOTICE( "SndCtl hanghup [car disconnected]"); + AFB_NOTICE("SndCtl hanghup [car disconnected]"); goto ExitOnSucess; } @@ -108,21 +108,30 @@ STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* err = alsaGetSingleCtl (evtHandle->ctlDev, elemId, &ctlRequest, evtHandle->mode); if (err) goto OnErrorExit; + + // If CTL as a value use it as container for response + if (ctlRequest.valuesJ) ctlEventJ= ctlRequest.valuesJ; + else { + ctlEventJ = json_object_new_object(); + ctlNumid = snd_ctl_event_elem_get_numid(eventId); + json_object_object_add(ctlEventJ, "id",json_object_new_int (ctlNumid)); + } - iface = snd_ctl_event_elem_get_interface(eventId); - device = snd_ctl_event_elem_get_device(eventId); - subdev = snd_ctl_event_elem_get_subdevice(eventId); - devname= snd_ctl_event_elem_get_name(eventId); - - // proxy ctlevent as a binder event - ctlEventJ = json_object_new_object(); - json_object_object_add(ctlEventJ, "device" ,json_object_new_int (device)); - json_object_object_add(ctlEventJ, "subdev" ,json_object_new_int (subdev)); - if (evtHandle->mode < 2) { - json_object_object_add(ctlEventJ, "iface" ,json_object_new_int (iface)); - json_object_object_add(ctlEventJ, "devname",json_object_new_string (devname)); + if (evtHandle->mode >= QUERY_COMPACT) { + ctlName = snd_ctl_event_elem_get_name(eventId); + json_object_object_add(ctlEventJ, "name",json_object_new_string (ctlName)); + } + + if (evtHandle->mode >= QUERY_VERBOSE) { + iface = snd_ctl_event_elem_get_interface(eventId); + device = snd_ctl_event_elem_get_device(eventId); + subdev = snd_ctl_event_elem_get_subdevice(eventId); + json_object_object_add(ctlEventJ, "ifc" ,json_object_new_int (iface)); + json_object_object_add(ctlEventJ, "dev" ,json_object_new_int (device)); + json_object_object_add(ctlEventJ, "sub" ,json_object_new_int (subdev)); } - if (ctlRequest.valuesJ) (json_object_object_add(ctlEventJ, "value" ,ctlRequest.valuesJ)); + + AFB_DEBUG( "sndCtlEventCB=%s", json_object_get_string(ctlEventJ)); afb_event_push(evtHandle->afbevt, ctlEventJ); } @@ -234,7 +243,7 @@ PUBLIC void alsaEvtSubcribe (afb_req request) { // Subscribe to every Alsa CtlEvent send by a given board STATIC json_object *alsaProbeCardId (afb_req request) { char devid [10]; - const char *devname, *shortname, *longname; + const char *ctlName, *shortname, *longname; int card, err, index, idx; json_object *responseJ; snd_ctl_t *ctlDev; @@ -260,12 +269,12 @@ STATIC json_object *alsaProbeCardId (afb_req request) { // extract sound card information snd_ctl_card_info(ctlDev, cardinfo); index = snd_ctl_card_info_get_card(cardinfo); - devname = snd_ctl_card_info_get_id(cardinfo); + ctlName = snd_ctl_card_info_get_id(cardinfo); shortname= snd_ctl_card_info_get_name(cardinfo); longname = snd_ctl_card_info_get_longname(cardinfo); // check if short|long name match - if (!strcmp (sndname, devname)) break; + if (!strcmp (sndname, ctlName)) break; if (!strcmp (sndname, shortname)) break; if (!strcmp (sndname, longname)) break; } diff --git a/ALSA-afb/Alsa-SetGet.c b/ALSA-afb/Alsa-SetGet.c index 50b62ce..5a41ec1 100644 --- a/ALSA-afb/Alsa-SetGet.c +++ b/ALSA-afb/Alsa-SetGet.c @@ -618,7 +618,7 @@ PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRe if (snd_ctl_elem_info_is_tlv_readable(elemInfo)) { unsigned int *tlv = alloca(TLV_BYTE_SIZE); if ((err = snd_ctl_elem_tlv_read(ctlDev, elemId, tlv, 4096)) < 0) { - fprintf (stderr, "Control %s element TLV read error\n", snd_strerror(err)); + AFB_NOTICE ("Control numid=%d err=%s element TLV read error\n", numid, snd_strerror(err)); goto OnErrorExit; } else { json_object_object_add (ctlRequest->valuesJ,"tlv", decodeTlv (tlv, TLV_BYTE_SIZE, queryMode)); @@ -642,15 +642,14 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { unsigned int ctlCount; snd_ctl_t *ctlDev; snd_ctl_elem_list_t *ctlList; - json_object *sndctls=json_object_new_array();; queryValuesT queryValues; - json_object *queryJ, *numidsJ; + json_object *queryJ, *numidsJ, *sndctls; queryJ = alsaCheckQuery (request, &queryValues); if (!queryJ) goto OnErrorExit; // Prase Numids + optional values - done= json_object_object_get_ex (queryJ, "numid" , &numidsJ); + done= json_object_object_get_ex (queryJ, "ctl" , &numidsJ); if (!done) queryValues.count=0; else { enum json_type jtype= json_object_get_type(numidsJ); @@ -701,6 +700,10 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { ctlRequest= alloca (sizeof(ctlRequestT)*(queryValues.count)); NumidsListParse (action, &queryValues, ctlRequest); } + + // if more than one crl requested prepare an array for response + if (queryValues.count!= 1 && action==ACTION_GET) sndctls=json_object_new_array(); + else sndctls=NULL; // Loop on all ctlDev controls for (int ctlIndex=0; ctlIndex < ctlCount; ctlIndex++) { @@ -745,7 +748,11 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { } if (err) status++; else { - if (action == ACTION_GET) json_object_array_add (sndctls, ctlRequest[jdx].valuesJ); + // Do not embed response in an array when only one ctl was requested + if (action == ACTION_GET) { + if (queryValues.count > 1) json_object_array_add (sndctls, ctlRequest[jdx].valuesJ); + else sndctls = ctlRequest[jdx].valuesJ; + } } } } @@ -755,16 +762,14 @@ STATIC void alsaSetGetCtls (ActionSetGetT action, afb_req request) { for (int jdx=0; jdx < queryValues.count; jdx++) { if (ctlRequest[jdx].used <= 0) { json_object *failctl = json_object_new_object(); - json_object_object_add (failctl, "numid", ctlRequest[jdx].jToken); - if (ctlRequest[jdx].valuesJ) json_object_object_add(failctl, "value", ctlRequest[jdx].valuesJ); - - AFB_NOTICE ("*** jToken=%s value=%s", json_object_get_string(ctlRequest[jdx].jToken), json_object_get_string(ctlRequest[jdx].valuesJ)); - - if (ctlRequest[jdx].numId == -1) json_object_object_add (failctl, "error", json_object_new_string ("Numid Invalid")); + if (ctlRequest[jdx].numId == -1) json_object_object_add (failctl, "warning", json_object_new_string ("Numid Invalid")); else { - if (ctlRequest[jdx].used == 0) json_object_object_add (failctl, "error", json_object_new_string ("Numid Does Not Exist")); - if (ctlRequest[jdx].used == -1) json_object_object_add (failctl, "error", json_object_new_string ("Value invalid")); + if (ctlRequest[jdx].used == 0) json_object_object_add (failctl, "warning", json_object_new_string ("Numid Does Not Exist")); + if (ctlRequest[jdx].used == -1) json_object_object_add (failctl, "warning", json_object_new_string ("Value Refused")); } + + json_object_object_add (failctl, "ctl", ctlRequest[jdx].jToken); + json_object_array_add (warningsJ, failctl); } /* WARNING!!!! Check with Jose why following put free valuesJ -- cgit 1.2.3-korg