/* * Copyright (C) 2018 "IoT.bzh" * Author Jonathan Aillet * * 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 #include #include #include #include #include #include #include "4a-internals-hal-value-handler.h" #include "4a-internals-hal-alsacore-link.h" /******************************************************************************* * Simple conversion value to/from percentage functions * ******************************************************************************/ int InternalHalConvertValueToPercentage(double val, double min, double max) { double range; range = max - min; if(range <= 0) return -INT_MAX; val -= min; return (int) round(val / range * 100); } int InternalHalConvertPercentageToValue(int percentage, int min, int max) { int range; range = max - min; if(range <= 0) return -INT_MAX; return (int) round((double) percentage * (double) range * 0.01 + (double) min); } /******************************************************************************* * Convert json object from percentage to value * ******************************************************************************/ int InternalHalConvertJsonValueForIntegerControl(afb_api_t apiHandle, struct InternalHalAlsaCtlProperties *alsaCtlProperties, json_object *toConvertJ, json_object **ConvertedJ, enum ConversionType requestedConversion) { int initialValue, convertedValue; if(! json_object_is_type(toConvertJ, json_type_int)) { AFB_API_ERROR(apiHandle, "Can't convert json value, unrecognized json format (must be an integer) : '%s'", json_object_get_string(toConvertJ)); return -1; } initialValue = json_object_get_int(toConvertJ); switch(requestedConversion) { case CONVERSION_NORMALIZED_TO_ALSACORE: if(initialValue < 0 || initialValue > 100) { AFB_API_ERROR(apiHandle, "Cannot convert '%i' value, value should be between 0 and 100 ('%s')", initialValue, json_object_get_string(toConvertJ)); return -2; } convertedValue = InternalHalConvertPercentageToValue(initialValue, alsaCtlProperties->minval, alsaCtlProperties->maxval); if(convertedValue == -INT_MAX) { AFB_API_ERROR(apiHandle, "Didn't succeed to convert %i (using min %i et max %i)", initialValue, alsaCtlProperties->minval, alsaCtlProperties->maxval); return -3; } if(alsaCtlProperties->step) { // Round value to the nearest step convertedValue = (int) round((double) convertedValue / (double) alsaCtlProperties->step); convertedValue *= alsaCtlProperties->step; } break; case CONVERSION_ALSACORE_TO_NORMALIZED: convertedValue = InternalHalConvertValueToPercentage(initialValue, alsaCtlProperties->minval, alsaCtlProperties->maxval); if(convertedValue == -INT_MAX) { AFB_API_ERROR(apiHandle, "Didn't succeed to convert %i (using min %i et max %i)", initialValue, alsaCtlProperties->minval, alsaCtlProperties->maxval); return -4; } break; default: AFB_API_ERROR(apiHandle, "Can't convert '%i' value, unrecognized conversion type : '%i'", initialValue, (int) requestedConversion); *ConvertedJ = NULL; return -5; } *ConvertedJ = json_object_new_int(convertedValue); if(! *ConvertedJ) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate converted value json integer"); return -5; } return 0; } int InternalHalConvertJsonValueForBooleanControl(afb_api_t apiHandle, struct InternalHalAlsaCtlProperties *alsaCtlProperties, json_object *toConvertJ, json_object **ConvertedJ, enum ConversionType requestedConversion) { int initialValue; switch(json_object_get_type(toConvertJ)) { case json_type_int: initialValue = json_object_get_int(toConvertJ); break; case json_type_boolean: initialValue = json_object_get_boolean(toConvertJ); break; default: AFB_API_ERROR(apiHandle, "Can't convert json value, unrecognized format (must be an integer or a boolean) : '%s'", json_object_get_string(toConvertJ)); return -1; } if(initialValue < 0 || initialValue > 1) { AFB_API_ERROR(apiHandle, "Cannot convert '%i' value, value should be 0 or 1 ('%s')", initialValue, json_object_get_string(toConvertJ)); return -2; } switch(requestedConversion) { case CONVERSION_NORMALIZED_TO_ALSACORE: *ConvertedJ = json_object_new_int(initialValue); break; case CONVERSION_ALSACORE_TO_NORMALIZED: *ConvertedJ = json_object_new_boolean(initialValue); break; default: AFB_API_ERROR(apiHandle, "Can't convert '%i' value, unrecognized conversion type : '%i'", initialValue, (int) requestedConversion); *ConvertedJ = NULL; return -3; } if(! *ConvertedJ) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate converted value json"); return -4; } return 0; } int InternalHalConvertJsonValues(afb_api_t apiHandle, struct InternalHalAlsaCtlProperties *alsaCtlProperties, json_object *toConvertJ, json_object **ConvertedJ, enum ConversionType requestedConversion) { int conversionError = 0, idx, count; json_type toConvertType; json_object *toConvertObjectJ, *convertedValueJ, *convertedArrayJ; *ConvertedJ = NULL; toConvertType = json_object_get_type(toConvertJ); count = (toConvertType == json_type_array) ? (int) json_object_array_length(toConvertJ) : 1; convertedArrayJ = json_object_new_array(); if(! convertedArrayJ) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate converted values json array"); return -1; } for(idx = 0; idx < count; idx++) { if(toConvertType == json_type_array) toConvertObjectJ = json_object_array_get_idx(toConvertJ, idx); else toConvertObjectJ = toConvertJ; switch(alsaCtlProperties->type) { case SND_CTL_ELEM_TYPE_INTEGER: case SND_CTL_ELEM_TYPE_INTEGER64: conversionError = InternalHalConvertJsonValueForIntegerControl(apiHandle, alsaCtlProperties, toConvertObjectJ, &convertedValueJ, requestedConversion); if(conversionError) { AFB_API_ERROR(apiHandle, "Error %i happened in when trying to convert index %i for integer control ('%s')", conversionError, idx, json_object_get_string(toConvertObjectJ)); json_object_put(convertedArrayJ); return -2; } break; case SND_CTL_ELEM_TYPE_BOOLEAN: conversionError = InternalHalConvertJsonValueForBooleanControl(apiHandle, alsaCtlProperties, toConvertObjectJ, &convertedValueJ, requestedConversion); if(conversionError) { AFB_API_ERROR(apiHandle, "Error %i happened in when trying to convert index %i for boolean control ('%s')", conversionError, idx, json_object_get_string(toConvertObjectJ)); json_object_put(convertedArrayJ); return -3; } break; default: AFB_API_ERROR(apiHandle, "Conversion not handle for the alsa control type %i", (int) alsaCtlProperties->type); json_object_put(convertedArrayJ); return -4; } json_object_array_put_idx(convertedArrayJ, idx, convertedValueJ); } *ConvertedJ = convertedArrayJ; return 0; } int InternalHalChangePreviousValuesUsingJson(afb_api_t apiHandle, struct InternalHalAlsaCtlProperties *alsaCtlProperties, json_object *requestedPercentageVariationJ, json_object *previousControlValuesJ, json_object **ChangedJ) { int requestedPercentageVariation, requestedVariation, toChangeValue, changedValue, idx, count; char *requestedPercentageVariationString, *conversionEnd; json_object *toChangeObjectJ, *changedArrayJ, *changedValueJ; *ChangedJ = NULL; requestedPercentageVariationString = (char *) json_object_get_string(requestedPercentageVariationJ); requestedPercentageVariation = (int) strtol(requestedPercentageVariationString, &conversionEnd, 10); if(conversionEnd == requestedPercentageVariationString) { AFB_API_ERROR(apiHandle, "Tried to increase/decrease an integer control " "but string sent in json is not a increase/decrease string : '%s'", json_object_get_string(requestedPercentageVariationJ)); return -1; } if(alsaCtlProperties->type != SND_CTL_ELEM_TYPE_INTEGER && alsaCtlProperties->type != SND_CTL_ELEM_TYPE_INTEGER64) { AFB_API_ERROR(apiHandle, "Tried to increase/decrease values on a incompatible " "control type (%i), control type must be an integer", alsaCtlProperties->type); return -2; } if(requestedPercentageVariation < -100 || requestedPercentageVariation > 100) { AFB_API_ERROR(apiHandle, "Tried to increase/decrease values but specified change is " "not a valid percentage, it should be between -100 and 100"); return -3; } requestedVariation = InternalHalConvertPercentageToValue((int) abs(requestedPercentageVariation), alsaCtlProperties->minval, alsaCtlProperties->maxval); if(requestedVariation == -INT_MAX) { AFB_API_ERROR(apiHandle, "Didn't succeed to convert %i (using min %i et max %i)", requestedPercentageVariation, alsaCtlProperties->minval, alsaCtlProperties->maxval); return -4; } if(requestedPercentageVariation < 0) requestedVariation = -requestedVariation; count = (int) json_object_array_length(previousControlValuesJ); changedArrayJ = json_object_new_array(); if(! changedArrayJ) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate changed values json array"); return -1; } for(idx = 0; idx < count; idx++) { toChangeObjectJ = json_object_array_get_idx(previousControlValuesJ, idx); if(! json_object_is_type(toChangeObjectJ, json_type_int)) { AFB_API_ERROR(apiHandle, "Current json object %s is not an integer (object %i out of %i)", json_object_get_string(toChangeObjectJ), idx, count); json_object_put(changedArrayJ); return -5; } toChangeValue = json_object_get_int(toChangeObjectJ); if((toChangeValue + requestedVariation) < alsaCtlProperties->minval) changedValue = alsaCtlProperties->minval; else if((toChangeValue + requestedVariation) > alsaCtlProperties->maxval) changedValue = alsaCtlProperties->maxval; else changedValue = toChangeValue + requestedVariation; changedValueJ = json_object_new_int(changedValue); if(! changedValueJ) { AFB_API_ERROR(apiHandle, "Didn't succeed to allocate changed value json integer"); json_object_put(changedArrayJ); return -6; } json_object_array_put_idx(changedArrayJ, idx, changedValueJ); } *ChangedJ = changedArrayJ; return 0; }