From a7a6ae4be87ee9178905ab784d84a4c0a8303f9b Mon Sep 17 00:00:00 2001 From: fulup Date: Sun, 23 Jul 2017 01:23:37 +0200 Subject: Added TLV->DBscale into HAL control 1st Working version of HAL ctl-get with TLV Cleanup HTML5 test pages WIP Only --- .vscode/c_cpp_properties.json | 65 ++++++++++++ ALSA-afb/Alsa-AddCtl.c | 33 ++++-- ALSA-afb/Alsa-ApiHat.c | 4 +- ALSA-afb/Alsa-ApiHat.h | 4 +- ALSA-afb/Alsa-RegEvt.c | 8 +- ALSA-afb/Alsa-SetGet.c | 189 +++++++++++++++++++-------------- ALSA-afb/Alsa-Ucm.c | 2 +- ALSA-afb/README.md | 4 +- HAL-afb/HAL-interface/CMakeLists.txt | 2 +- HAL-afb/HAL-interface/hal-interface.c | 194 ++++++++++++++++------------------ HAL-afb/HAL-interface/hal-interface.h | 17 ++- HAL-afb/HAL-interface/hal-volmap.c | 96 +++++++++++++++++ HAL-afb/HAL-plugin/HalPlugCtl.c | 2 +- README.md | 146 +++++++++++++++++++++++++ Shared-Interface/audio-interface.h | 7 ++ conf.d/cmake/config.cmake | 3 +- htdocs/AFB-websock.js | 7 +- htdocs/AudioBinding.css | 7 ++ htdocs/AudioBinding.js | 74 +++++++++---- htdocs/alsa-core.html | 68 ++++++------ htdocs/alsa-hal.html | 31 ++++-- htdocs/audio-logic.html | 26 +---- nbproject/configurations.xml | 109 ++++++++++++------- 23 files changed, 765 insertions(+), 333 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 HAL-afb/HAL-interface/hal-volmap.c create mode 100644 README.md create mode 100644 htdocs/AudioBinding.css diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..cd13c94 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,65 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Linux", + "includePath": [ + "/usr/include/c++/5", + "/usr/local/include", + "/usr/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include/c++/5", + "/usr/local/include", + "/usr/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", + "${workspaceRoot}" + ], + "defines": [ + "_DEBUG", + "UNICODE" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 2 +} \ No newline at end of file diff --git a/ALSA-afb/Alsa-AddCtl.c b/ALSA-afb/Alsa-AddCtl.c index 25e2a31..4635503 100644 --- a/ALSA-afb/Alsa-AddCtl.c +++ b/ALSA-afb/Alsa-AddCtl.c @@ -37,11 +37,11 @@ static const unsigned int *allocate_bool_elem_set_tlv (void) { return tlv; } -STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_object *ctlJ) { +STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_object *ctlJ, halQueryMode queryMode) { int err, ctlNumid; json_object *tmpJ; - ctlRequestT ctlRequest; const char *ctlName; + ctlRequestT ctlRequest; int ctlMax, ctlMin, ctlStep, ctlCount, ctlSubDev, ctlSndDev; snd_ctl_elem_type_t ctlType; snd_ctl_elem_info_t *elemInfo; @@ -68,10 +68,14 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje snd_ctl_elem_info_set_interface (elemInfo, SND_CTL_ELEM_IFACE_MIXER); err = snd_ctl_elem_info(ctlDev, elemInfo); if (!err) { - AFB_NOTICE ("ctlName=%s numid=%d already exit", snd_ctl_elem_info_get_name(elemInfo), snd_ctl_elem_info_get_numid(elemInfo)); snd_ctl_elem_id_alloca(&elemId); - snd_ctl_elem_info_get_id(elemInfo, elemId); - goto OnSucessExit; + snd_ctl_elem_info_get_id(elemInfo, elemId); + if (ctlNumid) 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; + } } // default for json_object_get_int is zero @@ -177,7 +181,7 @@ STATIC json_object * addOneSndCtl(afb_req request, snd_ctl_t *ctlDev, json_obje // return newly created as a JSON object OnSucessExit: - alsaGetSingleCtl (ctlDev, elemId, &ctlRequest, 0); + alsaGetSingleCtl (ctlDev, elemId, &ctlRequest, queryMode); if (ctlRequest.used < 0) goto OnErrorExit; return ctlRequest.jValues; @@ -190,7 +194,7 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { json_object *ctlsJ, *ctlsValues, *ctlValues; enum json_type; snd_ctl_t *ctlDev=NULL; - const char *devid; + const char *devid, *mode; devid = afb_req_value(request, "devid"); if (devid == NULL) { @@ -204,9 +208,16 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { afb_req_fail_f (request, "devid-unknown", "SndCard devid=[%s] Not Found err=%s", devid, snd_strerror(err)); goto OnErrorExit; } - + + // get verbosity level + halQueryMode queryMode = QUERY_QUIET; + mode = afb_req_value(request, "mode"); + if (mode != NULL) { + sscanf(mode,"%i", (int*)&queryMode); + } + // extract sound controls and parse json - ctlsJ = json_tokener_parse (afb_req_value(request, "ctls")); + ctlsJ = json_tokener_parse (afb_req_value(request, "ctl")); if (!ctlsJ) { afb_req_fail_f (request, "ctls-missing", "ctls MUST be defined as a JSON array for alsaAddCustomCtls"); goto OnErrorExit; @@ -214,7 +225,7 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { switch (json_object_get_type(ctlsJ)) { case json_type_object: - ctlsValues= addOneSndCtl(request, ctlDev, ctlsJ); + ctlsValues= addOneSndCtl(request, ctlDev, ctlsJ, queryMode); break; @@ -222,7 +233,7 @@ PUBLIC void alsaAddCustomCtls(afb_req request) { ctlsValues= json_object_new_array(); for (int idx= 0; idx < json_object_array_length (ctlsJ); idx++) { json_object *ctlJ = json_object_array_get_idx (ctlsJ, idx); - ctlValues= addOneSndCtl(request, ctlDev, ctlJ) ; + ctlValues= addOneSndCtl(request, ctlDev, ctlJ, queryMode) ; if (ctlValues) json_object_array_add (ctlsValues, ctlValues); } break; diff --git a/ALSA-afb/Alsa-ApiHat.c b/ALSA-afb/Alsa-ApiHat.c index 5256fb4..9797d3c 100644 --- a/ALSA-afb/Alsa-ApiHat.c +++ b/ALSA-afb/Alsa-ApiHat.c @@ -35,8 +35,8 @@ static const struct afb_verb_v2 api_verbs[] = { /* VERB'S NAME FUNCTION TO CALL */ { .verb= "ping" , .callback= pingtest }, { .verb= "getinfo", .callback= alsaGetInfo}, - { .verb= "getctls", .callback= alsaGetCtls}, - { .verb= "setctls", .callback= alsaSetCtls}, + { .verb= "getctl", .callback= alsaGetCtls}, + { .verb= "setctl", .callback= alsaSetCtls}, { .verb= "subscribe", .callback= alsaEvtSubcribe}, { .verb= "getcardid", .callback= alsaGetCardId}, { .verb= "halregister", .callback= alsaRegisterHal}, diff --git a/ALSA-afb/Alsa-ApiHat.h b/ALSA-afb/Alsa-ApiHat.h index 46f18de..6c9f4c3 100644 --- a/ALSA-afb/Alsa-ApiHat.h +++ b/ALSA-afb/Alsa-ApiHat.h @@ -33,7 +33,7 @@ typedef enum { typedef struct { const char *devid; json_object *jNumIds; - int quiet; + halQueryMode mode; int count; } queryValuesT; @@ -50,7 +50,7 @@ extern const struct afb_binding_interface *afbIface; PUBLIC int alsaCheckQuery (struct afb_req request, queryValuesT *queryValues); // AlseCoreSetGet exports -PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, int quiet); +PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, halQueryMode queryMode); PUBLIC void alsaGetInfo (struct afb_req request); PUBLIC void alsaGetCtls(struct afb_req request); PUBLIC void alsaSetCtls(struct afb_req request); diff --git a/ALSA-afb/Alsa-RegEvt.c b/ALSA-afb/Alsa-RegEvt.c index 6344e2d..7b0a722 100644 --- a/ALSA-afb/Alsa-RegEvt.c +++ b/ALSA-afb/Alsa-RegEvt.c @@ -25,7 +25,7 @@ typedef struct { struct pollfd pfds; sd_event_source *src; snd_ctl_t *ctlDev; - int quiet; + int mode; struct afb_event afbevt; } evtHandleT; @@ -81,7 +81,7 @@ STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* snd_ctl_event_elem_get_id (eventId, elemId); - err = alsaGetSingleCtl (evtHandle->ctlDev, elemId, &ctlRequest, evtHandle->quiet); + err = alsaGetSingleCtl (evtHandle->ctlDev, elemId, &ctlRequest, evtHandle->mode); if (err) goto OnErrorExit; iface = snd_ctl_event_elem_get_interface(eventId); @@ -93,7 +93,7 @@ STATIC int sndCtlEventCB (sd_event_source* src, int fd, uint32_t revents, void* 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->quiet < 2) { + 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)); } @@ -158,7 +158,7 @@ PUBLIC void alsaEvtSubcribe (afb_req request) { evtHandle = malloc (sizeof(evtHandleT)); evtHandle->ctlDev = ctlDev; - evtHandle->quiet = queryValues.quiet; + evtHandle->mode = queryValues.mode; sndHandles[idxFree].ucount = 0; sndHandles[idxFree].cardId = cardId; sndHandles[idxFree].evtHandle = evtHandle; diff --git a/ALSA-afb/Alsa-SetGet.c b/ALSA-afb/Alsa-SetGet.c index 31406f3..0815f35 100644 --- a/ALSA-afb/Alsa-SetGet.c +++ b/ALSA-afb/Alsa-SetGet.c @@ -90,17 +90,17 @@ PUBLIC int alsaCheckQuery (afb_req request, queryValuesT *queryValues) { const char *numids; json_object *jNumIds; - const char *rqtQuiet = afb_req_value(request, "quiet"); - if (!rqtQuiet) queryValues->quiet=99; // default super quiet - else if (rqtQuiet && ! sscanf (rqtQuiet, "%d", &queryValues->quiet)) { + const char *rqtmode = afb_req_value(request, "mode"); + if (!rqtmode) queryValues->mode=QUERY_QUIET; // default quiet + else if (rqtmode && ! sscanf (rqtmode, "%d", (int)&queryValues->mode)) { json_object *query = afb_req_json(request); - afb_req_fail_f (request, "quiet-notinteger","Query=%s Quiet not integer &quiet=%s&", json_object_get_string(query), rqtQuiet); + afb_req_fail_f (request, "mode-notinteger","Query=%s mode not integer &mode=%s&", json_object_get_string(query), rqtmode); goto OnErrorExit; }; // no NumId is interpreted as ALL for get and error for set - numids = afb_req_value(request, "numids"); + numids = afb_req_value(request, "numid"); if (numids == NULL) { queryValues->count=0; goto OnExit; @@ -153,7 +153,7 @@ STATIC json_object *DB2StringJsonOject (long dB) { // Direct port from amixer TLV decode routine. This code is too complex for me. // I hopefully did not break it when porting it. -STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { +STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode) { char label[20]; unsigned int type = tlv[0]; unsigned int size; @@ -187,7 +187,7 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { fprintf(stderr, "TLV size error in compound!\n"); return NULL; } - embedJson = decodeTlv(tlv + idx, tlv[idx + 1] + 8); + embedJson = decodeTlv(tlv + idx, tlv[idx + 1] + 8, mode); json_object_array_add(containerJson, embedJson); idx += (unsigned int) (2 + (tlv[idx + 1] + sizeof (unsigned int) - 1) / sizeof (unsigned int)); } @@ -202,15 +202,25 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { if (size != 2 * sizeof (unsigned int)) { json_object * arrayJson = json_object_new_array(); while (size > 0) { - snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); - json_object_array_add(arrayJson, json_object_new_string(label)); + if (mode >= QUERY_VERBOSE) { + snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); + json_object_array_add(arrayJson, json_object_new_string(label)); + } else { + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbscaleJson, "array", arrayJson); } else { - json_object_object_add(dbscaleJson, "min", DB2StringJsonOject((int) tlv[2])); - json_object_object_add(dbscaleJson, "step", DB2StringJsonOject(tlv[3] & 0xffff)); - json_object_object_add(dbscaleJson, "mute", DB2StringJsonOject((tlv[3] >> 16) & 1)); + if (mode >= QUERY_VERBOSE) { + json_object_object_add(dbscaleJson, "min", DB2StringJsonOject((int) tlv[2])); + json_object_object_add(dbscaleJson, "step", DB2StringJsonOject(tlv[3] & 0xffff)); + json_object_object_add(dbscaleJson, "mute", DB2StringJsonOject((tlv[3] >> 16) & 1)); + } else { + json_object_object_add(dbscaleJson, "min", json_object_new_int((int) tlv[2])); + json_object_object_add(dbscaleJson, "step", json_object_new_int(tlv[3] & 0xffff)); + json_object_object_add(dbscaleJson, "mute", json_object_new_int((tlv[3] >> 16) & 1)); + } } json_object_object_add(decodeTlvJson, "dbscale", dbscaleJson); break; @@ -224,14 +234,23 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { if (size != 2 * sizeof (unsigned int)) { json_object * arrayJson = json_object_new_array(); while (size > 0) { - snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); - json_object_array_add(arrayJson, json_object_new_string(label)); + if (mode >= QUERY_VERBOSE) { + snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); + json_object_array_add(arrayJson, json_object_new_string(label)); + } else { + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbLinearJson, "offset", arrayJson); } else { - json_object_object_add(dbLinearJson, "min", DB2StringJsonOject((int) tlv[2])); - json_object_object_add(dbLinearJson, "max", DB2StringJsonOject((int) tlv[3])); + if (mode >= QUERY_VERBOSE) { + json_object_object_add(dbLinearJson, "min", DB2StringJsonOject((int) tlv[2])); + json_object_object_add(dbLinearJson, "max", DB2StringJsonOject((int) tlv[3])); + } else { + json_object_object_add(dbLinearJson, "min", json_object_new_int((int) tlv[2])); + json_object_object_add(dbLinearJson, "max", json_object_new_int((int) tlv[3])); + } } json_object_object_add(decodeTlvJson, "dblinear", dbLinearJson); break; @@ -246,8 +265,12 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { if ((size % (6 * sizeof (unsigned int))) != 0) { json_object *arrayJson = json_object_new_array(); while (size > 0) { - snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); - json_object_array_add(arrayJson, json_object_new_string(label)); + if (mode >= QUERY_VERBOSE) { + snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); + json_object_array_add(arrayJson, json_object_new_string(label)); + } else { + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbRangeJson, "dbrange", arrayJson); @@ -255,11 +278,9 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { } while (size > 0) { json_object * embedJson = json_object_new_object(); - snprintf(label, sizeof (label), "%i,", tlv[idx++]); - json_object_object_add(embedJson, "rangemin", json_object_new_string(label)); - snprintf(label, sizeof (label), "%i", tlv[idx++]); - json_object_object_add(embedJson, "rangemax", json_object_new_string(label)); - embedJson = decodeTlv(tlv + idx, 4 * sizeof (unsigned int)); + json_object_object_add(embedJson, "rangemin", json_object_new_int(tlv[idx++])); + json_object_object_add(embedJson, "rangemax", json_object_new_int(tlv[idx++])); + embedJson = decodeTlv(tlv + idx, 4 * sizeof (unsigned int), mode); json_object_object_add(embedJson, "tlv", embedJson); idx += 4; size -= (unsigned int) (6 * sizeof (unsigned int)); @@ -278,15 +299,24 @@ STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size) { if (size != 2 * sizeof (unsigned int)) { json_object * arrayJson = json_object_new_array(); while (size > 0) { - snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); - json_object_array_add(arrayJson, json_object_new_string(label)); + if (mode >= QUERY_VERBOSE) { + snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); + json_object_array_add(arrayJson, json_object_new_string(label)); + } else { + json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); + } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbMinMaxJson, "array", arrayJson); } else { - json_object_object_add(dbMinMaxJson, "min", DB2StringJsonOject((int) tlv[2])); - json_object_object_add(dbMinMaxJson, "max", DB2StringJsonOject((int) tlv[3])); + if (mode >= QUERY_VERBOSE) { + json_object_object_add(dbMinMaxJson, "min", DB2StringJsonOject((int) tlv[2])); + json_object_object_add(dbMinMaxJson, "max", DB2StringJsonOject((int) tlv[3])); + } else { + json_object_object_add(dbMinMaxJson, "min", json_object_new_int((int) tlv[2])); + json_object_object_add(dbMinMaxJson, "max", json_object_new_int((int) tlv[3])); + } } if (type == SND_CTL_TLVT_DB_MINMAX_MUTE) { @@ -350,7 +380,7 @@ STATIC json_object* alsaCardProbe (const char *rqtSndId) { int err; if ((err = snd_ctl_open(&handle, rqtSndId, 0)) < 0) { - AFB_INFO ("SndCard [%s] Not Found", rqtSndId); + AFB_INFO ("alsaCardProbe [%s] Not Found", rqtSndId); return NULL; } @@ -522,7 +552,7 @@ PUBLIC int alsaSetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRe } // process ALSA control and store then into ctlRequest -PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, int quiet) { +PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRequestT *ctlRequest, halQueryMode queryMode) { snd_ctl_elem_type_t elemType; snd_ctl_elem_value_t *elemData; snd_ctl_elem_info_t *elemInfo; @@ -548,9 +578,9 @@ PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRe ctlRequest->jValues= json_object_new_object(); json_object_object_add (ctlRequest->jValues,"numid" , json_object_new_int(numid)); - if (quiet < 2) json_object_object_add (ctlRequest->jValues,"name" , json_object_new_string(snd_ctl_elem_id_get_name (elemId))); - if (quiet < 1) json_object_object_add (ctlRequest->jValues,"iface" , json_object_new_string(snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(elemId)))); - if (quiet < 3) json_object_object_add (ctlRequest->jValues,"actif", json_object_new_boolean(!snd_ctl_elem_info_is_inactive(elemInfo))); + if (queryMode >= 1) json_object_object_add (ctlRequest->jValues,"name" , json_object_new_string(snd_ctl_elem_id_get_name (elemId))); + if (queryMode >= 2) json_object_object_add (ctlRequest->jValues,"iface" , json_object_new_string(snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(elemId)))); + if (queryMode >= 3) json_object_object_add (ctlRequest->jValues,"actif", json_object_new_boolean(!snd_ctl_elem_info_is_inactive(elemInfo))); json_object *jsonValuesCtl = json_object_new_array(); for (idx = 0; idx < count; idx++) { // start from one in amixer.c !!! @@ -590,54 +620,55 @@ PUBLIC int alsaGetSingleCtl (snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, ctlRe } json_object_object_add (ctlRequest->jValues,"val",jsonValuesCtl); - if (!quiet) { // in simple mode do not print usable values + if (queryMode >= 1) { // in simple mode do not print usable values json_object *jsonClassCtl = json_object_new_object(); json_object_object_add (jsonClassCtl,"type" , json_object_new_int(elemType)); - json_object_object_add (jsonClassCtl,"count", json_object_new_int(count)); - - switch (elemType) { - case SND_CTL_ELEM_TYPE_INTEGER: - json_object_object_add (jsonClassCtl,"min", json_object_new_int((int)snd_ctl_elem_info_get_min(elemInfo))); - json_object_object_add (jsonClassCtl,"max", json_object_new_int((int)snd_ctl_elem_info_get_max(elemInfo))); - json_object_object_add (jsonClassCtl,"step", json_object_new_int((int)snd_ctl_elem_info_get_step(elemInfo))); - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - json_object_object_add (jsonClassCtl,"min", json_object_new_int64(snd_ctl_elem_info_get_min64(elemInfo))); - json_object_object_add (jsonClassCtl,"max", json_object_new_int64(snd_ctl_elem_info_get_max64(elemInfo))); - json_object_object_add (jsonClassCtl,"step", json_object_new_int64(snd_ctl_elem_info_get_step64(elemInfo))); - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: { - unsigned int item, items = snd_ctl_elem_info_get_items(elemInfo); - json_object *jsonEnum = json_object_new_array(); - - for (item = 0; item < items; item++) { - snd_ctl_elem_info_set_item(elemInfo, item); - if ((err = snd_ctl_elem_info(ctlDev, elemInfo)) >= 0) { - json_object_array_add (jsonEnum, json_object_new_string(snd_ctl_elem_info_get_item_name(elemInfo))); - } - } - json_object_object_add (jsonClassCtl, "enums",jsonEnum); - break; - } - default: break; // ignore any unknown type - } - - // add collected class info with associated ACLs - json_object_object_add (ctlRequest->jValues,"ctrl", jsonClassCtl); - json_object_object_add (ctlRequest->jValues,"acl" , getControlAcl (elemInfo)); - - // check for tlv [direct port from amixer.c] - if (snd_ctl_elem_info_is_tlv_readable(elemInfo)) { - unsigned int *tlv; - tlv = malloc(4096); - if ((err = snd_ctl_elem_info(ctlDev, elemInfo)) < 0) { - fprintf (stderr, "Control %s element TLV read error\n", snd_strerror(err)); - free(tlv); - } else { - json_object_object_add (ctlRequest->jValues,"tlv", decodeTlv (tlv, 4096)); - } - } + json_object_object_add (jsonClassCtl,"count", json_object_new_int(count)); + + switch (elemType) { + case SND_CTL_ELEM_TYPE_INTEGER: + json_object_object_add (jsonClassCtl,"min", json_object_new_int((int)snd_ctl_elem_info_get_min(elemInfo))); + json_object_object_add (jsonClassCtl,"max", json_object_new_int((int)snd_ctl_elem_info_get_max(elemInfo))); + json_object_object_add (jsonClassCtl,"step", json_object_new_int((int)snd_ctl_elem_info_get_step(elemInfo))); + break; + case SND_CTL_ELEM_TYPE_INTEGER64: + json_object_object_add (jsonClassCtl,"min", json_object_new_int64(snd_ctl_elem_info_get_min64(elemInfo))); + json_object_object_add (jsonClassCtl,"max", json_object_new_int64(snd_ctl_elem_info_get_max64(elemInfo))); + json_object_object_add (jsonClassCtl,"step", json_object_new_int64(snd_ctl_elem_info_get_step64(elemInfo))); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: { + unsigned int item, items = snd_ctl_elem_info_get_items(elemInfo); + json_object *jsonEnum = json_object_new_array(); + + for (item = 0; item < items; item++) { + snd_ctl_elem_info_set_item(elemInfo, item); + if ((err = snd_ctl_elem_info(ctlDev, elemInfo)) >= 0) { + json_object_array_add (jsonEnum, json_object_new_string(snd_ctl_elem_info_get_item_name(elemInfo))); + } + } + json_object_object_add (jsonClassCtl, "enums",jsonEnum); + break; + } + default: break; // ignore any unknown type + } + + // add collected class info with associated ACLs + json_object_object_add (ctlRequest->jValues,"ctl", jsonClassCtl); + + if (queryMode >= QUERY_FULL) json_object_object_add (ctlRequest->jValues,"acl" , getControlAcl (elemInfo)); + + // check for tlv [direct port from amixer.c] + if (snd_ctl_elem_info_is_tlv_readable(elemInfo)) { + unsigned int *tlv = alloca(4096); + if ((err = snd_ctl_elem_tlv_read(ctlDev, elemId, tlv, 4096)) < 0) { + fprintf (stderr, "Control %s element TLV read error\n", snd_strerror(err)); + goto OnErrorExit; + } else { + json_object_object_add (ctlRequest->jValues,"tlv", decodeTlv (tlv, 4096, queryMode)); + } + } } + ctlRequest->used=1; return 0; @@ -722,7 +753,7 @@ STATIC void alsaSetGetCtls (afb_req request, ActionSetGetT action) { snd_ctl_elem_list_get_id (ctlList, ctlIndex, elemId); switch (action) { case ACTION_GET: - err = alsaGetSingleCtl (ctlDev, elemId, &ctlRequest[jdx], queryValues.quiet); + err = alsaGetSingleCtl (ctlDev, elemId, &ctlRequest[jdx], queryValues.mode); break; case ACTION_SET: @@ -749,7 +780,7 @@ STATIC void alsaSetGetCtls (afb_req request, ActionSetGetT action) { if (ctlRequest[jdx].numId == -1) json_object_object_add (failctl, "info", json_object_new_string ("Invalid NumID")); else { - if (ctlRequest[jdx].used == 0) json_object_object_add (failctl, "info", json_object_new_string ("Does Not Exist")); + if (ctlRequest[jdx].used == 0) json_object_object_add (failctl, "info", json_object_new_string ("Does Not Exist")); if (ctlRequest[jdx].used == -1) json_object_object_add (failctl, "info", json_object_new_string ("Invalid Value")); } json_object_array_add (warnings, failctl); diff --git a/ALSA-afb/Alsa-Ucm.c b/ALSA-afb/Alsa-Ucm.c index b2039be..f512417 100644 --- a/ALSA-afb/Alsa-Ucm.c +++ b/ALSA-afb/Alsa-Ucm.c @@ -364,7 +364,7 @@ PUBLIC void alsaUseCaseSet(struct afb_req request) { // label are requested transfert request to get if (afb_req_value(request, "values")) return alsaUseCaseGet(request); - if (queryValues.quiet <= 3) { + if (queryValues.mode <= 3) { json_object *jValue; jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, dev, "OutputDspName"); diff --git a/ALSA-afb/README.md b/ALSA-afb/README.md index a4b0395..93d2d4d 100644 --- a/ALSA-afb/README.md +++ b/ALSA-afb/README.md @@ -18,8 +18,8 @@ Testing: (from project directory bindings) # Get all controls from a given sound card http://localhost:1234/api/alsacore/getctl?devid=hw:0 - # Get detail on a given control (optional quiet=0=verbose,1,2) - http://localhost:1234/api/alsacore/getctl?devid=hw:0&numid=1&quiet=0 + # Get detail on a given control (optional mode=0=verbose,1,2) + http://localhost:1234/api/alsacore/getctl?devid=hw:0&numid=1&mode=0 # Debug event with afb-client-demo ``` diff --git a/HAL-afb/HAL-interface/CMakeLists.txt b/HAL-afb/HAL-interface/CMakeLists.txt index d4dba4b..c367c5c 100644 --- a/HAL-afb/HAL-interface/CMakeLists.txt +++ b/HAL-afb/HAL-interface/CMakeLists.txt @@ -21,7 +21,7 @@ PROJECT_TARGET_ADD(hal-interface) # Define targets - ADD_LIBRARY(${TARGET_NAME} STATIC hal-interface.c) + ADD_LIBRARY(${TARGET_NAME} STATIC hal-volmap.c hal-interface.c) # Library properties SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES OUTPUT_NAME halinterface) diff --git a/HAL-afb/HAL-interface/hal-interface.c b/HAL-afb/HAL-interface/hal-interface.c index dd00d1c..1f39a81 100644 --- a/HAL-afb/HAL-interface/hal-interface.c +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -19,16 +19,7 @@ * http://www.tldp.org/HOWTO/Alsa-sound-6.html */ #define _GNU_SOURCE // needed for vasprintf -#include #include -#include -#include -#include -#include -#include -#include -#include - #include "hal-interface.h" static alsaHalMapT *halCtls; @@ -61,72 +52,30 @@ STATIC void halSubscribe(afb_req request) { } } -// HAL normalise volume values to 0-100% -STATIC struct json_object *UnNormaliseValue(const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { - int length; - // assert response as the right length - length = json_object_array_length(valuesJ); - if (length != halCtls->count) { - AFB_WARNING ("NormaliseValue invalid ctl='%s' values count=%d len=%d", halCtls->name, halCtls->count, length); - return NULL; - } - - json_object *normalisedJ= json_object_new_array(); - for (int idx=0; idx halCtls->maxval) value= halCtls->maxval; - if (value < halCtls->minval) value= halCtls->minval; - - // If Integer scale to 0/100 - if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - value = (value * 100) / (halCtls->maxval - halCtls->minval); - } - - json_object_array_add(normalisedJ, json_object_new_int(value)); + +// Map HAL ctlName to ctlLabel +STATIC int halCtlStringToIndex (const char* label) { + + for (int idx = 0; halCtls[idx].ctl.numid; idx++) { + if (!strcmp (halCtls[idx].label, label)) return idx; } - return (normalisedJ); + // not found + return -1; } +STATIC int halCtlTagToIndex (halCtlsEnumT tag) { - -// HAL normalise volume values to 0-100% -STATIC struct json_object *NormaliseValue(const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { - int length; - - // assert response as the right length - length = json_object_array_length(valuesJ); - if (length != halCtls->count) { - AFB_WARNING ("NormaliseValue invalid ctl=%s values count=%d len=%d", halCtls->name, halCtls->count, length); - return NULL; - } - - json_object *normalisedJ= json_object_new_array(); - for (int idx=0; idx halCtls->maxval) value= halCtls->maxval; - if (value < halCtls->minval) value= halCtls->minval; - - // If Integer scale to 0/100 - if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - value = (value * 100) / (halCtls->maxval - halCtls->minval); - } - - json_object_array_add(normalisedJ, json_object_new_int(value)); + for (int idx = 0; halCtls[idx].ctl.numid; idx++) { + if (halCtls[idx].tag == tag) return idx; } - return (normalisedJ); + // not found + return -1; } - // Return ALL HAL snd controls PUBLIC void halListCtls(afb_req request) { struct json_object *ctlsHalJ = json_object_new_array(); @@ -144,26 +93,6 @@ PUBLIC void halListCtls(afb_req request) { afb_req_success (request, ctlsHalJ, NULL); } -// Map HAL ctlName to ctlLabel -STATIC int halCtlStringToIndex (const char* label) { - - for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - if (!strcmp (halCtls[idx].label, label)) return idx; - } - - // not found - return -1; -} - -STATIC int halCtlTagToIndex (halCtlsEnumT tag) { - - for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - if (halCtls[idx].tag == tag) return idx; - } - - // not found - return -1; -} STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { struct json_object *tmpJ; @@ -200,6 +129,43 @@ STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { return -1; } +// HAL normalise volume values to 0-100% +STATIC struct json_object *UnNormaliseValue(const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { + int length; + + // assert response as the right length + length = json_object_array_length(valuesJ); + if (length != halCtls->count) { + AFB_WARNING ("UnNormaliseValue invalid ctl='%s' values count=%d len=%d", halCtls->name, halCtls->count, length); + return NULL; + } + + json_object *normalisedJ= json_object_new_array(); + for (int idx=0; idx < halCtls->count; idx++) { + int value; + + // use last value in array when number of values does not match with actual ctl count + if (idx < length) { + json_object *valueJ = json_object_array_get_idx (valuesJ, idx); + value = json_object_get_int(valueJ); + + // cleanup and normalise value + if (value > halCtls->maxval) value= halCtls->maxval; + if (value < halCtls->minval) value= halCtls->minval; + + // If Integer move from 0-100% to effective value + if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { + value = (value * (halCtls->maxval-halCtls->minval))/100; + } + } + + // add unnormalised value into response + json_object_array_add(normalisedJ, json_object_new_int(value)); + } + + return (normalisedJ); +} + // Translate high level control to low level and call lower layer PUBLIC void halSetCtls(afb_req request) { @@ -212,11 +178,11 @@ PUBLIC void halSetCtls(afb_req request) { switch (json_object_get_type(ctlsInJ)) { case json_type_object: { - // control is in literal form {tag=xxx, label=xxx, val=xxxx} + // control is in literal form {tag=xxx, label=xxx, value=xxxx} index = halGetCtlIndex (request, ctlsInJ); if (index <=0) goto OnErrorExit; - err= json_object_object_get_ex (ctlsInJ, "val" , &valuesJ); + err= json_object_object_get_ex (ctlsInJ, "value" , &valuesJ); if (err) { afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ)); goto OnErrorExit; @@ -234,7 +200,7 @@ PUBLIC void halSetCtls(afb_req request) { index= halGetCtlIndex (request, ctlInJ); if (index<=0) goto OnErrorExit; - err= json_object_object_get_ex (ctlInJ, "val" , &valuesJ); + err= json_object_object_get_ex (ctlInJ, "value" , &valuesJ); if (err) { afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ)); goto OnErrorExit; @@ -257,7 +223,7 @@ PUBLIC void halSetCtls(afb_req request) { // Call now level CTL queryJ = json_object_new_object(); json_object_object_add(queryJ, "devid", json_object_new_string (halDevid)); - json_object_object_add(queryJ, "numids", ctlsOutJ); + json_object_object_add(queryJ, "numid", ctlsOutJ); err= afb_service_call_sync("alsacore", "setctls", queryJ, &responseJ); if (err) { @@ -313,7 +279,7 @@ STATIC json_object *CtlGetPrepareResponse(afb_req request, struct json_object *c struct json_object *halCtlJ = json_object_new_object(); json_object_object_add(halCtlJ, "label", json_object_new_string(halCtls[idx].label)); // idx+1 == HAL/NUMID json_object_object_add(halCtlJ, "tag" , json_object_new_int(halCtls[idx].tag)); // idx+1 == HAL/NUMID - json_object_object_add(halCtlJ, "val" , NormaliseValue(&halCtls[idx].ctl, valJ)); + json_object_object_add(halCtlJ, "val" , GetNormaliseVolume(&halCtls[idx].ctl, valJ)); json_object_array_add(halResponseJ, halCtlJ); break; } @@ -366,9 +332,9 @@ PUBLIC void halGetCtls(afb_req request) { // Call now level CTL queryJ = json_object_new_object(); json_object_object_add(queryJ, "devid", json_object_new_string (halDevid)); - json_object_object_add(queryJ, "numids", ctlsOutJ); + json_object_object_add(queryJ, "numid", ctlsOutJ); - err= afb_service_call_sync("alsacore", "getctls", queryJ, &responseJ); + err= afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); if (err) { afb_req_fail_f(request, "subcall:alsacore/getctl", "%s", json_object_get_string(responseJ)); goto OnErrorExit; @@ -418,17 +384,14 @@ PUBLIC void halServiceEvent(const char *evtname, struct json_object *eventJ) { STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { struct json_object *tmpJ, *ctlJ; - AFB_NOTICE ("*** %s, ", json_object_get_string(sndCtlJ)); - - // make sure we face a valid Alsa Low level ctl - if (!json_object_object_get_ex (sndCtlJ, "ctrl" , &ctlJ)) return -1; - json_object_object_get_ex (sndCtlJ, "name" , &tmpJ); ctl->name = (char*)json_object_get_string(tmpJ); json_object_object_get_ex (sndCtlJ, "numid" , &tmpJ); ctl->numid = json_object_get_int(tmpJ); + // make sure we face a valid Alsa Low level ctl + if (!json_object_object_get_ex (sndCtlJ, "ctl" , &ctlJ)) goto OnErrorExit; json_object_object_get_ex (ctlJ, "min" , &tmpJ); ctl->minval = json_object_get_int(tmpJ); @@ -445,7 +408,33 @@ STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { json_object_object_get_ex (ctlJ, "type" , &tmpJ); ctl->type = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + // process dbscale TLV if any + if (json_object_object_get_ex (sndCtlJ, "tlv" , &tmpJ)) { + struct json_object *dbscaleJ; + + if (!json_object_object_get_ex (tmpJ, "dbscale" , &dbscaleJ)) { + AFB_WARNING("TLV found but not DBscale attached ctl name=%s numid=%d", ctl->name, ctl->numid); + } else { + ctl->dbscale = malloc (sizeof (alsaHalDBscaleT)); + + json_object_object_get_ex (dbscaleJ, "min" , &tmpJ); + ctl->dbscale->min = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + + json_object_object_get_ex (dbscaleJ, "max" , &tmpJ); + ctl->dbscale->max = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + + json_object_object_get_ex (dbscaleJ, "step" , &tmpJ); + ctl->dbscale->step = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + + json_object_object_get_ex (dbscaleJ, "mute" , &tmpJ); + ctl->dbscale->mute = (snd_ctl_elem_type_t)json_object_get_int(tmpJ); + } + } + return 0; + + OnErrorExit: + return -1; } // this is call when after all bindings are loaded @@ -510,7 +499,8 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar queryurl = json_object_new_object(); json_object_get(devidJ); // make sure devidJ does not get free by 1st call. json_object_object_add(queryurl, "devid",devidJ); - json_object_object_add(queryurl, "ctls",ctlsJ); + json_object_object_add(queryurl, "ctl",ctlsJ); + json_object_object_add(queryurl, "mode",json_object_new_int(QUERY_COMPACT)); err= afb_service_call_sync ("alsacore", "addcustomctl", queryurl, &responseJ); if (err) { AFB_ERROR ("Fail creating HAL Custom ALSA ctls Response='%s'", json_object_get_string(responseJ)); @@ -553,10 +543,10 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar // Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT PUBLIC afb_verb_v2 halServiceApi[] = { - /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ - { .verb = "ping", .callback = pingtest}, - { .verb = "listctls", .callback = halListCtls}, - { .verb = "getctls", .callback = halGetCtls}, - { .verb = "subscribe", .callback = halSubscribe}, + /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ + { .verb = "ping", .callback = pingtest}, + { .verb = "ctl-list", .callback = halListCtls}, + { .verb = "ctl-get", .callback = halGetCtls}, + { .verb = "evt-sub", .callback = halSubscribe}, { .verb = NULL} /* marker for end of the array */ }; diff --git a/HAL-afb/HAL-interface/hal-interface.h b/HAL-afb/HAL-interface/hal-interface.h index 2cd3586..afe480e 100644 --- a/HAL-afb/HAL-interface/hal-interface.h +++ b/HAL-afb/HAL-interface/hal-interface.h @@ -23,16 +23,22 @@ #include "audio-interface.h" +typedef struct { + int min; + int max; + int step; + int mute; +} alsaHalDBscaleT; + typedef struct { char* name; int numid; - int values; + snd_ctl_elem_type_t type; + int count; int minval; int maxval; int step; - int count; - snd_ctl_elem_type_t type; - halAclEnumT acl; + alsaHalDBscaleT *dbscale; } alsaHalCtlMapT; // avoid compiler warning [Jose does not like typedef :) ] @@ -62,6 +68,9 @@ extern afb_verb_v2 halServiceApi[]; PUBLIC void halServiceEvent(const char *evtname, struct json_object *object); PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard); +// hal-volmap.c +PUBLIC struct json_object *GetNormaliseVolume(const alsaHalCtlMapT *halCtls, struct json_object *valuesJ); + #endif /* SHAREHALLIB_H */ diff --git a/HAL-afb/HAL-interface/hal-volmap.c b/HAL-afb/HAL-interface/hal-volmap.c new file mode 100644 index 0000000..de99974 --- /dev/null +++ b/HAL-afb/HAL-interface/hal-volmap.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 "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. + * + * references: + * alsa-util/amixer.c + alsa-lib/simple.c + */ + +#define _GNU_SOURCE // needed for vasprintf +#include +#include "hal-interface.h" + + +typedef enum { + DB_NORMALIZE_LINEAR, + DB_NORMALIZE_MATH, +} enumRandeModeDB_T; + +// Return Value express from 0-100% +STATIC int dbNormalizeVal (enumRandeModeDB_T normaliseMode, const alsaHalDBscaleT *dbscale, int value) { + double normalized, min_norm; + + // To get real DB from TLV DB values should be divided by 100 + switch (normaliseMode) { + case DB_NORMALIZE_LINEAR: + normalized = ((double)(value-dbscale->min)/(double)(dbscale->max-dbscale->min))*100; + break; + + case DB_NORMALIZE_MATH: + normalized = exp10((double)(value - dbscale->max) / 6000.0); + if (dbscale->min != SND_CTL_TLV_DB_GAIN_MUTE) { + min_norm = exp10((double)(dbscale->min - dbscale->max) / 6000.0); + normalized = (normalized - min_norm) / (1 - min_norm); + } + break; + + default: + normalized=0; + } + + return (int)round(normalized*100); +} + +// HAL normalise volume values to 0-100% +PUBLIC struct json_object *GetNormaliseVolume(const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { + int useNormalizeDB; + + // If not valid db_scale let's use raw_scale + if (!halCtls->dbscale || (halCtls->dbscale->min >= halCtls->dbscale->max)) { + + // dbscale is invalid let's try raw range + if (halCtls->minval >= halCtls->maxval) goto ExitOnError; + + // Use Raw Scale Model + useNormalizeDB= 0; + + } else { // db_scale looks OK let's use it + if ((halCtls->dbscale->max - halCtls->dbscale->min) <= MAX_LINEAR_DB_SCALE * 100) useNormalizeDB= DB_NORMALIZE_LINEAR; + else useNormalizeDB = DB_NORMALIZE_MATH; + + } + + // loop on values to normalise + int length = json_object_array_length(valuesJ); + json_object *normalisedJ= json_object_new_array(); + for (int idx=0; idx type == SND_CTL_ELEM_TYPE_INTEGER) { + if (useNormalizeDB) value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); + else value = 100* (value - halCtls->minval) / (halCtls->maxval - halCtls->minval); + } + + json_object_array_add(normalisedJ, json_object_new_int(value)); + } + + return (normalisedJ); + + ExitOnError: + return NULL; +} + \ No newline at end of file diff --git a/HAL-afb/HAL-plugin/HalPlugCtl.c b/HAL-afb/HAL-plugin/HalPlugCtl.c index 181d4f8..3755876 100644 --- a/HAL-afb/HAL-plugin/HalPlugCtl.c +++ b/HAL-afb/HAL-plugin/HalPlugCtl.c @@ -291,7 +291,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(afbhal) { continue; } - if (strcmp(id, "ctls") == 0) { + if (strcmp(id, "ctl") == 0) { const char *ctlConf; snd_config_type_t ctype; snd_config_iterator_t currentCtl, follow; diff --git a/README.md b/README.md new file mode 100644 index 0000000..a746cbf --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ +------------------------------------------------------------------------ +AGL-AUDIO bindings expose low+high level API through AGL framework +------------------------------------------------------------------------ + +Cloning Audio-Binding from Git +------------------------------------------------------- + +``` +git clone --recurse-submodules https://github.com/iotbzh/audio-bindings +cd audio-binding + +``` + +AFB_daemon dependency on Standard Linux Distributions +------------------------------------------------------- + # handle dependencies > (OpenSuse-42.2, Fedora-25, Ubuntu 16.04.2LTS) + gcc > 4.8 + systemd-devel (libsystemd-dev>=222) + libuuid-devel + file-devel(OpenSuSe) or libmagic-dev(Ubuntu) + libjson-c-devel + alsa-devel + ElectricFence (BUG should not be mandatory) + libopenssl-devel libgcrypt-devel libgnutls-devel (optional but requested by libmicrohttpd for https) + + OpenSuse >=42.2 + zypper in gcc5 gdb gcc5-c++ git cmake make ElectricFence systemd-devel libopenssl-devel libuuid-devel alsa-devel libgcrypt-devel libgnutls-devel libjson-c-devel file-devel mxml-devel + + Ubuntu >= 16.4 libuuid-devel + apt-get install cmake git electric-fence libsystemd-dev libssl-dev uuid-dev libasound2-dev libgcrypt20-dev libgnutls-dev libgnutls-dev libjson-c-dev libmagic-dev libmxml-dev + + libmicrohttpd>=0.9.55 (as today OpenSuse-42.2 or Ubuntu-.16.4 ship older versions) + afb-daemon from AGL Gerrit git clone https://gerrit.automotivelinux.org/gerrit/src/app-framework-binder + +``` + # Might want to add following variables into ~/.bashrc + echo "#---------- AGL options Start ---------" >>~/.bashrc + echo "# Object: AGL cmake option for binder/bindings" >>~/.bashrc + echo "# Date: `date`" >>~/.bashrc + echo 'export CC=gcc-5; export CXX=g++-5' >>~/.bashrc # if using gcc5 + echo 'export INSTALL_PREFIX=$HOME/opt' >>~/.bashrc + echo 'export LD_LIBRARY_PATH=$INSTALL_PREFIX/lib64:$INSTALL_PREFIX/lib' >>~/.bashrc + echo 'export LIBRARY_PATH=$INSTALL_PREFIX/lib64:$INSTALL_PREFIX/lib' >>~/.bashrc + echo 'export PKG_CONFIG_PATH=$INSTALL_PREFIX/lib64/pkgconfig:$INSTALL_PREFIX/lib/pkgconfig' >>~/.bashrc + echo 'export PATH=$INSTALL_PREFIX/bin:$PATH' >>~/.bashrc + echo 'export RSYNC_TARGET=MY_TARGET_HOSTNAME' >>~/.bashrc + echo 'export RSYNC_PREFIX=./opt' >>~/.bashrc + + echo "#---------- AGL options End ---------" >>~/.bashrc + source ~/.bashrc + + # install LibMicroHttpd + LIB_MH_VERSION=0.9.55 + wget https://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-${LIB_MH_VERSION}.tar.gz + tar -xzf libmicrohttpd-${LIB_MH_VERSION}.tar.gz + cd libmicrohttpd-${LIB_MH_VERSION} + ./configure --prefix=${INSTALL_PREFIX} + make + sudo make install-strip + + # retrieve last AFB_daemon from AGL + git clone https://gerrit.automotivelinux.org/gerrit/src/app-framework-binder + + # Warning: previous GCC options should be set before initial cmake (clean Build/*) + cd app-framework-binder; mkdir -p build; cd build + cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX .. + make + make install +``` + + +# Compile binding + +``` + source ~/.bashrc # or any other file where your have place your compilation preferences + mkdir -p build + cd build + cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX .. + make + + afb-daemon --workdir=.. --ldpaths=build --port=1234 --roothttp=./htdocs --token="" --verbose +``` + +# Local Source Debug with GDB + +Warning: technically AGL bindings are shared libraries loaded thought 'dlopen'. GDB supports source debug of dynamically +loaded libraries, but user should be warn that the actual path to sharelib symbols is directly inherited from DLOPEN. +As a result if you change your directory after binder start with --workdir=xxx then GDB will not find symbols anymore + + +``` + Examples: + + # WORK when running in direct + afb-daemon --workdir=.. --ldpaths=build --port=1234 --roothttp=./htdocs + + # FAIL when using GDB with warning: Could not load shared library .... + gdb -args afb-daemon --workdir=.. --ldpaths=build --port=1234 --roothttp=./htdocs + ... + warning: Could not load shared library symbols for ./build/ucs2-afb/afb-ucs2.so. + Do you need "set solib-search-path" or "set sysroot"? + ... +``` + +To debug sharelib symbol path: start your binder under GDB. Then break your session after the binder has +loaded its bindings. Finally use "info sharedlibrary" and check 'SymsRead'. If equal to 'No' then either you start GDB +from the wrong relative directory, either you have to use 'set solib-search-path' to force the path. + +``` + gdb -args afb-daemon --workdir=.. --ldpaths=build --port=1234 --roothttp=./htdocs + run + ... + NOTICE: API UNICENS added + NOTICE: Waiting port=1234 rootdir=. + NOTICE: Browser URL= http://localhost:1234 + (hit Ctrl-C to break the execution) + info sharedlibrary afb-* +``` + + +# Running an debugging on a target + +``` +export RSYNC_TARGET=root@xx.xx.xx.xx +export RSYNC_PREFIX=/opt # WARNING: installation directory should exist + +mkdir $RSYNC_TARGET +cd $RSYNC_TARGET + +cmake -DRSYNC_TARGET=$RSYNC_TARGET -DRSYNC_PREFIX=$RSYNC_PREFIX .. +make remote-target-populate + + ./build/target/start-${RSYNC_TARGET}.sh + firefox http://localhost:1234 # WARNING: do not forget firewall if any + + ssh -tt ${RSYNC_TARGET} speaker-test -twav -D hw:ep01 -c 2 +``` + +Note: remote-target-populate will + - create a script to remotely start the binder on the target in ./build/target/start-on-target-name.sh + - create a gdbinit file to transparently debug remotely in source code with gdb -x ./build/target/gdb-on-target-name.ini + - to run and debug directly from your IDE just configure the run and debug properties with the corresponding filename + +Note that Netbeans impose to set debug directory to ./build/pkgout or it won't find binding symbols for source debugging + + diff --git a/Shared-Interface/audio-interface.h b/Shared-Interface/audio-interface.h index 8cbea6f..09cb6c2 100644 --- a/Shared-Interface/audio-interface.h +++ b/Shared-Interface/audio-interface.h @@ -35,6 +35,13 @@ #endif #define STATIC static +typedef enum { + QUERY_QUIET =0, + QUERY_COMPACT =1, + QUERY_VERBOSE =2, + QUERY_FULL =3, +} halQueryMode; + // Most controls are MIXER but some vendor specific are possible typedef enum { OUTVOL, diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index e703c80..97c4986 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -43,7 +43,8 @@ set(CMAKE_BUILD_TYPE "DEBUG") # Static constante definition # ----------------------------- -add_compile_options(-DMAX_SND_CARD=16) +add_compile_options(-DMAX_SND_CARD=16) # should be more than enough even in luxury vehicule +add_compile_options(-DMAX_LINEAR_DB_SCALE=24) # until 24db volume normalisation use a simple linear scale # Compiler selection if needed. Overload the detected compiler. # ----------------------------------------------- diff --git a/htdocs/AFB-websock.js b/htdocs/AFB-websock.js index e77ec89..ff9fa60 100644 --- a/htdocs/AFB-websock.js +++ b/htdocs/AFB-websock.js @@ -1,7 +1,10 @@ +var urlws; +var urlhttp; + AFB = function(base, initialtoken){ -var urlws = "ws://"+window.location.host+"/"+base; -var urlhttp = "http://"+window.location.host+"/"+base; +urlws = "ws://"+window.location.host+"/"+base; +urlhttp = "http://"+window.location.host+"/"+base; /*********************************************/ /**** ****/ diff --git a/htdocs/AudioBinding.css b/htdocs/AudioBinding.css new file mode 100644 index 0000000..1052aa7 --- /dev/null +++ b/htdocs/AudioBinding.css @@ -0,0 +1,7 @@ +pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; } +.string { color: green; } +.number { color: darkorange; } +.boolean { color: blue; } +.null { color: magenta; } +.key { color: red; } + diff --git a/htdocs/AudioBinding.js b/htdocs/AudioBinding.js index 7105b20..e000a70 100644 --- a/htdocs/AudioBinding.js +++ b/htdocs/AudioBinding.js @@ -1,8 +1,30 @@ var afb = new AFB("api", "mysecret"); var ws; - var halapi="HALNotSelected"; + var sndcard="HALNotSelected"; var evtidx=0; + function syntaxHighlight(json) { + if (typeof json !== 'string') { + json = JSON.stringify(json, undefined, 2); + } + json = json.replace(/&/g, '&').replace(//g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'key'; + } else { + cls = 'string'; + } + } else if (/true|false/.test(match)) { + cls = 'boolean'; + } else if (/null/.test(match)) { + cls = 'null'; + } + return '' + match + ''; + }); + } + function getParameterByName(name, url) { if (!url) { url = window.location.href; @@ -25,8 +47,8 @@ var sndname=getParameterByName("sndname"); if (!sndname) sndname="PCH"; - var quiet=getParameterByName("quiet"); - if (!quiet) quiet="99"; + var mode=getParameterByName("mode"); + if (!mode) mode="99"; @@ -54,12 +76,12 @@ function replyok(obj) { console.log("replyok:" + JSON.stringify(obj)); - document.getElementById("output").innerHTML = "OK: "+JSON.stringify(obj); + document.getElementById("output").innerHTML = "OK: "+ syntaxHighlight(obj); } function replyerr(obj) { console.log("replyerr:" + JSON.stringify(obj)); - document.getElementById("output").innerHTML = "ERROR: "+JSON.stringify(obj); + document.getElementById("output").innerHTML = "ERROR: "+ syntaxHighlight(obj); } function gotevent(obj) { @@ -78,7 +100,8 @@ // On button click from HTML page function callbinder(api, verb, query) { console.log ("subscribe api="+api+" verb="+verb+" query=" +query); - document.getElementById("question").innerHTML = "apicall: " + api+"/"+verb +" ("+ JSON.stringify(query)+")"; + var question = urlws +"/" +api +"/" +verb + "?query=" + JSON.stringify(query); + document.getElementById("question").innerHTML = syntaxHighlight(question); ws.call(api+"/"+verb, query).then(replyok, replyerr); } @@ -89,27 +112,38 @@ // onlick update selected HAL api selectobj.onclick=function(){ - halapi= this.value; - console.log ("New Default HAL=" + halapi); + sndcard= this.value; + console.log ("Default Selection=" + sndcard); }; function gotit (result) { - // display response as for normal onclick action - replyok(result); - var response=result.response; + // display response as for normal onclick action + replyok(result); + var response=result.response; - // fulfill select with avaliable active HAL - for (idx=0; idx - Hello world test + + Alsa Low Level Simple Test + + + + references -
    -
  1. http://localhost:1234/alsa-core.html?devid=hw:xx
  2. -
  3. amixer -D hw:PCH controls
  4. -
