update wma example

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@44 bbd45198-f89e-11dd-88c7-29a3b14d5316
This commit is contained in:
bernard.xiong 2009-09-10 09:49:50 +00:00
parent e860e11ef7
commit 91ed3ab183
4 changed files with 743 additions and 608 deletions

View File

@ -1,544 +0,0 @@
#include <rtthread.h>
#include <dfs_posix.h>
#include "libwma/asf.h"
#define DEBUGF rt_kprintf
/* always little endian */
#define read_uint16le(fd,buf) read((fd), (buf), 2)
#define read_uint32le(fd,buf) read((fd), (buf), 4)
#define read_uint64le(fd,buf) read((fd), (buf), 8)
struct id3_tag
{
rt_uint32_t bitrate; /* bit rate */
rt_uint32_t frequency; /* sample frequency */
rt_size_t first_frame_offset; /* Byte offset to first real MP3 frame.
Used for skipping leading garbage to
avoid gaps between tracks. */
rt_size_t filesize; /* file size; in bytes */
rt_uint32_t frame_count; /* number of frames in the file (if VBR) */
rt_size_t offset; /* bytes played */
rt_uint32_t user_data; /* user data */
};
/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
struct guid_s {
rt_uint32_t v1;
rt_uint16_t v2;
rt_uint16_t v3;
rt_uint8_t v4[8];
};
typedef struct guid_s guid_t;
typedef long long rt_uint64_t;
struct asf_object_s {
guid_t guid;
rt_uint64_t size;
rt_uint64_t datalen;
};
typedef struct asf_object_s asf_object_t;
enum asf_error_e {
ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
ASF_ERROR_EOF = -3, /* unexpected end of file */
ASF_ERROR_IO = -4, /* error reading or writing to file */
ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
ASF_ERROR_SEEKABLE = -9, /* file not seekable */
ASF_ERROR_SEEK = -10, /* file is seekable but seeking failed */
ASF_ERROR_ENCRYPTED = -11 /* file is encrypted */
};
static const guid_t asf_guid_null =
{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
/* top level object guids */
static const guid_t asf_guid_header =
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_data =
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_index =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
/* header level object guids */
static const guid_t asf_guid_file_properties =
{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_stream_properties =
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_content_description =
{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_extended_content_description =
{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
static const guid_t asf_guid_content_encryption =
{0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}};
static const guid_t asf_guid_extended_content_encryption =
{0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}};
/* stream type guids */
static const guid_t asf_guid_stream_type_audio =
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
struct stream_buffer
{
rt_uint32_t length;
rt_uint32_t position; /* current position */
rt_uint8_t *buffer;
/* file descriptor of this stream buffer */
int fd;
};
static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
{
if((guid1->v1 != guid2->v1) ||
(guid1->v2 != guid2->v2) ||
(guid1->v3 != guid2->v3) ||
(rt_memcmp(guid1->v4, guid2->v4, 8)))
{
return 0;
}
return 1;
}
/* Read the 16 byte GUID from a file */
static void asf_readGUID(int fd, guid_t* guid)
{
read_uint32le(fd, &guid->v1);
read_uint16le(fd, &guid->v2);
read_uint16le(fd, &guid->v3);
read(fd, guid->v4, 8);
}
static void asf_read_object_header(asf_object_t *obj, int fd)
{
asf_readGUID(fd, &obj->guid);
read_uint64le(fd, &obj->size);
obj->datalen = 0;
}
/* Parse an integer from the extended content object - we always
convert to an int, regardless of native format.
*/
static int asf_intdecode(int fd, int type, int length)
{
rt_uint16_t tmp16;
rt_uint32_t tmp32;
rt_uint64_t tmp64;
if (type==3) {
read_uint32le(fd, &tmp32);
lseek(fd,length - 4,SEEK_CUR);
return (int)tmp32;
} else if (type==4) {
read_uint64le(fd, &tmp64);
lseek(fd,length - 8,SEEK_CUR);
return (int)tmp64;
} else if (type == 5) {
read_uint16le(fd, &tmp16);
lseek(fd,length - 2,SEEK_CUR);
return (int)tmp16;
}
return 0;
}
/* Decode a LE utf16 string from a disk buffer into a fixed-sized
utf8 buffer.
*/
static void asf_utf16LEdecode(int fd,
rt_uint16_t utf16bytes,
unsigned char **utf8,
int* utf8bytes)
{
unsigned long ucs;
int n;
unsigned char utf16buf[256];
unsigned char* utf16 = utf16buf;
unsigned char* newutf8;
n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16bytes -= n;
while (n > 0) {
/* Check for a surrogate pair */
if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) {
if (n < 4) {
/* Run out of utf16 bytes, read some more */
utf16buf[0] = utf16[0];
utf16buf[1] = utf16[1];
n = read(fd, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
n += 2;
}
if (n < 4) {
/* Truncated utf16 string, abort */
break;
}
ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18)
| utf16[2] | ((utf16[3] - 0xDC) << 8));
utf16 += 4;
n -= 4;
} else {
ucs = (utf16[0] | (utf16[1] << 8));
utf16 += 2;
n -= 2;
}
if (*utf8bytes > 6) {
newutf8 = utf8encode(ucs, *utf8);
*utf8bytes -= (newutf8 - *utf8);
*utf8 += (newutf8 - *utf8);
}
/* We have run out of utf16 bytes, read more if available */
if ((n == 0) && (utf16bytes > 0)) {
n = read(fd, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
}
}
*utf8[0] = 0;
--*utf8bytes;
if (utf16bytes > 0) {
/* Skip any remaining bytes */
lseek(fd, utf16bytes, SEEK_CUR);
}
return;
}
static int asf_parse_header(int fd, struct id3_tag* id3,
asf_waveformatex_t* wfx)
{
asf_object_t current;
asf_object_t header;
rt_uint64_t datalen;
int i;
int fileprop = 0;
rt_uint64_t play_duration;
rt_uint16_t flags;
rt_uint32_t subobjects;
rt_uint8_t utf8buf[512];
int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
unsigned char* id3buf = (unsigned char*)id3->id3v2buf;
asf_read_object_header((asf_object_t *) &header, fd);
//DEBUGF("header.size=%d\n",(int)header.size);
if (header.size < 30) {
/* invalid size for header object */
return ASF_ERROR_OBJECT_SIZE;
}
read_uint32le(fd, &subobjects);
/* Two reserved bytes - do we need to read them? */
lseek(fd, 2, SEEK_CUR);
//DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects);
if (subobjects > 0) {
header.datalen = header.size - 30;
/* TODO: Check that we have datalen bytes left in the file */
datalen = header.datalen;
for (i=0; i<(int)subobjects; i++) {
//DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
if (datalen < 24) {
//DEBUGF("not enough data for reading object\n");
break;
}
asf_read_object_header(&current, fd);
if (current.size > datalen || current.size < 24) {
//DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
break;
}
if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
if (current.size < 104)
return ASF_ERROR_OBJECT_SIZE;
if (fileprop) {
/* multiple file properties objects not allowed */
return ASF_ERROR_INVALID_OBJECT;
}
fileprop = 1;
/* All we want is the play duration - uint64_t at offset 40 */
lseek(fd, 40, SEEK_CUR);
read_uint64le(fd, &play_duration);
id3->length = play_duration / 10000;
//DEBUGF("****** length = %lums\n", id3->length);
/* Read the packet size - uint32_t at offset 68 */
lseek(fd, 20, SEEK_CUR);
read_uint32le(fd, &wfx->packet_size);
/* Skip bytes remaining in object */
lseek(fd, current.size - 24 - 72, SEEK_CUR);
} else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
guid_t guid;
rt_uint32_t propdatalen;
if (current.size < 78)
return ASF_ERROR_OBJECT_SIZE;
asf_readGUID(fd, &guid);
lseek(fd, 24, SEEK_CUR);
read_uint32le(fd, &propdatalen);
lseek(fd, 4, SEEK_CUR);
read_uint16le(fd, &flags);
if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
//DEBUGF("Found stream properties for non audio stream, skipping\n");
lseek(fd,current.size - 24 - 50,SEEK_CUR);
} else if (wfx->audiostream == -1) {
lseek(fd, 4, SEEK_CUR);
//DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
if (propdatalen < 18) {
return ASF_ERROR_INVALID_LENGTH;
}
read_uint16le(fd, &wfx->codec_id);
read_uint16le(fd, &wfx->channels);
read_uint32le(fd, &wfx->rate);
read_uint32le(fd, &wfx->bitrate);
wfx->bitrate *= 8;
read_uint16le(fd, &wfx->blockalign);
read_uint16le(fd, &wfx->bitspersample);
read_uint16le(fd, &wfx->datalen);
/* Round bitrate to the nearest kbit */
id3->bitrate = (wfx->bitrate + 500) / 1000;
id3->frequency = wfx->rate;
if (wfx->codec_id == ASF_CODEC_ID_WMAV1) {
read(fd, wfx->data, 4);
lseek(fd,current.size - 24 - 72 - 4,SEEK_CUR);
wfx->audiostream = flags&0x7f;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) {
read(fd, wfx->data, 6);
lseek(fd,current.size - 24 - 72 - 6,SEEK_CUR);
wfx->audiostream = flags&0x7f;
} else {
DEBUGF("Unsupported WMA codec (Pro, Lossless, Voice, etc)\n");
lseek(fd,current.size - 24 - 72,SEEK_CUR);
}
}
} else if (asf_guid_match(&current.guid, &asf_guid_content_description)) {
/* Object contains five 16-bit string lengths, followed by the five strings:
title, artist, copyright, description, rating
*/
rt_uint16_t strlength[5];
int i;
//DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
/* Read the 5 string lengths - number of bytes included trailing zero */
for (i=0; i<5; i++) {
read_uint16le(fd, &strlength[i]);
//DEBUGF("strlength = %u\n",strlength[i]);
}
if (strlength[0] > 0) { /* 0 - Title */
id3->title = id3buf;
asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining);
}
if (strlength[1] > 0) { /* 1 - Artist */
id3->artist = id3buf;
asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining);
}
lseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */
if (strlength[3] > 0) { /* 3 - description */
id3->comment = id3buf;
asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining);
}
lseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */
} else if (asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
rt_uint16_t count;
int i;
int bytesleft = current.size - 24;
//DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
read_uint16le(fd, &count);
bytesleft -= 2;
//DEBUGF("extended metadata count = %u\n",count);
for (i=0; i < count; i++) {
rt_uint16_t length, type;
unsigned char* utf8 = utf8buf;
int utf8length = 512;
read_uint16le(fd, &length);
asf_utf16LEdecode(fd, length, &utf8, &utf8length);
bytesleft -= 2 + length;
read_uint16le(fd, &type);
read_uint16le(fd, &length);
if (!strcmp("WM/TrackNumber",utf8buf)) {
if (type == 0) {
id3->track_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
id3->tracknum = atoi(id3->track_string);
} else if ((type >=2) && (type <= 5)) {
id3->tracknum = asf_intdecode(fd, type, length);
} else {
lseek(fd, length, SEEK_CUR);
}
} else if ((!strcmp("WM/Genre",utf8buf)) && (type == 0)) {
id3->genre_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumTitle",utf8buf)) && (type == 0)) {
id3->album = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumArtist",utf8buf)) && (type == 0)) {
id3->albumartist = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/Composer",utf8buf)) && (type == 0)) {
id3->composer = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else if (!strcmp("WM/Year",utf8buf)) {
if (type == 0) {
id3->year_string = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
id3->year = atoi(id3->year_string);
} else if ((type >=2) && (type <= 5)) {
id3->year = asf_intdecode(fd, type, length);
} else {
lseek(fd, length, SEEK_CUR);
}
} else if (!strncmp("replaygain_", utf8buf, 11)) {
char* value = id3buf;
int buf_len = id3buf_remaining;
int len;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
len = parse_replaygain(utf8buf, value, id3,
value, buf_len);
if (len == 0) {
/* Don't need to keep the value */
id3buf = value;
id3buf_remaining = buf_len;
}
} else if (!strcmp("MusicBrainz/Track Id", utf8buf)) {
id3->mb_track_id = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
} else {
lseek(fd, length, SEEK_CUR);
}
bytesleft -= 4 + length;
}
lseek(fd, bytesleft, SEEK_CUR);
} else if (asf_guid_match(&current.guid, &asf_guid_content_encryption)
|| asf_guid_match(&current.guid, &asf_guid_extended_content_encryption)) {
//DEBUGF("File is encrypted\n");
return ASF_ERROR_ENCRYPTED;
} else {
//DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
lseek(fd,current.size - 24,SEEK_CUR);
}
//DEBUGF("Parsed object - size = %d\n",(int)current.size);
datalen -= current.size;
}
if (i != (int)subobjects || datalen != 0) {
//DEBUGF("header data doesn't match given subobject count\n");
return ASF_ERROR_INVALID_VALUE;
}
//DEBUGF("%d subobjects read successfully\n", i);
}
//DEBUGF("header validated correctly\n");
return 0;
}
static rt_off_t filesize(int fd)
{
struct dfs_stat buf;
stat(fd,&buf);
return buf.st_size;
}
rt_bool_t get_asf_metadata(int fd, struct id3_tag* id3)
{
int res;
asf_object_t obj;
asf_waveformatex_t* wfx;
wfx = (asf_waveformatex_t*) rt_malloc(sizeof(asf_waveformatex_t));
if (wfx == RT_NULL) return RT_FALSE;
wfx->audiostream = -1;
res = asf_parse_header(fd, id3, wfx);
if (res < 0) {
DEBUGF("ASF: parsing error - %d\n",res);
return RT_FALSE;
}
if (wfx->audiostream == -1) {
DEBUGF("ASF: No WMA streams found\n");
rt_free(wfx);
return RT_FALSE;
}
asf_read_object_header(&obj, fd);
if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
DEBUGF("ASF: No data object found\n");
rt_free(wfx);
return RT_FALSE;
}
/* Store the current file position - no need to parse the header
again in the codec. The +26 skips the rest of the data object
header.
*/
id3->first_frame_offset = lseek(fd, 0, SEEK_CUR) + 26;
id3->filesize = filesize(fd);
/* set wfx to user data */
id3->user_data = (rt_uint32_t)wfx;
return RT_TRUE;
}

