Use MEM_ALIGN_ATTR in pitch detector plugin.
[kugel-rb.git] / apps / metadata / smaf.c
blobfa12c99fb90e73668caab0fa05fd2528e40373a5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Yoshihisa Uchida
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <inttypes.h>
23 #include "string-extra.h"
24 #include "system.h"
25 #include "metadata.h"
26 #include "metadata_common.h"
27 #include "metadata_parsers.h"
28 #include "rbunicode.h"
29 #include "logf.h"
31 static const int basebits[4] = { 4, 8, 12, 16 };
33 static const int frequency[5] = { 4000, 8000, 11025, 22050, 44100 };
35 static const int support_codepages[5] = {
36 #ifdef HAVE_LCD_BITMAP
37 SJIS, ISO_8859_1, -1, GB_2312, BIG_5,
38 #else
39 -1, ISO_8859_1, -1, -1, -1,
40 #endif
43 /* extra codepage */
44 #define UCS2 (NUM_CODEPAGES + 1)
46 /* support id3 tag */
47 #define TAG_TITLE (('S'<<8)|'T')
48 #define TAG_ARTIST (('A'<<8)|'N')
49 #define TAG_COMPOSER (('S'<<8)|'W')
51 /* convert functions */
52 #define CONVERT_SMAF_CHANNELS(c) (((c) >> 7) + 1)
55 static inline int convert_smaf_audio_basebit(unsigned int basebit)
57 if (basebit > 3)
58 return 0;
59 return basebits[basebit];
62 static inline int convert_smaf_audio_frequency(unsigned int freq)
64 if (freq > 4)
65 return 0;
66 return frequency[freq];
69 static int convert_smaf_codetype(unsigned int codetype)
71 if (codetype < 5)
72 return support_codepages[codetype];
73 else if (codetype == 0x20 || codetype == 0x24) /* In Rockbox, UCS2 and UTF-16 are same. */
74 return UCS2;
75 else if (codetype == 0x23)
76 return UTF_8;
77 else if (codetype == 0xff)
78 return ISO_8859_1;
79 return -1;
82 static void set_length(struct mp3entry *id3, unsigned int ch, unsigned int basebit,
83 unsigned int numbytes)
85 int bitspersample = convert_smaf_audio_basebit(basebit);
87 if (bitspersample != 0 && id3->frequency != 0)
89 /* Calculate track length [ms] and bitrate [kbit/s] */
90 id3->length = (uint64_t)numbytes * 8000LL
91 / (bitspersample * CONVERT_SMAF_CHANNELS(ch) * id3->frequency);
92 id3->bitrate = bitspersample * id3->frequency / 1000;
95 /* output contents/wave data/id3 info (for debug) */
96 DEBUGF("contents info ----\n");
97 DEBUGF(" TITLE: %s\n", (id3->title)? id3->title : "(NULL)");
98 DEBUGF(" ARTIST: %s\n", (id3->artist)? id3->artist : "(NULL)");
99 DEBUGF(" COMPOSER: %s\n", (id3->composer)? id3->composer : "(NULL)");
100 DEBUGF("wave data info ----\n");
101 DEBUGF(" channels: %u\n", CONVERT_SMAF_CHANNELS(ch));
102 DEBUGF(" bitspersample: %d\n", bitspersample);
103 DEBUGF(" numbytes; %u\n", numbytes);
104 DEBUGF("id3 info ----\n");
105 DEBUGF(" frquency: %u\n", (unsigned int)id3->frequency);
106 DEBUGF(" bitrate: %d\n", id3->bitrate);
107 DEBUGF(" length: %u\n", (unsigned int)id3->length);
110 /* contents parse functions */
112 /* Note:
113 * 1) When the codepage is UTF-8 or UCS2, contents data do not start BOM.
114 * 2) The byte order of contents data is big endian.
117 static void decode2utf8(const unsigned char *src, unsigned char **dst,
118 int srcsize, int *dstsize, int codepage)
120 unsigned char tmpbuf[srcsize * 3 + 1];
121 unsigned char *p;
122 int utf8size;
124 if (codepage < NUM_CODEPAGES)
125 p = iso_decode(src, tmpbuf, codepage, srcsize);
126 else /* codepage == UCS2 */
127 p = utf16BEdecode(src, tmpbuf, srcsize);
129 *p = '\0';
131 strlcpy(*dst, tmpbuf, *dstsize);
132 utf8size = (p - tmpbuf) + 1;
133 if (utf8size > *dstsize)
135 DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n",
136 utf8size, *dstsize);
137 utf8size = *dstsize;
139 *dst += utf8size;
140 *dstsize -= utf8size;
143 static int read_audio_track_contets(int fd, int codepage, unsigned char **dst,
144 int *dstsize)
146 /* value length <= 256 bytes */
147 unsigned char buf[256];
148 unsigned char *p = buf;
149 unsigned char *q = buf;
150 int datasize;
152 read(fd, buf, 256);
154 while (p - buf < 256 && *p != ',')
156 /* skip yen mark */
157 if (codepage != UCS2)
159 if (*p == '\\')
160 p++;
162 else if (*p == '\0' && *(p+1) == '\\')
163 p += 2;
165 if (*p > 0x7f)
167 if (codepage == UTF_8)
169 while ((*p & MASK) != COMP)
170 *q++ = *p++;
172 #ifdef HAVE_LCD_BITMAP
173 else if (codepage == SJIS)
175 if (*p <= 0xa0 || *p >= 0xe0)
176 *q++ = *p++;
178 #endif
181 *q++ = *p++;
182 if (codepage == UCS2)
183 *q++ = *p++;
185 datasize = p - buf + 1;
186 lseek(fd, datasize - 256, SEEK_CUR);
188 if (dst != NULL)
189 decode2utf8(buf, dst, q - buf, dstsize, codepage);
191 return datasize;
194 static void read_score_track_contets(int fd, int codepage, int datasize,
195 unsigned char **dst, int *dstsize)
197 unsigned char buf[datasize];
199 read(fd, buf, datasize);
200 decode2utf8(buf, dst, datasize, dstsize, codepage);
203 /* traverse chunk functions */
205 static unsigned int search_chunk(int fd, const unsigned char *name, int nlen)
207 unsigned char buf[8];
208 unsigned int chunksize;
210 while (read(fd, buf, 8) > 0)
212 chunksize = get_long_be(buf + 4);
213 if (memcmp(buf, name, nlen) == 0)
214 return chunksize;
216 lseek(fd, chunksize, SEEK_CUR);
218 DEBUGF("metadata error: missing '%s' chunk\n", name);
219 return 0;
222 static bool parse_smaf_audio_track(int fd, struct mp3entry *id3, unsigned int datasize)
224 /* temporary buffer */
225 unsigned char *tmp = (unsigned char*)id3->path;
226 /* contents stored buffer */
227 unsigned char *buf = id3->id3v2buf;
228 int bufsize = sizeof(id3->id3v2buf);
230 unsigned int chunksize = datasize;
231 int valsize;
233 int codepage;
235 /* parse contents info */
236 read(fd, tmp, 5);
237 codepage = convert_smaf_codetype(tmp[2]);
238 if (codepage < 0)
240 DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[2]);
241 return false;
244 datasize -= 5;
245 while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
246 && (datasize > 0 && bufsize > 0))
248 if (read(fd, tmp, 3) <= 0)
249 return false;
251 if (tmp[2] != ':')
253 DEBUGF("metadata error: illegal tag: %c%c%c\n", tmp[0], tmp[1], tmp[2]);
254 return false;
256 switch ((tmp[0]<<8)|tmp[1])
258 case TAG_TITLE:
259 id3->title = buf;
260 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
261 break;
262 case TAG_ARTIST:
263 id3->artist = buf;
264 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
265 break;
266 case TAG_COMPOSER:
267 id3->composer = buf;
268 valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize);
269 break;
270 default:
271 valsize = read_audio_track_contets(fd, codepage, NULL, &bufsize);
272 break;
274 datasize -= (valsize + 3);
277 /* search PCM Audio Track Chunk */
278 lseek(fd, 16 + chunksize, SEEK_SET);
280 chunksize = search_chunk(fd, "ATR", 3);
281 if (chunksize == 0)
283 DEBUGF("metadata error: missing PCM Audio Track Chunk\n");
284 return false;
288 * get format
289 * tmp
290 * +0: Format Type
291 * +1: Sequence Type
292 * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency
293 * +3: bit 4-7: base bit
294 * +4: TimeBase_D
295 * +5: TimeBase_G
297 * Note: If PCM Audio Track does not include Sequence Data Chunk,
298 * tmp+6 is the start position of Wave Data Chunk.
300 read(fd, tmp, 6);
302 /* search Wave Data Chunk */
303 chunksize = search_chunk(fd, "Awa", 3);
304 if (chunksize == 0)
306 DEBUGF("metadata error: missing Wave Data Chunk\n");
307 return false;
310 /* set track length and bitrate */
311 id3->frequency = convert_smaf_audio_frequency(tmp[2] & 0x0f);
312 set_length(id3, tmp[2], tmp[3] >> 4, chunksize);
313 return true;
316 static bool parse_smaf_score_track(int fd, struct mp3entry *id3)
318 /* temporary buffer */
319 unsigned char *tmp = (unsigned char*)id3->path;
320 unsigned char *p = tmp;
321 /* contents stored buffer */
322 unsigned char *buf = id3->id3v2buf;
323 int bufsize = sizeof(id3->id3v2buf);
325 unsigned int chunksize;
326 unsigned int datasize;
327 int valsize;
329 int codepage;
331 /* parse Optional Data Chunk */
332 read(fd, tmp, 21);
333 if (memcmp(tmp + 5, "OPDA", 4) != 0)
335 DEBUGF("metadata error: missing Optional Data Chunk\n");
336 return false;
339 /* Optional Data Chunk size */
340 chunksize = get_long_be(tmp + 9);
342 /* parse Data Chunk */
343 if (memcmp(tmp + 13, "Dch", 3) != 0)
345 DEBUGF("metadata error: missing Data Chunk\n");
346 return false;
349 codepage = convert_smaf_codetype(tmp[16]);
350 if (codepage < 0)
352 DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[16]);
353 return false;
356 /* Data Chunk size */
357 datasize = get_long_be(tmp + 17);
358 while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL)
359 && (datasize > 0 && bufsize > 0))
361 if (read(fd, tmp, 4) <= 0)
362 return false;
364 valsize = (tmp[2] << 8) | tmp[3];
365 datasize -= (valsize + 4);
366 switch ((tmp[0]<<8)|tmp[1])
368 case TAG_TITLE:
369 id3->title = buf;
370 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
371 break;
372 case TAG_ARTIST:
373 id3->artist = buf;
374 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
375 break;
376 case TAG_COMPOSER:
377 id3->composer = buf;
378 read_score_track_contets(fd, codepage, valsize, &buf, &bufsize);
379 break;
380 default:
381 lseek(fd, valsize, SEEK_CUR);
382 break;
386 /* search Score Track Chunk */
387 lseek(fd, 29 + chunksize, SEEK_SET);
389 if (search_chunk(fd, "MTR", 3) == 0)
391 DEBUGF("metadata error: missing Score Track Chunk\n");
392 return false;
396 * search next chunk
397 * usually, next chunk ('M***') found within 40 bytes.
399 chunksize = 40;
400 read(fd, tmp, chunksize);
402 tmp[chunksize] = 'M'; /* stopper */
403 while (*p != 'M')
404 p++;
406 chunksize -= (p - tmp);
407 if (chunksize == 0)
409 DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk");
410 return false;
413 /* search Score Track Stream PCM Data Chunk */
414 lseek(fd, -chunksize, SEEK_CUR);
415 if (search_chunk(fd, "Mtsp", 4) == 0)
417 DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk\n");
418 return false;
422 * parse Score Track Stream Wave Data Chunk
423 * tmp
424 * +4-7: chunk size (WaveType(3bytes) + wave data count)
425 * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit
426 * +9: frequency (MSB)
427 * +10: frequency (LSB)
429 read(fd, tmp, 11);
430 if (memcmp(tmp, "Mwa", 3) != 0)
432 DEBUGF("metadata error: missing Score Track Stream Wave Data Chunk\n");
433 return false;
436 /* set track length and bitrate */
437 id3->frequency = (tmp[9] << 8) | tmp[10];
438 set_length(id3, tmp[8], tmp[8] & 0x0f, get_long_be(tmp + 4) - 3);
439 return true;
442 bool get_smaf_metadata(int fd, struct mp3entry* id3)
444 /* temporary buffer */
445 unsigned char *tmp = (unsigned char *)id3->path;
446 unsigned int chunksize;
448 id3->title = NULL;
449 id3->artist = NULL;
450 id3->composer = NULL;
452 id3->vbr = false; /* All SMAF files are CBR */
453 id3->filesize = filesize(fd);
455 /* check File Chunk and Contents Info Chunk */
456 lseek(fd, 0, SEEK_SET);
457 read(fd, tmp, 16);
458 if ((memcmp(tmp, "MMMD", 4) != 0) || (memcmp(tmp + 8, "CNTI", 4) != 0))
460 DEBUGF("metadata error: does not smaf format\n");
461 return false;
464 chunksize = get_long_be(tmp + 12);
465 if (chunksize > 5)
466 return parse_smaf_audio_track(fd, id3, chunksize);
468 return parse_smaf_score_track(fd, id3);