diff options
| author | Ryan Jackson <rdjackso@rockbox.org> | 2005-07-28 18:43:33 +0000 |
|---|---|---|
| committer | Ryan Jackson <rdjackso@rockbox.org> | 2005-07-28 18:43:33 +0000 |
| commit | b301b438259ca637826ce2c47367e9c45cd0742b (patch) | |
| tree | dc75dc313b66ac055ed8ca09b53a800ece7bd3bc /apps/codecs/flac.c | |
| parent | 773c28e489c6a2606aad0598672fb88f15c7c9bb (diff) | |
| download | rockbox-b301b438259ca637826ce2c47367e9c45cd0742b.zip rockbox-b301b438259ca637826ce2c47367e9c45cd0742b.tar.gz rockbox-b301b438259ca637826ce2c47367e9c45cd0742b.tar.bz2 rockbox-b301b438259ca637826ce2c47367e9c45cd0742b.tar.xz | |
Added comments, Replay Gain, and resume/bookmarks for FLAC.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7253 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/flac.c')
| -rw-r--r-- | apps/codecs/flac.c | 306 |
1 files changed, 207 insertions, 99 deletions
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c index 259686e..0d21c3c 100644 --- a/apps/codecs/flac.c +++ b/apps/codecs/flac.c @@ -20,6 +20,8 @@ #include "codec.h" #include <codecs/libFLAC/include/FLAC/seekable_stream_decoder.h> +#include <codecs/libFLAC/include/FLAC/format.h> +#include <codecs/libFLAC/include/FLAC/metadata.h> #include "playback.h" #include "lib/codeclib.h" #include "dsp.h" @@ -30,6 +32,10 @@ static struct codec_api* rb; static uint32_t samplesdone; +static FLAC__StreamMetadata *stream_info; +static FLAC__StreamMetadata *seek_table; +unsigned int metadata_length; + /* Called when the FLAC decoder needs some FLAC data to decode */ FLAC__SeekableStreamDecoderReadStatus flac_read_handler(const FLAC__SeekableStreamDecoder *dec, FLAC__byte buffer[], unsigned *bytes, void *data) @@ -49,9 +55,9 @@ static unsigned char pcmbuf[FLAC_MAX_SUPPORTED_BLOCKSIZE*FLAC_MAX_SUPPORTED_CHAN /* Called when the FLAC decoder has some decoded PCM data to write */ FLAC__StreamDecoderWriteStatus flac_write_handler(const FLAC__SeekableStreamDecoder *dec, - const FLAC__Frame *frame, - const FLAC__int32 * const buf[], - void *data) + const FLAC__Frame *frame, + const FLAC__int32 * const buf[], + void *data) { struct codec_api* ci = (struct codec_api*)data; (void)dec; @@ -92,10 +98,23 @@ FLAC__StreamDecoderWriteStatus flac_write_handler(const FLAC__SeekableStreamDeco void flac_metadata_handler(const FLAC__SeekableStreamDecoder *dec, const FLAC__StreamMetadata *meta, void *data) { - /* Ignore metadata for now... */ - (void)dec; - (void)meta; - (void)data; + /* Ignore metadata for now... */ + (void)dec; + (void)data; + + metadata_length += meta->length; + + if ( meta->type == FLAC__METADATA_TYPE_STREAMINFO ) { + stream_info = FLAC__metadata_object_clone( meta ); + if ( stream_info == NULL ) { + //return CODEC_ERROR; + } + } else if ( meta->type == FLAC__METADATA_TYPE_SEEKTABLE ) { + seek_table = FLAC__metadata_object_clone( meta ); + if ( seek_table == NULL ) { + //return CODEC_ERROR; + } + } } @@ -111,47 +130,47 @@ FLAC__SeekableStreamDecoderSeekStatus flac_seek_handler (const FLAC__SeekableStr FLAC__uint64 absolute_byte_offset, void *client_data) { - (void)decoder; - struct codec_api* ci = (struct codec_api*)client_data; + (void)decoder; + struct codec_api* ci = (struct codec_api*)client_data; - if (ci->seek_buffer(absolute_byte_offset)) { + if (ci->seek_buffer(absolute_byte_offset)) { return(FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK); - } else { + } else { return(FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR); - } + } } FLAC__SeekableStreamDecoderTellStatus flac_tell_handler (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { - struct codec_api* ci = (struct codec_api*)client_data; + struct codec_api* ci = (struct codec_api*)client_data; - (void)decoder; - *absolute_byte_offset=ci->curpos; - return(FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK); + (void)decoder; + *absolute_byte_offset=ci->curpos; + return(FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK); } FLAC__SeekableStreamDecoderLengthStatus flac_length_handler (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { - struct codec_api* ci = (struct codec_api*)client_data; + struct codec_api* ci = (struct codec_api*)client_data; - (void)decoder; - *stream_length=ci->filesize; - return(FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK); + (void)decoder; + *stream_length=ci->filesize; + return(FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK); } FLAC__bool flac_eof_handler (const FLAC__SeekableStreamDecoder *decoder, void *client_data) { - struct codec_api* ci = (struct codec_api*)client_data; + struct codec_api* ci = (struct codec_api*)client_data; - (void)decoder; - if (ci->curpos >= ci->filesize) { + (void)decoder; + if (ci->curpos >= ci->filesize) { return(true); - } else { + } else { return(false); - } + } } #ifndef SIMULATOR @@ -160,104 +179,193 @@ extern char iramstart[]; extern char iramend[]; #endif +FLAC__uint64 find_sample_number(size_t offset) +{ + FLAC__StreamMetadata_SeekPoint *points; + FLAC__uint64 prev_sample, next_sample; + size_t prev_offset, next_offset; + int percent; + + if ( offset >= (rb->id3->filesize - metadata_length)) { + return stream_info->data.stream_info.total_samples; + } + + prev_offset = 0; + prev_sample = 0; + next_offset = rb->id3->filesize - metadata_length; + next_sample = stream_info->data.stream_info.total_samples; + + if ( seek_table ) { + int left, right, middle; + + middle = 0; /* Silence compiler warnings */ + points = seek_table->data.seek_table.points; + left = 0; + right = seek_table->data.seek_table.num_points - 1; + + /* Do a binary search to find the matching seek point */ + while ( left <= right ) { + middle = (left + right) / 2; + + if ( (FLAC__uint64)offset < points[middle].stream_offset ) { + right = middle - 1; + } else if ( (FLAC__uint64)offset > points[middle].stream_offset ) { + left = middle + 1; + } else { + return points[middle].sample_number; + } + } + + /* Didn't find a matching seek point, so get the sample numbers of the + * seek points to the left and right of offset to make our guess more + * accurate. Accuracy depends on how close these sample numbers are to + * each other. + */ + if ( (unsigned)left >= seek_table->data.seek_table.num_points ) { + prev_offset = points[middle].stream_offset; + prev_sample = points[middle].sample_number; + } else if ( right < 0 ) { + next_offset = points[middle].stream_offset; + next_sample = points[middle].sample_number; + } else { + middle--; + prev_offset = points[middle].stream_offset; + prev_sample = points[middle].sample_number; + next_offset = points[middle+1].stream_offset; + next_sample = points[middle+1].sample_number; + } + } + + /* Either there's no seek table or we didn't find our seek point, so now we + * have to guess. + */ + percent = ((offset - prev_offset) * 100) / (next_offset - prev_offset); + return (FLAC__uint64)(percent * (next_sample - prev_sample) / 100 + prev_sample); +} + /* this is the codec entry point */ enum codec_status codec_start(struct codec_api* api) { struct codec_api* ci = api; FLAC__SeekableStreamDecoder* flacDecoder; + FLAC__uint64 offset; - /* Generic codec initialisation */ - TEST_CODEC_API(api); + /* Generic codec initialisation */ + TEST_CODEC_API(api); - /* if you are using a global api pointer, don't forget to copy it! - otherwise you will get lovely "I04: IllInstr" errors... :-) */ - rb = api; + /* if you are using a global api pointer, don't forget to copy it! + otherwise you will get lovely "I04: IllInstr" errors... :-) */ + rb = api; #ifndef SIMULATOR - rb->memcpy(iramstart, iramcopy, iramend-iramstart); + rb->memcpy(iramstart, iramcopy, iramend-iramstart); #endif - ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*10)); - ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); - ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); + ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*10)); + ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); + ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*1024)); - ci->configure(DSP_DITHER, (bool *)false); - ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); - ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); + ci->configure(CODEC_DSP_ENABLE, (bool *)true); + ci->configure(DSP_DITHER, (bool *)false); + ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); - next_track: + next_track: - if (codec_init(api)) { - return CODEC_ERROR; - } + metadata_length = 0; + seek_table = NULL; + stream_info = NULL; - while (!rb->taginfo_ready) + if (codec_init(api)) { + return CODEC_ERROR; + } + + while (!rb->taginfo_ready) rb->yield(); - if (rb->id3->frequency != NATIVE_FREQUENCY) { - rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); - rb->configure(CODEC_DSP_ENABLE, (bool *)true); - } else { - rb->configure(CODEC_DSP_ENABLE, (bool *)false); - } + rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); + codec_set_replaygain(ci->id3); - /* Create a decoder instance */ - - flacDecoder=FLAC__seekable_stream_decoder_new(); - - /* Set up the decoder and the callback functions - this must be done before init */ - - /* The following are required for stream_decoder and higher */ - FLAC__seekable_stream_decoder_set_client_data(flacDecoder,ci); - FLAC__seekable_stream_decoder_set_write_callback(flacDecoder, flac_write_handler); - FLAC__seekable_stream_decoder_set_read_callback(flacDecoder, flac_read_handler); - FLAC__seekable_stream_decoder_set_metadata_callback(flacDecoder, flac_metadata_handler); - FLAC__seekable_stream_decoder_set_error_callback(flacDecoder, flac_error_handler); - FLAC__seekable_stream_decoder_set_metadata_respond(flacDecoder, FLAC__METADATA_TYPE_STREAMINFO); - - /* The following are only for the seekable_stream_decoder */ - FLAC__seekable_stream_decoder_set_seek_callback(flacDecoder, flac_seek_handler); - FLAC__seekable_stream_decoder_set_tell_callback(flacDecoder, flac_tell_handler); - FLAC__seekable_stream_decoder_set_length_callback(flacDecoder, flac_length_handler); - FLAC__seekable_stream_decoder_set_eof_callback(flacDecoder, flac_eof_handler); - - - /* QUESTION: What do we do when the init fails? */ - if (FLAC__seekable_stream_decoder_init(flacDecoder)) { - return CODEC_ERROR; - } - - /* The first thing to do is to parse the metadata */ - FLAC__seekable_stream_decoder_process_until_end_of_metadata(flacDecoder); - - samplesdone=0; - ci->set_elapsed(0); - /* The main decoder loop */ - while (FLAC__seekable_stream_decoder_get_state(flacDecoder)!=FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) { - rb->yield(); - if (ci->stop_codec || ci->reload_codec) { - break; + /* Create a decoder instance */ + + flacDecoder=FLAC__seekable_stream_decoder_new(); + + /* Set up the decoder and the callback functions - this must be done before init */ + + /* The following are required for stream_decoder and higher */ + FLAC__seekable_stream_decoder_set_client_data(flacDecoder,ci); + FLAC__seekable_stream_decoder_set_write_callback(flacDecoder, flac_write_handler); + FLAC__seekable_stream_decoder_set_read_callback(flacDecoder, flac_read_handler); + FLAC__seekable_stream_decoder_set_metadata_callback(flacDecoder, flac_metadata_handler); + FLAC__seekable_stream_decoder_set_error_callback(flacDecoder, flac_error_handler); + FLAC__seekable_stream_decoder_set_metadata_respond_all(flacDecoder); + + /* The following are only for the seekable_stream_decoder */ + FLAC__seekable_stream_decoder_set_seek_callback(flacDecoder, flac_seek_handler); + FLAC__seekable_stream_decoder_set_tell_callback(flacDecoder, flac_tell_handler); + FLAC__seekable_stream_decoder_set_length_callback(flacDecoder, flac_length_handler); + FLAC__seekable_stream_decoder_set_eof_callback(flacDecoder, flac_eof_handler); + + + /* QUESTION: What do we do when the init fails? */ + if (FLAC__seekable_stream_decoder_init(flacDecoder)) { + return CODEC_ERROR; } - if (ci->seek_time) { - int sample_loc; + /* The first thing to do is to parse the metadata */ + FLAC__seekable_stream_decoder_process_until_end_of_metadata(flacDecoder); - sample_loc = ci->seek_time/1000 * ci->id3->frequency; - if (FLAC__seekable_stream_decoder_seek_absolute(flacDecoder,sample_loc)) { - samplesdone=sample_loc; - ci->set_elapsed(samplesdone/(ci->id3->frequency/1000)); - } - ci->seek_time = 0; + if ( ci->id3->offset && stream_info ) { + FLAC__uint64 sample; + + sample = find_sample_number( ci->id3->offset - metadata_length ); + ci->advance_buffer(ci->id3->offset); + FLAC__seekable_stream_decoder_seek_absolute(flacDecoder, sample); + FLAC__seekable_stream_decoder_get_decode_position(flacDecoder, &offset); + ci->set_offset(offset); + samplesdone=(uint32_t)sample; + ci->set_elapsed(sample/(ci->id3->frequency/1000)); + } else { + samplesdone=0; + ci->set_elapsed(0); } - FLAC__seekable_stream_decoder_process_single(flacDecoder); - } + /* The main decoder loop */ + while (FLAC__seekable_stream_decoder_get_state(flacDecoder)!=FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) { + rb->yield(); + if (ci->stop_codec || ci->reload_codec) { + break; + } + + if (ci->seek_time) { + int sample_loc; + + sample_loc = ci->seek_time/1000 * ci->id3->frequency; + if (FLAC__seekable_stream_decoder_seek_absolute(flacDecoder,sample_loc)) { + samplesdone=sample_loc; + ci->set_elapsed(samplesdone/(ci->id3->frequency/1000)); + } + ci->seek_time = 0; + } + + FLAC__seekable_stream_decoder_process_single(flacDecoder); + FLAC__seekable_stream_decoder_get_decode_position(flacDecoder, &offset); + ci->set_offset(offset); + } - /* Flush the libFLAC buffers */ - FLAC__seekable_stream_decoder_finish(flacDecoder); + /* Flush the libFLAC buffers */ + FLAC__seekable_stream_decoder_finish(flacDecoder); - if (ci->request_next_track()) - goto next_track; + if (ci->request_next_track()) { + if ( stream_info ) { + FLAC__metadata_object_delete(stream_info); + } + if ( seek_table ) { + FLAC__metadata_object_delete(seek_table); + } + metadata_length = 0; + goto next_track; + } - return CODEC_OK; + return CODEC_OK; } |