diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile index fb1228d7fc..62312e2288 100644 --- a/apps/codecs/Makefile +++ b/apps/codecs/Makefile @@ -50,6 +50,7 @@ $(BUILDDIR)/%.a : % $(CODECDEPS) $(OBJDIR)/wav.elf : $(OBJDIR)/wav.o $(OBJDIR)/sid.elf : $(OBJDIR)/sid.o +$(OBJDIR)/adx.elf : $(OBJDIR)/adx.o $(OBJDIR)/aiff.elf : $(OBJDIR)/aiff.o $(OBJDIR)/mpa.elf : $(OBJDIR)/mpa.o $(BUILDDIR)/libmad.a $(OBJDIR)/a52.elf : $(OBJDIR)/a52.o $(BUILDDIR)/liba52.a diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES index 3bd09b4dae..feadcde0ac 100644 --- a/apps/codecs/SOURCES +++ b/apps/codecs/SOURCES @@ -14,6 +14,7 @@ aac.c shorten.c aiff.c sid.c +adx.c #if defined(HAVE_RECORDING) && !defined(SIMULATOR) /* encoders */ mp3_enc.c diff --git a/apps/metadata.c b/apps/metadata.c index 676108116d..1b2dde3a24 100644 --- a/apps/metadata.c +++ b/apps/metadata.c @@ -82,6 +82,7 @@ static const struct format_list formats[] = { AFMT_AIFF, "aif" }, { AFMT_AIFF, "aiff" }, { AFMT_SID, "sid" }, + { AFMT_ADX, "adx" }, #endif }; @@ -1333,6 +1334,118 @@ static bool get_sid_metadata(int fd, struct mp3entry* id3) return true; } +static bool get_adx_metadata(int fd, struct mp3entry* id3) +{ + /* Use the trackname part of the id3 structure as a temporary buffer */ + unsigned char * buf = id3->path; + int chanstart, channels, read_bytes; + int looping = 0, start_adr = 0, end_adr = 0; + + /* try to get the basic header */ + if ((lseek(fd, 0, SEEK_SET) < 0) + || ((read_bytes = read(fd, buf, 0x38)) < 0x38)) + { + DEBUGF("lseek or read failed\n"); + return false; + } + + /* ADX starts with 0x80 */ + if (buf[0] != 0x80) { + DEBUGF("get_adx_metadata: wrong first byte %c\n",buf[0]); + return false; + } + + /* check for a reasonable offset */ + chanstart = ((buf[2] << 8) | buf[3]) + 4; + if (chanstart > 4096) { + DEBUGF("get_adx_metadata: bad chanstart %i\n", chanstart); + return false; + } + + /* check for a workable number of channels */ + channels = buf[7]; + if (channels != 1 && channels != 2) { + DEBUGF("get_adx_metadata: bad channel count %i\n",channels); + return false; + } + + id3->frequency = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11]; + /* 32 samples per 18 bytes */ + id3->bitrate = id3->frequency * channels * 18 * 8 / 32 / 1000; + id3->length = ((unsigned long) + (buf[12] << 24) | (buf[13] << 16) | (buf[14] << 8) | buf[15]) / + id3->frequency * 1000; + id3->vbr = false; + id3->filesize = filesize(fd); + + /* get loop info */ + if (!memcmp(buf+0x10,"\x01\xF4\x03\x00",4)) { + /* Soul Calibur 2 style (type 03) */ + DEBUGF("get_adx_metadata: type 03 found\n"); + /* check if header is too small for loop data */ + if (chanstart-6 < 0x2c) looping=0; + else { + looping = (buf[0x18]) || + (buf[0x19]) || + (buf[0x1a]) || + (buf[0x1b]); + end_adr = (buf[0x28]<<24) | + (buf[0x29]<<16) | + (buf[0x2a]<<8) | + (buf[0x2b]); + + start_adr = ( + (buf[0x1c]<<24) | + (buf[0x1d]<<16) | + (buf[0x1e]<<8) | + (buf[0x1f]) + )/32*channels*18+chanstart; + } + } else if (!memcmp(buf+0x10,"\x01\xF4\x04\x00",4)) { + /* Standard (type 04) */ + DEBUGF("get_adx_metadata: type 04 found\n"); + /* check if header is too small for loop data */ + if (chanstart-6 < 0x38) looping=0; + else { + looping = (buf[0x24]) || + (buf[0x25]) || + (buf[0x26]) || + (buf[0x27]); + end_adr = (buf[0x34]<<24) | + (buf[0x35]<<16) | + (buf[0x36]<<8) | + buf[0x37]; + start_adr = ( + (buf[0x28]<<24) | + (buf[0x29]<<16) | + (buf[0x2a]<<8) | + (buf[0x2b]) + )/32*channels*18+chanstart; + } + } else { + DEBUGF("get_adx_metadata: error, couldn't determine ADX type\n"); + return false; + } + + if (looping) { + /* 2 loops, 10 second fade */ + id3->length = (start_adr-chanstart + 2*(end_adr-start_adr)) + *8 / id3->bitrate + 10000; + } + + /* try to get the channel header */ + if ((lseek(fd, chanstart-6, SEEK_SET) < 0) + || ((read_bytes = read(fd, buf, 6)) < 6)) + { + return false; + } + + /* check channel header */ + if (memcmp(buf, "(c)CRI", 6) != 0) return false; + + return true; +} + #endif /* CONFIG_CODEC == SWCODEC */ static bool get_aiff_metadata(int fd, struct mp3entry* id3) @@ -1629,6 +1742,14 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, } break; + case AFMT_ADX: + if (!get_adx_metadata(fd, &(track->id3))) + { + DEBUGF("get_adx_metadata error\n"); + return false; + } + + break; #endif /* CONFIG_CODEC == SWCODEC */ case AFMT_AIFF: diff --git a/apps/tree.c b/apps/tree.c index 8e155a96a0..3ee3061540 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -104,6 +104,7 @@ const struct filetype filetypes[] = { { "aif", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, { "aiff",TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, { "sid", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, + { "adx", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, #endif { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, diff --git a/docs/CREDITS b/docs/CREDITS index 141f7d6d69..3fc97187c7 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -238,3 +238,4 @@ Andrew Melville Pengxuan Liu Andrew Cupper Thilo-Alexander Ginkel +Adam Gashlin diff --git a/firmware/export/id3.h b/firmware/export/id3.h index dc58178d50..2a42788788 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h @@ -42,6 +42,7 @@ enum { AFMT_SHN, /* Shorten */ AFMT_AIFF, /* Audio Interchange File Format */ AFMT_SID, /* SID File Format */ + AFMT_ADX, /* ADX */ /* New formats must be added to the end of this list */ diff --git a/firmware/id3.c b/firmware/id3.c index 5dfb3fbe50..d14134f215 100644 --- a/firmware/id3.c +++ b/firmware/id3.c @@ -119,6 +119,8 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = AFMT_ENTRY("AIFF", "aiff.codec", NULL, NULL ), /* SID File Format */ AFMT_ENTRY("SID", "sid.codec", NULL, NULL ), + /* ADX File Format */ + AFMT_ENTRY("ADX", "adx.codec", NULL, NULL ), #endif };