- - -

Hello world test

- + +

+ Selected SndCard + + + API Verbosity +
+ +
    -
  1. getinfo: List Sound Cards -
  2. -
  3. -
  4. -
  5. -
  6. -
  7. +
  8. +
  9. +
  10. +

  11. -
  12. -
  13. -
  14. -
  15. -
  16. +
  17. +
  18. +
  19. +
  20. +

  21. -
  22. -
  23. -
  24. -
  25. +
  26. +
  27. +
  28. +

  29. -
  30. +

  31. -
  32. +

diff --git a/htdocs/alsa-hal.html b/htdocs/alsa-hal.html index ce48ff3..7a8655c 100644 --- a/htdocs/alsa-hal.html +++ b/htdocs/alsa-hal.html @@ -1,7 +1,7 @@ Basic Audio Hardware Abstraction Layer Test - + @@ -9,29 +9,40 @@ - references -

Simple AlsaHAL tests



Selected HAL + + API Verbosity +

    -
  1. -
  2. -
  3. -
  4. +
  5. +
  6. +
  7. +
  8. +
    +
  9. +
  10. +
  11. +

diff --git a/htdocs/audio-logic.html b/htdocs/audio-logic.html index 79ab114..c31282a 100644 --- a/htdocs/audio-logic.html +++ b/htdocs/audio-logic.html @@ -1,29 +1,9 @@ - Hello world test + High Level API Simple Test Page - - -

