From 928557bb174fdc6ae44d784137e19a61b4f42693 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sat, 22 Dec 2018 20:01:42 -0500 Subject: [PATCH] AAC bitstream format files support Files with extension "aac" in ADTS or ADIF format are now playable. Full credit goes to Igor Poretsky. Change-Id: I413b34e15e5242fea60d3461966ae0984080f530 --- apps/filetypes.c | 1 + lib/rbcodec/SOURCES | 1 + lib/rbcodec/codecs/SOURCES | 1 + lib/rbcodec/codecs/aac_bsf.c | 157 ++++++++++++++++++++++++ lib/rbcodec/codecs/codecs.make | 1 + lib/rbcodec/metadata/aac.c | 122 ++++++++++++++++++ lib/rbcodec/metadata/metadata.c | 3 + lib/rbcodec/metadata/metadata.h | 1 + lib/rbcodec/metadata/metadata_parsers.h | 1 + 9 files changed, 288 insertions(+) create mode 100644 lib/rbcodec/codecs/aac_bsf.c create mode 100644 lib/rbcodec/metadata/aac.c diff --git a/apps/filetypes.c b/apps/filetypes.c index f1a6c389ba..16a00a423f 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -120,6 +120,7 @@ static const struct filetype inbuilt_filetypes[] = { { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "aac", 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 }, diff --git a/lib/rbcodec/SOURCES b/lib/rbcodec/SOURCES index c2a32e9c28..9180237632 100644 --- a/lib/rbcodec/SOURCES +++ b/lib/rbcodec/SOURCES @@ -63,4 +63,5 @@ metadata/vorbis.c metadata/vox.c metadata/wave.c metadata/wavpack.c +metadata/aac.c #endif diff --git a/lib/rbcodec/codecs/SOURCES b/lib/rbcodec/codecs/SOURCES index 039772cf9a..f0787d267d 100644 --- a/lib/rbcodec/codecs/SOURCES +++ b/lib/rbcodec/codecs/SOURCES @@ -42,6 +42,7 @@ vgm.c #if MEMORYSIZE > 2 kss.c #endif +aac_bsf.c #ifdef HAVE_RECORDING diff --git a/lib/rbcodec/codecs/aac_bsf.c b/lib/rbcodec/codecs/aac_bsf.c new file mode 100644 index 0000000000..3bce283958 --- /dev/null +++ b/lib/rbcodec/codecs/aac_bsf.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Codec for aac files without container + * + * Written by Igor B. Poretsky + * + * 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 "libfaad/common.h" +#include "libfaad/structs.h" +#include "libfaad/decoder.h" + +CODEC_HEADER + +/* The maximum buffer size handled by faad. 12 bytes are required by libfaad + * as headroom (see libfaad/bits.c). FAAD_BYTE_BUFFER_SIZE bytes are buffered + * for each frame. */ +#define FAAD_BYTE_BUFFER_SIZE (2048-12) + +static void update_playing_time(void) +{ + ci->set_elapsed((unsigned long)((ci->id3->offset - ci->id3->first_frame_offset) * 8LL / ci->id3->bitrate)); +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, 29); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + size_t n; + int32_t bread; + unsigned int frame_samples; + uint32_t s = 0; + unsigned char c = 0; + long action = CODEC_ACTION_NULL; + intptr_t param; + unsigned char* buffer; + NeAACDecFrameInfo frame_info; + NeAACDecHandle decoder; + NeAACDecConfigurationPtr conf; + + /* Clean and initialize decoder structures */ + if (codec_init()) { + LOGF("FAAD: Codec init error\n"); + return CODEC_ERROR; + } + + ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); + codec_set_replaygain(ci->id3); + + ci->seek_buffer(ci->id3->first_frame_offset); + + /* initialise the sound converter */ + decoder = NeAACDecOpen(); + + if (!decoder) { + LOGF("FAAD: Decode open error\n"); + return CODEC_ERROR; + } + + conf = NeAACDecGetCurrentConfiguration(decoder); + conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */ + NeAACDecSetConfiguration(decoder, conf); + + buffer=ci->request_buffer(&n, FAAD_BYTE_BUFFER_SIZE); + bread = NeAACDecInit(decoder, buffer, n, &s, &c); + if (bread < 0) { + LOGF("FAAD: DecInit: %ld, %d\n", bread, decoder->object_type); + return CODEC_ERROR; + } + ci->advance_buffer(bread); + + if (ci->id3->offset > ci->id3->first_frame_offset) { + /* Resume the desired (byte) position. */ + ci->seek_buffer(ci->id3->offset); + NeAACDecPostSeekReset(decoder, 0); + update_playing_time(); + } else if (ci->id3->elapsed) { + action = CODEC_ACTION_SEEK_TIME; + param = ci->id3->elapsed; + } else { + ci->set_elapsed(0); + ci->set_offset(ci->id3->first_frame_offset); + } + + /* The main decoding loop */ + while (1) { + if (action == CODEC_ACTION_NULL) + action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + /* Deal with any pending seek requests */ + if (action == CODEC_ACTION_SEEK_TIME) { + /* Seek to the desired time position. */ + ci->seek_buffer(ci->id3->first_frame_offset + (uint32_t)((uint64_t)param * ci->id3->bitrate / 8)); + ci->set_elapsed((unsigned long)param); + NeAACDecPostSeekReset(decoder, 0); + ci->seek_complete(); + } + + action = CODEC_ACTION_NULL; + + /* Request the required number of bytes from the input buffer */ + buffer=ci->request_buffer(&n, FAAD_BYTE_BUFFER_SIZE); + + if (n == 0) /* End of Stream */ + break; + + /* Decode one block - returned samples will be host-endian */ + if (NeAACDecDecode(decoder, &frame_info, buffer, n) == NULL || frame_info.error > 0) { + LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); + return CODEC_ERROR; + } + + /* Advance codec buffer (no need to call set_offset because of this) */ + ci->advance_buffer(frame_info.bytesconsumed); + + /* Output the audio */ + ci->yield(); + frame_samples = frame_info.samples >> 1; + ci->pcmbuf_insert(&decoder->time_out[0][0], &decoder->time_out[1][0], frame_samples); + + /* Update the elapsed-time indicator */ + update_playing_time(); + } + + LOGF("AAC: Decoding complete\n"); + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/codecs.make b/lib/rbcodec/codecs/codecs.make index 8934272fcf..afb8396938 100644 --- a/lib/rbcodec/codecs/codecs.make +++ b/lib/rbcodec/codecs/codecs.make @@ -181,6 +181,7 @@ $(CODECDIR)/sgc.codec : $(CODECDIR)/libsgc.a $(CODECDIR)/libemu2413.a $(CODECDIR)/vgm.codec : $(CODECDIR)/libvgm.a $(CODECDIR)/libemu2413.a $(CODECDIR)/kss.codec : $(CODECDIR)/libkss.a $(CODECDIR)/libemu2413.a $(CODECDIR)/opus.codec : $(CODECDIR)/libopus.a $(TLSFLIB) +$(CODECDIR)/aac_bsf.codec : $(CODECDIR)/libfaad.a $(CODECS): $(CODEC_LIBS) # this must be last in codec dependency list diff --git a/lib/rbcodec/metadata/aac.c b/lib/rbcodec/metadata/aac.c new file mode 100644 index 0000000000..82adeacbde --- /dev/null +++ b/lib/rbcodec/metadata/aac.c @@ -0,0 +1,122 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Parsing ADTS and ADIF headers + * + * Written by Igor B. Poretsky + * + * 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 "platform.h" + +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" + +static const int sample_rates[] = +{ + 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, + 7350, 0, 0, 0 +}; + +static bool check_adts_syncword(int fd) +{ + uint16_t syncword; + + read_uint16be(fd, &syncword); + return (syncword & 0xFFF6) == 0xFFF0; +} + +bool get_aac_metadata(int fd, struct mp3entry *entry) +{ + unsigned char buf[5]; + + entry->title = NULL; + entry->tracknum = 0; + entry->discnum = 0; + entry->id3v1len = 0; + entry->id3v2len = getid3v2len(fd); + entry->first_frame_offset = entry->id3v2len; + entry->filesize = filesize(fd) - entry->first_frame_offset; + entry->needs_upsampling_correction = false; + + if (entry->id3v2len) + setid3v2title(fd, entry); + + if (-1 == lseek(fd, entry->first_frame_offset, SEEK_SET)) + return false; + + if (check_adts_syncword(fd)) + { + int frames; + int stat_length; + uint64_t total; + if (read(fd, buf, 5) != 5) + return false; + entry->frequency = sample_rates[(buf[0] >> 2) & 0x0F]; + entry->vbr = ((buf[3] & 0x1F) == 0x1F) + && ((buf[4] & 0xFC) == 0xFC); + stat_length = entry->frequency >> ((entry->vbr) ? 5 : 7); + for (frames = 1, total = 0; frames < stat_length; frames++) + { + unsigned int frame_length = (((unsigned int)buf[1] & 0x3) << 11) + | ((unsigned int)buf[2] << 3) + | ((unsigned int)buf[3] >> 5); + total += frame_length; + if (frame_length < 7) + break; + if (-1 == lseek(fd, frame_length - 7, SEEK_CUR)) + break; + if (!check_adts_syncword(fd)) + break; + if (read(fd, buf, 5) != 5) + break; + } + entry->bitrate = (unsigned int)((total * entry->frequency / frames + 64000) / 128000); + if (entry->frequency <= 24000) + { + entry->frequency <<= 1; + entry->needs_upsampling_correction = true; + } + } + else + { + uint32_t bitrate; + if (-1 == lseek(fd, entry->first_frame_offset, SEEK_SET)) + return false; + if (read(fd, buf, 5) != 5) + return false; + if (memcmp(buf, "ADIF", 4)) + return false; + if (-1 == lseek(fd, (buf[4] & 0x80) ? (entry->first_frame_offset + 9) : entry->first_frame_offset, SEEK_SET)) + return false; + read_uint32be(fd, &bitrate); + entry->vbr = (bitrate & 0x10000000) != 0; + entry->bitrate = ((bitrate & 0xFFFFFE0) + 16000) / 32000; + read_uint32be(fd, (uint32_t*)(&(entry->frequency))); + entry->frequency = sample_rates[(entry->frequency >> (entry->vbr ? 23 : 3)) & 0x0F]; + } + entry->length = (unsigned long)((entry->filesize * 8LL + (entry->bitrate >> 1)) / entry->bitrate); + + return true; +} diff --git a/lib/rbcodec/metadata/metadata.c b/lib/rbcodec/metadata/metadata.c index 7ca4b1afd2..c24a27df2b 100644 --- a/lib/rbcodec/metadata/metadata.c +++ b/lib/rbcodec/metadata/metadata.c @@ -235,6 +235,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = /* Opus */ [AFMT_OPUS] = AFMT_ENTRY("Opus", "opus", NULL, get_ogg_metadata, "opus\0"), + /* AAC bitstream format */ + [AFMT_AAC_BSF] = + AFMT_ENTRY("AAC", "aac_bsf", NULL, get_aac_metadata, "aac\0"), #endif }; diff --git a/lib/rbcodec/metadata/metadata.h b/lib/rbcodec/metadata/metadata.h index 18cfce7523..a7ebcf16e5 100644 --- a/lib/rbcodec/metadata/metadata.h +++ b/lib/rbcodec/metadata/metadata.h @@ -90,6 +90,7 @@ enum AFMT_VGM, /* VGM (Video Game Music Format) */ AFMT_KSS, /* KSS (MSX computer KSS Music File) */ AFMT_OPUS, /* Opus (see http://www.opus-codec.org ) */ + AFMT_AAC_BSF, #endif /* add new formats at any index above this line to have a sensible order - diff --git a/lib/rbcodec/metadata/metadata_parsers.h b/lib/rbcodec/metadata/metadata_parsers.h index 304e393538..9f03c79bb5 100644 --- a/lib/rbcodec/metadata/metadata_parsers.h +++ b/lib/rbcodec/metadata/metadata_parsers.h @@ -56,4 +56,5 @@ bool get_hes_metadata(int fd, struct mp3entry* id3); bool get_sgc_metadata(int fd, struct mp3entry* id3); bool get_vgm_metadata(int fd, struct mp3entry* id3); bool get_kss_metadata(int fd, struct mp3entry* id3); +bool get_aac_metadata(int fd, struct mp3entry* id3); #endif /* CONFIG_CODEC == SWCODEC */