/*
 * audio_parser.c, helper parser for audio codec data
 *
 * Copyright (c) 2009-2010 Wind River Systems, Inc.
 *
 * 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.
 */

#include <endian.h>

//#define LOG_NDEBUG 0

#define LOG_TAG "audio_parser"
#include <log.h>

/*
 * MP3
 */

struct mp3_frame_header_s {
    union {
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
        struct {
            unsigned int
                emphasis : 2,
                original : 1,
                copyright : 1,
                mode_extension : 2,
                channel_mode : 2,
                private_bit : 1,
                padding_bit : 1,
                samplingrate_index : 2,
                bitrate_index : 4,
                protected : 1,
                layer_index : 2,
                version_index : 2,
                sync : 11;
        };
        struct {
            unsigned char h0, h1, h2, h3;
        };
#elif (__BYTE_ORDER == __BIG_ENDIAN)
        struct {
            unsigned int
                sync : 11,
                version_index : 2,
                layer_index : 2,
                protected : 1,
                bitrate_index : 4,
                samplingrate_index : 2,
                padding_bit : 1,
                private_bit : 1,
                channel_mode : 2,
                mode_extension : 2,
                copyright : 1,
                original : 1,
                emphasis : 2;
        };
        struct {
            unsigned char h3, h2, h1, h0;
        };
#endif
    };
} __attribute__ ((packed));

/* index : sampling rate index */
static const int sampling_rate_table_version_1[4] = {
    [0] = 44100,
    [1] = 48000,
    [2] = 32000,
    [3] = 0,
};

/* index : sampling rate index */
static const int sampling_rate_table_version_2[4] = {
    [0] = 22050,
    [1] = 24000,
    [2] = 16000,
    [3] = 0,
};

/* index : sampling rate index */
static const int sampling_rate_table_version_25[4] = {
    [0] = 11025,
    [1] = 12000,
    [2] = 8000,
    [3] = 0,
};

/* index : version index */
static const int *sampling_rate_table[4] = {
    [0] = &sampling_rate_table_version_25[0],
    [1] = NULL,
    [2] = &sampling_rate_table_version_2[0],
    [3] = &sampling_rate_table_version_1[0],
};

/* index : bitrate index */
static const int bitrate_table_version_1_layer_1[16] = {
    [0] = 0,
    [1] = 32,
    [2] = 64,
    [3] = 96,
    [4] = 128,
    [5] = 160,
    [6] = 192,
    [7] = 224,
    [8] = 256,
    [9] = 288,
    [10] = 320,
    [11] = 352,
    [12] = 384,
    [13] = 416,
    [14] = 448,
    [15] = 0,
};

/* index : bitrate index */
static const int bitrate_table_version_1_layer_2[16] = {
    [0] = 0,
    [1] = 32,
    [2] = 48,
    [3] = 56,
    [4] = 64,
    [5] = 80,
    [6] = 96,
    [7] = 112,
    [8] = 128,
    [9] = 160,
    [10] = 192,
    [11] = 224,
    [12] = 256,
    [13] = 320,
    [14] = 384,
    [15] = 0,
};

/* index : bitrate index */
static const int bitrate_table_version_1_layer_3[16] = {
    [0] = 0,
    [1] = 32,
    [2] = 40,
    [3] = 48,
    [4] = 56,
    [5] = 64,
    [6] = 80,
    [7] = 96,
    [8] = 112,
    [9] = 128,
    [10] = 160,
    [11] = 192,
    [12] = 224,
    [13] = 256,
    [14] = 320,
    [15] = 0,
};

/* index : bitrate index */
static const int bitrate_table_version_2_25_layer_1[16] = {
    [0] = 0,
    [1] = 32,
    [2] = 48,
    [3] = 56,
    [4] = 64,
    [5] = 80,
    [6] = 96,
    [7] = 112,
    [8] = 128,
    [9] = 144,
    [10] = 160,
    [11] = 176,
    [12] = 192,
    [13] = 224,
    [14] = 256,
    [15] = 0,
};

/* index : bitrate index */
static const int bitrate_table_version_2_25_layer_2_3[16] = {
    [0] = 0,
    [1] = 8,
    [2] = 16,
    [3] = 24,
    [4] = 32,
    [5] = 40,
    [6] = 48,
    [7] = 56,
    [8] = 64,
    [9] = 80,
    [10] = 96,
    [11] = 112,
    [12] = 128,
    [13] = 144,
    [14] = 160,
    [15] = 0,
};

