summaryrefslogtreecommitdiff
path: root/apps/metadata/smaf.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/metadata/smaf.c')
-rw-r--r--apps/metadata/smaf.c470
1 files changed, 0 insertions, 470 deletions
diff --git a/apps/metadata/smaf.c b/apps/metadata/smaf.c
deleted file mode 100644
index 1b745d3fa1..0000000000
--- a/apps/metadata/smaf.c
+++ /dev/null
@@ -1,470 +0,0 @@
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 <inttypes.h>
22#include <stdio.h>
23
24#include "string-extra.h"
25#include "system.h"
26#include "metadata.h"
27#include "metadata_common.h"
28#include "metadata_parsers.h"
29#include "rbunicode.h"
30#include "logf.h"
31
32static const int basebits[4] = { 4, 8, 12, 16 };
33
34static const int frequency[5] = { 4000, 8000, 11025, 22050, 44100 };
35
36static const int support_codepages[5] = {
37#ifdef HAVE_LCD_BITMAP
38 SJIS, ISO_8859_1, -1, GB_2312, BIG_5,
39#else
40 -1, ISO_8859_1, -1, -1, -1,
41#endif
42};
43
44/* extra codepage */
45#define UCS2 (NUM_CODEPAGES + 1)
46
47/* support id3 tag */
48#define TAG_TITLE (('S'<<8)|'T')
49#define TAG_ARTIST (('A'<<8)|'N')
50#define TAG_COMPOSER (('S'<<8)|'W')
51
52/* convert functions */
53#define CONVERT_SMAF_CHANNELS(c) (((c) >> 7) + 1)
54
55
56static inline int convert_smaf_audio_basebit(unsigned int basebit)
57{
58 if (basebit > 3)
59 return 0;
60 return basebits[basebit];
61}
62
63static inline int convert_smaf_audio_frequency(unsigned int freq)
64{
65 if (freq > 4)
66 return 0;
67 return frequency[freq];
68}
69
70static int convert_smaf_codetype(unsigned int codetype)
71{
72 if (codetype < 5)
73 return support_codepages[codetype];
74 else if (codetype == 0x20 || codetype == 0x24) /* In Rockbox, UCS2 and UTF-16 are same. */
75 return UCS2;
76 else if (codetype == 0x23)
77 return UTF_8;
78 else if (codetype == 0xff)
79 return ISO_8859_1;
80 return -1;
81}
82
83static void set_length(struct mp3entry *id3, unsigned int ch, unsigned int basebit,
84 unsigned int numbytes)
85{
86 int bitspersample = convert_smaf_audio_basebit(basebit);
87
88 if (bitspersample != 0 && id3->frequency != 0)
89 {
90 /* Calculate track length [ms] and bitrate [kbit/s] */
91 id3->length = (uint64_t)numbytes * 8000LL
92 / (bitspersample * CONVERT_SMAF_CHANNELS(ch) * id3->frequency);
93 id3->bitrate = bitspersample * id3->frequency / 1000;
94 }
95
96 /* output contents/wave data/id3 info (for debug) */
97 DEBUGF("contents info ----\n");
98 DEBUGF(" TITLE: %s\n", (id3->title)? id3->title : "(NULL)");
99 DEBUGF(" ARTIST: %s\n", (id3->artist)? id3->artist : "(NULL)");
100 DEBUGF(" COMPOSER: %s\n", (id3->composer)? id3->composer : "(NULL)");
101 DEBUGF("wave data info ----\n");
102 DEBUGF(" channels: %u\n", CONVERT_SMAF_CHANNELS(ch));
103 DEBUGF(" bitspersample: %d\n", bitspersample);
104 DEBUGF(" numbytes; %u\n", numbytes);
105 DEBUGF("id3 info ----\n");
106 DEBUGF(" frquency: %u\n", (unsigned int)id3->frequency);
107 DEBUGF(" bitrate: %d\n", id3->bitrate);
108 DEBUGF(" length: %u\n", (unsigned int)id3->length);
109}
110
111/* contents parse functions */
112
113/* Note:
114 * 1) When the codepage is UTF-8 or UCS2, contents data do not start BOM.
115 * 2) The byte order of contents data is big endian.
116 */
117
118static void decode2utf8(const unsigned char *src, unsigned char **dst,
119 int srcsize, int *dstsize, int codepage)
120{
121 unsigned char tmpbuf[srcsize * 3 + 1];
122 unsigned char *p;
123 int utf8size;
124
125 if (codepage < NUM_CODEPAGES)
126 p = iso_decode(src, tmpbuf, codepage, srcsize);
127 else /* codepage == UCS2 */
128 p = utf16BEdecode(src, tmpbuf, srcsize);
129
130 *p = '\0';
131
132 strlcpy(*dst, tmpbuf, *dstsize);
133 utf8size = (p - tmpbuf) + 1;
134 if (utf8size > *dstsize)
135 {
136 DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n",
137 utf8size, *dstsize);
138 utf8size = *dstsize;
139 }
140 *dst += utf8size;
141 *dstsize -= utf8size;
142}
143
144static int read_audio_track_contets(int fd, int codepage, unsigned char **dst,
145 int *dstsize)
146{
147 /* value length <= 256 bytes */
148 unsigned char buf[256];
149 unsigned char *p = buf;
150 unsigned char *q = buf;
151 int datasize;
152
153 read(fd, buf, 256);
154
155 while (p - buf < 256 && *p != ',')
156 {
157 /* skip yen mark */
158 if (codepage != UCS2)
159 {
160 if (*p == '\\')
161 p++;
162 }
163 else if (*p == '\0' && *(p+1) == '\\')
164 p += 2;
165
166 if (*p > 0x7f)
167 {
168 if (codepage == UTF_8)
169 {
170 while ((*p & MASK) != COMP)
171 *q++ = *p++;
172 }
173#ifdef HAVE_LCD_BITMAP
174 else if (codepage == SJIS)
175 {
176 if (*p <= 0xa0 || *p >= 0xe0)
177 *q++ = *p++;
178 }
179#endif
180 }
181
182 *q++ = *p++;
183 if (codepage == UCS2)
184 *q++ = *p++;
185 }
186 datasize = p - buf + 1;
187 lseek(fd, datasize - 256, SEEK_CUR);
188
189 if (dst != NULL)
190 decode2utf8(buf, dst, q - buf, dstsize, codepage);
191
192 return datasize;
193}
194
195static void read_score_track_contets(int fd, int codepage, int datasize,
196 unsigned char **dst, int *dstsize)
197{
198 unsigned char buf[datasize];
199
200 read(fd, buf, datasize);
201 decode2utf8(buf, dst, datasize, dstsize, codepage);
202}
203
204/* traverse chunk functions */
205
206static unsigned int search_chunk(int fd, const unsigned char *name, int nlen)
207{
208 unsigned char buf[8];
209 unsigned int chunksize;
210
211 while (read(fd, buf, 8) > 0)
212 {
213 chunksize = get_long_be(buf + 4);
214 if (memcmp(buf, name, nlen) == 0)
215 return chunksize;
216
217 lseek(fd, chunksize, SEEK_CUR);
218 }
219 DEBUGF("metadata error: missing '%s' chunk\n", name);
220 return 0;
221}
222
223static bool parse_smaf_audio_track(int fd, struct mp3entry *id3, unsigned int datasize)
224{
225 /* temporary buffer */
226 unsigned char *tmp = (unsigned char*)id3->path;
227 /* contents stored buffer */
228 unsigned char *buf = id3->id3v2buf;
229 int bufsize = sizeof(id3->id3v2buf);
230
231 unsigned int chunksize = datasize;
232 int valsize;
233
234 int codepage;
235
236 /* parse contents info */
237 read(fd, tmp, 5);
238 codepage = convert_smaf_codetype(tmp[2]);
239 if (codepage < 0)
240 {
241 DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[2]);
242 return false;
243 }
244
245 datasize -= 5;
246 while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
247 && (datasize > 0 && bufsize > 0))
248 {
249 if (read(fd, tmp, 3) <= 0)
250 return false;
251
252 if (tmp[2] != ':')
253 {
254 DEBUGF("metadata error: illegal tag: %c%c%c\n", tmp[0], tmp[1], tmp[2]);
255 return false;
256 }
257 switch ((tmp[0]<<8)|tmp[1])
258 {
259 case TAG_TITLE:
260 id3->title = buf;
261 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
262 break;
263 case TAG_ARTIST:
264 id3->artist = buf;
265 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
266 break;
267 case TAG_COMPOSER:
268 id3->composer = buf;
269 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
270 break;
271 default:
272 valsize = read_audio_track_contets(fd, codepage, NULL, &bufsize);
273 break;
274 }
275 datasize -= (valsize + 3);
276 }
277
278 /* search PCM Audio Track Chunk */
279 lseek(fd, 16 + chunksize, SEEK_SET);
280
281 chunksize = search_chunk(fd, "ATR", 3);
282 if (chunksize == 0)
283 {
284 DEBUGF("metadata error: missing PCM Audio Track Chunk\n");
285 return false;
286 }
287
288 /*
289 * get format
290 * tmp
291 * +0: Format Type
292 * +1: Sequence Type
293 * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency
294 * +3: bit 4-7: base bit
295 * +4: TimeBase_D
296 * +5: TimeBase_G
297 *
298 * Note: If PCM Audio Track does not include Sequence Data Chunk,
299 * tmp+6 is the start position of Wave Data Chunk.
300 */
301 read(fd, tmp, 6);
302
303 /* search Wave Data Chunk */
304 chunksize = search_chunk(fd, "Awa", 3);
305 if (chunksize == 0)
306 {
307 DEBUGF("metadata error: missing Wave Data Chunk\n");
308 return false;
309 }
310
311 /* set track length and bitrate */
312 id3->frequency = convert_smaf_audio_frequency(tmp[2] & 0x0f);
313 set_length(id3, tmp[2], tmp[3] >> 4, chunksize);
314 return true;
315}
316
317static bool parse_smaf_score_track(int fd, struct mp3entry *id3)
318{
319 /* temporary buffer */
320 unsigned char *tmp = (unsigned char*)id3->path;
321 unsigned char *p = tmp;
322 /* contents stored buffer */
323 unsigned char *buf = id3->id3v2buf;
324 int bufsize = sizeof(id3->id3v2buf);
325
326 unsigned int chunksize;
327 unsigned int datasize;
328 int valsize;
329
330 int codepage;
331
332 /* parse Optional Data Chunk */
333 read(fd, tmp, 21);
334 if (memcmp(tmp + 5, "OPDA", 4) != 0)
335 {
336 DEBUGF("metadata error: missing Optional Data Chunk\n");
337 return false;
338 }
339
340 /* Optional Data Chunk size */
341 chunksize = get_long_be(tmp + 9);
342
343 /* parse Data Chunk */
344 if (memcmp(tmp + 13, "Dch", 3) != 0)
345 {
346 DEBUGF("metadata error: missing Data Chunk\n");
347 return false;
348 }
349
350 codepage = convert_smaf_codetype(tmp[16]);
351 if (codepage < 0)
352 {
353 DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[16]);
354 return false;
355 }
356
357 /* Data Chunk size */
358 datasize = get_long_be(tmp + 17);
359 while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
360 && (datasize > 0 && bufsize > 0))
361 {
362 if (read(fd, tmp, 4) <= 0)
363 return false;
364
365 valsize = (tmp[2] << 8) | tmp[3];
366 datasize -= (valsize + 4);
367 switch ((tmp[0]<<8)|tmp[1])
368 {
369 case TAG_TITLE:
370 id3->title = buf;
371 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
372 break;
373 case TAG_ARTIST:
374 id3->artist = buf;
375 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
376 break;
377 case TAG_COMPOSER:
378 id3->composer = buf;
379 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
380 break;
381 default:
382 lseek(fd, valsize, SEEK_CUR);
383 break;
384 }
385 }
386
387 /* search Score Track Chunk */
388 lseek(fd, 29 + chunksize, SEEK_SET);
389
390 if (search_chunk(fd, "MTR", 3) == 0)
391 {
392 DEBUGF("metadata error: missing Score Track Chunk\n");
393 return false;
394 }
395
396 /*
397 * search next chunk
398 * usually, next chunk ('M***') found within 40 bytes.
399 */
400 chunksize = 40;
401 read(fd, tmp, chunksize);
402
403 tmp[chunksize] = 'M'; /* stopper */
404 while (*p != 'M')
405 p++;
406
407 chunksize -= (p - tmp);
408 if (chunksize == 0)
409 {
410 DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk");
411 return false;
412 }
413
414 /* search Score Track Stream PCM Data Chunk */
415 lseek(fd, -chunksize, SEEK_CUR);
416 if (search_chunk(fd, "Mtsp", 4) == 0)
417 {
418 DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk\n");
419 return false;
420 }
421
422 /*
423 * parse Score Track Stream Wave Data Chunk
424 * tmp
425 * +4-7: chunk size (WaveType(3bytes) + wave data count)
426 * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit
427 * +9: frequency (MSB)
428 * +10: frequency (LSB)
429 */
430 read(fd, tmp, 11);
431 if (memcmp(tmp, "Mwa", 3) != 0)
432 {
433 DEBUGF("metadata error: missing Score Track Stream Wave Data Chunk\n");
434 return false;
435 }
436
437 /* set track length and bitrate */
438 id3->frequency = (tmp[9] << 8) | tmp[10];
439 set_length(id3, tmp[8], tmp[8] & 0x0f, get_long_be(tmp + 4) - 3);
440 return true;
441}
442
443bool get_smaf_metadata(int fd, struct mp3entry* id3)
444{
445 /* temporary buffer */
446 unsigned char *tmp = (unsigned char *)id3->path;
447 unsigned int chunksize;
448
449 id3->title = NULL;
450 id3->artist = NULL;
451 id3->composer = NULL;
452
453 id3->vbr = false; /* All SMAF files are CBR */
454 id3->filesize = filesize(fd);
455
456 /* check File Chunk and Contents Info Chunk */
457 lseek(fd, 0, SEEK_SET);
458 read(fd, tmp, 16);
459 if ((memcmp(tmp, "MMMD", 4) != 0) || (memcmp(tmp + 8, "CNTI", 4) != 0))
460 {
461 DEBUGF("metadata error: does not smaf format\n");
462 return false;
463 }
464
465 chunksize = get_long_be(tmp + 12);
466 if (chunksize > 5)
467 return parse_smaf_audio_track(fd, id3, chunksize);
468
469 return parse_smaf_score_track(fd, id3);
470}