summaryrefslogtreecommitdiffstats
path: root/apps/codecs/libpcm/dialogic_oki_adpcm.c
blob: 60090aaa89d1e59b0c6a6d6b1eb94a539e0979fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/***************************************************************************
 *             __________               __   ___.
 *   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;
}