diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libpcm/yamaha_adpcm.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libpcm/yamaha_adpcm.c')
-rw-r--r-- | lib/rbcodec/codecs/libpcm/yamaha_adpcm.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libpcm/yamaha_adpcm.c b/lib/rbcodec/codecs/libpcm/yamaha_adpcm.c new file mode 100644 index 0000000000..c67fe7524a --- /dev/null +++ b/lib/rbcodec/codecs/libpcm/yamaha_adpcm.c | |||
@@ -0,0 +1,250 @@ | |||
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 "adpcm_seek.h" | ||
23 | #include "support_formats.h" | ||
24 | |||
25 | /* | ||
26 | * YAMAHA ADPCM | ||
27 | * | ||
28 | * References | ||
29 | * [1] YAMAHA, YAMAHA ADPCM ACM Driver Version 1.0.0.0, 2005 | ||
30 | * [2] BlendWorks, YM2608 ADPCM, | ||
31 | * http://web.archive.org/web/20050208190547/www.memb.jp/~dearna/ma/ym2608/adpcm.html | ||
32 | * [3] Naoyuki Sawa, ADPCM no shikumi #1, | ||
33 | * http://www.piece-me.org/piece-lab/adpcm/adpcm1.html | ||
34 | * [4] ffmpeg source code, libavcodec/adpcm.c | ||
35 | */ | ||
36 | |||
37 | /* ADPCM data block layout | ||
38 | * | ||
39 | * when the block header exists. (for example, encoding by YAMAHA ADPCM ACM Driver) | ||
40 | * blockAlign = (frequency / 60 + 4) * channels. | ||
41 | * | ||
42 | * block | ||
43 | * <Mono> (channels = 1) | ||
44 | * int16_t first value (Little endian) | ||
45 | * uint16_t first predictor (Little endian) | ||
46 | * uint8_t ADPCM data (1st data: 0-3 bit, 2nd data: 4-7 bit) | ||
47 | * .... | ||
48 | * | ||
49 | * <Stereo> (channels = 2) | ||
50 | * int16_t Left channel first value (Little endian) | ||
51 | * uint16_t Left channel first predictor (Little endian) | ||
52 | * int16_t Right channel first value (Little endian) | ||
53 | * uint16_t Right channel first predictor (Little endian) | ||
54 | * uint8_t ADPCM data (Left channel: 0-3 bit, Right channel: 4-7 bit) | ||
55 | * .... | ||
56 | * | ||
57 | * when the block header does not exist. (for example, encoding by ffmpeg) | ||
58 | * blockAlign = 8000 | ||
59 | * | ||
60 | * block | ||
61 | * <Mono> (channels = 1) | ||
62 | * uint8_t ADPCM data (1st data: 0-3 bit, 2nd data: 4-7 bit) | ||
63 | * .... | ||
64 | * | ||
65 | * <Stereo> (channels = 2) | ||
66 | * uint8_t ADPCM data (Left channel: 0-3 bit, Right channel: 4-7 bit) | ||
67 | * .... | ||
68 | */ | ||
69 | |||
70 | static const int32_t amplification_table[] ICONST_ATTR = { | ||
71 | 230, 230, 230, 230, 307, 409, 512, 614, 230, 230, 230, 230, 307, 409, 512, 614 | ||
72 | }; | ||
73 | |||
74 | static bool has_block_header = false; | ||
75 | |||
76 | static struct adpcm_data cur_data; | ||
77 | static int blocksperchunk; | ||
78 | |||
79 | static struct pcm_format *fmt; | ||
80 | |||
81 | static bool set_format(struct pcm_format *format) | ||
82 | { | ||
83 | fmt = format; | ||
84 | |||
85 | if (fmt->channels == 0) | ||
86 | { | ||
87 | DEBUGF("CODEC_ERROR: channels is 0\n"); | ||
88 | return false; | ||
89 | } | ||
90 | |||
91 | if (fmt->bitspersample != 4) | ||
92 | { | ||
93 | DEBUGF("CODEC_ERROR: yamaha adpcm must be 4 bitspersample: %d\n", | ||
94 | fmt->bitspersample); | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | /* check exists block header */ | ||
99 | if (fmt->blockalign == ((ci->id3->frequency / 60) + 4) * fmt->channels) | ||
100 | { | ||
101 | has_block_header = true; | ||
102 | |||
103 | /* chunksize = about 1/30 [sec] data */ | ||
104 | fmt->chunksize = fmt->blockalign; | ||
105 | blocksperchunk = 1; | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | uint32_t max_chunk_count; | ||
110 | |||
111 | has_block_header = false; | ||
112 | |||
113 | /* blockalign = 2 * channels samples */ | ||
114 | fmt->blockalign = fmt->channels; | ||
115 | fmt->samplesperblock = 2; | ||
116 | |||
117 | /* chunksize = about 1/32[sec] data */ | ||
118 | blocksperchunk = ci->id3->frequency >> 6; | ||
119 | fmt->chunksize = blocksperchunk * fmt->blockalign; | ||
120 | |||
121 | max_chunk_count = (uint64_t)ci->id3->length * ci->id3->frequency | ||
122 | / (2000LL * fmt->chunksize / fmt->channels); | ||
123 | |||
124 | /* initialize seek table */ | ||
125 | init_seek_table(max_chunk_count); | ||
126 | /* add first data */ | ||
127 | add_adpcm_data(&cur_data); | ||
128 | } | ||
129 | |||
130 | return true; | ||
131 | } | ||
132 | |||
133 | static int16_t create_pcmdata(int ch, uint8_t nibble) | ||
134 | { | ||
135 | int32_t tmp_pcmdata = cur_data.pcmdata[ch]; | ||
136 | int32_t step = cur_data.step[ch]; | ||
137 | int32_t delta = step >> 3; | ||
138 | |||
139 | if (nibble & 4) delta += step; | ||
140 | if (nibble & 2) delta += (step >> 1); | ||
141 | if (nibble & 1) delta += (step >> 2); | ||
142 | |||
143 | if (nibble & 0x08) | ||
144 | tmp_pcmdata -= delta; | ||
145 | else | ||
146 | tmp_pcmdata += delta; | ||
147 | |||
148 | CLIP(tmp_pcmdata, -32768, 32767); | ||
149 | cur_data.pcmdata[ch] = tmp_pcmdata; | ||
150 | |||
151 | step = (step * amplification_table[nibble & 0x07]) >> 8; | ||
152 | CLIP(step, 127, 24576); | ||
153 | cur_data.step[ch] = step; | ||
154 | |||
155 | return cur_data.pcmdata[ch]; | ||
156 | } | ||
157 | |||
158 | static int decode(const uint8_t *inbuf, size_t inbufsize, | ||
159 | int32_t *outbuf, int *outbufcount) | ||
160 | { | ||
161 | int ch; | ||
162 | size_t nsamples = 0; | ||
163 | |||
164 | /* read block header */ | ||
165 | if (has_block_header) | ||
166 | { | ||
167 | for (ch = 0; ch < fmt->channels; ch++) | ||
168 | { | ||
169 | cur_data.pcmdata[ch] = inbuf[0] | (SE(inbuf[1]) << 8); | ||
170 | cur_data.step[ch] = inbuf[2] | (inbuf[3] << 8); | ||
171 | |||
172 | inbuf += 4; | ||
173 | inbufsize -= 4; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /* read block data */ | ||
178 | ch = fmt->channels - 1; | ||
179 | while (inbufsize) | ||
180 | { | ||
181 | *outbuf++ = create_pcmdata(0, *inbuf ) << (PCM_OUTPUT_DEPTH - 16); | ||
182 | *outbuf++ = create_pcmdata(ch, *inbuf >> 4) << (PCM_OUTPUT_DEPTH - 16); | ||
183 | nsamples += 2; | ||
184 | |||
185 | inbuf++; | ||
186 | inbufsize--; | ||
187 | } | ||
188 | |||
189 | if (fmt->channels == 2) | ||
190 | nsamples >>= 1; | ||
191 | *outbufcount = nsamples; | ||
192 | |||
193 | if (!has_block_header) | ||
194 | add_adpcm_data(&cur_data); | ||
195 | |||
196 | return CODEC_OK; | ||
197 | } | ||
198 | |||
199 | static int decode_for_seek(const uint8_t *inbuf, size_t inbufsize) | ||
200 | { | ||
201 | int ch = fmt->channels - 1; | ||
202 | |||
203 | while (inbufsize) | ||
204 | { | ||
205 | create_pcmdata(0, *inbuf ); | ||
206 | create_pcmdata(ch, *inbuf >> 4); | ||
207 | |||
208 | inbuf++; | ||
209 | inbufsize--; | ||
210 | } | ||
211 | |||
212 | add_adpcm_data(&cur_data); | ||
213 | |||
214 | return CODEC_OK; | ||
215 | } | ||
216 | |||
217 | static struct pcm_pos *get_seek_pos(uint32_t seek_val, int seek_mode, | ||
218 | uint8_t *(*read_buffer)(size_t *realsize)) | ||
219 | { | ||
220 | static struct pcm_pos newpos; | ||
221 | uint32_t new_count = (seek_mode == PCM_SEEK_TIME)? | ||
222 | ((uint64_t)seek_val * ci->id3->frequency / 1000LL) | ||
223 | / (blocksperchunk * fmt->samplesperblock) : | ||
224 | seek_val / (unsigned long)fmt->chunksize; | ||
225 | |||
226 | if (!has_block_header) | ||
227 | { | ||
228 | new_count = seek(new_count, &cur_data, read_buffer, &decode_for_seek); | ||
229 | } | ||
230 | newpos.pos = new_count * fmt->chunksize; | ||
231 | newpos.samples = new_count * blocksperchunk * fmt->samplesperblock; | ||
232 | return &newpos; | ||
233 | } | ||
234 | |||
235 | static struct pcm_codec codec = { | ||
236 | set_format, | ||
237 | get_seek_pos, | ||
238 | decode, | ||
239 | }; | ||
240 | |||
241 | const struct pcm_codec *get_yamaha_adpcm_codec(void) | ||
242 | { | ||
243 | /* initialize first step, pcm data */ | ||
244 | cur_data.pcmdata[0] = 0; | ||
245 | cur_data.pcmdata[1] = 0; | ||
246 | cur_data.step[0] = 127; | ||
247 | cur_data.step[1] = 127; | ||
248 | |||
249 | return &codec; | ||
250 | } | ||