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);
 

Attachment: 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
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team

Reply via email to