Commit 8ec10e2b authored by Led's avatar Led

Merge commit '0.14.2' into alt-git

parents 1a421a5e fd69782a
...@@ -36,3 +36,5 @@ tags ...@@ -36,3 +36,5 @@ tags
*~ *~
.stgit* .stgit*
doc/protocol.html doc/protocol.html
doc/protocol
doc/api
...@@ -10,12 +10,6 @@ Max Kellermann <max@duempel.org> ...@@ -10,12 +10,6 @@ Max Kellermann <max@duempel.org>
José Anarch <anarchsss@gmail.com> José Anarch <anarchsss@gmail.com>
JACK plugin JACK plugin
Guus Sliepen <guus@sliepen.eu.org>
libsamplerate code
Jim Ramsay <i.am@jimramsay.com>
Zerconf/avahi support
Patrik Weiskircher <pat@icore.at> Patrik Weiskircher <pat@icore.at>
Stored playlist commands Stored playlist commands
...@@ -28,6 +22,8 @@ Viliam Mateicka <viliam.mateicka@gmail.com> ...@@ -28,6 +22,8 @@ Viliam Mateicka <viliam.mateicka@gmail.com>
Eric Wollesen <encoded@xmtp.net> Eric Wollesen <encoded@xmtp.net>
encoder API, shout output encoder API, shout output
Thomas Jansen <mithi@mithi.net>
Former Developers Former Developers
----------------- -----------------
...@@ -38,6 +34,12 @@ Warren Dukes <warren.dukes@gmail.com> ...@@ -38,6 +34,12 @@ Warren Dukes <warren.dukes@gmail.com>
Niklas Hofer Niklas Hofer
'next' and 'previous' patch 'next' and 'previous' patch
Jim Ramsay <i.am@jimramsay.com>
Zerconf/avahi support
Guus Sliepen <guus@sliepen.eu.org>
libsamplerate code
J. Alexander Treuman <jat@spatialrift.net> J. Alexander Treuman <jat@spatialrift.net>
general, MP3, ID3, PulseAudio, format conversion, stored playlists general, MP3, ID3, PulseAudio, format conversion, stored playlists
......
...@@ -95,6 +95,15 @@ For MOD support. You will need libmikmod. ...@@ -95,6 +95,15 @@ For MOD support. You will need libmikmod.
libavcodec, libavformat (ffmpeg) - http://ffmpeg.mplayerhq.hu/ libavcodec, libavformat (ffmpeg) - http://ffmpeg.mplayerhq.hu/
Multi-codec library. Multi-codec library.
libsidplay2 - http://sidplay2.sourceforge.net/
For C64 SID support.
libfluidsynth - http://fluidsynth.resonance.org/
For MIDI support.
libwildmidi - http://wildmidi.sourceforge.net/
For MIDI support.
Optional Miscellaneous Dependencies Optional Miscellaneous Dependencies
----------------------------------- -----------------------------------
...@@ -108,6 +117,9 @@ For advanced samplerate conversions. ...@@ -108,6 +117,9 @@ For advanced samplerate conversions.
libcurl - http://curl.haxx.se/ libcurl - http://curl.haxx.se/
For playing HTTP streams. For playing HTTP streams.
libmms - https://launchpad.net/libmms
For playing MMS streams.
pkg-config pkg-config
---------- ----------
......
ver 0.15 - (200?/??/??) ver 0.15 - (200?/??/??)
* input:
- parse Icy-Metadata
- added support for the MMS protocol
* tags:
- support the "album artist" tag
- support MusicBrainz tags
- parse RVA2 tags in mp3 files
* decoders:
- audiofile: streaming support added
- modplug: another MOD plugin, based on libmodplug
- mikmod disabled by default, due to severe security issues in libmikmod
- sidplay: new decoder plugin for C64 SID (using libsidplay2)
- fluidsynth: new decoder plugin for MIDI files (using libfluidsynth)
- wildmidi: another decoder plugin for MIDI files (using libwildmidi)
* audio outputs:
- shout: enlarged buffer size to 32 kB
- null: allow disabling synchronization
* commands:
- "playlistinfo" supports a range now
- added "sticker database", command "sticker", which allows clients
to implement features like "song rating"
* Rewritten mixer code to support multiple mixers * Rewritten mixer code to support multiple mixers
* Add audio archive extraction support: * Add audio archive extraction support:
- bzip2 - bzip2
- iso9660 - iso9660
- zip - zip
* Add RVA2 tag support
* the option "error_file" was removed, all messages are logged into * the option "error_file" was removed, all messages are logged into
"log_file" "log_file"
* support logging to syslog * support logging to syslog
* fall back to XDG music directory if no music_directory is configured * fall back to XDG music directory if no music_directory is configured
* failure to read the state file is non-fatal * failure to read the state file is non-fatal
* added Icy-Metadata support
* --create-db starts the MPD daemon instead of exiting * --create-db starts the MPD daemon instead of exiting
* playlist_directory and music_directory are optional
* playlist: recalculate the queued song after random is toggled
* playlist: don't unpause on delete
ver 0.14.2 (2009/02/13)
* configure.ac:
- define HAVE_FFMPEG after all checks
* decoders:
- ffmpeg: added support for the tags comment, genre, year
- ffmpeg: don't warn of empty packet output
- ffmpeg: check if the time stamp is valid
- ffmpeg: fixed seek integer overflow
- ffmpeg: enable WAV streaming
- ffmpeg: added TTA support
- wavpack: pass NULL if the .wvc file fails to open
- mikmod: call MikMod_Exit() only in the finish() method
- aac: fix stream metadata
* audio outputs:
- jack: allocate ring buffers before connecting
- jack: clear "shutdown" flag on reconnect
- jack: reduced sleep time to 1ms
- shout: fixed memory leak in the mp3 encoder
- shout: switch to blocking mode
- shout: use libshout's synchronization
- shout: don't postpone metadata
- shout: clear buffer before calling the encoder
* mapper: remove trailing slashes from music_directory
* player: set player error when output device fails
* update: recursively purge deleted directories
* update: free deleted subdirectories
ver 0.14.1 (2009/01/17) ver 0.14.1 (2009/01/17)
* decoders: * decoders:
- mp4: support the writer/composer tag - mp4: support the writer/composer tag
...@@ -43,7 +92,6 @@ ver 0.14.1 (2009/01/17) ...@@ -43,7 +92,6 @@ ver 0.14.1 (2009/01/17)
* use custom PRNG for volume dithering (speedup) * use custom PRNG for volume dithering (speedup)
* detect libid3tag without pkg-config * detect libid3tag without pkg-config
ver 0.14 (2008/12/25) ver 0.14 (2008/12/25)
* audio outputs: * audio outputs:
- wait 10 seconds before reopening a failed device - wait 10 seconds before reopening a failed device
......
...@@ -5,7 +5,7 @@ AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2]) ...@@ -5,7 +5,7 @@ AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_DEFINE(PROTOCOL_VERSION, "0.14.0", [The mpd protocol version]) AC_DEFINE(PROTOCOL_VERSION, "0.15.0", [The mpd protocol version])
dnl dnl
...@@ -13,6 +13,8 @@ dnl programs ...@@ -13,6 +13,8 @@ dnl programs
dnl dnl
AC_PROG_CC_C99 AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_MAKE_SET AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
...@@ -247,6 +249,18 @@ if test x$enable_curl = xyes; then ...@@ -247,6 +249,18 @@ if test x$enable_curl = xyes; then
fi fi
AM_CONDITIONAL(HAVE_CURL, test x$enable_curl = xyes) AM_CONDITIONAL(HAVE_CURL, test x$enable_curl = xyes)
AC_ARG_ENABLE(mms,
AS_HELP_STRING([--enable-mms],
[enable the MMS protocol with libmms (default: disable)]),,
[enable_mms=no])
if test x$enable_mms = xyes; then
PKG_CHECK_MODULES(MMS, [libmms],
AC_DEFINE(ENABLE_MMS, 1, [Define when libmms is used for the MMS protocol]),
AC_MSG_ERROR([libmms not found]))
fi
AM_CONDITIONAL(ENABLE_MMS, test x$enable_mms = xyes)
dnl dnl
dnl archive plugins dnl archive plugins
...@@ -407,6 +421,21 @@ AC_ARG_WITH(tremor,[[ --with-tremor[=PFX] Use Tremor(vorbisidec) intege ...@@ -407,6 +421,21 @@ AC_ARG_WITH(tremor,[[ --with-tremor[=PFX] Use Tremor(vorbisidec) intege
AC_ARG_WITH(tremor-libraries,[ --with-tremor-libraries=DIR Directory where Tremor library is installed (optional)], tremor_libraries="$withval", tremor_libraries="") AC_ARG_WITH(tremor-libraries,[ --with-tremor-libraries=DIR Directory where Tremor library is installed (optional)], tremor_libraries="$withval", tremor_libraries="")
AC_ARG_WITH(tremor-includes,[ --with-tremor-includes=DIR Directory where Tremor header files are installed (optional)], tremor_includes="$withval", tremor_includes="") AC_ARG_WITH(tremor-includes,[ --with-tremor-includes=DIR Directory where Tremor header files are installed (optional)], tremor_includes="$withval", tremor_includes="")
AC_ARG_ENABLE(sidplay,
AS_HELP_STRING([--enable-sidplay],
[enable C64 SID support via libsidplay2 (default: disable)]),,
enable_sidplay=no)
AC_ARG_ENABLE(fluidsynth,
AS_HELP_STRING([--enable-fluidsynth],
[enable MIDI support via fluidsynth (default: disable)]),,
enable_fluidsynth=no)
AC_ARG_ENABLE(wildmidi,
AS_HELP_STRING([--enable-wildmidi],
[enable MIDI support via wildmidi (default: disable)]),,
enable_wildmidi=no)
AC_ARG_ENABLE(wavpack, AC_ARG_ENABLE(wavpack,
AS_HELP_STRING([--disable-wavpack], AS_HELP_STRING([--disable-wavpack],
[disable WavPack support (default: enable)]), [disable WavPack support (default: enable)]),
...@@ -615,6 +644,16 @@ if test x$enable_jack = xyes; then ...@@ -615,6 +644,16 @@ if test x$enable_jack = xyes; then
[enable_jack=no;AC_MSG_WARN([JACK not found -- disabling])]) [enable_jack=no;AC_MSG_WARN([JACK not found -- disabling])])
fi fi
if test x$enable_jack = xyes; then
# check whether jack_set_info_function() is available
old_LIBS=$LIBS
LIBS="$LIBS $JACK_LIBS"
AC_CHECK_FUNCS(jack_set_info_function)
LIBS=$old_LIBS
fi
AM_CONDITIONAL(HAVE_JACK, test x$enable_jack = xyes) AM_CONDITIONAL(HAVE_JACK, test x$enable_jack = xyes)
if test x$enable_id3 = xyes; then if test x$enable_id3 = xyes; then
...@@ -962,8 +1001,7 @@ fi ...@@ -962,8 +1001,7 @@ fi
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes) AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
if test x$enable_ffmpeg = xyes; then if test x$enable_ffmpeg = xyes; then
PKG_CHECK_MODULES(FFMPEG, [libavformat libavcodec libavutil], PKG_CHECK_MODULES(FFMPEG, [libavformat libavcodec libavutil],,
AC_DEFINE(HAVE_FFMPEG, 1, [Define for FFMPEG support]),
enable_ffmpeg=no) enable_ffmpeg=no)
fi fi
...@@ -986,23 +1024,78 @@ if test x$enable_ffmpeg = xyes; then ...@@ -986,23 +1024,78 @@ if test x$enable_ffmpeg = xyes; then
CPPCFLAGS=$old_CPPFLAGS CPPCFLAGS=$old_CPPFLAGS
fi fi
if test x$enable_ffmpeg = xyes; then
AC_DEFINE(HAVE_FFMPEG, 1, [Define for FFMPEG support])
fi
AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes) AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
if test x$enable_sidplay = xyes; then
# we have no test yet.. we're not using pkg-config here
# because libsidplay2's .pc file requires libtool
AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder")
AC_SUBST(SIDPLAY_CFLAGS,)
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support])
fi
AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes)
if test x$enable_fluidsynth = xyes; then
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth],
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]),
enable_fluidsynth=no)
fi
AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes)
if test x$enable_wildmidi = xyes; then
oldcflags=$CFLAGS
oldlibs=$LIBS
oldcppflags=$CPPFLAGS
AC_CHECK_LIB(WildMidi, WildMidi_Init,,
AC_MSG_ERROR([libwildmidi not found]))
CFLAGS=$oldcflags
LIBS=$oldlibs
CPPFLAGS=$oldcppflags
AC_SUBST(WILDMIDI_LIBS,-lWildMidi)
AC_SUBST(WILDMIDI_CFLAGS,)
AC_DEFINE(ENABLE_WILDMIDI, 1, [Define for wildmidi support])
fi
AM_CONDITIONAL(ENABLE_WILDMIDI, test x$enable_wildmidi = xyes)
dnl dnl
dnl Documentation dnl Documentation
dnl dnl
AC_MSG_CHECKING([for xmlto (DocBook processing)]) AC_ARG_ENABLE(documentation,
AC_PATH_PROG(XMLTO, xmlto) AS_HELP_STRING([--enable-documentation],
if test x$XMLTO != x; then [build documentation (default: disable)]),,
[enable_documentation=no])
if test x$enable_documentation = xyes; then
AC_PATH_PROG(XMLTO, xmlto)
if test x$XMLTO = x; then
AC_MSG_ERROR([xmlto not found])
fi
AC_SUBST(XMLTO) AC_SUBST(XMLTO)
AC_MSG_RESULT($XMLTO)
else AC_PATH_PROG(DOXYGEN, doxygen)
AC_MSG_RESULT(no) if test x$DOXYGEN = x; then
AC_MSG_ERROR([doxygen not found])
fi
AC_SUBST(DOXYGEN)
fi fi
AM_CONDITIONAL(HAVE_XMLTO, test x$XMLTO != x) AM_CONDITIONAL(ENABLE_DOCUMENTATION, test x$enable_documentation = xyes)
dnl dnl
...@@ -1262,6 +1355,24 @@ else ...@@ -1262,6 +1355,24 @@ else
echo " MODPLUG support ...............disabled" echo " MODPLUG support ...............disabled"
fi fi
if test x$enable_sidplay = xyes; then
echo " C64 SID support ...............enabled"
else
echo " C64 SID support ...............disabled"
fi
if test x$enable_fluidsynth = xyes; then
echo " fluidsynth MIDI support .......enabled"
else
echo " fluidsynth MIDI support .......disabled"
fi
if test x$enable_wildmidi = xyes; then
echo " wildmidi MIDI support .........enabled"
else
echo " wildmidi MIDI support .........disabled"
fi
if test x$enable_ffmpeg = xyes; then if test x$enable_ffmpeg = xyes; then
echo " FFMPEG support ................enabled" echo " FFMPEG support ................enabled"
else else
...@@ -1279,6 +1390,9 @@ if ...@@ -1279,6 +1390,9 @@ if
test x$enable_wavpack = xno && test x$enable_wavpack = xno &&
test x$enable_ffmpeg = xno && test x$enable_ffmpeg = xno &&
test x$enable_modplug = xno && test x$enable_modplug = xno &&
test x$enable_sidplay = xno &&
test x$enable_fluidsynth = xno &&
test x$enable_wildmidi = xno &&
test x$enable_mod = xno; then test x$enable_mod = xno; then
AC_MSG_ERROR([No input plugins supported!]) AC_MSG_ERROR([No input plugins supported!])
fi fi
...@@ -1327,6 +1441,12 @@ else ...@@ -1327,6 +1441,12 @@ else
echo " HTTP streaming (libcurl) ......disabled" echo " HTTP streaming (libcurl) ......disabled"
fi fi
if test x$enable_mms != xno; then
echo " MMS streaming (libmms) ........enabled"
else
echo " MMS streaming (libmms) ........disabled"
fi
echo "" echo ""
echo "##########################################" echo "##########################################"
echo "" echo ""
......
DOCBOOK_FILES = protocol.xml DOCBOOK_FILES = protocol.xml
DOCBOOK_HTML = $(patsubst %.xml,%.html,$(DOCBOOK_FILES)) DOCBOOK_HTML = $(patsubst %.xml,%/index.html,$(DOCBOOK_FILES))
man_MANS = mpd.1 mpd.conf.5 man_MANS = mpd.1 mpd.conf.5
doc_DATA = mpdconf.example doc_DATA = mpdconf.example
EXTRA_DIST = $(man_MANS) $(DOCBOOK_FILES) mpdconf.example EXTRA_DIST = $(man_MANS) $(DOCBOOK_FILES) mpdconf.example
MOSTLYCLEANFILES = $(DOCBOOK_HTML) if ENABLE_DOCUMENTATION
protocoldir = $(docdir)/protocol
protocol_DATA = $(wildcard protocol/*.html)
if HAVE_XMLTO $(DOCBOOK_HTML): %/index.html: %.xml
doc_DATA += $(DOCBOOK_HTML) $(XMLTO) -o protocol --stringparam chunker.output.encoding=utf-8 html $<
api/html/index.html: doxygen.conf
$(DOXYGEN) $<
all-local: $(DOCBOOK_HTML) api/html/index.html
clean-local:
rm -rf $(patsubst %.xml,%,$(DOCBOOK_FILES))
rm -rf api
install-data-local: api/html/index.html
$(mkinstalldirs) $(DESTDIR)$(docdir)/api/html
$(INSTALL_DATA) -c -m 644 api/html/*.html api/html/*.css api/html/*.png api/html/*.gif $(DESTDIR)$(docdir)/api/html
$(DOCBOOK_HTML): %.html: %.xml
$(XMLTO) html-nochunks $<
endif endif
...@@ -29,12 +29,6 @@ See \fBdocs/mpdconf.example\fP in the source tarball for an example ...@@ -29,12 +29,6 @@ See \fBdocs/mpdconf.example\fP in the source tarball for an example
configuration file. configuration file.
.SH REQUIRED PARAMETERS .SH REQUIRED PARAMETERS
.TP .TP
.B music_directory <directory>
This specifies the directory where music is located.
.TP
.B playlist_directory <directory>
This specifies the directory where saved playlists are stored.
.TP
.B follow_outside_symlinks <yes or no> .B follow_outside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing outside the music dir. Control if MPD will follow symbolic links pointing outside the music dir.
You must recreate the database after changing this option. You must recreate the database after changing this option.
...@@ -49,6 +43,10 @@ The default is "yes". ...@@ -49,6 +43,10 @@ The default is "yes".
.B db_file <file> .B db_file <file>
This specifies where the db file will be stored. This specifies where the db file will be stored.
.TP .TP
.B sticker_file <file>
The location of the sticker database. This is a database which
manages dynamic information attached to songs.
.TP
.B log_file <file> .B log_file <file>
This specifies where the log file should be located. This specifies where the log file should be located.
The special value "syslog" makes MPD use the local syslog daemon. The special value "syslog" makes MPD use the local syslog daemon.
...@@ -57,6 +55,14 @@ The special value "syslog" makes MPD use the local syslog daemon. ...@@ -57,6 +55,14 @@ The special value "syslog" makes MPD use the local syslog daemon.
.B pid_file <file> .B pid_file <file>
This specifies the file to save mpd's process ID in. This specifies the file to save mpd's process ID in.
.TP .TP
.B music_directory <directory>
This specifies the directory where music is located.
If you do not configure this, you can only play streams.
.TP
.B playlist_directory <directory>
This specifies the directory where saved playlists are stored.
If you do not configure this, you cannot save playlists.
.TP
.B state_file <file> .B state_file <file>
This specifies if a state file is used and where it is located. The state of This specifies if a state file is used and where it is located. The state of
mpd will be saved to this file when mpd is terminated by a TERM signal or by mpd will be saved to this file when mpd is terminated by a TERM signal or by
......
...@@ -782,11 +782,14 @@ OK ...@@ -782,11 +782,14 @@ OK
<term> <term>
<cmdsynopsis> <cmdsynopsis>
<command>shuffle</command> <command>shuffle</command>
<arg><replaceable>SONGRANGE</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
</term> </term>
<listitem> <listitem>
<para> <para>
Shuffles the current playlist. Shuffles the current playlist.
<varname>SONGRANGE</varname> is optional and specifies
a range of songs.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -1156,6 +1159,73 @@ OK ...@@ -1156,6 +1159,73 @@ OK
</section> </section>
<section> <section>
<title>Stickers</title>
<para>
"Stickers" are pieces of information attached to existing MPD
objects (e.g. song files, directories, albums). Clients can
create arbitrary name/value pairs. MPD itself does not assume
any special meaning in them.
</para>
<para>
The goal is to allow clients to share additional (possibly
dynamic) information about songs, which is neither stored on
the client (not available to other clients), nor stored in the
song files (MPD has no write access).
</para>
<para>
Client developers should create a standard for common sticker
names, to ensure interoperability.
</para>
<para>
Objects which may have stickers are addressed by their object
type ("song" for song objects) and their URI (the path within
the database for songs).
</para>
<variablelist>
<varlistentry id="command_sticker_get">
<term>
<cmdsynopsis>
<command>sticker</command>
<arg choice="plain">get</arg>
<arg choice="req"><replaceable>TYPE</replaceable></arg>
<arg choice="req"><replaceable>URI</replaceable></arg>
<arg choice="req"><replaceable>NAME</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Reads a sticker value for the specified object.
</para>
</listitem>
</varlistentry>
<varlistentry id="command_sticker_set">
<term>
<cmdsynopsis>
<command>sticker</command>
<arg choice="plain">set</arg>
<arg choice="req"><replaceable>TYPE</replaceable></arg>
<arg choice="req"><replaceable>URI</replaceable></arg>
<arg choice="req"><replaceable>NAME</replaceable></arg>
<arg choice="req"><replaceable>VALUE</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Adds a sticker value to the specified object. If a
sticker item with that name already exists, it is
replaced.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section>
<title>Connection settings</title> <title>Connection settings</title>
<variablelist> <variablelist>
......
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<book>
<title>The Music Player Daemon Sticker Database</title>
<chapter>
<title>Introduction to MPD's Sticker Database</title>
<para>
This document shell give a short guideline for recommended tags
for use in MPD's Sticker Database.
MPD's Sticker Database is a subsystem that enables users to add
custom tags. MPD does not alter the media files.
</para>
</chapter>
<chapter>
<title>Guideline for recommended tags</title>
<para>
Since there is no standard for tags in media files, this
document is trying to give you some help deciding what tags to
use. The selection of these tags tries to cover the most
widely used tags. This way the tags might still work in other
players, if you sync the database with your original media
files.
Keep in mind that we stick with lower case tags with underscores
instead of spaces. If there will be a Sync tool in future
its easy to change this on the fly, if needed.
</para>
<tag>
<name>rating</name>
<value>1-5</value>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a given song.
</para>
</tag>
<tag>
<name>album_rating</name>
<value>1-5</value>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a given album.
</para>
</tag>
<tag>
<name>style</name>
<value>Keyword</value>
<para>
This tag is used to keep the Genre tag clean, by now having 1000's of genres.
Instead you define a Main Genre for each file and can make a more specific
description. This should be one Keyword like "Post Punk" or "Progressive Death Metal"
An Alternative name for this tag is "Subgenre", time will tell which one gets
more support.
</para>
</tag>
<tag>
<name>lyrics</name>
<value>The lyrics of the song, including header with Artist - Title</value>
<para>
This one is self explaining. This gives the option to store lyrics of
a song where they belong to: mapped to the song
</para>
</tag>
<tag>
<name>similar_artists</name>
<value>Comma seperated list of artists</value>
<para>
This tag enables a last.fm alike aproach which will still work when being offline
Keep in mind, that this tag is absolutely non-standard! I am not aware of any
other player that uses a comparable tag.
</para>
</tag>
</chapter>
</book>
...@@ -4,12 +4,16 @@ mpd_CFLAGS = $(MPD_CFLAGS) ...@@ -4,12 +4,16 @@ mpd_CFLAGS = $(MPD_CFLAGS)
mpd_CPPFLAGS = \ mpd_CPPFLAGS = \
$(SQLITE_CFLAGS) \ $(SQLITE_CFLAGS) \
$(CURL_CFLAGS) \ $(CURL_CFLAGS) \
$(MMS_CFLAGS) \
$(AO_CFLAGS) $(ALSA_CFLAGS) \ $(AO_CFLAGS) $(ALSA_CFLAGS) \
$(SHOUT_CFLAGS) \ $(SHOUT_CFLAGS) \
$(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \ $(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \ $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(AUDIOFILE_CFLAGS) $(LIBMIKMOD_CFLAGS) \ $(AUDIOFILE_CFLAGS) $(LIBMIKMOD_CFLAGS) \
$(MODPLUG_CFLAGS) \ $(MODPLUG_CFLAGS) \
$(SIDPLAY_CFLAGS) \
$(FLUIDSYNTH_CFLAGS) \
$(WILDMIDI_CFLAGS) \
$(ID3TAG_CFLAGS) \ $(ID3TAG_CFLAGS) \
$(MAD_CFLAGS) \ $(MAD_CFLAGS) \
$(FFMPEG_CFLAGS) \ $(FFMPEG_CFLAGS) \
...@@ -17,11 +21,15 @@ mpd_CPPFLAGS = \ ...@@ -17,11 +21,15 @@ mpd_CPPFLAGS = \
mpd_LDADD = $(MPD_LIBS) \ mpd_LDADD = $(MPD_LIBS) \
$(SQLITE_LIBS) \ $(SQLITE_LIBS) \
$(CURL_LIBS) \ $(CURL_LIBS) \
$(MMS_LIBS) \
$(AO_LIBS) $(ALSA_LIBS) \ $(AO_LIBS) $(ALSA_LIBS) \
$(SHOUT_LIBS) \ $(SHOUT_LIBS) \
$(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \ $(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \ $(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
$(MODPLUG_LIBS) \ $(MODPLUG_LIBS) \
$(SIDPLAY_LIBS) \
$(FLUIDSYNTH_LIBS) \
$(WILDMIDI_LIBS) \
$(ID3TAG_LIBS) \ $(ID3TAG_LIBS) \
$(MAD_LIBS) \ $(MAD_LIBS) \
$(MP4FF_LIBS) \ $(MP4FF_LIBS) \
...@@ -33,12 +41,17 @@ mpd_headers = \ ...@@ -33,12 +41,17 @@ mpd_headers = \
ack.h \ ack.h \
audio.h \ audio.h \
audio_format.h \ audio_format.h \
audio_parser.h \
audioOutput.h \ audioOutput.h \
output_internal.h \ output_internal.h \
output_api.h \ output_api.h \
output_list.h \ output_list.h \
output_all.h \
output_thread.h \ output_thread.h \
output_control.h \ output_control.h \
output_state.h \
output_print.h \
output_command.h \
output/shout_plugin.h \ output/shout_plugin.h \
buffer2array.h \ buffer2array.h \
command.h \ command.h \
...@@ -64,6 +77,7 @@ mpd_headers = \ ...@@ -64,6 +77,7 @@ mpd_headers = \
input_stream.h \ input_stream.h \
input_file.h \ input_file.h \
input_curl.h \ input_curl.h \
input_mms.h \
icy_metadata.h \ icy_metadata.h \
client.h \ client.h \
listen.h \ listen.h \
...@@ -90,17 +104,24 @@ mpd_headers = \ ...@@ -90,17 +104,24 @@ mpd_headers = \
permission.h \ permission.h \
player_thread.h \ player_thread.h \
player_control.h \ player_control.h \
playerData.h \
playlist.h \ playlist.h \
playlist_internal.h \
playlist_print.h \
playlist_save.h \ playlist_save.h \
playlist_state.h \
queue.h \
queue_print.h \
queue_save.h \
replay_gain.h \ replay_gain.h \
sig_handlers.h \ sig_handlers.h \
song.h \ song.h \
song_print.h \ song_print.h \
song_save.h \ song_save.h \
song_sticker.h \
songvec.h \ songvec.h \
state_file.h \ state_file.h \
stats.h \ stats.h \
sticker.h \
tag.h \ tag.h \
tag_internal.h \ tag_internal.h \
tag_pool.h \ tag_pool.h \
...@@ -123,11 +144,16 @@ mpd_SOURCES = \ ...@@ -123,11 +144,16 @@ mpd_SOURCES = \
$(mpd_headers) \ $(mpd_headers) \
notify.c \ notify.c \
audio.c \ audio.c \
audio_parser.c \
audioOutput.c \ audioOutput.c \
output_api.c \ output_api.c \
output_list.c \ output_list.c \
output_all.c \
output_thread.c \ output_thread.c \
output_control.c \ output_control.c \
output_state.c \
output_print.c \
output_command.c \
output_init.c \ output_init.c \
output/null_plugin.c \ output/null_plugin.c \
buffer2array.c \ buffer2array.c \
...@@ -172,9 +198,16 @@ mpd_SOURCES = \ ...@@ -172,9 +198,16 @@ mpd_SOURCES = \
permission.c \ permission.c \
player_thread.c \ player_thread.c \
player_control.c \ player_control.c \
playerData.c \
playlist.c \ playlist.c \
playlist_global.c \
playlist_control.c \
playlist_edit.c \
playlist_print.c \
playlist_save.c \ playlist_save.c \
playlist_state.c \
queue.c \
queue_print.c \
queue_save.c \
replay_gain.c \ replay_gain.c \
sig_handlers.c \ sig_handlers.c \
song.c \ song.c \
...@@ -194,6 +227,10 @@ mpd_SOURCES = \ ...@@ -194,6 +227,10 @@ mpd_SOURCES = \
stored_playlist.c \ stored_playlist.c \
timer.c timer.c
if ENABLE_SQLITE
mpd_SOURCES += sticker.c song_sticker.c
endif
if HAVE_LIBSAMPLERATE if HAVE_LIBSAMPLERATE
mpd_SOURCES += pcm_resample_libsamplerate.c mpd_SOURCES += pcm_resample_libsamplerate.c
else else
...@@ -273,13 +310,25 @@ mpd_SOURCES += decoder/audiofile_plugin.c ...@@ -273,13 +310,25 @@ mpd_SOURCES += decoder/audiofile_plugin.c
endif endif
if HAVE_MIKMOD if HAVE_MIKMOD
mpd_SOURCES += decoder/mod_plugin.c mpd_SOURCES += decoder/mikmod_plugin.c
endif endif
if HAVE_MODPLUG if HAVE_MODPLUG
mpd_SOURCES += decoder/modplug_plugin.c mpd_SOURCES += decoder/modplug_plugin.c
endif endif
if ENABLE_SIDPLAY
mpd_SOURCES += decoder/sidplay_plugin.cxx
endif
if ENABLE_FLUIDSYNTH
mpd_SOURCES += decoder/fluidsynth_plugin.c
endif
if ENABLE_WILDMIDI
mpd_SOURCES += decoder/wildmidi_plugin.c
endif
if HAVE_FFMPEG if HAVE_FFMPEG
mpd_SOURCES += decoder/ffmpeg_plugin.c mpd_SOURCES += decoder/ffmpeg_plugin.c
endif endif
...@@ -302,6 +351,10 @@ if HAVE_CURL ...@@ -302,6 +351,10 @@ if HAVE_CURL
mpd_SOURCES += input_curl.c icy_metadata.c mpd_SOURCES += input_curl.c icy_metadata.c
endif endif
if ENABLE_MMS
mpd_SOURCES += input_mms.c
endif
if HAVE_ALSA if HAVE_ALSA
mpd_SOURCES += output/alsa_plugin.c mpd_SOURCES += output/alsa_plugin.c
......
...@@ -148,22 +148,16 @@ bz2_close(struct archive_file *file) ...@@ -148,22 +148,16 @@ bz2_close(struct archive_file *file)
/* single archive handling */ /* single archive handling */
static void static bool
bz2_setup_stream(struct archive_file *file, struct input_stream *is) bz2_open_stream(struct archive_file *file, struct input_stream *is,
G_GNUC_UNUSED const char *path)
{ {
bz2_context *context = (bz2_context *) file; bz2_context *context = (bz2_context *) file;
//setup file ops //setup file ops
is->plugin = &bz2_inputplugin; is->plugin = &bz2_inputplugin;
//insert back reference //insert back reference
is->archive = context; is->data = context;
is->seekable = false; is->seekable = false;
}
static bool
bz2_is_open(struct input_stream *is, G_GNUC_UNUSED const char *url)
{
bz2_context *context = (bz2_context *) is->archive;
if (!bz2_alloc(context)) { if (!bz2_alloc(context)) {
g_warning("alloc bz2 failed\n"); g_warning("alloc bz2 failed\n");
...@@ -175,7 +169,7 @@ bz2_is_open(struct input_stream *is, G_GNUC_UNUSED const char *url) ...@@ -175,7 +169,7 @@ bz2_is_open(struct input_stream *is, G_GNUC_UNUSED const char *url)
static void static void
bz2_is_close(struct input_stream *is) bz2_is_close(struct input_stream *is)
{ {
bz2_context *context = (bz2_context *) is->archive; bz2_context *context = (bz2_context *) is->data;
bz2_destroy(context); bz2_destroy(context);
is->data = NULL; is->data = NULL;
} }
...@@ -211,7 +205,7 @@ bz2_fillbuffer(bz2_context *context, ...@@ -211,7 +205,7 @@ bz2_fillbuffer(bz2_context *context,
static size_t static size_t
bz2_is_read(struct input_stream *is, void *ptr, size_t size) bz2_is_read(struct input_stream *is, void *ptr, size_t size)
{ {
bz2_context *context = (bz2_context *) is->archive; bz2_context *context = (bz2_context *) is->data;
bz_stream *bzstream; bz_stream *bzstream;
int bz_result; int bz_result;
size_t numBytes = size; size_t numBytes = size;
...@@ -253,7 +247,7 @@ bz2_is_read(struct input_stream *is, void *ptr, size_t size) ...@@ -253,7 +247,7 @@ bz2_is_read(struct input_stream *is, void *ptr, size_t size)
static bool static bool
bz2_is_eof(struct input_stream *is) bz2_is_eof(struct input_stream *is)
{ {
bz2_context *context = (bz2_context *) is->archive; bz2_context *context = (bz2_context *) is->data;
if (context->last_bz_result == BZ_STREAM_END) { if (context->last_bz_result == BZ_STREAM_END) {
return true; return true;
...@@ -262,19 +256,6 @@ bz2_is_eof(struct input_stream *is) ...@@ -262,19 +256,6 @@ bz2_is_eof(struct input_stream *is)
return false; return false;
} }
static bool
bz2_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
static int
bz2_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */ /* exported structures */
static const char *const bz2_extensions[] = { static const char *const bz2_extensions[] = {
...@@ -283,12 +264,9 @@ static const char *const bz2_extensions[] = { ...@@ -283,12 +264,9 @@ static const char *const bz2_extensions[] = {
}; };
static const struct input_plugin bz2_inputplugin = { static const struct input_plugin bz2_inputplugin = {
.open = bz2_is_open,
.close = bz2_is_close, .close = bz2_is_close,
.read = bz2_is_read, .read = bz2_is_read,
.eof = bz2_is_eof, .eof = bz2_is_eof,
.seek = bz2_is_seek,
.buffer = bz2_is_buffer
}; };
const struct archive_plugin bz2_plugin = { const struct archive_plugin bz2_plugin = {
...@@ -296,7 +274,7 @@ const struct archive_plugin bz2_plugin = { ...@@ -296,7 +274,7 @@ const struct archive_plugin bz2_plugin = {
.open = bz2_open, .open = bz2_open,
.scan_reset = bz2_scan_reset, .scan_reset = bz2_scan_reset,
.scan_next = bz2_scan_next, .scan_next = bz2_scan_next,
.setup_stream = bz2_setup_stream, .open_stream = bz2_open_stream,
.close = bz2_close, .close = bz2_close,
.suffixes = bz2_extensions .suffixes = bz2_extensions
}; };
......
...@@ -136,23 +136,17 @@ iso_close(struct archive_file *file) ...@@ -136,23 +136,17 @@ iso_close(struct archive_file *file)
/* single archive handling */ /* single archive handling */
static void static bool
iso_setup_stream(struct archive_file *file, struct input_stream *is) iso_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
{ {
iso_context *context = (iso_context *) file; iso_context *context = (iso_context *) file;
//setup file ops //setup file ops
is->plugin = &iso_inputplugin; is->plugin = &iso_inputplugin;
//insert back reference //insert back reference
is->archive = context; is->data = context;
//we are not seekable //we are not seekable
is->seekable = false; is->seekable = false;
}
static bool
iso_is_open(struct input_stream *is, const char *pathname)
{
iso_context *context = (iso_context *) is->archive;
context->statbuf = iso9660_ifs_stat_translate (context->iso, pathname); context->statbuf = iso9660_ifs_stat_translate (context->iso, pathname);
...@@ -168,7 +162,7 @@ iso_is_open(struct input_stream *is, const char *pathname) ...@@ -168,7 +162,7 @@ iso_is_open(struct input_stream *is, const char *pathname)
static void static void
iso_is_close(struct input_stream *is) iso_is_close(struct input_stream *is)
{ {
iso_context *context = (iso_context *) is->archive; iso_context *context = (iso_context *) is->data;
g_free(context->statbuf); g_free(context->statbuf);
} }
...@@ -176,7 +170,7 @@ iso_is_close(struct input_stream *is) ...@@ -176,7 +170,7 @@ iso_is_close(struct input_stream *is)
static size_t static size_t
iso_is_read(struct input_stream *is, void *ptr, size_t size) iso_is_read(struct input_stream *is, void *ptr, size_t size)
{ {
iso_context *context = (iso_context *) is->archive; iso_context *context = (iso_context *) is->data;
int toread, readed = 0; int toread, readed = 0;
int no_blocks, cur_block; int no_blocks, cur_block;
size_t left_bytes = context->statbuf->size - context->cur_ofs; size_t left_bytes = context->statbuf->size - context->cur_ofs;
...@@ -213,23 +207,10 @@ iso_is_read(struct input_stream *is, void *ptr, size_t size) ...@@ -213,23 +207,10 @@ iso_is_read(struct input_stream *is, void *ptr, size_t size)
static bool static bool
iso_is_eof(struct input_stream *is) iso_is_eof(struct input_stream *is)
{ {
iso_context *context = (iso_context *) is->archive; iso_context *context = (iso_context *) is->data;
return (context->cur_ofs == context->statbuf->size); return (context->cur_ofs == context->statbuf->size);
} }
static bool
iso_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
static int
iso_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */ /* exported structures */
static const char *const iso_extensions[] = { static const char *const iso_extensions[] = {
...@@ -238,12 +219,9 @@ static const char *const iso_extensions[] = { ...@@ -238,12 +219,9 @@ static const char *const iso_extensions[] = {
}; };
static const struct input_plugin iso_inputplugin = { static const struct input_plugin iso_inputplugin = {
.open = iso_is_open,
.close = iso_is_close, .close = iso_is_close,
.read = iso_is_read, .read = iso_is_read,
.eof = iso_is_eof, .eof = iso_is_eof,
.seek = iso_is_seek,
.buffer = iso_is_buffer
}; };
const struct archive_plugin iso_plugin = { const struct archive_plugin iso_plugin = {
...@@ -251,7 +229,7 @@ const struct archive_plugin iso_plugin = { ...@@ -251,7 +229,7 @@ const struct archive_plugin iso_plugin = {
.open = iso_open, .open = iso_open,
.scan_reset = iso_scan_reset, .scan_reset = iso_scan_reset,
.scan_next = iso_scan_next, .scan_next = iso_scan_next,
.setup_stream = iso_setup_stream, .open_stream = iso_open_stream,
.close = iso_close, .close = iso_close,
.suffixes = iso_extensions .suffixes = iso_extensions
}; };
...@@ -103,24 +103,19 @@ zip_close(struct archive_file *file) ...@@ -103,24 +103,19 @@ zip_close(struct archive_file *file)
/* single archive handling */ /* single archive handling */
static void static bool
zip_setup_stream(struct archive_file *file, struct input_stream *is) zip_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
{ {
zip_context *context = (zip_context *) file; zip_context *context = (zip_context *) file;
ZZIP_STAT z_stat;
//setup file ops //setup file ops
is->plugin = &zip_inputplugin; is->plugin = &zip_inputplugin;
//insert back reference //insert back reference
is->archive = context; is->data = context;
//we are not seekable //we are seekable (but its not recommendent to do so)
is->seekable = false; is->seekable = true;
}
static bool
zip_is_open(struct input_stream *is, const char *pathname)
{
zip_context *context = (zip_context *) is->archive;
ZZIP_STAT z_stat;
context->file = zzip_file_open(context->dir, pathname, 0); context->file = zzip_file_open(context->dir, pathname, 0);
if (!context->file) { if (!context->file) {
...@@ -135,14 +130,14 @@ zip_is_open(struct input_stream *is, const char *pathname) ...@@ -135,14 +130,14 @@ zip_is_open(struct input_stream *is, const char *pathname)
static void static void
zip_is_close(struct input_stream *is) zip_is_close(struct input_stream *is)
{ {
zip_context *context = (zip_context *) is->archive; zip_context *context = (zip_context *) is->data;
zzip_file_close (context->file); zzip_file_close (context->file);
} }
static size_t static size_t
zip_is_read(struct input_stream *is, void *ptr, size_t size) zip_is_read(struct input_stream *is, void *ptr, size_t size)
{ {
zip_context *context = (zip_context *) is->archive; zip_context *context = (zip_context *) is->data;
int ret; int ret;
ret = zzip_file_read(context->file, ptr, size); ret = zzip_file_read(context->file, ptr, size);
if (ret < 0) { if (ret < 0) {
...@@ -155,7 +150,7 @@ zip_is_read(struct input_stream *is, void *ptr, size_t size) ...@@ -155,7 +150,7 @@ zip_is_read(struct input_stream *is, void *ptr, size_t size)
static bool static bool
zip_is_eof(struct input_stream *is) zip_is_eof(struct input_stream *is)
{ {
zip_context *context = (zip_context *) is->archive; zip_context *context = (zip_context *) is->data;
return ((size_t) zzip_tell(context->file) == context->length); return ((size_t) zzip_tell(context->file) == context->length);
} }
...@@ -163,15 +158,15 @@ static bool ...@@ -163,15 +158,15 @@ static bool
zip_is_seek(G_GNUC_UNUSED struct input_stream *is, zip_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence) G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{ {
zip_context *context = (zip_context *) is->data;
zzip_off_t ofs = zzip_seek(context->file, offset, whence);
if (ofs != -1) {
is->offset = ofs;
return true;
}
return false; return false;
} }
static int
zip_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */ /* exported structures */
static const char *const zip_extensions[] = { static const char *const zip_extensions[] = {
...@@ -180,12 +175,10 @@ static const char *const zip_extensions[] = { ...@@ -180,12 +175,10 @@ static const char *const zip_extensions[] = {
}; };
static const struct input_plugin zip_inputplugin = { static const struct input_plugin zip_inputplugin = {
.open = zip_is_open,
.close = zip_is_close, .close = zip_is_close,
.read = zip_is_read, .read = zip_is_read,
.eof = zip_is_eof, .eof = zip_is_eof,
.seek = zip_is_seek, .seek = zip_is_seek,
.buffer = zip_is_buffer
}; };
const struct archive_plugin zip_plugin = { const struct archive_plugin zip_plugin = {
...@@ -193,7 +186,7 @@ const struct archive_plugin zip_plugin = { ...@@ -193,7 +186,7 @@ const struct archive_plugin zip_plugin = {
.open = zip_open, .open = zip_open,
.scan_reset = zip_scan_reset, .scan_reset = zip_scan_reset,
.scan_next = zip_scan_next, .scan_next = zip_scan_next,
.setup_stream = zip_setup_stream, .open_stream = zip_open_stream,
.close = zip_close, .close = zip_close,
.suffixes = zip_extensions .suffixes = zip_extensions
}; };
...@@ -70,11 +70,12 @@ struct archive_plugin { ...@@ -70,11 +70,12 @@ struct archive_plugin {
char *(*scan_next)(struct archive_file *); char *(*scan_next)(struct archive_file *);
/** /**
* this is used to setup input stream handle, to be able to read * Opens an input_stream of a file within the archive.
* from archive. open method of inputstream can be the used to *
* extract particular file * @param path the path within the archive
*/ */
void (*setup_stream)(struct archive_file *, struct input_stream *is); bool (*open_stream)(struct archive_file *, struct input_stream *is,
const char *path);
/** /**
* closes archive file. * closes archive file.
......
...@@ -20,59 +20,18 @@ ...@@ -20,59 +20,18 @@
#define MPD_AUDIO_H #define MPD_AUDIO_H
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#define AUDIO_AO_DRIVER_DEFAULT "default"
struct audio_format; struct audio_format;
struct tag;
struct client;
struct config_param;
unsigned int audio_output_count(void);
void getOutputAudioFormat(const struct audio_format *inFormat, void getOutputAudioFormat(const struct audio_format *inFormat,
struct audio_format *outFormat); struct audio_format *outFormat);
int parseAudioConfig(struct audio_format *audioFormat, char *conf);
/* make sure initPlayerData is called before this function!! */ /* make sure initPlayerData is called before this function!! */
void initAudioConfig(void); void initAudioConfig(void);
void finishAudioConfig(void); void finishAudioConfig(void);
void initAudioDriver(void);
void finishAudioDriver(void);
bool openAudioDevice(const struct audio_format *audioFormat);
bool playAudio(const char *playChunk, size_t size);
void audio_output_pause_all(void);
void dropBufferedAudio(void);
void closeAudioDevice(void);
bool isCurrentAudioFormat(const struct audio_format *audioFormat);
void sendMetadataToAudioDevice(const struct tag *tag);
/* these functions are called in the main parent process while the child
process is busy playing to the audio */
int enableAudioDevice(unsigned int device);
int disableAudioDevice(unsigned int device);
void printAudioDevices(struct client *client);
void readAudioDevicesState(FILE *fp);
void saveAudioDevicesState(FILE *fp);
bool mixer_control_setvol(unsigned int device, int volume, int rel); bool mixer_control_setvol(unsigned int device, int volume, int rel);
bool mixer_control_getvol(unsigned int device, int *volume); bool mixer_control_getvol(unsigned int device, int *volume);
bool mixer_configure_legacy(char *name, struct config_param *param);
#endif #endif
...@@ -41,14 +41,45 @@ static inline bool audio_format_defined(const struct audio_format *af) ...@@ -41,14 +41,45 @@ static inline bool audio_format_defined(const struct audio_format *af)
} }
/** /**
* Checks whether the sample rate is valid.
*
* @param sample_rate the sample rate in Hz
*/
static inline bool
audio_valid_sample_rate(unsigned sample_rate)
{
return sample_rate > 0 && sample_rate < (1 << 30);
}
/**
* Checks whether the sample format is valid.
*
* @param bits the number of significant bits per sample
*/
static inline bool
audio_valid_sample_format(unsigned bits)
{
return bits == 16 || bits == 24 || bits == 8;
}
/**
* Checks whether the number of channels is valid.
*/
static inline bool
audio_valid_channel_count(unsigned channels)
{
return channels == 1 || channels == 2;
}
/**
* Returns false if the format is not valid for playback with MPD. * Returns false if the format is not valid for playback with MPD.
* This function performs some basic validity checks. * This function performs some basic validity checks.
*/ */
static inline bool audio_format_valid(const struct audio_format *af) static inline bool audio_format_valid(const struct audio_format *af)
{ {
return af->sample_rate > 0 && return audio_valid_sample_rate(af->sample_rate) &&
(af->bits == 8 || af->bits == 16 || af->bits == 24) && audio_valid_sample_format(af->bits) &&
af->channels >= 1; audio_valid_channel_count(af->channels);
} }
static inline bool audio_format_equals(const struct audio_format *a, static inline bool audio_format_equals(const struct audio_format *a,
......
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Parser functions for audio related objects.
*
*/
#include "audio_parser.h"
#include "audio_format.h"
#include <stdlib.h>
/**
* The GLib quark used for errors reported by this library.
*/
static inline GQuark
audio_parser_quark(void)
{
return g_quark_from_static_string("audio_parser");
}
bool
audio_format_parse(struct audio_format *dest, const char *src, GError **error)
{
char *endptr;
unsigned long value;
audio_format_clear(dest);
/* parse sample rate */
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample rate missing");
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
"Sample format missing");
return false;
} else if (!audio_valid_sample_rate(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample rate: %lu", value);
return false;
}
dest->sample_rate = value;
/* parse sample format */
src = endptr + 1;
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample format missing");
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
"Channel count missing");
return false;
} else if (!audio_valid_sample_format(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample format: %lu", value);
return false;
}
dest->bits = value;
/* parse channel count */
src = endptr + 1;
value = strtoul(src, &endptr, 10);
if (*endptr != 0 || !audio_valid_channel_count(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid channel count: %s", src);
return false;
}
dest->channels = value;
return true;
}
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Parser functions for audio related objects.
*
*/
#ifndef AUDIO_PARSER_H
#define AUDIO_PARSER_H
#include <glib.h>
#include <stdbool.h>
struct audio_format;
bool
audio_format_parse(struct audio_format *dest, const char *src, GError **error);
#endif
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
#include <glib.h> #include <glib.h>
#include <assert.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#define MAX_STRING_SIZE MPD_PATH_MAX+80 #define MAX_STRING_SIZE MPD_PATH_MAX+80
...@@ -171,6 +173,7 @@ void config_global_init(void) ...@@ -171,6 +173,7 @@ void config_global_init(void)
registerConfigParam(CONF_FOLLOW_INSIDE_SYMLINKS, 0, 0); registerConfigParam(CONF_FOLLOW_INSIDE_SYMLINKS, 0, 0);
registerConfigParam(CONF_FOLLOW_OUTSIDE_SYMLINKS, 0, 0); registerConfigParam(CONF_FOLLOW_OUTSIDE_SYMLINKS, 0, 0);
registerConfigParam(CONF_DB_FILE, 0, 0); registerConfigParam(CONF_DB_FILE, 0, 0);
registerConfigParam(CONF_STICKER_FILE, false, false);
registerConfigParam(CONF_LOG_FILE, 0, 0); registerConfigParam(CONF_LOG_FILE, 0, 0);
registerConfigParam(CONF_ERROR_FILE, 0, 0); registerConfigParam(CONF_ERROR_FILE, 0, 0);
registerConfigParam(CONF_PID_FILE, 0, 0); registerConfigParam(CONF_PID_FILE, 0, 0);
...@@ -347,8 +350,17 @@ void config_read_file(const char *file) ...@@ -347,8 +350,17 @@ void config_read_file(const char *file)
fclose(fp); fclose(fp);
} }
void
config_add_param(const char *name, struct config_param *param)
{
struct config_entry *entry = config_entry_get(name);
assert(entry != NULL);
entry->params = g_slist_append(entry->params, param);
}
struct config_param * struct config_param *
config_get_next_param(const char *name, struct config_param * last) config_get_next_param(const char *name, const struct config_param * last)
{ {
struct config_entry *entry; struct config_entry *entry;
GSList *node; GSList *node;
...@@ -379,7 +391,7 @@ config_get_next_param(const char *name, struct config_param * last) ...@@ -379,7 +391,7 @@ config_get_next_param(const char *name, struct config_param * last)
const char * const char *
config_get_string(const char *name, const char *default_value) config_get_string(const char *name, const char *default_value)
{ {
struct config_param *param = config_get_param(name); const struct config_param *param = config_get_param(name);
if (param == NULL) if (param == NULL)
return default_value; return default_value;
...@@ -387,12 +399,53 @@ config_get_string(const char *name, const char *default_value) ...@@ -387,12 +399,53 @@ config_get_string(const char *name, const char *default_value)
return param->value; return param->value;
} }
const char *
config_get_path(const char *name)
{
struct config_param *param = config_get_param(name);
char *path;
if (param == NULL)
return NULL;
path = parsePath(param->value);
if (path == NULL)
g_error("error parsing \"%s\" at line %i\n",
name, param->line);
g_free(param->value);
return param->value = path;
}
unsigned
config_get_positive(const char *name, unsigned default_value)
{
const struct config_param *param = config_get_param(name);
long value;
char *endptr;
if (param == NULL)
return default_value;
value = strtol(param->value, &endptr, 0);
if (*endptr != 0)
g_error("Not a valid number in line %i", param->line);
if (value <= 0)
g_error("Not a positive number in line %i", param->line);
return (unsigned)value;
}
struct block_param * struct block_param *
getBlockParam(struct config_param * param, const char *name) getBlockParam(const struct config_param * param, const char *name)
{ {
struct block_param *ret = NULL; struct block_param *ret = NULL;
int i; int i;
if (param == NULL)
return NULL;
for (i = 0; i < param->num_block_params; i++) { for (i = 0; i < param->num_block_params; i++) {
if (0 == strcmp(name, param->block_params[i].name)) { if (0 == strcmp(name, param->block_params[i].name)) {
if (ret) { if (ret) {
...@@ -407,32 +460,9 @@ getBlockParam(struct config_param * param, const char *name) ...@@ -407,32 +460,9 @@ getBlockParam(struct config_param * param, const char *name)
return ret; return ret;
} }
struct config_param *
parseConfigFilePath(const char *name, int force)
{
struct config_param *param = config_get_param(name);
char *path;
if (!param && force)
g_error("config parameter \"%s\" not found\n", name);
if (!param)
return NULL;
path = parsePath(param->value);
if (!path)
g_error("error parsing \"%s\" at line %i\n",
name, param->line);
g_free(param->value);
param->value = path;
return param;
}
bool config_get_bool(const char *name, bool default_value) bool config_get_bool(const char *name, bool default_value)
{ {
struct config_param *param = config_get_param(name); const struct config_param *param = config_get_param(name);
int value; int value;
if (param == NULL) if (param == NULL)
...@@ -450,8 +480,41 @@ bool config_get_bool(const char *name, bool default_value) ...@@ -450,8 +480,41 @@ bool config_get_bool(const char *name, bool default_value)
return !!value; return !!value;
} }
const char *
config_get_block_string(const struct config_param *param, const char *name,
const char *default_value)
{
struct block_param *bp = getBlockParam(param, name);
if (bp == NULL)
return default_value;
return bp->value;
}
unsigned
config_get_block_unsigned(const struct config_param *param, const char *name,
unsigned default_value)
{
struct block_param *bp = getBlockParam(param, name);
long value;
char *endptr;
if (bp == NULL)
return default_value;
value = strtol(bp->value, &endptr, 0);
if (*endptr != 0)
g_error("Not a valid number in line %i", bp->line);
if (value < 0)
g_error("Not a positive number in line %i", bp->line);
return (unsigned)value;
}
bool bool
config_get_block_bool(struct config_param *param, const char *name, config_get_block_bool(const struct config_param *param, const char *name,
bool default_value) bool default_value)
{ {
struct block_param *bp = getBlockParam(param, name); struct block_param *bp = getBlockParam(param, name);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define CONF_FOLLOW_INSIDE_SYMLINKS "follow_inside_symlinks" #define CONF_FOLLOW_INSIDE_SYMLINKS "follow_inside_symlinks"
#define CONF_FOLLOW_OUTSIDE_SYMLINKS "follow_outside_symlinks" #define CONF_FOLLOW_OUTSIDE_SYMLINKS "follow_outside_symlinks"
#define CONF_DB_FILE "db_file" #define CONF_DB_FILE "db_file"
#define CONF_STICKER_FILE "sticker_file"
#define CONF_LOG_FILE "log_file" #define CONF_LOG_FILE "log_file"
#define CONF_ERROR_FILE "error_file" #define CONF_ERROR_FILE "error_file"
#define CONF_PID_FILE "pid_file" #define CONF_PID_FILE "pid_file"
...@@ -68,6 +69,9 @@ ...@@ -68,6 +69,9 @@
#define CONF_BOOL_UNSET -1 #define CONF_BOOL_UNSET -1
#define CONF_BOOL_INVALID -2 #define CONF_BOOL_INVALID -2
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
struct block_param { struct block_param {
char *name; char *name;
char *value; char *value;
...@@ -87,10 +91,17 @@ void config_global_finish(void); ...@@ -87,10 +91,17 @@ void config_global_finish(void);
void config_read_file(const char *file); void config_read_file(const char *file);
/**
* Adds a new configuration parameter. The name must be registered
* with registerConfigParam().
*/
void
config_add_param(const char *name, struct config_param *param);
/* don't free the returned value /* don't free the returned value
set _last_ to NULL to get first entry */ set _last_ to NULL to get first entry */
struct config_param * struct config_param *
config_get_next_param(const char *name, struct config_param *last); config_get_next_param(const char *name, const struct config_param *last);
static inline struct config_param * static inline struct config_param *
config_get_param(const char *name) config_get_param(const char *name)
...@@ -101,16 +112,39 @@ config_get_param(const char *name) ...@@ -101,16 +112,39 @@ config_get_param(const char *name)
const char * const char *
config_get_string(const char *name, const char *default_value); config_get_string(const char *name, const char *default_value);
struct block_param * /**
getBlockParam(struct config_param *param, const char *name); * Returns an optional configuration variable which contains an
* absolute path. If there is a tilde prefix, it is expanded. Aborts
* MPD if the path is not a valid absolute path.
*/
const char *
config_get_path(const char *name);
struct config_param * unsigned
parseConfigFilePath(const char *name, int force); config_get_positive(const char *name, unsigned default_value);
struct block_param *
getBlockParam(const struct config_param *param, const char *name);
bool config_get_bool(const char *name, bool default_value); bool config_get_bool(const char *name, bool default_value);
const char *
config_get_block_string(const struct config_param *param, const char *name,
const char *default_value);
static inline char *
config_dup_block_string(const struct config_param *param, const char *name,
const char *default_value)
{
return g_strdup(config_get_block_string(param, name, default_value));
}
unsigned
config_get_block_unsigned(const struct config_param *param, const char *name,
unsigned default_value);
bool bool
config_get_block_bool(struct config_param *param, const char *name, config_get_block_bool(const struct config_param *param, const char *name,
bool default_value); bool default_value);
struct config_param * struct config_param *
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
*/ */
#include "crossfade.h" #include "crossfade.h"
#include "audio.h"
#include "pcm_mix.h" #include "pcm_mix.h"
#include "pipe.h" #include "pipe.h"
#include "audio_format.h" #include "audio_format.h"
...@@ -29,12 +28,14 @@ ...@@ -29,12 +28,14 @@
unsigned cross_fade_calc(float duration, float total_time, unsigned cross_fade_calc(float duration, float total_time,
const struct audio_format *af, const struct audio_format *af,
const struct audio_format *old_format,
unsigned max_chunks) unsigned max_chunks)
{ {
unsigned int chunks; unsigned int chunks;
if (duration <= 0 || duration >= total_time || if (duration <= 0 || duration >= total_time ||
!isCurrentAudioFormat(af)) /* we can't crossfade when the audio formats are different */
!audio_format_equals(af, old_format))
return 0; return 0;
assert(duration > 0); assert(duration > 0);
......
...@@ -25,6 +25,7 @@ struct music_chunk; ...@@ -25,6 +25,7 @@ struct music_chunk;
unsigned cross_fade_calc(float duration, float total_time, unsigned cross_fade_calc(float duration, float total_time,
const struct audio_format *af, const struct audio_format *af,
const struct audio_format *old_format,
unsigned max_chunks); unsigned max_chunks);
void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b, void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
......
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
*/ */
#include "daemon.h" #include "daemon.h"
#include "conf.h"
#include <glib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -28,6 +29,60 @@ ...@@ -28,6 +29,60 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#ifndef WIN32
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#endif
#ifndef WIN32
/** the Unix user name which MPD runs as */
static char *user_name;
/** the Unix user id which MPD runs as */
static uid_t user_uid;
/** the Unix group id which MPD runs as */
static gid_t user_gid;
/** the absolute path of the pidfile */
static char *pidfile;
#endif
void
daemonize_kill(void)
{
#ifndef WIN32
FILE *fp;
int pid, ret;
if (pidfile == NULL)
g_error("no pid_file specified in the config file");
fp = fopen(pidfile, "r");
if (fp == NULL)
g_error("unable to open pid file \"%s\": %s",
pidfile, g_strerror(errno));
if (fscanf(fp, "%i", &pid) != 1) {
g_error("unable to read the pid from file \"%s\"",
pidfile);
}
fclose(fp);
ret = kill(pid, SIGTERM);
if (ret < 0)
g_error("unable to kill proccess %i: %s",
pid, g_strerror(errno));
exit(EXIT_SUCCESS);
#else
g_error("--kill is not available on WIN32");
#endif
}
void void
daemonize_close_stdin(void) daemonize_close_stdin(void)
{ {
...@@ -42,26 +97,53 @@ daemonize_close_stdin(void) ...@@ -42,26 +97,53 @@ daemonize_close_stdin(void)
} }
void void
daemonize(Options *options) daemonize_set_user(void)
{
#ifndef WIN32
if (user_name != NULL) {
/* get uid */
if (setgid(user_gid) == -1) {
g_error("cannot setgid for user \"%s\": %s",
user_name, g_strerror(errno));
}
#ifdef _BSD_SOURCE
/* init suplementary groups
* (must be done before we change our uid)
*/
if (initgroups(user_name, user_gid) == -1) {
g_warning("cannot init supplementary groups "
"of user \"%s\": %s",
user_name, g_strerror(errno));
}
#endif
/* set uid */
if (setuid(user_uid) == -1) {
g_error("cannot change to uid of user \"%s\": %s",
user_name, g_strerror(errno));
}
}
#endif
}
void
daemonize(bool detach)
{ {
#ifndef WIN32 #ifndef WIN32
FILE *fp = NULL; FILE *fp = NULL;
struct config_param *pidFileParam =
parseConfigFilePath(CONF_PID_FILE, 0);
if (pidFileParam) { if (pidfile != NULL) {
/* do this before daemon'izing so we can fail gracefully if we can't /* do this before daemon'izing so we can fail gracefully if we can't
* write to the pid file */ * write to the pid file */
g_debug("opening pid file"); g_debug("opening pid file");
fp = fopen(pidFileParam->value, "w+"); fp = fopen(pidfile, "w+");
if (!fp) { if (!fp) {
g_error("could not open %s \"%s\" (at line %i) for writing: %s", g_error("could not create pid file \"%s\": %s",
CONF_PID_FILE, pidFileParam->value, pidfile, g_strerror(errno));
pidFileParam->line, strerror(errno));
} }
} }
if (options->daemon) { if (detach) {
int pid; int pid;
fflush(NULL); fflush(NULL);
...@@ -81,7 +163,7 @@ daemonize(Options *options) ...@@ -81,7 +163,7 @@ daemonize(Options *options)
g_debug("daemonized!"); g_debug("daemonized!");
} }
if (pidFileParam) { if (pidfile != NULL) {
g_debug("writing pid file"); g_debug("writing pid file");
fprintf(fp, "%lu\n", (unsigned long)getpid()); fprintf(fp, "%lu\n", (unsigned long)getpid());
fclose(fp); fclose(fp);
...@@ -91,3 +173,39 @@ daemonize(Options *options) ...@@ -91,3 +173,39 @@ daemonize(Options *options)
(void)options; (void)options;
#endif #endif
} }
void
daemonize_init(const char *user, const char *_pidfile)
{
#ifndef WIN32
user_name = g_strdup(user);
if (user_name != NULL) {
struct passwd *pwd = getpwnam(user_name);
if (pwd == NULL)
g_error("no such user \"%s\"", user_name);
user_uid = pwd->pw_uid;
user_gid = pwd->pw_gid;
/* this is needed by libs such as arts */
g_setenv("HOME", pwd->pw_dir, true);
}
pidfile = g_strdup(_pidfile);
#else
(void)user;
(void)_pidfile;
#endif
}
void
daemonize_finish(void)
{
#ifndef WIN32
if (pidfile != NULL)
unlink(pidfile);
g_free(user_name);
g_free(pidfile);
#endif
}
...@@ -19,7 +19,20 @@ ...@@ -19,7 +19,20 @@
#ifndef DAEMON_H #ifndef DAEMON_H
#define DAEMON_H #define DAEMON_H
#include "cmdline.h" #include <stdbool.h>
void
daemonize_init(const char *user, const char *pidfile);
void
daemonize_finish(void);
/**
* Kill the MPD which is currently running, pid determined from the
* pid file.
*/
void
daemonize_kill(void);
/** /**
* Close stdin (fd 0) and re-open it as /dev/null. * Close stdin (fd 0) and re-open it as /dev/null.
...@@ -27,7 +40,13 @@ ...@@ -27,7 +40,13 @@
void void
daemonize_close_stdin(void); daemonize_close_stdin(void);
/**
* Change to the configured Unix user.
*/
void
daemonize_set_user(void);
void void
daemonize(Options *options); daemonize(bool detach);
#endif #endif
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include "directory.h" #include "directory.h"
#include "directory_save.h" #include "directory_save.h"
#include "song.h" #include "song.h"
#include "conf.h"
#include "path.h" #include "path.h"
#include "stats.h" #include "stats.h"
#include "config.h" #include "config.h"
...@@ -39,20 +38,39 @@ ...@@ -39,20 +38,39 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "database" #define G_LOG_DOMAIN "database"
static char *database_path;
static struct directory *music_root; static struct directory *music_root;
static time_t directory_dbModTime; static time_t directory_dbModTime;
void void
db_init(void) db_init(const char *path)
{ {
music_root = directory_new("", NULL); database_path = g_strdup(path);
if (path != NULL)
music_root = directory_new("", NULL);
} }
void void
db_finish(void) db_finish(void)
{ {
assert((database_path == NULL) == (music_root == NULL));
if (music_root != NULL)
directory_free(music_root);
g_free(database_path);
}
void
db_clear(void)
{
assert(music_root != NULL);
directory_free(music_root); directory_free(music_root);
music_root = directory_new("", NULL);
} }
struct directory * struct directory *
...@@ -66,6 +84,9 @@ db_get_root(void) ...@@ -66,6 +84,9 @@ db_get_root(void)
struct directory * struct directory *
db_get_directory(const char *name) db_get_directory(const char *name)
{ {
if (music_root == NULL)
return NULL;
if (name == NULL) if (name == NULL)
return music_root; return music_root;
...@@ -75,30 +96,37 @@ db_get_directory(const char *name) ...@@ -75,30 +96,37 @@ db_get_directory(const char *name)
struct song * struct song *
db_get_song(const char *file) db_get_song(const char *file)
{ {
struct song *song = NULL; struct song *song;
struct directory *directory; struct directory *directory;
char *dir = NULL; char *duplicated, *shortname, *dir;
char *duplicated = g_strdup(file);
char *shortname = strrchr(duplicated, '/'); assert(file != NULL);
g_debug("get song: %s", file); g_debug("get song: %s", file);
if (music_root == NULL)
return NULL;
duplicated = g_strdup(file);
shortname = strrchr(duplicated, '/');
if (!shortname) { if (!shortname) {
shortname = duplicated; shortname = duplicated;
dir = NULL;
} else { } else {
*shortname = '\0'; *shortname = '\0';
++shortname; ++shortname;
dir = duplicated; dir = duplicated;
} }
if (!(directory = db_get_directory(dir))) directory = db_get_directory(dir);
goto out; if (directory != NULL)
if (!(song = songvec_find(&directory->songs, shortname))) song = songvec_find(&directory->songs, shortname);
goto out; else
assert(song->parent == directory); song = NULL;
out: assert(song == NULL || song->parent == directory);
free(duplicated);
g_free(duplicated);
return song; return song;
} }
...@@ -109,6 +137,9 @@ db_walk(const char *name, ...@@ -109,6 +137,9 @@ db_walk(const char *name,
{ {
struct directory *directory; struct directory *directory;
if (music_root == NULL)
return -1;
if ((directory = db_get_directory(name)) == NULL) { if ((directory = db_get_directory(name)) == NULL) {
struct song *song; struct song *song;
if ((song = db_get_song(name)) && forEachSong) { if ((song = db_get_song(name)) && forEachSong) {
...@@ -120,42 +151,32 @@ db_walk(const char *name, ...@@ -120,42 +151,32 @@ db_walk(const char *name,
return directory_walk(directory, forEachSong, forEachDir, data); return directory_walk(directory, forEachSong, forEachDir, data);
} }
static char *
db_get_file(void)
{
struct config_param *param = parseConfigFilePath(CONF_DB_FILE, 1);
assert(param);
assert(param->value);
return param->value;
}
bool bool
db_check(void) db_check(void)
{ {
struct stat st; struct stat st;
char *dbFile = db_get_file();
assert(database_path != NULL);
/* Check if the file exists */ /* Check if the file exists */
if (access(dbFile, F_OK)) { if (access(database_path, F_OK)) {
/* If the file doesn't exist, we can't check if we can write /* If the file doesn't exist, we can't check if we can write
* it, so we are going to try to get the directory path, and * it, so we are going to try to get the directory path, and
* see if we can write a file in that */ * see if we can write a file in that */
char *dirPath = g_path_get_dirname(dbFile); char *dirPath = g_path_get_dirname(database_path);
/* Check that the parent part of the path is a directory */ /* Check that the parent part of the path is a directory */
if (stat(dirPath, &st) < 0) { if (stat(dirPath, &st) < 0) {
g_free(dirPath); g_free(dirPath);
g_warning("Couldn't stat parent directory of db file " g_warning("Couldn't stat parent directory of db file "
"\"%s\": %s", dbFile, strerror(errno)); "\"%s\": %s", database_path, strerror(errno));
return false; return false;
} }
if (!S_ISDIR(st.st_mode)) { if (!S_ISDIR(st.st_mode)) {
g_free(dirPath); g_free(dirPath);
g_warning("Couldn't create db file \"%s\" because the " g_warning("Couldn't create db file \"%s\" because the "
"parent path is not a directory", dbFile); "parent path is not a directory", database_path);
return false; return false;
} }
...@@ -173,21 +194,21 @@ db_check(void) ...@@ -173,21 +194,21 @@ db_check(void)
} }
/* Path exists, now check if it's a regular file */ /* Path exists, now check if it's a regular file */
if (stat(dbFile, &st) < 0) { if (stat(database_path, &st) < 0) {
g_warning("Couldn't stat db file \"%s\": %s", g_warning("Couldn't stat db file \"%s\": %s",
dbFile, strerror(errno)); database_path, strerror(errno));
return false; return false;
} }
if (!S_ISREG(st.st_mode)) { if (!S_ISREG(st.st_mode)) {
g_warning("db file \"%s\" is not a regular file", dbFile); g_warning("db file \"%s\" is not a regular file", database_path);
return false; return false;
} }
/* And check that we can write to it */ /* And check that we can write to it */
if (access(dbFile, R_OK | W_OK)) { if (access(database_path, R_OK | W_OK)) {
g_warning("Can't open db file \"%s\" for reading/writing: %s", g_warning("Can't open db file \"%s\" for reading/writing: %s",
dbFile, strerror(errno)); database_path, strerror(errno));
return false; return false;
} }
...@@ -198,9 +219,11 @@ bool ...@@ -198,9 +219,11 @@ bool
db_save(void) db_save(void)
{ {
FILE *fp; FILE *fp;
char *dbFile = db_get_file();
struct stat st; struct stat st;
assert(database_path != NULL);
assert(music_root != NULL);
g_debug("removing empty directories from DB"); g_debug("removing empty directories from DB");
directory_prune_empty(music_root); directory_prune_empty(music_root);
...@@ -210,10 +233,10 @@ db_save(void) ...@@ -210,10 +233,10 @@ db_save(void)
g_debug("writing DB"); g_debug("writing DB");
fp = fopen(dbFile, "w"); fp = fopen(database_path, "w");
if (!fp) { if (!fp) {
g_warning("unable to write to db file \"%s\": %s", g_warning("unable to write to db file \"%s\": %s",
dbFile, strerror(errno)); database_path, strerror(errno));
return false; return false;
} }
...@@ -232,7 +255,7 @@ db_save(void) ...@@ -232,7 +255,7 @@ db_save(void)
while (fclose(fp) && errno == EINTR); while (fclose(fp) && errno == EINTR);
if (stat(dbFile, &st) == 0) if (stat(database_path, &st) == 0)
directory_dbModTime = st.st_mtime; directory_dbModTime = st.st_mtime;
return true; return true;
...@@ -242,19 +265,19 @@ bool ...@@ -242,19 +265,19 @@ bool
db_load(void) db_load(void)
{ {
FILE *fp = NULL; FILE *fp = NULL;
char *dbFile = db_get_file();
struct stat st; struct stat st;
char buffer[100]; char buffer[100];
bool foundFsCharset = false, foundVersion = false; bool foundFsCharset = false, foundVersion = false;
assert(database_path != NULL);
assert(music_root != NULL); assert(music_root != NULL);
if (!music_root) if (!music_root)
music_root = directory_new("", NULL); music_root = directory_new("", NULL);
while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ; while (!(fp = fopen(database_path, "r")) && errno == EINTR) ;
if (fp == NULL) { if (fp == NULL) {
g_warning("unable to open db file \"%s\": %s", g_warning("unable to open db file \"%s\": %s",
dbFile, strerror(errno)); database_path, strerror(errno));
return false; return false;
} }
...@@ -289,16 +312,14 @@ db_load(void) ...@@ -289,16 +312,14 @@ db_load(void)
foundFsCharset = true; foundFsCharset = true;
fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]); fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]);
tempCharset = config_get_string(CONF_FS_CHARSET, NULL); tempCharset = path_get_fs_charset();
if (tempCharset != NULL if (tempCharset != NULL
&& strcmp(fsCharset, tempCharset)) { && strcmp(fsCharset, tempCharset)) {
g_message("Using \"%s\" for the " g_message("Existing database has charset \"%s\" "
"filesystem charset "
"instead of \"%s\"; " "instead of \"%s\"; "
"maybe you need to " "discarding database file",
"recreate the db?", fsCharset, tempCharset);
fsCharset, tempCharset); return false;
path_set_fs_charset(fsCharset);
} }
} else } else
g_error("unknown line in db info: %s", g_error("unknown line in db info: %s",
...@@ -312,7 +333,7 @@ db_load(void) ...@@ -312,7 +333,7 @@ db_load(void)
stats_update(); stats_update();
if (stat(dbFile, &st) == 0) if (stat(database_path, &st) == 0)
directory_dbModTime = st.st_mtime; directory_dbModTime = st.st_mtime;
return true; return true;
......
...@@ -27,9 +27,11 @@ struct directory; ...@@ -27,9 +27,11 @@ struct directory;
/** /**
* Initialize the database library. * Initialize the database library.
*
* @param path the absolute path of the database file
*/ */
void void
db_init(void); db_init(const char *path);
void void
db_finish(void); db_finish(void);
...@@ -37,13 +39,13 @@ db_finish(void); ...@@ -37,13 +39,13 @@ db_finish(void);
/** /**
* Clear the database. * Clear the database.
*/ */
static inline void void
db_clear(void) db_clear(void);
{
db_finish();
db_init();
}
/**
* Returns the root directory object. Returns NULL if there is no
* configured music directory.
*/
struct directory * struct directory *
db_get_root(void); db_get_root(void);
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
*/ */
#include "dbUtils.h" #include "dbUtils.h"
#include "locate.h"
#include "directory.h" #include "directory.h"
#include "database.h" #include "database.h"
#include "client.h" #include "client.h"
...@@ -35,32 +35,16 @@ ...@@ -35,32 +35,16 @@
typedef struct _ListCommandItem { typedef struct _ListCommandItem {
int8_t tagType; int8_t tagType;
int numConditionals; const struct locate_item_list *criteria;
LocateTagItem *conditionals;
} ListCommandItem; } ListCommandItem;
typedef struct _LocateTagItemArray {
int numItems;
LocateTagItem *items;
} LocateTagItemArray;
typedef struct _SearchStats { typedef struct _SearchStats {
LocateTagItemArray locateArray; const struct locate_item_list *criteria;
int numberOfSongs; int numberOfSongs;
unsigned long playTime; unsigned long playTime;
} SearchStats; } SearchStats;
static int static int
countSongsInDirectory(struct directory *directory, void *data)
{
int *count = (int *)data;
*count += directory->songs.nr;
return 0;
}
static int
printDirectoryInDirectory(struct directory *directory, void *data) printDirectoryInDirectory(struct directory *directory, void *data)
{ {
struct client *client = data; struct client *client = data;
...@@ -81,47 +65,35 @@ printSongInDirectory(struct song *song, G_GNUC_UNUSED void *data) ...@@ -81,47 +65,35 @@ printSongInDirectory(struct song *song, G_GNUC_UNUSED void *data)
struct search_data { struct search_data {
struct client *client; struct client *client;
LocateTagItemArray array; const struct locate_item_list *criteria;
}; };
static int static int
searchInDirectory(struct song *song, void *_data) searchInDirectory(struct song *song, void *_data)
{ {
struct search_data *data = _data; struct search_data *data = _data;
LocateTagItemArray *array = &data->array;
if (strstrSearchTags(song, array->numItems, array->items)) if (locate_song_search(song, data->criteria))
return song_print_info(data->client, song); return song_print_info(data->client, song);
return 0; return 0;
} }
int searchForSongsIn(struct client *client, const char *name, int
int numItems, LocateTagItem * items) searchForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria)
{ {
int ret; int ret;
int i; struct locate_item_list *new_list
= locate_item_list_casefold(criteria);
char **originalNeedles = g_new(char *, numItems);
struct search_data data; struct search_data data;
for (i = 0; i < numItems; i++) {
originalNeedles[i] = items[i].needle;
items[i].needle = g_utf8_casefold(originalNeedles[i], -1);
}
data.client = client; data.client = client;
data.array.numItems = numItems; data.criteria = new_list;
data.array.items = items;
ret = db_walk(name, searchInDirectory, NULL, &data); ret = db_walk(name, searchInDirectory, NULL, &data);
for (i = 0; i < numItems; i++) { locate_item_list_free(new_list);
g_free(items[i].needle);
items[i].needle = originalNeedles[i];
}
free(originalNeedles);
return ret; return ret;
} }
...@@ -130,22 +102,21 @@ static int ...@@ -130,22 +102,21 @@ static int
findInDirectory(struct song *song, void *_data) findInDirectory(struct song *song, void *_data)
{ {
struct search_data *data = _data; struct search_data *data = _data;
LocateTagItemArray *array = &data->array;
if (tagItemsFoundAndMatches(song, array->numItems, array->items)) if (locate_song_match(song, data->criteria))
return song_print_info(data->client, song); return song_print_info(data->client, song);
return 0; return 0;
} }
int findSongsIn(struct client *client, const char *name, int
int numItems, LocateTagItem * items) findSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria)
{ {
struct search_data data; struct search_data data;
data.client = client; data.client = client;
data.array.numItems = numItems; data.criteria = criteria;
data.array.items = items;
return db_walk(name, findInDirectory, NULL, &data); return db_walk(name, findInDirectory, NULL, &data);
} }
...@@ -161,8 +132,7 @@ searchStatsInDirectory(struct song *song, void *data) ...@@ -161,8 +132,7 @@ searchStatsInDirectory(struct song *song, void *data)
{ {
SearchStats *stats = data; SearchStats *stats = data;
if (tagItemsFoundAndMatches(song, stats->locateArray.numItems, if (locate_song_match(song, stats->criteria)) {
stats->locateArray.items)) {
stats->numberOfSongs++; stats->numberOfSongs++;
if (song->tag->time > 0) if (song->tag->time > 0)
stats->playTime += song->tag->time; stats->playTime += song->tag->time;
...@@ -171,14 +141,14 @@ searchStatsInDirectory(struct song *song, void *data) ...@@ -171,14 +141,14 @@ searchStatsInDirectory(struct song *song, void *data)
return 0; return 0;
} }
int searchStatsForSongsIn(struct client *client, const char *name, int
int numItems, LocateTagItem * items) searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria)
{ {
SearchStats stats; SearchStats stats;
int ret; int ret;
stats.locateArray.numItems = numItems; stats.criteria = criteria;
stats.locateArray.items = items;
stats.numberOfSongs = 0; stats.numberOfSongs = 0;
stats.playTime = 0; stats.playTime = 0;
...@@ -198,7 +168,7 @@ int printAllIn(struct client *client, const char *name) ...@@ -198,7 +168,7 @@ int printAllIn(struct client *client, const char *name)
static int static int
directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data) directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data)
{ {
return addSongToPlaylist(song, NULL); return addSongToPlaylist(&g_playlist, song, NULL);
} }
struct add_data { struct add_data {
...@@ -237,58 +207,26 @@ directoryPrintSongInfo(struct song *song, void *data) ...@@ -237,58 +207,26 @@ directoryPrintSongInfo(struct song *song, void *data)
return 0; return 0;
} }
static int
sumSongTime(struct song *song, void *data)
{
unsigned long *sum_time = (unsigned long *)data;
if (song->tag && song->tag->time >= 0)
*sum_time += song->tag->time;
return 0;
}
int printInfoForAllIn(struct client *client, const char *name) int printInfoForAllIn(struct client *client, const char *name)
{ {
return db_walk(name, directoryPrintSongInfo, return db_walk(name, directoryPrintSongInfo,
printDirectoryInDirectory, client); printDirectoryInDirectory, client);
} }
int countSongsIn(const char *name) static ListCommandItem *
{ newListCommandItem(int tagType, const struct locate_item_list *criteria)
int count = 0;
void *ptr = (void *)&count;
db_walk(name, NULL, countSongsInDirectory, ptr);
return count;
}
unsigned long sumSongTimesIn(const char *name)
{
unsigned long dbPlayTime = 0;
void *ptr = (void *)&dbPlayTime;
db_walk(name, sumSongTime, NULL, ptr);
return dbPlayTime;
}
static ListCommandItem *newListCommandItem(int tagType, int numConditionals,
LocateTagItem * conditionals)
{ {
ListCommandItem *item = g_new(ListCommandItem, 1); ListCommandItem *item = g_new(ListCommandItem, 1);
item->tagType = tagType; item->tagType = tagType;
item->numConditionals = numConditionals; item->criteria = criteria;
item->conditionals = conditionals;
return item; return item;
} }
static void freeListCommandItem(ListCommandItem * item) static void freeListCommandItem(ListCommandItem * item)
{ {
free(item); g_free(item);
} }
static void static void
...@@ -327,20 +265,17 @@ listUniqueTagsInDirectory(struct song *song, void *_data) ...@@ -327,20 +265,17 @@ listUniqueTagsInDirectory(struct song *song, void *_data)
struct list_tags_data *data = _data; struct list_tags_data *data = _data;
ListCommandItem *item = data->item; ListCommandItem *item = data->item;
if (tagItemsFoundAndMatches(song, item->numConditionals, if (locate_song_match(song, item->criteria))
item->conditionals)) {
visitTag(data->client, data->set, song, item->tagType); visitTag(data->client, data->set, song, item->tagType);
}
return 0; return 0;
} }
int listAllUniqueTags(struct client *client, int type, int numConditionals, int listAllUniqueTags(struct client *client, int type,
LocateTagItem * conditionals) const struct locate_item_list *criteria)
{ {
int ret; int ret;
ListCommandItem *item = newListCommandItem(type, numConditionals, ListCommandItem *item = newListCommandItem(type, criteria);
conditionals);
struct list_tags_data data = { struct list_tags_data data = {
.client = client, .client = client,
.item = item, .item = item,
......
...@@ -19,9 +19,8 @@ ...@@ -19,9 +19,8 @@
#ifndef MPD_DB_UTILS_H #ifndef MPD_DB_UTILS_H
#define MPD_DB_UTILS_H #define MPD_DB_UTILS_H
#include "locate.h"
struct client; struct client;
struct locate_item_list;
int printAllIn(struct client *client, const char *name); int printAllIn(struct client *client, const char *name);
...@@ -31,21 +30,23 @@ int addAllInToStoredPlaylist(const char *name, const char *utf8file); ...@@ -31,21 +30,23 @@ int addAllInToStoredPlaylist(const char *name, const char *utf8file);
int printInfoForAllIn(struct client *client, const char *name); int printInfoForAllIn(struct client *client, const char *name);
int searchForSongsIn(struct client *client, const char *name, int
int numItems, LocateTagItem * items); searchForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria);
int findSongsIn(struct client *client, const char *name,
int numItems, LocateTagItem * items);
int searchStatsForSongsIn(struct client *client, const char *name, int
int numItems, LocateTagItem * items); findSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria);
int countSongsIn(const char *name); int
searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria);
unsigned long sumSongTimesIn(const char *name); unsigned long sumSongTimesIn(const char *name);
int listAllUniqueTags(struct client *client, int type, int numConditiionals, int
LocateTagItem * conditionals); listAllUniqueTags(struct client *client, int type,
const struct locate_item_list *criteria);
void printSavedMemoryFromFilenames(void); void printSavedMemoryFromFilenames(void);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define MPD_FLAC_COMMON_H #define MPD_FLAC_COMMON_H
#include "../decoder_api.h" #include "../decoder_api.h"
#include "config.h"
#include <glib.h> #include <glib.h>
......
...@@ -431,7 +431,7 @@ aac_stream_decode(struct decoder *mpd_decoder, struct input_stream *inStream) ...@@ -431,7 +431,7 @@ aac_stream_decode(struct decoder *mpd_decoder, struct input_stream *inStream)
sampleBufferLen = sampleCount * 2; sampleBufferLen = sampleCount * 2;
cmd = decoder_data(mpd_decoder, NULL, sampleBuffer, cmd = decoder_data(mpd_decoder, inStream, sampleBuffer,
sampleBufferLen, file_time, sampleBufferLen, file_time,
bitRate, NULL); bitRate, NULL);
if (cmd == DECODE_COMMAND_SEEK) if (cmd == DECODE_COMMAND_SEEK)
......
...@@ -177,6 +177,9 @@ ffmpeg_helper(struct input_stream *input, ...@@ -177,6 +177,9 @@ ffmpeg_helper(struct input_stream *input,
} }
codec_context = format_context->streams[audio_stream]->codec; codec_context = format_context->streams[audio_stream]->codec;
if (codec_context->codec_name[0] != 0)
g_debug("codec '%s'", codec_context->codec_name);
codec = avcodec_find_decoder(codec_context->codec_id); codec = avcodec_find_decoder(codec_context->codec_id);
if (!codec) { if (!codec) {
...@@ -227,10 +230,6 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ...@@ -227,10 +230,6 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
&audio_size, &audio_size,
packet_data, packet_size); packet_data, packet_size);
position = av_rescale_q(packet->pts, *time_base,
(AVRational){1, 1});
if (len < 0) { if (len < 0) {
/* if error, we skip the frame */ /* if error, we skip the frame */
g_message("decoding failed\n"); g_message("decoding failed\n");
...@@ -240,10 +239,14 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ...@@ -240,10 +239,14 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
packet_data += len; packet_data += len;
packet_size -= len; packet_size -= len;
if (audio_size <= 0) { if (audio_size <= 0)
g_message("no audio frame\n");
continue; continue;
}
position = packet->pts != (int64_t)AV_NOPTS_VALUE
? av_rescale_q(packet->pts, *time_base,
(AVRational){1, 1})
: 0;
cmd = decoder_data(decoder, is, cmd = decoder_data(decoder, is,
audio_buf, audio_size, audio_buf, audio_size,
position, position,
...@@ -261,7 +264,7 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx) ...@@ -261,7 +264,7 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
AVPacket packet; AVPacket packet;
struct audio_format audio_format; struct audio_format audio_format;
enum decoder_command cmd; enum decoder_command cmd;
int current, total_time; int total_time;
total_time = 0; total_time = 0;
...@@ -303,9 +306,10 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx) ...@@ -303,9 +306,10 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
av_free_packet(&packet); av_free_packet(&packet);
if (cmd == DECODE_COMMAND_SEEK) { if (cmd == DECODE_COMMAND_SEEK) {
current = decoder_seek_where(decoder) * AV_TIME_BASE; int64_t where =
decoder_seek_where(decoder) * AV_TIME_BASE;
if (av_seek_frame(format_context, -1, current, 0) < 0) if (av_seek_frame(format_context, -1, where, 0) < 0)
decoder_seek_error(decoder); decoder_seek_error(decoder);
else else
decoder_command_finished(decoder); decoder_command_finished(decoder);
...@@ -347,6 +351,16 @@ static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx) ...@@ -347,6 +351,16 @@ static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
tag_add_item(tag, TAG_ITEM_TRACK, buffer); tag_add_item(tag, TAG_ITEM_TRACK, buffer);
} }
if (f->comment[0])
tag_add_item(tag, TAG_ITEM_COMMENT, f->comment);
if (f->genre[0])
tag_add_item(tag, TAG_ITEM_GENRE, f->genre);
if (f->year > 0) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%d", f->year);
tag_add_item(tag, TAG_ITEM_DATE, buffer);
}
return true; return true;
} }
...@@ -389,6 +403,7 @@ static const char *const ffmpeg_suffixes[] = { ...@@ -389,6 +403,7 @@ static const char *const ffmpeg_suffixes[] = {
"wma", "asf", "wmv", "mpeg", "mpg", "avi", "vob", "mov", "qt", "swf", "wma", "asf", "wmv", "mpeg", "mpg", "avi", "vob", "mov", "qt", "swf",
"rm", "swf", "mp1", "mp2", "mp3", "mp4", "m4a", "flac", "ogg", "wav", "rm", "swf", "mp1", "mp2", "mp3", "mp4", "m4a", "flac", "ogg", "wav",
"au", "aiff", "aif", "ac3", "aac", "mpc", "ape", "au", "aiff", "aif", "ac3", "aac", "mpc", "ape",
"tta",
NULL NULL
}; };
...@@ -405,6 +420,7 @@ static const char *const ffmpeg_mime_types[] = { ...@@ -405,6 +420,7 @@ static const char *const ffmpeg_mime_types[] = {
"application/x-ms-wmd", "application/x-ms-wmd",
"audio/mpeg", "audio/mpeg",
"audio/x-wav", "audio/x-wav",
"audio/x-tta",
NULL NULL
}; };
......
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../decoder_api.h"
#include "../timer.h"
#include "../conf.h"
#include <glib.h>
#include <fluidsynth.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "fluidsynth"
/**
* Convert a fluidsynth log level to a GLib log level.
*/
static GLogLevelFlags
fluidsynth_level_to_glib(enum fluid_log_level level)
{
switch (level) {
case FLUID_PANIC:
case FLUID_ERR:
return G_LOG_LEVEL_CRITICAL;
case FLUID_WARN:
return G_LOG_LEVEL_WARNING;
case FLUID_INFO:
return G_LOG_LEVEL_INFO;
case FLUID_DBG:
case LAST_LOG_LEVEL:
return G_LOG_LEVEL_DEBUG;
}
/* invalid fluidsynth log level */
return G_LOG_LEVEL_MESSAGE;
}
/**
* The fluidsynth logging callback. It forwards messages to the GLib
* logging library.
*/
static void
fluidsynth_mpd_log_function(int level, char *message, G_GNUC_UNUSED void *data)
{
g_log(G_LOG_DOMAIN, fluidsynth_level_to_glib(level), "%s", message);
}
static bool
fluidsynth_init(void)
{
fluid_set_log_function(LAST_LOG_LEVEL,
fluidsynth_mpd_log_function, NULL);
return true;
}
static void
fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
{
static const struct audio_format audio_format = {
.sample_rate = 48000,
.bits = 16,
.channels = 2,
};
char setting_sample_rate[] = "synth.sample-rate";
/*
char setting_verbose[] = "synth.verbose";
char setting_yes[] = "yes";
*/
const char *soundfont_path;
fluid_settings_t *settings;
fluid_synth_t *synth;
fluid_player_t *player;
char *path_dup;
int ret;
Timer *timer;
enum decoder_command cmd;
soundfont_path =
config_get_string("soundfont",
"/usr/share/sounds/sf2/FluidR3_GM.sf2");
/* set up fluid settings */
settings = new_fluid_settings();
if (settings == NULL)
return;
fluid_settings_setnum(settings, setting_sample_rate, 48000);
/*
fluid_settings_setstr(settings, setting_verbose, setting_yes);
*/
/* create the fluid synth */
synth = new_fluid_synth(settings);
if (synth == NULL) {
delete_fluid_settings(settings);
return;
}
ret = fluid_synth_sfload(synth, soundfont_path, true);
if (ret < 0) {
g_warning("fluid_synth_sfload() failed");
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
}
/* create the fluid player */
player = new_fluid_player(synth);
if (player == NULL) {
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
}
/* temporarily duplicate the path_fs string, because
fluidsynth wants a writable string */
path_dup = g_strdup(path_fs);
ret = fluid_player_add(player, path_dup);
g_free(path_dup);
if (ret != 0) {
g_warning("fluid_player_add() failed");
delete_fluid_player(player);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
}
/* start the player */
ret = fluid_player_play(player);
if (ret != 0) {
g_warning("fluid_player_play() failed");
delete_fluid_player(player);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
return;
}
/* set up a timer for synchronization; fluidsynth always
decodes in real time, which forces us to synchronize */
/* XXX is there any way to switch off real-time decoding? */
timer = timer_new(&audio_format);
timer_start(timer);
/* initialization complete - announce the audio format to the
MPD core */
decoder_initialized(decoder, &audio_format, false, -1);
do {
int16_t buffer[2048];
const unsigned max_frames = G_N_ELEMENTS(buffer) / 2;
/* synchronize with the fluid player */
timer_add(timer, sizeof(buffer));
timer_sync(timer);
/* read samples from fluidsynth and send them to the
MPD core */
ret = fluid_synth_write_s16(synth, max_frames,
buffer, 0, 2,
buffer, 1, 2);
/* XXX how do we see whether the player is done? We
can't access the private attribute
player->status */
if (ret != 0)
break;
cmd = decoder_data(decoder, NULL, buffer, sizeof(buffer),
0, 0, NULL);
} while (cmd == DECODE_COMMAND_NONE);
/* clean up */
timer_free(timer);
fluid_player_stop(player);
fluid_player_join(player);
delete_fluid_player(player);
delete_fluid_synth(synth);
delete_fluid_settings(settings);
}
static struct tag *
fluidsynth_tag_dup(const char *file)
{
struct tag *tag = tag_new();
/* to be implemented */
(void)file;
return tag;
}
static const char *const fluidsynth_suffixes[] = {
"mid",
NULL
};
const struct decoder_plugin fluidsynth_decoder_plugin = {
.name = "fluidsynth",
.init = fluidsynth_init,
.file_decode = fluidsynth_file_decode,
.tag_dup = fluidsynth_tag_dup,
.suffixes = fluidsynth_suffixes,
};
...@@ -152,7 +152,7 @@ static void mod_close(mod_Data * data) ...@@ -152,7 +152,7 @@ static void mod_close(mod_Data * data)
{ {
Player_Stop(); Player_Stop();
Player_Free(data->moduleHandle); Player_Free(data->moduleHandle);
free(data); g_free(data);
} }
static void static void
...@@ -167,7 +167,6 @@ mod_decode(struct decoder *decoder, const char *path) ...@@ -167,7 +167,6 @@ mod_decode(struct decoder *decoder, const char *path)
if (!(data = mod_open(path))) { if (!(data = mod_open(path))) {
g_warning("failed to open mod: %s\n", path); g_warning("failed to open mod: %s\n", path);
MikMod_Exit();
return; return;
} }
...@@ -190,8 +189,6 @@ mod_decode(struct decoder *decoder, const char *path) ...@@ -190,8 +189,6 @@ mod_decode(struct decoder *decoder, const char *path)
} }
mod_close(data); mod_close(data);
MikMod_Exit();
} }
static struct tag *modTagDup(const char *file) static struct tag *modTagDup(const char *file)
...@@ -207,7 +204,6 @@ static struct tag *modTagDup(const char *file) ...@@ -207,7 +204,6 @@ static struct tag *modTagDup(const char *file)
if (moduleHandle == NULL) { if (moduleHandle == NULL) {
g_debug("modTagDup: Failed to open file: %s\n", file); g_debug("modTagDup: Failed to open file: %s\n", file);
MikMod_Exit();
return NULL; return NULL;
} }
...@@ -223,8 +219,6 @@ static struct tag *modTagDup(const char *file) ...@@ -223,8 +219,6 @@ static struct tag *modTagDup(const char *file)
if (title) if (title)
tag_add_item(ret, TAG_ITEM_TITLE, title); tag_add_item(ret, TAG_ITEM_TITLE, title);
MikMod_Exit();
return ret; return ret;
} }
...@@ -247,8 +241,8 @@ static const char *const modSuffixes[] = { ...@@ -247,8 +241,8 @@ static const char *const modSuffixes[] = {
NULL NULL
}; };
const struct decoder_plugin modPlugin = { const struct decoder_plugin mikmod_decoder_plugin = {
.name = "mod", .name = "mikmod",
.init = mod_initMikMod, .init = mod_initMikMod,
.finish = mod_finishMikMod, .finish = mod_finishMikMod,
.file_decode = mod_decode, .file_decode = mod_decode,
......
...@@ -17,56 +17,68 @@ ...@@ -17,56 +17,68 @@
*/ */
#include "../decoder_api.h" #include "../decoder_api.h"
#include "../utils.h"
#include "../log.h"
#include <glib.h> #include <glib.h>
#include <modplug.h> #include <modplug.h>
#define MODPLUG_FRAME_SIZE (4096) #undef G_LOG_DOMAIN
#define MODPLUG_PREALLOC_BLOCK (256*1024) #define G_LOG_DOMAIN "modplug"
#define MODPLUG_READ_BLOCK (128*1024)
#define MODPLUG_FILE_LIMIT (1024*1024*100) enum {
MODPLUG_FRAME_SIZE = 4096,
MODPLUG_PREALLOC_BLOCK = 256 * 1024,
MODPLUG_READ_BLOCK = 128 * 1024,
MODPLUG_FILE_LIMIT = 100 * 1024 * 1024,
};
static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is) static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is)
{ {
unsigned char *data; unsigned char *data;
GByteArray *bdatas; GByteArray *bdatas;
int total_len; size_t ret;
int ret;
if (is->size == 0) {
g_warning("file is empty");
return NULL;
}
if (is->size > MODPLUG_FILE_LIMIT) {
g_warning("file too large");
return NULL;
}
//known/unknown size, preallocate array, lets read in chunks //known/unknown size, preallocate array, lets read in chunks
if (is->size) { if (is->size > 0) {
if (is->size > MODPLUG_FILE_LIMIT) {
g_warning("file too large\n");
return NULL;
}
bdatas = g_byte_array_sized_new(is->size); bdatas = g_byte_array_sized_new(is->size);
} else { } else {
bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK); bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK);
} }
data = g_malloc(MODPLUG_READ_BLOCK); data = g_malloc(MODPLUG_READ_BLOCK);
total_len = 0;
do { while (true) {
if (decoder) { ret = decoder_read(decoder, is, data, MODPLUG_READ_BLOCK);
ret = decoder_read(decoder, is, data, MODPLUG_READ_BLOCK); if (ret == 0) {
} else { if (input_stream_eof(is))
ret = input_stream_read(is, data, MODPLUG_READ_BLOCK); /* end of file */
} break;
if (ret > 0) {
g_byte_array_append(bdatas, data, ret); /* I/O error - skip this song */
total_len += ret; g_free(data);
} else { g_byte_array_free(bdatas, true);
//end of file, or read error return NULL;
break;
} }
if (total_len > MODPLUG_FILE_LIMIT) {
if (bdatas->len + ret > MODPLUG_FILE_LIMIT) {
g_warning("stream too large\n"); g_warning("stream too large\n");
g_free(data); g_free(data);
g_byte_array_free(bdatas, TRUE); g_byte_array_free(bdatas, TRUE);
return NULL; return NULL;
} }
} while (input_stream_eof(is));
g_byte_array_append(bdatas, data, ret);
}
g_free(data); g_free(data);
return bdatas; return bdatas;
...@@ -91,12 +103,7 @@ mod_decode(struct decoder *decoder, struct input_stream *is) ...@@ -91,12 +103,7 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
g_warning("could not load stream\n"); g_warning("could not load stream\n");
return; return;
} }
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
if (!f) {
g_warning("could not decode stream\n");
return;
}
ModPlug_GetSettings(&settings); ModPlug_GetSettings(&settings);
/* alter setting */ /* alter setting */
settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */ settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */
...@@ -106,6 +113,13 @@ mod_decode(struct decoder *decoder, struct input_stream *is) ...@@ -106,6 +113,13 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
/* insert more setting changes here */ /* insert more setting changes here */
ModPlug_SetSettings(&settings); ModPlug_SetSettings(&settings);
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
if (!f) {
g_warning("could not decode stream\n");
return;
}
audio_format.bits = 16; audio_format.bits = 16;
audio_format.sample_rate = 44100; audio_format.sample_rate = 44100;
audio_format.channels = 2; audio_format.channels = 2;
......
...@@ -179,7 +179,7 @@ mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream) ...@@ -179,7 +179,7 @@ mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track); file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
scale = mp4ff_time_scale(mp4fh, track); scale = mp4ff_time_scale(mp4fh, track);
g_free(mp4_buffer); free(mp4_buffer);
if (scale < 0) { if (scale < 0) {
g_warning("Error getting audio format of mp4 AAC track.\n"); g_warning("Error getting audio format of mp4 AAC track.\n");
...@@ -314,7 +314,7 @@ mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream) ...@@ -314,7 +314,7 @@ mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
file_time, bit_rate, NULL); file_time, bit_rate, NULL);
} }
free(seek_table); g_free(seek_table);
faacDecClose(decoder); faacDecClose(decoder);
mp4ff_close(mp4fh); mp4ff_close(mp4fh);
} }
......
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
extern "C" {
#include "../decoder_api.h"
}
#include <glib.h>
#include <sidplay/sidplay2.h>
#include <sidplay/builders/resid.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "sidplay"
static void
sidplay_file_decode(struct decoder *decoder, const char *path_fs)
{
int ret;
/* load the tune */
SidTune tune(path_fs, NULL, true);
if (!tune) {
g_warning("failed to load file");
return;
}
tune.selectSong(1);
/* initialize the player */
sidplay2 player;
int iret = player.load(&tune);
if (iret != 0) {
g_warning("sidplay2.load() failed: %s", player.error());
return;
}
/* initialize the builder */
ReSIDBuilder builder("ReSID");
if (!builder) {
g_warning("failed to initialize ReSIDBuilder");
return;
}
builder.create(player.info().maxsids);
if (!builder) {
g_warning("ReSIDBuilder.create() failed");
return;
}
builder.filter(false);
if (!builder) {
g_warning("ReSIDBuilder.filter() failed");
return;
}
/* configure the player */
sid2_config_t config = player.config();
config.clockDefault = SID2_CLOCK_PAL;
config.clockForced = true;
config.clockSpeed = SID2_CLOCK_CORRECT;
config.frequency = 48000;
config.optimisation = SID2_DEFAULT_OPTIMISATION;
config.playback = sid2_stereo;
config.precision = 16;
config.sidDefault = SID2_MOS6581;
config.sidEmulation = &builder;
config.sidModel = SID2_MODEL_CORRECT;
config.sidSamples = true;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
config.sampleFormat = SID2_LITTLE_SIGNED;
#else
config.sampleFormat = SID2_BIG_SIGNED;
#endif
iret = player.config(config);
if (iret != 0) {
g_warning("sidplay2.config() failed: %s", player.error());
return;
}
/* initialize the MPD decoder */
struct audio_format audio_format;
audio_format.sample_rate = 48000;
audio_format.bits = 16;
audio_format.channels = 2;
decoder_initialized(decoder, &audio_format, false, -1);
/* .. and play */
enum decoder_command cmd;
do {
char buffer[4096];
size_t nbytes;
nbytes = player.play(buffer, sizeof(buffer));
if (nbytes == 0)
break;
cmd = decoder_data(decoder, NULL, buffer, nbytes,
0, 0, NULL);
} while (cmd == DECODE_COMMAND_NONE);
}
static struct tag *
sidplay_tag_dup(const char *path_fs)
{
SidTune tune(path_fs, NULL, true);
if (!tune)
return NULL;
const SidTuneInfo &info = tune.getInfo();
struct tag *tag = tag_new();
if (info.numberOfInfoStrings > 0 && info.infoString[0] != NULL)
tag_add_item(tag, TAG_ITEM_TITLE, info.infoString[0]);
if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL)
tag_add_item(tag, TAG_ITEM_ARTIST, info.infoString[1]);
return tag;
}
static const char *const sidplay_suffixes[] = {
"sid",
NULL
};
extern const struct decoder_plugin sidplay_decoder_plugin;
const struct decoder_plugin sidplay_decoder_plugin = {
"sidplay",
NULL, /* init() */
NULL, /* finish() */
NULL, /* stream_decode() */
sidplay_file_decode,
sidplay_tag_dup,
sidplay_suffixes,
NULL, /* mime_types */
};
...@@ -522,7 +522,9 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is) ...@@ -522,7 +522,9 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
wavpack_input_init(&isp, decoder, is); wavpack_input_init(&isp, decoder, is);
wpc = WavpackOpenFileInputEx( wpc = WavpackOpenFileInputEx(
&mpd_is_reader, &isp, &isp_wvc, error, open_flags, 23 &mpd_is_reader, &isp,
open_flags & OPEN_WVC ? &isp_wvc : NULL,
error, open_flags, 23
); );
if (wpc == NULL) { if (wpc == NULL) {
......
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../decoder_api.h"
#include <glib.h>
#include <wildmidi_lib.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "wildmidi"
enum {
WILDMIDI_SAMPLE_RATE = 48000,
};
static bool
wildmidi_init(void)
{
int ret;
ret = WildMidi_Init("/etc/timidity/timidity.cfg",
WILDMIDI_SAMPLE_RATE, 0);
return ret == 0;
}
static void
wildmidi_finish(void)
{
WildMidi_Shutdown();
}
static void
wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
{
static const struct audio_format audio_format = {
.sample_rate = WILDMIDI_SAMPLE_RATE,
.bits = 16,
.channels = 2,
};
midi *wm;
const struct _WM_Info *info;
enum decoder_command cmd;
wm = WildMidi_Open(path_fs);
if (wm == NULL)
return;
info = WildMidi_GetInfo(wm);
if (info == NULL) {
WildMidi_Close(wm);
return;
}
decoder_initialized(decoder, &audio_format, true,
info->approx_total_samples / WILDMIDI_SAMPLE_RATE);
do {
char buffer[4096];
int len;
info = WildMidi_GetInfo(wm);
if (info == NULL)
break;
len = WildMidi_GetOutput(wm, buffer, sizeof(buffer));
if (len <= 0)
break;
cmd = decoder_data(decoder, NULL, buffer, len,
(float)info->current_sample /
(float)WILDMIDI_SAMPLE_RATE,
0, NULL);
if (cmd == DECODE_COMMAND_SEEK) {
unsigned long seek_where = WILDMIDI_SAMPLE_RATE *
decoder_seek_where(decoder);
WildMidi_SampledSeek(wm, &seek_where);
decoder_command_finished(decoder);
cmd = DECODE_COMMAND_NONE;
}
} while (cmd == DECODE_COMMAND_NONE);
WildMidi_Close(wm);
}
static struct tag *
wildmidi_tag_dup(const char *path_fs)
{
midi *wm;
const struct _WM_Info *info;
struct tag *tag;
wm = WildMidi_Open(path_fs);
if (wm == NULL)
return NULL;
info = WildMidi_GetInfo(wm);
if (info == NULL) {
WildMidi_Close(wm);
return NULL;
}
tag = tag_new();
tag->time = info->approx_total_samples / WILDMIDI_SAMPLE_RATE;
WildMidi_Close(wm);
return tag;
}
static const char *const wildmidi_suffixes[] = {
"mid",
NULL
};
const struct decoder_plugin wildmidi_decoder_plugin = {
.name = "wildmidi",
.init = wildmidi_init,
.finish = wildmidi_finish,
.file_decode = wildmidi_file_decode,
.tag_dup = wildmidi_tag_dup,
.suffixes = wildmidi_suffixes,
};
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
#include "replay_gain.h" #include "replay_gain.h"
#include "tag.h" #include "tag.h"
#include "audio_format.h" #include "audio_format.h"
#include "playerData.h"
#include <stdbool.h> #include <stdbool.h>
......
...@@ -102,8 +102,13 @@ dc_seek(struct notify *notify, double where) ...@@ -102,8 +102,13 @@ dc_seek(struct notify *notify, double where)
} }
void void
dc_quit(struct notify *notify) dc_quit(void)
{ {
assert(dc.thread != NULL);
dc.quit = true; dc.quit = true;
dc_command(notify, DECODE_COMMAND_STOP); dc_command_async(DECODE_COMMAND_STOP);
g_thread_join(dc.thread);
dc.thread = NULL;
} }
...@@ -45,6 +45,10 @@ enum decoder_state { ...@@ -45,6 +45,10 @@ enum decoder_state {
}; };
struct decoder_control { struct decoder_control {
/** the handle of the decoder thread, or NULL if the decoder
thread isn't running */
GThread *thread;
struct notify notify; struct notify notify;
volatile enum decoder_state state; volatile enum decoder_state state;
...@@ -124,6 +128,6 @@ bool ...@@ -124,6 +128,6 @@ bool
dc_seek(struct notify *notify, double where); dc_seek(struct notify *notify, double where);
void void
dc_quit(struct notify *notify); dc_quit(void);
#endif #endif
...@@ -33,7 +33,10 @@ extern const struct decoder_plugin aacPlugin; ...@@ -33,7 +33,10 @@ extern const struct decoder_plugin aacPlugin;
extern const struct decoder_plugin mpcPlugin; extern const struct decoder_plugin mpcPlugin;
extern const struct decoder_plugin wavpack_plugin; extern const struct decoder_plugin wavpack_plugin;
extern const struct decoder_plugin modplug_plugin; extern const struct decoder_plugin modplug_plugin;
extern const struct decoder_plugin modPlugin; extern const struct decoder_plugin mikmod_decoder_plugin;
extern const struct decoder_plugin sidplay_decoder_plugin;
extern const struct decoder_plugin fluidsynth_decoder_plugin;
extern const struct decoder_plugin wildmidi_decoder_plugin;
extern const struct decoder_plugin ffmpeg_plugin; extern const struct decoder_plugin ffmpeg_plugin;
static const struct decoder_plugin *const decoder_plugins[] = { static const struct decoder_plugin *const decoder_plugins[] = {
...@@ -68,7 +71,16 @@ static const struct decoder_plugin *const decoder_plugins[] = { ...@@ -68,7 +71,16 @@ static const struct decoder_plugin *const decoder_plugins[] = {
&modplug_plugin, &modplug_plugin,
#endif #endif
#ifdef HAVE_MIKMOD #ifdef HAVE_MIKMOD
&modPlugin, &mikmod_decoder_plugin,
#endif
#ifdef ENABLE_SIDPLAY
&sidplay_decoder_plugin,
#endif
#ifdef ENABLE_FLUIDSYNTH
&fluidsynth_decoder_plugin,
#endif
#ifdef ENABLE_WILDMIDI
&wildmidi_decoder_plugin,
#endif #endif
#ifdef HAVE_FFMPEG #ifdef HAVE_FFMPEG
&ffmpeg_plugin, &ffmpeg_plugin,
......
...@@ -258,8 +258,10 @@ static gpointer decoder_task(G_GNUC_UNUSED gpointer arg) ...@@ -258,8 +258,10 @@ static gpointer decoder_task(G_GNUC_UNUSED gpointer arg)
void decoder_thread_start(void) void decoder_thread_start(void)
{ {
GError *e = NULL; GError *e = NULL;
GThread *t;
if (!(t = g_thread_create(decoder_task, NULL, FALSE, &e))) assert(dc.thread == NULL);
dc.thread = g_thread_create(decoder_task, NULL, true, &e);
if (dc.thread == NULL)
FATAL("Failed to spawn decoder task: %s\n", e->message); FATAL("Failed to spawn decoder task: %s\n", e->message);
} }
...@@ -54,7 +54,7 @@ directory_free(struct directory *directory) ...@@ -54,7 +54,7 @@ directory_free(struct directory *directory)
dirvec_destroy(&directory->children); dirvec_destroy(&directory->children);
songvec_destroy(&directory->songs); songvec_destroy(&directory->songs);
free(directory); g_free(directory);
/* this resets last dir returned */ /* this resets last dir returned */
/*directory_get_path(NULL); */ /*directory_get_path(NULL); */
} }
...@@ -108,7 +108,7 @@ directory_get_directory(struct directory *directory, const char *name) ...@@ -108,7 +108,7 @@ directory_get_directory(struct directory *directory, const char *name)
locate = strchr(locate + 1, '/'); locate = strchr(locate + 1, '/');
} }
free(duplicated); g_free(duplicated);
return found; return found;
} }
......
...@@ -36,6 +36,9 @@ enum pipe_event { ...@@ -36,6 +36,9 @@ enum pipe_event {
/** must call syncPlayerAndPlaylist() */ /** must call syncPlayerAndPlaylist() */
PIPE_EVENT_PLAYLIST, PIPE_EVENT_PLAYLIST,
/** the current song's tag has changed */
PIPE_EVENT_TAG,
/** SIGHUP received: reload configuration, roll log file */ /** SIGHUP received: reload configuration, roll log file */
PIPE_EVENT_RELOAD, PIPE_EVENT_RELOAD,
......
...@@ -38,7 +38,7 @@ static const char *const idle_names[] = { ...@@ -38,7 +38,7 @@ static const char *const idle_names[] = {
"mixer", "mixer",
"output", "output",
"options", "options",
"elapsed", "sticker",
NULL NULL
}; };
......
...@@ -46,6 +46,9 @@ enum { ...@@ -46,6 +46,9 @@ enum {
/** options have changed: crossfade, random, repeat, ... */ /** options have changed: crossfade, random, repeat, ... */
IDLE_OPTIONS = 0x40, IDLE_OPTIONS = 0x40,
/** a sticker has been modified. */
IDLE_STICKER = 0x80,
}; };
/** /**
......
...@@ -16,29 +16,13 @@ ...@@ -16,29 +16,13 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include "input_archive.h"
#include "archive_api.h" #include "archive_api.h"
#include "archive_list.h" #include "archive_list.h"
#include "input_archive.h"
#include "input_stream.h" #include "input_stream.h"
#include "gcc.h"
#include "log.h"
#include "ls.h"
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <glib.h> #include <glib.h>
typedef struct {
const struct archive_plugin *aplugin;
const struct input_plugin *iplugin;
struct archive_file *file;
} archive_context;
/** /**
* select correct archive plugin to handle the input stream * select correct archive plugin to handle the input stream
* may allow stacking of archive plugins. for example for handling * may allow stacking of archive plugins. for example for handling
...@@ -50,8 +34,8 @@ typedef struct { ...@@ -50,8 +34,8 @@ typedef struct {
static bool static bool
input_archive_open(struct input_stream *is, const char *pathname) input_archive_open(struct input_stream *is, const char *pathname)
{ {
archive_context *arch_ctx;
const struct archive_plugin *arplug; const struct archive_plugin *arplug;
struct archive_file *file;
char *archive, *filename, *suffix, *pname; char *archive, *filename, *suffix, *pname;
bool opened; bool opened;
...@@ -74,23 +58,10 @@ input_archive_open(struct input_stream *is, const char *pathname) ...@@ -74,23 +58,10 @@ input_archive_open(struct input_stream *is, const char *pathname)
return false; return false;
} }
arch_ctx = (archive_context *) g_malloc(sizeof(archive_context)); file = arplug->open(archive);
//setup archive plugin pointer
arch_ctx->aplugin = arplug;
//open archive file
arch_ctx->file = arplug->open(archive);
//setup fileops //setup fileops
arplug->setup_stream(arch_ctx->file, is); opened = arplug->open_stream(file, is, filename);
//setup input plugin backup
arch_ctx->iplugin = is->plugin;
is->plugin = &input_plugin_archive;
//internal handle
is->data = arch_ctx;
//open archive
opened = arch_ctx->iplugin->open(is, filename);
if (!opened) { if (!opened) {
g_warning("open inarchive file %s failed\n\n",filename); g_warning("open inarchive file %s failed\n\n",filename);
...@@ -101,53 +72,6 @@ input_archive_open(struct input_stream *is, const char *pathname) ...@@ -101,53 +72,6 @@ input_archive_open(struct input_stream *is, const char *pathname)
return opened; return opened;
} }
static void
input_archive_close(struct input_stream *is)
{
archive_context *arch_ctx = (archive_context *)is->data;
//close archive infile ops
arch_ctx->iplugin->close(is);
//close archive
arch_ctx->aplugin->close(arch_ctx->file);
//free private data
g_free(arch_ctx);
}
static bool
input_archive_seek(struct input_stream *is, off_t offset, int whence)
{
archive_context *arch_ctx = (archive_context *)is->data;
return arch_ctx->iplugin->seek(is, offset, whence);
}
static size_t
input_archive_read(struct input_stream *is, void *ptr, size_t size)
{
archive_context *arch_ctx = (archive_context *)is->data;
assert(ptr != NULL);
assert(size > 0);
return arch_ctx->iplugin->read(is, ptr, size);
}
static bool
input_archive_eof(struct input_stream *is)
{
archive_context *arch_ctx = (archive_context *)is->data;
return arch_ctx->iplugin->eof(is);
}
static int
input_archive_buffer(struct input_stream *is)
{
archive_context *arch_ctx = (archive_context *)is->data;
return arch_ctx->iplugin->buffer(is);
}
const struct input_plugin input_plugin_archive = { const struct input_plugin input_plugin_archive = {
.open = input_archive_open, .open = input_archive_open,
.close = input_archive_close,
.buffer = input_archive_buffer,
.read = input_archive_read,
.eof = input_archive_eof,
.seek = input_archive_seek,
}; };
...@@ -630,10 +630,10 @@ input_curl_easy_init(struct input_stream *is) ...@@ -630,10 +630,10 @@ input_curl_easy_init(struct input_stream *is)
struct input_curl *c = is->data; struct input_curl *c = is->data;
CURLcode code; CURLcode code;
CURLMcode mcode; CURLMcode mcode;
struct config_param *proxy_host; const char *proxy_host;
struct config_param *proxy_port; const char *proxy_port;
struct config_param *proxy_user; const char *proxy_user;
struct config_param *proxy_pass; const char *proxy_pass;
c->eof = false; c->eof = false;
...@@ -661,27 +661,28 @@ input_curl_easy_init(struct input_stream *is) ...@@ -661,27 +661,28 @@ input_curl_easy_init(struct input_stream *is)
curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true); curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error); curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
proxy_host = config_get_param(CONF_HTTP_PROXY_HOST); proxy_host = config_get_string(CONF_HTTP_PROXY_HOST, NULL);
proxy_port = config_get_param(CONF_HTTP_PROXY_PORT); proxy_port = config_get_string(CONF_HTTP_PROXY_PORT, NULL);
proxy_user = config_get_param(CONF_HTTP_PROXY_USER);
proxy_pass = config_get_param(CONF_HTTP_PROXY_PASSWORD);
if (proxy_host != NULL) { if (proxy_host != NULL) {
char *proxy_host_str; char *proxy_host_str;
if (proxy_port == NULL) { if (proxy_port == NULL) {
proxy_host_str = g_strdup(proxy_host->value); proxy_host_str = g_strdup(proxy_host);
} else { } else {
proxy_host_str = proxy_host_str =
g_strconcat(proxy_host->value, ":", proxy_port->value, NULL); g_strconcat(proxy_host, ":", proxy_port, NULL);
} }
curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy_host_str); curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy_host_str);
g_free(proxy_host_str); g_free(proxy_host_str);
} }
proxy_user = config_get_string(CONF_HTTP_PROXY_USER, NULL);
proxy_pass = config_get_string(CONF_HTTP_PROXY_PASSWORD, NULL);
if ((proxy_user != NULL) && (proxy_pass != NULL)) { if ((proxy_user != NULL) && (proxy_pass != NULL)) {
char *proxy_auth_str = char *proxy_auth_str =
g_strconcat(proxy_user->value, ":", proxy_pass->value, NULL); g_strconcat(proxy_user, ":", proxy_pass, NULL);
curl_easy_setopt(c->easy, CURLOPT_PROXYUSERPWD, proxy_auth_str); curl_easy_setopt(c->easy, CURLOPT_PROXYUSERPWD, proxy_auth_str);
g_free(proxy_auth_str); g_free(proxy_auth_str);
} }
...@@ -908,6 +909,7 @@ input_curl_open(struct input_stream *is, const char *url) ...@@ -908,6 +909,7 @@ input_curl_open(struct input_stream *is, const char *url)
c->buffers = g_queue_new(); c->buffers = g_queue_new();
c->rewind = g_queue_new(); c->rewind = g_queue_new();
is->plugin = &input_plugin_curl;
is->data = c; is->data = c;
c->multi = curl_multi_init(); c->multi = curl_multi_init();
......
...@@ -62,6 +62,7 @@ input_file_open(struct input_stream *is, const char *filename) ...@@ -62,6 +62,7 @@ input_file_open(struct input_stream *is, const char *filename)
posix_fadvise(fd, (off_t)0, is->size, POSIX_FADV_SEQUENTIAL); posix_fadvise(fd, (off_t)0, is->size, POSIX_FADV_SEQUENTIAL);
#endif #endif
is->plugin = &input_plugin_file;
is->data = GINT_TO_POINTER(fd); is->data = GINT_TO_POINTER(fd);
is->ready = true; is->ready = true;
...@@ -115,16 +116,9 @@ input_file_eof(struct input_stream *is) ...@@ -115,16 +116,9 @@ input_file_eof(struct input_stream *is)
return is->offset >= is->size; return is->offset >= is->size;
} }
static int
input_file_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
const struct input_plugin input_plugin_file = { const struct input_plugin input_plugin_file = {
.open = input_file_open, .open = input_file_open,
.close = input_file_close, .close = input_file_close,
.buffer = input_file_buffer,
.read = input_file_read, .read = input_file_read,
.eof = input_file_eof, .eof = input_file_eof,
.seek = input_file_seek, .seek = input_file_seek,
......
/*
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "input_mms.h"
#include "input_stream.h"
#include <glib.h>
#include <libmms/mmsx.h>
#include <string.h>
#include <errno.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "jack"
struct input_mms {
mmsx_t *mms;
bool eof;
};
static bool
input_mms_open(struct input_stream *is, const char *url)
{
struct input_mms *m;
if (!g_str_has_prefix(url, "mms://") &&
!g_str_has_prefix(url, "mmsh://") &&
!g_str_has_prefix(url, "mmst://") &&
!g_str_has_prefix(url, "mmsu://"))
return false;
m = g_new(struct input_mms, 1);
m->mms = mmsx_connect(NULL, NULL, url, 128 * 1024);
if (m->mms == NULL) {
g_warning("mmsx_connect() failed");
return false;
}
/* XX is this correct? at least this selects the ffmpeg
decoder, which seems to work fine*/
is->mime = g_strdup("audio/x-ms-wma");
is->data = m;
is->ready = true;
return true;
}
static size_t
input_mms_read(struct input_stream *is, void *ptr, size_t size)
{
struct input_mms *m = is->data;
int ret;
ret = mmsx_read(NULL, m->mms, ptr, size);
if (ret <= 0) {
if (ret < 0) {
is->error = errno;
g_warning("mmsx_read() failed: %s", g_strerror(errno));
}
m->eof = true;
return false;
}
is->offset += ret;
return (size_t)ret;
}
static void
input_mms_close(struct input_stream *is)
{
struct input_mms *m = is->data;
mmsx_close(m->mms);
g_free(m);
}
static bool
input_mms_eof(struct input_stream *is)
{
struct input_mms *m = is->data;
return m->eof;
}
static int
input_mms_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
static bool
input_mms_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
const struct input_plugin input_plugin_mms = {
.open = input_mms_open,
.close = input_mms_close,
.buffer = input_mms_buffer,
.read = input_mms_read,
.eof = input_mms_eof,
.seek = input_mms_seek,
};
/* the Music Player Daemon (MPD) /*
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) * Copyright (C) 2003-2009 The Music Player Daemon Project
* This project's homepage is: http://www.musicpd.org * http://www.musicpd.org
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -16,12 +16,9 @@ ...@@ -16,12 +16,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifndef MPD_PLAYER_DATA_H #ifndef INPUT_MMS_H
#define MPD_PLAYER_DATA_H #define INPUT_MMS_H
extern unsigned int buffered_chunks; extern const struct input_plugin input_plugin_mms;
extern unsigned int buffered_before_play;
void initPlayerData(void);
#endif #endif
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
#include "input_curl.h" #include "input_curl.h"
#endif #endif
#ifdef ENABLE_MMS
#include "input_mms.h"
#endif
#include <glib.h> #include <glib.h>
#include <assert.h> #include <assert.h>
...@@ -40,6 +44,9 @@ static const struct input_plugin *const input_plugins[] = { ...@@ -40,6 +44,9 @@ static const struct input_plugin *const input_plugins[] = {
#ifdef HAVE_CURL #ifdef HAVE_CURL
&input_plugin_curl, &input_plugin_curl,
#endif #endif
#ifdef ENABLE_MMS
&input_plugin_mms,
#endif
}; };
static const unsigned num_input_plugins = static const unsigned num_input_plugins =
...@@ -73,7 +80,14 @@ input_stream_open(struct input_stream *is, const char *url) ...@@ -73,7 +80,14 @@ input_stream_open(struct input_stream *is, const char *url)
const struct input_plugin *plugin = input_plugins[i]; const struct input_plugin *plugin = input_plugins[i];
if (plugin->open(is, url)) { if (plugin->open(is, url)) {
is->plugin = plugin; assert(is->plugin != NULL);
assert(is->plugin->open == NULL ||
is->plugin == plugin);
assert(is->plugin->close != NULL);
assert(is->plugin->read != NULL);
assert(is->plugin->eof != NULL);
assert(!is->seekable || is->plugin->seek != NULL);
return true; return true;
} }
} }
...@@ -84,6 +98,9 @@ input_stream_open(struct input_stream *is, const char *url) ...@@ -84,6 +98,9 @@ input_stream_open(struct input_stream *is, const char *url)
bool bool
input_stream_seek(struct input_stream *is, off_t offset, int whence) input_stream_seek(struct input_stream *is, off_t offset, int whence)
{ {
if (is->plugin->seek == NULL)
return false;
return is->plugin->seek(is, offset, whence); return is->plugin->seek(is, offset, whence);
} }
...@@ -120,5 +137,8 @@ bool input_stream_eof(struct input_stream *is) ...@@ -120,5 +137,8 @@ bool input_stream_eof(struct input_stream *is)
int input_stream_buffer(struct input_stream *is) int input_stream_buffer(struct input_stream *is)
{ {
if (is->plugin->buffer == NULL)
return 0;
return is->plugin->buffer(is); return is->plugin->buffer(is);
} }
...@@ -37,33 +37,90 @@ struct input_plugin { ...@@ -37,33 +37,90 @@ struct input_plugin {
}; };
struct input_stream { struct input_stream {
/**
* the plugin which implements this input stream
*/
const struct input_plugin *plugin; const struct input_plugin *plugin;
bool seekable; /**
* an opaque pointer managed by the plugin
*/
void *data;
/**
* indicates whether the stream is ready for reading and
* whether the other attributes in this struct are valid
*/
bool ready; bool ready;
/**
* if true, then the stream is fully seekable
*/
bool seekable;
/**
* an optional errno error code, set to non-zero after an error occured
*/
int error; int error;
off_t size, offset;
char *mime;
void *data; /**
* the size of the resource, or -1 if unknown
*/
off_t size;
/**
* the current offset within the stream
*/
off_t offset;
void *archive; /**
* the MIME content type of the resource, or NULL if unknown
*/
char *mime;
}; };
/**
* Initializes this library and all input_stream implementations.
*/
void input_stream_global_init(void); void input_stream_global_init(void);
/**
* Deinitializes this library and all input_stream implementations.
*/
void input_stream_global_finish(void); void input_stream_global_finish(void);
/* if an error occurs for these 3 functions, then -1 is returned and errno /**
for the input stream is set */ * Opens a new input stream. You may not access it until the "ready"
* flag is set.
*
* @param is the input_stream object allocated by the caller
* @return true on success
*/
bool bool
input_stream_open(struct input_stream *is, const char *url); input_stream_open(struct input_stream *is, const char *url);
/**
* Closes the input stream and free resources. This does not free the
* input_stream pointer itself, because it is assumed to be allocated
* by the caller.
*/
void
input_stream_close(struct input_stream *is);
/**
* Seeks to the specified position in the stream. This will most
* likely fail if the "seekable" flag is false.
*
* @param is the input_stream object
* @param offset the relative offset
* @param whence the base of the seek, one of SEEK_SET, SEEK_CUR, SEEK_END
*/
bool bool
input_stream_seek(struct input_stream *is, off_t offset, int whence); input_stream_seek(struct input_stream *is, off_t offset, int whence);
void input_stream_close(struct input_stream *is); /**
* Returns true if the stream has reached end-of-file.
*/
bool input_stream_eof(struct input_stream *is); bool input_stream_eof(struct input_stream *is);
/** /**
...@@ -75,10 +132,25 @@ bool input_stream_eof(struct input_stream *is); ...@@ -75,10 +132,25 @@ bool input_stream_eof(struct input_stream *is);
struct tag * struct tag *
input_stream_tag(struct input_stream *is); input_stream_tag(struct input_stream *is);
/* return value: -1 is error, 1 inidicates stuff was buffered, 0 means nothing /**
was buffered */ * Reads some of the stream into its buffer. The following return
* codes are defined: -1 = error, 1 = something was buffered, 0 =
* nothing was buffered.
*
* The semantics of this function are not well-defined, and it will
* eventually be removed.
*/
int input_stream_buffer(struct input_stream *is); int input_stream_buffer(struct input_stream *is);
/**
* Reads data from the stream into the caller-supplied buffer.
* Returns 0 on error or eof (check with input_stream_eof()).
*
* @param is the input_stream object
* @param ptr the buffer to read into
* @param size the maximum number of bytes to read
* @return the number of bytes read
*/
size_t size_t
input_stream_read(struct input_stream *is, void *ptr, size_t size); input_stream_read(struct input_stream *is, void *ptr, size_t size);
......
...@@ -128,7 +128,7 @@ static bool ipv6Supported(void) ...@@ -128,7 +128,7 @@ static bool ipv6Supported(void)
static void static void
parseListenConfigParam(G_GNUC_UNUSED unsigned int port, parseListenConfigParam(G_GNUC_UNUSED unsigned int port,
struct config_param *param) const struct config_param *param)
{ {
const struct sockaddr *addrp; const struct sockaddr *addrp;
socklen_t addrlen; socklen_t addrlen;
...@@ -253,27 +253,14 @@ parseListenConfigParam(G_GNUC_UNUSED unsigned int port, ...@@ -253,27 +253,14 @@ parseListenConfigParam(G_GNUC_UNUSED unsigned int port,
void listenOnPort(void) void listenOnPort(void)
{ {
int port = DEFAULT_PORT; int port = config_get_positive(CONF_PORT, DEFAULT_PORT);
struct config_param *param = const struct config_param *param =
config_get_next_param(CONF_BIND_TO_ADDRESS, NULL); config_get_next_param(CONF_BIND_TO_ADDRESS, NULL);
struct config_param *portParam = config_get_param(CONF_PORT);
if (portParam) {
char *test;
port = strtol(portParam->value, &test, 10);
if (port <= 0 || *test != '\0') {
g_error("%s \"%s\" specified at line %i is not a "
"positive integer",
CONF_PORT,
portParam->value, portParam->line);
}
}
boundPort = port;
do { do {
parseListenConfigParam(port, param); parseListenConfigParam(port, param);
} while ((param = config_get_next_param(CONF_BIND_TO_ADDRESS, param))); } while ((param = config_get_next_param(CONF_BIND_TO_ADDRESS, param)));
boundPort = port;
} }
void closeAllListenSockets(void) void closeAllListenSockets(void)
......
...@@ -29,110 +29,122 @@ ...@@ -29,110 +29,122 @@
#define LOCATE_TAG_FILE_KEY_OLD "filename" #define LOCATE_TAG_FILE_KEY_OLD "filename"
#define LOCATE_TAG_ANY_KEY "any" #define LOCATE_TAG_ANY_KEY "any"
int getLocateTagItemType(const char *str) int
locate_parse_type(const char *str)
{ {
int i; int i;
if (0 == strcasecmp(str, LOCATE_TAG_FILE_KEY) || if (0 == strcasecmp(str, LOCATE_TAG_FILE_KEY) ||
0 == strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD)) 0 == strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD))
{
return LOCATE_TAG_FILE_TYPE; return LOCATE_TAG_FILE_TYPE;
}
if (0 == strcasecmp(str, LOCATE_TAG_ANY_KEY)) if (0 == strcasecmp(str, LOCATE_TAG_ANY_KEY))
{
return LOCATE_TAG_ANY_TYPE; return LOCATE_TAG_ANY_TYPE;
}
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++)
{
if (0 == strcasecmp(str, mpdTagItemKeys[i])) if (0 == strcasecmp(str, mpdTagItemKeys[i]))
return i; return i;
}
return -1; return -1;
} }
static int initLocateTagItem(LocateTagItem * item, static bool
const char *typeStr, const char *needle) locate_item_init(struct locate_item *item,
const char *type_string, const char *needle)
{ {
item->tagType = getLocateTagItemType(typeStr); item->tag = locate_parse_type(type_string);
if (item->tagType < 0) if (item->tag < 0)
return -1; return false;
item->needle = g_strdup(needle); item->needle = g_strdup(needle);
return 0; return true;
} }
LocateTagItem *newLocateTagItem(const char *typeStr, const char *needle) struct locate_item *
locate_item_new(const char *type_string, const char *needle)
{ {
LocateTagItem *ret = g_new(LocateTagItem, 1); struct locate_item *ret = g_new(struct locate_item, 1);
if (initLocateTagItem(ret, typeStr, needle) < 0) { if (!locate_item_init(ret, type_string, needle)) {
free(ret); g_free(ret);
ret = NULL; ret = NULL;
} }
return ret; return ret;
} }
void freeLocateTagItemArray(int count, LocateTagItem * array) void
locate_item_list_free(struct locate_item_list *list)
{ {
int i; for (unsigned i = 0; i < list->length; ++i)
g_free(list->items[i].needle);
for (i = 0; i < count; i++)
free(array[i].needle);
free(array); g_free(list);
} }
int newLocateTagItemArrayFromArgArray(char *argArray[], struct locate_item_list *
int numArgs, LocateTagItem ** arrayRet) locate_item_list_new(unsigned length)
{ {
int i, j; struct locate_item_list *list;
LocateTagItem *item;
list = g_malloc0(sizeof(*list) - sizeof(list->items[0]) +
length * sizeof(list->items[0]));
list->length = length;
if (numArgs == 0) return list;
return 0; }
struct locate_item_list *
locate_item_list_parse(char *argv[], int argc)
{
struct locate_item_list *list;
if (numArgs % 2 != 0) if (argc % 2 != 0)
return -1; return NULL;
*arrayRet = g_new(LocateTagItem, numArgs / 2); list = locate_item_list_new(argc / 2);
for (i = 0, item = *arrayRet; i < numArgs / 2; i++, item++) { for (unsigned i = 0; i < list->length; ++i) {
if (initLocateTagItem if (!locate_item_init(&list->items[i], argv[i * 2],
(item, argArray[i * 2], argArray[i * 2 + 1]) < 0) argv[i * 2 + 1])) {
goto fail; locate_item_list_free(list);
return NULL;
}
} }
return numArgs / 2; return list;
}
struct locate_item_list *
locate_item_list_casefold(const struct locate_item_list *list)
{
struct locate_item_list *new_list = locate_item_list_new(list->length);
fail: for (unsigned i = 0; i < list->length; i++){
for (j = 0; j < i; j++) { new_list->items[i].needle =
free((*arrayRet)[j].needle); g_utf8_casefold(list->items[i].needle, -1);
new_list->items[i].tag = list->items[i].tag;
} }
free(*arrayRet); return new_list;
*arrayRet = NULL;
return -1;
} }
void freeLocateTagItem(LocateTagItem * item) void
locate_item_free(struct locate_item *item)
{ {
free(item->needle); g_free(item->needle);
free(item); g_free(item);
} }
static int static bool
strstrSearchTag(struct song *song, enum tag_type type, char *str) locate_tag_search(const struct song *song, enum tag_type type, const char *str)
{ {
int i; int i;
char *duplicate; char *duplicate;
int ret = 0; bool ret = false;
int8_t visitedTypes[TAG_NUM_OF_ITEM_TYPES] = { 0 }; bool visited_types[TAG_NUM_OF_ITEM_TYPES];
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) { if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
char *uri, *p; char *uri, *p;
...@@ -142,17 +154,19 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str) ...@@ -142,17 +154,19 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str)
g_free(uri); g_free(uri);
if (strstr(p, str)) if (strstr(p, str))
ret = 1; ret = true;
g_free(p); g_free(p);
if (ret == 1 || type == LOCATE_TAG_FILE_TYPE) if (ret == 1 || type == LOCATE_TAG_FILE_TYPE)
return ret; return ret;
} }
if (!song->tag) if (!song->tag)
return 0; return false;
memset(visited_types, 0, sizeof(visited_types));
for (i = 0; i < song->tag->numOfItems && !ret; i++) { for (i = 0; i < song->tag->numOfItems && !ret; i++) {
visitedTypes[song->tag->items[i]->type] = 1; visited_types[song->tag->items[i]->type] = true;
if (type != LOCATE_TAG_ANY_TYPE && if (type != LOCATE_TAG_ANY_TYPE &&
song->tag->items[i]->type != type) { song->tag->items[i]->type != type) {
continue; continue;
...@@ -160,7 +174,7 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str) ...@@ -160,7 +174,7 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str)
duplicate = g_utf8_casefold(song->tag->items[i]->value, -1); duplicate = g_utf8_casefold(song->tag->items[i]->value, -1);
if (*str && strstr(duplicate, str)) if (*str && strstr(duplicate, str))
ret = 1; ret = true;
g_free(duplicate); g_free(duplicate);
} }
...@@ -168,34 +182,31 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str) ...@@ -168,34 +182,31 @@ strstrSearchTag(struct song *song, enum tag_type type, char *str)
* through the song's tag, it means this field is absent from * through the song's tag, it means this field is absent from
* the tag or empty. Thus, if the searched string is also * the tag or empty. Thus, if the searched string is also
* empty (first char is a \0), then it's a match as well and * empty (first char is a \0), then it's a match as well and
* we should return 1. * we should return true.
*/ */
if (!*str && !visitedTypes[type]) if (!*str && !visited_types[type])
return 1; return true;
return ret; return ret;
} }
int bool
strstrSearchTags(struct song *song, int numItems, LocateTagItem *items) locate_song_search(const struct song *song,
const struct locate_item_list *criteria)
{ {
int i; for (unsigned i = 0; i < criteria->length; i++)
if (!locate_tag_search(song, criteria->items[i].tag,
for (i = 0; i < numItems; i++) { criteria->items[i].needle))
if (!strstrSearchTag(song, items[i].tagType, return false;
items[i].needle)) {
return 0;
}
}
return 1; return true;
} }
static int static bool
tagItemFoundAndMatches(struct song *song, enum tag_type type, char *str) locate_tag_match(const struct song *song, enum tag_type type, const char *str)
{ {
int i; int i;
int8_t visitedTypes[TAG_NUM_OF_ITEM_TYPES] = { 0 }; bool visited_types[TAG_NUM_OF_ITEM_TYPES];
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) { if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
char *uri = song_get_uri(song); char *uri = song_get_uri(song);
...@@ -203,51 +214,48 @@ tagItemFoundAndMatches(struct song *song, enum tag_type type, char *str) ...@@ -203,51 +214,48 @@ tagItemFoundAndMatches(struct song *song, enum tag_type type, char *str)
g_free(uri); g_free(uri);
if (matches) if (matches)
return 1; return true;
if (type == LOCATE_TAG_FILE_TYPE) if (type == LOCATE_TAG_FILE_TYPE)
return 0; return false;
} }
if (!song->tag) if (!song->tag)
return 0; return false;
memset(visited_types, 0, sizeof(visited_types));
for (i = 0; i < song->tag->numOfItems; i++) { for (i = 0; i < song->tag->numOfItems; i++) {
visitedTypes[song->tag->items[i]->type] = 1; visited_types[song->tag->items[i]->type] = true;
if (type != LOCATE_TAG_ANY_TYPE && if (type != LOCATE_TAG_ANY_TYPE &&
song->tag->items[i]->type != type) { song->tag->items[i]->type != type) {
continue; continue;
} }
if (0 == strcmp(str, song->tag->items[i]->value)) if (0 == strcmp(str, song->tag->items[i]->value))
return 1; return true;
} }
/** If the search critieron was not visited during the sweep /** If the search critieron was not visited during the sweep
* through the song's tag, it means this field is absent from * through the song's tag, it means this field is absent from
* the tag or empty. Thus, if the searched string is also * the tag or empty. Thus, if the searched string is also
* empty (first char is a \0), then it's a match as well and * empty (first char is a \0), then it's a match as well and
* we should return 1. * we should return true.
*/ */
if (!*str && !visitedTypes[type]) if (!*str && !visited_types[type])
return 1; return true;
return 0; return false;
} }
bool
int locate_song_match(const struct song *song,
tagItemsFoundAndMatches(struct song *song, int numItems, const struct locate_item_list *criteria)
LocateTagItem * items)
{ {
int i; for (unsigned i = 0; i < criteria->length; i++)
if (!locate_tag_match(song, criteria->items[i].tag,
for (i = 0; i < numItems; i++) { criteria->items[i].needle))
if (!tagItemFoundAndMatches(song, items[i].tagType, return false;
items[i].needle)) {
return 0;
}
}
return 1; return true;
} }
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define MPD_LOCATE_H #define MPD_LOCATE_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10 #define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20 #define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
...@@ -27,30 +28,60 @@ ...@@ -27,30 +28,60 @@
struct song; struct song;
/* struct used for search, find, list queries */ /* struct used for search, find, list queries */
typedef struct _LocateTagItem { struct locate_item {
int8_t tagType; int8_t tag;
/* what we are looking for */ /* what we are looking for */
char *needle; char *needle;
} LocateTagItem; };
int getLocateTagItemType(const char *str); /**
* An array of struct locate_item objects.
*/
struct locate_item_list {
/** number of items */
unsigned length;
/** this is a variable length array */
struct locate_item items[1];
};
int
locate_parse_type(const char *str);
/* returns NULL if not a known type */ /* returns NULL if not a known type */
LocateTagItem *newLocateTagItem(const char *typeString, const char *needle); struct locate_item *
locate_item_new(const char *type_string, const char *needle);
/**
* Allocates a new struct locate_item_list, and initializes all
* members with zero bytes.
*/
struct locate_item_list *
locate_item_list_new(unsigned length);
/* return number of items or -1 on error */ /* return number of items or -1 on error */
int newLocateTagItemArrayFromArgArray(char *argArray[], int numArgs, struct locate_item_list *
LocateTagItem ** arrayRet); locate_item_list_parse(char *argv[], int argc);
void freeLocateTagItemArray(int count, LocateTagItem * array); /**
* Duplicate the struct locate_item_list object and convert all
* needles with g_utf8_casefold().
*/
struct locate_item_list *
locate_item_list_casefold(const struct locate_item_list *list);
void freeLocateTagItem(LocateTagItem * item); void
locate_item_list_free(struct locate_item_list *list);
int void
strstrSearchTags(struct song *song, int numItems, LocateTagItem * items); locate_item_free(struct locate_item *item);
int bool
tagItemsFoundAndMatches(struct song *song, int numItems, locate_song_search(const struct song *song,
LocateTagItem * items); const struct locate_item_list *criteria);
bool
locate_song_match(const struct song *song,
const struct locate_item_list *criteria);
#endif #endif
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment