/*
 * Copyright 2018-2020 NXP
 * This software is owned or controlled by NXP and may only be used strictly
 * in accordance with the applicable license terms.  By expressly accepting
 * such terms or by downloading, installing, activating and/or otherwise using
 * the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms.  If you do not agree to
 * be bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software.
 */

#include <stdint.h>
#include "usb_device_config.h"
#if ((defined(USB_DEVICE_CONFIG_AUDIO)) && (USB_DEVICE_CONFIG_AUDIO > 0U))

#include "usb_volume_conversion.h"

/*
 * From the "USB Device Class Definition for Audio devices: 5.2.2.4.3.2 Volume Control" document:
 * The settings for the CUR, MIN, and MAX attributes can range from +127.9961 dB (0x7FFF) down to -127.9961 dB (0x8001)
 * in steps of 1/256 dB or 0.00390625 dB (0x0001).
 * The range for the CUR attribute is extended by code 0x8000, representing silence, i.e., - dB.
 * The settings for the RES attribute can only take positive values and range from 1/256 dB (0x0001) to +127.9961 dB (0x7FFF).
 */

/*
 * USB db values are between [-127.9961 , 127.9961].
 * We use the approximation of [-128, 128] in our calculations.
 * We will compensate in the final result by subtracting 0x1.
 *
 * In the USB-volume-range, the resolution is 1/256 dB,
 * so subtracting 0x1 == subtracting 1/256 dB in 'real'-volume-range
 */
#define USB_COMPENSATED_MIN_DB   ((int8_t)-128)
#define USB_COMPENSATED_MAX_DB   ((uint8_t)128)
#define USB_VOLUME_COMPENSATION  (0x1)

/*
 * USB minimum value
 * (0x8001 == -127.9961 dB)
 */
#define USB_MIN_VAL  (0x8001)

/*
 * USB volume range uses 1/256 dB resolution,
 * so shifting by 8 converts a db-Value to the USB resolution of 1/256 dB */
#define CONVERT_DB_TO_USB_RANGE(_vol)  ((_vol) << 8)

uint16_t USB_VolumeConversion_ConvertVolumeToUsb(int8_t volumeInDb)
{
    /*
     * Conversion algorithm only works for values [-127,127].
     * Besides, USB_COMPENSATED_MIN_DB(==-128) is not a valid value for USB,
     * so use the minimal value for usb (0x8001 == -127.9961 dB)
     */
    if (USB_COMPENSATED_MIN_DB == volumeInDb)
        return USB_MIN_VAL;

    /* Absolute difference between our volume and the compensated USB db-minimum */
    uint16_t dbDiffBetweenVolAndUsbMin = volumeInDb - USB_COMPENSATED_MIN_DB;
    uint16_t diffInUsbRange = CONVERT_DB_TO_USB_RANGE(dbDiffBetweenVolAndUsbMin);

    /* Subtract USB_VOLUME_COMPENSATION(0x1) to compensate for using approximation of USB min/max value */
    return USB_MIN_VAL + diffInUsbRange - USB_VOLUME_COMPENSATION;
}

uint16_t USB_VolumeConversion_ConvertResolution(uint8_t resolutionInDb)
{
    if (resolutionInDb >= USB_COMPENSATED_MAX_DB)
        return 0x7fff;

    /* Return minimum setting if zero is supplied */
    if (0 == resolutionInDb)
        return 0x1;

    return CONVERT_DB_TO_USB_RANGE(resolutionInDb);
}

#endif
