| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2005 Dave Chapman |
| * |
| * 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 "libm4a/m4a.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) |
| |
| /* 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) |
| { |
| /* Note that when dealing with QuickTime/MPEG4 files, terminology is |
| * a bit confusing. Files with sound are split up in chunks, where |
| * each chunk contains one or more samples. Each sample in turn |
| * contains a number of "sound samples" (the kind you refer to with |
| * the sampling frequency). |
| */ |
| size_t n; |
| demux_res_t demux_res; |
| stream_t input_stream; |
| uint32_t sound_samples_done; |
| uint32_t elapsed_time; |
| int file_offset; |
| int framelength; |
| int lead_trim = 0; |
| unsigned int i; |
| unsigned char* buffer; |
| NeAACDecFrameInfo frame_info; |
| NeAACDecHandle decoder; |
| int err; |
| uint32_t seek_idx = 0; |
| uint32_t s = 0; |
| uint32_t sbr_fac = 1; |
| unsigned char c = 0; |
| void *ret; |
| intptr_t param; |
| |
| /* Clean and initialize decoder structures */ |
| memset(&demux_res , 0, sizeof(demux_res)); |
| if (codec_init()) { |
| LOGF("FAAD: Codec init error\n"); |
| return CODEC_ERROR; |
| } |
| |
| file_offset = ci->id3->offset; |
| |
| ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
| codec_set_replaygain(ci->id3); |
| |
| stream_create(&input_stream,ci); |
| |
| ci->seek_buffer(ci->id3->first_frame_offset); |
| |
| /* if qtmovie_read returns successfully, the stream is up to |
| * the movie data, which can be used directly by the decoder */ |
| if (!qtmovie_read(&input_stream, &demux_res)) { |
| LOGF("FAAD: File init error\n"); |
| return CODEC_ERROR; |
| } |
| |
| /* initialise the sound converter */ |
| decoder = NeAACDecOpen(); |
| |
| if (!decoder) { |
| LOGF("FAAD: Decode open error\n"); |
| return CODEC_ERROR; |
| } |
| |
| NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); |
| conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */ |
| NeAACDecSetConfiguration(decoder, conf); |
| |
| err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c); |
| if (err) { |
| LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); |
| return CODEC_ERROR; |
| } |
| |
| #ifdef SBR_DEC |
| /* Check for need of special handling for seek/resume and elapsed time. */ |
| if (ci->id3->needs_upsampling_correction) { |
| sbr_fac = 2; |
| } else { |
| sbr_fac = 1; |
| } |
| #endif |
| |
| ci->id3->frequency = s; |
| |
| i = 0; |
| |
| if (file_offset > 0) { |
| /* Resume the desired (byte) position. Important: When resuming SBR |
| * upsampling files the resulting sound_samples_done must be expanded |
| * by a factor of 2. This is done via using sbr_fac. */ |
| if (m4a_seek_raw(&demux_res, &input_stream, file_offset, |
| &sound_samples_done, (int*) &i)) { |
| sound_samples_done *= sbr_fac; |
| elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); |
| ci->set_elapsed(elapsed_time); |
| } else { |
| sound_samples_done = 0; |
| } |
| NeAACDecPostSeekReset(decoder, i); |
| } else { |
| sound_samples_done = 0; |
| } |
| |
| if (i == 0) |
| { |
| lead_trim = ci->id3->lead_trim; |
| } |
| |
| /* The main decoding loop */ |
| while (i < demux_res.num_sample_byte_sizes) { |
| enum codec_command_action 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. Important: When seeking in SBR |
| * upsampling files the seek_time must be divided by 2 when calling |
| * m4a_seek and the resulting sound_samples_done must be expanded |
| * by a factor 2. This is done via using sbr_fac. */ |
| if (m4a_seek(&demux_res, &input_stream, |
| (param/10/sbr_fac)*(ci->id3->frequency/100), |
| &sound_samples_done, (int*) &i)) { |
| sound_samples_done *= sbr_fac; |
| elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); |
| ci->set_elapsed(elapsed_time); |
| seek_idx = 0; |
| |
| if (i == 0) |
| { |
| lead_trim = ci->id3->lead_trim; |
| } |
| } |
| NeAACDecPostSeekReset(decoder, i); |
| ci->seek_complete(); |
| } |
| |
| /* There can be gaps between chunks, so skip ahead if needed. It |
| * doesn't seem to happen much, but it probably means that a |
| * "proper" file can have chunks out of order. Why one would want |
| * that an good question (but files with gaps do exist, so who |
| * knows?), so we don't support that - for now, at least. |
| */ |
| file_offset = m4a_check_sample_offset(&demux_res, i, &seek_idx); |
| |
| if (file_offset > ci->curpos) |
| { |
| ci->advance_buffer(file_offset - ci->curpos); |
| } |
| else if (file_offset == 0) |
| { |
| LOGF("AAC: get_sample_offset error\n"); |
| return CODEC_ERROR; |
| } |
| |
| /* Request the required number of bytes from the input buffer */ |
| buffer=ci->request_buffer(&n, FAAD_BYTE_BUFFER_SIZE); |
| |
| /* Decode one block - returned samples will be host-endian */ |
| ret = NeAACDecDecode(decoder, &frame_info, buffer, n); |
| |
| /* NeAACDecDecode may sometimes return NULL without setting error. */ |
| if (ret == 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(); |
| |
| /* Gather number of samples for the decoded frame. */ |
| framelength = (frame_info.samples >> 1) - lead_trim; |
| |
| if (i == demux_res.num_sample_byte_sizes - 1 && framelength > 0) |
| { |
| framelength -= ci->id3->tail_trim; |
| } |
| |
| if (framelength > 0) |
| { |
| ci->pcmbuf_insert(&decoder->time_out[0][lead_trim], |
| &decoder->time_out[1][lead_trim], |
| framelength); |
| } |
| |
| if (lead_trim > 0) |
| { |
| /* frame_info.samples can be 0 for the first frame */ |
| lead_trim -= (i > 0 || frame_info.samples) |
| ? (frame_info.samples >> 1) : (uint32_t)framelength; |
| |
| if (lead_trim < 0 || ci->id3->lead_trim == 0) |
| { |
| lead_trim = 0; |
| } |
| } |
| |
| /* Update the elapsed-time indicator */ |
| sound_samples_done += framelength; |
| elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); |
| ci->set_elapsed(elapsed_time); |
| i++; |
| } |
| |
| LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done); |
| return CODEC_OK; |
| } |