From 561cb2c4018ba6a83f742964de8cb892fac02c26 Mon Sep 17 00:00:00 2001 From: Yoshihisa Uchida Date: Sun, 28 Feb 2010 08:48:07 +0000 Subject: Add wave64(.w64) codec (FS#11022) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24959 a1c6a512-1295-4272-9138-f99709370657 --- apps/metadata/metadata_common.c | 9 +++ apps/metadata/metadata_common.h | 1 + apps/metadata/metadata_parsers.h | 1 + apps/metadata/wave.c | 146 ++++++++++++++++++++++++++++++++++----- 4 files changed, 138 insertions(+), 19 deletions(-) (limited to 'apps/metadata') diff --git a/apps/metadata/metadata_common.c b/apps/metadata/metadata_common.c index 318399693c..fd1cd553e8 100644 --- a/apps/metadata/metadata_common.c +++ b/apps/metadata/metadata_common.c @@ -174,6 +174,15 @@ int read_uint64le(int fd, uint64_t* buf) } #endif +/* Read an unaligned 64-bit little endian unsigned integer from buffer. */ +uint64_t get_uint64_le(void* buf) +{ + unsigned char* p = (unsigned char*) buf; + + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24) | ((uint64_t)p[4] << 32) | + ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56); +} + /* Read an unaligned 32-bit little endian long from buffer. */ unsigned long get_long_le(void* buf) { diff --git a/apps/metadata/metadata_common.h b/apps/metadata/metadata_common.h index c7d7d67dfb..3962e2cd17 100644 --- a/apps/metadata/metadata_common.h +++ b/apps/metadata/metadata_common.h @@ -57,6 +57,7 @@ int read_uint64be(int fd, uint64_t* buf); #define read_uint64le(fd,buf) read((fd), (buf), 8) #endif +uint64_t get_uint64_le(void* buf); unsigned long get_long_le(void* buf); unsigned short get_short_le(void* buf); unsigned long get_long_be(void* buf); diff --git a/apps/metadata/metadata_parsers.h b/apps/metadata/metadata_parsers.h index 3e7abb5c53..0e813ccb48 100644 --- a/apps/metadata/metadata_parsers.h +++ b/apps/metadata/metadata_parsers.h @@ -45,3 +45,4 @@ bool get_oma_metadata(int fd, struct mp3entry* id3); bool get_smaf_metadata(int fd, struct mp3entry* id3); bool get_au_metadata(int fd, struct mp3entry* id3); bool get_vox_metadata(int fd, struct mp3entry* id3); +bool get_wave64_metadata(int fd, struct mp3entry* id3); diff --git a/apps/metadata/wave.c b/apps/metadata/wave.c index 79bb8178bd..8fe755735d 100644 --- a/apps/metadata/wave.c +++ b/apps/metadata/wave.c @@ -8,6 +8,7 @@ * $Id$ * * Copyright (C) 2005 Dave Chapman + * Copyright (C) 2010 Yoshihisa Uchida * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,6 +42,15 @@ ((uint8_t*)(p))[1] = (d)>>8; \ } while(0) +/* Wave(RIFF)/Wave64 format */ + +/* Wave64 GUIDs */ +#define WAVE64_GUID_RIFF "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00" +#define WAVE64_GUID_WAVE "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" +#define WAVE64_GUID_FMT "fmt \xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" +#define WAVE64_GUID_FACT "fact\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" +#define WAVE64_GUID_DATA "data\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" + enum { WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ @@ -64,7 +74,7 @@ struct wave_fmt { unsigned int blockalign; unsigned long bitspersample; unsigned int samplesperblock; - unsigned long numbytes; + uint64_t numbytes; }; static unsigned long get_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3) @@ -114,6 +124,28 @@ static unsigned long get_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3 return totalsamples; } +static void parse_riff_format(unsigned char* buf, int fmtsize, struct wave_fmt *fmt, + struct mp3entry* id3) +{ + /* wFormatTag */ + fmt->formattag = buf[0] | (buf[1] << 8); + /* wChannels */ + fmt->channels = buf[2] | (buf[3] << 8); + /* dwSamplesPerSec */ + id3->frequency = get_long_le(&buf[4]); + /* dwAvgBytesPerSec */ + id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000; + /* wBlockAlign */ + fmt->blockalign = buf[12] | (buf[13] << 8); + /* wBitsPerSample */ + fmt->bitspersample = buf[14] | (buf[15] << 8); + if (fmtsize > 19) + { + /* wSamplesPerBlock */ + fmt->samplesperblock = buf[18] | (buf[19] << 8); + } +} + bool get_wave_metadata(int fd, struct mp3entry* id3) { /* Use the trackname part of the id3 structure as a temporary buffer */ @@ -166,24 +198,7 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) offset += read_bytes; i -= read_bytes; - /* wFormatTag */ - fmt.formattag = buf[0] | (buf[1] << 8); - /* wChannels */ - fmt.channels = buf[2] | (buf[3] << 8); - /* dwSamplesPerSec */ - id3->frequency = get_long_le(&buf[4]); - /* dwAvgBytesPerSec */ - id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000; - /* wBlockAlign */ - fmt.blockalign = buf[12] | (buf[13] << 8); - id3->bytesperframe = fmt.blockalign; - /* wBitsPerSample */ - fmt.bitspersample = buf[14] | (buf[15] << 8); - if (read_bytes > 19) - { - /* wSamplesPerBlock */ - fmt.samplesperblock = buf[18] | (buf[19] << 8); - } + parse_riff_format(buf, i, &fmt, id3); /* Check for ATRAC3 stream */ if (fmt.formattag == WAVE_FORMAT_ATRAC3) @@ -256,3 +271,96 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) return true; } + +bool get_wave64_metadata(int fd, struct mp3entry* id3) +{ + /* Use the trackname part of the id3 structure as a temporary buffer */ + unsigned char* buf = (unsigned char *)id3->path; + struct wave_fmt fmt; + unsigned long totalsamples = 0; + int read_bytes; + uint64_t i; + + memset(&fmt, 0, sizeof(struct wave_fmt)); + + /* get RIFF chunk header */ + if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 40) < 40)) + return false; + + if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0)|| + (memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0)) + { + DEBUGF("metada error: does not wave64 file\n"); + return false; + } + + /* iterate over WAVE chunks until 'data' chunk */ + while (true) + { + /* get chunk header */ + if (read(fd, buf, 24) < 24) + return false; + + /* chunkSize (excludes GUID and size length) */ + i = get_uint64_le(&buf[16]) - 24; + + if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) + { + DEBUGF("find 'fmt ' chunk\n"); + if (i < 16) + return false; + + read_bytes = 16; + if (i > 16) + { + read_bytes = 24; + if (i < 24) + i = 24; + } + + /* get rest of chunk */ + if (read(fd, buf, read_bytes) < read_bytes) + return false; + + i -= read_bytes; + parse_riff_format(buf, read_bytes, &fmt, id3); + } + else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) + { + DEBUGF("find 'data' chunk\n"); + fmt.numbytes = i; + break; + } + else if (memcmp(buf, WAVE64_GUID_FACT, 16) == 0) + { + /* Skip 'fact' chunk */ + DEBUGF("find 'fact' chunk\n"); + } + + /* seek to next chunk (8byte bound) */ + if (i & 0x07) + i += 8 - (i & 0x7); + + if(lseek(fd, i, SEEK_CUR) < 0) + return false; + } + + if ((fmt.numbytes == 0) || (fmt.channels == 0) || (fmt.blockalign == 0)) + { + DEBUGF("metadata error: numbytes, channels, or blockalign is 0\n"); + return false; + } + + if (totalsamples == 0) + { + totalsamples = get_totalsamples(&fmt, id3); + } + + id3->vbr = false; /* All Wave64 files are CBR */ + id3->filesize = filesize(fd); + + /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ + id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; + + return true; +} -- cgit v1.2.3