Browse Source

update wma example

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@44 bbd45198-f89e-11dd-88c7-29a3b14d5316
bernard.xiong 15 years ago
parent
commit
91ed3ab183
4 changed files with 743 additions and 608 deletions
  1. 0 544
      bsp/stm32_radio/asf.c
  2. 12 14
      bsp/stm32_radio/libwma/wmadeci.c
  3. 10 2
      bsp/stm32_radio/project.Uv2
  4. 721 48
      bsp/stm32_radio/wma.c

+ 0 - 544
bsp/stm32_radio/asf.c

@@ -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;
-}
-

+ 12 - 14
bsp/stm32_radio/libwma/wmadeci.c

@@ -311,8 +311,8 @@ int wma_decode_init(WMADecodeContext* s, asf_waveformatex_t *wfx)
     fixed32 high_freq;
     fixed64 bps;
     int sample_rate1;
-    int coef_vlc_table;
-
+    int coef_vlc_table;
+
     fixed32 *temp[5];
 
     #ifdef CPU_COLDFIRE
@@ -403,19 +403,19 @@ int wma_decode_init(WMADecodeContext* s, asf_waveformatex_t *wfx)
             sample_rate1 = 11025;
         else if (sample_rate1 >= 8000)
             sample_rate1 = 8000;