/* index : layer index */
static const int *bitrate_table_version_1[4] = {
    [0] = NULL,
    [1] = &bitrate_table_version_1_layer_3[0],
    [2] = &bitrate_table_version_1_layer_2[0],
    [3] = &bitrate_table_version_1_layer_1[0]
};

/* index : layer index */
static const int *bitrate_table_version_2_25[4] = {
    [0] = NULL,
    [1] = &bitrate_table_version_2_25_layer_2_3[0],
    [2] = &bitrate_table_version_2_25_layer_2_3[0],
    [3] = &bitrate_table_version_2_25_layer_1[0],
};

/* index : version index */
static const int **bitrate_table[4] = {
    [0] = &bitrate_table_version_2_25[0],
    [1] = NULL,
    [2] = &bitrate_table_version_2_25[0],
    [3] = &bitrate_table_version_1[0],
};

/* index : version index */
static const char *version_string[4] = {
    "MPEG Version 2.5", "reserved", "MPEG Version 2", "MPEG Version 1"
};

/* index : layer index */
static const char *layer_string[4] = {
    "reserved", "Layer III", "Layer II", "Layer I"
};

/* index : crc index */
static const char *protection_string[2] = {
    "Protected by CRC", "Not protected"
};

/* index : padding bit */
static const char *padding_string[2] = {
    "frame is not padded", "frame is padded with one extra slot"
};

/* index : channel mode */
static const char *channel_mode_string[4] = {
    "Stereo", "Joint Stereo (Stereo)", "Dual Channel (2 mono channels)",
    "Single Channel (Mono)"
};

/* index : layer index, mode extension */
static const char *mode_extention_string[4][4] = {
    [0] = {NULL, NULL, NULL, NULL},
    [1] = {"intensity:off, MS stereo:off", "intensity:on, MS stereo:off",
           "intensity:off, MS stereo:on", "intensity:on, MS stereo:on"},
    [2] = {"bands 4 to 31", "bands 8 to 31",
           "bands 12 to 31", "bands 16 to 31"},
    [3] = { "bands 4 to 31", "bands 8 to 31",
            "bands 12 to 31", "bands 16 to 31"},
};

/* index : copyright bit */
static const char *copyright_string[2] = {
    "Audio is not copyrighted", "Audio is copyrighted"
};

/* index : original bit */
static const char *original_string[2] = {
    "Copy of original media", "Original media"
};

/* index : emphasis */
static const char *emphasis_string[4] = {
    "none", "50/15 ms", "reserved", "CCIT J.17"
};

/* index : layer */
static const int one_slot_length_table[4] = {
    [0] = -1,
    [1] = 1,
    [2] = 1,
    [3] = 4,
};

static const int bitrate_coeff_table[4] = {
    [0] = -1,
    [1] = 144,
    [2] = 144,
    [3] = 12,
};

static inline int mp3_calculate_frame_length(int bitrate, int samplingrate,
                                             int layer, int extraslot)
{
    int one_slot_length;
    int coeff;
    int frame_length;

    if (layer < 1 || layer > 3)
        return -1;

    if (extraslot)
        one_slot_length = one_slot_length_table[layer];
    else
        one_slot_length = 0;

    coeff = bitrate_coeff_table[layer];

    frame_length = coeff * bitrate * 1000 / samplingrate + one_slot_length;

    /* layer I */
    if (layer == 3)
        frame_length *= 4;

    return frame_length;
}

/*
 * FIXME
 *   - It's hard coded for version 1, layer 3
 */
static inline int mp3_calculate_frame_duration(int version,
                                               int layer,
                                               int frequency)
{
    return 1152 * 1000 / frequency;
}

