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 --- HAL-afb/HAL-interface/CMakeLists.txt | 2 +- HAL-afb/HAL-interface/hal-interface.c | 325 +++++++++++++++++++++++----------- HAL-afb/HAL-interface/hal-interface.h | 47 +++-- HAL-afb/HAL-interface/hal-volmap.c | 147 --------------- HAL-afb/HAL-interface/hal-volramp.c | 166 +++++++++++++++++ HAL-afb/HAL-interface/hal-volume.c | 150 ++++++++++++++++ 6 files changed, 577 insertions(+), 260 deletions(-) delete mode 100644 HAL-afb/HAL-interface/hal-volmap.c create mode 100644 HAL-afb/HAL-interface/hal-volramp.c create mode 100644 HAL-afb/HAL-interface/hal-volume.c (limited to 'HAL-afb/HAL-interface') diff --git a/HAL-afb/HAL-interface/CMakeLists.txt b/HAL-afb/HAL-interface/CMakeLists.txt index c367c5c..d20a283 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-volmap.c hal-interface.c) + ADD_LIBRARY(${TARGET_NAME} STATIC hal-volume.c hal-volramp.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 f4fc8df..76cdbd8 100644 --- a/HAL-afb/HAL-interface/hal-interface.c +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -21,24 +21,37 @@ #define _GNU_SOURCE // needed for vasprintf #include #include "hal-interface.h" +#include -static alsaHalMapT *halCtls; -static const char *halDevid; - - +alsaHalSndCardT *halSndCard; STATIC const char *halCtlsLabels[] = { - [StartHalCrlTag] = "NOT_USED", - [Master_Playback_Volume] = "Master_Playback_Volume", - [Master_OnOff_Switch] = "Master_OnOff_Switch", - [Master_Playback_Ramp] = "Master_Playback_Ramp", - [PCM_Playback_Volume] = "PCM_Playback_Volume", - [PCM_Playback_Switch] = "PCM_Playback_Switch", - [Capture_Volume] = "Capture_Volume", - - [EndHalCrlTag] = "NOT_USED" - + [Master_Playback_Volume] = "Master_Playback_Volume", + [Master_OnOff_Switch] = "Master_OnOff_Switch", + [Master_Playback_Ramp]= "Master_Playback_Ramp", + [PCM_Playback_Volume] = "PCM_Playback_Volume", + [PCM_Playback_Switch] = "PCM_Playback_Switch", + [Capture_Volume] = "Capture_Volume", + + [Vol_Ramp] = "Volume_Ramp", + [Vol_Ramp_Set_Mode] = "Volume_Ramp_Mode", + [Vol_Ramp_Set_Delay] = "Volume_Ramp_Delay", + [Vol_Ramp_Set_Down] = "Volume_Ramp_Down", + [Vol_Ramp_Set_Up] = "Volume_Ramp_Up", + + [EndHalCrlTag] = NULL +}; + +PUBLIC char *halVolRampModes[] = { + + [RAMP_VOL_NONE] = "None", + [RAMP_VOL_NORMAL] = "Normal", + [RAMP_VOL_SMOOTH] = "Smooth", + [RAMP_VOL_EMERGENCY] = "Emergency", + + [EndHalVolMod] = NULL, + }; // Force specific HAL to depend on ShareHalLib @@ -54,6 +67,7 @@ STATIC void halSubscribe(afb_req request) { // Map HAL ctlName to ctlLabel STATIC int halCtlStringToIndex (const char* label) { + alsaHalMapT *halCtls= halSndCard->ctls; for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { if (!strcmp (halCtls[idx].label, label)) return idx; @@ -64,6 +78,7 @@ STATIC int halCtlStringToIndex (const char* label) { } STATIC int halCtlTagToIndex (halCtlsEnumT tag) { + alsaHalMapT *halCtls= halSndCard->ctls; for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { if (halCtls[idx].tag == tag) return idx; @@ -76,10 +91,11 @@ STATIC int halCtlTagToIndex (halCtlsEnumT tag) { // Return ALL HAL snd controls PUBLIC void halListCtls(afb_req request) { - struct json_object *ctlsHalJ = json_object_new_array(); + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *ctlsHalJ = json_object_new_array(); for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - struct json_object *ctlHalJ = json_object_new_object(); + json_object *ctlHalJ = json_object_new_object(); json_object_object_add(ctlHalJ, "label", json_object_new_string(halCtls[idx].label)); json_object_object_add(ctlHalJ, "tag" , json_object_new_int(halCtls[idx].tag)); @@ -91,8 +107,8 @@ PUBLIC void halListCtls(afb_req request) { afb_req_success (request, ctlsHalJ, NULL); } -STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { - struct json_object *tmpJ; +STATIC int halGetCtlIndex (afb_req request, json_object*ctlInJ) { + json_object *tmpJ; int tag, index, done; // check 1st short command mode [tag1, tag2, ...] @@ -127,7 +143,6 @@ STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { goto OnErrorExit; } - if (index < 0) goto OnErrorExit; // return corresponding lowlevel numid to querylist @@ -139,10 +154,48 @@ STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { } +STATIC int halCallAlsaSetCtls (json_object *ctlsOutJ) { + json_object *responseJ, *queryJ; + int err; + + // Call now level CTL + queryJ = json_object_new_object(); + json_object_object_add(queryJ, "devid", json_object_new_string (halSndCard->devid)); + json_object_object_add(queryJ, "ctl", ctlsOutJ); + + err= afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ); + json_object_put (responseJ); // let's ignore response + + return err; +} + + +// retrieve a single HAL control from its tag. +PUBLIC int halSetCtlByTag (halRampEnumT tag, int value) { + json_object *ctlJ = json_object_new_array(); + alsaHalMapT *halCtls= halSndCard->ctls; + int err, index; + + index = halCtlTagToIndex(tag); + if (index < 0) goto OnErrorExit; + + json_object_array_add(ctlJ, json_object_new_int(halCtls[index].ctl.numid)); + json_object_array_add(ctlJ, volumeNormalise (ACTION_SET, &halCtls[index].ctl, json_object_new_int(value))); + + err = halCallAlsaSetCtls(ctlJ); + + return err; + + OnErrorExit: + return -1; +} + + // Translate high level control to low level and call lower layer PUBLIC void halSetCtls(afb_req request) { + alsaHalMapT *halCtls= halSndCard->ctls; int err, done, index; - struct json_object *ctlsInJ, *ctlsOutJ, *queryJ, *valuesJ, *responseJ; + json_object *ctlsInJ, *ctlsOutJ, *valuesJ; // get query from request ctlsInJ = afb_req_json(request); @@ -162,7 +215,7 @@ PUBLIC void halSetCtls(afb_req request) { } json_object_object_add (ctlsOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); - json_object_object_add (ctlsOutJ,"val", SetGetNormaliseVolumes (ACTION_SET, &halCtls[index].ctl, valuesJ)); + json_object_object_add (ctlsOutJ,"val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); break; } @@ -170,7 +223,7 @@ PUBLIC void halSetCtls(afb_req request) { ctlsOutJ = json_object_new_array(); for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - struct json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); + json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); index= halGetCtlIndex (request, ctlInJ); if (index < 0) goto OnErrorExit; @@ -180,9 +233,9 @@ PUBLIC void halSetCtls(afb_req request) { goto OnErrorExit; } // let's create alsa low level set control request - struct json_object *ctlOutJ = json_object_new_object(); + json_object *ctlOutJ = json_object_new_object(); json_object_object_add (ctlOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); - json_object_object_add (ctlOutJ, "val", SetGetNormaliseVolumes (ACTION_SET, &halCtls[index].ctl, valuesJ)); + json_object_object_add (ctlOutJ, "val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); json_object_array_add (ctlsOutJ, ctlOutJ); } @@ -194,14 +247,9 @@ PUBLIC void halSetCtls(afb_req request) { goto OnErrorExit; } - // 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, "numid", ctlsOutJ); - - err= afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ); + err = halCallAlsaSetCtls (ctlsOutJ); if (err) { - afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(responseJ)); + afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(ctlsOutJ)); goto OnErrorExit; } @@ -213,24 +261,35 @@ OnErrorExit: }; // Remap low level controls into HAL hight level ones -STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *ctlsJ) { - struct json_object *halResponseJ; - - // make sure return controls have a valid type - if (json_object_get_type(ctlsJ) != json_type_array) { - afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol ctlsJ=%s", json_object_get_string(ctlsJ)); - goto OnErrorExit; +STATIC json_object *HalGetPrepareResponse(afb_req request, json_object *ctlsJ) { + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *halResponseJ; + int length; + + switch (json_object_get_type(ctlsJ)) { + case json_type_array: + // responseJ is a JSON array + halResponseJ = json_object_new_array(); + length = json_object_array_length(ctlsJ); + break; + case json_type_object: + halResponseJ=NULL; + length = 1; + break; + default: + afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol ctlsJ=%s", json_object_get_string(ctlsJ)); + goto OnErrorExit; } - // responseJ is a JSON array - halResponseJ = json_object_new_array(); - // loop on array and store values into client context - for (int idx = 0; idx < json_object_array_length(ctlsJ); idx++) { - struct json_object *sndCtlJ, *valJ, *numidJ; + for (int idx = 0; idx < length; idx++) { + json_object *sndCtlJ, *valJ, *numidJ; int numid; - sndCtlJ = json_object_array_get_idx(ctlsJ, idx); + // extract control from array if any + if (halResponseJ) sndCtlJ = json_object_array_get_idx(ctlsJ, idx); + else sndCtlJ=ctlsJ; + if (!json_object_object_get_ex(sndCtlJ, "id", &numidJ) || !json_object_object_get_ex(sndCtlJ, "val", &valJ)) { afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from alsa/getcontrol ctl=%s", json_object_get_string(sndCtlJ)); goto OnErrorExit; @@ -240,14 +299,16 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *c numid= (halCtlsEnumT) json_object_get_int(numidJ); for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - if (halCtls[idx].ctl.numid == numid) { - + if (halCtls[idx].ctl.numid == numid) { // translate low level numid to HAL one and normalise values - struct json_object *halCtlJ = json_object_new_object(); + 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" , SetGetNormaliseVolumes(ACTION_GET, &halCtls[idx].ctl, valJ)); - json_object_array_add(halResponseJ, halCtlJ); + json_object_object_add(halCtlJ, "val" , volumeNormalise(ACTION_GET, &halCtls[idx].ctl, valJ)); + + if (halResponseJ) json_object_array_add(halResponseJ, halCtlJ); + else halResponseJ=halCtlJ; + break; } } @@ -262,10 +323,55 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *c return NULL; } + + +STATIC json_object *halCallAlsaGetCtls (json_object *ctlsOutJ) { + json_object *responseJ, *queryJ; + int err, done; + + // Call now level CTL + queryJ = json_object_new_object(); + json_object_object_add(queryJ, "devid", json_object_new_string (halSndCard->devid)); + json_object_object_add(queryJ, "ctl", ctlsOutJ); + + err= afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); + if (err) goto OnErrorExit; + + // Let ignore info data if any and keep on response + done = json_object_object_get_ex (responseJ, "response", &responseJ); + if (!done) goto OnErrorExit; + + return responseJ; + + OnErrorExit: + return NULL; +} + +// retrieve a single HAL control from its tag. +PUBLIC json_object *halGetCtlByTag (halRampEnumT tag) { + json_object *responseJ, *valJ; + alsaHalMapT *halCtls= halSndCard->ctls; + int done, index; + + index = halCtlTagToIndex(tag); + if (index < 0) goto OnErrorExit; + responseJ = halCallAlsaGetCtls(json_object_new_int(halCtls[index].ctl.numid)); + + done = json_object_object_get_ex(responseJ, "val", &valJ); + if (!done) goto OnErrorExit; + + return volumeNormalise(ACTION_GET, &halCtls[index].ctl, valJ); + + OnErrorExit: + return NULL; +} + + // Translate high level control to low level and call lower layer PUBLIC void halGetCtls(afb_req request) { - int err, index; - struct json_object *ctlsInJ, *ctlsOutJ, *queryJ, *responseJ; + int index; + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *ctlsInJ, *ctlsOutJ, *responseJ; // get query from request ctlsInJ = afb_req_json(request); @@ -283,7 +389,7 @@ PUBLIC void halGetCtls(afb_req request) { case json_type_array: { for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - struct json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); + json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); index= halGetCtlIndex (request, ctlInJ); if (index < 0) goto OnErrorExit; json_object_array_add (ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid)); @@ -297,21 +403,14 @@ 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, "numid", ctlsOutJ); - - err= afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); - if (err) { + responseJ = halCallAlsaGetCtls (ctlsOutJ); + if (!responseJ) { afb_req_fail_f(request, "subcall:alsacore/getctl", "%s", json_object_get_string(responseJ)); goto OnErrorExit; } - // Let ignore info data if any and keep on response - json_object_object_get_ex (responseJ, "response", &responseJ); - // map back low level response to HAL ctl with normalised values - struct json_object *halResponse = HalGetPrepareResponse(request, responseJ); + json_object *halResponse = HalGetPrepareResponse(request, responseJ); if (!halResponse) goto OnErrorExit; afb_req_success (request, halResponse, NULL); @@ -321,35 +420,9 @@ OnErrorExit: return; }; -// This receive all event this binding subscribe to -PUBLIC void halServiceEvent(const char *evtname, struct json_object *eventJ) { - int numid; - struct json_object *numidJ, *valuesJ, *valJ; - - AFB_NOTICE("halServiceEvent evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); - - json_object_object_get_ex (eventJ, "val" , &valuesJ); - if (!valuesJ) { - AFB_ERROR("halServiceEvent novalues: evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); - return; - } - - json_object_object_get_ex (valuesJ, "numid" , &numidJ); - numid = json_object_get_int (numidJ); - - // search it corresponding numid in halCtls attach a callback - if (numid) { - for (int idx= 0; halCtls[idx].ctl.numid; idx++) { - if (halCtls[idx].ctl.numid == numid && halCtls[idx].cb.callback != NULL) { - json_object_object_get_ex (valuesJ, "val" , &valJ); - halCtls[idx].cb.callback (&halCtls[idx].ctl, halCtls[idx].cb.handle, valJ); - } - } - } -} -STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { - struct json_object *tmpJ, *ctlJ; +STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, json_object *sndCtlJ) { + json_object *tmpJ, *ctlJ; json_object_object_get_ex (sndCtlJ, "name" , &tmpJ); ctl->name = (char*)json_object_get_string(tmpJ); @@ -377,7 +450,7 @@ STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { // process dbscale TLV if any if (json_object_object_get_ex (sndCtlJ, "tlv" , &tmpJ)) { - struct json_object *dbscaleJ; + 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); @@ -407,8 +480,12 @@ STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { // this is call when after all bindings are loaded PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard) { int err; - struct json_object *queryurl, *responseJ, *devidJ, *ctlsJ, *tmpJ; - halCtls = alsaHalSndCard->ctls; // Get sndcard specific HAL control mapping + json_object *queryurl, *responseJ, *devidJ, *ctlsJ, *tmpJ; + alsaHalMapT *halCtls= alsaHalSndCard->ctls; + + // if not volume normalisation CB provided use default one + if (!alsaHalSndCard->volumeCB) alsaHalSndCard->volumeCB=volumeNormalise; + halSndCard= alsaHalSndCard; err= afb_daemon_require_api("alsacore", 1); if (err) { @@ -435,12 +512,12 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar } // save devid for future use - halDevid = strdup (json_object_get_string(devidJ)); + halSndCard->devid = strdup (json_object_get_string(devidJ)); // for each Non Alsa Control callback create a custom control ctlsJ= json_object_new_array(); for (int idx= 0; (halCtls[idx].ctl.name||halCtls[idx].ctl.numid); idx++) { - struct json_object *ctlJ; + json_object *ctlJ; // map HAL ctlTad with halCrlLabel to simplify set/get ctl operations using Labels halCtls[idx].label = halCtlsLabels[halCtls[idx].tag]; @@ -452,10 +529,29 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar if (halCtls[idx].ctl.maxval) json_object_object_add(ctlJ, "max" , json_object_new_int(halCtls[idx].ctl.maxval)); if (halCtls[idx].ctl.step) json_object_object_add(ctlJ, "step" , json_object_new_int(halCtls[idx].ctl.step)); if (halCtls[idx].ctl.type) json_object_object_add(ctlJ, "type" , json_object_new_int(halCtls[idx].ctl.type)); + if (halCtls[idx].ctl.count) json_object_object_add(ctlJ, "count", json_object_new_int(halCtls[idx].ctl.count)); + if (halCtls[idx].ctl.value) json_object_object_add(ctlJ, "value", json_object_new_int(halCtls[idx].ctl.value)); + + if (halCtls[idx].ctl.dbscale) { + json_object *dbscaleJ=json_object_new_object(); + if (halCtls[idx].ctl.dbscale->max) json_object_object_add(dbscaleJ, "max" , json_object_new_int(halCtls[idx].ctl.dbscale->max)); + if (halCtls[idx].ctl.dbscale->min) json_object_object_add(dbscaleJ, "min" , json_object_new_int(halCtls[idx].ctl.dbscale->min)); + if (halCtls[idx].ctl.dbscale->step) json_object_object_add(dbscaleJ, "step", json_object_new_int(halCtls[idx].ctl.dbscale->step)); + if (halCtls[idx].ctl.dbscale->mute) json_object_object_add(dbscaleJ, "mute", json_object_new_int(halCtls[idx].ctl.dbscale->mute)); + json_object_object_add(ctlJ, "dbscale" , dbscaleJ); + } + + if (halCtls[idx].ctl.enums) { + json_object *enumsJ=json_object_new_array(); + for (int jdx=0; halCtls[idx].ctl.enums[jdx]; jdx++) { + json_object_array_add(enumsJ, json_object_new_string(halCtls[idx].ctl.enums[jdx])); + } + json_object_object_add(ctlJ, "enums" , enumsJ); + } json_object_array_add(ctlsJ, ctlJ); } else { ctlJ = json_object_new_object(); - if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "numid" , json_object_new_int(halCtls[idx].ctl.numid)); + if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "ctl" , json_object_new_int(halCtls[idx].ctl.numid)); if (halCtls[idx].ctl.name) json_object_object_add(ctlJ, "name" , json_object_new_string(halCtls[idx].ctl.name)); json_object_array_add(ctlsJ, ctlJ); } @@ -508,13 +604,40 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar return (1); }; + +// This receive all event this binding subscribe to +PUBLIC void halServiceEvent(const char *evtname, json_object *eventJ) { + int numid; + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *numidJ, *valuesJ; + + AFB_DEBUG("halServiceEvent evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); + + json_object_object_get_ex (eventJ, "id" , &numidJ); + numid = json_object_get_int (numidJ); + if (!numid) { + AFB_ERROR("halServiceEvent noid: evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); + return; + } + json_object_object_get_ex (eventJ, "val" , &valuesJ); + + // search it corresponding numid in halCtls attach a callback + if (numid) { + for (int idx= 0; halCtls[idx].ctl.numid; idx++) { + if (halCtls[idx].ctl.numid == numid && halCtls[idx].cb.callback != NULL) { + halCtls[idx].cb.callback (halCtls[idx].tag, &halCtls[idx].ctl, halCtls[idx].cb.handle, valuesJ); + } + } + } +} + // 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 = "ctl-list", .callback = halListCtls}, - { .verb = "ctl-get", .callback = halGetCtls}, - { .verb = "ctl-set", .callback = halSetCtls}, - { .verb = "evt-sub", .callback = halSubscribe}, + { .verb = "ping", .callback = pingtest , .info="ping test for API"}, + { .verb = "ctl-list", .callback = halListCtls , .info="List AGL normalised Sound Controls"}, + { .verb = "ctl-get", .callback = halGetCtls , .info="Get one/many sound controls"}, + { .verb = "ctl-set", .callback = halSetCtls , .info="Set one/many sound controls"}, + { .verb = "evt-sub", .callback = halSubscribe, .info="Subscribe to HAL events"}, { .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 c31c145..23944ad 100644 --- a/HAL-afb/HAL-interface/hal-interface.h +++ b/HAL-afb/HAL-interface/hal-interface.h @@ -22,12 +22,26 @@ #include #include "audio-interface.h" +#include + typedef enum { - ACTION_SET, - ACTION_GET + ACTION_SET, + ACTION_GET } ActionSetGetT; +// VolRamp Handle Store current status for a given VolRam CB set +typedef struct { + halRampEnumT mode; + halCtlsEnumT slave; + int delay; // delay between volset in us + int stepDown; // linear % + int stepUp; // linear % + int current; // current volume for slave ctl + int target; // target volume + sd_event_source *evtsrc; // event loop timer source +} halVolRampT; + typedef struct { int min; int max; @@ -42,7 +56,9 @@ typedef struct { int count; int minval; int maxval; + int value; int step; + char **enums; alsaHalDBscaleT *dbscale; } alsaHalCtlMapT; @@ -50,8 +66,8 @@ typedef struct { typedef struct afb_service alsaHalServiceT; typedef struct { - struct json_object* (*callback)(alsaHalCtlMapT *control, void* handle, struct json_object *valuesJ); - void* handle; + void (*callback)(halCtlsEnumT tag, alsaHalCtlMapT *control, void* handle, json_object *valuesJ); + void* handle; } alsaHalCbMapT; typedef struct { @@ -62,19 +78,28 @@ typedef struct { char* info; } alsaHalMapT; - -typedef const struct { - char *name; +typedef struct { + const char *name; const char *info; - alsaHalMapT *ctls; + alsaHalMapT *ctls; + const char *devid; + json_object* (*volumeCB)(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); } alsaHalSndCardT; +// hal-interface.c extern afb_verb_v2 halServiceApi[]; -PUBLIC void halServiceEvent(const char *evtname, struct json_object *object); +extern char *halVolRampModes[]; +PUBLIC void halServiceEvent(const char *evtname, json_object *object); PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard); +PUBLIC json_object *halGetCtlByTag (halRampEnumT tag); +PUBLIC int halSetCtlByTag (halRampEnumT tag, int value); + + +// hal-volramp.c +PUBLIC void volumeRamp (halCtlsEnumT halTag,alsaHalCtlMapT *control, void* handle, json_object *valJ); -// hal-volmap.c -PUBLIC struct json_object *SetGetNormaliseVolumes(ActionSetGetT action, const alsaHalCtlMapT *halCtls, struct json_object *valuesJ); +// hal-volume.c +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); #endif /* SHAREHALLIB_H */ diff --git a/HAL-afb/HAL-interface/hal-volmap.c b/HAL-afb/HAL-interface/hal-volmap.c deleted file mode 100644 index 2b619fd..0000000 --- a/HAL-afb/HAL-interface/hal-volmap.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 { - NORMALIZE_NONE=0, - NORMALIZE_DB_LINEAR, - NORMALIZE_DB_MATH, - NORMALIZE_LINEAR, -} 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 NORMALIZE_DB_LINEAR: - normalized = ((double)(value-dbscale->min)/(double)(dbscale->max-dbscale->min)); - break; - - case NORMALIZE_DB_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) / 20); - 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 *SetGetNormaliseVolumes(ActionSetGetT action, const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { - enumRandeModeDB_T useNormalizeDB; - int length; - - // If Integer look for DBscale - if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - - // 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= NORMALIZE_LINEAR; - - } else { // db_scale looks OK let's use it - if ((halCtls->dbscale->max - halCtls->dbscale->min) <= MAX_LINEAR_DB_SCALE * 100) useNormalizeDB= NORMALIZE_DB_LINEAR; - else useNormalizeDB = NORMALIZE_LINEAR; // Fulup not sure how to handle this useNormalizeDB=NORMALIZE_DB_MATH; - } - } else useNormalizeDB= NORMALIZE_NONE; - - - // loop on values to normalise - enum json_type jtype=json_object_get_type(valuesJ); - if (jtype == json_type_array) length = json_object_array_length(valuesJ); - else length = 1; - - json_object *normalisedJ= json_object_new_array(); - for (int idx=0; idx < length; idx++) { - int value; - - if (jtype == json_type_array) { - json_object *valueJ = json_object_array_get_idx (valuesJ, idx); - value = json_object_get_int(valueJ); - } else { - value = json_object_get_int(valuesJ); - } - - // If Integer scale to 0/100 - if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { - - switch (action) { - - case ACTION_GET: - switch (useNormalizeDB) { - case NORMALIZE_LINEAR: - value = 100* (value - halCtls->minval) / (halCtls->maxval - halCtls->minval); - break; - case NORMALIZE_DB_MATH: //ToBeDone - value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); - break; - case NORMALIZE_NONE: - default: - value = value; - } - break; - - case ACTION_SET: - switch (useNormalizeDB) { - case NORMALIZE_LINEAR: - value = (value * (halCtls->maxval - halCtls->minval))/100; - break; - case NORMALIZE_DB_MATH: //ToBeDone - value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); - break; - case NORMALIZE_NONE: - default: - value = value; - } - break; - - default: - AFB_NOTICE ("SetGetNormaliseVolumes: invalid action value=%d", (int)action); - goto ExitOnError; - } - } - - 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-interface/hal-volramp.c b/HAL-afb/HAL-interface/hal-volramp.c new file mode 100644 index 0000000..6343a06 --- /dev/null +++ b/HAL-afb/HAL-interface/hal-volramp.c @@ -0,0 +1,166 @@ +/* + * 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. + * + */ + +#define _GNU_SOURCE // needed for vasprintf +#include "hal-interface.h" + +STATIC int RampTimerCB (sd_event_source* source,uint64_t timer, void* handle) { + halVolRampT *volRamp= (halVolRampT*)handle; + int err; + uint64_t usec; + + // RampDown + if (volRamp->current > volRamp->target) { + volRamp->current = volRamp->current - volRamp->stepDown; + if (volRamp->current < volRamp->target) volRamp->current = volRamp->target; + } + + // RampUp + if (volRamp->current < volRamp->target) { + volRamp->current = volRamp->current + volRamp->stepUp; + if (volRamp->current > volRamp->target) volRamp->current = volRamp->target; + } + + // request current Volume Level + err = halSetCtlByTag(volRamp->slave, volRamp->current); + if (err) goto OnErrorExit; + + // we reach target stop volram event + if (volRamp->current == volRamp->target) sd_event_source_unref(source); + else { + // otherwise validate timer for a new run + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); + err=sd_event_source_set_time(source, usec+volRamp->delay); + } + + return 0; + + OnErrorExit: + AFB_WARNING ("RampTimerCB Fail to set HAL ctl tag=%d vol=%d", Master_Playback_Volume, volRamp->current); + sd_event_source_unref(source); // abandon VolRamp + return -1; +} + +/* UCS2 Interface Timer Callback */ +STATIC void SetRampTimer(void *handle) { + halVolRampT *volRamp= (halVolRampT*)handle; + + uint64_t usec; + /* set a timer with 250ms accuracy */ + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_add_time(afb_daemon_get_event_loop(), &volRamp->evtsrc, CLOCK_MONOTONIC, usec, 250, RampTimerCB, volRamp); +} + +STATIC int volumeDoRamp (halVolRampT *volRamp, int numid, json_object *volumeJ) { + json_object *responseJ; + + // request current Volume Level + responseJ = halGetCtlByTag(volRamp->slave); + if (!responseJ) { + AFB_WARNING ("volumeDoRamp Fail to get HAL ctl tag=%d", Master_Playback_Volume); + goto OnErrorExit; + } + + // use 1st volume value as target for ramping + switch (json_object_get_type(volumeJ)) { + case json_type_array: + volRamp->target=json_object_get_int(json_object_array_get_idx(volumeJ,0)); + break; + + case json_type_int: + volRamp->target=json_object_get_int(volumeJ); + break; + + default: + AFB_WARNING ("volumeDoRamp Invalid volumeJ=%s", json_object_get_string(volumeJ)); + goto OnErrorExit; + } + + // use 1st volume value as current for ramping + switch (json_object_get_type(responseJ)) { + case json_type_array: + volRamp->current=json_object_get_int(json_object_array_get_idx(responseJ,0)); + break; + + case json_type_int: + volRamp->current=json_object_get_int(responseJ); + break; + + default: + AFB_WARNING ("volumeDoRamp Invalid reponseJ=%s", json_object_get_string(responseJ)); + goto OnErrorExit; + } + + SetRampTimer (volRamp); + + return 0; + + OnErrorExit: + return -1; +} + + +PUBLIC void volumeRamp (halCtlsEnumT halTag, alsaHalCtlMapT *ctl, void* handle, json_object *valJ) { + halVolRampT *volRamp = (halVolRampT*) handle; + json_object *tmpJ; + + if (json_object_get_type(valJ) != json_type_array || volRamp==NULL) goto OnErrorExit; + + switch (halTag) { + case Vol_Ramp: + tmpJ = json_object_array_get_idx(valJ, 0); + volumeDoRamp (volRamp, ctl->numid, tmpJ); + break; + + case Vol_Ramp_Set_Mode: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->mode = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Slave: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->slave = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Delay: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->delay = 1000*json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Down: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepDown = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Up: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepUp = json_object_get_int (tmpJ); + break; + + default: + goto OnErrorExit; + } + + return; + + OnErrorExit: + AFB_WARNING ("Invalid Ctrl Event halCtlsEnumT=%d numid=%d name=%s value=%s", halTag, ctl->numid, ctl->name, json_object_get_string(valJ)); + return; +} + \ No newline at end of file diff --git a/HAL-afb/HAL-interface/hal-volume.c b/HAL-afb/HAL-interface/hal-volume.c new file mode 100644 index 0000000..593b74c --- /dev/null +++ b/HAL-afb/HAL-interface/hal-volume.c @@ -0,0 +1,150 @@ +/* + * 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 + * snd_tlv_convert_from_dB + * nt snd_tlv_convert_to_dB + * snd_tlv_get_dB_range + */ + +#define _GNU_SOURCE // needed for vasprintf +#include +#include "hal-interface.h" + + +typedef enum { + NORMALIZE_NONE=0, + NORMALIZE_DB_LINEAR, + NORMALIZE_DB_MATH, + NORMALIZE_LINEAR, +} 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 NORMALIZE_DB_LINEAR: + normalized = ((double)(value-dbscale->min)/(double)(dbscale->max-dbscale->min)); + break; + + case NORMALIZE_DB_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) / 20); + normalized = (normalized - min_norm) / (1 - min_norm); + } + + break; + + default: + normalized=0; + } + + return (int)round(normalized*100); +} + +// HAL normalise volume values to 0-100% +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ) { + enumRandeModeDB_T useNormalizeDB; + int length; + + // If Integer look for DBscale + if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { + + // 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= NORMALIZE_LINEAR; + + } else { // db_scale looks OK let's use it + if ((halCtls->dbscale->max - halCtls->dbscale->min) <= MAX_LINEAR_DB_SCALE * 100) useNormalizeDB= NORMALIZE_DB_LINEAR; + else useNormalizeDB = NORMALIZE_LINEAR; // Fulup not sure how to handle this useNormalizeDB=NORMALIZE_DB_MATH; + } + } else useNormalizeDB= NORMALIZE_NONE; + + + // loop on values to normalise + enum json_type jtype=json_object_get_type(valuesJ); + if (jtype == json_type_array) length = json_object_array_length(valuesJ); + else length = 1; + + json_object *normalisedJ= json_object_new_array(); + for (int idx=0; idx < length; idx++) { + int value; + + if (jtype == json_type_array) { + json_object *valueJ = json_object_array_get_idx (valuesJ, idx); + value = json_object_get_int(valueJ); + } else { + value = json_object_get_int(valuesJ); + } + + // If Integer scale to 0/100 + if (halCtls->type == SND_CTL_ELEM_TYPE_INTEGER) { + + switch (action) { + + case ACTION_GET: + switch (useNormalizeDB) { + case NORMALIZE_LINEAR: + value = 100* (value - halCtls->minval) / (halCtls->maxval - halCtls->minval); + break; + case NORMALIZE_DB_MATH: //ToBeDone + value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); + break; + case NORMALIZE_NONE: + default: + value = value; + } + break; + + case ACTION_SET: + switch (useNormalizeDB) { + case NORMALIZE_LINEAR: + value = (value * (halCtls->maxval - halCtls->minval))/100; + break; + case NORMALIZE_DB_MATH: //ToBeDone + value = dbNormalizeVal (useNormalizeDB, halCtls->dbscale, value); + break; + case NORMALIZE_NONE: + default: + value = value; + } + break; + + default: + AFB_NOTICE ("volumeNormalise: invalid action value=%d", (int)action); + goto ExitOnError; + } + } + + json_object_array_add(normalisedJ, json_object_new_int(value)); + } + + return (normalisedJ); + + ExitOnError: + return NULL; +} + \ No newline at end of file -- cgit 1.2.3-korg