-    }
+    }
+
+	{
+		fixed64 tmp, tmp2;
+		fixed64 tim, tmpi;
 
-	{
-		fixed64 tmp, tmp2;
-		fixed64 tim, tmpi;
-
 	    tmp = itofix64(s->bit_rate);
 	    tmp2 = itofix64(s->nb_channels * s->sample_rate);
 	    bps = fixdiv64(tmp, tmp2);
 	    tim = bps * s->frame_len;
 	    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
        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 */
     {
         int a, b, pos, lpos, k, block_len, i, j, n;
-        const uint8_t *table;
-		
-		fixed32 tmp1, tmp2;
+        const uint8_t *table;
+		
+		fixed32 tmp1, tmp2;
 
         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;
             wma_window(s, scratch_buffer, &((*s->frame_out)[ch][index]));
 
-
-
             /* specific fast case for ms-stereo : add to second
                channel if it is not coded */
             if (s->ms_stereo && !s->channel_coded[1])

+ 10 - 2
bsp/stm32_radio/project.Uv2

@@ -11,6 +11,7 @@ Group (finsh)
 Group (Filesystem)
 Group (LwIP)
 Group (mp3)
+Group (libwma)
 
 File 1,1,<.\application.c><application.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,<.\wm8753.c><wm8753.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_bkp.c><stm32f10x_bkp.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,2,<.\mp3\real\arm\asmpoly_thumb2.s><asmpoly_thumb2.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'
@@ -207,7 +215,7 @@ Options 1,0,0  // Target 'RT-Thread/STM32-Radio'
  OCMADSIRAM { 0,0,0,0,32,0,0,1,0 }
  OCMADSIROM { 1,0,0,0,8,0,0,8,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 ()
  ADSCCFLG { 5,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
  ADSCMISC ()
@@ -238,7 +246,7 @@ Options 1,0,0  // Target 'RT-Thread/STM32-Radio'
  ADSLDIF ()
  ADSLDDW ()
   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 }
  FLASH2 (Segger\JL2CM3.dll)
  FLASH3 ("" ())

+ 721 - 48
bsp/stm32_radio/wma.c

@@ -1,3 +1,6 @@
+#include <rtthread.h>
+#include <dfs_posix.h>
+
 #include "libwma/asf.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_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_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. */
 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;
     /*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;
     }
     bytesread++;
@@ -96,21 +767,21 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
        }
 
        /* Skip ec_data */
-       ci->advance_buffer(ec_length);
+       stream_buffer_advance(stream, ec_length);
        bytesread += ec_length;
     } else {
         ec_length = 0;
     }
 
-    if (ci->read_filebuf(&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_flags, 1) == 0) { return ASF_ERROR_EOF; }
+    if (stream_buffer_read(stream, &packet_property, 1) == 0) { return ASF_ERROR_EOF; }
     bytesread += 2;
 
     datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
               GETLEN2b((packet_flags >> 3) & 0x03) +
               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;
     }
 
@@ -152,7 +823,7 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
 
     /* check if we have multiple payloads */
     if (packet_flags & 0x01) {
-        if (ci->read_filebuf(&tmp8, 1) == 0) {
+        if (stream_buffer_read(stream, &tmp8, 1) == 0) {
             return ASF_ERROR_EOF;
         }
         payload_count = tmp8 & 0x3f;
@@ -179,7 +850,7 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
     *audiobufsize = 0;
     *packetlength = length - bytesread;
 
-    buf = ci->request_buffer(&bufsize, length);
+    buf = stream_buffer_request(stream, &bufsize, length);
     datap = buf;
 
     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
            know what is expected.
         */
-        DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
-               (int)length,(int)bufsize,(int)ci->curpos);
+        DEBUGF("Could not read packet (requested %d bytes, received %d), aborting\n",
+               (int)length,(int)bufsize);
         return -1;
     }
 
@@ -269,53 +940,52 @@ static int asf_read_packet(rt_uint8_t** audiobuf, int* audiobufsize, int* packet
         return 0;
 }
 
+#include <finsh.h>
 /* this is the codec entry point */
-void wma_codec_main(void)
+void wma(const char* filename)
 {
     rt_uint32_t elapsedtime;
-    int retval;
-    asf_waveformatex_t wfx;
-    size_t resume_offset;
+    asf_waveformatex_t* wfx;
     int i;
     int wmares, res;
     rt_uint8_t* audiobuf;
     int audiobufsize;
     int packetlength = 0;
     int errcount = 0;
+	rt_device_t snd = RT_NULL;
 
-    /* Remember the resume position - when the codec is opened, the
-       playback engine will reset it. */
-    resume_offset = ci->id3->offset;
+	int fd;
 
-    /* Copy the format metadata we've stored in the id3 TOC field.  This
-       saves us from parsing it again here. */
-    memcpy(&wfx, ci->id3->toc, sizeof(wfx));
+	snd = rt_device_find("snd");
+	if (snd == RT_NULL)
+	{
+		rt_kprintf("open audio device failed\n");
+		return;
+	}
 
-    if (wma_decode_init(&wmadec,&wfx) < 0) {
-        LOGF("WMA: Unsupported or corrupt file\n");
-        retval = CODEC_ERROR;
-        goto exit;
-    }
+	rt_device_open(snd, RT_DEVICE_OFLAG_RDONLY);
 
-    DEBUGF("**************** IN WMA.C ******************\n");
+	fd = open(filename, O_RDONLY, 0);
+	if (fd < 0)
+	{
+		rt_kprintf("open file: %s failed\n", filename);
+		return ;
+	}
 
-    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;
+	/* 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;
     }
 
-    resume_offset = 0;
+    DEBUGF("**************** IN WMA.C ******************\n");
 
     /* The main decoding loop */
 
@@ -324,7 +994,7 @@ void wma_codec_main(void)
     {
         errcount = 0;
 new_packet:
-        res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
+        res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, wfx);
 
         if (res < 0) {
             /* We'll try to recover from a parse error a certain number of
@@ -335,7 +1005,7 @@ new_packet:
             if (errcount > 5) {
                 goto done;
             } else {
-                ci->advance_buffer(packetlength);
+       	 		stream_buffer_advance(stream, packetlength);
                 goto new_packet;
             }
         } else if (res > 0) {
@@ -354,23 +1024,26 @@ new_packet:
                     if (errcount > 5) {
                         goto done;
                     } else {
-                        ci->advance_buffer(packetlength);
+                        stream_buffer_advance(stream, packetlength);
                         goto new_packet;
                     }
                 } else if (wmares > 0) {
                 	/* write to audio device */
-                    elapsedtime += (wmares*10)/(wfx.rate/100);
-                    ci->set_elapsed(elapsedtime);
+                    elapsedtime += (wmares*10)/(wfx->rate/100);
+                    // ci->set_elapsed(elapsedtime);
                 }
-                ci->yield();
             }
         }
 
-        ci->advance_buffer(packetlength);
+        stream_buffer_advance(stream, packetlength);
     }
 
 done:
 exit:
+	stream_buffer_close(stream);
+	stream = RT_NULL;
+
     return ;
 }
+FINSH_FUNCTION_EXPORT(wma, wma decode test)