int mp3_header_parse(const unsigned char *buffer,
                     int *version, int *layer, int *crc, int *bitrate,
                     int *frequency, int *channel, int *mode_extension,
                     int *frame_length, int *frame_duration)
{
    const unsigned char *p = buffer;
    struct mp3_frame_header_s header;
    unsigned int version_index, layer_index, bitrate_index, samplingrate_index;
    const int *psampling_rate_table;
    const int *pbitrate_table, **ppbitrate_table;

    if (!p || !(p + 1) || !(p + 2) || !(p + 3))
        return -1;

    if (!version || !layer || !crc || !bitrate || !frequency ||
        !channel || !mode_extension)
        return -1;

    header.h0 = *(p + 3);
    header.h1 = *(p + 2);
    header.h2 = *(p + 1);
    header.h3 = *(p + 0);

    if (header.sync != 0x7ff) {
        ALOGE("cannot find sync (0x%03x)\n", header.sync);
        return -1;
    }

    version_index = header.version_index;
    layer_index = header.layer_index;
    bitrate_index = header.bitrate_index;
    samplingrate_index = header.samplingrate_index;

    if ((version_index > 0x3) || (version_index == 0x1)) {
        ALOGE("invalid version index (%d)\n", version_index);
        return -1;
    }

    if (layer_index > 0x3 || layer_index < 0x1) {
        ALOGE("invalid layer index (%d)\n", layer_index);
        return -1;
    }

    if (bitrate_index > 0xe) {
        ALOGE("invalid bitrate index (%d)\n", bitrate_index);
        return -1;
    }

    if (samplingrate_index > 0x2) {
        ALOGE("invalid sampling rate index (%d)\n", samplingrate_index);
        return -1;
    }

    psampling_rate_table = sampling_rate_table[version_index];

    ppbitrate_table = bitrate_table[version_index];
    pbitrate_table = ppbitrate_table[layer_index];

    *version = version_index;
    *layer = layer_index;
    *crc = header.protected;
    *bitrate = pbitrate_table[bitrate_index];
    *frequency = psampling_rate_table[samplingrate_index];
    *channel = header.channel_mode;
    *mode_extension = header.mode_extension;
    *frame_length = mp3_calculate_frame_length(*bitrate, *frequency,
                                               *layer, header.padding_bit);
    *frame_duration = mp3_calculate_frame_duration(*version, *layer,
                                                   *frequency);

    ALOGV("mp3 frame header\n");
    ALOGV("  sync: 0x%x\n", header.sync);
    ALOGV("  version: 0x%x, %s\n", header.version_index,
         version_string[header.version_index]);
    ALOGV("  layer: 0x%x, %s\n", header.layer_index,
         layer_string[header.layer_index]);
    ALOGV("  protection: 0x%x, %s\n", header.protected,
         protection_string[header.protected]);
    ALOGV("  bitrate: 0x%x, %u\n", header.bitrate_index, *bitrate);
    ALOGV("  sampling rate: 0x%x, %u\n", header.samplingrate_index, *frequency);
    ALOGV("  padding bit: 0x%x, %s\n", header.padding_bit,
         padding_string[header.padding_bit]);
    ALOGV("  private bit: 0x%x\n", header.private_bit);
    ALOGV("  channel mode: 0x%x, %s\n", header.channel_mode,
         channel_mode_string[header.channel_mode]);
    ALOGV("  mode extension: 0x%x, %s\n", header.mode_extension,
         mode_extention_string[header.layer_index][header.mode_extension]);
    ALOGV("  copyright: 0x%x, %s\n", header.copyright,
         copyright_string[header.copyright]);
    ALOGV("  original: 0x%x, %s\n", header.original,
         original_string[header.original]);
    ALOGV("  emphasis: 0x%x, %s\n", header.emphasis,
         emphasis_string[header.emphasis]);
    ALOGV("  frame length: %d\n", *frame_length);
    ALOGV("  frame duration: %d\n", *frame_duration);

    return 0;
}

/* end of MP3 */

/*
 * MP4
 *   FIXME
 *     - aot escape, explicit frequency
 */

struct audio_specific_config_s {
    union {
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
        struct {
            unsigned int
                extension_flag : 1,
                dependson_corecoder : 1,
                frame_length_flag : 1,
                channel_config : 4,
                frequency_index : 4,
                object_type : 5;
        };
        struct {
            unsigned char h0, h1;
        };
#elif (__BYTE_ORDER == __BIG_ENDIAN)
        struct {
            unsigned int
                object_type : 5,
                frequency_index : 4,
                channel_config : 4,
                frame_length_flag : 1,
                dependson_corecoder : 1,
                extension_flag : 1;
        };
        struct {
            unsigned char h1, h0;
        };
#endif
    };
} __attribute__ ((packed));

/* index : frequecy_index */
static const unsigned int frequency_table[16] = {
    [0] = 96000,
    [1] = 88200,
    [2] = 64000,
    [3] = 48000,
    [4] = 44100,
    [5] = 32000,
    [6] = 24000,
    [7] = 22050,
    [8] = 16000,
    [9] = 12000,
    [10] = 11025,
    [11] = 8000,
    [12] = 7350,
    [13] = 0,
    [14] = 0,
    [15] = 0, /* explicit specified ? */
};

