diff options
Diffstat (limited to 'lib/rbcodec/codecs/libpcm/ms_adpcm.c')
-rw-r--r-- | lib/rbcodec/codecs/libpcm/ms_adpcm.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libpcm/ms_adpcm.c b/lib/rbcodec/codecs/libpcm/ms_adpcm.c new file mode 100644 index 0000000000..a385d6c99f --- /dev/null +++ b/lib/rbcodec/codecs/libpcm/ms_adpcm.c | |||
@@ -0,0 +1,168 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Yoshihisa Uchida | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "codeclib.h" | ||
22 | #include "support_formats.h" | ||
23 | |||
24 | /* | ||
25 | * Microsoft ADPCM | ||
26 | * | ||
27 | * References | ||
28 | * [1] Microsoft, New Multimedia Data Types and Data Techniques Revision 3.0, 1994 | ||
29 | * [2] MulitimediaWiki, Microsoft ADPCM, 2006 | ||
30 | * (http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM) | ||
31 | * [3] ffmpeg source code, libavcodec/adpcm.c | ||
32 | */ | ||
33 | |||
34 | #define ADPCM_NUM_COEFF 7 | ||
35 | |||
36 | static int16_t dec_coeff[2][2]; | ||
37 | static uint16_t delta[2]; | ||
38 | static int16_t sample[2][2]; | ||
39 | |||
40 | static struct pcm_format *fmt; | ||
41 | |||
42 | static const int16_t adaptation_table[] ICONST_ATTR = { | ||
43 | 230, 230, 230, 230, 307, 409, 512, 614, | ||
44 | 768, 614, 512, 409, 307, 230, 230, 230 | ||
45 | }; | ||
46 | |||
47 | static bool set_format(struct pcm_format *format) | ||
48 | { | ||
49 | fmt = format; | ||
50 | |||
51 | if (fmt->bitspersample != 4) | ||
52 | { | ||
53 | DEBUGF("CODEC_ERROR: microsoft adpcm must be 4 bitspersample: %d\n", | ||
54 | fmt->bitspersample); | ||
55 | return false; | ||
56 | } | ||
57 | |||
58 | fmt->chunksize = fmt->blockalign; | ||
59 | |||
60 | return true; | ||
61 | } | ||
62 | |||
63 | static struct pcm_pos *get_seek_pos(uint32_t seek_val, int seek_mode, | ||
64 | uint8_t *(*read_buffer)(size_t *realsize)) | ||
65 | { | ||
66 | static struct pcm_pos newpos; | ||
67 | uint32_t newblock = (seek_mode == PCM_SEEK_TIME) ? | ||
68 | ((uint64_t)seek_val * ci->id3->frequency / 1000LL) | ||
69 | / fmt->samplesperblock : | ||
70 | seek_val / fmt->blockalign; | ||
71 | |||
72 | (void)read_buffer; | ||
73 | newpos.pos = newblock * fmt->blockalign; | ||
74 | newpos.samples = newblock * fmt->samplesperblock; | ||
75 | return &newpos; | ||
76 | } | ||
77 | |||
78 | static int16_t create_pcmdata(int ch, uint8_t nibble) | ||
79 | { | ||
80 | int32_t pcmdata; | ||
81 | |||
82 | pcmdata = (sample[ch][0] * dec_coeff[ch][0] + | ||
83 | sample[ch][1] * dec_coeff[ch][1]) / 256; | ||
84 | pcmdata += (delta[ch] * (nibble - ((nibble & 0x8) << 1))); | ||
85 | |||
86 | CLIP(pcmdata, -32768, 32767); | ||
87 | |||
88 | sample[ch][1] = sample[ch][0]; | ||
89 | sample[ch][0] = pcmdata; | ||
90 | |||
91 | delta[ch] = (adaptation_table[nibble] * delta[ch]) >> 8; | ||
92 | if (delta[ch] < 16) | ||
93 | delta[ch] = 16; | ||
94 | |||
95 | return (int16_t)pcmdata; | ||
96 | } | ||
97 | |||
98 | static int decode(const uint8_t *inbuf, size_t inbufsize, | ||
99 | int32_t *outbuf, int *outbufcount) | ||
100 | { | ||
101 | int ch; | ||
102 | size_t nsamples = 0; | ||
103 | int size = fmt->samplesperblock; | ||
104 | |||
105 | /* read block header */ | ||
106 | for (ch = 0; ch < fmt->channels; ch++) | ||
107 | { | ||
108 | if (*inbuf >= ADPCM_NUM_COEFF) | ||
109 | { | ||
110 | DEBUGF("CODEC_ERROR: microsoft adpcm illegal initial coeff=%d > 7\n", | ||
111 | *inbuf); | ||
112 | return CODEC_ERROR; | ||
113 | } | ||
114 | dec_coeff[ch][0] = fmt->coeffs[*inbuf][0]; | ||
115 | dec_coeff[ch][1] = fmt->coeffs[*inbuf][1]; | ||
116 | inbuf++; | ||
117 | } | ||
118 | |||
119 | for (ch = 0; ch < fmt->channels; ch++) | ||
120 | { | ||
121 | delta[ch] = inbuf[0] | (SE(inbuf[1]) << 8); | ||
122 | inbuf += 2; | ||
123 | } | ||
124 | |||
125 | for (ch = 0; ch < fmt->channels; ch++) | ||
126 | { | ||
127 | sample[ch][0] = inbuf[0] | (SE(inbuf[1]) << 8); | ||
128 | inbuf += 2; | ||
129 | } | ||
130 | |||
131 | for (ch = 0; ch < fmt->channels; ch++) | ||
132 | { | ||
133 | sample[ch][1] = inbuf[0] | (SE(inbuf[1]) << 8); | ||
134 | inbuf += 2; | ||
135 | } | ||
136 | |||
137 | inbufsize -= 7 * fmt->channels; | ||
138 | ch = fmt->channels - 1; | ||
139 | |||
140 | while (size-- > 0) | ||
141 | { | ||
142 | *outbuf++ = create_pcmdata(0, *inbuf >> 4 ) << (PCM_OUTPUT_DEPTH - 16); | ||
143 | *outbuf++ = create_pcmdata(ch, *inbuf & 0xf) << (PCM_OUTPUT_DEPTH - 16); | ||
144 | nsamples += 2; | ||
145 | |||
146 | inbuf++; | ||
147 | inbufsize--; | ||
148 | if (inbufsize <= 0) | ||
149 | break; | ||
150 | } | ||
151 | |||
152 | if (fmt->channels == 2) | ||
153 | nsamples >>= 1; | ||
154 | *outbufcount = nsamples; | ||
155 | |||
156 | return CODEC_OK; | ||
157 | } | ||
158 | |||
159 | static const struct pcm_codec codec = { | ||
160 | set_format, | ||
161 | get_seek_pos, | ||
162 | decode, | ||
163 | }; | ||
164 | |||
165 | const struct pcm_codec *get_ms_adpcm_codec(void) | ||
166 | { | ||
167 | return &codec; | ||
168 | } | ||