diff options
-rw-r--r-- | NEWS | 11 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | doc/mpdconf.example | 22 | ||||
-rw-r--r-- | doc/protocol.xml | 18 | ||||
-rw-r--r-- | src/decoder/sidplay_plugin.cxx | 257 | ||||
-rwxr-xr-x | src/decoder/vorbis_plugin.c | 2 | ||||
-rw-r--r-- | src/output_init.c | 1 | ||||
-rw-r--r-- | src/update.c | 3 |
8 files changed, 308 insertions, 15 deletions
@@ -15,6 +15,9 @@ ver 0.16 (20??/??/??) - sndfile: new decoder plugin based on libsndfile - flac: load external cue sheet when no internal one - mpg123: new decoder plugin based on libmpg123 + - sidplay: support sub-tunes + - sidplay: implemented songlength database + - sidplay: support seeking * encoders: - twolame: new encoder plugin based on libtwolame * output: @@ -32,6 +35,14 @@ ver 0.16 (20??/??/??) * obey $(sysconfdir) for default mpd.conf location +ver 0.15.3 (2009/08/29) +* decoders: + - vorbis: faster tag scanning with ov_test_callback() +* output: + - fix stuttering due to uninitialized variable +* update: don't re-read unchanged container files + + ver 0.15.2 (2009/08/15) * tags: - ape: check the tag size (fixes integer underflow) diff --git a/configure.ac b/configure.ac index 681feae6c..e730c6977 100644 --- a/configure.ac +++ b/configure.ac @@ -554,12 +554,17 @@ if test x$enable_sidplay != xno; then # library name triggers an autoconf bug AC_CHECK_LIB(resid-builder, main, [found_sidplay=yes], [found_sidplay=no]) + + if test x$found_sidplay = xyes; then + AC_HAVE_LIBRARY(sidutils,, [found_sidplay=no]) + fi + MPD_AUTO_RESULT(sidplay, [sidplay decoder plugin], - [libresid-builder not found]) + [libresid-builder or libsidutils not found]) fi if test x$enable_sidplay = xyes; then - AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder") + AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder -lsidutils") AC_SUBST(SIDPLAY_CFLAGS,) AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support]) diff --git a/doc/mpdconf.example b/doc/mpdconf.example index 4e3b19a5a..8ce3a1b9f 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -363,3 +363,25 @@ input { #id3v1_encoding "ISO-8859-1" # ############################################################################### + + +# SIDPlay decoder ############################################################# +# +# songlength_database: +# Location of your songlengths file, as distributed with the HVSC. +# The sidplay plugin checks this for matching MD5 fingerprints. +# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq +# +# default_songlength: +# This is the default playing time in seconds for songs not in the +# songlength database, or in case you're not using a database. +# A value of 0 means play indefinitely. +# +#decoder { +# plugin "sidplay" +# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt" +# default_songlength "120" +#} +# +############################################################################### + diff --git a/doc/protocol.xml b/doc/protocol.xml index 95728197b..30f8792c8 100644 --- a/doc/protocol.xml +++ b/doc/protocol.xml @@ -278,6 +278,24 @@ </listitem> <listitem> <para> + <varname>nextsong</varname>: + <footnoteref linkend="since_0_15"/> + <returnvalue> playlist song number of the next + song to be played + </returnvalue> + </para> + </listitem> + <listitem> + <para> + <varname>nextsongid</varname>: + <footnoteref linkend="since_0_15"/> + <returnvalue>playlist songid of the next song + to be played + </returnvalue> + </para> + </listitem> + <listitem> + <para> <varname>time</varname>: <returnvalue>total time elapsed (of current playing/paused song)</returnvalue> diff --git a/src/decoder/sidplay_plugin.cxx b/src/decoder/sidplay_plugin.cxx index 54ab746e2..5228dbe89 100644 --- a/src/decoder/sidplay_plugin.cxx +++ b/src/decoder/sidplay_plugin.cxx @@ -21,14 +21,165 @@ extern "C" { #include "../decoder_api.h" } +#include <errno.h> +#include <stdlib.h> #include <glib.h> #include <sidplay/sidplay2.h> #include <sidplay/builders/resid.h> +#include <sidplay/utils/SidTuneMod.h> #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "sidplay" +#define SUBTUNE_PREFIX "tune_" + +static GPatternSpec *path_with_subtune; +static const char *songlength_file; +static GKeyFile *songlength_database; + +static bool all_files_are_containers; +static unsigned default_songlength; + +static bool +sidplay_init(const struct config_param *param) +{ + GError *err=NULL; + gchar *songlen_data; + gsize songlen_data_size; + + /* read the songlengths database file */ + songlength_file=config_get_block_string(param, + "songlength_database", NULL); + if(songlength_file) { + if(g_file_get_contents(songlength_file, &songlen_data, &songlen_data_size, NULL)) { + /* replace any ; comment characters with # */ + for(int i=0; i<songlen_data_size; i++) + if(songlen_data[i]==';') songlen_data[i]='#'; + + songlength_database=g_key_file_new(); + if(!g_key_file_load_from_data(songlength_database, + songlen_data, songlen_data_size, + G_KEY_FILE_NONE, &err)) { + g_warning("unable to parse songlengths file %s: %s", + songlength_file, err->message); + g_key_file_free(songlength_database); + songlength_database=NULL; + } + g_key_file_set_list_separator(songlength_database, ' '); + g_free(songlen_data); + } else { + g_warning("unable to read songlengths file %s: %s", + songlength_file, err->message); + } + } + + default_songlength=config_get_block_unsigned(param, + "default_songlength", 0); + + all_files_are_containers=config_get_block_bool(param, + "all_files_are_containers", true); + + path_with_subtune=g_pattern_spec_new( + "*/" SUBTUNE_PREFIX "???.sid"); + + return true; +} + +void +sidplay_finish() +{ + g_pattern_spec_free(path_with_subtune); + + if(songlength_database) + g_key_file_free(songlength_database); +} + +/** + * returns the file path stripped of any /tune_xxx.sid subtune + * suffix + */ +static char * +get_container_name(const char *path_fs) +{ + char *path_container=g_strdup(path_fs); + + if(!g_pattern_match(path_with_subtune, + strlen(path_container), path_container, NULL)) + return path_container; + + char *ptr=g_strrstr(path_container, "/" SUBTUNE_PREFIX); + if(ptr) *ptr='\0'; + + return path_container; +} + +/** + * returns tune number from file.sid/tune_xxx.sid style path or 1 if + * no subtune is appended + */ +static int +get_song_num(const char *path_fs) +{ + if(g_pattern_match(path_with_subtune, + strlen(path_fs), path_fs, NULL)) { + char *sub=g_strrstr(path_fs, "/" SUBTUNE_PREFIX); + if(!sub) return 1; + + sub+=strlen("/" SUBTUNE_PREFIX); + int song_num=strtol(sub, NULL, 10); + + if (errno == EINVAL) + return 1; + else + return song_num; + } else + return 1; +} + +/* get the song length in seconds */ +static int +get_song_length(const char *path_fs) +{ + if (songlength_database == NULL) + return -1; + + gchar *sid_file=get_container_name(path_fs); + SidTuneMod tune(sid_file); + g_free(sid_file); + if(!tune) { + g_warning("failed to load file for calculating md5 sum"); + return -1; + } + char md5sum[SIDTUNE_MD5_LENGTH+1]; + tune.createMD5(md5sum); + + int song_num=get_song_num(path_fs); + + gsize num_items; + gchar **values=g_key_file_get_string_list(songlength_database, + "Database", md5sum, &num_items, NULL); + if(!values || song_num>num_items) { + g_strfreev(values); + return -1; + } + + int minutes=strtol(values[song_num-1], NULL, 10); + if(errno==EINVAL) minutes=0; + + int seconds; + char *ptr=strchr(values[song_num-1], ':'); + if(ptr) { + seconds=strtol(ptr+1, NULL, 10); + if(errno==EINVAL) seconds=0; + } else + seconds=0; + + g_strfreev(values); + + return (minutes*60)+seconds; +} + static void sidplay_file_decode(struct decoder *decoder, const char *path_fs) { @@ -36,13 +187,19 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs) /* load the tune */ - SidTune tune(path_fs, NULL, true); + char *path_container=get_container_name(path_fs); + SidTune tune(path_container, NULL, true); + g_free(path_container); if (!tune) { g_warning("failed to load file"); return; } - tune.selectSong(1); + int song_num=get_song_num(path_fs); + tune.selectSong(song_num); + + int song_len=get_song_length(path_fs); + if(song_len==-1) song_len=default_songlength; /* initialize the player */ @@ -105,10 +262,12 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs) struct audio_format audio_format; audio_format_init(&audio_format, 48000, 16, 2); - decoder_initialized(decoder, &audio_format, false, -1); + decoder_initialized(decoder, &audio_format, true, (float)song_len); /* .. and play */ + float data_time=0; + int timebase=player.timebase(); enum decoder_command cmd; do { char buffer[4096]; @@ -119,29 +278,105 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs) break; cmd = decoder_data(decoder, NULL, buffer, nbytes, - 0, 0, NULL); - } while (cmd == DECODE_COMMAND_NONE); + data_time, 0, NULL); + + data_time=player.time()/timebase; + + if(cmd==DECODE_COMMAND_SEEK) { + int target_time=decoder_seek_where(decoder); + + /* can't rewind so return to zero and seek forward */ + if(target_time<data_time) { + player.stop(); + data_time=0; + } + + /* ignore data until target time is reached */ + while(data_time<target_time) { + nbytes=player.play(buffer, sizeof(buffer)); + if(nbytes==0) + break; + data_time=player.time()/timebase; + } + + decoder_command_finished(decoder); + } + + if(song_len && data_time>=(float)song_len) + break; + + } while (cmd != DECODE_COMMAND_STOP); } static struct tag * sidplay_tag_dup(const char *path_fs) { - SidTune tune(path_fs, NULL, true); + int song_num=get_song_num(path_fs); + char *path_container=get_container_name(path_fs); + + SidTune tune(path_container, NULL, true); + g_free(path_container); if (!tune) return NULL; const SidTuneInfo &info = tune.getInfo(); struct tag *tag = tag_new(); + /* title */ + const char *title; if (info.numberOfInfoStrings > 0 && info.infoString[0] != NULL) - tag_add_item(tag, TAG_ITEM_TITLE, info.infoString[0]); - + title=info.infoString[0]; + else + title=""; + + if(info.songs>1) { + char *tag_title=g_strdup_printf("%s (%d/%d)", + title, song_num, info.songs); + tag_add_item(tag, TAG_ITEM_TITLE, tag_title); + g_free(tag_title); + } else + tag_add_item(tag, TAG_ITEM_TITLE, title); + + /* artist */ if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL) tag_add_item(tag, TAG_ITEM_ARTIST, info.infoString[1]); + /* track */ + char *track=g_strdup_printf("%d", song_num); + tag_add_item(tag, TAG_ITEM_TRACK, track); + g_free(track); + + /* time */ + int song_len=get_song_length(path_fs); + if(song_len!=-1) tag->time=song_len; + return tag; } +static char * +sidplay_container_scan(const char *path_fs, const unsigned int tnum) +{ + SidTune tune(path_fs, NULL, true); + if (!tune) + return NULL; + + const SidTuneInfo &info=tune.getInfo(); + + /* Don't treat sids containing a single tune + as containers */ + if(!all_files_are_containers && info.songs<2) + return NULL; + + /* Construct container/tune path names, eg. + Delta.sid/tune_001.sid */ + if(tnum<=info.songs) { + char *subtune= g_strdup_printf( + SUBTUNE_PREFIX "%03u.sid", tnum); + return subtune; + } else + return NULL; +} + static const char *const sidplay_suffixes[] = { "sid", NULL @@ -150,12 +385,12 @@ static const char *const sidplay_suffixes[] = { extern const struct decoder_plugin sidplay_decoder_plugin; const struct decoder_plugin sidplay_decoder_plugin = { "sidplay", - NULL, /* init() */ - NULL, /* finish() */ + sidplay_init, + sidplay_finish, NULL, /* stream_decode() */ sidplay_file_decode, sidplay_tag_dup, - NULL, /* container_scan */ + sidplay_container_scan, sidplay_suffixes, NULL, /* mime_types */ }; diff --git a/src/decoder/vorbis_plugin.c b/src/decoder/vorbis_plugin.c index 39a075176..4548d1b57 100755 --- a/src/decoder/vorbis_plugin.c +++ b/src/decoder/vorbis_plugin.c @@ -382,7 +382,7 @@ vorbis_tag_dup(const char *file) return NULL; } - if (ov_open(fp, &vf, NULL, 0) < 0) { + if (ov_test_callbacks(fp, &vf, NULL, 0, OV_CALLBACKS_STREAMONLY) < 0) { fclose(fp); return NULL; } diff --git a/src/output_init.c b/src/output_init.c index 08873ac20..66444e9a5 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -178,6 +178,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, ao->plugin = plugin; ao->enabled = config_get_block_bool(param, "enabled", true); ao->open = false; + ao->pause = false; ao->fail_timer = NULL; /* set up the filter chain */ diff --git a/src/update.c b/src/update.c index 6778a5989..cfe574e17 100644 --- a/src/update.c +++ b/src/update.c @@ -502,7 +502,8 @@ update_regular_file(struct directory *directory, { struct song* song = songvec_find(&directory->songs, name); - if (plugin->container_scan != NULL) + if (!(song != NULL && st->st_mtime == song->mtime) && + plugin->container_scan != NULL) { if (update_container_file(directory, name, st, plugin)) { |