From 0996bbd27895874ce8b7738e839fb82e068a30fc Mon Sep 17 00:00:00 2001 From: Yoshihisa Uchida Date: Sat, 13 Mar 2010 05:53:54 +0000 Subject: corrects the smaf parser the following. - It is normally retrieved even if the position of wave data is not in given position from the head. - The problem that artist/title/composer cannot be given normally when the character code != iso8859-1, utf-8. - The problem that the position of chunk does not given correctly some mmf files. - artist/title/composer stored in not id3v1buf but id3v2buf. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25141 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/libpcm/yamaha_adpcm.c | 6 + apps/codecs/smaf.c | 348 ++++++++++++----------- apps/metadata/smaf.c | 578 ++++++++++++++++++++++---------------- 3 files changed, 536 insertions(+), 396 deletions(-) diff --git a/apps/codecs/libpcm/yamaha_adpcm.c b/apps/codecs/libpcm/yamaha_adpcm.c index 9f1a4742ee..0b997ad776 100644 --- a/apps/codecs/libpcm/yamaha_adpcm.c +++ b/apps/codecs/libpcm/yamaha_adpcm.c @@ -82,6 +82,12 @@ static bool set_format(struct pcm_format *format) { fmt = format; + if (fmt->channels == 0) + { + DEBUGF("CODEC_ERROR: channels is 0\n"); + return false; + } + if (fmt->bitspersample != 4) { DEBUGF("CODEC_ERROR: yamaha adpcm must be 4 bitspersample: %d\n", diff --git a/apps/codecs/smaf.c b/apps/codecs/smaf.c index 8349d394ac..33a2a4b403 100644 --- a/apps/codecs/smaf.c +++ b/apps/codecs/smaf.c @@ -32,8 +32,8 @@ CODEC_HEADER */ enum { - SMAF_TRACK_CHUNK_SCORE = 0, /* Score Track */ - SMAF_TRACK_CHUNK_AUDIO, /* PCM Audio Track */ + SMAF_AUDIO_TRACK_CHUNK = 0, /* PCM Audio Track */ + SMAF_SCORE_TRACK_CHUNK, /* Score Track */ }; /* SMAF supported codec formats */ @@ -44,9 +44,9 @@ enum { SMAF_FORMAT_ADPCM, /* YAMAHA ADPCM */ }; -static int support_formats[2][3] = { - {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_UNSIGNED_PCM, SMAF_FORMAT_ADPCM }, +static const int support_formats[2][3] = { {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_ADPCM, SMAF_FORMAT_UNSUPPORT }, + {SMAF_FORMAT_SIGNED_PCM, SMAF_FORMAT_UNSIGNED_PCM, SMAF_FORMAT_ADPCM }, }; static const struct pcm_entry pcm_codecs[] = { @@ -57,7 +57,7 @@ static const struct pcm_entry pcm_codecs[] = { #define NUM_FORMATS 3 -static int basebits[4] = { 4, 8, 12, 16 }; +static const int basebits[4] = { 4, 8, 12, 16 }; #define PCM_SAMPLE_SIZE (2048*2) @@ -79,183 +79,243 @@ static const struct pcm_codec *get_codec(uint32_t formattag) return 0; } -static unsigned int get_be32(uint8_t *buf) +static unsigned int get_be32(const uint8_t *buf) { return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } -static int convert_smaf_audio_format(int track_chunk, unsigned int audio_format) +static int convert_smaf_channels(unsigned int ch) +{ + return (ch >> 7) + 1; +} + +static int convert_smaf_audio_format(unsigned int chunk, unsigned int audio_format) { - if (audio_format > 3) - return SMAF_FORMAT_UNSUPPORT; + int idx = (audio_format & 0x70) >> 4; + + if (idx < 3) + return support_formats[chunk][idx]; - return support_formats[track_chunk][audio_format]; + DEBUGF("CODEC_ERROR: unsupport audio format: %d\n", audio_format); + return SMAF_FORMAT_UNSUPPORT; } static int convert_smaf_audio_basebit(unsigned int basebit) { - if (basebit > 4) - return 0; - return basebits[basebit]; + if (basebit < 4) + return basebits[basebit]; + + DEBUGF("CODEC_ERROR: illegal basebit: %d\n", basebit); + return 0; +} + +static unsigned int search_chunk(const unsigned char *name, int nlen, off_t *pos) +{ + const unsigned char *buf; + unsigned int chunksize; + size_t size; + + while (true) + { + buf = ci->request_buffer(&size, 8); + if (size < 8) + break; + + chunksize = get_be32(buf + 4); + ci->advance_buffer(8); + *pos += 8; + if (memcmp(buf, name, nlen) == 0) + return chunksize; + + ci->advance_buffer(chunksize); + *pos += chunksize; + } + DEBUGF("CODEC_ERROR: missing '%s' chunk\n", name); + return 0; } -static bool parse_audio_track(struct pcm_format *fmt, - unsigned char **stbuf, unsigned char *endbuf) +static bool parse_audio_track(struct pcm_format *fmt, unsigned int chunksize, off_t *pos) { - unsigned char *buf = *stbuf; - int chunksize; - - buf += 8; - fmt->channels = ((buf[2] & 0x80) >> 7) + 1; - fmt->formattag = convert_smaf_audio_format(SMAF_TRACK_CHUNK_AUDIO, - (buf[2] >> 4) & 0x07); - if (fmt->formattag == SMAF_FORMAT_UNSUPPORT) + const unsigned char *buf; + size_t size; + + /* search PCM Audio Track Chunk */ + ci->advance_buffer(chunksize); + *pos += chunksize; + if (search_chunk("ATR", 3, pos) == 0) { - DEBUGF("CODEC_ERROR: unsupport pcm data format : %d\n", (buf[2] >> 4) & 0x07); + DEBUGF("CODEC_ERROR: missing PCM Audio Track Chunk\n"); return false; } - fmt->bitspersample = convert_smaf_audio_basebit(buf[3] >> 4); - if (fmt->bitspersample == 0) + + /* + * get format + * buf + * +0: Format Type + * +1: Sequence Type + * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency + * +3: bit 4-7: base bit + * +4: TimeBase_D + * +5: TimeBase_G + * + * Note: If PCM Audio Track does not include Sequence Data Chunk, + * tmp+6 is the start position of Wave Data Chunk. + */ + buf = ci->request_buffer(&size, 6); + if (size < 6) { - DEBUGF("CODEC_ERROR: unsupport pcm data basebit : %d\n", buf[3] >> 4); + DEBUGF("CODEC_ERROR: smaf is too small\n"); return false; } - buf += 6; - while (buf < endbuf) + + fmt->formattag = convert_smaf_audio_format(SMAF_AUDIO_TRACK_CHUNK, buf[2]); + fmt->channels = convert_smaf_channels(buf[2]); + fmt->bitspersample = convert_smaf_audio_basebit(buf[3] >> 4); + + /* search Wave Data Chunk */ + ci->advance_buffer(6); + *pos += 6; + fmt->numbytes = search_chunk("Awa", 3, pos); + if (fmt->numbytes == 0) { - chunksize = get_be32(buf + 4) + 8; - if (memcmp(buf, "Awa", 3) == 0) - { - fmt->numbytes = get_be32(buf + 4); - buf += 8; - return true; - } - buf += chunksize; + DEBUGF("CODEC_ERROR: missing Wave Data Chunk\n"); + return false; } - DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n"); - return false; + + return true; } -static bool parse_score_track(struct pcm_format *fmt, - unsigned char **stbuf, unsigned char *endbuf) +static bool parse_score_track(struct pcm_format *fmt, off_t *pos) { - unsigned char *buf = *stbuf; - int chunksize; + const unsigned char *buf; + unsigned int chunksize; + size_t size; + + /* parse Optional Data Chunk */ + buf = ci->request_buffer(&size, 13); + if (size < 13) + { + DEBUGF("CODEC_ERROR: smaf is too small\n"); + return false; + } + + if (memcmp(buf + 5, "OPDA", 4) != 0) + { + DEBUGF("CODEC_ERROR: missing Optional Data Chunk\n"); + return false; + } - if (buf[9] != 0x00) + /* Optional Data Chunk size */ + chunksize = get_be32(buf + 9); + + /* search Score Track Chunk */ + ci->advance_buffer(13 + chunksize); + *pos += (13 + chunksize); + if (search_chunk("MTR", 3, pos) == 0) { - DEBUGF("CODEC_ERROR: score track chunk unsupport sequence type %d\n", buf[9]); + DEBUGF("CODEC_ERROR: missing Score Track Chunk\n"); return false; } /* - * skip to the next chunk. - * MA-2/MA-3/MA-5: padding 16 bytes - * MA-7: padding 32 bytes + * search next chunk + * usually, next chunk ('M***') found within 40 bytes. */ - if (buf[3] < 7) - buf += 28; - else - buf += 44; - - while (buf < endbuf) + buf = ci->request_buffer(&size, 40); + if (size < 40) { - chunksize = get_be32(buf + 4) + 8; - if (memcmp(buf, "Mtsp", 4) == 0) - { - buf += 8; - if (memcmp(buf, "Mwa", 3) != 0) - { - DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n"); - return false; - } - fmt->numbytes = get_be32(buf + 4) - 3; - fmt->channels = ((buf[8] & 0x80) >> 7) + 1; - fmt->formattag = convert_smaf_audio_format(SMAF_TRACK_CHUNK_SCORE, - (buf[8] >> 4) & 0x07); - if (fmt->formattag == SMAF_FORMAT_UNSUPPORT) - { - DEBUGF("CODEC_ERROR: unsupport pcm data format : %d\n", - (buf[8] >> 4) & 0x07); - return false; - } - fmt->bitspersample = convert_smaf_audio_basebit(buf[8] & 0x0f); - if (fmt->bitspersample == 0) - { - DEBUGF("CODEC_ERROR: unsupport pcm data basebit : %d\n", - buf[8] & 0x0f); - return false; - } - buf += 11; - return true; - } - buf += chunksize; + DEBUGF("CODEC_ERROR: smaf is too small\n"); + return false; } - DEBUGF("CODEC_ERROR: smaf does not include stream pcm data\n"); - return false; -} + size = 0; + while (size < 40 && buf[size] != 'M') + size++; -static bool parse_header(struct pcm_format *fmt, size_t *pos) -{ - unsigned char *buf, *stbuf, *endbuf; - size_t chunksize; + if (size >= 40) + { + DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk"); + return false; + } - ci->memset(fmt, 0, sizeof(struct pcm_format)); + /* search Score Track Stream PCM Data Chunk */ + ci->advance_buffer(size); + *pos += size; + if (search_chunk("Mtsp", 4, pos) == 0) + { + DEBUGF("CODEC_ERROR: missing Score Track Stream PCM Data Chunk\n"); + return false; + } - /* assume the SMAF pcm data position is less than 1024 bytes */ - stbuf = ci->request_buffer(&chunksize, 1024); - if (chunksize < 1024) + /* + * parse Score Track Stream Wave Data Chunk + * buf + * +4-7: chunk size (WaveType(3bytes) + wave data count) + * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit + * +9: frequency (MSB) + * +10: frequency (LSB) + */ + buf = ci->request_buffer(&size, 9); + if (size < 9) + { + DEBUGF("CODEC_ERROR: smaf is too small\n"); return false; + } - buf = stbuf; - endbuf = stbuf + chunksize; - - if (memcmp(buf, "MMMD", 4) != 0) + if (memcmp(buf, "Mwa", 3) != 0) { - DEBUGF("CODEC_ERROR: does not smaf format %c%c%c%c\n", - buf[0], buf[1], buf[2], buf[3]); + DEBUGF("CODEC_ERROR: missing Score Track Stream Wave Data Chunk\n"); return false; } - buf += 8; - while (buf < endbuf) + fmt->formattag = convert_smaf_audio_format(SMAF_SCORE_TRACK_CHUNK, buf[8]); + fmt->channels = convert_smaf_channels(buf[8]); + fmt->bitspersample = convert_smaf_audio_basebit(buf[8] & 0xf); + fmt->numbytes = get_be32(buf + 4) - 3; + + *pos += 11; + return true; +} + +static bool parse_header(struct pcm_format *fmt, off_t *pos) +{ + const unsigned char *buf; + unsigned int chunksize; + size_t size; + + ci->memset(fmt, 0, sizeof(struct pcm_format)); + + /* check File Chunk and Contents Info Chunk */ + buf = ci->request_buffer(&size, 16); + if (size < 16) { - chunksize = get_be32(buf + 4) + 8; - if (memcmp(buf, "ATR", 3) == 0) - { - if (!parse_audio_track(fmt, &buf, endbuf)) - return false; - break; - } - if (memcmp(buf, "MTR", 3) == 0) - { - if (!parse_score_track(fmt, &buf, endbuf)) - return false; - break; - } - buf += chunksize; + DEBUGF("CODEC_ERROR: smaf is too small\n"); + return false; } - if (buf >= endbuf) + if ((memcmp(buf, "MMMD", 4) != 0) || (memcmp(buf + 8, "CNTI", 4) != 0)) { - DEBUGF("CODEC_ERROR: unsupported smaf format\n"); + DEBUGF("CODEC_ERROR: does not smaf format\n"); return false; } - /* blockalign */ - if (fmt->formattag == SMAF_FORMAT_SIGNED_PCM || - fmt->formattag == SMAF_FORMAT_UNSIGNED_PCM) - fmt->blockalign = fmt->channels * fmt->bitspersample >> 3; + chunksize = get_be32(buf + 12); + ci->advance_buffer(16); + *pos = 16; + if (chunksize > 5) + { + if (!parse_audio_track(fmt, chunksize, pos)) + return false; + } + else if (!parse_score_track(fmt, pos)) + return false; /* data signess (default signed) */ fmt->is_signed = (fmt->formattag != SMAF_FORMAT_UNSIGNED_PCM); + /* data is always big endian */ fmt->is_little_endian = false; - /* sets pcm data position */ - *pos = buf - stbuf; - return true; } @@ -297,14 +357,13 @@ next_track: codec_set_replaygain(ci->id3); - ci->memset(&format, 0, sizeof(struct pcm_format)); - format.is_signed = true; - format.is_little_endian = false; + /* Need to save offset for later use (cleared indirectly by advance_buffer) */ + bytesdone = ci->id3->offset; decodedsamples = 0; codec = 0; - if (!parse_header(&format, &n)) + if (!parse_header(&format, &firstblockposn)) { status = CODEC_ERROR; goto done; @@ -324,29 +383,6 @@ next_track: goto done; } - /* common format check */ - if (format.channels == 0) { - DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n"); - status = CODEC_ERROR; - goto done; - } - if (format.samplesperblock == 0) { - DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); - status = CODEC_ERROR; - goto done; - } - if (format.blockalign == 0) - { - DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); - status = CODEC_ERROR; - goto done; - } - if (format.numbytes == 0) { - DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); - status = CODEC_ERROR; - goto done; - } - /* check chunksize */ if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels > PCM_SAMPLE_SIZE) @@ -370,12 +406,10 @@ next_track: goto done; } - firstblockposn = 1024 - n; - ci->advance_buffer(firstblockposn); + ci->seek_buffer(firstblockposn); + ci->seek_complete(); /* The main decoder loop */ - bytesdone = 0; - ci->set_elapsed(0); endofstream = 0; while (!endofstream) { diff --git a/apps/metadata/smaf.c b/apps/metadata/smaf.c index 586802b7b5..ca5b690e0b 100644 --- a/apps/metadata/smaf.c +++ b/apps/metadata/smaf.c @@ -18,10 +18,7 @@ * KIND, either express or implied. * ****************************************************************************/ -#include #include -#include -#include #include #include "system.h" @@ -31,339 +28,442 @@ #include "rbunicode.h" #include "logf.h" -static int basebits[4] = { 4, 8, 12, 16 }; +static const int basebits[4] = { 4, 8, 12, 16 }; -static int frequency[5] = { 4000, 8000, 11025, 22050, 44100 }; +static const int frequency[5] = { 4000, 8000, 11025, 22050, 44100 }; -static int support_codepages[7] = { - SJIS, ISO_8859_1, -1, GB_2312, BIG_5, -1, -1, +static const int support_codepages[5] = { +#ifdef HAVE_LCD_BITMAP + SJIS, ISO_8859_1, -1, GB_2312, BIG_5, +#else + -1, ISO_8859_1, -1, -1, -1, +#endif }; /* extra codepage */ -#define UCS_2 (NUM_CODEPAGES + 1) -#define UTF_16 (NUM_CODEPAGES + 2) +#define UCS2 (NUM_CODEPAGES + 1) /* support id3 tag */ #define TAG_TITLE (('S'<<8)|'T') #define TAG_ARTIST (('A'<<8)|'N') #define TAG_COMPOSER (('S'<<8)|'W') -static bool read_datachunk(unsigned char *src, int size, unsigned short tag, - int codepage, unsigned char *dst) -{ - int datasize = 0; - unsigned char *utf8; +/* convert functions */ +#define CONVERT_SMAF_CHANNELS(c) (((c) >> 7) + 1) - while(size > datasize + 4) - { - datasize = (src[2] << 8) | src[3]; - if (tag == ((src[0] << 8) | src[1])) - { - src += 4; - if (codepage < NUM_CODEPAGES) - utf8 = iso_decode(src, dst, codepage, datasize); - else /* codepage == UTF_16, UCS_2 */ - utf8 = utf16BEdecode(src, dst, datasize); - *utf8 = '\0'; - - return true; - } - src += (datasize + 4); - } - return false; -} -static bool read_option(unsigned char *src, int size, unsigned short tag, - int codepage, unsigned char *dst) +static inline int convert_smaf_audio_basebit(unsigned int basebit) { - int datasize = 0; - unsigned char *endsrc = src + size; - unsigned char *utf8; - - while(src < endsrc) - { - utf8 = src; - src += 3; - datasize = 0; - while (*src != ',' || *(src-1) == '\\') - { - datasize++; - src++; - } - if (tag == ((utf8[0] << 8) | utf8[1]) && utf8[2] == ':') - { - utf8 += 3; - if (codepage < NUM_CODEPAGES) - utf8 = iso_decode(utf8, dst, codepage, datasize); - else /* codepage == UTF_16, UCS_2 */ - utf8 = utf16BEdecode(utf8, dst, datasize); - *utf8 = '\0'; - - return true; - } - src++; - } - return false; + if (basebit > 3) + return 0; + return basebits[basebit]; } -static int convert_smaf_audio_frequency(unsigned int freq) +static inline int convert_smaf_audio_frequency(unsigned int freq) { if (freq > 4) return 0; return frequency[freq]; } -static int convert_smaf_audio_basebit(unsigned int basebit) -{ - if (basebit > 4) - return 0; - return basebits[basebit]; -} - static int convert_smaf_codetype(unsigned int codetype) { - if (codetype < 7) + if (codetype < 5) return support_codepages[codetype]; - else if (codetype < 0x20) - return -1; - else if (codetype == 0x20) - return UCS_2; + else if (codetype == 0x20 || codetype == 0x24) /* In Rockbox, UCS2 and UTF-16 are same. */ + return UCS2; else if (codetype == 0x23) return UTF_8; - else if (codetype == 0x24) - return UTF_16; else if (codetype == 0xff) return ISO_8859_1; return -1; } -static bool get_smaf_metadata_audio_track(struct mp3entry *id3, - unsigned char* buf, unsigned char *endbuf) +static void set_length(struct mp3entry *id3, unsigned int ch, unsigned int basebit, + unsigned int numbytes) { - int bitspersample; - int channels; - int chunksize; - long numbytes; - unsigned long totalsamples; - - channels = ((buf[10] & 0x80) >> 7) + 1; - bitspersample = convert_smaf_audio_basebit(buf[11] >> 4); - if (bitspersample == 0) + int bitspersample = convert_smaf_audio_basebit(basebit); + + if (bitspersample != 0 && id3->frequency != 0) { - DEBUGF("metada error: smaf unsupport basebit %d\n", buf[11] >> 4); - return false; + /* Calculate track length [ms] and bitrate [kbit/s] */ + id3->length = (uint64_t)numbytes * 8000LL + / (bitspersample * CONVERT_SMAF_CHANNELS(ch) * id3->frequency); + id3->bitrate = bitspersample * id3->frequency / 1000; } - id3->frequency = convert_smaf_audio_frequency(buf[10] & 0x0f); - buf += 14; - while (buf < endbuf) - { - chunksize = get_long_be(buf + 4) + 8; - if (memcmp(buf, "Awa", 3) == 0) - { - numbytes = get_long_be(buf + 4) - 3; - totalsamples = (numbytes << 3) / (bitspersample * channels); + /* output contents/wave data/id3 info (for debug) */ + DEBUGF("contents info ----\n"); + DEBUGF(" TITLE: %s\n", (id3->title)? id3->title : "(NULL)"); + DEBUGF(" ARTIST: %s\n", (id3->artist)? id3->artist : "(NULL)"); + DEBUGF(" COMPOSER: %s\n", (id3->composer)? id3->composer : "(NULL)"); + DEBUGF("wave data info ----\n"); + DEBUGF(" channels: %u\n", CONVERT_SMAF_CHANNELS(ch)); + DEBUGF(" bitspersample: %d\n", bitspersample); + DEBUGF(" numbytes; %u\n", numbytes); + DEBUGF("id3 info ----\n"); + DEBUGF(" frquency: %u\n", (unsigned int)id3->frequency); + DEBUGF(" bitrate: %d\n", id3->bitrate); + DEBUGF(" length: %u\n", (unsigned int)id3->length); +} - /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ - id3->length = ((int64_t)totalsamples * 1000LL) / id3->frequency; +/* contents parse functions */ - return true; - } - buf += chunksize; +/* Note: + * 1) When the codepage is UTF-8 or UCS2, contents data do not start BOM. + * 2) The byte order of contents data is big endian. + */ + +static void decode2utf8(const unsigned char *src, unsigned char **dst, + int srcsize, int *dstsize, int codepage) +{ + unsigned char tmpbuf[srcsize * 3 + 1]; + unsigned char *p; + int utf8size; + + if (codepage < NUM_CODEPAGES) + p = iso_decode(src, tmpbuf, codepage, srcsize); + else /* codepage == UCS2 */ + p = utf16BEdecode(src, tmpbuf, srcsize); + + *p = '\0'; + + strlcpy(*dst, tmpbuf, *dstsize); + utf8size = (p - tmpbuf) + 1; + if (utf8size > *dstsize) + { + DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n", + utf8size, *dstsize); + utf8size = *dstsize; } - DEBUGF("metada error: smaf does not include pcm audio data\n"); - return false; + *dst += utf8size; + *dstsize -= utf8size; } -static bool get_smaf_metadata_score_track(struct mp3entry *id3, - unsigned char* buf, unsigned char *endbuf) +static int read_audio_track_contets(int fd, int codepage, unsigned char **dst, + int *dstsize) { - int bitspersample; - int channels; - int chunksize; - long numbytes; - unsigned long totalsamples; + /* value length <= 256 bytes */ + unsigned char buf[256]; + unsigned char *p = buf; + unsigned char *q = buf; + int datasize; - /* - * skip to the next chunk. - * MA-2/MA-3/MA-5: padding 16 bytes - * MA-7: padding 32 bytes - */ - if (buf[3] < 7) - buf += 28; - else - buf += 44; + read(fd, buf, 256); - while (buf + 10 < endbuf) + while (p - buf < 256 && *p != ',') { - chunksize = get_long_be(buf + 4) + 8; - if (memcmp(buf, "Mtsp", 4) == 0) + /* skip yen mark */ + if (codepage != UCS2) { - buf += 8; - if (memcmp(buf, "Mwa", 3) != 0) + if (*p == '\\') + p++; + } + else if (*p == '\0' && *(p+1) == '\\') + p += 2; + + if (*p > 0x7f) + { + if (codepage == UTF_8) { - DEBUGF("metada error: smaf unsupport format: %c%c%c%c\n", - buf[0], buf[1], buf[2], buf[3]); - return false; + while ((*p & MASK) != COMP) + *q++ = *p++; } - - channels = ((buf[8] & 0x80) >> 7) + 1; - bitspersample = convert_smaf_audio_basebit(buf[8] & 0x0f); - if (bitspersample == 0) +#ifdef HAVE_LCD_BITMAP + else if (codepage == SJIS) { - DEBUGF("metada error: smaf unsupport basebit %d\n", buf[8] & 0x0f); - return false; + if (*p <= 0xa0 || *p >= 0xe0) + *q++ = *p++; } +#endif + } - numbytes = get_long_be(buf + 4) - 3; - totalsamples = numbytes * 8 / (bitspersample * channels); - - id3->frequency = (buf[9] << 8) | buf[10]; + *q++ = *p++; + if (codepage == UCS2) + *q++ = *p++; + } + datasize = p - buf + 1; + lseek(fd, datasize - 256, SEEK_CUR); - /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ - id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; + if (dst != NULL) + decode2utf8(buf, dst, q - buf, dstsize, codepage); - return true; - } - buf += chunksize; - } - DEBUGF("metada error: smaf does not include pcm audio data\n"); - return false; + return datasize; } -bool get_smaf_metadata(int fd, struct mp3entry* id3) +static void read_score_track_contets(int fd, int codepage, int datasize, + unsigned char **dst, int *dstsize) { - unsigned char smafbuf[1024]; + unsigned char buf[datasize]; - /* Use the trackname part of the id3 structure as a temporary buffer */ - unsigned char* buf = (unsigned char *)id3->path; - unsigned char *endbuf = smafbuf + sizeof(smafbuf); - int i; - int contents_size; - int codepage = ISO_8859_1; + read(fd, buf, datasize); + decode2utf8(buf, dst, datasize, dstsize, codepage); +} - id3->title = NULL; - id3->artist = NULL; - id3->composer = NULL; +/* traverse chunk functions */ - id3->vbr = false; /* All SMAF files are CBR */ - id3->filesize = filesize(fd); +static unsigned int search_chunk(int fd, const unsigned char *name, int nlen) +{ + unsigned char buf[8]; + unsigned int chunksize; - /* get RIFF chunk header */ - if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 21) < 21)) + while (read(fd, buf, 8) > 0) { - return false; - } + chunksize = get_long_be(buf + 4); + if (memcmp(buf, name, nlen) == 0) + return chunksize; - if ((memcmp(buf, "MMMD", 4) != 0) || (memcmp(&buf[8], "CNTI", 4) != 0)) - { - DEBUGF("metada error: does not smaf format\n"); - return false; + lseek(fd, chunksize, SEEK_CUR); } + DEBUGF("metadata error: missing '%s' chunk\n", name); + return 0; +} + +static bool parse_smaf_audio_track(int fd, struct mp3entry *id3, unsigned int datasize) +{ + /* temporary buffer */ + unsigned char *tmp = (unsigned char*)id3->path; + /* contents stored buffer */ + unsigned char *buf = id3->id3v2buf; + int bufsize = sizeof(id3->id3v2buf); + + unsigned int chunksize = datasize; + int valsize; - contents_size = get_long_be(buf + 12); - if (contents_size < 5) + int codepage; + + /* parse contents info */ + read(fd, tmp, 5); + codepage = convert_smaf_codetype(tmp[2]); + if (codepage < 0) { - DEBUGF("metada error: CNTI chunk size is small %d\n", contents_size); + DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[2]); return false; } - contents_size -= 5; - i = contents_size; - if (i == 0) + datasize -= 5; + while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL) + && (datasize > 0 && bufsize > 0)) { - read(fd, buf, 16); - if (memcmp(buf, "OPDA", 4) != 0) - { - DEBUGF("metada error: smaf does not include OPDA chunk\n"); + if (read(fd, tmp, 3) <= 0) return false; - } - contents_size = get_long_be(buf + 4) - 8; - if (memcmp(buf + 8, "Dch", 3) != 0) + if (tmp[2] != ':') { - DEBUGF("metada error: smaf does not include Dch chunk\n"); + DEBUGF("metadata error: illegal tag: %c%c%c\n", tmp[0], tmp[1], tmp[2]); return false; } - codepage = convert_smaf_codetype(buf[11]); - if (codepage < 0) + switch ((tmp[0]<<8)|tmp[1]) { - DEBUGF("metada error: smaf unsupport codetype: %d\n", buf[11]); - return false; + case TAG_TITLE: + id3->title = buf; + valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); + break; + case TAG_ARTIST: + id3->artist = buf; + valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); + break; + case TAG_COMPOSER: + id3->composer = buf; + valsize = read_audio_track_contets(fd, codepage, &buf, &bufsize); + break; + default: + valsize = read_audio_track_contets(fd, codepage, NULL, &bufsize); + break; } + datasize -= (valsize + 3); + } - i = get_long_be(buf + 12); + /* search PCM Audio Track Chunk */ + lseek(fd, 16 + chunksize, SEEK_SET); - if (i > MAX_PATH) - { - DEBUGF("metada warning: smaf contents size is big %d\n", i); - i = MAX_PATH; - } - if (read(fd, buf, i) < i) - return false; + chunksize = search_chunk(fd, "ATR", 3); + if (chunksize == 0) + { + DEBUGF("metadata error: missing PCM Audio Track Chunk\n"); + return false; + } + + /* + * get format + * tmp + * +0: Format Type + * +1: Sequence Type + * +2: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: frequency + * +3: bit 4-7: base bit + * +4: TimeBase_D + * +5: TimeBase_G + * + * Note: If PCM Audio Track does not include Sequence Data Chunk, + * tmp+6 is the start position of Wave Data Chunk. + */ + read(fd, tmp, 6); + + /* search Wave Data Chunk */ + chunksize = search_chunk(fd, "Awa", 3); + if (chunksize == 0) + { + DEBUGF("metadata error: missing Wave Data Chunk\n"); + return false; + } - /* title */ - if (read_datachunk(buf, i, TAG_TITLE, codepage, id3->id3v1buf[0])) - id3->title = id3->id3v1buf[0]; + /* set track length and bitrate */ + id3->frequency = convert_smaf_audio_frequency(tmp[2] & 0x0f); + set_length(id3, tmp[2], tmp[3] >> 4, chunksize); + return true; +} + +static bool parse_smaf_score_track(int fd, struct mp3entry *id3) +{ + /* temporary buffer */ + unsigned char *tmp = (unsigned char*)id3->path; + unsigned char *p = tmp; + /* contents stored buffer */ + unsigned char *buf = id3->id3v2buf; + int bufsize = sizeof(id3->id3v2buf); + + unsigned int chunksize; + unsigned int datasize; + int valsize; + + int codepage; + + /* parse Optional Data Chunk */ + read(fd, tmp, 21); + if (memcmp(tmp + 5, "OPDA", 4) != 0) + { + DEBUGF("metadata error: missing Optional Data Chunk\n"); + return false; + } - /* artist */ - if (read_datachunk(buf, i, TAG_ARTIST, codepage, id3->id3v1buf[1])) - id3->artist = id3->id3v1buf[1]; + /* Optional Data Chunk size */ + chunksize = get_long_be(tmp + 9); - /* composer */ - if (read_datachunk(buf, i, TAG_COMPOSER, codepage, id3->id3v1buf[2])) - id3->composer = id3->id3v1buf[2]; + /* parse Data Chunk */ + if (memcmp(tmp + 13, "Dch", 3) != 0) + { + DEBUGF("metadata error: missing Data Chunk\n"); + return false; } - else + + codepage = convert_smaf_codetype(tmp[16]); + if (codepage < 0) { - codepage = convert_smaf_codetype(buf[14]); - if (codepage < 0) - { - DEBUGF("metada error: smaf unsupport codetype: %d\n", buf[11]); + DEBUGF("metadata error: smaf unsupport codetype: %d\n", tmp[16]); + return false; + } + + /* Data Chunk size */ + datasize = get_long_be(tmp + 17); + while ((id3->title == NULL || id3->artist == NULL || id3->composer == NULL) + && (datasize > 0 && bufsize > 0)) + { + if (read(fd, tmp, 4) <= 0) return false; - } - if (i > MAX_PATH) + valsize = (tmp[2] << 8) | tmp[3]; + datasize -= (valsize + 4); + switch ((tmp[0]<<8)|tmp[1]) { - DEBUGF("metada warning: smaf contents size is big %d\n", i); - i = MAX_PATH; + case TAG_TITLE: + id3->title = buf; + read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); + break; + case TAG_ARTIST: + id3->artist = buf; + read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); + break; + case TAG_COMPOSER: + id3->composer = buf; + read_score_track_contets(fd, codepage, valsize, &buf, &bufsize); + break; + default: + lseek(fd, valsize, SEEK_CUR); + break; } - if (read(fd, buf, i) < i) - return false; - - /* title */ - if (read_option(buf, i, TAG_TITLE, codepage, id3->id3v1buf[0])) - id3->title = id3->id3v1buf[0]; + } - /* artist */ - if (read_option(buf, i, TAG_ARTIST, codepage, id3->id3v1buf[1])) - id3->artist = id3->id3v1buf[1]; + /* search Score Track Chunk */ + lseek(fd, 29 + chunksize, SEEK_SET); - /* composer */ - if (read_option(buf, i, TAG_COMPOSER, codepage, id3->id3v1buf[2])) - id3->composer = id3->id3v1buf[2]; + if (search_chunk(fd, "MTR", 3) == 0) + { + DEBUGF("metadata error: missing Score Track Chunk\n"); + return false; } - if (contents_size > i) - lseek(fd, contents_size - i, SEEK_CUR); + /* + * search next chunk + * usually, next chunk ('M***') found within 40 bytes. + */ + chunksize = 40; + read(fd, tmp, chunksize); + + tmp[chunksize] = 'M'; /* stopper */ + while (*p != 'M') + p++; - /* assume the SMAF pcm data position is near the start */ - if (read(fd, smafbuf, sizeof(smafbuf)) < (ssize_t)sizeof(smafbuf)) + chunksize -= (p - tmp); + if (chunksize == 0) + { + DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk"); return false; + } - buf = smafbuf; - while (buf + 8 < endbuf) + /* search Score Track Stream PCM Data Chunk */ + lseek(fd, -chunksize, SEEK_CUR); + if (search_chunk(fd, "Mtsp", 4) == 0) { - i = get_long_be(buf + 4) + 8; + DEBUGF("metadata error: missing Score Track Stream PCM Data Chunk\n"); + return false; + } - if (memcmp(buf, "ATR", 3) == 0) - return get_smaf_metadata_audio_track(id3, buf, endbuf); - else if (memcmp(buf, "MTR", 3) == 0) - return get_smaf_metadata_score_track(id3, buf, endbuf); + /* + * parse Score Track Stream Wave Data Chunk + * tmp + * +4-7: chunk size (WaveType(3bytes) + wave data count) + * +8: bit 7 0:mono/1:stereo, bit 4-6 format, bit 0-3: base bit + * +9: frequency (MSB) + * +10: frequency (LSB) + */ + read(fd, tmp, 11); + if (memcmp(tmp, "Mwa", 3) != 0) + { + DEBUGF("metadata error: missing Score Track Stream Wave Data Chunk\n"); + return false; + } + + /* set track length and bitrate */ + id3->frequency = (tmp[9] << 8) | tmp[10]; + set_length(id3, tmp[8], tmp[8] & 0x0f, get_long_be(tmp + 4) - 3); + return true; +} + +bool get_smaf_metadata(int fd, struct mp3entry* id3) +{ + /* temporary buffer */ + unsigned char *tmp = (unsigned char *)id3->path; + unsigned int chunksize; - buf += i; + id3->title = NULL; + id3->artist = NULL; + id3->composer = NULL; + + id3->vbr = false; /* All SMAF files are CBR */ + id3->filesize = filesize(fd); + + /* check File Chunk and Contents Info Chunk */ + lseek(fd, 0, SEEK_SET); + read(fd, tmp, 16); + if ((memcmp(tmp, "MMMD", 4) != 0) || (memcmp(tmp + 8, "CNTI", 4) != 0)) + { + DEBUGF("metadata error: does not smaf format\n"); + return false; } - DEBUGF("metada error: smaf does not include track chunk\n"); - return false; + chunksize = get_long_be(tmp + 12); + if (chunksize > 5) + return parse_smaf_audio_track(fd, id3, chunksize); + + return parse_smaf_score_track(fd, id3); } -- cgit v1.2.3