| /*************************************************************************** |
| * __________ __ ___. |
| * 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 "codeclib.h" |
| #include "librm/rm.h" |
| #include "libfaad/common.h" |
| #include "libfaad/structs.h" |
| #include "libfaad/decoder.h" |
| #include "libfaad/output.h" |
| |
| CODEC_HEADER |
| |
| static void init_rm(RMContext *rmctx) |
| { |
| memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); |
| } |
| |
| static RMContext rmctx; |
| static RMPacket pkt; |
| |
| /* 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) |
| { |
| static NeAACDecFrameInfo frame_info; |
| NeAACDecHandle decoder; |
| size_t n; |
| void *ret; |
| unsigned int i; |
| unsigned char* buffer; |
| int err, consumed, pkt_offset, skipped = 0; |
| uint32_t s = 0; /* sample rate */ |
| unsigned char c = 0; /* channels */ |
| int playback_on = -1; |
| size_t resume_offset; |
| intptr_t param; |
| enum codec_command_action action = CODEC_ACTION_NULL; |
| |
| if (codec_init()) { |
| DEBUGF("FAAD: Codec init error\n"); |
| return CODEC_ERROR; |
| } |
| |
| resume_offset = ci->id3->offset; |
| |
| ci->memset(&rmctx,0,sizeof(RMContext)); |
| ci->memset(&pkt,0,sizeof(RMPacket)); |
| |
| ci->seek_buffer(0); |
| |
| init_rm(&rmctx); |
| ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
| codec_set_replaygain(ci->id3); |
| |
| /* initialise the sound converter */ |
| decoder = NeAACDecOpen(); |
| |
| if (!decoder) { |
| DEBUGF("FAAD: Decode open error\n"); |
| return CODEC_ERROR; |
| } |
| |
| NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); |
| conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */ |
| NeAACDecSetConfiguration(decoder, conf); |
| |
| decoder->config.defObjectType = rmctx.codec_extradata[0]; |
| decoder->config.defSampleRate = rmctx.sample_rate; |
| err = NeAACDecInit(decoder, NULL, 0, &s, &c); |
| |
| if (err) { |
| DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); |
| return CODEC_ERROR; |
| } |
| |
| /* check for a mid-track resume and force a seek time accordingly */ |
| if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { |
| resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; |
| /* put number of subpackets to skip in resume_offset */ |
| resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); |
| param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); |
| action = CODEC_ACTION_SEEK_TIME; |
| } |
| |
| ci->id3->frequency = s; /* FIXME: Won't get it to the UI */ |
| ci->set_elapsed(0); |
| ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
| |
| /* The main decoding loop */ |
| while (1) { |
| if (action == CODEC_ACTION_NULL) |
| action = ci->get_command(¶m); |
| |
| if (action == CODEC_ACTION_HALT) |
| break; |
| |
| if (action == CODEC_ACTION_SEEK_TIME) { |
| /* Do not allow seeking beyond the file's length */ |
| if ((unsigned) param > ci->id3->length) { |
| ci->set_elapsed(ci->id3->length); |
| ci->seek_complete(); |
| break; |
| } |
| |
| ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
| |
| /* Seek to the start of the track */ |
| if (param == 0) { |
| ci->set_elapsed(0); |
| ci->seek_complete(); |
| action = CODEC_ACTION_NULL; |
| continue; |
| } |
| |
| skipped = 0; |
| while(1) { |
| buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000); |
| pkt_offset = skipped - pkt.length; |
| consumed = rm_get_packet(&buffer, &rmctx, &pkt); |
| if(consumed < 0 && playback_on != 0) { |
| if(playback_on == -1) { |
| /* Error only if packet-parsing failed and playback hadn't started */ |
| DEBUGF("rm_get_packet failed\n"); |
| ci->seek_complete(); |
| return CODEC_ERROR; |
| } |
| else { |
| ci->seek_complete(); |
| return CODEC_OK; |
| } |
| } |
| skipped += pkt.length; |
| |
| if(pkt.timestamp > (unsigned)param) |
| break; |
| |
| ci->advance_buffer(pkt.length); |
| } |
| ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE); |
| buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000); |
| NeAACDecPostSeekReset(decoder, decoder->frame); |
| ci->set_elapsed(pkt.timestamp); |
| ci->seek_complete(); |
| } |
| |
| action = CODEC_ACTION_NULL; |
| |
| /* Request the required number of bytes from the input buffer */ |
| buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000); |
| consumed = rm_get_packet(&buffer, &rmctx, &pkt); |
| |
| if(consumed < 0 && playback_on != 0) { |
| if(playback_on == -1) { |
| /* Error only if packet-parsing failed and playback hadn't started */ |
| DEBUGF("rm_get_packet failed\n"); |
| return CODEC_ERROR; |
| } |
| else |
| break; |
| } |
| |
| playback_on = 1; |
| if (pkt.timestamp >= ci->id3->length) |
| break; |
| |
| /* Decode one block - returned samples will be host-endian */ |
| for(i = 0; i < rmctx.sub_packet_cnt; i++) { |
| ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]); |
| buffer += rmctx.sub_packet_lengths[i]; |
| if (frame_info.error > 0) { |
| DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); |
| return CODEC_ERROR; |
| } |
| ci->pcmbuf_insert(decoder->time_out[0], |
| decoder->time_out[1], |
| decoder->frameLength); |
| ci->set_elapsed(pkt.timestamp); |
| } |
| |
| ci->advance_buffer(pkt.length); |
| } |
| |
| return CODEC_OK; |
| } |