From f40bfc9267b13b54e6379dfe7539447662879d24 Mon Sep 17 00:00:00 2001 From: Sean Bartell Date: Sat, 25 Jun 2011 21:32:25 -0400 Subject: Add codecs to librbcodec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97 Reviewed-on: http://gerrit.rockbox.org/137 Reviewed-by: Nils Wallménius Tested-by: Nils Wallménius --- lib/rbcodec/codecs/flac.c | 536 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 lib/rbcodec/codecs/flac.c (limited to 'lib/rbcodec/codecs/flac.c') diff --git a/lib/rbcodec/codecs/flac.c b/lib/rbcodec/codecs/flac.c new file mode 100644 index 0000000000..e10403819c --- /dev/null +++ b/lib/rbcodec/codecs/flac.c @@ -0,0 +1,536 @@ +/*************************************************************************** + * __________ __ ___. + * 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 + +CODEC_HEADER + +static FLACContext fc IBSS_ATTR_FLAC; + +/* The output buffers containing the decoded samples (channels 0 and 1) */ +static int32_t decoded0[MAX_BLOCKSIZE] IBSS_ATTR_FLAC; +static int32_t decoded1[MAX_BLOCKSIZE] IBSS_ATTR_FLAC; +static int32_t decoded2[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_LARGE_IRAM; +static int32_t decoded3[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_LARGE_IRAM; +static int32_t decoded4[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_XLARGE_IRAM; +static int32_t decoded5[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_XLARGE_IRAM; + +#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000 + +/* Notes about seeking: + + The full seek table consists of: + uint64_t sample (only 36 bits are used) + uint64_t offset + uint32_t blocksize + + We also limit the sample and offset values to 32-bits - Rockbox doesn't + support files bigger than 2GB on FAT32 filesystems. + + The reference FLAC encoder produces a seek table with points every + 10 seconds, but this can be overridden by the user when encoding a file. + + With the default settings, a typical 4 minute track will contain + 24 seek points. + + Taking the extreme case of a Rockbox supported file to be a 2GB (compressed) + 16-bit/44.1KHz mono stream with a likely uncompressed size of 4GB: + Total duration is: 48694 seconds (about 810 minutes - 13.5 hours) + Total number of seek points: 4869 + + Therefore we limit the number of seek points to 5000. This is a + very extreme case, and requires 5000*8=40000 bytes of storage. + + If we come across a FLAC file with more than this number of seekpoints, we + just use the first 5000. + +*/ + +struct FLACseekpoints { + uint32_t sample; + uint32_t offset; + uint16_t blocksize; +}; + +static struct FLACseekpoints seekpoints[MAX_SUPPORTED_SEEKTABLE_SIZE]; +static int nseekpoints; + +static int8_t *bit_buffer; +static size_t buff_size; + +static bool flac_init(FLACContext* fc, int first_frame_offset) +{ + unsigned char buf[255]; + bool found_streaminfo=false; + uint32_t seekpoint_hi,seekpoint_lo; + uint32_t offset_hi,offset_lo; + uint16_t blocksize; + int endofmetadata=0; + uint32_t blocklength; + + ci->memset(fc,0,sizeof(FLACContext)); + nseekpoints=0; + + fc->sample_skip = 0; + + /* Reset sample buffers */ + memset(decoded0, 0, sizeof(decoded0)); + memset(decoded1, 0, sizeof(decoded1)); + memset(decoded2, 0, sizeof(decoded2)); + memset(decoded3, 0, sizeof(decoded3)); + memset(decoded4, 0, sizeof(decoded4)); + memset(decoded5, 0, sizeof(decoded5)); + + /* Set sample buffers in decoder structure */ + fc->decoded[0] = decoded0; + fc->decoded[1] = decoded1; + fc->decoded[2] = decoded2; + fc->decoded[3] = decoded3; + fc->decoded[4] = decoded4; + fc->decoded[5] = decoded5; + + + /* Skip any foreign tags at start of file */ + ci->seek_buffer(first_frame_offset); + + fc->metadatalength = first_frame_offset; + + if (ci->read_filebuf(buf, 4) < 4) + { + return false; + } + + if (ci->memcmp(buf,"fLaC",4) != 0) + { + return false; + } + fc->metadatalength += 4; + + while (!endofmetadata) { + if (ci->read_filebuf(buf, 4) < 4) + { + return false; + } + + endofmetadata=(buf[0]&0x80); + blocklength = (buf[1] << 16) | (buf[2] << 8) | buf[3]; + fc->metadatalength+=blocklength+4; + + if ((buf[0] & 0x7f) == 0) /* 0 is the STREAMINFO block */ + { + if (ci->read_filebuf(buf, blocklength) < blocklength) return false; + + fc->filesize = ci->filesize; + fc->min_blocksize = (buf[0] << 8) | buf[1]; + int max_blocksize = (buf[2] << 8) | buf[3]; + if (max_blocksize > MAX_BLOCKSIZE) + { + LOGF("FLAC: Maximum blocksize is too large (%d > %d)\n", + max_blocksize, MAX_BLOCKSIZE); + return false; + } + fc->max_blocksize = max_blocksize; + fc->min_framesize = (buf[4] << 16) | (buf[5] << 8) | buf[6]; + fc->max_framesize = (buf[7] << 16) | (buf[8] << 8) | buf[9]; + fc->samplerate = (buf[10] << 12) | (buf[11] << 4) + | ((buf[12] & 0xf0) >> 4); + fc->channels = ((buf[12]&0x0e)>>1) + 1; + fc->bps = (((buf[12]&0x01) << 4) | ((buf[13]&0xf0)>>4) ) + 1; + + /* totalsamples is a 36-bit field, but we assume <= 32 bits are + used */ + fc->totalsamples = (buf[14] << 24) | (buf[15] << 16) + | (buf[16] << 8) | buf[17]; + + /* Calculate track length (in ms) and estimate the bitrate + (in kbit/s) */ + fc->length = ((int64_t) fc->totalsamples * 1000) / fc->samplerate; + + found_streaminfo=true; + } else if ((buf[0] & 0x7f) == 3) { /* 3 is the SEEKTABLE block */ + while ((nseekpoints < MAX_SUPPORTED_SEEKTABLE_SIZE) && + (blocklength >= 18)) { + if (ci->read_filebuf(buf,18) < 18) return false; + blocklength-=18; + + seekpoint_hi=(buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]; + seekpoint_lo=(buf[4] << 24) | (buf[5] << 16) | + (buf[6] << 8) | buf[7]; + offset_hi=(buf[8] << 24) | (buf[9] << 16) | + (buf[10] << 8) | buf[11]; + offset_lo=(buf[12] << 24) | (buf[13] << 16) | + (buf[14] << 8) | buf[15]; + + blocksize=(buf[16] << 8) | buf[17]; + + /* Only store seekpoints where the high 32 bits are zero */ + if ((seekpoint_hi == 0) && (seekpoint_lo != 0xffffffff) && + (offset_hi == 0)) { + seekpoints[nseekpoints].sample=seekpoint_lo; + seekpoints[nseekpoints].offset=offset_lo; + seekpoints[nseekpoints].blocksize=blocksize; + nseekpoints++; + } + } + /* Skip any unread seekpoints */ + if (blocklength > 0) + ci->advance_buffer(blocklength); + } else { + /* Skip to next metadata block */ + ci->advance_buffer(blocklength); + } + } + + if (found_streaminfo) { + fc->bitrate = ((int64_t) (fc->filesize-fc->metadatalength) * 8) + / fc->length; + return true; + } else { + return false; + } +} + +/* Synchronize to next frame in stream - adapted from libFLAC 1.1.3b2 */ +static bool frame_sync(FLACContext* fc) { + unsigned int x = 0; + bool cached = false; + + /* Make sure we're byte aligned. */ + align_get_bits(&fc->gb); + + while(1) { + if(fc->gb.size_in_bits - get_bits_count(&fc->gb) < 8) { + /* Error, end of bitstream, a valid stream should never reach here + * since the buffer should contain at least one frame header. + */ + return false; + } + + if(cached) + cached = false; + else + x = get_bits(&fc->gb, 8); + + if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ + x = get_bits(&fc->gb, 8); + /* We have to check if we just read two 0xff's in a row; the second + * may actually be the beginning of the sync code. + */ + if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ + cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for last 6 sync bits. */ + /* Succesfully synced. */ + break; + } + } + } + + /* Advance and init bit buffer to the new frame. */ + ci->advance_buffer((get_bits_count(&fc->gb)-16)>>3); /* consumed bytes */ + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE+16); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + /* Decode the frame to verify the frame crc and + * fill fc with its metadata. + */ + if(flac_decode_frame(fc, + bit_buffer, buff_size, ci->yield) < 0) { + return false; + } + + return true; +} + +/* Seek to sample - adapted from libFLAC 1.1.3b2+ */ +static bool flac_seek(FLACContext* fc, uint32_t target_sample) { + off_t orig_pos = ci->curpos; + off_t pos = -1; + unsigned long lower_bound, upper_bound; + unsigned long lower_bound_sample, upper_bound_sample; + int i; + unsigned approx_bytes_per_frame; + uint32_t this_frame_sample = fc->samplenumber; + unsigned this_block_size = fc->blocksize; + bool needs_seek = true, first_seek = true; + + /* We are just guessing here. */ + if(fc->max_framesize > 0) + approx_bytes_per_frame = (fc->max_framesize + fc->min_framesize)/2 + 1; + /* Check if it's a known fixed-blocksize stream. */ + else if(fc->min_blocksize == fc->max_blocksize && fc->min_blocksize > 0) + approx_bytes_per_frame = fc->min_blocksize*fc->channels*fc->bps/8 + 64; + else + approx_bytes_per_frame = 4608 * fc->channels * fc->bps/8 + 64; + + /* Set an upper and lower bound on where in the stream we will search. */ + lower_bound = fc->metadatalength; + lower_bound_sample = 0; + upper_bound = fc->filesize; + upper_bound_sample = fc->totalsamples>0 ? fc->totalsamples : target_sample; + + /* Refine the bounds if we have a seektable with suitable points. */ + if(nseekpoints > 0) { + /* Find the closest seek point <= target_sample, if it exists. */ + for(i = nseekpoints-1; i >= 0; i--) { + if(seekpoints[i].sample <= target_sample) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + lower_bound = fc->metadatalength + seekpoints[i].offset; + lower_bound_sample = seekpoints[i].sample; + } + + /* Find the closest seek point > target_sample, if it exists. */ + for(i = 0; i < nseekpoints; i++) { + if(seekpoints[i].sample > target_sample) + break; + } + if(i < nseekpoints) { /* i.e. we found a suitable seek point... */ + upper_bound = fc->metadatalength + seekpoints[i].offset; + upper_bound_sample = seekpoints[i].sample; + } + } + + while(1) { + /* Check if bounds are still ok. */ + if(lower_bound_sample >= upper_bound_sample || + lower_bound > upper_bound) { + return false; + } + + /* Calculate new seek position */ + if(needs_seek) { + pos = (off_t)(lower_bound + + (((target_sample - lower_bound_sample) * + (int64_t)(upper_bound - lower_bound)) / + (upper_bound_sample - lower_bound_sample)) - + approx_bytes_per_frame); + + if(pos >= (off_t)upper_bound) + pos = (off_t)upper_bound-1; + if(pos < (off_t)lower_bound) + pos = (off_t)lower_bound; + } + + if(!ci->seek_buffer(pos)) + return false; + + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE+16); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + /* Now we need to get a frame. It is possible for our seek + * to land in the middle of audio data that looks exactly like + * a frame header from a future version of an encoder. When + * that happens, frame_sync() will return false. + * But there is a remote possibility that it is properly + * synced at such a "future-codec frame", so to make sure, + * we wait to see several "unparseable" errors in a row before + * bailing out. + */ + { + unsigned unparseable_count; + bool got_a_frame = false; + for(unparseable_count = 0; !got_a_frame + && unparseable_count < 10; unparseable_count++) { + if(frame_sync(fc)) + got_a_frame = true; + } + if(!got_a_frame) { + ci->seek_buffer(orig_pos); + return false; + } + } + + this_frame_sample = fc->samplenumber; + this_block_size = fc->blocksize; + + if(target_sample >= this_frame_sample + && target_sample < this_frame_sample+this_block_size) { + /* Found the frame containing the target sample. */ + fc->sample_skip = target_sample - this_frame_sample; + break; + } + + if(this_frame_sample + this_block_size >= upper_bound_sample && + !first_seek) { + if(pos == (off_t)lower_bound || !needs_seek) { + ci->seek_buffer(orig_pos); + return false; + } + /* Our last move backwards wasn't big enough, try again. */ + approx_bytes_per_frame *= 2; + continue; + } + /* Allow one seek over upper bound, + * required for streams with unknown total samples. + */ + first_seek = false; + + /* Make sure we are not seeking in a corrupted stream */ + if(this_frame_sample < lower_bound_sample) { + ci->seek_buffer(orig_pos); + return false; + } + + approx_bytes_per_frame = this_block_size*fc->channels*fc->bps/8 + 64; + + /* We need to narrow the search. */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample; + upper_bound = ci->curpos; + } + else { /* Target is beyond this frame. */ + /* We are close, continue in decoding next frames. */ + if(target_sample < this_frame_sample + 4*this_block_size) { + pos = ci->curpos + fc->framesize; + needs_seek = false; + } + + lower_bound_sample = this_frame_sample + this_block_size; + lower_bound = ci->curpos + fc->framesize; + } + } + + return true; +} + +/* Seek to file offset */ +static bool flac_seek_offset(FLACContext* fc, uint32_t offset) { + unsigned unparseable_count; + bool got_a_frame = false; + + if(!ci->seek_buffer(offset)) + return false; + + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + for(unparseable_count = 0; !got_a_frame + && unparseable_count < 10; unparseable_count++) { + if(frame_sync(fc)) + got_a_frame = true; + } + + if(!got_a_frame) { + ci->seek_buffer(fc->metadatalength); + return false; + } + + return true; +} + +/* 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_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + int8_t *buf; + uint32_t samplesdone; + uint32_t elapsedtime; + size_t bytesleft; + int consumed; + int res; + int frame; + intptr_t param; + + if (codec_init()) { + LOGF("FLAC: Error initialising codec\n"); + return CODEC_ERROR; + } + + /* Need to save offset for later use (cleared indirectly by flac_init) */ + samplesdone = ci->id3->offset; + + if (!flac_init(&fc,ci->id3->first_frame_offset)) { + LOGF("FLAC: Error initialising codec\n"); + return CODEC_ERROR; + } + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + ci->configure(DSP_SET_STEREO_MODE, fc.channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + codec_set_replaygain(ci->id3); + + flac_seek_offset(&fc, samplesdone); + samplesdone=fc.samplenumber+fc.blocksize; + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + ci->set_elapsed(elapsedtime); + + /* The main decoding loop */ + frame=0; + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + while (bytesleft) { + 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) { + if (flac_seek(&fc,(uint32_t)(((uint64_t)param + *ci->id3->frequency)/1000))) { + /* Refill the input buffer */ + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + } + + ci->set_elapsed(param); + ci->seek_complete(); + } + + if((res=flac_decode_frame(&fc,buf, + bytesleft,ci->yield)) < 0) { + LOGF("FLAC: Frame %d, error %d\n",frame,res); + return CODEC_ERROR; + } + consumed=fc.gb.index/8; + frame++; + + ci->yield(); + ci->pcmbuf_insert(&fc.decoded[0][fc.sample_skip], &fc.decoded[1][fc.sample_skip], + fc.blocksize - fc.sample_skip); + + fc.sample_skip = 0; + + /* Update the elapsed-time indicator */ + samplesdone=fc.samplenumber+fc.blocksize; + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + ci->set_elapsed(elapsedtime); + + ci->advance_buffer(consumed); + + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + } + + LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone); + return CODEC_OK; +} -- cgit v1.2.3