summaryrefslogtreecommitdiff
path: root/plugins/flac/flac.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/flac/flac.c')
-rw-r--r--plugins/flac/flac.c160
1 files changed, 115 insertions, 45 deletions
diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c
index aebe061e..a2e8506a 100644
--- a/plugins/flac/flac.c
+++ b/plugins/flac/flac.c
@@ -24,8 +24,8 @@
static DB_decoder_t plugin;
static DB_functions_t *deadbeef;
-//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-#define trace(fmt,...)
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
@@ -39,8 +39,10 @@ static int endsample;
static int currentsample;
typedef struct {
+ DB_FILE *file;
DB_playItem_t *after;
DB_playItem_t *last;
+ DB_playItem_t *it;
const char *fname;
int samplerate;
int channels;
@@ -48,6 +50,45 @@ typedef struct {
int bps;
} cue_cb_data_t;
+static cue_cb_data_t flac_callbacks;
+
+// callbacks
+FLAC__StreamDecoderReadStatus flac_read_cb (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) {
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ size_t r = deadbeef->fread (buffer, 1, *bytes, cb->file);
+ *bytes = r;
+ if (r == 0) {
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus flac_seek_cb (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ int r = deadbeef->fseek (cb->file, absolute_byte_offset, SEEK_SET);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderTellStatus flac_tell_cb (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ size_t r = deadbeef->ftell (cb->file);
+ *absolute_byte_offset = r;
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderLengthStatus flac_lenght_cb (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ size_t pos = deadbeef->ftell (cb->file);
+ deadbeef->fseek (cb->file, 0, SEEK_END);
+ *stream_length = deadbeef->ftell (cb->file);
+ deadbeef->fseek (cb->file, pos, SEEK_SET);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+FLAC__bool flac_eof_cb (const FLAC__StreamDecoder *decoder, void *client_data) {
+ return 0;
+}
+
static FLAC__StreamDecoderWriteStatus
cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const inputbuffer[], void *client_data) {
if (frame->header.blocksize == 0) {
@@ -73,7 +114,7 @@ cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *fra
}
}
if (readbytes > bufsize) {
- fprintf (stderr, "flac: buffer overflow, distortion will occur\n");
+ trace ("flac: buffer overflow, distortion will occur\n");
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
@@ -101,70 +142,66 @@ cflac_init_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecode
cflac_init_stop_decoding = 1;
}
-static void
-cflac_free (void);
-
static int
cflac_init (DB_playItem_t *it) {
- FILE *fp = fopen (it->fname, "rb");
- if (!fp) {
+ memset (&flac_callbacks, 0, sizeof (flac_callbacks));
+ flac_callbacks.file = deadbeef->fopen (it->fname);
+ if (!flac_callbacks.file) {
return -1;
}
- int skip = deadbeef->junk_get_leading_size (fp);
+ int skip = deadbeef->junk_get_leading_size (flac_callbacks.file);
if (skip > 0) {
- fseek (fp, skip, SEEK_SET);
+ deadbeef->fseek (flac_callbacks.file, skip, SEEK_SET);
}
char sign[4];
- if (fread (sign, 1, 4, fp) != 4) {
- fclose (fp);
+ if (deadbeef->fread (sign, 1, 4, flac_callbacks.file) != 4) {
+ plugin.free ();
return -1;
}
if (strncmp (sign, "fLaC", 4)) {
- fclose (fp);
+ plugin.free ();
return -1;
}
- fclose (fp);
- fp = NULL;
+ deadbeef->fseek (flac_callbacks.file, -4, SEEK_CUR);
FLAC__StreamDecoderInitStatus status;
decoder = FLAC__stream_decoder_new();
if (!decoder) {
-// printf ("FLAC__stream_decoder_new failed\n");
+ trace ("FLAC__stream_decoder_new failed\n");
return -1;
}
FLAC__stream_decoder_set_md5_checking(decoder, 0);
- cue_cb_data_t cb;
- status = FLAC__stream_decoder_init_file(decoder, it->fname, cflac_write_callback, cflac_metadata_callback, cflac_error_callback, &cb);
+ status = FLAC__stream_decoder_init_stream (decoder, flac_read_cb, flac_seek_cb, flac_tell_cb, flac_lenght_cb, flac_eof_cb, cflac_write_callback, cflac_metadata_callback, cflac_error_callback, &flac_callbacks);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- cflac_free ();
+ plugin.free ();
return -1;
}
//plugin.info.samplerate = -1;
if (!FLAC__stream_decoder_process_until_end_of_metadata (decoder)) {
- cflac_free ();
+ plugin.free ();
return -1;
}
- plugin.info.samplerate = cb.samplerate;
- plugin.info.channels = cb.channels;
- plugin.info.bps = cb.bps;
+ plugin.info.samplerate = flac_callbacks.samplerate;
+ plugin.info.channels = flac_callbacks.channels;
+ plugin.info.bps = flac_callbacks.bps;
plugin.info.readpos = 0;
if (plugin.info.samplerate == -1) { // not a FLAC stream
- cflac_free ();
+ plugin.free ();
return -1;
}
if (it->endsample > 0) {
startsample = it->startsample;
endsample = it->endsample;
if (plugin.seek_sample (0) < 0) {
- cflac_free ();
+ plugin.free ();
return -1;
}
}
else {
startsample = 0;
- endsample = cb.totalsamples-1;
+ endsample = flac_callbacks.totalsamples-1;
currentsample = 0;
- trace ("startsample=%d, endsample=%d, totalsamples=%d\n", startsample, endsample, cb.totalsamples);
+ trace ("startsample=%d, endsample=%d, totalsamples=%d\n", startsample, endsample, flac_callbacks.totalsamples);
}
remaining = 0;
@@ -287,16 +324,19 @@ cflac_init_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame
static void
cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
if (cflac_init_stop_decoding) {
+ trace ("flac: cflac_init_cue_metadata_callback: cflac_init_stop_decoding=1\n");
return;
}
cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+ trace ("flac: cflac_init_cue_metadata_callback: got FLAC__METADATA_TYPE_STREAMINFO\n");
cb->samplerate = metadata->data.stream_info.sample_rate;
cb->channels = metadata->data.stream_info.channels;
//cb->duration = metadata->data.stream_info.total_samples / (float)metadata->data.stream_info.sample_rate;
cb->totalsamples = metadata->data.stream_info.total_samples;
}
else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+ trace ("flac: cflac_init_cue_metadata_callback: got FLAC__METADATA_TYPE_VORBIS_COMMENT\n");
const FLAC__StreamMetadata_VorbisComment *vc = &metadata->data.vorbis_comment;
for (int i = 0; i < vc->num_comments; i++) {
const FLAC__StreamMetadata_VorbisComment_Entry *c = &vc->comments[i];
@@ -315,10 +355,11 @@ cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC_
static void
cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
if (cflac_init_stop_decoding) {
- fprintf (stderr, "error flag is set, ignoring init_metadata callback..\n");
+ trace ("error flag is set, ignoring init_metadata callback..\n");
return;
}
- DB_playItem_t *it = (DB_playItem_t *)client_data;
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ DB_playItem_t *it = cb->it;
//it->tracknum = 0;
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
it->duration = metadata->data.stream_info.total_samples / (float)metadata->data.stream_info.sample_rate;
@@ -380,64 +421,80 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str
static DB_playItem_t *
cflac_insert (DB_playItem_t *after, const char *fname) {
+ trace ("flac: inserting %s\n", fname);
DB_playItem_t *it = NULL;
FLAC__StreamDecoder *decoder = NULL;
- FILE *fp = fopen (fname, "rb");
- if (!fp) {
+ cue_cb_data_t cb = {
+ .fname = fname,
+ .after = after,
+ .last = after
+ };
+ cb.file = deadbeef->fopen (fname);
+ if (!cb.file) {
goto cflac_insert_fail;
}
// skip id3 junk
- int skip = deadbeef->junk_get_leading_size (fp);
+ int skip = deadbeef->junk_get_leading_size (cb.file);
if (skip > 0) {
- fseek (fp, skip, SEEK_SET);
+ deadbeef->fseek (cb.file, skip, SEEK_SET);
}
char sign[4];
- if (fread (sign, 1, 4, fp) != 4) {
+ if (deadbeef->fread (sign, 1, 4, cb.file) != 4) {
+ trace ("flac: failed to read signature\n");
goto cflac_insert_fail;
}
if (strncmp (sign, "fLaC", 4)) {
+ trace ("flac: file signature is not fLaC\n");
goto cflac_insert_fail;
}
- fclose (fp);
- fp = NULL;
+ deadbeef->fseek (cb.file, -4, SEEK_CUR);
cflac_init_stop_decoding = 0;
//try embedded cue, and calculate duration
FLAC__StreamDecoderInitStatus status;
decoder = FLAC__stream_decoder_new();
if (!decoder) {
+ trace ("flac: failed to create decoder\n");
goto cflac_insert_fail;
}
FLAC__stream_decoder_set_md5_checking(decoder, 0);
// try embedded cue
- cue_cb_data_t cb = {
- .fname = fname,
- .after = after,
- .last = after
- };
FLAC__stream_decoder_set_metadata_respond_all (decoder);
- status = FLAC__stream_decoder_init_file (decoder, fname, cflac_init_write_callback, cflac_init_cue_metadata_callback, cflac_init_error_callback, &cb);
+ status = FLAC__stream_decoder_init_stream (decoder, flac_read_cb, flac_seek_cb, flac_tell_cb, flac_lenght_cb, flac_eof_cb, cflac_init_write_callback, cflac_init_cue_metadata_callback, cflac_init_error_callback, &cb);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK || cflac_init_stop_decoding) {
+ trace ("flac: FLAC__stream_decoder_init_stream failed\n");
goto cflac_insert_fail;
}
if (!FLAC__stream_decoder_process_until_end_of_metadata (decoder) || cflac_init_stop_decoding) {
+ trace ("flac: FLAC__stream_decoder_process_until_end_of_metadata failed\n");
goto cflac_insert_fail;
}
FLAC__stream_decoder_delete(decoder);
decoder = NULL;
if (cb.last != after) {
+ trace ("flac: loaded embedded cuesheet\n");
// that means embedded cue is loaded
+ if (cb.file) {
+ deadbeef->fclose (cb.file);
+ }
return cb.last;
}
// try external cue
DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "flac", cb.totalsamples, cb.samplerate);
if (cue_after) {
+ if (cb.file) {
+ deadbeef->fclose (cb.file);
+ }
+ trace ("flac: loaded external cuesheet\n");
return cue_after;
}
decoder = FLAC__stream_decoder_new();
if (!decoder) {
+ if (cb.file) {
+ deadbeef->fclose (cb.file);
+ }
goto cflac_insert_fail;
}
FLAC__stream_decoder_set_md5_checking(decoder, 0);
@@ -447,17 +504,30 @@ cflac_insert (DB_playItem_t *after, const char *fname) {
it = deadbeef->pl_item_alloc ();
it->decoder = &plugin;
it->fname = strdup (fname);
- status = FLAC__stream_decoder_init_file (decoder, fname, cflac_init_write_callback, cflac_init_metadata_callback, cflac_init_error_callback, it);
+ cb.it = it;
+ if (skip > 0) {
+ deadbeef->fseek (cb.file, skip, SEEK_SET);
+ }
+ else {
+ deadbeef->rewind (cb.file);
+ }
+ deadbeef->fseek (cb.file, -4, SEEK_CUR);
+ status = FLAC__stream_decoder_init_stream (decoder, flac_read_cb, flac_seek_cb, flac_tell_cb, flac_lenght_cb, flac_eof_cb, cflac_init_write_callback, cflac_init_metadata_callback, cflac_init_error_callback, &cb);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK || cflac_init_stop_decoding) {
+ trace ("flac: FLAC__stream_decoder_init_stream [2] failed\n");
goto cflac_insert_fail;
}
if (!FLAC__stream_decoder_process_until_end_of_metadata (decoder) || cflac_init_stop_decoding) {
+ trace ("flac: FLAC__stream_decoder_process_until_end_of_metadata [2] failed\n");
goto cflac_insert_fail;
}
FLAC__stream_decoder_delete(decoder);
decoder = NULL;
it->filetype = "FLAC";
after = deadbeef->pl_insert_item (after, it);
+ if (cb.file) {
+ deadbeef->fclose (cb.file);
+ }
return after;
cflac_insert_fail:
if (it) {
@@ -466,8 +536,8 @@ cflac_insert_fail:
if (decoder) {
FLAC__stream_decoder_delete(decoder);
}
- if (fp) {
- fclose (fp);
+ if (cb.file) {
+ deadbeef->fclose (cb.file);
}
return NULL;
}