/* 
 | 
 * BlueALSA - utils.c 
 | 
 * Copyright (c) 2016-2018 Arkadiusz Bokowy 
 | 
 * 
 | 
 * This file is a part of bluez-alsa. 
 | 
 * 
 | 
 * This project is licensed under the terms of the MIT license. 
 | 
 * 
 | 
 */ 
 | 
  
 | 
#include "utils.h" 
 | 
  
 | 
#include <errno.h> 
 | 
#include <stdlib.h> 
 | 
#include <string.h> 
 | 
  
 | 
#include <bluetooth/bluetooth.h> 
 | 
#include <bluetooth/hci_lib.h> 
 | 
#include <bluetooth/sco.h> 
 | 
  
 | 
#if ENABLE_LDAC 
 | 
# include "ldacBT.h" 
 | 
#endif 
 | 
  
 | 
#include "a2dp-codecs.h" 
 | 
#include "bluez.h" 
 | 
#include "shared/log.h" 
 | 
  
 | 
  
 | 
/** 
 | 
 * Calculate the optimum bitpool for given parameters. 
 | 
 * 
 | 
 * @param freq Sampling frequency. 
 | 
 * @param model Channel mode. 
 | 
 * @return Coded SBC bitpool. */ 
 | 
int a2dp_sbc_default_bitpool(int freq, int mode) { 
 | 
    switch (freq) { 
 | 
    case SBC_SAMPLING_FREQ_16000: 
 | 
    case SBC_SAMPLING_FREQ_32000: 
 | 
        return 53; 
 | 
    case SBC_SAMPLING_FREQ_44100: 
 | 
        switch (mode) { 
 | 
        case SBC_CHANNEL_MODE_MONO: 
 | 
        case SBC_CHANNEL_MODE_DUAL_CHANNEL: 
 | 
            return 31; 
 | 
        case SBC_CHANNEL_MODE_STEREO: 
 | 
        case SBC_CHANNEL_MODE_JOINT_STEREO: 
 | 
            return 53; 
 | 
        default: 
 | 
            warn("Invalid channel mode: %u", mode); 
 | 
            return 53; 
 | 
        } 
 | 
    case SBC_SAMPLING_FREQ_48000: 
 | 
        switch (mode) { 
 | 
        case SBC_CHANNEL_MODE_MONO: 
 | 
        case SBC_CHANNEL_MODE_DUAL_CHANNEL: 
 | 
            return 29; 
 | 
        case SBC_CHANNEL_MODE_STEREO: 
 | 
        case SBC_CHANNEL_MODE_JOINT_STEREO: 
 | 
            return 51; 
 | 
        default: 
 | 
            warn("Invalid channel mode: %u", mode); 
 | 
            return 51; 
 | 
        } 
 | 
    default: 
 | 
        warn("Invalid sampling freq: %u", freq); 
 | 
        return 53; 
 | 
    } 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Get the list of all available HCI controllers. 
 | 
 * 
 | 
 * @param di The address to the device info structure pointer, where the list 
 | 
 *     of all available devices will be stored. Allocated memory should be freed 
 | 
 *     with the free(). 
 | 
 * @param num The address, where the number of initialized device structures 
 | 
 *     will be stored. 
 | 
 * @return On success this function returns 0. Otherwise, -1 is returned and 
 | 
 *     errno is set to indicate the error. */ 
 | 
int hci_devlist(struct hci_dev_info **di, int *num) { 
 | 
  
 | 
    int i; 
 | 
  
 | 
    if ((*di = malloc(HCI_MAX_DEV * sizeof(**di))) == NULL) 
 | 
        return -1; 
 | 
  
 | 
    for (i = *num = 0; i < HCI_MAX_DEV; i++) 
 | 
        if (hci_devinfo(i, &(*di)[*num]) == 0) 
 | 
            (*num)++; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Open SCO link for given Bluetooth device. 
 | 
 * 
 | 
 * @param di The address to the HCI device info structure for which the SCO 
 | 
 *   link should be established. 
 | 
 * @param ba Pointer to the Bluetooth address structure for a target device. 
 | 
 * @param transparent Use transparent mode for voice transmission. 
 | 
 * @return On success this function returns socket file descriptor. Otherwise, 
 | 
 *   -1 is returned and errno is set to indicate the error. */ 
 | 
int hci_open_sco(const struct hci_dev_info *di, const bdaddr_t *ba, bool transparent) { 
 | 
  
 | 
    struct sockaddr_sco addr_hci = { 
 | 
        .sco_family = AF_BLUETOOTH, 
 | 
        .sco_bdaddr = di->bdaddr, 
 | 
    }; 
 | 
    struct sockaddr_sco addr_dev = { 
 | 
        .sco_family = AF_BLUETOOTH, 
 | 
        .sco_bdaddr = *ba, 
 | 
    }; 
 | 
    int dd, err; 
 | 
  
 | 
    if ((dd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) == -1) 
 | 
        return -1; 
 | 
    if (bind(dd, (struct sockaddr *)&addr_hci, sizeof(addr_hci)) == -1) 
 | 
        goto fail; 
 | 
  
 | 
    if (transparent) { 
 | 
        struct bt_voice voice = { 
 | 
            .setting = BT_VOICE_TRANSPARENT, 
 | 
        }; 
 | 
        if (setsockopt(dd, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) == -1) 
 | 
            goto fail; 
 | 
    } 
 | 
  
 | 
    if (connect(dd, (struct sockaddr *)&addr_dev, sizeof(addr_dev)) == -1) 
 | 
        goto fail; 
 | 
  
 | 
    return dd; 
 | 
  
 | 
fail: 
 | 
    err = errno; 
 | 
    close(dd); 
 | 
    errno = err; 
 | 
    return -1; 
 | 
} 
 | 
  
 | 
int hci_submit_cmd_wait(uint16_t ogf, uint16_t ocf, uint8_t *params, 
 | 
            uint8_t plen) 
 | 
{ 
 | 
    int fd; 
 | 
    uint16_t index = 0; 
 | 
    uint8_t status; 
 | 
    int ret; 
 | 
    struct hci_request rq; 
 | 
  
 | 
    fd = hci_open_dev(index); 
 | 
    if (fd < 0) { 
 | 
        error("Couldn't open device: %s(%d)\n", strerror(errno), errno); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    memset(&rq, 0, sizeof(rq)); 
 | 
    rq.ogf = ogf; 
 | 
    rq.ocf = ocf; 
 | 
    rq.cparam = params; 
 | 
    rq.clen = plen; 
 | 
    rq.rparam = &status; 
 | 
    rq.rlen = 1; 
 | 
  
 | 
    ret = hci_send_req(fd, &rq, 1000); 
 | 
    if (status || ret < 0) { 
 | 
        error("Can't send cmd for hci%d: %s (%d)\n", index, 
 | 
                strerror(errno), errno); 
 | 
        hci_close_dev(fd); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    hci_close_dev(fd); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Get BlueZ D-Bus object path for given profile and codec. 
 | 
 * 
 | 
 * @param profile Bluetooth profile. 
 | 
 * @param codec Bluetooth profile codec. 
 | 
 * @return This function returns BlueZ D-Bus object path. */ 
 | 
const char *g_dbus_get_profile_object_path(enum bluetooth_profile profile, uint16_t codec) { 
 | 
    switch (profile) { 
 | 
    case BLUETOOTH_PROFILE_A2DP_SOURCE: 
 | 
        switch (codec) { 
 | 
        case A2DP_CODEC_SBC: 
 | 
            return "/A2DP/SBC/Source"; 
 | 
#if ENABLE_MPEG 
 | 
        case A2DP_CODEC_MPEG12: 
 | 
            return "/A2DP/MPEG12/Source"; 
 | 
#endif 
 | 
#if ENABLE_AAC 
 | 
        case A2DP_CODEC_MPEG24: 
 | 
            return "/A2DP/MPEG24/Source"; 
 | 
#endif 
 | 
#if ENABLE_APTX 
 | 
        case A2DP_CODEC_VENDOR_APTX: 
 | 
            return "/A2DP/APTX/Source"; 
 | 
#endif 
 | 
#if ENABLE_LDAC 
 | 
        case A2DP_CODEC_VENDOR_LDAC: 
 | 
            return "/A2DP/LDAC/Source"; 
 | 
#endif 
 | 
        default: 
 | 
            warn("Unsupported A2DP codec: %#x", codec); 
 | 
            return "/A2DP/Source"; 
 | 
        } 
 | 
    case BLUETOOTH_PROFILE_A2DP_SINK: 
 | 
        switch (codec) { 
 | 
        case A2DP_CODEC_SBC: 
 | 
            return "/A2DP/SBC/Sink"; 
 | 
#if ENABLE_MPEG 
 | 
        case A2DP_CODEC_MPEG12: 
 | 
            return "/A2DP/MPEG12/Sink"; 
 | 
#endif 
 | 
#if ENABLE_AAC 
 | 
        case A2DP_CODEC_MPEG24: 
 | 
            return "/A2DP/MPEG24/Sink"; 
 | 
#endif 
 | 
#if ENABLE_APTX 
 | 
        case A2DP_CODEC_VENDOR_APTX: 
 | 
            return "/A2DP/APTX/Sink"; 
 | 
#endif 
 | 
#if ENABLE_LDAC 
 | 
        case A2DP_CODEC_VENDOR_LDAC: 
 | 
            return "/A2DP/LDAC/Sink"; 
 | 
#endif 
 | 
        default: 
 | 
            warn("Unsupported A2DP codec: %#x", codec); 
 | 
            return "/A2DP/Sink"; 
 | 
        } 
 | 
    case BLUETOOTH_PROFILE_HSP_HS: 
 | 
        return "/HSP/Headset"; 
 | 
    case BLUETOOTH_PROFILE_HSP_AG: 
 | 
        return "/HSP/AudioGateway"; 
 | 
    case BLUETOOTH_PROFILE_HFP_HF: 
 | 
        return "/HFP/HandsFree"; 
 | 
    case BLUETOOTH_PROFILE_HFP_AG: 
 | 
        return "/HFP/AudioGateway"; 
 | 
    case BLUETOOTH_PROFILE_NULL: 
 | 
        break; 
 | 
    } 
 | 
    return "/"; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Convert BlueZ D-Bus object path into a Bluetooth profile. 
 | 
 * 
 | 
 * @param path BlueZ D-Bus object path. 
 | 
 * @return On success this function returns Bluetooth profile. If object 
 | 
 *   path cannot be recognize, NULL profile is returned. */ 
 | 
enum bluetooth_profile g_dbus_object_path_to_profile(const char *path) { 
 | 
    if (strncmp(path, "/A2DP", 5) == 0) { 
 | 
        if (strstr(path + 5, "/Source") != NULL) 
 | 
            return BLUETOOTH_PROFILE_A2DP_SOURCE; 
 | 
        if (strstr(path + 5, "/Sink") != NULL) 
 | 
            return BLUETOOTH_PROFILE_A2DP_SINK; 
 | 
    } 
 | 
    if (strncmp(path, "/HSP", 4) == 0) { 
 | 
        if (strcmp(path + 4, "/Headset") == 0) 
 | 
            return BLUETOOTH_PROFILE_HSP_HS; 
 | 
        if (strcmp(path + 4, "/AudioGateway") == 0) 
 | 
            return BLUETOOTH_PROFILE_HSP_AG; 
 | 
    } 
 | 
    if (strncmp(path, "/HFP", 4) == 0) { 
 | 
        if (strcmp(path + 4, "/HandsFree") == 0) 
 | 
            return BLUETOOTH_PROFILE_HFP_HF; 
 | 
        if (strcmp(path + 4, "/AudioGateway") == 0) 
 | 
            return BLUETOOTH_PROFILE_HFP_AG; 
 | 
    } 
 | 
    return BLUETOOTH_PROFILE_NULL; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Convert BlueZ D-Bus device path into a bdaddr_t structure. 
 | 
 * 
 | 
 * @param path BlueZ D-Bus device path. 
 | 
 * @param addr Address where the parsed address will be stored. 
 | 
 * @return On success this function returns 0. Otherwise, -1 is returned. */ 
 | 
int g_dbus_device_path_to_bdaddr(const char *path, bdaddr_t *addr) { 
 | 
  
 | 
    char *tmp, *p; 
 | 
    int ret; 
 | 
  
 | 
    if ((path = strrchr(path, '/')) == NULL) 
 | 
        return -1; 
 | 
    if ((path = strstr(path, "dev_")) == NULL) 
 | 
        return -1; 
 | 
    if ((tmp = strdup(path + 4)) == NULL) 
 | 
        return -1; 
 | 
  
 | 
    for (p = tmp; *p != '\0'; p++) 
 | 
        if (*p == '_') 
 | 
            *p = ':'; 
 | 
  
 | 
    ret = str2ba(tmp, addr); 
 | 
  
 | 
    free(tmp); 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Get a property of a given D-Bus interface. 
 | 
 * 
 | 
 * @param conn D-Bus connection handler. 
 | 
 * @param name Valid D-Bus name or NULL. 
 | 
 * @param path Valid D-Bus object path. 
 | 
 * @param interface Interface with the given property. 
 | 
 * @param property The property name. 
 | 
 * @return On success this function returns variant containing property value. 
 | 
 *   Otherwise, NULL is returned. */ 
 | 
GVariant *g_dbus_get_property(GDBusConnection *conn, const char *name, 
 | 
        const char *path, const char *interface, const char *property) { 
 | 
  
 | 
    GDBusMessage *msg = NULL, *rep = NULL; 
 | 
    GVariant *value = NULL; 
 | 
    GError *err = NULL; 
 | 
  
 | 
    msg = g_dbus_message_new_method_call(name, path, "org.freedesktop.DBus.Properties", "Get"); 
 | 
    g_dbus_message_set_body(msg, g_variant_new("(ss)", interface, property)); 
 | 
  
 | 
    if ((rep = g_dbus_connection_send_message_with_reply_sync(conn, msg, 
 | 
                    G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err)) == NULL) 
 | 
        goto fail; 
 | 
  
 | 
    if (g_dbus_message_get_message_type(rep) == G_DBUS_MESSAGE_TYPE_ERROR) { 
 | 
        g_dbus_message_to_gerror(rep, &err); 
 | 
        goto fail; 
 | 
    } 
 | 
  
 | 
    g_variant_get(g_dbus_message_get_body(rep), "(v)", &value); 
 | 
  
 | 
fail: 
 | 
  
 | 
    if (msg != NULL) 
 | 
        g_object_unref(msg); 
 | 
    if (rep != NULL) 
 | 
        g_object_unref(rep); 
 | 
    if (err != NULL) { 
 | 
        warn("Couldn't get property: %s", err->message); 
 | 
        g_error_free(err); 
 | 
    } 
 | 
  
 | 
    return value; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Set a property of a given D-Bus interface. 
 | 
 * 
 | 
 * @param conn D-Bus connection handler. 
 | 
 * @param name Valid D-Bus name or NULL. 
 | 
 * @param path Valid D-Bus object path. 
 | 
 * @param interface Interface with the given property. 
 | 
 * @param property The property name. 
 | 
 * @param value Variant containing property value. 
 | 
 * @return On success this function returns TRUE. Otherwise, FALSE. */ 
 | 
gboolean g_dbus_set_property(GDBusConnection *conn, const char *name, 
 | 
        const char *path, const char *interface, const char *property, 
 | 
        const GVariant *value) { 
 | 
  
 | 
    GDBusMessage *msg = NULL, *rep = NULL; 
 | 
    GError *err = NULL; 
 | 
  
 | 
    msg = g_dbus_message_new_method_call(name, path, "org.freedesktop.DBus.Properties", "Set"); 
 | 
    g_dbus_message_set_body(msg, g_variant_new("(ssv)", interface, property, value)); 
 | 
  
 | 
    if ((rep = g_dbus_connection_send_message_with_reply_sync(conn, msg, 
 | 
                    G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err)) == NULL) 
 | 
        goto fail; 
 | 
  
 | 
    if (g_dbus_message_get_message_type(rep) == G_DBUS_MESSAGE_TYPE_ERROR) { 
 | 
        g_dbus_message_to_gerror(rep, &err); 
 | 
        goto fail; 
 | 
    } 
 | 
  
 | 
fail: 
 | 
  
 | 
    if (msg != NULL) 
 | 
        g_object_unref(msg); 
 | 
    if (rep != NULL) 
 | 
        g_object_unref(rep); 
 | 
    if (err != NULL) { 
 | 
        warn("Couldn't set property: %s", err->message); 
 | 
        g_error_free(err); 
 | 
        return FALSE; 
 | 
    } 
 | 
  
 | 
    return TRUE; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Convert Bluetooth profile into a human-readable string. 
 | 
 * 
 | 
 * @param profile Bluetooth profile. 
 | 
 * @return Human-readable string. */ 
 | 
const char *bluetooth_profile_to_string(enum bluetooth_profile profile) { 
 | 
    switch (profile) { 
 | 
    case BLUETOOTH_PROFILE_NULL: 
 | 
        return "N/A"; 
 | 
    case BLUETOOTH_PROFILE_A2DP_SOURCE: 
 | 
        return "A2DP Source"; 
 | 
    case BLUETOOTH_PROFILE_A2DP_SINK: 
 | 
        return "A2DP Sink"; 
 | 
    case BLUETOOTH_PROFILE_HSP_HS: 
 | 
        return "HSP Headset"; 
 | 
    case BLUETOOTH_PROFILE_HSP_AG: 
 | 
        return "HSP Audio Gateway"; 
 | 
    case BLUETOOTH_PROFILE_HFP_HF: 
 | 
        return "HFP Hands-Free"; 
 | 
    case BLUETOOTH_PROFILE_HFP_AG: 
 | 
        return "HFP Audio Gateway"; 
 | 
    } 
 | 
    return "N/A"; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Convert Bluetooth A2DP codec into a human-readable string. 
 | 
 * 
 | 
 * @param codec Bluetooth A2DP audio codec. 
 | 
 * @return Human-readable string. */ 
 | 
const char *bluetooth_a2dp_codec_to_string(uint16_t codec) { 
 | 
    switch (codec) { 
 | 
    case A2DP_CODEC_SBC: 
 | 
        return "SBC"; 
 | 
#if ENABLE_MPEG 
 | 
    case A2DP_CODEC_MPEG12: 
 | 
        return "MPEG"; 
 | 
#endif 
 | 
#if ENABLE_AAC 
 | 
    case A2DP_CODEC_MPEG24: 
 | 
        return "AAC"; 
 | 
#endif 
 | 
#if ENABLE_APTX 
 | 
    case A2DP_CODEC_VENDOR_APTX: 
 | 
        return "APT-X"; 
 | 
#endif 
 | 
#if ENABLE_APTX_HD 
 | 
    case A2DP_CODEC_VENDOR_APTX_HD: 
 | 
        return "APT-X HD"; 
 | 
#endif 
 | 
#if ENABLE_LDAC 
 | 
    case A2DP_CODEC_VENDOR_LDAC: 
 | 
        return "LDAC"; 
 | 
#endif 
 | 
    } 
 | 
    return "N/A"; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Convert Bluetooth address into a human-readable string. 
 | 
 * 
 | 
 * This function returns statically allocated buffer. It is not by any means 
 | 
 * thread safe. This function should be used for debugging purposes only. 
 | 
 * 
 | 
 * For debugging purposes, one could use the batostr() function provided by 
 | 
 * the bluez library. However, this function converts the Bluetooth address 
 | 
 * to the string with a incorrect (reversed) bytes order... 
 | 
 * 
 | 
 * @param ba Pointer to the Bluetooth address structure. 
 | 
 * @return On success this function returns statically allocated buffer with 
 | 
 *   human-readable Bluetooth address. On error, it returns NULL. */ 
 | 
const char *batostr_(const bdaddr_t *ba) { 
 | 
    static char addr[18]; 
 | 
    if (ba2str(ba, addr) > 0) 
 | 
        return addr; 
 | 
    return NULL; 
 | 
} 
 | 
  
 | 
/** 
 | 
 * Scale PCM signal stored in the buffer. 
 | 
 * 
 | 
 * Neutral value for scaling factor is 1.0. It is possible to increase 
 | 
 * signal gain by using scaling factor values greater than 1, however 
 | 
 * clipping will most certainly occur. 
 | 
 * 
 | 
 * @param buffer Address to the buffer where the PCM signal is stored. 
 | 
 * @param size The number of samples in the buffer. 
 | 
 * @param channels The number of channels in the buffer. 
 | 
 * @param ch1_scale The scaling factor for 1st channel. 
 | 
 * @param ch1_scale The scaling factor for 2nd channel. */ 
 | 
void snd_pcm_scale_s16le(int16_t *buffer, size_t size, int channels, 
 | 
        double ch1_scale, double ch2_scale) { 
 | 
    switch (channels) { 
 | 
    case 1: 
 | 
        if (ch1_scale != 1.0) 
 | 
            while (size--) 
 | 
                buffer[size] = buffer[size] * ch1_scale; 
 | 
        break; 
 | 
    case 2: 
 | 
        if (ch1_scale != 1.0 || ch2_scale != 1.0) 
 | 
            while (size--) { 
 | 
                double scale = size % 2 == 0 ? ch1_scale : ch2_scale; 
 | 
                buffer[size] = buffer[size] * scale; 
 | 
            } 
 | 
        break; 
 | 
    } 
 | 
} 
 | 
  
 | 
#if ENABLE_AAC 
 | 
/** 
 | 
 * Get string representation of the FDK-AAC decoder error code. 
 | 
 * 
 | 
 * @param error FDK-AAC decoder error code. 
 | 
 * @return Human-readable string. */ 
 | 
const char *aacdec_strerror(AAC_DECODER_ERROR err) { 
 | 
    switch (err) { 
 | 
    case AAC_DEC_OK: 
 | 
        return "Success"; 
 | 
    case AAC_DEC_OUT_OF_MEMORY: 
 | 
        return "Out of memory"; 
 | 
    case AAC_DEC_TRANSPORT_SYNC_ERROR: 
 | 
        return "Transport sync error"; 
 | 
    case AAC_DEC_NOT_ENOUGH_BITS: 
 | 
        return "Not enough bits"; 
 | 
    case AAC_DEC_INVALID_HANDLE: 
 | 
        return "Invalid handle"; 
 | 
    case AAC_DEC_UNSUPPORTED_AOT: 
 | 
        return "Unsupported AOT"; 
 | 
    case AAC_DEC_UNSUPPORTED_FORMAT: 
 | 
        return "Unsupported format"; 
 | 
    case AAC_DEC_UNSUPPORTED_ER_FORMAT: 
 | 
        return "Unsupported ER format"; 
 | 
    case AAC_DEC_UNSUPPORTED_EPCONFIG: 
 | 
        return "Unsupported EP format"; 
 | 
    case AAC_DEC_UNSUPPORTED_MULTILAYER: 
 | 
        return "Unsupported multilayer"; 
 | 
    case AAC_DEC_UNSUPPORTED_CHANNELCONFIG: 
 | 
        return "Unsupported channels"; 
 | 
    case AAC_DEC_UNSUPPORTED_SAMPLINGRATE: 
 | 
        return "Unsupported sample rate"; 
 | 
    case AAC_DEC_INVALID_SBR_CONFIG: 
 | 
        return "Unsupported SBR"; 
 | 
    case AAC_DEC_SET_PARAM_FAIL: 
 | 
        return "Unsupported parameter"; 
 | 
    case AAC_DEC_NEED_TO_RESTART: 
 | 
        return "Restart required"; 
 | 
    case AAC_DEC_TRANSPORT_ERROR: 
 | 
        return "Transport error"; 
 | 
    case AAC_DEC_PARSE_ERROR: 
 | 
        return "Parse error"; 
 | 
    case AAC_DEC_UNSUPPORTED_EXTENSION_PAYLOAD: 
 | 
        return "Unsupported extension payload"; 
 | 
    case AAC_DEC_DECODE_FRAME_ERROR: 
 | 
        return "Bitstream corrupted"; 
 | 
    case AAC_DEC_CRC_ERROR: 
 | 
        return "CRC mismatch"; 
 | 
    case AAC_DEC_INVALID_CODE_BOOK: 
 | 
        return "Invalid codebook"; 
 | 
    case AAC_DEC_UNSUPPORTED_PREDICTION: 
 | 
        return "Unsupported prediction"; 
 | 
    case AAC_DEC_UNSUPPORTED_CCE: 
 | 
        return "Unsupported CCE"; 
 | 
    case AAC_DEC_UNSUPPORTED_LFE: 
 | 
        return "Unsupported LFE"; 
 | 
    case AAC_DEC_UNSUPPORTED_GAIN_CONTROL_DATA: 
 | 
        return "Unsupported gain control data"; 
 | 
    case AAC_DEC_UNSUPPORTED_SBA: 
 | 
        return "Unsupported SBA"; 
 | 
    case AAC_DEC_TNS_READ_ERROR: 
 | 
        return "TNS read error"; 
 | 
    case AAC_DEC_RVLC_ERROR: 
 | 
        return "RVLC decode error"; 
 | 
    case AAC_DEC_ANC_DATA_ERROR: 
 | 
        return "Ancillary data error"; 
 | 
    case AAC_DEC_TOO_SMALL_ANC_BUFFER: 
 | 
        return "Too small ancillary buffer"; 
 | 
    case AAC_DEC_TOO_MANY_ANC_ELEMENTS: 
 | 
        return "Too many ancillary elements"; 
 | 
    default: 
 | 
        debug("Unknown error code: %#x", err); 
 | 
        return "Unknown error"; 
 | 
    } 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if ENABLE_AAC 
 | 
/** 
 | 
 * Get string representation of the FDK-AAC encoder error code. 
 | 
 * 
 | 
 * @param error FDK-AAC encoder error code. 
 | 
 * @return Human-readable string. */ 
 | 
const char *aacenc_strerror(AACENC_ERROR err) { 
 | 
    switch (err) { 
 | 
    case AACENC_OK: 
 | 
        return "Success"; 
 | 
    case AACENC_INVALID_HANDLE: 
 | 
        return "Invalid handle"; 
 | 
    case AACENC_MEMORY_ERROR: 
 | 
        return "Out of memory"; 
 | 
    case AACENC_UNSUPPORTED_PARAMETER: 
 | 
        return "Unsupported parameter"; 
 | 
    case AACENC_INVALID_CONFIG: 
 | 
        return "Invalid config"; 
 | 
    case AACENC_INIT_ERROR: 
 | 
        return "Initialization error"; 
 | 
    case AACENC_INIT_AAC_ERROR: 
 | 
        return "AAC library initialization error"; 
 | 
    case AACENC_INIT_SBR_ERROR: 
 | 
        return "SBR library initialization error"; 
 | 
    case AACENC_INIT_TP_ERROR: 
 | 
        return "Transport library initialization error"; 
 | 
    case AACENC_INIT_META_ERROR: 
 | 
        return "Metadata library initialization error"; 
 | 
    case AACENC_ENCODE_ERROR: 
 | 
        return "Encoding error"; 
 | 
    case AACENC_ENCODE_EOF: 
 | 
        return "End of file"; 
 | 
    default: 
 | 
        debug("Unknown error code: %#x", err); 
 | 
        return "Unknown error"; 
 | 
    } 
 | 
} 
 | 
#endif 
 | 
  
 | 
#if ENABLE_LDAC 
 | 
/** 
 | 
 * Get string representation of the LDAC error code. 
 | 
 * 
 | 
 * @param error LDAC error code. 
 | 
 * @return Human-readable string. */ 
 | 
const char *ldacBT_strerror(int err) { 
 | 
    switch (LDACBT_API_ERR(err)) { 
 | 
    case LDACBT_ERR_NONE: 
 | 
        return "Success"; 
 | 
    case LDACBT_ERR_ASSERT_SAMPLING_FREQ: 
 | 
    case LDACBT_ERR_ASSERT_SUP_SAMPLING_FREQ: 
 | 
    case LDACBT_ERR_CHECK_SAMPLING_FREQ: 
 | 
        return "Invalid sample rate"; 
 | 
    case LDACBT_ERR_ASSERT_CHANNEL_CONFIG: 
 | 
    case LDACBT_ERR_CHECK_CHANNEL_CONFIG: 
 | 
        return "Invalid channel config"; 
 | 
    case LDACBT_ERR_ASSERT_FRAME_LENGTH: 
 | 
    case LDACBT_ERR_ASSERT_SUP_FRAME_LENGTH: 
 | 
    case LDACBT_ERR_ASSERT_FRAME_STATUS: 
 | 
        return "Invalid frame status"; 
 | 
    case LDACBT_ERR_ASSERT_NSHIFT: 
 | 
        return "Invalid N-shift"; 
 | 
    case LDACBT_ERR_ASSERT_CHANNEL_MODE: 
 | 
        return "Invalid channel mode"; 
 | 
    case LDACBT_ERR_ALTER_EQMID_LIMITED: 
 | 
        return "EQMID limited"; 
 | 
    case LDACBT_ERR_HANDLE_NOT_INIT: 
 | 
        return "Invalid handle"; 
 | 
    case LDACBT_ERR_ILL_EQMID: 
 | 
        return "Unsupported EQMID"; 
 | 
    case LDACBT_ERR_ILL_SAMPLING_FREQ: 
 | 
        return "Unsupported sample rate"; 
 | 
    case LDACBT_ERR_ILL_NUM_CHANNEL: 
 | 
        return "Unsupported channels"; 
 | 
    case LDACBT_ERR_ILL_MTU_SIZE: 
 | 
        return "Unsupported MTU"; 
 | 
    default: 
 | 
        debug("Unknown error code: %#x", err); 
 | 
        return "Unknown error"; 
 | 
    } 
 | 
} 
 | 
#endif 
 |