summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorYoshihisa Uchida <uchida@rockbox.org>2010-03-13 05:53:54 +0000
committerYoshihisa Uchida <uchida@rockbox.org>2010-03-13 05:53:54 +0000
commit0996bbd27895874ce8b7738e839fb82e068a30fc (patch)
tree0e22744757cf4c268ea272d8087683e8e6f4647e /apps
parent4446d1bc857b41e491d04b05eeccc873a206fd18 (diff)
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
Diffstat (limited to 'apps')
-rw-r--r--apps/codecs/libpcm/yamaha_adpcm.c6
-rw-r--r--apps/codecs/smaf.c348
-rw-r--r--apps/metadata/smaf.c578
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 <stdio.h>
#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
#include <inttypes.h>
#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);
}