Hello Max, as promised i send the patch against the latest git sources (cloned on Mo 23 Feb 2009 10:57:36 CET). This will add support for embedded cue sheets in flac files. Please send me any comments for improvement, critisim or bugs.
I have commented some parts where i think more work has to be done. Whatsmore i'm not so happy with it overall, to me it seems more like a hack rather then something clean.. anyway for the moment it's the best i can do. Hope you like it anyway. ;) Any hints where i could get started to get the subsongs proper tagged? Max Kellermann wrote: > If you're developing only for the new libflac API > (FLAC_API_VERSION_CURRENT > 7), then you don't need any macros, but be > sure your code won't be compiled on older libflac APIs. There are > still people around using those ancient versions.. For the moment my patch isn't #defined for FLAC > 7 but i'll change that asap. One last question: Is there a way to access song->tag from the decoder plugin (flac_plugin.c)? Trying access decoder->stream_tag or decoder->decoder_tag doesn't work/wouldn't even compile. Thanks and regards, Jochen
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index 63dc6e1..7dd0b8a 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -345,3 +345,97 @@ flac_common_write(struct flac_data *data, const
FLAC__Frame * frame,
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
+
+char*
+flac_cue_track( const char* pathname,
+ const uint8_t tnum)
+{
+ FLAC__StreamMetadata* cs =
FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
+
+ FLAC__metadata_get_cuesheet(pathname, &cs);
+
+ if (cs == NULL)
+ return NULL;
+
+ if (cs->data.cue_sheet.num_tracks <= 1)
+ {
+ FLAC__metadata_object_delete(cs);
+ return NULL;
+ }
+
+ if (tnum > 0 && tnum < cs->data.cue_sheet.num_tracks)
+ {
+ char* track = g_malloc(sizeof(char));
+ if (tnum < 10)
+ sprintf(track, "track_00%ld.flac", (long)tnum);
+
+ else if (tnum >= 10)
+ sprintf(track, "track_0%ld.flac", (long)tnum);
+
+ else if (tnum >= 100)
+ sprintf(track, "track_%ld.flac", (long)tnum);
+
+ FLAC__metadata_object_delete(cs);
+
+ return track;
+ }
+ else
+ {
+ FLAC__metadata_object_delete(cs);
+ return NULL;
+ }
+}
+
+const char*
+flac_get_basename(const char* pathname)
+{
+ uint8_t tnum = 0;
+ char* ptr = NULL;
+ char* ptr_ = NULL;
+ char* ptrdot = NULL;
+ const char* filename;
+
+ FLAC__StreamMetadata* cs;
+
+ cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
+
+ FLAC__metadata_get_cuesheet(pathname, &cs);
+
+ filename = strdup(pathname);
+
+ if (cs == NULL)
+ {
+ // last '/' in pathname: ++ptr should now start at
'track_xxx.flac'
+ ptr = strrchr(filename, '/');
+ *ptr = '\0';
+
+ // underscore in 'track_xxx.flac'; ++ptr_ = 'xxx.flac'
+ ptr_ = strrchr(++ptr, '_');
+ *ptr_ = '\0';
+
+ // ++ptrdot should start at 'flac'
+ ptrdot = strrchr(++ptr_, '.');
+ *ptrdot = '\0';
+
+ tnum = strtol(ptr_, NULL, 10);
+
+ if (strcmp(ptr, "track") == 0 &&
+ strcmp(++ptrdot, "flac") == 0 &&
+ tnum > 0 && tnum < 255)
+ {
+ g_debug("return filename: %s", filename);
+ return filename;
+ }
+ else
+ return NULL;
+ }
+ else
+ return pathname;
+}
+
+void
+flac_cue_tag_song(struct tag* tag, const uint8_t tnum)
+{
+ //tag = NULL;
+ //tnum = 0;
+}
diff --git a/src/decoder/_flac_common.h b/src/decoder/_flac_common.h
index e876c35..cbbc496 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/_flac_common.h
@@ -175,4 +175,17 @@ FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[]);
+char*
+flac_cue_track( const char* pathname,
+ const uint8_t tnum);
+
+const char*
+flac_get_basename(const char* pathname);
+
+void
+flac_check_cue_track(struct tag* tag, const uint8_t tnum);
+
+void
+flac_cue_tag_song(struct tag* tag, const uint8_t tnum);
+
#endif /* _FLAC_COMMON_H */
diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index ce056d2..6a44479 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -382,6 +382,233 @@ flac_decode(struct decoder * decoder, struct input_stream
*input_stream)
#ifndef HAVE_OGGFLAC
+/*
+ * @brief Open a flac file for decoding
+ * @param fname filename on fs
+ */
+static void
+flac_filedecode_internal(struct decoder* decoder,
+ const char* fname,
+ bool is_ogg)
+{
+ bool cuemode = false;
+
+ uint8_t tnum = 0;
+ FLAC__uint64 t_start = 0;
+ FLAC__uint64 t_end = 0;
+ FLAC__uint64 track_time = 0;
+ FLAC__StreamMetadata* cs = NULL;
+
+ flac_decoder *flac_dec;
+ struct flac_data data;
+ const char *err = NULL;
+
+ const char* filename;
+
+ /* we will do this whole voodoo only if we really
+ * have something like /foobar.flac/track_xxx.flac
+ * where xxx = 001, 002, and so on
+ */
+ if ((filename = flac_get_basename(fname)) != NULL)
+ cuemode = true;
+ else
+ filename = fname;
+
+ g_debug("flac_plugin filename: %s", filename);
+
+ if (cuemode)
+ {
+ /* find last occurrence of '_' in fname
+ * which is hopefully something like track_xxx.flac
+ * another/better way would be to use tag struct
+ */
+ char* ptr = strrchr(fname, '_');
+
+ // copy ascii tracknumber to int
+ char vtrack[3];
+ strncpy(vtrack, ++ptr, 3);
+ vtrack[3] = '\0';
+ tnum = strtol(vtrack, NULL, 10);
+
+ cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
+
+ FLAC__metadata_get_cuesheet(filename, &cs);
+
+ if (cs != NULL)
+ {
+ if (cs->data.cue_sheet.tracks != NULL && (tnum <=
cs->data.cue_sheet.num_tracks - 1))
+ {
+ t_start = cs->data.cue_sheet.tracks[tnum -
1].offset;
+ t_end = cs->data.cue_sheet.tracks[tnum].offset
- 1;
+ track_time =
cs->data.cue_sheet.tracks[tnum].offset - 1
+ - cs->data.cue_sheet.tracks[tnum -
1].offset;
+ }
+
+ FLAC__metadata_object_delete(cs);
+ }
+ else
+ return;
+ }
+
+ if (!(flac_dec = flac_new()))
+ return;
+
+ flac_data_init(&data, decoder, NULL);
+
+#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
+ if(!FLAC__stream_decoder_set_metadata_respond(flac_dec,
FLAC__METADATA_TYPE_VORBIS_COMMENT))
+ {
+ g_debug("Failed to set metadata respond\n");
+ }
+#endif
+
+
+ if (is_ogg)
+ {
+ if (FLAC__stream_decoder_init_ogg_file( flac_dec,
+ filename,
+ flac_write_cb,
+ flacMetadata,
+ flac_error_cb,
+ (void*) &data )
+ != FLAC__STREAM_DECODER_INIT_STATUS_OK )
+ {
+ err = "doing Ogg init()";
+ goto fail;
+ }
+ }
+ else
+ {
+ if (FLAC__stream_decoder_init_file( flac_dec,
+ filename,
+ flac_write_cb,
+ flacMetadata,
+ flac_error_cb,
+ (void*) &data )
+ != FLAC__STREAM_DECODER_INIT_STATUS_OK )
+ {
+ err = "doing init()";
+ goto fail;
+ }
+ }
+
+ if (!flac_process_metadata(flac_dec))
+ {
+ err = "problem reading metadata";
+ goto fail;
+ }
+
+ if (!audio_format_valid(&data.audio_format))
+ {
+ g_warning("Invalid audio format: %u:%u:%u\n",
+ data.audio_format.sample_rate,
+ data.audio_format.bits,
+ data.audio_format.channels);
+ goto fail;
+ }
+
+ // set track time (order is important: after stream init)
+ if (cuemode)
+ {
+ data.total_time = (float)(track_time /
data.audio_format.sample_rate);
+ data.position = 0;
+ }
+
+ decoder_initialized(decoder, &data.audio_format,
+ true, data.total_time);
+
+ // seek to song start (order is important: after decoder init)
+ if (cuemode)
+ {
+ flac_seek_absolute(flac_dec, (FLAC__uint64)t_start);
+ }
+
+ while (true)
+ {
+ if (!flac_process_single(flac_dec))
+ break;
+
+ // we only need to break at the end of track if we are in "cue
mode"
+ if (cuemode)
+ {
+ if (data.time >= data.total_time)
+ {
+ flacPrintErroredState(flac_get_state(flac_dec));
+ flac_finish(flac_dec);
+ }
+
+ if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK)
+ {
+ FLAC__uint64 seek_sample = t_start +
+ (decoder_seek_where(decoder) *
data.audio_format.sample_rate);
+
+ if (seek_sample >= t_start && seek_sample <=
t_end && data.total_time > 30)
+ {
+ if (flac_seek_absolute(flac_dec,
(FLAC__uint64)seek_sample))
+ {
+ data.time = (float)(seek_sample
- t_start) /
+
data.audio_format.sample_rate;
+ data.position = 0;
+
+
decoder_command_finished(decoder);
+ }
+ else
+ decoder_seek_error(decoder);
+ }
+ }
+ else if (flac_get_state(flac_dec) == flac_decoder_eof)
+ break;
+ }
+ else
+ {
+ if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK)
+ {
+ FLAC__uint64 seek_sample =
decoder_seek_where(decoder) *
+ data.audio_format.sample_rate + 0.5;
+ if (flac_seek_absolute(flac_dec, seek_sample))
+ {
+ data.time = ((float)seek_sample) /
+ data.audio_format.sample_rate;
+ data.position = 0;
+ decoder_command_finished(decoder);
+ }
+ else
+ decoder_seek_error(decoder);
+
+ }
+ else if (flac_get_state(flac_dec) == flac_decoder_eof)
+ break;
+ }
+ }
+
+ if (decoder_get_command(decoder) != DECODE_COMMAND_STOP)
+ {
+ flacPrintErroredState(flac_get_state(flac_dec));
+ flac_finish(flac_dec);
+ }
+
+fail:
+ if (data.replay_gain_info)
+ replay_gain_info_free(data.replay_gain_info);
+
+ if (flac_dec)
+ flac_delete(flac_dec);
+
+ if (err)
+ g_warning("%s\n", err);
+}
+
+/*
+ * @brief wrapper function for
+ * flac_filedecode_internal method
+ * for decoding without ogg
+ */
+static void
+flac_filedecode(struct decoder *decoder, const char *fname)
+{
+ flac_filedecode_internal(decoder, fname, false);
+}
+
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
@@ -438,6 +665,10 @@ oggflac_decode(struct decoder *decoder, struct
input_stream *input_stream)
if (ogg_stream_type_detect(input_stream) != FLAC)
return;
+ /* should we check for an embedded cue sheet here in order
+ * to use flac_filedecode_internal?
+ */
+
/* rewind the stream, because ogg_stream_type_detect() has
moved it */
input_stream_seek(input_stream, 0, SEEK_SET);
@@ -476,7 +707,8 @@ static const char *const flac_mime_types[] = {
const struct decoder_plugin flac_decoder_plugin = {
.name = "flac",
.stream_decode = flac_decode,
+ .file_decode = flac_filedecode,
.tag_dup = flac_tag_dup,
.suffixes = flac_suffixes,
- .mime_types = flac_mime_types
+ .mime_types = flac_mime_types,
};
diff --git a/src/input_file.c b/src/input_file.c
index b29a412..cd3f2e7 100644
--- a/src/input_file.c
+++ b/src/input_file.c
@@ -25,6 +25,9 @@
#include <string.h>
#include <glib.h>
+#include "ls.h"
+#include "decoder/_flac_common.h"
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_file"
@@ -33,11 +36,20 @@ input_file_open(struct input_stream *is, const char
*filename)
{
int fd, ret;
struct stat st;
+ const char* pathname;
if (filename[0] != '/')
return false;
- fd = open(filename, O_RDONLY);
+ if (strcmp(uri_get_suffix(filename), "flac") == 0)
+ {
+ if ((pathname = flac_get_basename(filename)) == NULL)
+ {
+ pathname = filename;
+ }
+ }
+
+ fd = open(pathname, O_RDONLY);
if (fd < 0) {
is->error = errno;
return false;
@@ -53,7 +65,7 @@ input_file_open(struct input_stream *is, const char *filename)
}
if (!S_ISREG(st.st_mode)) {
- g_debug("Not a regular file: %s\n", filename);
+ g_debug("Not a regular file: %s\n", pathname);
is->error = EINVAL;
close(fd);
return false;
diff --git a/src/update.c b/src/update.c
index 17059ff..9599e9a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -35,6 +35,9 @@
#include "stats.h"
#include "main.h"
#include "config.h"
+#include "tag.h"
+
+#include "decoder/_flac_common.h"
#ifdef ENABLE_SQLITE
#include "sticker.h"
@@ -368,6 +371,57 @@ update_regular_file(struct directory *directory,
if (suffix == NULL)
return;
+ if (strcmp(suffix, "flac") == 0)
+ {
+ const char* pathname = map_directory_child_fs(directory, name);
+ const char* filename;
+
+ if ((filename = flac_get_basename(pathname)) != NULL)
+ {
+ const struct decoder_plugin *plugin;
+ const char* vtrack;
+ uint8_t tnum = 0;
+
+ struct directory* flacdir = make_subdir(directory,
name);
+
+ while ((vtrack = flac_cue_track(pathname, ++tnum)) !=
NULL)
+ {
+ struct song *song =
songvec_find(&flacdir->songs, vtrack);
+
+ if (song == NULL)
+ {
+ song = song_file_new(vtrack, flacdir);
+ if (song == NULL)
+ return;
+
+ plugin =
decoder_plugin_from_suffix(suffix, false);
+
+ song->tag = plugin->tag_dup(pathname);
+
+ // subfunction
+ //flac_cue_tag_song(song->tag);
+ {
+ char tracknum[3];
+
+ snprintf( tracknum,
+ sizeof(tracknum), "%u",
+ tnum);
+
+ tag_add_item( song->tag,
+ TAG_ITEM_TRACK,
+ tracknum);
+ }
+
+ songvec_add(&flacdir->songs, song);
+ }
+ }
+
+ modified = true;
+
+ return;
+ }
+ }
+
if (decoder_plugin_from_suffix(suffix, false) != NULL) {
struct song *song = songvec_find(&directory->songs, name);
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------------------------------ Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA -OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise -Strategies to boost innovation and cut costs with open source participation -Receive a $600 discount off the registration fee with the source code: SFAD http://p.sf.net/sfu/XcvMzF8H
_______________________________________________ Musicpd-dev-team mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team