Hello world test

- -
-
    -
  1. -
    -
  2. -
  3. -
  4. -
  5. -
    -
  6. -
  7. -
    -
  8. + -
    - +ToBeDone \ No newline at end of file diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index adb9376..18d1e63 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -15,6 +15,8 @@ hal-interface.c + hal-volmap.c + hal-volmap.h HalPlugCb.c @@ -80,9 +82,10 @@ - - - + + + @@ -102,44 +105,51 @@ - + ../../../opt/include/alsa /usr/include/json-c + Shared-Interface + ../../../opt/include build/ALSA-afb - + build/ALSA-afb - + ../../../opt/include/alsa /usr/include/json-c + Shared-Interface + ../../../opt/include build/ALSA-afb - + ../../../opt/include/alsa /usr/include/json-c + Shared-Interface + ../../../opt/include build/ALSA-afb - + ../../../opt/include/alsa /usr/include/json-c + Shared-Interface ../../../opt/include build/ALSA-afb @@ -153,72 +163,86 @@ ex="false" tool="0" flavor2="3"> - + + + ../../../opt/include/afb + ../../../opt/include + ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + HAL-afb/HAL-interface + Shared-Interface + build/HAL-afb/HAL-interface + - - + + - HAL-afb/HAL-plugin + ../../../opt/include ../../../opt/include/alsa + /usr/include/p11-kit-1 + /usr/include/json-c + HAL-afb/HAL-interface + Shared-Interface + build/HAL-afb/HAL-interface + + + + + + ../../../opt/include build/HAL-afb/HAL-plugin - + - HAL-afb/HAL-plugin - ../../../opt/include/alsa ../../../opt/include build/HAL-afb/HAL-plugin - + - ../../../opt/include/afb - HAL-afb/HAL-plugin - ../../../opt/include/alsa build/HAL-afb/HAL-plugin - - CONTROL_CDEV_RX="/dev/inic-usb-crx" - CONTROL_CDEV_TX="/dev/inic-usb-ctx" - MAX_SND_CARD=16 - - + - + - + + Shared-Interface + HAL-afb/HAL-interface ../../../opt/include build/HAL-afb/Unicens-USB - + + HAL-afb/HAL-interface build/HAL-afb/Unicens-USB - + Shared-Interface ../../../opt/include @@ -227,7 +251,7 @@ - + /usr/include/json-c Shared-Interface @@ -271,7 +295,7 @@ - + @@ -310,14 +334,17 @@ + + + MAX_LINEAR_DB_SCALE=24 + + + + - HAL-afb/HAL-interface + HAL-afb/HAL-plugin ../../../opt/include/alsa - /usr/include/json-c - Shared-Interface - ../../../opt/include - build/HAL-afb/HAL-interface @@ -350,8 +377,6 @@ HAL-afb/Unicens-USB ../../../opt/include/alsa - Shared-Interface - HAL-afb/HAL-interface @@ -457,6 +482,10 @@ + + + + @@ -709,6 +738,10 @@ + + + + -- cgit 1.2.3-korg