diff options
Diffstat (limited to 'apps/codecs/libpcm/swf_adpcm.c')
-rw-r--r-- | apps/codecs/libpcm/swf_adpcm.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/apps/codecs/libpcm/swf_adpcm.c b/apps/codecs/libpcm/swf_adpcm.c new file mode 100644 index 0000000000..456e1cdd4c --- /dev/null +++ b/apps/codecs/libpcm/swf_adpcm.c | |||
@@ -0,0 +1,233 @@ | |||
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 "pcm_common.h" | ||
23 | #include "ima_adpcm_common.h" | ||
24 | |||
25 | /* | ||
26 | * Adobe SWF ADPCM | ||
27 | * | ||
28 | * References | ||
29 | * [1] Adobe, SWF File Format Specification Version 10, 2008 | ||
30 | * [2] Jack Jansen, adpcm.c in adpcm.zip | ||
31 | * [3] ffmpeg source code, libavcodec/adpcm.c | ||
32 | */ | ||
33 | |||
34 | /* step index table when bitspersample is 3. | ||
35 | * (when bitspersample is 2, 4, 5, step index table uses the table | ||
36 | * which is defined ima_adpcm_common.c.) | ||
37 | */ | ||
38 | static const int index_table[4] ICONST_ATTR = { | ||
39 | -1, -1, 2, 4, | ||
40 | }; | ||
41 | |||
42 | static int validity_bits = 8; | ||
43 | static bool first_block = true; | ||
44 | static int blockbits = 0; | ||
45 | static int lastbytebits = 0; | ||
46 | static bool after_seek = false; | ||
47 | |||
48 | static struct pcm_format *fmt; | ||
49 | |||
50 | static bool set_format(struct pcm_format *format) | ||
51 | { | ||
52 | fmt = format; | ||
53 | |||
54 | if (fmt->bitspersample < 2 || fmt->bitspersample > 5) | ||
55 | { | ||
56 | DEBUGF("CODEC_ERROR: swf adpcm must be 2, 3, 4 or 5 bitspersample: %d\n", | ||
57 | fmt->bitspersample); | ||
58 | return false; | ||
59 | } | ||
60 | |||
61 | if (fmt->samplesperblock == 0) | ||
62 | fmt->samplesperblock = (((fmt->blockalign << 3) - 2) / fmt->channels - 22) | ||
63 | / fmt->bitspersample + 1; | ||
64 | |||
65 | blockbits = ((fmt->samplesperblock - 1) * fmt->bitspersample + 22) * fmt->channels; | ||
66 | |||
67 | /* | ||
68 | * chunksize = about 93 [ms] data (frequency:44.1kHz, 4096 [sample/block]) | ||
69 | * chunksize changes depending upon the position of block. | ||
70 | */ | ||
71 | fmt->chunksize = (blockbits + 9) >> 3; | ||
72 | |||
73 | /* initialize for ima adpcm common functions */ | ||
74 | if (fmt->bitspersample == 3) | ||
75 | init_ima_adpcm_decoder(fmt->bitspersample, index_table); | ||
76 | else | ||
77 | init_ima_adpcm_decoder(fmt->bitspersample, NULL); | ||
78 | |||
79 | return true; | ||
80 | } | ||
81 | |||
82 | static struct pcm_pos *get_seek_pos(long seek_time, | ||
83 | uint8_t *(*read_buffer)(size_t *realsize)) | ||
84 | { | ||
85 | static struct pcm_pos newpos; | ||
86 | uint32_t chunkbits = blockbits; | ||
87 | uint32_t seekbits = (((uint64_t)seek_time * ci->id3->frequency) | ||
88 | / (1000LL * fmt->samplesperblock)) * blockbits + 2; | ||
89 | |||
90 | (void)read_buffer; | ||
91 | |||
92 | newpos.pos = seekbits >> 3; | ||
93 | newpos.samples = (((uint64_t)seek_time * ci->id3->frequency) | ||
94 | / (1000LL * fmt->samplesperblock)) | ||
95 | * fmt->samplesperblock; | ||
96 | |||
97 | if (newpos.pos == 0) | ||
98 | { | ||
99 | first_block = true; | ||
100 | lastbytebits = 0; | ||
101 | } | ||
102 | else | ||
103 | { | ||
104 | first_block = false; | ||
105 | lastbytebits = seekbits & 0x07; | ||
106 | if (lastbytebits != 0) | ||
107 | chunkbits -= (8 - lastbytebits); | ||
108 | } | ||
109 | |||
110 | /* calculates next read bytes */ | ||
111 | fmt->chunksize = (chunkbits >> 3) + (((chunkbits & 0x07) > 0)?1:0) | ||
112 | + ((lastbytebits > 0)?1:0); | ||
113 | |||
114 | after_seek = true; | ||
115 | return &newpos; | ||
116 | } | ||
117 | |||
118 | static uint8_t get_data(const uint8_t **buf, int bit) | ||
119 | { | ||
120 | uint8_t res = 0; | ||
121 | uint8_t mask = (1 << bit) - 1; | ||
122 | |||
123 | if (validity_bits >= bit) | ||
124 | { | ||
125 | validity_bits -= bit; | ||
126 | return (**buf >> validity_bits) & mask; | ||
127 | } | ||
128 | |||
129 | if (validity_bits > 0) | ||
130 | res = **buf << (bit - validity_bits); | ||
131 | |||
132 | validity_bits += 8 - bit; | ||
133 | res = (res | (*(++(*buf)) >> validity_bits)) & mask; | ||
134 | return res; | ||
135 | } | ||
136 | |||
137 | static int decode(const uint8_t *inbuf, size_t inbufsize, | ||
138 | int32_t *outbuf, int *outbufcount) | ||
139 | { | ||
140 | int ch; | ||
141 | int adpcm_code_size; | ||
142 | int count = fmt->samplesperblock; | ||
143 | int32_t init_pcmdata[2]; | ||
144 | int8_t init_index[2]; | ||
145 | static uint8_t lastbyte = 0; | ||
146 | |||
147 | (void)inbufsize; | ||
148 | |||
149 | validity_bits = 8; | ||
150 | |||
151 | /* read block header */ | ||
152 | ch = fmt->channels - 1; | ||
153 | if (first_block) | ||
154 | { | ||
155 | adpcm_code_size = get_data(&inbuf, 2) + 2; | ||
156 | if (fmt->bitspersample != adpcm_code_size) | ||
157 | { | ||
158 | DEBUGF("CODEC_ERROR: swf adpcm different adpcm code size=%d != %d\n", | ||
159 | adpcm_code_size, fmt->bitspersample); | ||
160 | return CODEC_ERROR; | ||
161 | } | ||
162 | init_pcmdata[0] = (get_data(&inbuf, 8) << 8) | get_data(&inbuf, 8); | ||
163 | |||
164 | lastbytebits = 0; | ||
165 | first_block = false; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | if (after_seek && lastbytebits > 0) | ||
170 | { | ||
171 | lastbyte = *inbuf++; | ||
172 | after_seek = false; | ||
173 | } | ||
174 | if (lastbytebits > 0) | ||
175 | init_pcmdata[0] = ((lastbyte << (8 + lastbytebits)) | | ||
176 | (get_data(&inbuf, 8) << lastbytebits) | | ||
177 | get_data(&inbuf, lastbytebits)) & 65535; | ||
178 | else | ||
179 | init_pcmdata[0] = (get_data(&inbuf, 8) << 8) | get_data(&inbuf, 8); | ||
180 | } | ||
181 | after_seek = false; | ||
182 | |||
183 | init_index[0] = get_data(&inbuf, 6); | ||
184 | if (init_pcmdata[0] > 32767) | ||
185 | init_pcmdata[0] -= 65536; | ||
186 | |||
187 | if (ch > 0) | ||
188 | { | ||
189 | init_pcmdata[1] = (get_data(&inbuf, 8) << 8) | get_data(&inbuf, 8); | ||
190 | init_index[1] = get_data(&inbuf, 6); | ||
191 | if (init_pcmdata[1] > 32767) | ||
192 | init_pcmdata[1] -= 65536; | ||
193 | } | ||
194 | |||
195 | *outbuf++ = init_pcmdata[0] << 13; | ||
196 | if (ch > 0) | ||
197 | *outbuf++ = init_pcmdata[1] << 13; | ||
198 | |||
199 | set_decode_parameters(fmt->channels, init_pcmdata, init_index); | ||
200 | |||
201 | /* read block data */ | ||
202 | while (--count > 0) | ||
203 | { | ||
204 | *outbuf++ = create_pcmdata(0, get_data(&inbuf, fmt->bitspersample)) << 13; | ||
205 | if (ch > 0) | ||
206 | *outbuf++ = create_pcmdata(ch, get_data(&inbuf, fmt->bitspersample)) << 13; | ||
207 | } | ||
208 | |||
209 | *outbufcount = fmt->samplesperblock; | ||
210 | |||
211 | lastbyte = *inbuf; | ||
212 | lastbytebits = (8 - validity_bits) & 0x07; | ||
213 | |||
214 | /* calculates next read bytes */ | ||
215 | fmt->chunksize = (blockbits - validity_bits + 7) >> 3; | ||
216 | |||
217 | return CODEC_OK; | ||
218 | } | ||
219 | |||
220 | static const struct pcm_codec codec = { | ||
221 | set_format, | ||
222 | get_seek_pos, | ||
223 | decode, | ||
224 | }; | ||
225 | |||
226 | const struct pcm_codec *get_swf_adpcm_codec(void) | ||
227 | { | ||
228 | first_block = true; | ||
229 | lastbytebits = 0; | ||
230 | after_seek = false; | ||
231 | |||
232 | return &codec; | ||
233 | } | ||