2009-07-28 07:28:26 +08:00
|
|
|
#include <rtthread.h>
|
|
|
|
#include <dfs_posix.h>
|
|
|
|
#include <mp3/pub/mp3dec.h>
|
2009-08-19 21:14:50 +08:00
|
|
|
|
|
|
|
#define MP3_AUDIO_BUF_SZ 4096
|
|
|
|
#define MP3_DECODE_MP_CNT 2
|
|
|
|
#define MP3_DECODE_MP_SZ 2560
|
|
|
|
|
|
|
|
#define STATIC_MEMORY_POOL
|
|
|
|
#ifdef STATIC_MEMORY_POOL
|
|
|
|
static rt_uint8_t mempool[(MP3_DECODE_MP_SZ * 2 + 4)* 2]; // 5k x 2
|
|
|
|
static struct rt_mempool _mp;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct mp3_decoder
|
|
|
|
{
|
|
|
|
/* mp3 information */
|
|
|
|
HMP3Decoder decoder;
|
|
|
|
MP3FrameInfo frame_info;
|
|
|
|
rt_uint32_t frames;
|
|
|
|
|
|
|
|
/* mp3 file descriptor */
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
/* mp3 read session */
|
|
|
|
rt_uint8_t *read_buffer;
|
|
|
|
rt_uint8_t* read_ptr;
|
|
|
|
rt_int32_t read_offset;
|
|
|
|
rt_uint32_t bytes_left, bytes_left_before_decoding;
|
|
|
|
|
|
|
|
/* mp3 decode memory pool */
|
|
|
|
rt_mp_t mp;
|
|
|
|
|
|
|
|
/* audio device */
|
|
|
|
rt_device_t snd_device;
|
|
|
|
};
|
|
|
|
|
|
|
|
static rt_err_t mp3_decoder_tx_done(rt_device_t dev, void *buffer)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
/* release memory block to memory pool */
|
|
|
|
rt_mp_free(buffer);
|
|
|
|
|
|
|
|
return RT_EOK;
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_uint8_t mp3_fd_buffer[MP3_AUDIO_BUF_SZ];
|
|
|
|
void mp3_decoder_init(struct mp3_decoder* decoder)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
RT_ASSERT(decoder != RT_NULL);
|
|
|
|
|
|
|
|
/* init read session */
|
|
|
|
decoder->read_ptr = RT_NULL;
|
|
|
|
decoder->bytes_left_before_decoding = decoder->bytes_left = 0;
|
|
|
|
decoder->frames = 0;
|
|
|
|
|
|
|
|
// decoder->read_buffer = rt_malloc(MP3_AUDIO_BUF_SZ);
|
|
|
|
decoder->read_buffer = &mp3_fd_buffer[0];
|
|
|
|
if (decoder->read_buffer == RT_NULL) return;
|
|
|
|
|
|
|
|
/* create memory pool for decoding */
|
|
|
|
#ifdef STATIC_MEMORY_POOL
|
|
|
|
rt_mp_init(&_mp, "mp3", &mempool[0], sizeof(mempool), MP3_DECODE_MP_SZ * 2);
|
|
|
|
decoder->mp = &_mp;
|
|
|
|
#else
|
|
|
|
decoder->mp = rt_mp_create("mp3dec", MP3_DECODE_MP_CNT, MP3_DECODE_MP_SZ * 2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
decoder->decoder = MP3InitDecoder();
|
|
|
|
|
|
|
|
/* open audio device */
|
|
|
|
decoder->snd_device = rt_device_find("snd");
|
|
|
|
if (decoder->snd_device != RT_NULL)
|
|
|
|
{
|
|
|
|
/* set tx complete call back function */
|
|
|
|
rt_device_set_tx_complete(decoder->snd_device, mp3_decoder_tx_done);
|
|
|
|
rt_device_open(decoder->snd_device, RT_DEVICE_OFLAG_WRONLY);
|
|
|
|
}
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
void mp3_decoder_detach(struct mp3_decoder* decoder)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
RT_ASSERT(decoder != RT_NULL);
|
|
|
|
|
|
|
|
/* close audio device */
|
|
|
|
if (decoder->snd_device != RT_NULL)
|
|
|
|
rt_device_close(decoder->snd_device);
|
|
|
|
|
|
|
|
/* release mp3 decoder */
|
|
|
|
MP3FreeDecoder(decoder->decoder);
|
|
|
|
|
|
|
|
#ifdef STATIC_MEMORY_POOL
|
|
|
|
rt_mp_detach(decoder->mp);
|
|
|
|
#else
|
|
|
|
/* delete memory pool for decoding */
|
|
|
|
rt_mp_delete(decoder->mp);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mp3_decoder* mp3_decoder_create()
|
|
|
|
{
|
|
|
|
struct mp3_decoder* decoder;
|
|
|
|
|
|
|
|
/* allocate object */
|
|
|
|
decoder = (struct mp3_decoder*) rt_malloc (sizeof(struct mp3_decoder));
|
|
|
|
if (decoder != RT_NULL)
|
|
|
|
{
|
|
|
|
mp3_decoder_init(decoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
return decoder;
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
void mp3_decoder_delete(struct mp3_decoder* decoder)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
RT_ASSERT(decoder != RT_NULL);
|
|
|
|
|
|
|
|
/* de-init mp3 decoder object */
|
|
|
|
mp3_decoder_detach(decoder);
|
|
|
|
/* release this object */
|
|
|
|
rt_free(decoder);
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_uint16_t is_first = 1;
|
|
|
|
rt_uint32_t current_offset = 0;
|
|
|
|
static rt_int32_t mp3_decoder_fill_buffer(struct mp3_decoder* decoder)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_size_t bytes_read;
|
|
|
|
rt_size_t bytes_to_read;
|
|
|
|
|
|
|
|
// rt_kprintf("left: %d. refilling inbuffer...\n", decoder->bytes_left);
|
|
|
|
if (decoder->bytes_left > 0)
|
|
|
|
{
|
2009-07-28 07:28:26 +08:00
|
|
|
// better: move unused rest of buffer to the start
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_memmove(decoder->read_buffer, decoder->read_ptr, decoder->bytes_left);
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes_to_read = (MP3_AUDIO_BUF_SZ - decoder->bytes_left) & ~(512 - 1);
|
|
|
|
// rt_kprintf("read bytes: %d\n", bytes_to_read);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
if (is_first) is_first = 0;
|
|
|
|
else current_offset += MP3_AUDIO_BUF_SZ - decoder->bytes_left;
|
|
|
|
|
|
|
|
bytes_read = read(decoder->fd, (char *)(decoder->read_buffer + decoder->bytes_left),
|
|
|
|
bytes_to_read);
|
|
|
|
|
2009-07-28 07:28:26 +08:00
|
|
|
if (bytes_read == bytes_to_read)
|
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
decoder->read_ptr = decoder->read_buffer;
|
|
|
|
decoder->read_offset = 0;
|
|
|
|
decoder->bytes_left = decoder->bytes_left + bytes_to_read;
|
2009-07-28 07:28:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rt_kprintf("can't read more data");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
int mp3_decoder_run(struct mp3_decoder* decoder)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
int err;
|
|
|
|
rt_uint16_t* buffer;
|
|
|
|
|
|
|
|
RT_ASSERT(decoder != RT_NULL);
|
|
|
|
|
|
|
|
if ((decoder->read_ptr == RT_NULL) || decoder->bytes_left < 2*MAINBUF_SIZE)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
if(mp3_decoder_fill_buffer(decoder) != 0)
|
|
|
|
return -1;
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
// rt_kprintf("read offset: 0x%08x\n", decoder->read_ptr - decoder->read_buffer);
|
|
|
|
decoder->read_offset = MP3FindSyncWord(decoder->read_ptr, decoder->bytes_left);
|
|
|
|
if (decoder->read_offset < 0)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
|
|
|
rt_kprintf("Error: MP3FindSyncWord returned <0");
|
2009-08-19 21:14:50 +08:00
|
|
|
|
|
|
|
if(mp3_decoder_fill_buffer(decoder) != 0)
|
|
|
|
return -1;
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
2009-08-19 21:14:50 +08:00
|
|
|
// rt_kprintf("sync position: %x\n", decoder->read_offset);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
decoder->read_ptr += decoder->read_offset;
|
|
|
|
decoder->bytes_left -= decoder->read_offset;
|
|
|
|
decoder->bytes_left_before_decoding = decoder->bytes_left;
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
#if 0
|
2009-07-28 07:28:26 +08:00
|
|
|
// check if this is really a valid frame
|
|
|
|
// (the decoder does not seem to calculate CRC, so make some plausibility checks)
|
2009-08-19 21:14:50 +08:00
|
|
|
if (!(MP3GetNextFrameInfo(decoder->decoder, &decoder->frame_info, decoder->read_ptr) == 0 &&
|
|
|
|
decoder->frame_info.nChans == 2 &&
|
|
|
|
decoder->frame_info.version == 0))
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_kprintf("this is an invalid frame\n");
|
2009-07-28 07:28:26 +08:00
|
|
|
// advance data pointer
|
|
|
|
// TODO: handle bytes_left == 0
|
2009-08-19 21:14:50 +08:00
|
|
|
RT_ASSERT(decoder->bytes_left > 0);
|
|
|
|
|
|
|
|
decoder->bytes_left --;
|
|
|
|
decoder->read_ptr ++;
|
2009-07-28 07:28:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
if (decoder->bytes_left < 1024)
|
|
|
|
{
|
|
|
|
if(mp3_decoder_fill_buffer(decoder) != 0)
|
|
|
|
return -1;
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
2009-08-19 21:14:50 +08:00
|
|
|
#endif
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
/* get a decoder buffer */
|
|
|
|
buffer = (rt_uint16_t*)rt_mp_alloc(decoder->mp, RT_WAITING_FOREVER);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
// rt_kprintf("bytes left before decode: %d\n", decoder->bytes_left);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
err = MP3Decode(decoder->decoder, &decoder->read_ptr,
|
|
|
|
(int*)&decoder->bytes_left, (short*)buffer, 0);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
// rt_kprintf("bytes left after decode: %d\n", decoder->bytes_left);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
decoder->frames++;
|
|
|
|
|
|
|
|
if (err != ERR_MP3_NONE)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
switch (err)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
|
|
|
case ERR_MP3_INDATA_UNDERFLOW:
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_kprintf("ERR_MP3_INDATA_UNDERFLOW\n");
|
|
|
|
decoder->bytes_left = 0;
|
|
|
|
if(mp3_decoder_fill_buffer(decoder) != 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ERR_MP3_MAINDATA_UNDERFLOW:
|
|
|
|
/* do nothing - next call to decode will provide more mainData */
|
|
|
|
rt_kprintf("ERR_MP3_MAINDATA_UNDERFLOW\n");
|
2009-07-28 07:28:26 +08:00
|
|
|
break;
|
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
case ERR_MP3_INVALID_FRAMEHEADER:
|
|
|
|
rt_kprintf("ERR_MP3_INVALID_FRAMEHEADER\n");
|
|
|
|
rt_kprintf("current offset: 0x%08x, frames: %d\n", current_offset, decoder->frames);
|
|
|
|
/* dump sector */
|
|
|
|
{
|
|
|
|
rt_uint8_t *ptr;
|
|
|
|
rt_size_t size = 0, col = 0;
|
|
|
|
|
|
|
|
ptr = decoder->read_buffer;
|
|
|
|
rt_kprintf(" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
|
|
|
|
rt_kprintf("00 ");
|
|
|
|
while (size ++ < 512)
|
|
|
|
{
|
|
|
|
rt_kprintf("%02x ", *ptr ++);
|
|
|
|
if (size % 16 == 0) rt_kprintf("\n%02x ", ++col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RT_ASSERT(0);
|
|
|
|
// break;
|
|
|
|
|
|
|
|
case ERR_MP3_INVALID_HUFFCODES:
|
|
|
|
rt_kprintf("ERR_MP3_INVALID_HUFFCODES\n");
|
|
|
|
break;
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
default:
|
|
|
|
rt_kprintf("unknown error: %i\n", err);
|
|
|
|
// skip this frame
|
|
|
|
if (decoder->bytes_left > 0)
|
2009-07-28 07:28:26 +08:00
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
decoder->bytes_left --;
|
|
|
|
decoder->read_ptr ++;
|
|
|
|
}
|
2009-07-28 07:28:26 +08:00
|
|
|
else
|
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
// TODO
|
|
|
|
RT_ASSERT(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
/* release this memory block */
|
|
|
|
rt_mp_free(buffer);
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no error */
|
2009-08-19 21:14:50 +08:00
|
|
|
MP3GetLastFrameInfo(decoder->decoder, &decoder->frame_info);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
// #ifdef MP3_DECODER_TRACE
|
|
|
|
rt_kprintf("Bitrate: %i\n", decoder->frame_info.bitrate);
|
|
|
|
rt_kprintf("%i samples\n", decoder->frame_info.outputSamps);
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
rt_kprintf("%lu Hz, %i kbps\n", decoder->frame_info.samprate,
|
|
|
|
decoder->frame_info.bitrate/1000);
|
|
|
|
// #endif
|
2009-07-28 07:28:26 +08:00
|
|
|
|
2009-08-19 21:14:50 +08:00
|
|
|
/* set sample rate */
|
|
|
|
|
|
|
|
/* write to sound device */
|
|
|
|
rt_device_write(decoder->snd_device, 0, buffer, decoder->frame_info.outputSamps * 2);
|
|
|
|
// rt_mp_free(buffer);
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <finsh.h>
|
|
|
|
void mp3(char* filename)
|
|
|
|
{
|
|
|
|
int fd;
|
2009-08-19 21:14:50 +08:00
|
|
|
struct mp3_decoder* decoder;
|
2009-07-28 07:28:26 +08:00
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY, 0);
|
|
|
|
if (fd >= 0)
|
|
|
|
{
|
2009-08-19 21:14:50 +08:00
|
|
|
decoder = mp3_decoder_create();
|
|
|
|
if (decoder != RT_NULL)
|
|
|
|
{
|
|
|
|
decoder->fd = fd;
|
|
|
|
while (mp3_decoder_run(decoder) != -1);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
/* delete decoder object */
|
|
|
|
mp3_decoder_delete(decoder);
|
|
|
|
}
|
2009-07-28 07:28:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
FINSH_FUNCTION_EXPORT(mp3, mp3 decode test)
|