/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2010 Yoshihisa Uchida
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "codeclib.h"
#include "adpcm_seek.h"
#include "support_formats.h"

/*
 * Dialogic OKI ADPCM
 *
 * References
 * [1] Dialogic Corporation, Dialogic ADPCM Algorithm, 1988
 * [2] MultimediaWiki, Dialogic IMA ADPCM, URL:http://wiki.multimedia.cx/index.php?title=Dialogic_IMA_ADPCM
 * [3] sox source code, src/adpcms.c
 * [4] Tetsuya Isaki, NetBSD:/sys/dev/audio.c, http://www.tri-tree.gr.jp/~isaki/NetBSD/src/sys/dev/ic/msm6258.c.html
 */

static const uint16_t step_table[] ICONST_ATTR = {
    16,  17,   19,   21,   23,   25,   28,  31,  34,  37,  41,  45,  50,  55,
    60,  66,   73,   80,   88,   97,  107, 118, 130, 143, 157, 173, 190, 209,
   230, 253,  279,  307,  337,  371,  408, 449, 494, 544, 598, 658, 724, 796,
   876, 963, 1060, 1166, 1282, 1411, 1552,
};

static const int index_table[] ICONST_ATTR = {
    -1, -1, -1, -1, 2, 4, 6, 8
};

static struct adpcm_data cur_data;
static int blocksperchunk;

static struct pcm_format *fmt;

static bool set_format(struct pcm_format *format)
{
    uint32_t max_chunk_count;

    fmt = format;

    if (fmt->bitspersample != 4)
    {
        DEBUGF("CODEC_ERROR: dialogic oki adpcm must be 4 bitspersample: %d\n",
                             fmt->bitspersample);
        return false;
    }

    if (fmt->channels != 1)
    {
        DEBUGF("CODEC_ERROR: dialogic oki adpcm must be monaural\n");
        return false;
    }

    /* blockalign = 2 samples */
    fmt->blockalign = 1;
    fmt->samplesperblock = 2;

    /* chunksize = about 1/32[sec] data */
    blocksperchunk = ci->id3->frequency >> 6;
    fmt->chunksize = blocksperchunk * fmt->blockalign;

    max_chunk_count = (uint64_t)ci->id3->length * ci->id3->frequency
                                         / (2000LL * fmt->chunksize);

    /* initialize seek table */
    init_seek_table(max_chunk_count);
    /* add first data */
    add_adpcm_data(&cur_data);

    return true;
}

static int16_t create_pcmdata(uint8_t nibble)
{
    int16_t delta;
    int16_t index = cur_data.step[0];
    int16_t step = step_table[index];

    delta = (step >> 3);
    if (nibble & 4) delta += step;
    if (nibble & 2) delta += (step >> 1);
    if (nibble & 1) delta += (step >> 2);

    if (nibble & 0x08)
        cur_data.pcmdata[0] -= delta;
    else
        cur_data.pcmdata[0] += delta;

    CLIP(cur_data.pcmdata[0], -2048, 2047);

    index += index_table[nibble & 0x07];
    CLIP(index, 0, 48);
    cur_data.step[0] = index;

    return cur_data.pcmdata[0];
}

static int decode(const uint8_t *inbuf, size_t inbufsize,
                  int32_t *outbuf, int *outbufcount)
{
    size_t nsamples = 0;

    while (inbufsize)
    {
        *outbuf++ = create_pcmdata(*inbuf >> 4) << (PCM_OUTPUT_DEPTH - 12);
        *outbuf++ = create_pcmdata(*inbuf     ) << (PCM_OUTPUT_DEPTH - 12);
        nsamples += 2;

        inbuf++;
        inbufsize--;
    }

    *outbufcount = nsamples;
    add_adpcm_data(&cur_data);

    return CODEC_OK;
}

static int decode_for_seek(const uint8_t *inbuf, size_t inbufsize)
{
    while (inbufsize)
    {
        create_pcmdata(*inbuf >> 4);
        create_pcmdata(*inbuf     );

        inbuf++;
        inbufsize--;
    }

    add_adpcm_data(&cur_data);

    return CODEC_OK;
}

static struct pcm_pos *get_seek_pos(uint32_t seek_val, int seek_mode,
                                    uint8_t *(*read_buffer)(size_t *realsize))
{
    static struct pcm_pos newpos;
    uint32_t seek_count = (seek_mode == PCM_SEEK_TIME)?
                          ((uint64_t)seek_val * ci->id3->frequency / 1000LL)
                                              / (blocksperchunk * fmt->samplesperblock) :
                          seek_val / (unsigned long)fmt->chunksize;
    uint32_t new_count  = seek(seek_count, &cur_data, read_buffer, &decode_for_seek);

    newpos.pos     = new_count * fmt->chunksize;
    newpos.samples = new_count * blocksperchunk * fmt->samplesperblock;
    return &newpos;
}

static const struct pcm_codec codec = {
                                          set_format,
                                          get_seek_pos,
                                          decode,
                                      };

const struct pcm_codec *get_dialogic_oki_adpcm_codec(void)
{
    /*
     * initialize first pcm data, step index
     * because the dialogic oki adpcm is always monaural,
     * pcmdata[1], step[1] do not use.
     */
    cur_data.pcmdata[0] = 0;
    cur_data.step[0]    = 0;

    return &codec;
}
