summaryrefslogtreecommitdiff
path: root/apps/codecs
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/codecs
parent4446d1bc857b41e491d04b05eeccc873a206fd18 (diff)
downloadrockbox-0996bbd27895874ce8b7738e839fb82e068a30fc.zip
rockbox-0996bbd27895874ce8b7738e839fb82e068a30fc.tar.gz
rockbox-0996bbd27895874ce8b7738e839fb82e068a30fc.tar.bz2
rockbox-0996bbd27895874ce8b7738e839fb82e068a30fc.tar.xz
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/codecs')
-rw-r--r--apps/codecs/libpcm/yamaha_adpcm.c6
-rw-r--r--apps/codecs/smaf.c348
2 files changed, 197 insertions, 157 deletions
diff --git a/apps/codecs/libpcm/yamaha_adpcm.c b/apps/codecs/libpcm/yamaha_adpcm.c
index 9f1a474..0b997ad 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 8349d39..33a2a4b 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) {