From 1735284a2ac1773614786b9f5caf50fa29347654 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sun, 12 Feb 2012 15:20:38 +0100 Subject: playlist/embcue: new plugin for reading embedded cue sheets Parses CUE data from the "CUESHEET" tag. Needs further integration in the update thread. --- Makefile.am | 10 +- doc/user.xml | 8 ++ src/playlist/embcue_playlist_plugin.c | 168 ++++++++++++++++++++++++++++++++++ src/playlist/embcue_playlist_plugin.h | 25 +++++ src/playlist_list.c | 2 + src/tag_file.c | 90 ++++++++++++++++++ src/tag_file.h | 37 ++++++++ test/dump_playlist.c | 93 +++++++++++++++++++ 8 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 src/playlist/embcue_playlist_plugin.c create mode 100644 src/playlist/embcue_playlist_plugin.h create mode 100644 src/tag_file.c create mode 100644 src/tag_file.h diff --git a/Makefile.am b/Makefile.am index 636bf064..977a9c22 100644 --- a/Makefile.am +++ b/Makefile.am @@ -336,6 +336,7 @@ src_mpd_SOURCES = \ src/tag_print.c \ src/tag_save.c \ src/tag_handler.c src/tag_handler.h \ + src/tag_file.c src/tag_file.h \ src/tokenizer.c \ src/text_file.c \ src/text_input_stream.c \ @@ -864,6 +865,8 @@ libplaylist_plugins_a_SOURCES = \ src/playlist/asx_playlist_plugin.c \ src/playlist/rss_playlist_plugin.c \ src/playlist/cue_playlist_plugin.c \ + src/playlist/embcue_playlist_plugin.c \ + src/playlist/embcue_playlist_plugin.h \ src/playlist_list.c libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) @@ -1001,15 +1004,20 @@ test_dump_playlist_LDADD = \ $(FLAC_LIBS) \ $(INPUT_LIBS) \ $(ARCHIVE_LIBS) \ + $(DECODER_LIBS) \ + $(TAG_LIBS) \ $(GLIB_LIBS) test_dump_playlist_SOURCES = test/dump_playlist.c \ + $(DECODER_SRC) \ src/io_thread.c src/io_thread.h \ src/conf.c src/tokenizer.c src/utils.c src/string_util.c\ src/uri.c \ src/song.c src/tag.c src/tag_pool.c src/tag_save.c \ - src/tag_handler.c \ + src/tag_handler.c src/tag_file.c \ + src/audio_check.c src/pcm_buffer.c \ src/text_input_stream.c src/fifo_buffer.c \ src/cue/cue_parser.c src/cue/cue_parser.h \ + src/timer.c \ src/fd_util.c if HAVE_FLAC diff --git a/doc/user.xml b/doc/user.xml index 4bd849f9..d0db21ef 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -1789,6 +1789,14 @@ cd mpd-version +
+ <varname>embcue</varname> + + + Reads CUE sheets from the "CUESHEET" tag of song files. + +
+
<varname>m3u</varname> diff --git a/src/playlist/embcue_playlist_plugin.c b/src/playlist/embcue_playlist_plugin.c new file mode 100644 index 00000000..b05b98e4 --- /dev/null +++ b/src/playlist/embcue_playlist_plugin.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** \file + * + * Playlist plugin that reads embedded cue sheets from the "CUESHEET" + * tag of a music file. + */ + +#include "config.h" +#include "playlist/embcue_playlist_plugin.h" +#include "playlist_plugin.h" +#include "tag.h" +#include "tag_handler.h" +#include "tag_file.h" +#include "tag_ape.h" +#include "tag_id3.h" +#include "song.h" +#include "cue/cue_parser.h" + +#include +#include +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "cue" + +struct embcue_playlist { + struct playlist_provider base; + + /** + * The value of the file's "CUESHEET" tag. + */ + char *cuesheet; + + /** + * The offset of the next line within "cuesheet". + */ + char *next; + + struct cue_parser *parser; +}; + +static void +embcue_tag_pair(const char *name, const char *value, void *ctx) +{ + struct embcue_playlist *playlist = ctx; + + if (playlist->cuesheet == NULL && + g_ascii_strcasecmp(name, "cuesheet") == 0) + playlist->cuesheet = g_strdup(value); +} + +static const struct tag_handler embcue_tag_handler = { + .pair = embcue_tag_pair, +}; + +static struct playlist_provider * +embcue_playlist_open_uri(const char *uri, + G_GNUC_UNUSED GMutex *mutex, + G_GNUC_UNUSED GCond *cond) +{ + if (!g_path_is_absolute(uri)) + /* only local files supported */ + return NULL; + + struct embcue_playlist *playlist = g_new(struct embcue_playlist, 1); + playlist_provider_init(&playlist->base, &embcue_playlist_plugin); + playlist->cuesheet = NULL; + + tag_file_scan(uri, &embcue_tag_handler, playlist); + if (playlist->cuesheet == NULL) { + tag_ape_scan2(uri, &embcue_tag_handler, playlist); + if (playlist->cuesheet == NULL) + tag_id3_scan(uri, &embcue_tag_handler, playlist); + } + + if (playlist->cuesheet == NULL) { + /* no "CUESHEET" tag found */ + g_free(playlist); + return NULL; + } + + playlist->next = playlist->cuesheet; + playlist->parser = cue_parser_new(); + + return &playlist->base; +} + +static void +embcue_playlist_close(struct playlist_provider *_playlist) +{ + struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; + + cue_parser_free(playlist->parser); + g_free(playlist->cuesheet); + g_free(playlist); +} + +static struct song * +embcue_playlist_read(struct playlist_provider *_playlist) +{ + struct embcue_playlist *playlist = (struct embcue_playlist *)_playlist; + + struct song *song = cue_parser_get(playlist->parser); + if (song != NULL) + return song; + + while (*playlist->next != 0) { + const char *line = playlist->next; + char *eol = strpbrk(playlist->next, "\r\n"); + if (eol != NULL) { + /* null-terminate the line */ + *eol = 0; + playlist->next = eol + 1; + } else + /* last line; put the "next" pointer to the + end of the buffer */ + playlist->next += strlen(line); + + cue_parser_feed(playlist->parser, line); + song = cue_parser_get(playlist->parser); + if (song != NULL) + return song; + } + + cue_parser_finish(playlist->parser); + return cue_parser_get(playlist->parser); +} + +static const char *const embcue_playlist_suffixes[] = { + /* a few codecs that are known to be supported; there are + probably many more */ + "flac", + "mp3", "mp2", + "mp4", "mp4a", "m4b", + "ape", + "wv", + "ogg", "oga", + NULL +}; + +const struct playlist_plugin embcue_playlist_plugin = { + .name = "cue", + + .open_uri = embcue_playlist_open_uri, + .close = embcue_playlist_close, + .read = embcue_playlist_read, + + .suffixes = embcue_playlist_suffixes, + .mime_types = NULL, +}; diff --git a/src/playlist/embcue_playlist_plugin.h b/src/playlist/embcue_playlist_plugin.h new file mode 100644 index 00000000..c5f21b27 --- /dev/null +++ b/src/playlist/embcue_playlist_plugin.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H +#define MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H + +extern const struct playlist_plugin embcue_playlist_plugin; + +#endif diff --git a/src/playlist_list.c b/src/playlist_list.c index f89ab386..0593ed0d 100644 --- a/src/playlist_list.c +++ b/src/playlist_list.c @@ -29,6 +29,7 @@ #include "playlist/asx_playlist_plugin.h" #include "playlist/rss_playlist_plugin.h" #include "playlist/cue_playlist_plugin.h" +#include "playlist/embcue_playlist_plugin.h" #include "playlist/flac_playlist_plugin.h" #include "input_stream.h" #include "uri.h" @@ -55,6 +56,7 @@ static const struct playlist_plugin *const playlist_plugins[] = { &lastfm_playlist_plugin, #endif &cue_playlist_plugin, + &embcue_playlist_plugin, #ifdef HAVE_FLAC &flac_playlist_plugin, #endif diff --git a/src/tag_file.c b/src/tag_file.c new file mode 100644 index 00000000..8d8a0f5f --- /dev/null +++ b/src/tag_file.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "tag_file.h" +#include "uri.h" +#include "decoder_list.h" +#include "decoder_plugin.h" +#include "input_stream.h" + +#include +#include /* for SEEK_SET */ + +bool +tag_file_scan(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + assert(path_fs != NULL); + assert(handler != NULL); + + /* check if there's a suffix and a plugin */ + + const char *suffix = uri_get_suffix(path_fs); + if (suffix == NULL) + return false; + + const struct decoder_plugin *plugin = + decoder_plugin_from_suffix(suffix, NULL); + if (plugin == NULL) + return false; + + struct input_stream *is = NULL; + GMutex *mutex = NULL; + GCond *cond = NULL; + + do { + /* load file tag */ + if (decoder_plugin_scan_file(plugin, path_fs, + handler, handler_ctx)) + break; + + /* fall back to stream tag */ + if (plugin->scan_stream != NULL) { + /* open the input_stream (if not already + open) */ + if (is == NULL) { + mutex = g_mutex_new(); + cond = g_cond_new(); + is = input_stream_open(path_fs, mutex, cond, + NULL); + } + + /* now try the stream_tag() method */ + if (is != NULL) { + if (decoder_plugin_scan_stream(plugin, is, + handler, + handler_ctx)) + break; + + input_stream_lock_seek(is, 0, SEEK_SET, NULL); + } + } + + plugin = decoder_plugin_from_suffix(suffix, plugin); + } while (plugin != NULL); + + if (is != NULL) { + input_stream_close(is); + g_cond_free(cond); + g_mutex_free(mutex); + } + + return plugin != NULL; +} diff --git a/src/tag_file.h b/src/tag_file.h new file mode 100644 index 00000000..8cf1af3c --- /dev/null +++ b/src/tag_file.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TAG_FILE_H +#define MPD_TAG_FILE_H + +#include "check.h" + +#include + +struct tag_handler; + +/** + * Scan the tags of a song file. Invokes matching decoder plugins, + * but does not invoke the special "APE" and "ID3" scanners. + */ +bool +tag_file_scan(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx); + +#endif diff --git a/test/dump_playlist.c b/test/dump_playlist.c index 079fdeac..b3818003 100644 --- a/test/dump_playlist.c +++ b/test/dump_playlist.c @@ -25,6 +25,8 @@ #include "tag_save.h" #include "conf.h" #include "song.h" +#include "decoder_api.h" +#include "decoder_list.h" #include "playlist_list.h" #include "playlist_plugin.h" @@ -43,6 +45,95 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, g_printerr("%s\n", message); } +void +decoder_initialized(G_GNUC_UNUSED struct decoder *decoder, + G_GNUC_UNUSED const struct audio_format *audio_format, + G_GNUC_UNUSED bool seekable, + G_GNUC_UNUSED float total_time) +{ +} + +enum decoder_command +decoder_get_command(G_GNUC_UNUSED struct decoder *decoder) +{ + return DECODE_COMMAND_NONE; +} + +void +decoder_command_finished(G_GNUC_UNUSED struct decoder *decoder) +{ +} + +double +decoder_seek_where(G_GNUC_UNUSED struct decoder *decoder) +{ + return 1.0; +} + +void +decoder_seek_error(G_GNUC_UNUSED struct decoder *decoder) +{ +} + +size_t +decoder_read(G_GNUC_UNUSED struct decoder *decoder, + struct input_stream *is, + void *buffer, size_t length) +{ + return input_stream_lock_read(is, buffer, length, NULL); +} + +void +decoder_timestamp(G_GNUC_UNUSED struct decoder *decoder, + G_GNUC_UNUSED double t) +{ +} + +enum decoder_command +decoder_data(G_GNUC_UNUSED struct decoder *decoder, + G_GNUC_UNUSED struct input_stream *is, + const void *data, size_t datalen, + G_GNUC_UNUSED uint16_t kbit_rate) +{ + write(1, data, datalen); + return DECODE_COMMAND_NONE; +} + +enum decoder_command +decoder_tag(G_GNUC_UNUSED struct decoder *decoder, + G_GNUC_UNUSED struct input_stream *is, + G_GNUC_UNUSED const struct tag *tag) +{ + return DECODE_COMMAND_NONE; +} + +float +decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder, + const struct replay_gain_info *replay_gain_info) +{ + const struct replay_gain_tuple *tuple = + &replay_gain_info->tuples[REPLAY_GAIN_ALBUM]; + if (replay_gain_tuple_defined(tuple)) + g_printerr("replay_gain[album]: gain=%f peak=%f\n", + tuple->gain, tuple->peak); + + tuple = &replay_gain_info->tuples[REPLAY_GAIN_TRACK]; + if (replay_gain_tuple_defined(tuple)) + g_printerr("replay_gain[track]: gain=%f peak=%f\n", + tuple->gain, tuple->peak); + + return 0.0; +} + +void +decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder, + G_GNUC_UNUSED float replay_gain_db, + char *mixramp_start, char *mixramp_end) +{ + g_free(mixramp_start); + g_free(mixramp_end); +} + int main(int argc, char **argv) { const char *uri; @@ -89,6 +180,7 @@ int main(int argc, char **argv) } playlist_list_global_init(); + decoder_plugin_init_all(); /* open the playlist */ @@ -152,6 +244,7 @@ int main(int argc, char **argv) g_cond_free(cond); g_mutex_free(mutex); + decoder_plugin_deinit_all(); playlist_list_global_finish(); input_stream_global_finish(); io_thread_deinit(); -- cgit v1.2.3