/* * Copyright (C) 2016 "IoT.bzh" * Author Fulup Ar Foll <fulup@iot.bzh> * * 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; } STATIC void SetRampTimer(void *handle) { halVolRampT *volRamp = (halVolRampT*) handle; uint64_t usec; // set a timer with ~250us 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(halCtlsTagT 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) { // Only config use wellknown tag. Default is DoVolRamp default: 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); switch (volRamp->mode) { case RAMP_VOL_SMOOTH: volRamp->delay = 100 * 1000; volRamp->stepDown = 1; volRamp->stepUp = 1; break; case RAMP_VOL_NORMAL: volRamp->delay = 100 * 1000; volRamp->stepDown = 3; volRamp->stepUp = 2; break; case RAMP_VOL_EMERGENCY: volRamp->delay = 50 * 1000; volRamp->stepDown = 6; volRamp->stepUp = 2; break; default: goto OnErrorExit; } 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; } return; OnErrorExit: AFB_WARNING("volumeRamp: Invalid Ctrl Event halCtlsTagT=%d numid=%d name=%s value=%s", halTag, ctl->numid, ctl->name, json_object_get_string(valJ)); return; }