View File

@ -311,8 +311,8 @@ int wma_decode_init(WMADecodeContext* s, asf_waveformatex_t *wfx)
fixed32 high_freq; fixed32 high_freq;
fixed64 bps; fixed64 bps;
int sample_rate1; int sample_rate1;
int coef_vlc_table; int coef_vlc_table;
fixed32 *temp[5]; fixed32 *temp[5];
#ifdef CPU_COLDFIRE #ifdef CPU_COLDFIRE
@ -403,19 +403,19 @@ int wma_decode_init(WMADecodeContext* s, asf_waveformatex_t *wfx)
sample_rate1 = 11025; sample_rate1 = 11025;
else if (sample_rate1 >= 8000) else if (sample_rate1 >= 8000)
sample_rate1 = 8000; sample_rate1 = 8000;
} }
{
fixed64 tmp, tmp2;
fixed64 tim, tmpi;
{
fixed64 tmp, tmp2;
fixed64 tim, tmpi;
tmp = itofix64(s->bit_rate); tmp = itofix64(s->bit_rate);
tmp2 = itofix64(s->nb_channels * s->sample_rate); tmp2 = itofix64(s->nb_channels * s->sample_rate);
bps = fixdiv64(tmp, tmp2); bps = fixdiv64(tmp, tmp2);
tim = bps * s->frame_len; tim = bps * s->frame_len;
tmpi = fixdiv64(tim,itofix64(8)); tmpi = fixdiv64(tim,itofix64(8));
s->byte_offset_bits = av_log2(fixtoi64(tmpi+0x8000)) + 2; s->byte_offset_bits = av_log2(fixtoi64(tmpi+0x8000)) + 2;
} }
/* compute high frequency value and choose if noise coding should /* compute high frequency value and choose if noise coding should
be activated */ be activated */
@ -483,9 +483,9 @@ int wma_decode_init(WMADecodeContext* s, asf_waveformatex_t *wfx)
/* compute the scale factor band sizes for each MDCT block size */ /* compute the scale factor band sizes for each MDCT block size */
{ {
int a, b, pos, lpos, k, block_len, i, j, n; int a, b, pos, lpos, k, block_len, i, j, n;
const uint8_t *table; const uint8_t *table;
fixed32 tmp1, tmp2; fixed32 tmp1, tmp2;
if (s->version == 1) if (s->version == 1)
{ {
@ -1389,8 +1389,6 @@ static int wma_decode_block(WMADecodeContext *s, int32_t *scratch_buffer)
index = (s->frame_len / 2) + s->block_pos - n4; index = (s->frame_len / 2) + s->block_pos - n4;
wma_window(s, scratch_buffer, &((*s->frame_out)[ch][index])); wma_window(s, scratch_buffer, &((*s->frame_out)[ch][index]));
/* specific fast case for ms-stereo : add to second /* specific fast case for ms-stereo : add to second
channel if it is not coded */ channel if it is not coded */
if (s->ms_stereo && !s->channel_coded[1]) if (s->ms_stereo && !s->channel_coded[1])

View File

@ -11,6 +11,7 @@ Group (finsh)
Group (Filesystem) Group (Filesystem)
Group (LwIP) Group (LwIP)
Group (mp3) Group (mp3)
Group (libwma)
File 1,1,<.\application.c><application.c> File 1,1,<.\application.c><application.c>
File 1,1,<.\board.c><board.c> File 1,1,<.\board.c><board.c>
@ -26,6 +27,7 @@ File 1,1,<.\rtc.c><rtc.c>
File 1,1,<.\mp3.c><mp3.c> File 1,1,<.\mp3.c><mp3.c>
File 1,1,<.\wm8753.c><wm8753.c> File 1,1,<.\wm8753.c><wm8753.c>
File 1,1,<.\wav.c><wav.c> File 1,1,<.\wav.c><wav.c>
File 1,1,<.\wma.c><wma.c>
File 2,1,<.\library\src\stm32f10x_adc.c><stm32f10x_adc.c> File 2,1,<.\library\src\stm32f10x_adc.c><stm32f10x_adc.c>
File 2,1,<.\library\src\stm32f10x_bkp.c><stm32f10x_bkp.c> File 2,1,<.\library\src\stm32f10x_bkp.c><stm32f10x_bkp.c>
File 2,1,<.\library\src\stm32f10x_can.c><stm32f10x_can.c> File 2,1,<.\library\src\stm32f10x_can.c><stm32f10x_can.c>
@ -153,6 +155,12 @@ File 8,1,<.\mp3\real\subband.c><subband.c>
File 8,1,<.\mp3\real\trigtabs.c><trigtabs.c> File 8,1,<.\mp3\real\trigtabs.c><trigtabs.c>
File 8,2,<.\mp3\real\arm\asmpoly_thumb2.s><asmpoly_thumb2.s> File 8,2,<.\mp3\real\arm\asmpoly_thumb2.s><asmpoly_thumb2.s>
File 8,2,<.\mp3\real\arm\asmmisc.s><asmmisc.s> File 8,2,<.\mp3\real\arm\asmmisc.s><asmmisc.s>
File 9,1,<.\libwma\wmabitstream.c><wmabitstream.c>
File 9,1,<.\libwma\wmadeci.c><wmadeci.c>
File 9,1,<.\libwma\wmafixed.c><wmafixed.c>
File 9,1,<.\libwma\mdct2.c><mdct2.c>
File 9,2,<.\libwma\mdct_arm.S><mdct_arm.S>
File 9,2,<.\libwma\wma_arm.S><wma_arm.S>
Options 1,0,0 // Target 'RT-Thread/STM32-Radio' Options 1,0,0 // Target 'RT-Thread/STM32-Radio'
@ -207,7 +215,7 @@ Options 1,0,0 // Target 'RT-Thread/STM32-Radio'
OCMADSIRAM { 0,0,0,0,32,0,0,1,0 } OCMADSIRAM { 0,0,0,0,32,0,0,1,0 }
OCMADSIROM { 1,0,0,0,8,0,0,8,0 } OCMADSIROM { 1,0,0,0,8,0,0,8,0 }
OCMADSXRAM { 0,0,0,0,0,0,0,0,0 } OCMADSXRAM { 0,0,0,0,0,0,0,0,0 }
OCR_RVCT { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0,8,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,1,0,0,0,0,0,0,0,0,0,0 } OCR_RVCT { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0,8,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,8,0,0,0,0,0,0,0,0,0,0 }
RV_STAVEC () RV_STAVEC ()
ADSCCFLG { 5,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } ADSCCFLG { 5,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
ADSCMISC () ADSCMISC ()
@ -238,7 +246,7 @@ Options 1,0,0 // Target 'RT-Thread/STM32-Radio'
ADSLDIF () ADSLDIF ()
ADSLDDW () ADSLDDW ()
OPTDL (SARMCM3.DLL)()(DARMSTM.DLL)(-pSTM32F103ZE)(SARMCM3.DLL)()(TARMSTM.DLL)(-pSTM32F103ZE) OPTDL (SARMCM3.DLL)()(DARMSTM.DLL)(-pSTM32F103ZE)(SARMCM3.DLL)()(TARMSTM.DLL)(-pSTM32F103ZE)
OPTDBG 48118,7,()()()()()()()()()() (Segger\JL2CM3.dll)()()() OPTDBG 48117,7,()()()()()()()()()() (Segger\JL2CM3.dll)()()()
FLASH1 { 1,0,0,0,1,0,0,0,5,16,0,0,0,0,0,0,0,0,0,0 } FLASH1 { 1,0,0,0,1,0,0,0,5,16,0,0,0,0,0,0,0,0,0,0 }
FLASH2 (Segger\JL2CM3.dll) FLASH2 (Segger\JL2CM3.dll)
FLASH3 ("" ()) FLASH3 ("" ())

View File

@ -1,3 +1,6 @@
#include <rtthread.h>
#include <dfs_posix.h>
#include "libwma/asf.h" #include "libwma/asf.h"
#include "libwma/wmadec.h" #include "libwma/wmadec.h"
@ -22,9 +25,677 @@ enum asf_error_e {
ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
ASF_ERROR_SEEKABLE = -9, /* file not seekable */ ASF_ERROR_SEEKABLE = -9, /* file not seekable */
ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ ASF_ERROR_SEEK = -10, /* file is seekable but seeking failed */
ASF_ERROR_ENCRYPTED = -11 /* file is encrypted */
}; };
// #define DEBUGF rt_kprintf
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
/* always little endian */
#define read_uint16le(stream,buf) stream_buffer_read((stream), (rt_uint8_t*)(buf), 2)
#define read_uint32le(stream,buf) stream_buffer_read((stream), (rt_uint8_t*)(buf), 4)
#define read_uint64le(stream,buf) stream_buffer_read((stream), (rt_uint8_t*)(buf), 8)
#define ID3V2_BUF_SIZE 300
struct id3_tag
{
char* title;
char* artist;
char* album;
char* year_string;
char* comment;
char* genre_string;
char* track_string;
char* albumartist;
char* composer;
/* Musicbrainz Track ID */
char* mb_track_id;
int year;
int tracknum;
rt_uint32_t bitrate; /* bit rate */
rt_uint32_t frequency; /* sample frequency */
rt_uint32_t length; /* length */
rt_size_t first_frame_offset; /* Byte offset to first real MP3 frame.
Used for skipping leading garbage to
avoid gaps between tracks. */
rt_uint32_t frame_count; /* number of frames in the file (if VBR) */
rt_size_t offset; /* bytes played */
/* these following two fields are used for local buffering */
char id3v2buf[ID3V2_BUF_SIZE];
char id3v1buf[4][92];
rt_uint32_t user_data; /* user data */
};
/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
struct guid_s {
rt_uint32_t v1;
rt_uint16_t v2;
rt_uint16_t v3;
rt_uint8_t v4[8];
};
typedef struct guid_s guid_t;
typedef long long rt_uint64_t;
struct asf_object_s {
guid_t guid;
rt_uint64_t size;
rt_uint64_t datalen;
};
typedef struct asf_object_s asf_object_t;
static const guid_t asf_guid_null =
{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
/* top level object guids */
static const guid_t asf_guid_header =
{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_data =
{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_index =
{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
/* header level object guids */
static const guid_t asf_guid_file_properties =
{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_stream_properties =
{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
static const guid_t asf_guid_content_description =
{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
static const guid_t asf_guid_extended_content_description =
{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
static const guid_t asf_guid_content_encryption =
{0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}};
static const guid_t asf_guid_extended_content_encryption =
{0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}};
/* stream type guids */
static const guid_t asf_guid_stream_type_audio =
{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
#define STREAM_BUFFER_SIZE 1024
struct stream_buffer
{
rt_uint32_t length;
rt_uint32_t position; /* current position in the buffer */
rt_uint8_t *buffer;
rt_size_t buffer_size;
/* file descriptor of this stream buffer */
int fd;
rt_bool_t eof;
};
struct stream_buffer* stream;
struct id3_tag _id3;
struct id3_tag* id3;
rt_size_t stream_buffer_read(struct stream_buffer* stream, rt_uint8_t* ptr, rt_size_t len)
{
rt_size_t rest_bytes;
if (stream->position + len > stream->length)
{
rest_bytes = stream->length - stream->position;
memcpy(ptr, &stream->buffer[stream->position], rest_bytes);
ptr += rest_bytes;
/* read more buffer */
stream->length = read(stream->fd, &stream->buffer[0], stream->buffer_size);
if (stream->length < len - rest_bytes)
{
stream->position = stream->length;
stream->eof = RT_TRUE;
}
else
stream->position = len - rest_bytes;
memcpy(ptr, &stream->buffer[0], stream->position);
return rest_bytes + stream->position;
}
memcpy(ptr, &stream->buffer[stream->position], len);
stream->position += len;
return len;
}
void stream_buffer_advance(struct stream_buffer* stream, rt_size_t size)
{
if (stream->position + size < stream->length)
{
stream->position += size;
return;
}
stream->position = size - (stream->length - stream->position);
stream->length = read(stream->fd, stream->buffer, stream->buffer_size);
if (stream->length < stream->position) stream->eof = RT_TRUE;
};
rt_uint8_t *stream_buffer_request(struct stream_buffer* stream, rt_size_t *r_size, rt_size_t size)
{
rt_size_t rest_bytes, read_bytes, read_result;
if (size > stream->buffer_size) return RT_NULL; /* request size more than the buffer size */
if (stream->position + size < stream->length)
{
*r_size = size;
return &stream->buffer[stream->position];
}
/* read more data */
rest_bytes = stream->length - stream->position;
memmove(&stream->buffer[0], &stream->buffer[stream->position], rest_bytes);
read_bytes = stream->buffer_size - rest_bytes;
read_bytes = (read_bytes / 512) * 512; /* align to 512 */
read_result = read(stream->fd, &stream->buffer[rest_bytes], read_bytes);
stream->position = 0;
stream->length = read_result + rest_bytes;
*r_size = size;
return &stream->buffer[0];
}
struct stream_buffer* stream_buffer_create(int fd)
{
struct stream_buffer* buffer = (struct stream_buffer*) rt_malloc(sizeof(struct stream_buffer));
if (buffer != RT_NULL)
{
buffer->fd = fd;
buffer->eof = RT_FALSE;
buffer->length = 0;
buffer->position = 0;
buffer->buffer_size = STREAM_BUFFER_SIZE;
buffer->buffer = rt_malloc(buffer->buffer_size);
}
return buffer;
}
void stream_buffer_close(struct stream_buffer* stream)
{
rt_free(stream->buffer);
close(stream->fd);
}
static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
{
if((guid1->v1 != guid2->v1) ||
(guid1->v2 != guid2->v2) ||
(guid1->v3 != guid2->v3) ||
(rt_memcmp(guid1->v4, guid2->v4, 8)))
{
return 0;
}
return 1;
}
/* Read the 16 byte GUID from a file */
static void asf_readGUID(struct stream_buffer* stream, guid_t* guid)
{
read_uint32le(stream, &guid->v1);
read_uint16le(stream, &guid->v2);
read_uint16le(stream, &guid->v3);
stream_buffer_read(stream, guid->v4, 8);
}
static void asf_read_object_header(asf_object_t *obj, struct stream_buffer* stream)
{
asf_readGUID(stream, &obj->guid);
read_uint64le(stream, &obj->size);
obj->datalen = 0;
}
/* Parse an integer from the extended content object - we always
convert to an int, regardless of native format.
*/
static int asf_intdecode(struct stream_buffer* stream, int type, int length)
{
rt_uint16_t tmp16;
rt_uint32_t tmp32;
rt_uint64_t tmp64;
if (type==3) {
read_uint32le(stream, &tmp32);
stream_buffer_advance(stream,length - 4);
return (int)tmp32;
} else if (type==4) {
read_uint64le(stream, &tmp64);
stream_buffer_advance(stream,length - 8);
return (int)tmp64;
} else if (type == 5) {
read_uint16le(stream, &tmp16);
stream_buffer_advance(stream,length - 2);
return (int)tmp16;
}
return 0;
}
#define MASK 0xC0 /* 11000000 */
#define COMP 0x80 /* 10x */
static const unsigned char utf8comp[6] =
{
0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
};
/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */
unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8)
{
int tail = 0;
if (ucs > 0x7F)
while (ucs >> (5*tail + 6))
tail++;
*utf8++ = (ucs >> (6*tail)) | utf8comp[tail];
while (tail--)
*utf8++ = ((ucs >> (6*tail)) & (MASK ^ 0xFF)) | COMP;
return utf8;
}
/* Decode a LE utf16 string from a disk buffer into a fixed-sized
utf8 buffer.
*/
static void asf_utf16LEdecode(struct stream_buffer* stream,
rt_uint16_t utf16bytes,
unsigned char **utf8,
int* utf8bytes)
{
unsigned long ucs;
int n;
unsigned char utf16buf[256];
unsigned char* utf16 = utf16buf;
unsigned char* newutf8;
n = stream_buffer_read(stream, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16bytes -= n;
while (n > 0) {
/* Check for a surrogate pair */
if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) {
if (n < 4) {
/* Run out of utf16 bytes, read some more */
utf16buf[0] = utf16[0];
utf16buf[1] = utf16[1];
n = stream_buffer_read(stream, utf16buf + 2, MIN(sizeof(utf16buf)-2, utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
n += 2;
}
if (n < 4) {
/* Truncated utf16 string, abort */
break;
}
ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18)
| utf16[2] | ((utf16[3] - 0xDC) << 8));
utf16 += 4;
n -= 4;
} else {
ucs = (utf16[0] | (utf16[1] << 8));
utf16 += 2;
n -= 2;
}
if (*utf8bytes > 6) {
newutf8 = utf8encode(ucs, *utf8);
*utf8bytes -= (newutf8 - *utf8);
*utf8 += (newutf8 - *utf8);
}
/* We have run out of utf16 bytes, read more if available */
if ((n == 0) && (utf16bytes > 0)) {
n = stream_buffer_read(stream, utf16buf, MIN(sizeof(utf16buf), utf16bytes));
utf16 = utf16buf;
utf16bytes -= n;
}
}
*utf8[0] = 0;
--*utf8bytes;
if (utf16bytes > 0) {
/* Skip any remaining bytes */
stream_buffer_advance(stream, utf16bytes);
}
return;
}
static int asf_parse_header(struct stream_buffer* stream, struct id3_tag* id3,
asf_waveformatex_t* wfx)
{
asf_object_t current;
asf_object_t header;
rt_uint64_t datalen;
int i;
int fileprop = 0;
rt_uint64_t play_duration;
rt_uint16_t flags;
rt_uint32_t subobjects;
rt_uint8_t utf8buf[512];
int id3buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf);
unsigned char* id3buf = (unsigned char*)id3->id3v2buf;
asf_read_object_header((asf_object_t *) &header, stream);
DEBUGF("header.size=%d\n",(int)header.size);
if (header.size < 30) {
/* invalid size for header object */
return ASF_ERROR_OBJECT_SIZE;
}
read_uint32le(stream, &subobjects);
/* Two reserved bytes - do we need to read them? */
stream_buffer_advance(stream, 2);
DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects);
if (subobjects > 0) {
header.datalen = header.size - 30;
/* TODO: Check that we have datalen bytes left in the file */
datalen = header.datalen;
for (i=0; i<(int)subobjects; i++) {
DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
if (datalen < 24) {
DEBUGF("not enough data for reading object\n");
break;
}
asf_read_object_header(&current, stream);
if (current.size > datalen || current.size < 24) {
DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
break;
}
if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
if (current.size < 104)
return ASF_ERROR_OBJECT_SIZE;
if (fileprop) {
/* multiple file properties objects not allowed */
return ASF_ERROR_INVALID_OBJECT;
}
fileprop = 1;
/* All we want is the play duration - uint64_t at offset 40 */
stream_buffer_advance(stream, 40);
read_uint64le(stream, &play_duration);
id3->length = play_duration / 10000;
DEBUGF("****** length = %lums\n", id3->length);
/* Read the packet size - uint32_t at offset 68 */
stream_buffer_advance(stream, 20);
read_uint32le(stream, &wfx->packet_size);
/* Skip bytes remaining in object */
stream_buffer_advance(stream, current.size - 24 - 72);
} else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
guid_t guid;
rt_uint32_t propdatalen;
if (current.size < 78)
return ASF_ERROR_OBJECT_SIZE;
asf_readGUID(stream, &guid);
stream_buffer_advance(stream, 24);
read_uint32le(stream, &propdatalen);
stream_buffer_advance(stream, 4);
read_uint16le(stream, &flags);
if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
DEBUGF("Found stream properties for non audio stream, skipping\n");
stream_buffer_advance(stream,current.size - 24 - 50);
} else if (wfx->audiostream == -1) {
stream_buffer_advance(stream, 4);
DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
if (propdatalen < 18) {
return ASF_ERROR_INVALID_LENGTH;
}
read_uint16le(stream, &wfx->codec_id);
read_uint16le(stream, &wfx->channels);
read_uint32le(stream, &wfx->rate);
read_uint32le(stream, &wfx->bitrate);
wfx->bitrate *= 8;
read_uint16le(stream, &wfx->blockalign);
read_uint16le(stream, &wfx->bitspersample);
read_uint16le(stream, &wfx->datalen);
/* Round bitrate to the nearest kbit */
id3->bitrate = (wfx->bitrate + 500) / 1000;
id3->frequency = wfx->rate;
if (wfx->codec_id == ASF_CODEC_ID_WMAV1) {
stream_buffer_read(stream, wfx->data, 4);
stream_buffer_advance(stream, current.size - 24 - 72 - 4);
wfx->audiostream = flags&0x7f;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) {
stream_buffer_read(stream, wfx->data, 6);
stream_buffer_advance(stream,current.size - 24 - 72 - 6);
wfx->audiostream = flags&0x7f;
} else {
DEBUGF("Unsupported WMA codec (Pro, Lossless, Voice, etc)\n");
stream_buffer_advance(stream,current.size - 24 - 72);
}
}
} else if (asf_guid_match(&current.guid, &asf_guid_content_description)) {
/* Object contains five 16-bit string lengths, followed by the five strings:
title, artist, copyright, description, rating
*/
rt_uint16_t strlength[5];
int i;
DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
/* Read the 5 string lengths - number of bytes included trailing zero */
for (i=0; i<5; i++) {
read_uint16le(stream, &strlength[i]);
DEBUGF("strlength = %u\n",strlength[i]);
}
if (strlength[0] > 0) { /* 0 - Title */
id3->title = id3buf;
asf_utf16LEdecode(stream, strlength[0], &id3buf, &id3buf_remaining);
}
if (strlength[1] > 0) { /* 1 - Artist */
id3->artist = id3buf;
asf_utf16LEdecode(stream, strlength[1], &id3buf, &id3buf_remaining);
}
stream_buffer_advance(stream, strlength[2]); /* 2 - copyright */
if (strlength[3] > 0) { /* 3 - description */
id3->comment = id3buf;
asf_utf16LEdecode(stream, strlength[3], &id3buf, &id3buf_remaining);
}
stream_buffer_advance(stream, strlength[4]); /* 4 - rating */
} else if (asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
rt_uint16_t count;
int i;
int bytesleft = current.size - 24;
DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
read_uint16le(stream, &count);
bytesleft -= 2;
DEBUGF("extended metadata count = %u\n",count);
for (i=0; i < count; i++) {
rt_uint16_t length, type;
unsigned char* utf8 = utf8buf;
int utf8length = 512;
read_uint16le(stream, &length);
asf_utf16LEdecode(stream, length, &utf8, &utf8length);
bytesleft -= 2 + length;
read_uint16le(stream, &type);
read_uint16le(stream, &length);
if (!strcmp("WM/TrackNumber",utf8buf)) {
if (type == 0) {
id3->track_string = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
id3->tracknum = atoi(id3->track_string);
} else if ((type >=2) && (type <= 5)) {
id3->tracknum = asf_intdecode(stream, type, length);
} else {
stream_buffer_advance(stream, length);
}
} else if ((!strcmp("WM/Genre",utf8buf)) && (type == 0)) {
id3->genre_string = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumTitle",utf8buf)) && (type == 0)) {
id3->album = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/AlbumArtist",utf8buf)) && (type == 0)) {
id3->albumartist = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
} else if ((!strcmp("WM/Composer",utf8buf)) && (type == 0)) {
id3->composer = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
} else if (!strcmp("WM/Year",utf8buf)) {
if (type == 0) {
id3->year_string = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
id3->year = atoi(id3->year_string);
} else if ((type >=2) && (type <= 5)) {
id3->year = asf_intdecode(stream, type, length);
} else {
stream_buffer_advance(stream, length);
}
} else if (!strncmp("replaygain_", utf8buf, 11)) {
char* value = id3buf;
int buf_len = id3buf_remaining;
int len;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
len = 0; // parse_replaygain(utf8buf, value, id3, value, buf_len);
if (len == 0) {
/* Don't need to keep the value */
id3buf = value;
id3buf_remaining = buf_len;
}
} else if (!strcmp("MusicBrainz/Track Id", utf8buf)) {
id3->mb_track_id = id3buf;
asf_utf16LEdecode(stream, length, &id3buf, &id3buf_remaining);
} else {
stream_buffer_advance(stream, length);
}
bytesleft -= 4 + length;
}
stream_buffer_advance(stream, bytesleft);
} else if (asf_guid_match(&current.guid, &asf_guid_content_encryption)
|| asf_guid_match(&current.guid, &asf_guid_extended_content_encryption)) {
DEBUGF("File is encrypted\n");
return ASF_ERROR_ENCRYPTED;
} else {
DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
stream_buffer_advance(stream,current.size - 24);
}
DEBUGF("Parsed object - size = %d\n",(int)current.size);
datalen -= current.size;
}
if (i != (int)subobjects || datalen != 0) {
DEBUGF("header data doesn't match given subobject count\n");
return ASF_ERROR_INVALID_VALUE;
}
DEBUGF("%d subobjects read successfully\n", i);
}
DEBUGF("header validated correctly\n");
return 0;
}
rt_bool_t get_asf_metadata(struct stream_buffer* stream, struct id3_tag* id3)
{
int res;
asf_object_t obj;
asf_waveformatex_t* wfx;
wfx = (asf_waveformatex_t*) rt_malloc(sizeof(asf_waveformatex_t));
if (wfx == RT_NULL) return RT_FALSE;
wfx->audiostream = -1;
res = asf_parse_header(stream, id3, wfx);
if (res < 0) {
DEBUGF("ASF: parsing error - %d\n",res);
return RT_FALSE;
}
if (wfx->audiostream == -1) {
DEBUGF("ASF: No WMA streams found\n");
rt_free(wfx);
return RT_FALSE;
}
asf_read_object_header(&obj, stream);
if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
DEBUGF("ASF: No data object found\n");
rt_free(wfx);
return RT_FALSE;
}
/* Store the current file position - no need to parse the header
again in the codec. The +26 skips the rest of the data object
header.
*/
id3->first_frame_offset = 26;
/* set wfx to user data */
id3->user_data = (rt_uint32_t)wfx;
return RT_TRUE;
}
/* Read an unaligned 32-bit little endian long from buffer. */ /* Read an unaligned 32-bit little endian long from buffer. */
static unsigned long get_long_le(void* buf) static unsigned long get_long_le(void* buf)
{ {
@ -73,7 +744,7 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
int i; int i;
/*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/ /*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/
if (ci->read_filebuf(&tmp8, 1) == 0) { if (stream_buffer_read(stream, &tmp8, 1) == 0) {
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
bytesread++; bytesread++;
@ -96,21 +767,21 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
} }
/* Skip ec_data */ /* Skip ec_data */
ci->advance_buffer(ec_length); stream_buffer_advance(stream, ec_length);
bytesread += ec_length; bytesread += ec_length;
} else { } else {
ec_length = 0; ec_length = 0;
} }
if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; } if (stream_buffer_read(stream, &packet_flags, 1) == 0) { return ASF_ERROR_EOF; }
if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; } if (stream_buffer_read(stream, &packet_property, 1) == 0) { return ASF_ERROR_EOF; }
bytesread += 2; bytesread += 2;
datalen = GETLEN2b((packet_flags >> 1) & 0x03) + datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
GETLEN2b((packet_flags >> 3) & 0x03) + GETLEN2b((packet_flags >> 3) & 0x03) +
GETLEN2b((packet_flags >> 5) & 0x03) + 6; GETLEN2b((packet_flags >> 5) & 0x03) + 6;
if (ci->read_filebuf(data, datalen) == 0) { if (stream_buffer_read(stream, data, datalen) == 0) {
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
@ -152,7 +823,7 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
/* check if we have multiple payloads */ /* check if we have multiple payloads */
if (packet_flags & 0x01) { if (packet_flags & 0x01) {
if (ci->read_filebuf(&tmp8, 1) == 0) { if (stream_buffer_read(stream, &tmp8, 1) == 0) {
return ASF_ERROR_EOF; return ASF_ERROR_EOF;
} }
payload_count = tmp8 & 0x3f; payload_count = tmp8 & 0x3f;
@ -179,7 +850,7 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
*audiobufsize = 0; *audiobufsize = 0;
*packetlength = length - bytesread; *packetlength = length - bytesread;
buf = ci->request_buffer(&bufsize, length); buf = stream_buffer_request(stream, &bufsize, length);
datap = buf; datap = buf;
if (bufsize != length) { if (bufsize != length) {
@ -188,8 +859,8 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
relatively small packets less than about 8KB), but I don't relatively small packets less than about 8KB), but I don't
know what is expected. know what is expected.
*/ */
DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", DEBUGF("Could not read packet (requested %d bytes, received %d), aborting\n",
(int)length,(int)bufsize,(int)ci->curpos); (int)length,(int)bufsize);
return -1; return -1;
} }
@ -269,54 +940,53 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
return 0; return 0;
} }
#include <finsh.h>
/* this is the codec entry point */ /* this is the codec entry point */
void wma_codec_main(void) void wma(const char* filename)
{ {
rt_uint32_t elapsedtime; rt_uint32_t elapsedtime;
int retval; asf_waveformatex_t* wfx;
asf_waveformatex_t wfx;
size_t resume_offset;
int i; int i;
int wmares, res; int wmares, res;
rt_uint8_t* audiobuf; rt_uint8_t* audiobuf;
int audiobufsize; int audiobufsize;
int packetlength = 0; int packetlength = 0;
int errcount = 0; int errcount = 0;
rt_device_t snd = RT_NULL;
/* Remember the resume position - when the codec is opened, the int fd;
playback engine will reset it. */
resume_offset = ci->id3->offset;
/* Copy the format metadata we've stored in the id3 TOC field. This snd = rt_device_find("snd");
saves us from parsing it again here. */ if (snd == RT_NULL)
memcpy(&wfx, ci->id3->toc, sizeof(wfx)); {
rt_kprintf("open audio device failed\n");
return;
}
if (wma_decode_init(&wmadec,&wfx) < 0) { rt_device_open(snd, RT_DEVICE_OFLAG_RDONLY);
LOGF("WMA: Unsupported or corrupt file\n");
retval = CODEC_ERROR; fd = open(filename, O_RDONLY, 0);
if (fd < 0)
{
rt_kprintf("open file: %s failed\n", filename);
return ;
}
/* create stream */
stream = stream_buffer_create(fd);
id3 = &_id3;
/* get meta information */
get_asf_metadata(stream, id3);
wfx = (asf_waveformatex_t*)id3->user_data;
if (wma_decode_init(&wmadec, wfx) < 0) {
DEBUGF("WMA: Unsupported or corrupt file\n");
goto exit; goto exit;
} }
DEBUGF("**************** IN WMA.C ******************\n"); DEBUGF("**************** IN WMA.C ******************\n");
if (resume_offset > ci->id3->first_frame_offset)
{
/* Get start of current packet */
int packet_offset = (resume_offset - ci->id3->first_frame_offset)
% wfx.packet_size;
ci->seek_buffer(resume_offset - packet_offset);
elapsedtime = get_timestamp(&i);
ci->set_elapsed(elapsedtime);
}
else
{
/* Now advance the file position to the first frame */
ci->seek_buffer(ci->id3->first_frame_offset);
elapsedtime = 0;
}
resume_offset = 0;
/* The main decoding loop */ /* The main decoding loop */
res = 1; res = 1;
@ -324,7 +994,7 @@ void wma_codec_main(void)
{ {
errcount = 0; errcount = 0;
new_packet: new_packet:
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, wfx);
if (res < 0) { if (res < 0) {
/* We'll try to recover from a parse error a certain number of /* We'll try to recover from a parse error a certain number of
@ -335,7 +1005,7 @@ new_packet:
if (errcount > 5) { if (errcount > 5) {
goto done; goto done;
} else { } else {
ci->advance_buffer(packetlength); stream_buffer_advance(stream, packetlength);
goto new_packet; goto new_packet;
} }
} else if (res > 0) { } else if (res > 0) {
@ -354,23 +1024,26 @@ new_packet:
if (errcount > 5) { if (errcount > 5) {
goto done; goto done;
} else { } else {
ci->advance_buffer(packetlength); stream_buffer_advance(stream, packetlength);
goto new_packet; goto new_packet;
} }
} else if (wmares > 0) { } else if (wmares > 0) {
/* write to audio device */ /* write to audio device */
elapsedtime += (wmares*10)/(wfx.rate/100); elapsedtime += (wmares*10)/(wfx->rate/100);
ci->set_elapsed(elapsedtime); // ci->set_elapsed(elapsedtime);
} }
ci->yield();
} }
} }
ci->advance_buffer(packetlength); stream_buffer_advance(stream, packetlength);
} }
done: done:
exit: exit:
stream_buffer_close(stream);
stream = RT_NULL;
return ; return ;
} }
FINSH_FUNCTION_EXPORT(wma, wma decode test)