rockbox/apps/metadata/wave.c
Yoshihisa Uchida 20f5a533ae Oops. forget static.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25107 a1c6a512-1295-4272-9138-f99709370657
2010-03-10 11:19:17 +00:00

342 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "system.h"
#include "metadata.h"
#include "metadata_common.h"
#include "metadata_parsers.h"
#include "logf.h"
/* Wave(RIFF)/Wave64 format */
# define AV_WL32(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
} while(0)
# define AV_WL16(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
} while(0)
enum {
RIFF_CHUNK = 0,
WAVE_CHUNK,
FMT_CHUNK,
FACT_CHUNK,
DATA_CHUNK,
NUM_CHUNKS,
};
/* Wave chunk names */
static const unsigned char *wave_chunknames[NUM_CHUNKS] =
{
[RIFF_CHUNK] = "RIFF",
[WAVE_CHUNK] = "WAVE",
[FMT_CHUNK] = "fmt ",
[FACT_CHUNK] = "fact",
[DATA_CHUNK] = "data",
};
/* Wave64 GUIDs */
static const unsigned char *wave64_chunknames[NUM_CHUNKS] =
{
[RIFF_CHUNK] = "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00",
[WAVE_CHUNK] = "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a",
[FMT_CHUNK] = "fmt \xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a",
[FACT_CHUNK] = "fact\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a",
[DATA_CHUNK] = "data\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a",
};
/* support formats */
enum
{
WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */
WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */
WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */
WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */
WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */
WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */
WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */
WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */
WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */
IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */
IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */
WAVE_FORMAT_ATRAC3 = 0x0270, /* Atrac3 stream */
WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */
WAVE_FORMAT_EXTENSIBLE = 0xFFFE,
};
struct wave_fmt {
unsigned int formattag;
unsigned int channels;
unsigned int blockalign;
unsigned int bitspersample;
unsigned int samplesperblock;
uint32_t totalsamples;
uint64_t numbytes;
};
static void set_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3)
{
switch (fmt->formattag)
{
case WAVE_FORMAT_PCM:
case WAVE_FORMAT_IEEE_FLOAT:
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
case IBM_FORMAT_ALAW:
case IBM_FORMAT_MULAW:
if (fmt->bitspersample != 0 && fmt->channels != 0)
fmt->totalsamples = fmt->numbytes / ((fmt->bitspersample >> 3) * fmt->channels);
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
case WAVE_FORMAT_XBOX_ADPCM:
if (fmt->blockalign != 0)
fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock;
break;
case WAVE_FORMAT_YAMAHA_ADPCM:
if (fmt->blockalign != 0 && fmt->channels != 0)
{
if (fmt->samplesperblock == 0)
{
if (fmt->blockalign == ((id3->frequency / 60) + 4) * fmt->channels)
fmt->samplesperblock = id3->frequency / 30;
else
fmt->samplesperblock = (fmt->blockalign << 1) / fmt->channels;
}
fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock;
}
break;
case WAVE_FORMAT_DIALOGIC_OKI_ADPCM:
fmt->totalsamples = fmt->numbytes << 1;
break;
case WAVE_FORMAT_SWF_ADPCM:
if (fmt->blockalign != 0 && fmt->bitspersample != 0 && fmt->channels != 0)
{
if (fmt->samplesperblock == 0)
fmt->samplesperblock = (((fmt->blockalign << 3) - 2) / fmt->channels - 22)
/ fmt->bitspersample;
fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock;
}
break;
default:
fmt->totalsamples = 0;
break;
}
}
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 (fmt->formattag != WAVE_FORMAT_EXTENSIBLE)
{
if (fmtsize > 19)
{
/* wSamplesPerBlock */
fmt->samplesperblock = buf[18] | (buf[19] << 8);
}
}
else if (fmtsize > 25)
{
/* wValidBitsPerSample */
fmt->bitspersample = buf[18] | (buf[19] << 8);
/* SubFormat */
fmt->formattag = buf[24] | (buf[25] << 8);
}
/* Check for ATRAC3 stream */
if (fmt->formattag == WAVE_FORMAT_ATRAC3)
{
int jsflag = 0;
if(id3->bitrate == 66 || id3->bitrate == 94)
jsflag = 1;
id3->extradata_size = 14;
id3->channels = 2;
id3->codectype = AFMT_OMA_ATRAC3;
/* Store the extradata for the codec */
AV_WL16(&id3->id3v2buf[0], 1); // always 1
AV_WL32(&id3->id3v2buf[2], id3->frequency);// samples rate
AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode
AV_WL16(&id3->id3v2buf[10], 1); // always 1
AV_WL16(&id3->id3v2buf[12], 0); // always 0
}
}
static bool read_header(int fd, struct mp3entry* id3, const unsigned char **chunknames, bool is_64)
{
/* Use the temporary buffer */
unsigned char* buf = (unsigned char *)id3->path;
struct wave_fmt fmt;
unsigned int namelen = (is_64)? 16 : 4;
unsigned int sizelen = (is_64)? 8 : 4;
unsigned int len = namelen + sizelen;
uint64_t chunksize;
uint64_t offset = len + namelen;
int read_data;
memset(&fmt, 0, sizeof(struct wave_fmt));
/* get RIFF chunk header */
lseek(fd, 0, SEEK_SET);
read(fd, buf, offset);
if ((memcmp(buf, chunknames[RIFF_CHUNK], namelen) != 0) ||
(memcmp(buf + len, chunknames[WAVE_CHUNK], namelen) != 0))
{
DEBUGF("metadata error: missing riff header.\n");
return false;
}
/* iterate over WAVE chunks until 'data' chunk */
while (true)
{
/* get chunk header */
if (read(fd, buf, len) <= 0)
{
DEBUGF("metadata error: read error or missing 'data' chunk.\n");
return false;
}
offset += len;
/* get chunk size (when the header is wave64, chunksize includes GUID and data length) */
chunksize = (is_64) ? get_uint64_le(buf + namelen) - len :
get_long_le(buf + namelen);
if (memcmp(buf, chunknames[DATA_CHUNK], namelen) == 0)
{
DEBUGF("find 'data' chunk\n");
fmt.numbytes = chunksize;
if (fmt.formattag == WAVE_FORMAT_ATRAC3)
id3->first_frame_offset = offset;
break;
}
/* padded to next chunk */
chunksize += ((is_64)? ((1 + ~chunksize) & 0x07) : (chunksize & 1));
offset += chunksize;
read_data = 0;
if (memcmp(buf, chunknames[FMT_CHUNK], namelen) == 0)
{
DEBUGF("find 'fmt ' chunk\n");
if (chunksize < 16)
{
DEBUGF("metadata error: 'fmt ' chunk is too small: %d\n", (int)chunksize);
return false;
}
/* get and parse format */
read_data = (chunksize > 25)? 26 : chunksize;
read(fd, buf, read_data);
parse_riff_format(buf, read_data, &fmt, id3);
}
else if (memcmp(buf, chunknames[FACT_CHUNK], namelen) == 0)
{
DEBUGF("find 'fact' chunk\n");
/* dwSampleLength */
if (chunksize >= sizelen)
{
/* get totalsamples */
read_data = sizelen;
read(fd, buf, read_data);
fmt.totalsamples = (is_64)? get_uint64_le(buf) : get_long_le(buf);
}
}
lseek(fd, chunksize - read_data, SEEK_CUR);
}
if (fmt.totalsamples == 0)
set_totalsamples(&fmt, id3);
id3->vbr = false; /* All Wave/Wave64 files are CBR */
id3->filesize = filesize(fd);
/* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
if(fmt.formattag != AFMT_OMA_ATRAC3)
{
if (id3->frequency != 0)
id3->length = ((int64_t) fmt.totalsamples * 1000) / id3->frequency;
}
else
{
if (id3->bitrate != 0)
id3->length = ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate;
}
/* output header/id3 info (for debug) */
DEBUGF("%s header info ----\n", (is_64)? "wave64" : "wave");
DEBUGF(" format: %04x\n", (int)fmt.formattag);
DEBUGF(" channels: %u\n", fmt.channels);
DEBUGF(" blockalign: %u\n", fmt.blockalign);
DEBUGF(" bitspersample: %u\n", fmt.bitspersample);
DEBUGF(" samplesperblock: %u\n", fmt.samplesperblock);
DEBUGF(" totalsamples: %u\n", (unsigned int)fmt.totalsamples);
DEBUGF(" numbytes; %u\n", (unsigned int)fmt.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);
return true;
}
bool get_wave_metadata(int fd, struct mp3entry* id3)
{
return read_header(fd, id3, (const unsigned char **)&wave_chunknames, false);
}
bool get_wave64_metadata(int fd, struct mp3entry* id3)
{
return read_header(fd, id3, (const unsigned char **)&wave64_chunknames, true);
}