Index: apps/metadata/rm.c =================================================================== --- apps/metadata/rm.c (revision 0) +++ apps/metadata/rm.c (revision 0) @@ -0,0 +1,388 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id:$ + * + * Copyright (C) 2009 Mohamed Tarek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include + +#include "system.h" +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" +#include "logf.h" + +char title[256]; +char author[256]; +char copyright[256]; +char comment[256]; + +#undef uint32_t +#define uint32_t unsigned int + +struct real_object_t +{ + uint32_t fourcc; + uint32_t size; + uint16_t version; +}; + +#define FOURCC(a,b,c,d) (((a)<<24) | ((b) << 16) | ((c) << 8) | (d)) + +static int read_uint8(int fd, uint8_t* buf) +{ + unsigned char tmp[1]; + int res; + + res=read(fd, tmp, 1); + *buf = tmp[0]; + return res; +} + +static int read_uint16be(int fd, uint16_t* buf) +{ + unsigned char tmp[2]; + int res; + + res=read(fd, tmp, 2); + *buf = (tmp[0] << 8) | tmp[1]; + return res; +} + +static int read_str(int fd, char* buf) +{ + uint8_t len; + int res; + + res = read(fd, &len, 1); + res = read(fd, buf, len); + buf[len]=0; + + return len+1; +} + +static int real_read_object_header(int fd, struct real_object_t* obj) +{ + int n; + + if ((n = read_uint32be(fd, &obj->fourcc)) <= 0) return n; + if ((n = read_uint32be(fd, &obj->size)) <= 0) return n; + if ((n = read_uint16be(fd, &obj->version)) <= 0) return n; + + return 1; +} + +static char* fourcc2str(uint32_t f) +{ + static char res[5]; + + res[0] = (f & 0xff000000) >> 24; + res[1] = (f & 0xff0000) >> 16; + res[2] = (f & 0xff00) >> 8; + res[3] = (f & 0xff); + res[4] = 0; + + return res; +} + +int read_cook_extradata(int fd, uint32_t extradata_size) { + uint32_t var32; + uint16_t var16; + read_uint32be(fd, &var32); + read_uint16be(fd, &var16); + read_uint16be(fd, &var16); + if(extradata_size == 16) { + read_uint32be(fd, &var32); + read_uint16be(fd, &var16); + read_uint16be(fd, &var16); + } + return extradata_size; /* for 'skipped' */ +} + +static int real_read_audio_stream_info(int fd, struct mp3entry *id3) +{ + int skipped = 0; + uint32_t version; + struct real_object_t obj; + memset(&obj,0,sizeof(obj)); + uint32_t header_size; + uint16_t flavor; + uint32_t coded_framesize; + uint8_t interleaver_id_length; + uint32_t interleaver_id; + uint8_t fourcc_length; + uint32_t fourcc = 0; + uint32_t unknown32; + uint16_t unknown16; + uint8_t unknown8; + + read_uint32be(fd, &version); + skipped += 4; + + DEBUGF(" version=0x%04x\n",((version >> 16) & 0xff)); + if (((version >> 16) & 0xff) == 3) { + /* Very old version */ + } else { + real_read_object_header(fd, &obj); + skipped += 10; + read_uint32be(fd, &header_size); + skipped += 4; + /* obj.size will be filled with an unknown value, replaced with header_size */ + DEBUGF(" Object: %s, size: %d bytes, version: 0x%04x\n",fourcc2str(obj.fourcc),header_size,obj.version); + + read_uint16be(fd, &flavor); + read_uint32be(fd, &coded_framesize); + read_uint32be(fd, &unknown32); + read_uint32be(fd, &unknown32); + read_uint32be(fd, &unknown32); + read_uint16be(fd, &unknown16); /* sub_packet_h */ + read_uint16be(fd, &unknown16); /* block_align */ + read_uint16be(fd, &unknown16); /* sub_packet_size */ + read_uint16be(fd, &unknown16); + skipped += 26; + if (((version >> 16) & 0xff) == 5) + { + read_uint16be(fd, &unknown16); + read_uint16be(fd, &unknown16); + read_uint16be(fd, &unknown16); + skipped += 6; + } + read_uint16be(fd, &unknown16); /* sample_rate */ + read_uint32be(fd, &unknown32); + read_uint16be(fd, &unknown16); /* nb_channels */ + skipped += 8; + if (((version >> 16) & 0xff) == 4) + { + read_uint8(fd, &interleaver_id_length); + read_uint32be(fd, &interleaver_id); + read_uint8(fd, &fourcc_length); + read_uint32be(fd, &fourcc); + skipped += 10; + } + if (((version >> 16) & 0xff) == 5) + { + read_uint32be(fd, &interleaver_id); + read_uint32be(fd, &fourcc); + skipped += 8; + } + read_uint8(fd,&unknown8); + read_uint16be(fd,&unknown16); + skipped += 3; + if (((version >> 16) & 0xff) == 5) + { + read_uint8(fd, &unknown8); + skipped += 1; + } + + read_uint32be(fd, &unknown32); /* extradata_size */ + skipped += 4; + if(!strncmp(fourcc2str(fourcc),"cook",4)){ + skipped += read_cook_extradata(fd, unknown32); + id3->codectype = AFMT_COOK; + } + } + + return skipped; +} + +int real_parse_header(int fd, struct mp3entry *id3) +{ + struct real_object_t obj; + memset(&obj,0,sizeof(obj)); + int res; + int skipped; + off_t curpos; + + uint32_t unknown1; + uint32_t unknown2; + + uint32_t max_bitrate; + uint32_t avg_bitrate = 0; + uint32_t max_packet_size; + uint32_t avg_packet_size; + uint32_t packet_count; + uint32_t duration; + uint32_t preroll; + uint32_t index_offset; + uint32_t data_offset; + uint16_t num_streams; + uint16_t flags = 0; + + uint16_t stream_id; + uint32_t start_time; + char desc[256]; + char mimetype[256]; + uint32_t codec_data_size; + uint32_t v; + + uint32_t nb_packets; + uint32_t next_data_off; + uint8_t header_end; + + curpos = lseek(fd, 0, SEEK_SET); + res = real_read_object_header(fd, &obj); + + if (obj.fourcc == FOURCC('.','r','a',0xfd)) + { + /* Very old .ra format - not yet supported */ + return -1; + } + else if (obj.fourcc != FOURCC('.','R','M','F')) + { + return -1; + } + + read_uint32be(fd, &unknown1); + read_uint32be(fd, &unknown2); + + DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); + DEBUGF(" unknown1=%d (0x%08x)\n",unknown1,unknown1); + DEBUGF(" unknown2=%d (0x%08x)\n",unknown2,unknown2); + + res = real_read_object_header(fd, &obj); + header_end = 0; + while(res) + { + DEBUGF("Object: %s, size: %d bytes, version: 0x%04x, pos: %d\n",fourcc2str(obj.fourcc),(int)obj.size,obj.version,(int)curpos); + skipped = 10; + if(obj.fourcc == FOURCC('I','N','D','X')) + break; + switch (obj.fourcc) + { + case FOURCC('P','R','O','P'): /* File properties */ + read_uint32be(fd, &max_bitrate); + read_uint32be(fd, &id3->bitrate); /*avg bitrate*/ + read_uint32be(fd, &max_packet_size); + read_uint32be(fd, &avg_packet_size); + read_uint32be(fd, &packet_count); + read_uint32be(fd, (uint32_t*)&id3->length); + read_uint32be(fd, &preroll); + read_uint32be(fd, &index_offset); + read_uint32be(fd, &data_offset); + read_uint16be(fd, &num_streams); + read_uint16be(fd, &flags); + skipped += 40; + + DEBUGF(" max_bitrate = %d\n",max_bitrate); + DEBUGF(" avg_bitrate = %d\n",avg_bitrate); + DEBUGF(" max_packet_size = %d\n",max_packet_size); + DEBUGF(" avg_packet_size = %d\n",avg_packet_size); + DEBUGF(" packet_count = %d\n",packet_count); + DEBUGF(" duration = %d\n",duration); + DEBUGF(" preroll = %d\n",preroll); + DEBUGF(" index_offset = %d\n",index_offset); + DEBUGF(" data_offset = %d\n",data_offset); + DEBUGF(" num_streams = %d\n",num_streams); + DEBUGF(" flags=0x%04x\n",flags); + break; + + case FOURCC('C','O','N','T'): + /* Four strings - Title, Author, Copyright, Comment. */ + /* Since there only four tags in rm files, id3v1buf could be used. */ + skipped += read_str(fd,id3->id3v1buf[0]); + skipped += read_str(fd,id3->id3v1buf[1]); + skipped += read_str(fd,id3->id3v1buf[2]); + skipped += read_str(fd,id3->id3v1buf[3]); + + id3->title = id3->id3v1buf[0]; + id3->artist = id3->id3v1buf[1]; + id3->comment = id3->id3v1buf[3]; + + DEBUGF(" title=\"%s\"\n",title); + DEBUGF(" author=\"%s\"\n",author); + DEBUGF(" copyright=\"%s\"\n",copyright); + DEBUGF(" comment=\"%s\"\n",comment); + break; + + case FOURCC('M','D','P','R'): /* Media properties */ + read_uint16be(fd,&stream_id); + skipped += 2; + read_uint32be(fd,&max_bitrate); + skipped += 4; + read_uint32be(fd,&avg_bitrate); + skipped += 4; + read_uint32be(fd,&max_packet_size); + skipped += 4; + read_uint32be(fd,&avg_packet_size); + skipped += 4; + read_uint32be(fd,&start_time); + skipped += 4; + read_uint32be(fd,&preroll); + skipped += 4; + read_uint32be(fd,&duration); + skipped += 4; + skipped += read_str(fd,desc); + skipped += read_str(fd,mimetype); + read_uint32be(fd,&codec_data_size); + skipped += 4; + //From ffmpeg: codec_pos = url_ftell(pb); + read_uint32be(fd,&v); + skipped += 4; + + DEBUGF(" stream_id = 0x%04x\n",stream_id); + DEBUGF(" max_bitrate = %d\n",max_bitrate); + DEBUGF(" avg_bitrate = %d\n",avg_bitrate); + DEBUGF(" max_packet_size = %d\n",max_packet_size); + DEBUGF(" avg_packet_size = %d\n",avg_packet_size); + DEBUGF(" start_time = %d\n",start_time); + DEBUGF(" preroll = %d\n",preroll); + DEBUGF(" duration = %d\n",duration); + DEBUGF(" desc=\"%s\"\n",desc); + DEBUGF(" mimetype=\"%s\"\n",mimetype); + DEBUGF(" codec_data_size = %d\n",codec_data_size); + DEBUGF(" v=\"%s\"\n", fourcc2str(v)); + + if (v == FOURCC('.','r','a',0xfd)) + { + skipped += real_read_audio_stream_info(fd, id3); + } + + break; + + case FOURCC('D','A','T','A'): + + read_uint32be(fd,&nb_packets); + skipped += 4; + read_uint32be(fd,&next_data_off); + skipped += 4; + if (!nb_packets && (flags & 4)) + nb_packets = 3600 * 25; + DEBUGF(" data_nb_packets = %d\n",nb_packets); + DEBUGF(" next DATA offset = %d\n",next_data_off); + header_end = 1; + break; + } + if(header_end) break; + curpos = lseek(fd, obj.size - skipped, SEEK_CUR); + res = real_read_object_header(fd, &obj); + } + + + return 0; +} + +bool get_rm_metadata(int fd, struct mp3entry* id3) +{ + real_parse_header(fd, id3); + id3->filesize = filesize(fd); + return true; +} + Index: apps/metadata/metadata_parsers.h =================================================================== --- apps/metadata/metadata_parsers.h (revision 20941) +++ apps/metadata/metadata_parsers.h (working copy) @@ -39,3 +39,4 @@ bool get_a52_metadata(int fd, struct mp3entry* id3); bool get_asf_metadata(int fd, struct mp3entry* id3); bool get_asap_metadata(int fd, struct mp3entry* id3); +bool get_rm_metadata(int fd, struct mp3entry* id3); Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 20941) +++ apps/metadata.c (working copy) @@ -115,6 +115,9 @@ /* Amiga SAP File */ [AFMT_SAP] = AFMT_ENTRY("SAP", "asap", NULL, "sap\0" ), + /* Cook in RM/RA */ + [AFMT_COOK] = + AFMT_ENTRY("COOK", "cook", NULL, "rm\0ra\0" ), #endif }; @@ -372,6 +375,14 @@ id3->filesize = filesize(fd); id3->genre_string = id3_get_num_genre(36); break; + + case AFMT_COOK: + if (!get_rm_metadata(fd, id3)) + { + DEBUGF("get_rm_metadata error\n"); + return false; + } + break; #endif /* CONFIG_CODEC == SWCODEC */ Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 20941) +++ apps/metadata.h (working copy) @@ -61,6 +61,7 @@ AFMT_WMA, /* WMAV1/V2 in ASF */ AFMT_MOD, /* Amiga MOD File Format */ AFMT_SAP, /* Amiga 8Bit SAP Format */ + AFMT_COOK, /* Cook in RM/RA */ #endif /* add new formats at any index above this line to have a sensible order - Index: apps/SOURCES =================================================================== --- apps/SOURCES (revision 20941) +++ apps/SOURCES (working copy) @@ -162,6 +162,7 @@ metadata/wavpack.c metadata/a52.c metadata/asap.c +metadata/rm.c #endif #ifdef HAVE_TAGCACHE tagcache.c Index: apps/filetypes.c =================================================================== --- apps/filetypes.c (revision 20941) +++ apps/filetypes.c (working copy) @@ -83,6 +83,7 @@ { "ape", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "sap" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "rm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, #endif { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, Index: apps/codecs/SOURCES =================================================================== --- apps/codecs/SOURCES (revision 20941) +++ apps/codecs/SOURCES (working copy) @@ -22,6 +22,7 @@ aiff.c speex.c adx.c +cook.c #if defined(HAVE_RECORDING) && !defined(SIMULATOR) /* encoders */ aiff_enc.c Index: apps/codecs/cook.c =================================================================== --- apps/codecs/cook.c (revision 0) +++ apps/codecs/cook.c (revision 0) @@ -0,0 +1,99 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * Copyright (C) 2009 Mohamed Tarek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "codeclib.h" +#include "inttypes.h" + +/********************************************************** + frequency = 44100Hz --> 44100 samples/second on MONO + sample_depth = 16bits --> 16*441 = buffer size. + Number of samples sent to pcmbuf_insert = 441 for 10ms + of sound. + Inside the codec's loop, a counter keeps track of the + elapsed time to make sure we don't go past id3->length. +************************************************************/ + +#define BUF_SIZE 882 +#define NUM_SAMPLES 441 +#define ELAPSED 10 /* milliseconds */ + +CODEC_HEADER + +/* this is the codec entry point */ +enum codec_status codec_main(void) +{ + static int n; + uint8_t buf[BUF_SIZE]; + memset(buf,0,BUF_SIZE); + + +next_track: + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + + while (!*ci->taginfo_ready && !ci->stop_codec) + ci->sleep(1); + + codec_set_replaygain(ci->id3); + + ci->configure(DSP_SET_FREQUENCY, 44100); + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + + ci->set_elapsed(0); + + /* The main decoder loop */ + n = 0; + while (1) + { + ci->yield(); + if (ci->stop_codec || ci->new_track) + goto done; + + if (ci->seek_time) { + + ci->set_elapsed(ci->seek_time); + n = ci->seek_time/10; + memset(buf,0,BUF_SIZE); + ci->seek_complete(); + } + + + ci->pcmbuf_insert(buf, NULL, NUM_SAMPLES); + ci->set_elapsed(n*ELAPSED); /* a multiple of 10ms */ + ++n; + + /******* + * Check if the end of the file is reached.In a real codec, bytes_done + * would be used, rather than id3->length. + */ + if(n*ELAPSED > (int)ci->id3->length) + goto done; + + + } + done : + if (ci->request_next_track()) + goto next_track; + + return CODEC_OK; +} +