From 030e61115c9dcc804a5441329a9aef2ac9c3619b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 5 Jan 2010 21:46:32 +0100 Subject: [PATCH] playlist: added a FLAC playlist plugin This playlist plugin handles FLAC files with embedded CUE sheets. --- Makefile.am | 13 +++ src/playlist/flac_playlist_plugin.c | 170 ++++++++++++++++++++++++++++ src/playlist/flac_playlist_plugin.h | 25 ++++ src/playlist_list.c | 4 + 4 files changed, 212 insertions(+) create mode 100644 src/playlist/flac_playlist_plugin.c create mode 100644 src/playlist/flac_playlist_plugin.h diff --git a/Makefile.am b/Makefile.am index 5e8a6b4d9..c84362c0d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -162,6 +162,7 @@ mpd_headers = \ src/playlist/asx_playlist_plugin.h \ src/playlist/lastfm_playlist_plugin.h \ src/playlist/cue_playlist_plugin.h \ + src/playlist/flac_playlist_plugin.h \ src/poison.h \ src/riff.h \ src/aiff.h \ @@ -720,6 +721,10 @@ if HAVE_CUE PLAYLIST_SRC += src/playlist/cue_playlist_plugin.c endif +if HAVE_FLAC +PLAYLIST_SRC += src/playlist/flac_playlist_plugin.c +endif + # # Filter plugins @@ -799,10 +804,12 @@ test_run_input_SOURCES = test/run_input.c \ test_dump_playlist_CPPFLAGS = $(AM_CPPFLAGS) \ $(CUE_CFLAGS) \ + $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \ $(ARCHIVE_CFLAGS) \ $(INPUT_CFLAGS) test_dump_playlist_LDADD = $(MPD_LIBS) \ $(CUE_LIBS) \ + $(FLAC_LIBS) \ $(ARCHIVE_LIBS) \ $(INPUT_LIBS) \ $(GLIB_LIBS) @@ -820,6 +827,12 @@ if HAVE_CUE test_dump_playlist_SOURCES += src/cue/cue_tag.c endif +if HAVE_FLAC +test_dump_playlist_SOURCES += \ + src/replay_gain_info.c \ + src/decoder/flac_metadata.c +endif + test_run_decoder_CPPFLAGS = $(AM_CPPFLAGS) \ $(TAG_CFLAGS) \ $(ARCHIVE_CFLAGS) \ diff --git a/src/playlist/flac_playlist_plugin.c b/src/playlist/flac_playlist_plugin.c new file mode 100644 index 000000000..9d66fb331 --- /dev/null +++ b/src/playlist/flac_playlist_plugin.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2003-2010 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 "playlist/flac_playlist_plugin.h" +#include "playlist_plugin.h" +#include "tag.h" +#include "song.h" +#include "decoder/flac_metadata.h" + +#include + +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "flac" + +#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 + +struct flac_playlist { + struct playlist_provider base; + + char *uri; + + FLAC__StreamMetadata *cuesheet; + FLAC__StreamMetadata streaminfo; + + unsigned next_track; +}; + +static struct playlist_provider * +flac_playlist_open_uri(const char *uri) +{ + if (!g_path_is_absolute(uri)) + /* only local files supported */ + return NULL; + + FLAC__StreamMetadata *cuesheet; + if (!FLAC__metadata_get_cuesheet(uri, &cuesheet)) + return NULL; + + struct flac_playlist *playlist = g_new(struct flac_playlist, 1); + playlist_provider_init(&playlist->base, &flac_playlist_plugin); + + if (!FLAC__metadata_get_streaminfo(uri, &playlist->streaminfo)) { + FLAC__metadata_object_delete(playlist->cuesheet); + g_free(playlist); + return NULL; + } + + playlist->uri = g_strdup(uri); + playlist->cuesheet = cuesheet; + playlist->next_track = 0; + + return &playlist->base; +} + +static void +flac_playlist_close(struct playlist_provider *_playlist) +{ + struct flac_playlist *playlist = (struct flac_playlist *)_playlist; + + g_free(playlist->uri); + FLAC__metadata_object_delete(playlist->cuesheet); + g_free(playlist); +} + +static struct song * +flac_playlist_read(struct playlist_provider *_playlist) +{ + struct flac_playlist *playlist = (struct flac_playlist *)_playlist; + const FLAC__StreamMetadata_CueSheet *cs = + &playlist->cuesheet->data.cue_sheet; + + /* find the next audio track */ + + while (playlist->next_track < cs->num_tracks && + (cs->tracks[playlist->next_track].number > cs->num_tracks || + cs->tracks[playlist->next_track].type != 0)) + ++playlist->next_track; + + if (playlist->next_track >= cs->num_tracks) + return NULL; + + FLAC__uint64 start = cs->tracks[playlist->next_track].offset; + ++playlist->next_track; + FLAC__uint64 end = playlist->next_track < cs->num_tracks + ? cs->tracks[playlist->next_track].offset + : playlist->streaminfo.data.stream_info.total_samples; + + struct song *song = song_file_new(playlist->uri, NULL); + song->start_ms = start * 1000 / + playlist->streaminfo.data.stream_info.sample_rate; + song->end_ms = end * 1000 / + playlist->streaminfo.data.stream_info.sample_rate; + + char track[16]; + g_snprintf(track, sizeof(track), "%u", playlist->next_track); + song->tag = flac_tag_load(playlist->uri, track); + if (song->tag == NULL) + song->tag = tag_new(); + + song->tag->time = end > start + ? ((end - start - 1 + + playlist->streaminfo.data.stream_info.sample_rate) / + playlist->streaminfo.data.stream_info.sample_rate) + : 0; + + tag_clear_items_by_type(song->tag, TAG_TRACK); + tag_add_item(song->tag, TAG_TRACK, track); + + return song; +} + +static const char *const flac_playlist_suffixes[] = { + "flac", + NULL +}; + +static const char *const flac_playlist_mime_types[] = { + "application/flac", + "application/x-flac", + "audio/flac", + "audio/x-flac", + NULL +}; + +const struct playlist_plugin flac_playlist_plugin = { + .name = "flac", + + .open_uri = flac_playlist_open_uri, + .close = flac_playlist_close, + .read = flac_playlist_read, + + .suffixes = flac_playlist_suffixes, + .mime_types = flac_playlist_mime_types, +}; + +#else /* FLAC_API_VERSION_CURRENT <= 7 */ + +static bool +flac_playlist_init(G_GNUC_UNUSED const struct config_param *param) +{ + /* this libFLAC version does not support embedded CUE sheets; + disable this plugin */ + return false; +} + +const struct playlist_plugin flac_playlist_plugin = { + .name = "flac", + .init = flac_playlist_init, +}; + +#endif diff --git a/src/playlist/flac_playlist_plugin.h b/src/playlist/flac_playlist_plugin.h new file mode 100644 index 000000000..7b141264f --- /dev/null +++ b/src/playlist/flac_playlist_plugin.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2010 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_FLAC_PLAYLIST_PLUGIN_H +#define MPD_PLAYLIST_FLAC_PLAYLIST_PLUGIN_H + +extern const struct playlist_plugin flac_playlist_plugin; + +#endif diff --git a/src/playlist_list.c b/src/playlist_list.c index d034f4c1e..a836beb35 100644 --- a/src/playlist_list.c +++ b/src/playlist_list.c @@ -27,6 +27,7 @@ #include "playlist/pls_playlist_plugin.h" #include "playlist/asx_playlist_plugin.h" #include "playlist/cue_playlist_plugin.h" +#include "playlist/flac_playlist_plugin.h" #include "input_stream.h" #include "uri.h" #include "utils.h" @@ -49,6 +50,9 @@ static const struct playlist_plugin *const playlist_plugins[] = { #endif #ifdef HAVE_CUE &cue_playlist_plugin, +#endif +#ifdef HAVE_FLAC + &flac_playlist_plugin, #endif NULL };