/* this is called for each file to process */ enum codec_status codec_run(void) { int error = CODEC_ERROR; intptr_t param; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int64_t page_granule = 0; int stream_init = 0; int sample_rate = 48000; OpusDecoder *st = NULL; OpusHeader header; int ret; unsigned long strtoffset = ci->id3->offset; int skip = 0; int64_t seek_target; uint64_t granule_pos; ogg_malloc_init(); global_stack = 0; #if defined(CPU_COLDFIRE) /* EMAC rounding is disabled because of MULT16_32_Q15, which will be inaccurate with rounding in its current incarnation */ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); #endif /* pre-init the ogg_sync_state buffer, so it won't need many reallocs */ ogg_sync_init(&oy); oy.storage = 64*1024; oy.data = _ogg_malloc(oy.storage); /* allocate output buffer */ uint16_t *output = (uint16_t*) _ogg_malloc(MAX_FRAME_SIZE*sizeof(uint16_t)); ci->seek_buffer(0); ci->set_elapsed(0); while (1) { enum codec_command_action action = ci->get_command(¶m); if (action == CODEC_ACTION_HALT) break; if (action == CODEC_ACTION_SEEK_TIME) { if (st != NULL) { /* calculate granule to seek to (including seek rewind) */ seek_target = (48LL * param) + header.preskip; skip = MIN(seek_target, SEEK_REWIND); seek_target -= skip; LOGF("Opus seek page:%lld,%lld,%ld\n", seek_target, page_granule, (long)param); speex_seek_page_granule(seek_target, page_granule, &oy, &os); } ci->set_elapsed(param); ci->seek_complete(); } /*Get the ogg buffer for writing*/ if (get_more_data(&oy) < 1) { goto done; } /* Loop for all complete pages we got (most likely only one) */ while (ogg_sync_pageout(&oy, &og) == 1) { if (stream_init == 0) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = 1; } /* Add page to the bitstream */ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); granule_pos = page_granule; /* Do this to avoid allocating space for huge comment packets (embedded Album Art) */ if(os.packetno == 1 && ogg_stream_packetpeek(&os, &op) != 1){ ogg_sync_reset(&oy); } while ((ogg_stream_packetout(&os, &op) == 1) && !op.e_o_s) { if (op.packetno == 0){ /* identification header */ if (opus_header_parse(op.packet, op.bytes, &header) == 0) { LOGF("Could not parse header"); goto done; } skip = header.preskip; st = opus_decoder_create(sample_rate, header.channels, &ret); if (ret != OPUS_OK) { LOGF("opus_decoder_create failed %d", ret); goto done; } LOGF("Decoder inited"); codec_set_replaygain(ci->id3); opus_decoder_ctl(st, OPUS_SET_GAIN(header.gain)); ci->configure(DSP_SET_FREQUENCY, sample_rate); ci->configure(DSP_SET_SAMPLE_DEPTH, 16); ci->configure(DSP_SET_STEREO_MODE, (header.channels == 2) ? STEREO_INTERLEAVED : STEREO_MONO); } else if (op.packetno == 1) { /* Comment header */ } else { if (strtoffset) { ci->seek_buffer(strtoffset); ogg_sync_reset(&oy); strtoffset = 0; break;//next page } /* report progress */ ci->set_elapsed((granule_pos - header.preskip) / 48); /* Decode audio packets */ ret = opus_decode(st, op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); if (ret > 0) { if (skip > 0) { if (ret <= skip) { /* entire output buffer is skipped */ skip -= ret; ret = 0; } else { /* part of output buffer is played */ ret -= skip; ci->pcmbuf_insert(&output[skip * header.channels], NULL, ret); skip = 0; } } else { /* entire buffer is played */ ci->pcmbuf_insert(output, NULL, ret); } granule_pos += ret; } else { if (ret < 0) { LOGF("opus_decode failed %d", ret); goto done; } break; } } } } } LOGF("Returned OK"); error = CODEC_OK; done: ogg_malloc_destroy(); return error; }
/* this is the codec entry point */ enum codec_status codec_main(void) { ov_callbacks callbacks; OggVorbis_File vf; ogg_int32_t **pcm; int error; long n; int current_section; int previous_section; int eof; ogg_int64_t vf_offsets[2]; ogg_int64_t vf_dataoffsets; ogg_uint32_t vf_serialnos; ogg_int64_t vf_pcmlengths[2]; ci->configure(DSP_SET_SAMPLE_DEPTH, 24); if (codec_init()) { error = CODEC_ERROR; goto exit; } ogg_malloc_init(); #if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS) if (setjmp(rb_jump_buf) != 0) { /* malloc failed; skip to next track */ error = CODEC_ERROR; goto done; } #endif next_track: while (!*ci->taginfo_ready && !ci->stop_codec) ci->sleep(1); /* 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; /* 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", error); error = CODEC_ERROR; goto done; } if (ci->id3->offset) { ci->advance_buffer(ci->id3->offset); ov_raw_seek(&vf, ci->id3->offset); ci->set_elapsed(ov_time_tell(&vf)); ci->set_offset(ov_raw_tell(&vf)); } previous_section = -1; eof = 0; while (!eof) { ci->yield(); if (ci->stop_codec || ci->new_track) break; if (ci->seek_time) { if (ov_time_seek(&vf, ci->seek_time - 1)) { //ci->logf("ov_time_seek failed"); } 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)) { error = CODEC_ERROR; goto done; } else { previous_section = current_section; } } if (n == 0) { eof = 1; } 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: %u\n", get_max_size(buf)); } #endif if (ci->request_next_track()) { /* Clean things up for the next track */ vf.dataoffsets = NULL; vf.offsets = NULL; vf.serialnos = NULL; vf.pcmlengths = NULL; ov_clear(&vf); goto next_track; } exit: ogg_malloc_destroy(); return error; }