summaryrefslogtreecommitdiff
path: root/apps/codecs/flac.c
diff options
context:
space:
mode:
authorRyan Jackson <rdjackso@rockbox.org>2005-07-28 18:43:33 +0000
committerRyan Jackson <rdjackso@rockbox.org>2005-07-28 18:43:33 +0000
commitb301b438259ca637826ce2c47367e9c45cd0742b (patch)
treedc75dc313b66ac055ed8ca09b53a800ece7bd3bc /apps/codecs/flac.c
parent773c28e489c6a2606aad0598672fb88f15c7c9bb (diff)
downloadrockbox-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.c306
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;
}