/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 Björn Stenberg * * 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 "libtremor/ivorbisfile.h" #include "libtremor/ogg.h" #ifdef SIMULATOR #include #endif CODEC_HEADER #if (CONFIG_PLATFORM & PLATFORM_NATIVE) #include jmp_buf rb_jump_buf; #endif /* Some standard functions and variables needed by Tremor */ static size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) { (void)datasource; return ci->read_filebuf(ptr, nmemb*size); } static int initial_seek_handler(void *datasource, ogg_int64_t offset, int whence) { (void)datasource; (void)offset; (void)whence; return -1; } static int seek_handler(void *datasource, ogg_int64_t offset, int whence) { (void)datasource; if (whence == SEEK_CUR) { offset += ci->curpos; } else if (whence == SEEK_END) { offset += ci->filesize; } if (ci->seek_buffer(offset)) { return 0; } return -1; } static int close_handler(void *datasource) { (void)datasource; return 0; } static long tell_handler(void *datasource) { (void)datasource; return ci->curpos; } /* This sets the DSP parameters based on the current logical bitstream * (sampling rate, number of channels, etc). */ static bool vorbis_set_codec_parameters(OggVorbis_File *vf) { vorbis_info *vi; vi = ov_info(vf, -1); if (vi == NULL) { return false; } ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); codec_set_replaygain(ci->id3); if (vi->channels == 2) { ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); } else if (vi->channels == 1) { ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); } return true; } /* this is the codec entry point */ enum codec_status codec_main(enum codec_entry_call_reason reason) { if (reason == CODEC_LOAD) { if (codec_init()) return CODEC_ERROR; ci->configure(DSP_SET_SAMPLE_DEPTH, 24); } return CODEC_OK; } /* this is called for each file to process */ enum codec_status codec_run(void) { ov_callbacks callbacks; OggVorbis_File vf; ogg_int32_t **pcm; /* mark variable volatile because it can be modified between setjmp() and * longjmp() so the compiler needs to know it should not put it in a register, * see "man longjmp" for details. */ volatile int error = CODEC_ERROR; long n; int current_section; int previous_section; ogg_int64_t vf_offsets[2]; ogg_int64_t vf_dataoffsets; ogg_uint32_t vf_serialnos; ogg_int64_t vf_pcmlengths[2]; intptr_t param; #if (CONFIG_PLATFORM & PLATFORM_NATIVE) if (setjmp(rb_jump_buf) != 0) { /* malloc failed; finish with this track */ goto done; } #endif ogg_malloc_init(); /* Create a decoder instance */ callbacks.read_func = read_handler; callbacks.seek_func = initial_seek_handler; callbacks.tell_func = tell_handler; callbacks.close_func = close_handler; ci->seek_buffer(0); /* Open a non-seekable stream */ error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks); /* If the non-seekable open was successful, we need to supply the missing * data to make it seekable. This is a hack, but it's reasonable since we * don't want to run the whole file through the buffer before we start * playing. Using Tremor's seekable open routine would cause us to do * this, so we pretend not to be seekable at first. Then we fill in the * missing fields of vf with 1) information in ci->id3, and 2) info * obtained by Tremor in the above ov_open call. * * Note that this assumes there is only ONE logical Vorbis bitstream in our * physical Ogg bitstream. This is verified in metadata.c, well before we * get here. */ if (!error) { ogg_free(vf.offsets); ogg_free(vf.dataoffsets); ogg_free(vf.serialnos); vf.offsets = vf_offsets; vf.dataoffsets = &vf_dataoffsets; vf.serialnos = &vf_serialnos; vf.pcmlengths = vf_pcmlengths; vf.offsets[0] = 0; vf.offsets[1] = ci->id3->filesize; vf.dataoffsets[0] = vf.offset; vf.pcmlengths[0] = 0; vf.pcmlengths[1] = ci->id3->samples; vf.serialnos[0] = vf.current_serialno; vf.callbacks.seek_func = seek_handler; vf.seekable = 1; vf.end = ci->id3->filesize; vf.ready_state = OPENED; vf.links = 1; } else { DEBUGF("Vorbis: ov_open failed: %d\n", error); goto done; } if (ci->id3->offset) { ci->seek_buffer(ci->id3->offset); ov_raw_seek(&vf, ci->id3->offset); ci->set_offset(ov_raw_tell(&vf)); } else if (ci->id3->elapsed) { ov_time_seek(&vf, ci->id3->elapsed); } ci->set_elapsed(ov_time_tell(&vf)); previous_section = -1; while (1) { long action = ci->get_command(¶m); if (action == CODEC_ACTION_HALT) break; if (action == CODEC_ACTION_SEEK_TIME) { if (ov_time_seek(&vf, param)) { //ci->logf("ov_time_seek failed"); } ci->set_elapsed(ov_time_tell(&vf)); ci->seek_complete(); } /* Read host-endian signed 24-bit PCM samples */ n = ov_read_fixed(&vf, &pcm, 1024, ¤t_section); /* Change DSP and buffer settings for this bitstream */ if (current_section != previous_section) { if (!vorbis_set_codec_parameters(&vf)) { goto done; } else { previous_section = current_section; } } if (n == 0) { break; } else if (n < 0) { DEBUGF("Vorbis: Error decoding frame\n"); } else { ci->pcmbuf_insert(pcm[0], pcm[1], n); ci->set_offset(ov_raw_tell(&vf)); ci->set_elapsed(ov_time_tell(&vf)); } } error = CODEC_OK; done: #if 0 /* defined(SIMULATOR) */ { size_t bufsize; void* buf = ci->codec_get_buffer(&bufsize); DEBUGF("Vorbis: Memory max: %zu\n", get_max_size(buf)); } #endif ogg_malloc_destroy(); /* Clean things up for the next track */ vf.dataoffsets = NULL; vf.offsets = NULL; vf.serialnos = NULL; vf.pcmlengths = NULL; ov_clear(&vf); return error; }