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/smaf.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/smaf.c')
-rw-r--r-- | lib/rbcodec/codecs/smaf.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/smaf.c b/lib/rbcodec/codecs/smaf.c new file mode 100644 index 0000000000..0203d1b4e3 --- /dev/null +++ b/lib/rbcodec/codecs/smaf.c | |||
@@ -0,0 +1,492 @@ | |||
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 | |||
22 | #include "codeclib.h" | ||
23 | #include "codecs/libpcm/support_formats.h" | ||
24 | |||
25 | CODEC_HEADER | ||
26 | |||
27 | /* | ||
28 | * SMAF (Synthetic music Mobile Application Format) | ||
29 | * | ||
30 | * References | ||
31 | * [1] YAMAHA Corporation, Synthetic music Mobile Application Format Ver.3.05, 2002 | ||
32 | */ | ||
33 | |||
34 | enum { | ||
35 | SMAF_AUDIO_TRACK_CHUNK = 0, /* PCM Audio Track */ | ||
36 | SMAF_SCORE_TRACK_CHUNK, /* Score Track */ | ||
37 | }; | ||
38 | |||
39 | /* SMAF supported codec formats */ | ||
40 | enum { | ||
41 | SMAF_FORMAT_UNSUPPORT = 0, /* unsupported format */ | ||
42 | SMAF_FORMAT_SIGNED_PCM, /* 2's complement PCM */ | ||
43 | SMAF_FORMAT_UNSIGNED_PCM, /* Offset Binary PCM */ | ||
44 | SMAF_FORMAT_ADPCM, /* YAMAHA ADPCM */ | ||
45 | }; | ||
46 | |||
47 | static const int support_formats[2][3] = { | ||
48 | {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_ADPCM, SMAF_FORMAT_UNSUPPORT }, | ||
49 | {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_UNSIGNED_PCM, SMAF_FORMAT_ADPCM }, | ||
50 | }; | ||
51 | |||
52 | static const struct pcm_entry pcm_codecs[] = { | ||
53 | { SMAF_FORMAT_SIGNED_PCM, get_linear_pcm_codec }, | ||
54 | { SMAF_FORMAT_UNSIGNED_PCM, get_linear_pcm_codec }, | ||
55 | { SMAF_FORMAT_ADPCM, get_yamaha_adpcm_codec }, | ||
56 | }; | ||
57 | |||
58 | #define NUM_FORMATS 3 | ||
59 | |||
60 | static const int basebits[4] = { 4, 8, 12, 16 }; | ||
61 | |||
62 | #define PCM_SAMPLE_SIZE (2048*2) | ||
63 | |||
64 | static int32_t samples[PCM_SAMPLE_SIZE] IBSS_ATTR; | ||
65 | |||
66 | static const struct pcm_codec *get_codec(uint32_t formattag) | ||
67 | { | ||
68 | int i; | ||
69 | |||
70 | for (i = 0; i < NUM_FORMATS; i++) | ||
71 | { | ||
72 | if (pcm_codecs[i].format_tag == formattag) | ||
73 | { | ||
74 | if (pcm_codecs[i].get_codec) | ||
75 | return pcm_codecs[i].get_codec(); | ||
76 | return 0; | ||
77 | } | ||
78 | } | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static unsigned int get_be32(const uint8_t *buf) | ||
83 | { | ||
84 | return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
85 | } | ||
86 | |||
87 | static int convert_smaf_channels(unsigned int ch) | ||
88 | { | ||
89 | return (ch >> 7) + 1; | ||
90 | } | ||
91 | |||
92 | static int convert_smaf_audio_format(unsigned int chunk, unsigned int audio_format) | ||
93 | { | ||
94 | int idx = (audio_format & 0x70) >> 4; | ||
95 | |||
96 | if (idx < 3) | ||
97 | return support_formats[chunk][idx]; | ||
98 | |||
99 | DEBUGF("CODEC_ERROR: unsupport audio format: %d\n", audio_format); | ||
100 | return SMAF_FORMAT_UNSUPPORT; | ||
101 | } | ||
102 | |||
103 | static int convert_smaf_audio_basebit(unsigned int basebit) | ||
104 | { | ||
105 | if (basebit < 4) | ||
106 | return basebits[basebit]; | ||
107 | |||
108 | DEBUGF("CODEC_ERROR: illegal basebit: %d\n", basebit); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static unsigned int search_chunk(const unsigned char *name, int nlen, off_t *pos) | ||
113 | { | ||
114 | const unsigned char *buf; | ||
115 | unsigned int chunksize; | ||
116 | size_t size; | ||
117 | |||
118 | while (true) | ||
119 | { | ||
120 | buf = ci->request_buffer(&size, 8); | ||
121 | if (size < 8) | ||
122 | break; | ||
123 | |||
124 | chunksize = get_be32(buf + 4); | ||
125 | ci->advance_buffer(8); | ||
126 | *pos += 8; | ||
127 | if (memcmp(buf, name, nlen) == 0) | ||
128 | return chunksize; | ||
129 | |||
130 | ci->advance_buffer(chunksize); | ||
131 | *pos += chunksize; | ||
132 | } | ||
133 | DEBUGF("CODEC_ERROR: missing '%s' chunk\n", name); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static bool parse_audio_track(struct pcm_format *fmt, unsigned int chunksize, off_t *pos) | ||
138 | { | ||
139 | const unsigned char *buf; | ||
140 | size_t size; | ||
141 | |||
142 | /* search PCM Audio Track Chunk */ | ||
143 | ci->advance_buffer(chunksize); | ||
144 | *pos += chunksize; | ||
145 | if (search_chunk("ATR", 3, pos) == 0) | ||
146 | { | ||
147 | DEBUGF("CODEC_ERROR: missing PCM Audio Track Chunk\n"); | ||
148 | return false; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * get format | ||
153 | * buf | ||
154 | * +0: Format Type | ||
155 | * +1: Sequence Type | ||
156 | * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency | ||
157 | * +3: bit 4-7: base bit | ||
158 | * +4: TimeBase_D | ||
159 | * +5: TimeBase_G | ||
160 | * | ||
161 | * Note: If PCM Audio Track does not include Sequence Data Chunk, | ||
162 | * tmp+6 is the start position of Wave Data Chunk. | ||
163 | */ | ||
164 | buf = ci->request_buffer(&size, 6); | ||
165 | if (size < 6) | ||
166 | { | ||
167 | DEBUGF("CODEC_ERROR: smaf is too small\n"); | ||
168 | return false; | ||
169 | } | ||
170 | |||
171 | fmt->formattag = convert_smaf_audio_format(SMAF_AUDIO_TRACK_CHUNK, buf[2]); | ||
172 | fmt->channels = convert_smaf_channels(buf[2]); | ||
173 | fmt->bitspersample = convert_smaf_audio_basebit(buf[3] >> 4); | ||
174 | |||
175 | /* search Wave Data Chunk */ | ||
176 | ci->advance_buffer(6); | ||
177 | *pos += 6; | ||
178 | fmt->numbytes = search_chunk("Awa", 3, pos); | ||
179 | if (fmt->numbytes == 0) | ||
180 | { | ||
181 | DEBUGF("CODEC_ERROR: missing Wave Data Chunk\n"); | ||
182 | return false; | ||
183 | } | ||
184 | |||
185 | return true; | ||
186 | } | ||
187 | |||
188 | static bool parse_score_track(struct pcm_format *fmt, off_t *pos) | ||
189 | { | ||
190 | const unsigned char *buf; | ||
191 | unsigned int chunksize; | ||
192 | size_t size; | ||
193 | |||
194 | /* parse Optional Data Chunk */ | ||
195 | buf = ci->request_buffer(&size, 13); | ||
196 | if (size < 13) | ||
197 | { | ||
198 | DEBUGF("CODEC_ERROR: smaf is too small\n"); | ||
199 | return false; | ||
200 | } | ||
201 | |||
202 | if (memcmp(buf + 5, "OPDA", 4) != 0) | ||
203 | { | ||
204 | DEBUGF("CODEC_ERROR: missing Optional Data Chunk\n"); | ||
205 | return false; | ||
206 | } | ||
207 | |||
208 | /* Optional Data Chunk size */ | ||
209 | chunksize = get_be32(buf + 9); | ||
210 | |||
211 | /* search Score Track Chunk */ | ||
212 | ci->advance_buffer(13 + chunksize); | ||
213 | *pos += (13 + chunksize); | ||
214 | if (search_chunk("MTR", 3, pos) == 0) | ||
215 | { | ||
216 | DEBUGF("CODEC_ERROR: missing Score Track Chunk\n"); | ||
217 | return false; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * search next chunk | ||
222 | * usually, next chunk ('M***') found within 40 bytes. | ||
223 | */ | ||
224 | buf = ci->request_buffer(&size, 40); | ||
225 | if (size < 40) | ||
226 | { | ||
227 | DEBUGF("CODEC_ERROR: smaf is too small\n"); | ||
228 | return false; | ||
229 | } | ||
230 | |||
231 | size = 0; | ||
232 | while (size < 40 && buf[size] != 'M') | ||
233 | size++; | ||
234 | |||
235 | if (size >= 40) | ||
236 | { | ||
237 | DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk"); | ||
238 | return false; | ||
239 | } | ||
240 | |||
241 | /* search Score Track Stream PCM Data Chunk */ | ||
242 | ci->advance_buffer(size); | ||
243 | *pos += size; | ||
244 | if (search_chunk("Mtsp", 4, pos) == 0) | ||
245 | { | ||
246 | DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk\n"); | ||
247 | return false; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * parse Score Track Stream Wave Data Chunk | ||
252 | * buf | ||
253 | * +4-7: chunk size (WaveType(3bytes) + wave data count) | ||
254 | * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit | ||
255 | * +9: frequency (MSB) | ||
256 | * +10: frequency (LSB) | ||
257 | */ | ||
258 | buf = ci->request_buffer(&size, 9); | ||
259 | if (size < 9) | ||
260 | { | ||
261 | DEBUGF("CODEC_ERROR: smaf is too small\n"); | ||
262 | return false; | ||
263 | } | ||
264 | |||
265 | if (memcmp(buf, "Mwa", 3) != 0) | ||
266 | { | ||
267 | DEBUGF("CODEC_ERROR: missing Score Track Stream Wave Data Chunk\n"); | ||
268 | return false; | ||
269 | } | ||
270 | |||
271 | fmt->formattag = convert_smaf_audio_format(SMAF_SCORE_TRACK_CHUNK, buf[8]); | ||
272 | fmt->channels = convert_smaf_channels(buf[8]); | ||
273 | fmt->bitspersample = convert_smaf_audio_basebit(buf[8] & 0xf); | ||
274 | fmt->numbytes = get_be32(buf + 4) - 3; | ||
275 | |||
276 | *pos += 11; | ||
277 | return true; | ||
278 | } | ||
279 | |||
280 | static bool parse_header(struct pcm_format *fmt, off_t *pos) | ||
281 | { | ||
282 | const unsigned char *buf; | ||
283 | unsigned int chunksize; | ||
284 | size_t size; | ||
285 | |||
286 | ci->memset(fmt, 0, sizeof(struct pcm_format)); | ||
287 | |||
288 | /* check File Chunk and Contents Info Chunk */ | ||
289 | buf = ci->request_buffer(&size, 16); | ||
290 | if (size < 16) | ||
291 | { | ||
292 | DEBUGF("CODEC_ERROR: smaf is too small\n"); | ||
293 | return false; | ||
294 | } | ||
295 | |||
296 | if ((memcmp(buf, "MMMD", 4) != 0) || (memcmp(buf + 8, "CNTI", 4) != 0)) | ||
297 | { | ||
298 | DEBUGF("CODEC_ERROR: does not smaf format\n"); | ||
299 | return false; | ||
300 | } | ||
301 | |||
302 | chunksize = get_be32(buf + 12); | ||
303 | ci->advance_buffer(16); | ||
304 | *pos = 16; | ||
305 | if (chunksize > 5) | ||
306 | { | ||
307 | if (!parse_audio_track(fmt, chunksize, pos)) | ||
308 | return false; | ||
309 | } | ||
310 | else if (!parse_score_track(fmt, pos)) | ||
311 | return false; | ||
312 | |||
313 | /* data signess (default signed) */ | ||
314 | fmt->is_signed = (fmt->formattag != SMAF_FORMAT_UNSIGNED_PCM); | ||
315 | |||
316 | /* data is always big endian */ | ||
317 | fmt->is_little_endian = false; | ||
318 | |||
319 | return true; | ||
320 | } | ||
321 | |||
322 | static struct pcm_format format; | ||
323 | static uint32_t bytesdone; | ||
324 | |||
325 | static uint8_t *read_buffer(size_t *realsize) | ||
326 | { | ||
327 | uint8_t *buffer = (uint8_t *)ci->request_buffer(realsize, format.chunksize); | ||
328 | if (bytesdone + (*realsize) > format.numbytes) | ||
329 | *realsize = format.numbytes - bytesdone; | ||
330 | bytesdone += *realsize; | ||
331 | ci->advance_buffer(*realsize); | ||
332 | return buffer; | ||
333 | } | ||
334 | |||
335 | /* this is the codec entry point */ | ||
336 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
337 | { | ||
338 | if (reason == CODEC_LOAD) { | ||
339 | /* Generic codec initialisation */ | ||
340 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
341 | } | ||
342 | |||
343 | return CODEC_OK; | ||
344 | } | ||
345 | |||
346 | /* this is called for each file to process */ | ||
347 | enum codec_status codec_run(void) | ||
348 | { | ||
349 | uint32_t decodedsamples; | ||
350 | size_t n; | ||
351 | int bufcount; | ||
352 | int endofstream; | ||
353 | uint8_t *smafbuf; | ||
354 | off_t firstblockposn; /* position of the first block in file */ | ||
355 | const struct pcm_codec *codec; | ||
356 | intptr_t param; | ||
357 | |||
358 | if (codec_init()) | ||
359 | return CODEC_ERROR; | ||
360 | |||
361 | codec_set_replaygain(ci->id3); | ||
362 | |||
363 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | ||
364 | bytesdone = ci->id3->offset; | ||
365 | |||
366 | decodedsamples = 0; | ||
367 | codec = 0; | ||
368 | |||
369 | ci->seek_buffer(0); | ||
370 | if (!parse_header(&format, &firstblockposn)) | ||
371 | { | ||
372 | return CODEC_ERROR; | ||
373 | } | ||
374 | |||
375 | codec = get_codec(format.formattag); | ||
376 | if (codec == 0) | ||
377 | { | ||
378 | DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag); | ||
379 | return CODEC_ERROR; | ||
380 | } | ||
381 | |||
382 | if (!codec->set_format(&format)) | ||
383 | { | ||
384 | return CODEC_ERROR; | ||
385 | } | ||
386 | |||
387 | /* check chunksize */ | ||
388 | if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels | ||
389 | > PCM_SAMPLE_SIZE) | ||
390 | format.chunksize = (PCM_SAMPLE_SIZE / format.blockalign) * format.blockalign; | ||
391 | if (format.chunksize == 0) | ||
392 | { | ||
393 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | ||
394 | return CODEC_ERROR; | ||
395 | } | ||
396 | |||
397 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | ||
398 | |||
399 | if (format.channels == 2) { | ||
400 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | ||
401 | } else if (format.channels == 1) { | ||
402 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
403 | } else { | ||
404 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); | ||
405 | return CODEC_ERROR; | ||
406 | } | ||
407 | |||
408 | ci->seek_buffer(firstblockposn); | ||
409 | |||
410 | /* make sure we're at the correct offset */ | ||
411 | if (bytesdone > (uint32_t) firstblockposn) | ||
412 | { | ||
413 | /* Round down to previous block */ | ||
414 | struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, | ||
415 | PCM_SEEK_POS, &read_buffer); | ||
416 | |||
417 | if (newpos->pos > format.numbytes) | ||
418 | return CODEC_OK; | ||
419 | |||
420 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | ||
421 | { | ||
422 | bytesdone = newpos->pos; | ||
423 | decodedsamples = newpos->samples; | ||
424 | } | ||
425 | } | ||
426 | else | ||
427 | { | ||
428 | /* already where we need to be */ | ||
429 | bytesdone = 0; | ||
430 | } | ||
431 | |||
432 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
433 | |||
434 | /* The main decoder loop */ | ||
435 | endofstream = 0; | ||
436 | |||
437 | while (!endofstream) { | ||
438 | enum codec_command_action action = ci->get_command(¶m); | ||
439 | |||
440 | if (action == CODEC_ACTION_HALT) | ||
441 | break; | ||
442 | |||
443 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
444 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, | ||
445 | &read_buffer); | ||
446 | |||
447 | if (newpos->pos > format.numbytes) | ||
448 | { | ||
449 | ci->set_elapsed(ci->id3->length); | ||
450 | ci->seek_complete(); | ||
451 | break; | ||
452 | } | ||
453 | |||
454 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | ||
455 | { | ||
456 | bytesdone = newpos->pos; | ||
457 | decodedsamples = newpos->samples; | ||
458 | } | ||
459 | |||
460 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
461 | ci->seek_complete(); | ||
462 | } | ||
463 | |||
464 | smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); | ||
465 | |||
466 | if (n == 0) | ||
467 | break; /* End of stream */ | ||
468 | |||
469 | if (bytesdone + n > format.numbytes) { | ||
470 | n = format.numbytes - bytesdone; | ||
471 | endofstream = 1; | ||
472 | } | ||
473 | |||
474 | if (codec->decode(smafbuf, n, samples, &bufcount) == CODEC_ERROR) | ||
475 | { | ||
476 | DEBUGF("codec error\n"); | ||
477 | return CODEC_ERROR; | ||
478 | } | ||
479 | |||
480 | ci->pcmbuf_insert(samples, NULL, bufcount); | ||
481 | |||
482 | ci->advance_buffer(n); | ||
483 | bytesdone += n; | ||
484 | decodedsamples += bufcount; | ||
485 | if (bytesdone >= format.numbytes) | ||
486 | endofstream = 1; | ||
487 | |||
488 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
489 | } | ||
490 | |||
491 | return CODEC_OK; | ||
492 | } | ||