static const char *aot_string[46] = {
    [0] = "Null",
    [1] = "AAC Main",
    [2] = "AAC LC (Low Complexity)",
    [3] = "AAC SSR (Scalable Sample Rate)",
    [4] = "AAC LTP (Long Term Prediction)",
    [5] = "SBR (Spectral Band Replication)",
    [6] = "AAC Scalable",
    [7] = "TwinVQ",
    [8] = "CELP (Code Excited Linear Prediction)",
    [9] = "HXVC (Harmonic Vector eXcitation Coding)",
    [10] = "Reserved",
    [11] = "Reserved",
    [12] = "TTSI (Text-To-Speech Interface)",
    [13] = "Main Synthesis",
    [14] = "Wavetable Synthesis",
    [15] = "General MIDI",
    [16] = "Algorithmic Synthesis and Audio Effects",
    [17] = "ER (Error Resilient) AAC LC",
    [18] = "Reserved",
    [19] = "ER AAC LTP",
    [20] = "ER AAC Scalable",
    [21] = "ER TwinVQ",
    [22] = "ER BSAC (Bit-Sliced Arithmetic Coding)",
    [23] = "ER AAC LD (Low Delay)",
    [24] = "ER CELP",
    [25] = "ER HVXC",
    [26] = "ER HILN (Harmonic and Individual Lines plus Noise)",
    [27] = "ER Parametric",
    [28] = "SSC (SinuSoidal Coding)",
    [29] = "PS (Parametric Stereo)",
    [30] = "MPEG Surround",
    [31] = "(Escape value)",
    [32] = "Layer-1",
    [33] = "Layer-2",
    [34] = "Layer-3",
    [35] = "DST (Direct Stream Transfer)",
    [36] = "ALS (Audio Lossless)",
    [37] = "SLS (Scalable LosslesS)",
    [38] = "SLS non-core",
    [39] = "ER AAC ELD (Enhanced Low Delay)",
    [40] = "SMR (Symbolic Music Representation) Simple",
    [41] = "SMR Main",
    [42] = "USAC (Unified Speech and Audio Coding) (no SBR)",
    [43] = "SAOC (Spatial Audio Object Coding)",
    [44] = "Reserved",
    [45] = "USAC",
};

/* index  = channel_index */
static const char *channel_string[16] = {
    [0] = "Defined in AOT Specifc Config",
    [1] = "front-center",
    [2] = "front-left, front-right",
    [3] = "front-center, front-left, front-right",
    [4] = "front-center, front-left, front-right, back-center",
    [5] = "front-center, front-left, front-right, back-left, back-right",
    [6] = "front-center, front-left, front-right, back-left, back-right, LFE-channel",
    [7] = "front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel",
    [8] = "Reserved",
    [9] = "Reserved",
    [10] = "Reserved",
    [11] = "Reserved",
    [12] = "Reserved",
    [13] = "Reserved",
    [14] = "Reserved",
    [15] = "Reserved",
};

int audio_specific_config_parse(const unsigned char *buffer,
                                int *aot, int *frequency, int *channel)
{
    const unsigned char *p = buffer;
    struct audio_specific_config_s config;

    if (!p || !(p + 1))
        return -1;

    if (!aot || !frequency || !channel)
        return -1;

    config.h0 = *(p + 1);
    config.h1 = *(p + 0);

    *aot = config.object_type;
    *frequency = frequency_table[config.frequency_index];
    *channel = config.channel_config;

    ALOGV("audio specific config\n");
    ALOGV("  aot: 0x%x, %s\n", config.object_type,
         aot_string[config.object_type]);
    ALOGV("  frequency: 0x%x, %u\n", config.frequency_index,
         frequency_table[config.frequency_index]);
    ALOGV("  channel: %d, %s\n", config.channel_config,
         channel_string[config.channel_config]);

    return 0;
}

int audio_specific_config_bitcoding(unsigned char *buffer,
                                    int aot, int frequency, int channel)
{
    unsigned char *p = buffer;
    struct audio_specific_config_s config;
    int i;

    if (!p)
        return -1;

    for (i = 0; i < 16; i++) {
        if ((int)frequency_table[i] == frequency) {
            frequency = i;
            break;
        }
    }
    if (i > 12)
        return -1;

    config.object_type = aot;
    config.frequency_index = frequency;
    config.channel_config = channel;

    *(p + 0) = config.h1;
    *(p + 1) = config.h0;

    ALOGV("bitfield coding for audio specific config\n");
    ALOGV("  aot : %d, %s\n", config.object_type,
         aot_string[config.object_type]);
    ALOGV("  frequency : %d\n", frequency_table[config.frequency_index]);
    ALOGV("  channel : %d, %s\n", config.channel_config,
         channel_string[config.channel_config]);

    return 0;
}

/* end of MP4 */
