Commit 2ccb1f5d authored by Alexey Morsov's avatar Alexey Morsov

Merge commit 'v0.16.2' into alt

parents 8c743fd3 a1d1c2be
......@@ -24,6 +24,7 @@ config.sub
config_detected.h
config_detected.mk
configure
configure.lineno
depcomp
depmode
install-sh
......@@ -32,9 +33,11 @@ ltmain.sh
missing
mkinstalldirs
mpd
mpd.exe
stamp-h1
tags
*~
.#*
.stgit*
doc/protocol.html
doc/protocol
......@@ -43,10 +46,16 @@ doc/developer
doc/sticker
doc/api
test/software_volume
test/run_convert
test/run_decoder
test/read_tags
test/run_filter
test/run_encoder
test/run_output
test/read_conf
test/run_input
test/read_mixer
test/dump_playlist
test/run_normalize
test/tmp
test/run_inotify
......@@ -19,9 +19,6 @@ Eric Wollesen <encoded@xmtp.net>
Thomas Jansen <mithi@mithi.net>
multithreading tweaks, miscellaneous
Rasmus Steinke <rasi1979@googlemail.com>
documentation
Romain Bignon <romain@peerfuse.org>
playlist manipulation
......
......@@ -13,7 +13,7 @@ Dependencies
gcc - http://gcc.gnu.org/
Any other C99 compliant compiler should also work.
glib - http://www.gtk.org/
GLib 2.12 - http://www.gtk.org/
General-purpose utility library.
......@@ -56,6 +56,12 @@ libshout - http://www.icecast.org/
For streaming to an Icecast or Shoutcast server.
You also need an encoder: either libvorbisenc (ogg), or liblame (mp3).
OpenAL - http://kcat.strangesoft.net/openal.html
Open Audio Library
libffado - http://www.ffado.org/
For FireWire audio devices.
Optional Input Dependencies
---------------------------
......@@ -69,6 +75,9 @@ MAD - http://www.underbit.com/products/mad/
For MP3 support. You will need libmad, and optionally libid3tag if you want
ID3 tag support.
libmpg123 - http://www.mpg123.de/
Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
......@@ -104,6 +113,12 @@ For MIDI support (DO NOT USE - use libwildmidi instead)
libwildmidi - http://wildmidi.sourceforge.net/
For MIDI support.
libsndfile - http://www.mega-nerd.com/libsndfile/
WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
Optional Miscellaneous Dependencies
-----------------------------------
......
ver 0.16.2 (2011/??/??)
* decoder:
- tremor: fix configure test
- gme: detect end of song
* output:
- httpd: fix uninitialized variable
- oss: AFMT_S24_PACKED is little-endian
- oss: disable 24 bit playback on FreeBSD
ver 0.16.1 (2011/01/09)
* audio_check: fix parameter in prototype
* add void casts to suppress "result unused" warnings (clang)
* input:
- ffado: disable by default
* decoder:
- mad: work around build failure on Solaris
- resolve modplug vs. libsndfile cflags/headers conflict
* output:
- solaris: add missing parameter to open_cloexec() cal
- osx: fix up audio format first, then apply it to device
* player_thread: discard empty chunks while cross-fading
* player_thread: fix assertion failure due to early seek
* output_thread: fix double lock
ver 0.16 (2010/12/11)
* protocol:
- send song modification time to client
- added "update" idle event
- removed the deprecated "volume" command
- added the "findadd" command
- range support for "delete"
- "previous" really plays the previous song
- "addid" with negative position is deprecated
- "load" supports remote playlists (extm3u, pls, asx, xspf, lastfm://)
- allow changing replay gain mode on-the-fly
- omitting the range end is possible
- "update" checks if the path is malformed
* archive:
- iso: renamed plugin to "iso9660"
- zip: renamed plugin to "zzip"
* input:
- lastfm: obsolete plugin removed
- ffmpeg: new input plugin using libavformat's "avio" library
* tags:
- added tags "ArtistSort", "AlbumArtistSort"
- id3: revised "performer" tag support
- id3: support multiple values
- ape: MusicBrainz tags
- ape: support multiple values
* decoders:
- don't try a plugin twice (MIME type & suffix)
- don't fall back to "mad" unless no plugin matches
- ffmpeg: support multiple tags
- ffmpeg: convert metadata to generic format
- ffmpeg: implement the libavutil log callback
- sndfile: new decoder plugin based on libsndfile
- flac: moved CUE sheet support to a playlist plugin
- flac: support streams without STREAMINFO block
- mikmod: sample rate is configurable
- mpg123: new decoder plugin based on libmpg123
- sidplay: support sub-tunes
- sidplay: implemented songlength database
- sidplay: support seeking
- sidplay: play monaural SID tunes in mono
- sidplay: play mus, str, prg, x00 files
- wavpack: activate 32 bit support
- wavpack: allow more than 2 channels
- mp4ff: rename plugin "mp4" to "mp4ff"
* encoders:
- twolame: new encoder plugin based on libtwolame
- flac: new encoder plugin based on libFLAC
- wave: new encoder plugin for PCM WAV format
* output:
- recorder: new output plugin for recording radio streams
- alsa: don't recover on CANCEL
- alsa: fill period buffer with silence before draining
- openal: new output plugin
- pulse: announce "media.role=music"
- pulse: renamed context to "Music Player Daemon"
- pulse: connect to server on MPD startup, implement pause
- jack: require libjack 0.100
- jack: don't disconnect during pause
- jack: connect to server on MPD startup
- jack: added options "client_name", "server_name"
- jack: clear ring buffers before activating
- jack: renamed option "ports" to "destination_ports"
- jack: support more than two audio channels
- httpd: bind port when output is enabled
- httpd: added name/genre/website configuration
- httpd: implement "pause"
- httpd: bind_to_address support (including IPv6)
- oss: 24 bit support via OSS4
- win32: new output plugin for Windows Wave
- shout, httpd: more responsive to control commands
- wildcards allowed in audio_format configuration
- consistently lock audio output objects
* player:
- drain audio outputs at the end of the playlist
* mixers:
- removed support for legacy mixer configuration
- reimplemented software volume as mixer+filter plugin
- per-device software/hardware mixer setting
* commands:
- added new "status" line with more precise "elapsed time"
* update:
- automatically update the database with Linux inotify
- support .mpdignore files in the music directory
- sort songs by album name first, then disc/track number
- rescan after metadata_to_use change
* normalize: upgraded to AudioCompress 2.0
- automatically convert to 16 bit samples
* replay gain:
- reimplemented as a filter plugin
- fall back to track gain if album gain is unavailable
- optionally use hardware mixer to apply replay gain
- added mode "auto"
- parse replay gain from APE tags
* log unused/unknown block parameters
* removed the deprecated "error_file" option
* save state when stopped
* renamed option "--stdout" to "--stderr"
* removed options --create-db and --no-create-db
* state_file: save only if something has changed
* database: eliminated maximum line length
* log: redirect stdout/stderr to /dev/null if syslog is used
* set the close-on-exec flag on all file descriptors
* pcm_volume, pcm_mix: implemented 32 bit support
* support packed 24 bit samples
* CUE sheet support
* support for MixRamp tags
* obey $(sysconfdir) for default mpd.conf location
* build with large file support by default
* added test suite ("make check")
* require GLib 2.12
* added libwrap support
* make single mode 'sticky'
ver 0.15.16 (2010/??/??)
* encoders:
- lame: explicitly configure the output sample rate
ver 0.15.15 (2010/11/08)
* input:
- rewind: fix assertion failure
* output:
- shout: artist comes first in stream title
ver 0.15.14 (2010/11/06)
* player_thread: fix assertion failure due to wrong music pipe on seek
* output_thread: fix assertion failure due to race condition in OPEN
* input:
- rewind: fix double free bug
* decoders:
- mp4ff, ffmpeg: add extension ".m4b" (audio book)
ver 0.15.13 (2010/10/10)
* output_thread: fix race condition after CANCEL command
* output:
......
......@@ -9,20 +9,20 @@ srcdir="`dirname $0`"
test -z "$srcdir" && srcdir=.
cd "$srcdir"
DIE=
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/"
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]*\).*/\1/"
AC_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]\).*/\1/"
VERSIONMKINT="sed -e s/[^0-9]//"
if test -n "$AM_FORCE_VERSION"
then
AM_VERSIONS="$AM_FORCE_VERSION"
else
AM_VERSIONS='1.9 1.10'
AM_VERSIONS='1.11 1.10'
fi
if test -n "$AC_FORCE_VERSION"
then
AC_VERSIONS="$AC_FORCE_VERSION"
else
AC_VERSIONS='2.58 2.59 2.60 2.61'
AC_VERSIONS='2.60 2.61'
fi
versioned_bins ()
......
......@@ -534,7 +534,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = src
INPUT = src/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
......
......@@ -25,17 +25,11 @@ Output a brief help message.
Kill the currently running mpd session. The pid_file parameter must be
specified in the config file for this to work.
.TP
.BI --create-db
Force (re)creation of database.
.TP
.BI --no-create-db
Do not create database, even if it doesn't exist.
.TP
.BI --no-daemon
Don't detach from console.
.TP
.BI --stdout
Print messages to stdout and stderr.
.BI --stderr
Print messages stderr.
.TP
.BI --verbose
Verbose logging.
......
......@@ -129,6 +129,8 @@ audio that is sent to each audio output. Note that audio outputs may specify
their own audio format which will be used for actual output to the audio
device. An example is "44100:16:2" for 44100Hz, 16 bits, and 2 channels. The
default is to use the audio format of the input file.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
.TP
.B samplerate_converter <integer or prefix>
This specifies the libsamplerate converter to use. The supplied value should
......@@ -168,31 +170,14 @@ only choice) if MPD was compiled without libsamplerate.
For an up-to-date list of available converters, please see the libsamplerate
documentation (available online at <\fBhttp://www.mega-nerd.com/SRC/\fP>).
.TP
.B mixer_type <alsa, oss, software, hardware or disabled>
This specifies which mixer to use. The default is hardware and depends on
what audio output support mpd was built with. Options alsa and oss are
legacy and should not be used in new configs, but when set mixer_device
and mixer_control will apply.
.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default for oss is
"/dev/mixer"; the default for alsa is "default". This global option is
deprecated and should not be used. Look at the mixer_device option of
corresponding output device instead.
.TP
.B mixer_control <mixer ctrl>
This specifies which mixer control to use (sometimes referred to as
the "device"). Examples of mixer controls are PCM, Line1, Master,
etc. An example for OSS is "Pcm", and an example for alsa is
"PCM". This global option is deprecated and should not be used. Look
at the mixer_control option of corresponding output device instead.
.TP
.B replaygain <album or track>
.B replaygain <off or album or track or auto>
If specified, mpd will adjust the volume of songs played using ReplayGain tags
(see <\fBhttp://www.replaygain.org/\fP>). Setting this to "album" will adjust
volume using the album's ReplayGain tags, while setting it to "track" will
adjust it using the track ReplayGain tags. Currently only FLAC, Ogg Vorbis,
Musepack, and MP3 (through ID3v2 ReplayGain tags, not APEv2) are supported.
adjust it using the track ReplayGain tags. "auto" uses the track ReplayGain
tags if random play is activated otherwise the album ReplayGain tags. Currently
only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2 ReplayGain tags, not
APEv2) are supported.
.TP
.B replaygain_preamp <-15 to 15>
This is the gain (in dB) applied to songs with ReplayGain tags.
......@@ -265,6 +250,15 @@ tags may be specified as a comma separated list. An example value is
"artist,album,title,track". The special value "none" may be used alone to
disable all metadata. The default is to use all known tag types except for
comments.
.TP
.B auto_update <yes or no>
This specifies the wheter to support automatic update of music database when
files are changed in music_directory. The default is to disable autoupdate
of database.
.TP
.B auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default.
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
.B type <type>
......@@ -280,11 +274,25 @@ This specifies the sample rate, bits per sample, and number of channels of
audio that is sent to the audio output device. See documentation for the
\fBaudio_output_format\fP parameter for more details. The default is to use
whatever audio format is passed to the audio output.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
.TP
.B replay_gain_handler <software, mixer or none>
Specifies how replay gain is applied. The default is "software",
which uses an internal software volume control. "mixer" uses the
configured (hardware) mixer control. "none" disables replay gain on
this audio output.
.SH OPTIONAL ALSA OUTPUT PARAMETERS
.TP
.B device <dev>
This specifies the device to use for audio output. The default is "default".
.TP
.B mixer_type <hardware, software or none>
Specifies which mixer should be used for this audio output: the
hardware mixer (available for ALSA, OSS and PulseAudio), the software
mixer or no mixer ("none"). By default, the hardware mixer is used
for devices which support it, and none for the others.
.TP
.B mixer_device <mixer dev>
This specifies which mixer to use. The default is "default". To use
the second sound card in a system, use "hw:1".
......@@ -352,13 +360,12 @@ after another until it successfully establishes a connection.
.TP
.B sink <sink>
The sink to output to. The default is to let PulseAudio choose a sink.
.SH REQUIRED JACK OUTPUT PARAMETERS
.SH OPTIONAL JACK OUTPUT PARAMETERS
.TP
.B name <name>
.B client_name <name>
The client name to use when connecting to JACK. The output ports <name>:left
and <name>:right will also be created for the left and right channels,
respectively.
.SH OPTIONAL JACK OUTPUT PARAMETERS
.TP
.B ports <left_port,right_port>
This specifies the left and right ports to connect to for the left and right
......
......@@ -49,6 +49,11 @@
#
#state_file "~/.mpd/state"
#
# The location of the sticker database. This is a database which
# manages dynamic information attached to songs.
#
#sticker_file "~/.mpd/sticker.sql"
#
###############################################################################
......@@ -61,6 +66,13 @@
#
#user "nobody"
#
# This setting specifies the group that MPD will run as. If not specified
# primary group of user specified with "user" setting will be used (if set).
# This is useful if MPD needs to be a member of group such as "audio" to
# have permission to use sound card.
#
#group "nogroup"
#
# This setting sets the address for the daemon to listen on. Careful attention
# should be paid if this is assigned to anything other then the default, any.
# This setting can deny access to control of the daemon.
......@@ -102,6 +114,16 @@
#
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in
# music_directory are changed.
#
#auto_update "yes"
#
# Limit the depth of the directories being watched, 0 means only watch
# the music directory itself. There is no limit by default.
#
#auto_update_depth "3"
#
###############################################################################
......@@ -179,6 +201,7 @@ input {
# name "My ALSA Device"
## device "hw:0,0" # optional
## format "44100:16:2" # optional
## mixer_type "hardware" # optional
## mixer_device "default" # optional
## mixer_control "PCM" # optional
## mixer_index "0" # optional
......@@ -191,6 +214,7 @@ input {
# name "My OSS Device"
## device "/dev/dsp" # optional
## format "44100:16:2" # optional
## mixer_type "hardware" # optional
## mixer_device "/dev/mixer" # optional
## mixer_control "PCM" # optional
#}
......@@ -214,6 +238,19 @@ input {
## genre "jazz" # optional
## public "no" # optional
## timeout "2" # optional
## mixer_type "software" # optional
#}
#
# An example of a recorder output:
#
#audio_output {
# type "recorder"
# name "My recorder"
# encoder "vorbis" # optional, vorbis or lame
# path "/var/lib/mpd/recorder/mpd.ogg"
## quality "5.0" # do not define if bitrate is defined
# bitrate "128" # do not define if quality is defined
# format "44100:16:1"
#}
#
# An example of a httpd output (built-in HTTP streaming server):
......@@ -223,9 +260,11 @@ input {
# name "My HTTP Stream"
# encoder "vorbis" # optional, vorbis or lame
# port "8000"
# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6
## quality "5.0" # do not define if bitrate is defined
# bitrate "128" # do not define if quality is defined
# format "44100:16:1"
# max_clients "0" # optional 0=no limit
#}
#
# An example of a pulseaudio output (streaming to a remote pulseaudio server)
......@@ -255,6 +294,7 @@ input {
#audio_output {
# type "null"
# name "My Null Output"
# mixer_type "none" # optional
#}
#
# This setting will change all decoded audio to be converted to the specified
......@@ -273,38 +313,11 @@ input {
###############################################################################
# Volume control mixer ########################################################
#
# These are the global volume control settings. By default, this setting will
# be detected to the available audio output device, with preference going to
# hardware mixing. Hardware and software mixers for individual audio_output
# sections cannot yet be mixed.
#
# An example for controlling an ALSA, OSS or Pulseaudio mixer; If this
# setting is used other sound applications will be affected by the volume
# being controlled by MPD.
#
#mixer_type "hardware"
#
# An example for controlling all mixers through software. This will control
# all controls, even if the mixer is not supported by the device and will not
# affect any other sound producing applications.
#
#mixer_type "software"
#
# This example will not allow MPD to touch the mixer at all and will disable
# all volume controls.
#
#mixer_type "disabled"
#
###############################################################################
# Normalization automatic volume adjustments ##################################
#
# This setting specifies the type of ReplayGain to use. This setting can have
# the argument "album" or "track". See <http://www.replaygain.org> for more
# details. This setting is disabled by default.
# the argument "off", "album" or "track". See <http://www.replaygain.org>
# for more details. This setting is off by default.
#
#replaygain "album"
#
......@@ -357,8 +370,7 @@ input {
# Character Encoding ##########################################################
#
# If file or directory names do not display correctly for your locale then you
# may need to modify this setting. After modification of this setting mpd
# --create-db must be run to change the database.
# may need to modify this setting.
#
#filesystem_charset "UTF-8"
#
......@@ -367,3 +379,29 @@ input {
#id3v1_encoding "ISO-8859-1"
#
###############################################################################
# SIDPlay decoder #############################################################
#
# songlength_database:
# Location of your songlengths file, as distributed with the HVSC.
# The sidplay plugin checks this for matching MD5 fingerprints.
# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq
#
# default_songlength:
# This is the default playing time in seconds for songs not in the
# songlength database, or in case you're not using a database.
# A value of 0 means play indefinitely.
#
# filter:
# Turns the SID filter emulation on or off.
#
#decoder {
# plugin "sidplay"
# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt"
# default_songlength "120"
# filter "true"
#}
#
###############################################################################
<?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>
<variablelist>
<varlistentry>
<term><varname>rating</varname></term>
<listitem>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a
given song.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>album_rating</varname></term>
<listitem>
<para>
Will store a rating value from 1 (worst) to 5 (best) for a
given album.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>style</varname></term>
<listitem>
<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>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>lyrics</varname></term>
<listitem>
<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>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>similar_artists</varname> (Comma seperated list of artists)</term>
<listitem>
<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>
</listitem>
</varlistentry>
</variablelist>
</chapter>
</book>
......@@ -58,7 +58,7 @@ if test x$enable_aac = xyes; then
fi
if test x$enable_aac = xyes; then
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
int main() {
......@@ -82,9 +82,9 @@ int main() {
return 0;
}
],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
AC_MSG_CHECKING(that FAAD2 can even be used)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
int main() {
......@@ -113,7 +113,7 @@ int main() {
return 0;
}
],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
])
fi
if test x$enable_aac = xyes; then
......@@ -136,7 +136,7 @@ if test x$enable_aac = xyes; then
CPPFLAGS=$CFLAGS
AC_MSG_CHECKING(for broken libfaad headers)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
#include <stddef.h>
#include <stdint.h>
......@@ -148,7 +148,7 @@ int main() {
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
return 0;
}
],
])],
[AC_MSG_RESULT(correct)],
[AC_MSG_RESULT(broken);
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
......
dnl borrowed from oddsock.org
dnl AM_PATH_LAME([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for liblame, and define LAME_CFLAGS and LAME_LIBS
dnl
AC_DEFUN([AM_PATH_LAME],
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(lame,
AS_HELP_STRING([--with-lame=PFX],
[prefix where liblame is installed (optional)]),,
lame_prefix="")
AC_ARG_WITH(lame-libraries,
AS_HELP_STRING([--with-lame-libraries=DIR],
[directory where liblame library is installed (optional)]),,
lame_libraries="")
AC_ARG_WITH(lame-includes,
AS_HELP_STRING([--with-lame-includes=DIR],
[directory where liblame header files are installed (optional)]),,
lame_includes="")
if test "x$lame_prefix" != "xno" ; then
if test "x$lame_libraries" != "x" ; then
LAME_LIBS="-L$lame_libraries"
elif test "x$lame_prefix" != "x" ; then
LAME_LIBS="-L$lame_prefix/lib"
elif test "x$prefix" != "xNONE" ; then
LAME_LIBS="-L$prefix/lib"
fi
LAME_LIBS="$LAME_LIBS -lmp3lame -lm"
if test "x$lame_includes" != "x" ; then
LAME_CFLAGS="-I$lame_includes"
elif test "x$lame_prefix" != "x" ; then
LAME_CFLAGS="-I$lame_prefix/include"
elif test "x$prefix" != "xNONE"; then
LAME_CFLAGS="-I$prefix/include"
fi
AC_MSG_CHECKING(for liblame)
no_lame=""
ac_save_CFLAGS="$CFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $LAME_CFLAGS"
LIBS="$LIBS $LAME_LIBS"
dnl
dnl Now check if the installed liblame is sufficiently new.
dnl
rm -f conf.lametest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lame/lame.h>
int main ()
{
system("touch conf.lametest");
return 0;
}
],, no_lame=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_lame" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.lametest ; then
:
else
echo "*** Could not run liblame test program, checking why..."
CFLAGS="$CFLAGS $LAME_CFLAGS"
LIBS="$LIBS $LAME_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <lame/lame.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding liblame or finding the wrong"
echo "*** version of liblame. If it is not finding liblame, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means liblame was incorrectly installed"
echo "*** or that you have moved liblame since it was installed." ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
LAME_CFLAGS=""
LAME_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_DEFINE(HAVE_LAME, 1, [Define if you have liblame.])
use_lame="1"
AC_SUBST(LAME_CFLAGS)
AC_SUBST(LAME_LIBS)
rm -f conf.lametest
])
# Configure paths for libOggFLAC
# "Inspired" by ogg.m4
dnl AM_PATH_LIBOGGFLAC([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl Test for libOggFLAC, and define LIBOGGFLAC_CFLAGS and LIBOGGFLAC_LIBS
dnl
AC_DEFUN([AM_PATH_LIBOGGFLAC],
[dnl
dnl Get the cflags and libraries
dnl
AC_ARG_WITH(libOggFLAC,
AS_HELP_STRING([--with-libOggFLAC=PFX],
[prefix where libOggFLAC is installed (optional)]),,
libOggFLAC_prefix="")
AC_ARG_WITH(libOggFLAC-libraries,
AS_HELP_STRING([--with-libOggFLAC-libraries=DIR],
[directory where libOggFLAC library is installed (optional)]),,
libOggFLAC_libraries="")
AC_ARG_WITH(libOggFLAC-includes,
AS_HELP_STRING([--with-libOggFLAC-includes=DIR],
[directory where libOggFLAC header files are installed (optional)]),,
libOggFLAC_includes="")
AC_ARG_ENABLE(libOggFLACtest,
AS_HELP_STRING([--disable-libOggFLACtest],
[do not try to compile and run a test libOggFLAC program]),,
enable_libOggFLACtest=yes)
if test "x$libOggFLAC_libraries" != "x" ; then
LIBOGGFLAC_LIBS="-L$libOggFLAC_libraries"
elif test "x$libOggFLAC_prefix" != "x" ; then
LIBOGGFLAC_LIBS="-L$libOggFLAC_prefix/lib"
elif test "x$prefix" != "xNONE" ; then
LIBOGGFLAC_LIBS="-L$libdir"
fi
LIBOGGFLAC_LIBS="$LIBOGGFLAC_LIBS -lOggFLAC -lFLAC -lm"
if test "x$libOggFLAC_includes" != "x" ; then
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_includes"
elif test "x$libOggFLAC_prefix" != "x" ; then
LIBOGGFLAC_CFLAGS="-I$libOggFLAC_prefix/include"
elif test "x$prefix" != "xNONE"; then
LIBOGGFLAC_CFLAGS="-I$prefix/include"
fi
AC_MSG_CHECKING(for libOggFLAC)
no_libOggFLAC=""
if test "x$enable_libOggFLACtest" = "xyes" ; then
ac_save_CFLAGS="$CFLAGS"
ac_save_CXXFLAGS="$CXXFLAGS"
ac_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
CXXFLAGS="$CXXFLAGS $LIBOGGFLAC_CFLAGS"
LIBS="$LIBS $LIBOGGFLAC_LIBS"
dnl
dnl Now check if the installed libOggFLAC is sufficiently new.
dnl
rm -f conf.libOggFLACtest
AC_TRY_RUN([
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <OggFLAC/stream_decoder.h>
int main ()
{
system("touch conf.libOggFLACtest");
return 0;
}
],, no_libOggFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
if test "x$no_libOggFLAC" = "x" ; then
AC_MSG_RESULT(yes)
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT(no)
if test -f conf.libOggFLACtest ; then
:
else
echo "*** Could not run libOggFLAC test program, checking why..."
CFLAGS="$CFLAGS $LIBOGGFLAC_CFLAGS"
LIBS="$LIBS $LIBOGGFLAC_LIBS"
AC_TRY_LINK([
#include <stdio.h>
#include <OggFLAC/stream_decoder.h>
], [ return 0; ],
[ echo "*** The test program compiled, but did not run. This usually means"
echo "*** that the run-time linker is not finding libOggFLAC or finding the wrong"
echo "*** version of libOggFLAC. If it is not finding libOggFLAC, you'll need to set your"
echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
echo "*** to the installed location Also, make sure you have run ldconfig if that"
echo "*** is required on your system"
echo "***"
echo "*** If you have an old version installed, it is best to remove it, although"
echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
[ echo "*** The test program failed to compile or link. See the file config.log for the"
echo "*** exact error that occured. This usually means libOggFLAC was incorrectly installed"
echo "*** or that you have moved libOggFLAC since it was installed. In the latter case, you"
echo "*** may want to edit the libOggFLAC-config script: $LIBOGGFLAC_CONFIG" ])
CFLAGS="$ac_save_CFLAGS"
LIBS="$ac_save_LIBS"
fi
LIBOGGFLAC_CFLAGS=""
LIBOGGFLAC_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_SUBST(LIBOGGFLAC_CFLAGS)
AC_SUBST(LIBOGGFLAC_LIBS)
rm -f conf.libOggFLACtest
])
dnl
dnl Usage:
dnl AC_CHECK_LIBWRAP([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl
AC_DEFUN([AC_CHECK_LIBWRAP],[
AC_CHECK_HEADERS([tcpd.h],
AC_CHECK_LIB([wrap], [request_init],
[LIBWRAP_CFLAGS=""
LIBWRAP_LDFLAGS="-lwrap"
$1],
$2),
$2)
])
......@@ -4,9 +4,9 @@ AC_DEFUN([MPD_CHECK_FLAG],[
[mpd_check_cflag_$var],[
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main(void) { return 0; }
], [ eval "mpd_check_cflag_$var=yes"
])], [ eval "mpd_check_cflag_$var=yes"
], [ eval "mpd_check_cflag_$var=no" ])
CFLAGS="$save_CFLAGS"
])
......
AC_DEFUN([results], [
dnl This is a hack to allow "with" names, otherwise "enable".
num=`expr $1 : 'with'`
if test "$num" != "0"; then
var="`echo '$'$1`"
else
var="`echo '$'enable_$1`"
fi
printf '('
if eval "test x$var = xyes"; then
printf '+'
elif test -n "$3" && eval "test x$var = x$3"; then
printf '+'
else
printf '-'
fi
printf '%s) ' "$2"
])
#!/usr/bin/env ruby
#
# This script verifies that every source includes config.h first.
# This is very important for consistent Large File Support.
#
def check_file(file)
first = true
file.each_line do |line|
if line =~ /^\#include\s+(\S+)/ then
if $1 == '"config.h"'
unless first
puts "#{file.path}: config.h included too late"
end
else
if first
puts "#{file.path}: config.h missing"
end
end
first = false
end
end
end
def check_path(path)
File.open(path) do |file|
check_file(file)
end
end
if ARGV.empty?
Dir["src/*.c"].each do |path|
check_path(path)
end
Dir["src/*/*.c"].each do |path|
check_path(path)
end
Dir["test/*.c"].each do |path|
check_path(path)
end
else
ARGV.each do |path|
check_path(path)
end
end
......@@ -18,6 +18,7 @@ test -x configure || NOCONFIGURE=1 ./autogen.sh
./configure --prefix=$PREFIX/full \
--disable-dependency-tracking --enable-debug --enable-werror \
--enable-un \
--enable-modplug \
--enable-ao --enable-mikmod --enable-mvp
$MAKE install
$MAKE distclean
......@@ -47,6 +48,7 @@ $MAKE install
$MAKE distclean
# shout: ogg without mp3
# sndfile instead of modplug
./configure --prefix=$PREFIX/shout_ogg \
--disable-dependency-tracking --disable-debug --enable-werror \
--disable-tcp \
......@@ -56,6 +58,7 @@ $MAKE distclean
--enable-shout-ogg --disable-shout-mp3 --disable-lame-encoder \
--disable-ffmpeg --disable-wavpack --disable-mpc --disable-aac \
--disable-flac --enable-vorbis --disable-oggflac --disable-audiofile \
--disable-modplug --enable-sndfile \
--with-zeroconf=no
$MAKE install
$MAKE distclean
......
/* compress.c
* Compressor logic
*
* (c)2007 busybee (http://beesbuzz.biz/
* Licensed under the terms of the LGPL. See the file COPYING for details.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "compress.h"
struct Compressor {
//! The compressor's preferences
struct CompressorConfig prefs;
//! History of the peak values
int *peaks;
//! History of the gain values
int *gain;
//! History of clip amounts
int *clipped;
unsigned int pos;
unsigned int bufsz;
};
struct Compressor *Compressor_new(unsigned int history)
{
struct Compressor *obj = malloc(sizeof(struct Compressor));
obj->prefs.target = TARGET;
obj->prefs.maxgain = GAINMAX;
obj->prefs.smooth = GAINSMOOTH;
obj->peaks = obj->gain = obj->clipped = NULL;
obj->bufsz = 0;
obj->pos = 0;
Compressor_setHistory(obj, history);
return obj;
}
void Compressor_delete(struct Compressor *obj)
{
if (obj->peaks)
free(obj->peaks);
if (obj->gain)
free(obj->gain);
if (obj->clipped)
free(obj->clipped);
free(obj);
}
static int *resizeArray(int *data, int newsz, int oldsz)
{
data = realloc(data, newsz*sizeof(int));
if (newsz > oldsz)
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
return data;
}
void Compressor_setHistory(struct Compressor *obj, unsigned int history)
{
if (!history)
history = BUCKETS;
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
obj->bufsz = history;
}
struct CompressorConfig *Compressor_getConfig(struct Compressor *obj)
{
return &obj->prefs;
}
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
unsigned int count)
{
struct CompressorConfig *prefs = Compressor_getConfig(obj);
int16_t *ap;
unsigned int i;
int *peaks = obj->peaks;
int curGain = obj->gain[obj->pos];
int newGain;
int peakVal = 1;
int peakPos = 0;
int slot = (obj->pos + 1) % obj->bufsz;
int *clipped = obj->clipped + slot;
unsigned int ramp = count;
int delta;
ap = audio;
for (i = 0; i < count; i++)
{
int val = *ap++;
if (val < 0)
val = -val;
if (val > peakVal)
{
peakVal = val;
peakPos = i;
}
}
peaks[slot] = peakVal;
for (i = 0; i < obj->bufsz; i++)
{
if (peaks[i] > peakVal)
{
peakVal = peaks[i];
peakPos = 0;
}
}
//! Determine target gain
newGain = (1 << 10)*prefs->target/peakVal;
//! Adjust the gain with inertia from the previous gain value
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
>> prefs->smooth;
//! Make sure it's no more than the maximum gain value
if (newGain > (prefs->maxgain << 10))
newGain = prefs->maxgain << 10;
//! Make sure it's no less than 1:1
if (newGain < (1 << 10))
newGain = 1 << 10;
//! Make sure the adjusted gain won't cause clipping
if ((peakVal*newGain >> 10) > 32767)
{
newGain = (32767 << 10)/peakVal;
//! Truncate the ramp time
ramp = peakPos;
}
//! Record the new gain
obj->gain[slot] = newGain;
if (!ramp)
ramp = 1;
if (!curGain)
curGain = 1 << 10;
delta = (newGain - curGain) / (int)ramp;
ap = audio;
*clipped = 0;
for (i = 0; i < count; i++)
{
int sample;
//! Amplify the sample
sample = *ap*curGain >> 10;
if (sample < -32768)
{
*clipped += -32768 - sample;
sample = -32768;
} else if (sample > 32767)
{
*clipped += sample - 32767;
sample = 32767;
}
*ap++ = sample;
//! Adjust the gain
if (i < ramp)
curGain += delta;
else
curGain = newGain;
}
obj->pos = slot;
}
/*! compress.h
* interface to audio compression
*
* (c)2007 busybee (http://beesbuzz.biz/)
* Licensed under the terms of the LGPL. See the file COPYING for details.
*/
#ifndef COMPRESS_H
#define COMPRESS_H
#include <stdint.h>
//! Configuration values for the compressor object
struct CompressorConfig {
int target;
int maxgain;
int smooth;
};
struct Compressor;
//! Create a new compressor (use history value of 0 for default)
struct Compressor *Compressor_new(unsigned int history);
//! Delete a compressor
void Compressor_delete(struct Compressor *);
//! Set the history length
void Compressor_setHistory(struct Compressor *, unsigned int history);
//! Get the configuration for a compressor
struct CompressorConfig *Compressor_getConfig(struct Compressor *);
//! Process 16-bit signed data
void Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count);
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed
//! TODO: functions for getting at the peak/gain/clip history buffers (for monitoring)
#endif
/* config.h
** Default values for the configuration, and also a few random debug things
*/
#ifndef AC_CONFIG_H
#define AC_CONFIG_H
/*** Version information ***/
#define ACVERSION "2.0"
/*** Default configuration stuff ***/
#define TARGET 16384 /*!< Target level (on a scale of 0-32767) */
#define GAINMAX 32 /*!< The maximum amount to amplify by */
#define GAINSMOOTH 8 /*!< How much inertia ramping has*/
#define BUCKETS 400 /*!< How long of a history to use by default */
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "aiff.h"
#include <glib.h>
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "ape.h"
#include <glib.h>
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
struct ape_footer {
unsigned char id[8];
uint32_t version;
uint32_t length;
uint32_t count;
unsigned char flags[4];
unsigned char reserved[8];
};
static bool
ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
{
/* determine if file has an apeV2 tag */
struct ape_footer footer;
if (fseek(fp, -(long)sizeof(footer), SEEK_END) ||
fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) ||
memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
GUINT32_FROM_LE(footer.version) != 2000)
return false;
/* find beginning of ape tag */
size_t remaining = GUINT32_FROM_LE(footer.length);
if (remaining <= sizeof(footer) + 10 ||
/* refuse to load more than one megabyte of tag data */
remaining > 1024 * 1024 ||
fseek(fp, -(long)remaining, SEEK_END))
return false;
/* read tag into buffer */
remaining -= sizeof(footer);
assert(remaining > 10);
char *buffer = g_malloc(remaining);
if (fread(buffer, 1, remaining, fp) != remaining)
return false;
/* read tags */
unsigned n = GUINT32_FROM_LE(footer.count);
const char *p = buffer;
while (n-- && remaining > 10) {
size_t size = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
remaining -= 4;
unsigned long flags = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
remaining -= 4;
/* get the key */
const char *key = p;
while (remaining > size && *p != '\0') {
p++;
remaining--;
}
p++;
remaining--;
/* get the value */
if (remaining < size)
break;
if (!callback(flags, key, p, size, ctx))
break;
p += size;
remaining -= size;
}
g_free(buffer);
return true;
}
bool
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx)
{
FILE *fp;
fp = fopen(path_fs, "rb");
if (fp == NULL)
return false;
bool success = ape_scan_internal(fp, callback, ctx);
fclose(fp);
return success;
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_APE_H
#define MPD_APE_H
#include "check.h"
#include <stdbool.h>
#include <stddef.h>
typedef bool (*tag_ape_callback_t)(unsigned long flags, const char *key,
const char *value, size_t value_length,
void *ctx);
/**
* Scans the APE tag values from a file.
*
* @param path_fs the path of the file in filesystem encoding
* @return false if the file could not be opened or if no APE tag is
* present
*/
bool
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx);
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,9 +21,11 @@
* single bz2 archive handling (requires libbz2)
*/
#include "config.h"
#include "archive/bz2_archive_plugin.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "config.h"
#include "refcount.h"
#include <stdint.h>
#include <stddef.h>
......@@ -32,231 +34,246 @@
#include <bzlib.h>
#ifdef HAVE_OLDER_BZIP2
#define BZ2_bzDecompressInit bzDecompressInit
#define BZ2_bzDecompress bzDecompress
#define BZ2_bzDecompressInit bzDecompressInit
#define BZ2_bzDecompress bzDecompress
#endif
#define BZ_BUFSIZE 5000
struct bz2_archive_file {
struct archive_file base;
struct refcount ref;
char *name;
bool reset;
struct input_stream *istream;
};
struct bz2_input_stream {
struct input_stream base;
typedef struct {
char *name;
bool reset;
struct input_stream istream;
int last_bz_result;
int last_parent_result;
bz_stream bzstream;
char *buffer;
} bz2_context;
struct bz2_archive_file *archive;
bool eof;
bz_stream bzstream;
char buffer[5000];
};
static const struct input_plugin bz2_inputplugin;
static inline GQuark
bz2_quark(void)
{
return g_quark_from_static_string("bz2");
}
/* single archive handling allocation helpers */
static bool
bz2_alloc(bz2_context *data)
bz2_alloc(struct bz2_input_stream *data, GError **error_r)
{
int ret;
data->bzstream.bzalloc = NULL;
data->bzstream.bzfree = NULL;
data->bzstream.opaque = NULL;
data->buffer = g_malloc(BZ_BUFSIZE);
data->bzstream.next_in = (void *) data->buffer;
data->bzstream.avail_in = 0;
if (BZ2_bzDecompressInit(&data->bzstream, 0, 0) != BZ_OK) {
g_free(data->buffer);
ret = BZ2_bzDecompressInit(&data->bzstream, 0, 0);
if (ret != BZ_OK) {
g_free(data);
g_set_error(error_r, bz2_quark(), ret,
"BZ2_bzDecompressInit() has failed");
return false;
}
data->last_bz_result = BZ_OK;
data->last_parent_result = 0;
return true;
}
static void
bz2_destroy(bz2_context *data)
bz2_destroy(struct bz2_input_stream *data)
{
BZ2_bzDecompressEnd(&data->bzstream);
g_free(data->buffer);
}
/* archive open && listing routine */
static struct archive_file *
bz2_open(char * pathname)
bz2_open(const char *pathname, GError **error_r)
{
bz2_context *context;
char *name;
struct bz2_archive_file *context;
int len;
context = g_malloc(sizeof(bz2_context));
if (!context) {
return NULL;
}
context = g_malloc(sizeof(*context));
archive_file_init(&context->base, &bz2_archive_plugin);
refcount_init(&context->ref);
//open archive
if (!input_stream_open(&context->istream, pathname)) {
g_warning("failed to open an bzip2 archive %s\n",pathname);
g_free(context);
return NULL;
}
//capture filename
name = strrchr(pathname, '/');
if (name == NULL) {
g_warning("failed to get bzip2 name from %s\n",pathname);
context->istream = input_stream_open(pathname, error_r);
if (context->istream == NULL) {
g_free(context);
return NULL;
}
context->name = g_strdup(name+1);
context->name = g_path_get_basename(pathname);
//remove suffix
len = strlen(context->name);
if (len > 4) {
context->name[len-4] = 0; //remove .bz2 suffix
context->name[len - 4] = 0; //remove .bz2 suffix
}
return (struct archive_file *) context;
return &context->base;
}
static void
bz2_scan_reset(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
context->reset = true;
}
static char *
bz2_scan_next(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
char *name = NULL;
if (context->reset) {
name = context->name;
context->reset = false;
}
return name;
}
static void
bz2_close(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
if (!refcount_dec(&context->ref))
return;
g_free(context->name);
input_stream_close(&context->istream);
input_stream_close(context->istream);
g_free(context);
}
/* single archive handling */
static bool
bz2_open_stream(struct archive_file *file, struct input_stream *is,
G_GNUC_UNUSED const char *path)
static struct input_stream *
bz2_open_stream(struct archive_file *file, const char *path, GError **error_r)
{
bz2_context *context = (bz2_context *) file;
//setup file ops
is->plugin = &bz2_inputplugin;
//insert back reference
is->data = context;
is->seekable = false;
if (!bz2_alloc(context)) {
g_warning("alloc bz2 failed\n");
return false;
struct bz2_archive_file *context = (struct bz2_archive_file *) file;
struct bz2_input_stream *bis = g_new(struct bz2_input_stream, 1);
input_stream_init(&bis->base, &bz2_inputplugin, path);
bis->archive = context;
bis->base.ready = true;
bis->base.seekable = false;
if (!bz2_alloc(bis, error_r)) {
input_stream_deinit(&bis->base);
g_free(bis);
return NULL;
}
return true;
bis->eof = false;
refcount_inc(&context->ref);
return &bis->base;
}
static void
bz2_is_close(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->data;
bz2_destroy(context);
is->data = NULL;
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
bz2_close((struct archive_file *)context);
bz2_destroy(bis);
bz2_close(&bis->archive->base);
input_stream_deinit(&bis->base);
g_free(bis);
}
static int
bz2_fillbuffer(bz2_context *context,
size_t numBytes)
static bool
bz2_fillbuffer(struct bz2_input_stream *bis, GError **error_r)
{
size_t count;
bz_stream *bzstream;
bzstream = &context->bzstream;
bzstream = &bis->bzstream;
if (bzstream->avail_in > 0)
return 0;
return true;
count = input_stream_read(&context->istream,
context->buffer, BZ_BUFSIZE);
if (count == 0) {
if (bzstream->avail_out == numBytes)
return -1;
if (!input_stream_eof(&context->istream))
context->last_parent_result = 1;
} else {
bzstream->next_in = context->buffer;
bzstream->avail_in = count;
}
count = input_stream_read(bis->archive->istream,
bis->buffer, sizeof(bis->buffer),
error_r);
if (count == 0)
return false;
return 0;
bzstream->next_in = bis->buffer;
bzstream->avail_in = count;
return true;
}
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 length,
GError **error_r)
{
bz2_context *context = (bz2_context *) is->data;
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
bz_stream *bzstream;
int bz_result;
size_t numBytes = size;
size_t bytesRead = 0;
size_t nbytes = 0;
if (context->last_bz_result != BZ_OK)
return 0;
if (context->last_parent_result != 0)
if (bis->eof)
return 0;
bzstream = &context->bzstream;
bzstream = &bis->bzstream;
bzstream->next_out = ptr;
bzstream->avail_out = numBytes;
bzstream->avail_out = length;
while (bzstream->avail_out != 0) {
if (bz2_fillbuffer(context, numBytes) != 0)
break;
do {
if (!bz2_fillbuffer(bis, error_r))
return 0;
bz_result = BZ2_bzDecompress(bzstream);
if (context->last_bz_result != BZ_OK
&& bzstream->avail_out == numBytes) {
context->last_bz_result = bz_result;
if (bz_result == BZ_STREAM_END) {
bis->eof = true;
break;
}
if (bz_result == BZ_STREAM_END) {
context->last_bz_result = bz_result;
break;
if (bz_result != BZ_OK) {
g_set_error(error_r, bz2_quark(), bz_result,
"BZ2_bzDecompress() has failed");
return 0;
}
}
} while (bzstream->avail_out == length);
bytesRead = numBytes - bzstream->avail_out;
is->offset += bytesRead;
nbytes = length - bzstream->avail_out;
is->offset += nbytes;
return bytesRead;
return nbytes;
}
static bool
bz2_is_eof(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->data;
if (context->last_bz_result == BZ_STREAM_END) {
return true;
}
struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
return false;
return bis->eof;
}
/* exported structures */
......@@ -272,7 +289,7 @@ static const struct input_plugin bz2_inputplugin = {
.eof = bz2_is_eof,
};
const struct archive_plugin bz2_plugin = {
const struct archive_plugin bz2_archive_plugin = {
.name = "bz2",
.open = bz2_open,
.scan_reset = bz2_scan_reset,
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,9 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef LASTFM_INPUT_PLUGIN_H
#define LASTFM_INPUT_PLUGIN_H
#ifndef MPD_ARCHIVE_BZ2_H
#define MPD_ARCHIVE_BZ2_H
extern const struct input_plugin lastfm_input_plugin;
extern const struct archive_plugin bz2_archive_plugin;
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,8 +21,11 @@
* iso archive handling (requires cdio, and iso9660)
*/
#include "config.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "refcount.h"
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
......@@ -32,21 +35,28 @@
#define CEILING(x, y) ((x+(y-1))/y)
typedef struct {
struct iso9660_archive_file {
struct archive_file base;
struct refcount ref;
iso9660_t *iso;
iso9660_stat_t *statbuf;
size_t cur_ofs;
size_t max_blocks;
GSList *list;
GSList *iter;
} iso_context;
};
static const struct input_plugin iso9660_input_plugin;
static const struct input_plugin iso_inputplugin;
static inline GQuark
iso9660_quark(void)
{
return g_quark_from_static_string("iso9660");
}
/* archive open && listing routine */
static void
listdir_recur(const char *psz_path, iso_context *context)
listdir_recur(const char *psz_path, struct iso9660_archive_file *context)
{
iso9660_t *iso = context->iso;
CdioList_t *entlist;
......@@ -80,36 +90,45 @@ listdir_recur(const char *psz_path, iso_context *context)
}
static struct archive_file *
iso_open(char * pathname)
iso9660_archive_open(const char *pathname, GError **error_r)
{
iso_context *context = g_malloc(sizeof(iso_context));
struct iso9660_archive_file *context =
g_new(struct iso9660_archive_file, 1);
archive_file_init(&context->base, &iso9660_archive_plugin);
refcount_init(&context->ref);
context->list = NULL;
/* open archive */
context->iso = iso9660_open (pathname);
if (context->iso == NULL) {
g_warning("iso %s open failed\n", pathname);
g_set_error(error_r, iso9660_quark(), 0,
"Failed to open ISO9660 file %s", pathname);
return NULL;
}
listdir_recur("/", context);
return (struct archive_file *)context;
return &context->base;
}
static void
iso_scan_reset(struct archive_file *file)
iso9660_archive_scan_reset(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
//reset iterator
context->iter = context->list;
}
static char *
iso_scan_next(struct archive_file *file)
iso9660_archive_scan_next(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
......@@ -120,10 +139,15 @@ iso_scan_next(struct archive_file *file)
}
static void
iso_close(struct archive_file *file)
iso9660_archive_close(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
GSList *tmp;
if (!refcount_dec(&context->ref))
return;
if (context->list) {
//free list
for (tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
......@@ -138,46 +162,69 @@ iso_close(struct archive_file *file)
/* single archive handling */
static bool
iso_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
struct iso9660_input_stream {
struct input_stream base;
struct iso9660_archive_file *archive;
iso9660_stat_t *statbuf;
size_t max_blocks;
};
static struct input_stream *
iso9660_archive_open_stream(struct archive_file *file,
const char *pathname, GError **error_r)
{
iso_context *context = (iso_context *) file;
//setup file ops
is->plugin = &iso_inputplugin;
//insert back reference
is->data = context;
struct iso9660_archive_file *context =
(struct iso9660_archive_file *)file;
struct iso9660_input_stream *iis;
iis = g_new(struct iso9660_input_stream, 1);
input_stream_init(&iis->base, &iso9660_input_plugin, pathname);
iis->archive = context;
iis->statbuf = iso9660_ifs_stat_translate(context->iso, pathname);
if (iis->statbuf == NULL) {
g_free(iis);
g_set_error(error_r, iso9660_quark(), 0,
"not found in the ISO file: %s", pathname);
return NULL;
}
iis->base.ready = true;
//we are not seekable
is->seekable = false;
iis->base.seekable = false;
context->statbuf = iso9660_ifs_stat_translate (context->iso, pathname);
iis->base.size = iis->statbuf->size;
if (context->statbuf == NULL) {
g_warning("file %s not found in iso\n", pathname);
return false;
}
context->cur_ofs = 0;
context->max_blocks = CEILING(context->statbuf->size, ISO_BLOCKSIZE);
return true;
iis->max_blocks = CEILING(iis->statbuf->size, ISO_BLOCKSIZE);
refcount_inc(&context->ref);
return &iis->base;
}
static void
iso_is_close(struct input_stream *is)
iso9660_input_close(struct input_stream *is)
{
iso_context *context = (iso_context *) is->data;
g_free(context->statbuf);
struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
iso_close((struct archive_file *)context);
g_free(iis->statbuf);
iso9660_archive_close(&iis->archive->base);
input_stream_deinit(&iis->base);
g_free(iis);
}
static size_t
iso_is_read(struct input_stream *is, void *ptr, size_t size)
iso9660_input_read(struct input_stream *is, void *ptr, size_t size, GError **error_r)
{
iso_context *context = (iso_context *) is->data;
struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
int toread, readed = 0;
int no_blocks, cur_block;
size_t left_bytes = context->statbuf->size - context->cur_ofs;
size_t left_bytes = iis->statbuf->size - is->offset;
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
......@@ -190,50 +237,51 @@ iso_is_read(struct input_stream *is, void *ptr, size_t size)
}
if (no_blocks > 0) {
cur_block = context->cur_ofs / ISO_BLOCKSIZE;
cur_block = is->offset / ISO_BLOCKSIZE;
readed = iso9660_iso_seek_read (context->iso, ptr,
context->statbuf->lsn + cur_block, no_blocks);
readed = iso9660_iso_seek_read (iis->archive->iso, ptr,
iis->statbuf->lsn + cur_block, no_blocks);
if (readed != no_blocks * ISO_BLOCKSIZE) {
g_warning("error reading ISO file at lsn %lu\n",
(long unsigned int) cur_block );
return -1;
g_set_error(error_r, iso9660_quark(), 0,
"error reading ISO file at lsn %lu",
(long unsigned int) cur_block);
return 0;
}
if (left_bytes < size) {
readed = left_bytes;
}
context->cur_ofs += readed;
is->offset += readed;
}
return readed;
}
static bool
iso_is_eof(struct input_stream *is)
iso9660_input_eof(struct input_stream *is)
{
iso_context *context = (iso_context *) is->data;
return (context->cur_ofs == context->statbuf->size);
return is->offset == is->size;
}
/* exported structures */
static const char *const iso_extensions[] = {
static const char *const iso9660_archive_extensions[] = {
"iso",
NULL
};
static const struct input_plugin iso_inputplugin = {
.close = iso_is_close,
.read = iso_is_read,
.eof = iso_is_eof,
static const struct input_plugin iso9660_input_plugin = {
.close = iso9660_input_close,
.read = iso9660_input_read,
.eof = iso9660_input_eof,
};
const struct archive_plugin iso_plugin = {
const struct archive_plugin iso9660_archive_plugin = {
.name = "iso",
.open = iso_open,
.scan_reset = iso_scan_reset,
.scan_next = iso_scan_next,
.open_stream = iso_open_stream,
.close = iso_close,
.suffixes = iso_extensions
.open = iso9660_archive_open,
.scan_reset = iso9660_archive_scan_reset,
.scan_next = iso9660_archive_scan_next,
.open_stream = iso9660_archive_open_stream,
.close = iso9660_archive_close,
.suffixes = iso9660_archive_extensions
};
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,15 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_BUFFER_2_ARRAY_H
#define MPD_BUFFER_2_ARRAY_H
#ifndef MPD_ARCHIVE_ISO9660_H
#define MPD_ARCHIVE_ISO9660_H
/* tokenizes up to max elements in buffer (a null-terminated string) and
* stores the result in array (which must be capable of holding up to
* max elements). Tokenization is based on C string quoting rules.
* The arguments buffer and array are modified.
* Returns the number of elements tokenized.
*/
int buffer2array(char *buffer, char *array[], const int max);
extern const struct archive_plugin iso9660_archive_plugin;
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,37 +21,52 @@
* zip archive handling (requires zziplib)
*/
#include "config.h"
#include "archive/zzip_archive_plugin.h"
#include "archive_api.h"
#include "archive_api.h"
#include "input_plugin.h"
#include "refcount.h"
#include <zzip/zzip.h>
#include <glib.h>
#include <string.h>
typedef struct {
struct zzip_archive {
struct archive_file base;
struct refcount ref;
ZZIP_DIR *dir;
ZZIP_FILE *file;
size_t length;
GSList *list;
GSList *iter;
} zip_context;
};
static const struct input_plugin zip_inputplugin;
static const struct input_plugin zzip_input_plugin;
static inline GQuark
zzip_quark(void)
{
return g_quark_from_static_string("zzip");
}
/* archive open && listing routine */
static struct archive_file *
zip_open(char * pathname)
zzip_archive_open(const char *pathname, GError **error_r)
{
zip_context *context = g_malloc(sizeof(zip_context));
struct zzip_archive *context = g_malloc(sizeof(*context));
ZZIP_DIRENT dirent;
archive_file_init(&context->base, &zzip_archive_plugin);
refcount_init(&context->ref);
// open archive
context->list = NULL;
context->dir = zzip_dir_open(pathname, NULL);
if (context->dir == NULL) {
g_warning("zipfile %s open failed\n", pathname);
g_set_error(error_r, zzip_quark(), 0,
"Failed to open ZIP file %s", pathname);
return NULL;
}
......@@ -63,21 +78,21 @@ zip_open(char * pathname)
}
}
return (struct archive_file *)context;
return &context->base;
}
static void
zip_scan_reset(struct archive_file *file)
zzip_archive_scan_reset(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
struct zzip_archive *context = (struct zzip_archive *) file;
//reset iterator
context->iter = context->list;
}
static char *
zip_scan_next(struct archive_file *file)
zzip_archive_scan_next(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
struct zzip_archive *context = (struct zzip_archive *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
......@@ -88,9 +103,13 @@ zip_scan_next(struct archive_file *file)
}
static void
zip_close(struct archive_file *file)
zzip_archive_close(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
struct zzip_archive *context = (struct zzip_archive *) file;
if (!refcount_dec(&context->ref))
return;
if (context->list) {
//free list
for (GSList *tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
......@@ -105,66 +124,93 @@ zip_close(struct archive_file *file)
/* single archive handling */
static bool
zip_open_stream(struct archive_file *file, struct input_stream *is,
const char *pathname)
struct zzip_input_stream {
struct input_stream base;
struct zzip_archive *archive;
ZZIP_FILE *file;
};
static struct input_stream *
zzip_archive_open_stream(struct archive_file *file,
const char *pathname, GError **error_r)
{
zip_context *context = (zip_context *) file;
struct zzip_archive *context = (struct zzip_archive *) file;
struct zzip_input_stream *zis;
ZZIP_STAT z_stat;
//setup file ops
is->plugin = &zip_inputplugin;
//insert back reference
is->data = context;
//we are seekable (but its not recommendent to do so)
is->seekable = true;
zis = g_new(struct zzip_input_stream, 1);
input_stream_init(&zis->base, &zzip_input_plugin, pathname);
context->file = zzip_file_open(context->dir, pathname, 0);
if (!context->file) {
g_warning("file %s not found in the zipfile\n", pathname);
return false;
zis->archive = context;
zis->file = zzip_file_open(context->dir, pathname, 0);
if (zis->file == NULL) {
g_free(zis);
g_set_error(error_r, zzip_quark(), 0,
"not found in the ZIP file: %s", pathname);
return NULL;
}
zzip_file_stat(context->file, &z_stat);
context->length = z_stat.st_size;
return true;
zis->base.ready = true;
//we are seekable (but its not recommendent to do so)
zis->base.seekable = true;
zzip_file_stat(zis->file, &z_stat);
zis->base.size = z_stat.st_size;
refcount_inc(&context->ref);
return &zis->base;
}
static void
zip_is_close(struct input_stream *is)
zzip_input_close(struct input_stream *is)
{
zip_context *context = (zip_context *) is->data;
zzip_file_close (context->file);
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
zip_close((struct archive_file *)context);
zzip_file_close(zis->file);
zzip_archive_close(&zis->archive->base);
input_stream_deinit(&zis->base);
g_free(zis);
}
static size_t
zip_is_read(struct input_stream *is, void *ptr, size_t size)
zzip_input_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
zip_context *context = (zip_context *) is->data;
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
int ret;
ret = zzip_file_read(context->file, ptr, size);
ret = zzip_file_read(zis->file, ptr, size);
if (ret < 0) {
g_warning("error %d reading zipfile\n", ret);
g_set_error(error_r, zzip_quark(), ret,
"zzip_file_read() has failed");
return 0;
}
is->offset = zzip_tell(zis->file);
return ret;
}
static bool
zip_is_eof(struct input_stream *is)
zzip_input_eof(struct input_stream *is)
{
zip_context *context = (zip_context *) is->data;
return ((size_t) zzip_tell(context->file) == context->length);
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
return (goffset)zzip_tell(zis->file) == is->size;
}
static bool
zip_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
zzip_input_seek(struct input_stream *is,
goffset offset, int whence, GError **error_r)
{
zip_context *context = (zip_context *) is->data;
zzip_off_t ofs = zzip_seek(context->file, offset, whence);
struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
zzip_off_t ofs = zzip_seek(zis->file, offset, whence);
if (ofs != -1) {
g_set_error(error_r, zzip_quark(), ofs,
"zzip_seek() has failed");
is->offset = ofs;
return true;
}
......@@ -173,24 +219,24 @@ zip_is_seek(G_GNUC_UNUSED struct input_stream *is,
/* exported structures */
static const char *const zip_extensions[] = {
static const char *const zzip_archive_extensions[] = {
"zip",
NULL
};
static const struct input_plugin zip_inputplugin = {
.close = zip_is_close,
.read = zip_is_read,
.eof = zip_is_eof,
.seek = zip_is_seek,
static const struct input_plugin zzip_input_plugin = {
.close = zzip_input_close,
.read = zzip_input_read,
.eof = zzip_input_eof,
.seek = zzip_input_seek,
};
const struct archive_plugin zip_plugin = {
.name = "zip",
.open = zip_open,
.scan_reset = zip_scan_reset,
.scan_next = zip_scan_next,
.open_stream = zip_open_stream,
.close = zip_close,
.suffixes = zip_extensions
const struct archive_plugin zzip_archive_plugin = {
.name = "zzip",
.open = zzip_archive_open,
.scan_reset = zzip_archive_scan_reset,
.scan_next = zzip_archive_scan_next,
.open_stream = zzip_archive_open_stream,
.close = zzip_archive_close,
.suffixes = zzip_archive_extensions
};
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,18 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_NORMALIZE_H
#define MPD_NORMALIZE_H
#ifndef MPD_ARCHIVE_ZZIP_H
#define MPD_ARCHIVE_ZZIP_H
struct audio_format;
extern const struct archive_plugin zzip_archive_plugin;
extern int normalizationEnabled;
void initNormalization(void);
void finishNormalization(void);
void normalizeData(char *buffer, int bufferSize,
const struct audio_format *format);
#endif /* !NORMALIZE_H */
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,6 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "archive_api.h"
#include <stdio.h>
#include <string.h>
......@@ -26,8 +29,6 @@
#include <errno.h>
#include <glib.h>
#include "archive_api.h"
/**
*
* archive_lookup is used to determine if part of pathname refers to an regular
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -27,72 +27,11 @@
*/
#include "archive_internal.h"
#include "archive_plugin.h"
#include "input_stream.h"
#include <stdbool.h>
struct archive_file;
struct archive_plugin {
const char *name;
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one this must false if there is an error and
* true otherwise
*/
bool (*init)(void);
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one
*/
void (*finish)(void);
/**
* tryes to open archive file and associates handle with archive
* returns pointer to handle used is all operations with this archive
* or NULL when opening fails
*/
struct archive_file *(*open)(char * pathname);
/**
* reset routine will move current read index in archive to default
* position and then the filenames from archives can be read
* via scan_next routine
*/
void (*scan_reset)(struct archive_file *);
/**
* the read method will return corresponding files from archive
* (as pathnames) and move read index to next file. When there is no
* next file it return NULL.
*/
char *(*scan_next)(struct archive_file *);
/**
* Opens an input_stream of a file within the archive.
*
* If this function succeeds, then the #input_stream "owns"
* the archive file and will automatically close it.
*
* @param path the path within the archive
*/
bool (*open_stream)(struct archive_file *, struct input_stream *is,
const char *path);
/**
* closes archive file.
*/
void (*close)(struct archive_file *);
/**
* suffixes handled by this plugin.
* last element in these arrays must always be a NULL
*/
const char *const*suffixes;
};
bool archive_lookup(char *pathname, char **archive, char **inpath, char **suffix);
#endif
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,7 +21,14 @@
#define MPD_ARCHIVE_INTERNAL_H
struct archive_file {
int placeholder;
const struct archive_plugin *plugin;
};
static inline void
archive_file_init(struct archive_file *archive_file,
const struct archive_plugin *plugin)
{
archive_file->plugin = plugin;
}
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,50 +17,44 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "archive_list.h"
#include "archive_api.h"
#include "archive_plugin.h"
#include "utils.h"
#include "config.h"
#include "archive/bz2_archive_plugin.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive/zzip_archive_plugin.h"
#include <string.h>
#include <glib.h>
extern const struct archive_plugin bz2_plugin;
extern const struct archive_plugin zip_plugin;
extern const struct archive_plugin iso_plugin;
static const struct archive_plugin *const archive_plugins[] = {
#ifdef HAVE_BZ2
&bz2_plugin,
&bz2_archive_plugin,
#endif
#ifdef HAVE_ZIP
&zip_plugin,
#ifdef HAVE_ZZIP
&zzip_archive_plugin,
#endif
#ifdef HAVE_ISO
&iso_plugin,
#ifdef HAVE_ISO9660
&iso9660_archive_plugin,
#endif
NULL
};
enum {
num_archive_plugins = G_N_ELEMENTS(archive_plugins)-1,
};
/** which plugins have been initialized successfully? */
static bool archive_plugins_enabled[num_archive_plugins+1];
static bool archive_plugins_enabled[G_N_ELEMENTS(archive_plugins) - 1];
const struct archive_plugin *
archive_plugin_from_suffix(const char *suffix)
{
unsigned i;
if (suffix == NULL)
return NULL;
for (i=0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
stringFoundInStringArray(plugin->suffixes, suffix)) {
plugin->suffixes != NULL &&
string_array_contains(plugin->suffixes, suffix)) {
++i;
return plugin;
}
......@@ -71,7 +65,7 @@ archive_plugin_from_suffix(const char *suffix)
const struct archive_plugin *
archive_plugin_from_name(const char *name)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
strcmp(plugin->name, name) == 0)
......@@ -84,7 +78,7 @@ void archive_plugin_print_all_suffixes(FILE * fp)
{
const char *const*suffixes;
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (!archive_plugins_enabled[i])
continue;
......@@ -101,7 +95,7 @@ void archive_plugin_print_all_suffixes(FILE * fp)
void archive_plugin_init_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (plugin->init == NULL || archive_plugins[i]->init())
archive_plugins_enabled[i] = true;
......@@ -110,7 +104,7 @@ void archive_plugin_init_all(void)
void archive_plugin_deinit_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
for (unsigned i = 0; archive_plugins[i] != NULL; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] && plugin->finish != NULL)
archive_plugins[i]->finish();
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,8 +20,6 @@
#ifndef MPD_ARCHIVE_LIST_H
#define MPD_ARCHIVE_LIST_H
#include "archive_api.h"
#include <stdio.h>
struct archive_plugin;
......
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "archive_plugin.h"
#include "archive_internal.h"
#include <assert.h>
struct archive_file *
archive_file_open(const struct archive_plugin *plugin, const char *path,
GError **error_r)
{
struct archive_file *file;
assert(plugin != NULL);
assert(plugin->open != NULL);
assert(path != NULL);
assert(error_r == NULL || *error_r == NULL);
file = plugin->open(path, error_r);
if (file != NULL) {
assert(file->plugin != NULL);
assert(file->plugin->close != NULL);
assert(file->plugin->scan_reset != NULL);
assert(file->plugin->scan_next != NULL);
assert(file->plugin->open_stream != NULL);
assert(error_r == NULL || *error_r == NULL);
} else {
assert(error_r == NULL || *error_r != NULL);
}
return file;
}
void
archive_file_close(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->close != NULL);
file->plugin->close(file);
}
void
archive_file_scan_reset(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->scan_reset != NULL);
assert(file->plugin->scan_next != NULL);
file->plugin->scan_reset(file);
}
char *
archive_file_scan_next(struct archive_file *file)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->scan_next != NULL);
return file->plugin->scan_next(file);
}
struct input_stream *
archive_file_open_stream(struct archive_file *file,
const char *path, GError **error_r)
{
assert(file != NULL);
assert(file->plugin != NULL);
assert(file->plugin->open_stream != NULL);
return file->plugin->open_stream(file, path, error_r);
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ARCHIVE_PLUGIN_H
#define MPD_ARCHIVE_PLUGIN_H
#include <glib.h>
#include <stdbool.h>
struct input_stream;
struct archive_file;
struct archive_plugin {
const char *name;
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one this must false if there is an error and
* true otherwise
*/
bool (*init)(void);
/**
* optional, set this to NULL if the archive plugin doesn't
* have/need one
*/
void (*finish)(void);
/**
* tryes to open archive file and associates handle with archive
* returns pointer to handle used is all operations with this archive
* or NULL when opening fails
*/
struct archive_file *(*open)(const char *path_fs, GError **error_r);
/**
* reset routine will move current read index in archive to default
* position and then the filenames from archives can be read
* via scan_next routine
*/
void (*scan_reset)(struct archive_file *);
/**
* the read method will return corresponding files from archive
* (as pathnames) and move read index to next file. When there is no
* next file it return NULL.
*/
char *(*scan_next)(struct archive_file *);
/**
* Opens an input_stream of a file within the archive.
*
* @param path the path within the archive
* @param error_r location to store the error occuring, or
* NULL to ignore errors
*/
struct input_stream *(*open_stream)(struct archive_file *af,
const char *path,
GError **error_r);
/**
* closes archive file.
*/
void (*close)(struct archive_file *);
/**
* suffixes handled by this plugin.
* last element in these arrays must always be a NULL
*/
const char *const*suffixes;
};
struct archive_file *
archive_file_open(const struct archive_plugin *plugin, const char *path,
GError **error_r);
void
archive_file_close(struct archive_file *file);
void
archive_file_scan_reset(struct archive_file *file);
char *
archive_file_scan_next(struct archive_file *file);
struct input_stream *
archive_file_open_stream(struct archive_file *file,
const char *path, GError **error_r);
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "audio.h"
#include "audio_format.h"
#include "audio_parser.h"
......@@ -24,6 +25,7 @@
#include "output_plugin.h"
#include "output_all.h"
#include "conf.h"
#include "mpd_error.h"
#include <glib.h>
......@@ -35,9 +37,8 @@ static struct audio_format configured_audio_format;
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
struct audio_format *outAudioFormat)
{
*outAudioFormat = audio_format_defined(&configured_audio_format)
? configured_audio_format
: *inAudioFormat;
*outAudioFormat = *inAudioFormat;
audio_format_mask_apply(outAudioFormat, &configured_audio_format);
}
void initAudioConfig(void)
......@@ -46,17 +47,13 @@ void initAudioConfig(void)
GError *error = NULL;
bool ret;
if (NULL == param || NULL == param->value)
if (param == NULL)
return;
ret = audio_format_parse(&configured_audio_format, param->value,
&error);
true, &error);
if (!ret)
g_error("error parsing \"%s\" at line %i: %s",
CONF_AUDIO_OUTPUT_FORMAT, param->line, error->message);
}
void finishAudioConfig(void)
{
audio_format_clear(&configured_audio_format);
MPD_ERROR("error parsing \"%s\" at line %i: %s",
CONF_AUDIO_OUTPUT_FORMAT, param->line,
error->message);
}
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -30,6 +30,4 @@ void getOutputAudioFormat(const struct audio_format *inFormat,
/* make sure initPlayerData is called before this function!! */
void initAudioConfig(void);
void finishAudioConfig(void);
#endif
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "audio_check.h"
#include "audio_format.h"
#include <assert.h>
bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r)
{
if (!audio_valid_sample_rate(sample_rate)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid sample rate: %lu", sample_rate);
return false;
}
return true;
}
bool
audio_check_sample_format(enum sample_format sample_format, GError **error_r)
{
if (!audio_valid_sample_format(sample_format)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid sample format: %u", sample_format);
return false;
}
return true;
}
bool
audio_check_channel_count(unsigned channels, GError **error_r)
{
if (!audio_valid_channel_count(channels)) {
g_set_error(error_r, audio_format_quark(), 0,
"Invalid channel count: %u", channels);
return false;
}
return true;
}
bool
audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
enum sample_format sample_format, unsigned channels,
GError **error_r)
{
if (audio_check_sample_rate(sample_rate, error_r) &&
audio_check_sample_format(sample_format, error_r) &&
audio_check_channel_count(channels, error_r)) {
audio_format_init(af, sample_rate, sample_format, channels);
assert(audio_format_valid(af));
return true;
} else
return false;
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_AUDIO_CHECK_H
#define MPD_AUDIO_CHECK_H
#include "audio_format.h"
#include <glib.h>
#include <stdbool.h>
/**
* The GLib quark used for errors reported by this library.
*/
static inline GQuark
audio_format_quark(void)
{
return g_quark_from_static_string("audio_format");
}
bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
bool
audio_check_sample_format(enum sample_format, GError **error_r);
bool
audio_check_channel_count(unsigned sample_format, GError **error_r);
/**
* Wrapper for audio_format_init(), which checks all attributes.
*/
bool
audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
enum sample_format sample_format, unsigned channels,
GError **error_r);
#endif
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "audio_format.h"
#include <assert.h>
#include <stdio.h>
#if G_BYTE_ORDER == G_BIG_ENDIAN
#define REVERSE_ENDIAN_SUFFIX "_le"
#else
#define REVERSE_ENDIAN_SUFFIX "_be"
#endif
const char *
sample_format_to_string(enum sample_format format)
{
switch (format) {
case SAMPLE_FORMAT_UNDEFINED:
return "?";
case SAMPLE_FORMAT_S8:
return "8";
case SAMPLE_FORMAT_S16:
return "16";
case SAMPLE_FORMAT_S24:
return "24_3";
case SAMPLE_FORMAT_S24_P32:
return "24";
case SAMPLE_FORMAT_S32:
return "32";
}
/* unreachable */
assert(false);
return "?";
}
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s)
{
assert(af != NULL);
assert(s != NULL);
snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
af->sample_rate, sample_format_to_string(af->format),
af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
af->channels);
return s->buffer;
}
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -23,25 +23,123 @@
#include <stdint.h>
#include <stdbool.h>
enum sample_format {
SAMPLE_FORMAT_UNDEFINED = 0,
SAMPLE_FORMAT_S8,
SAMPLE_FORMAT_S16,
/**
* Signed 24 bit integer samples, without padding.
*/
SAMPLE_FORMAT_S24,
/**
* Signed 24 bit integer samples, packed in 32 bit integers
* (the most significant byte is filled with the sign bit).
*/
SAMPLE_FORMAT_S24_P32,
SAMPLE_FORMAT_S32,
};
/**
* This structure describes the format of a raw PCM stream.
*/
struct audio_format {
/**
* The sample rate in Hz. A better name for this attribute is
* "frame rate", because technically, you have two samples per
* frame in stereo sound.
*/
uint32_t sample_rate;
uint8_t bits;
/**
* The format samples are stored in. See the #sample_format
* enum for valid values.
*/
uint8_t format;
/**
* The number of channels. Only mono (1) and stereo (2) are
* fully supported currently.
*/
uint8_t channels;
/**
* If zero, then samples are stored in host byte order. If
* nonzero, then samples are stored in the reverse host byte
* order.
*/
uint8_t reverse_endian;
};
/**
* Buffer for audio_format_string().
*/
struct audio_format_string {
char buffer[24];
};
/**
* Clears the #audio_format object, i.e. sets all attributes to an
* undefined (invalid) value.
*/
static inline void audio_format_clear(struct audio_format *af)
{
af->sample_rate = 0;
af->bits = 0;
af->format = SAMPLE_FORMAT_UNDEFINED;
af->channels = 0;
af->reverse_endian = 0;
}
/**
* Initializes an #audio_format object, i.e. sets all
* attributes to valid values.
*/
static inline void audio_format_init(struct audio_format *af,
uint32_t sample_rate,
enum sample_format format, uint8_t channels)
{
af->sample_rate = sample_rate;
af->format = (uint8_t)format;
af->channels = channels;
af->reverse_endian = 0;
}
/**
* Checks whether the specified #audio_format object has a defined
* value.
*/
static inline bool audio_format_defined(const struct audio_format *af)
{
return af->sample_rate != 0;
}
/**
* Checks whether the specified #audio_format object is full, i.e. all
* attributes are defined. This is more complete than
* audio_format_defined(), but slower.
*/
static inline bool
audio_format_fully_defined(const struct audio_format *af)
{
return af->sample_rate != 0 && af->format != SAMPLE_FORMAT_UNDEFINED &&
af->channels != 0;
}
/**
* Checks whether the specified #audio_format object has at least one
* defined value.
*/
static inline bool
audio_format_mask_defined(const struct audio_format *af)
{
return af->sample_rate != 0 || af->format != SAMPLE_FORMAT_UNDEFINED ||
af->channels != 0;
}
/**
* Checks whether the sample rate is valid.
*
* @param sample_rate the sample rate in Hz
......@@ -58,9 +156,21 @@ audio_valid_sample_rate(unsigned sample_rate)
* @param bits the number of significant bits per sample
*/
static inline bool
audio_valid_sample_format(unsigned bits)
audio_valid_sample_format(enum sample_format format)
{
return bits == 16 || bits == 24 || bits == 32 || bits == 8;
switch (format) {
case SAMPLE_FORMAT_S8:
case SAMPLE_FORMAT_S16:
case SAMPLE_FORMAT_S24:
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
return true;
case SAMPLE_FORMAT_UNDEFINED:
break;
}
return false;
}
/**
......@@ -79,16 +189,44 @@ audio_valid_channel_count(unsigned channels)
static inline bool audio_format_valid(const struct audio_format *af)
{
return audio_valid_sample_rate(af->sample_rate) &&
audio_valid_sample_format(af->bits) &&
audio_valid_sample_format((enum sample_format)af->format) &&
audio_valid_channel_count(af->channels);
}
/**
* Returns false if the format mask is not valid for playback with
* MPD. This function performs some basic validity checks.
*/
static inline bool audio_format_mask_valid(const struct audio_format *af)
{
return (af->sample_rate == 0 ||
audio_valid_sample_rate(af->sample_rate)) &&
(af->format == SAMPLE_FORMAT_UNDEFINED ||
audio_valid_sample_format((enum sample_format)af->format)) &&
(af->channels == 0 || audio_valid_channel_count(af->channels));
}
static inline bool audio_format_equals(const struct audio_format *a,
const struct audio_format *b)
{
return a->sample_rate == b->sample_rate &&
a->bits == b->bits &&
a->channels == b->channels;
a->format == b->format &&
a->channels == b->channels &&
a->reverse_endian == b->reverse_endian;
}
static inline void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask)
{
if (mask->sample_rate != 0)
af->sample_rate = mask->sample_rate;
if (mask->format != SAMPLE_FORMAT_UNDEFINED)
af->format = mask->format;
if (mask->channels != 0)
af->channels = mask->channels;
}
/**
......@@ -96,28 +234,65 @@ static inline bool audio_format_equals(const struct audio_format *a,
*/
static inline unsigned audio_format_sample_size(const struct audio_format *af)
{
if (af->bits <= 8)
switch (af->format) {
case SAMPLE_FORMAT_S8:
return 1;
else if (af->bits <= 16)
case SAMPLE_FORMAT_S16:
return 2;
else
case SAMPLE_FORMAT_S24:
return 3;
case SAMPLE_FORMAT_S24_P32:
case SAMPLE_FORMAT_S32:
return 4;
case SAMPLE_FORMAT_UNDEFINED:
break;
}
return 0;
}
/**
* Returns the size of each full frame in bytes.
*/
static inline unsigned
audio_format_frame_size(const struct audio_format *af)
{
return audio_format_sample_size(af) * af->channels;
}
/**
* Returns the floating point factor which converts a time span to a
* storage size in bytes.
*/
static inline double audio_format_time_to_size(const struct audio_format *af)
{
return af->sample_rate * audio_format_frame_size(af);
}
static inline double audioFormatSizeToTime(const struct audio_format *af)
{
return 1.0 / audio_format_time_to_size(af);
}
/**
* Renders a #sample_format enum into a string, e.g. for printing it
* in a log file.
*
* @param format a #sample_format enum value
* @return the string
*/
const char *
sample_format_to_string(enum sample_format format);
/**
* Renders the #audio_format object into a string, e.g. for printing
* it in a log file.
*
* @param af the #audio_format object
* @param s a buffer to print into
* @return the string, or NULL if the #audio_format object is invalid
*/
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s);
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -22,9 +22,13 @@
*
*/
#include "config.h"
#include "audio_parser.h"
#include "audio_format.h"
#include "audio_check.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
/**
......@@ -36,64 +40,158 @@ 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)
static bool
parse_sample_rate(const char *src, bool mask, uint32_t *sample_rate_r,
const char **endptr_r, GError **error_r)
{
char *endptr;
unsigned long value;
char *endptr;
audio_format_clear(dest);
/* parse sample rate */
if (mask && *src == '*') {
*sample_rate_r = 0;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample rate missing");
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the sample rate");
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
"Sample format missing");
} else if (!audio_check_sample_rate(value, error_r))
return false;
*sample_rate_r = value;
*endptr_r = endptr;
return true;
}
static bool
parse_sample_format(const char *src, bool mask,
enum sample_format *sample_format_r,
const char **endptr_r, GError **error_r)
{
unsigned long value;
char *endptr;
enum sample_format sample_format;
if (mask && *src == '*') {
*sample_format_r = SAMPLE_FORMAT_UNDEFINED;
*endptr_r = src + 1;
return true;
}
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the sample format");
return false;
} else if (!audio_valid_sample_rate(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample rate: %lu", value);
}
switch (value) {
case 8:
sample_format = SAMPLE_FORMAT_S8;
break;
case 16:
sample_format = SAMPLE_FORMAT_S16;
break;
case 24:
if (memcmp(endptr, "_3", 2) == 0) {
sample_format = SAMPLE_FORMAT_S24;
endptr += 2;
} else
sample_format = SAMPLE_FORMAT_S24_P32;
break;
case 32:
sample_format = SAMPLE_FORMAT_S32;
break;
default:
g_set_error(error_r, audio_parser_quark(), 0,
"Invalid sample format: %lu", value);
return false;
}
dest->sample_rate = value;
assert(audio_valid_sample_format(sample_format));
/* parse sample format */
*sample_format_r = sample_format;
*endptr_r = endptr;
return true;
}
static bool
parse_channel_count(const char *src, bool mask, uint8_t *channels_r,
const char **endptr_r, GError **error_r)
{
unsigned long value;
char *endptr;
if (mask && *src == '*') {
*channels_r = 0;
*endptr_r = src + 1;
return true;
}
src = endptr + 1;
value = strtoul(src, &endptr, 10);
if (endptr == src) {
g_set_error(error, audio_parser_quark(), 0,
"Sample format missing");
g_set_error(error_r, audio_parser_quark(), 0,
"Failed to parse the channel count");
return false;
} else if (*endptr != ':') {
g_set_error(error, audio_parser_quark(), 0,
"Channel count missing");
} else if (!audio_check_channel_count(value, error_r))
return false;
} else if (!audio_valid_sample_format(value)) {
g_set_error(error, audio_parser_quark(), 0,
"Invalid sample format: %lu", value);
*channels_r = value;
*endptr_r = endptr;
return true;
}
bool
audio_format_parse(struct audio_format *dest, const char *src,
bool mask, GError **error_r)
{
uint32_t rate;
enum sample_format sample_format;
uint8_t channels;
audio_format_clear(dest);
/* parse sample rate */
if (!parse_sample_rate(src, mask, &rate, &src, error_r))
return false;
if (*src++ != ':') {
g_set_error(error_r, audio_parser_quark(), 0,
"Sample format missing");
return false;
}
dest->bits = value;
/* parse sample format */
if (!parse_sample_format(src, mask, &sample_format, &src, error_r))
return false;
if (*src++ != ':') {
g_set_error(error_r, audio_parser_quark(), 0,
"Channel count missing");
return false;
}
/* 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);
if (!parse_channel_count(src, mask, &channels, &src, error_r))
return false;
if (*src != 0) {
g_set_error(error_r, audio_parser_quark(), 0,
"Extra data after channel count: %s", src);
return false;
}
dest->channels = value;
audio_format_init(dest, rate, sample_format, channels);
return true;
}
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -37,11 +37,13 @@ struct audio_format;
*
* @param dest the destination #audio_format struct
* @param src the input string
* @param error location to store the error occuring, or NULL to
* @param mask if true, then "*" is allowed for any number of items
* @param error_r location to store the error occuring, or NULL to
* ignore errors
* @return true on success
*/
bool
audio_format_parse(struct audio_format *dest, const char *src, GError **error);
audio_format_parse(struct audio_format *dest, const char *src,
bool mask, GError **error_r);
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "buffer.h"
#include "chunk.h"
#include "poison.h"
......@@ -117,6 +118,9 @@ music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
assert(buffer != NULL);
assert(chunk != NULL);
if (chunk->other != NULL)
music_buffer_return(buffer, chunk->other);
g_mutex_lock(buffer->mutex);
music_chunk_free(chunk);
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "buffer2array.h"
#include <glib.h>
#include <string.h>
int buffer2array(char *buffer, char *array[], const int max)
{
int i = 0;
char *c = buffer;
while (*c != '\0' && i < max) {
if (*c == '\"') {
array[i++] = ++c;
while (*c != '\0') {
if (*c == '\"') {
*(c++) = '\0';
break;
}
else if (*(c++) == '\\' && *c != '\0') {
memmove(c - 1, c, strlen(c) + 1);
}
}
} else {
c = g_strchug(c);
if (*c == '\0')
return i;
array[i++] = c++;
while (!g_ascii_isspace(*c) && *c != '\0')
++c;
}
if (*c == '\0')
return i;
*(c++) = '\0';
c = g_strchug(c);
}
return i;
}
#ifdef UNIT_TEST
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char *a[4] = { NULL };
char *b;
int max;
b = strdup("lsinfo \"/some/dir/name \\\"test\\\"\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\"", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir/name \\\"test\\\" something else\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\" something else", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir\\\\name\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir\\name", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"/some/dir name\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir name", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"\\\"/some/dir\\\"\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\"", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"\\\"/some/dir\\\" x\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\" x", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"single quote\\'d from php magicquotes\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("single quote\'d from php magicquotes", a[1]) );
assert( !a[2] );
b = strdup("lsinfo \"double quote\\\"d from php magicquotes\"");
assert(b);
max = buffer2array(b, a, 4);
assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("double quote\"d from php magicquotes", a[1]) );
assert( !a[2] );
return 0;
}
#endif
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CHECK_H
#define MPD_CHECK_H
/*
* All sources must include config.h on the first line to ensure that
* Large File Support is configured properly. This header checks
* whether this has happened.
*
* Usage: include this header before you use any of the above types.
* It will stop the compiler if something went wrong.
*
* This is Linux/glibc specific, and only enabled in the debug build,
* so bugs in this headers don't affect users with production builds.
*
*/
#ifndef PACKAGE_VERSION
#error config.h missing
#endif
#if defined(__linux__) && !defined(NDEBUG) && defined(ENABLE_LARGEFILE) && \
defined(_FEATURES_H) && defined(__i386__) && \
!defined(__USE_FILE_OFFSET64)
/* on i386, check if LFS is enabled */
#error config.h was included too late
#endif
#endif
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "chunk.h"
#include "audio_format.h"
#include "tag.h"
......@@ -26,8 +27,10 @@
void
music_chunk_init(struct music_chunk *chunk)
{
chunk->other = NULL;
chunk->length = 0;
chunk->tag = NULL;
chunk->replay_gain_serial = 0;
}
void
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,6 +20,8 @@
#ifndef MPD_CHUNK_H
#define MPD_CHUNK_H
#include "replay_gain_info.h"
#ifndef NDEBUG
#include "audio_format.h"
#endif
......@@ -42,6 +44,18 @@ struct music_chunk {
/** the next chunk in a linked list */
struct music_chunk *next;
/**
* An optional chunk which should be mixed into this chunk.
* This is used for cross-fading.
*/
struct music_chunk *other;
/**
* The current mix ratio for cross-fading: 1.0 means play 100%
* of this chunk, 0.0 means play 100% of the "other" chunk.
*/
float mix_ratio;
/** number of bytes stored in this chunk */
uint16_t length;
......@@ -59,6 +73,19 @@ struct music_chunk {
*/
struct tag *tag;
/**
* Replay gain information associated with this chunk.
* Only valid if the serial is not 0.
*/
struct replay_gain_info replay_gain_info;
/**
* A serial number for checking if replay gain info has
* changed since the last chunk. The magic value 0 indicates
* that there is no replay gain info available.
*/
unsigned replay_gain_serial;
/** the data (probably PCM) */
char data[CHUNK_SIZE];
......
/*
* Copyright (C) 2003-2009 The Music Player Daemon Project
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "main.h"
#include <assert.h>
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
struct client *client = data;
assert(!client_is_expired(client));
if (condition != G_IO_OUT) {
client_set_expired(client);
return false;
}
client_write_deferred(client);
if (client_is_expired(client)) {
client_close(client);
return false;
}
g_timer_start(client->last_activity);
if (g_queue_is_empty(client->deferred_send)) {
/* done sending deferred buffers exist: schedule
read */
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
return false;
}
/* write more */
return true;
}
gboolean
client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
gpointer data)
{
struct client *client = data;
enum command_return ret;
assert(!client_is_expired(client));
if (condition != G_IO_IN) {
client_set_expired(client);
return false;
}
g_timer_start(client->last_activity);
ret = client_read(client);
switch (ret) {
case COMMAND_RETURN_OK:
case COMMAND_RETURN_ERROR:
break;
case COMMAND_RETURN_KILL:
client_close(client);
g_main_loop_quit(main_loop);
return false;
case COMMAND_RETURN_CLOSE:
client_close(client);
return false;
}
if (client_is_expired(client)) {
client_close(client);
return false;
}
if (!g_queue_is_empty(client->deferred_send)) {
/* deferred buffers exist: schedule write */
client->source_id = g_io_add_watch(client->channel,
G_IO_OUT|G_IO_ERR|G_IO_HUP,
client_out_event, client);
return false;
}
/* read more */
return true;
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
static guint expire_source_id;
void
client_set_expired(struct client *client)
{
if (!client_is_expired(client))
client_schedule_expire();
if (client->source_id != 0) {
g_source_remove(client->source_id);
client->source_id = 0;
}
if (client->channel != NULL) {
g_io_channel_unref(client->channel);
client->channel = NULL;
}
}
static void
client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct client *client = data;
if (client_is_expired(client)) {
g_debug("[%u] expired", client->num);
client_close(client);
} else if (!client->idle_waiting && /* idle clients
never expire */
(int)g_timer_elapsed(client->last_activity, NULL) >
client_timeout) {
g_debug("[%u] timeout", client->num);
client_close(client);
}
}
static void
client_manager_expire(void)
{
client_list_foreach(client_check_expired_callback, NULL);
}
/**
* An idle event which calls client_manager_expire().
*/
static gboolean
client_manager_expire_event(G_GNUC_UNUSED gpointer data)
{
expire_source_id = 0;
client_manager_expire();
return false;
}
void
client_schedule_expire(void)
{
if (expire_source_id == 0)
/* delayed deletion */
expire_source_id = g_idle_add(client_manager_expire_event,
NULL);
}
void
client_deinit_expire(void)
{
if (expire_source_id != 0)
g_source_remove(expire_source_id);
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "conf.h"
#include <assert.h>
#define CLIENT_TIMEOUT_DEFAULT (60)
#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
/* set this to zero to indicate we have no possible clients */
unsigned int client_max_connections;
int client_timeout;
size_t client_max_command_list_size;
size_t client_max_output_buffer_size;
void client_manager_init(void)
{
client_timeout = config_get_positive(CONF_CONN_TIMEOUT,
CLIENT_TIMEOUT_DEFAULT);
client_max_connections =
config_get_positive(CONF_MAX_CONN,
CLIENT_MAX_CONNECTIONS_DEFAULT);
client_max_command_list_size =
config_get_positive(CONF_MAX_COMMAND_LIST_SIZE,
CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024)
* 1024;
client_max_output_buffer_size =
config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE,
CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024)
* 1024;
}
static void client_close_all(void)
{
while (!client_list_is_empty()) {
struct client *client = client_list_get_first();
client_close(client);
}
assert(client_list_is_empty());
}
void client_manager_deinit(void)
{
client_close_all();
client_max_connections = 0;
client_deinit_expire();
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "idle.h"
#include <assert.h>
/**
* Send "idle" response to this client.
*/
static void
client_idle_notify(struct client *client)
{
unsigned flags, i;
const char *const* idle_names;
assert(client->idle_waiting);
assert(client->idle_flags != 0);
flags = client->idle_flags;
client->idle_flags = 0;
client->idle_waiting = false;
idle_names = idle_get_names();
for (i = 0; idle_names[i]; ++i) {
if (flags & (1 << i) & client->idle_subscriptions)
client_printf(client, "changed: %s\n",
idle_names[i]);
}
client_puts(client, "OK\n");
g_timer_start(client->last_activity);
}
static void
client_idle_callback(gpointer data, gpointer user_data)
{
struct client *client = data;
unsigned flags = GPOINTER_TO_UINT(user_data);
if (client_is_expired(client))
return;
client->idle_flags |= flags;
if (client->idle_waiting
&& (client->idle_flags & client->idle_subscriptions)) {
client_idle_notify(client);
client_write_output(client);
}
}
void client_manager_idle_add(unsigned flags)
{
assert(flags != 0);
client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags));
}
bool client_idle_wait(struct client *client, unsigned flags)
{
assert(!client->idle_waiting);
client->idle_waiting = true;
client->idle_subscriptions = flags;
if (client->idle_flags & client->idle_subscriptions) {
client_idle_notify(client);
return true;
} else
return false;
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CLIENT_INTERNAL_H
#define MPD_CLIENT_INTERNAL_H
#include "client.h"
#include "command.h"
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
struct deferred_buffer {
size_t size;
char data[sizeof(long)];
};
struct client {
GIOChannel *channel;
guint source_id;
/** the buffer for reading lines from the #channel */
struct fifo_buffer *input;
unsigned permission;
/** the uid of the client process, or -1 if unknown */
int uid;
/**
* How long since the last activity from this client?
*/
GTimer *last_activity;
GSList *cmd_list; /* for when in list mode */
int cmd_list_OK; /* print OK after each command execution */
size_t cmd_list_size; /* mem cmd_list consumes */
GQueue *deferred_send; /* for output if client is slow */
size_t deferred_bytes; /* mem deferred_send consumes */
unsigned int num; /* client number */
char send_buf[16384];
size_t send_buf_used; /* bytes used this instance */
/** is this client waiting for an "idle" response? */
bool idle_waiting;
/** idle flags pending on this client, to be sent as soon as
the client enters "idle" */
unsigned idle_flags;
/** idle flags that the client wants to receive */
unsigned idle_subscriptions;
};
extern unsigned int client_max_connections;
extern int client_timeout;
extern size_t client_max_command_list_size;
extern size_t client_max_output_buffer_size;
bool
client_list_is_empty(void);
bool
client_list_is_full(void);
struct client *
client_list_get_first(void);
void
client_list_add(struct client *client);
void
client_list_foreach(GFunc func, gpointer user_data);
void
client_list_remove(struct client *client);
void
client_close(struct client *client);
static inline void
new_cmd_list_ptr(struct client *client, const char *s)
{
client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
}
static inline void
free_cmd_list(GSList *list)
{
for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(list);
}
void
client_set_expired(struct client *client);
/**
* Schedule an "expired" check for all clients: permanently delete
* clients which have been set "expired" with client_set_expired().
*/
void
client_schedule_expire(void);
/**
* Removes a scheduled "expired" check.
*/
void
client_deinit_expire(void);
enum command_return
client_read(struct client *client);
enum command_return
client_process_line(struct client *client, char *line);
void
client_write_deferred(struct client *client);
void
client_write_output(struct client *client);
gboolean
client_in_event(GIOChannel *source, GIOCondition condition,
gpointer data);
#endif
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include <assert.h>
static GList *clients;
static unsigned num_clients;
bool
client_list_is_empty(void)
{
return num_clients == 0;
}
bool
client_list_is_full(void)
{
return num_clients >= client_max_connections;
}
struct client *
client_list_get_first(void)
{
assert(clients != NULL);
return clients->data;
}
void
client_list_add(struct client *client)
{
clients = g_list_prepend(clients, client);
++num_clients;
}
void
client_list_foreach(GFunc func, gpointer user_data)
{
g_list_foreach(clients, func, user_data);
}
void
client_list_remove(struct client *client)
{
assert(num_clients > 0);
assert(clients != NULL);
clients = g_list_remove(clients, client);
--num_clients;
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include "fifo_buffer.h"
#include "socket_util.h"
#include "permission.h"
#include <assert.h>
#include <sys/types.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif
#include <unistd.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
#endif
#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
static unsigned int next_client_num;
struct client *client;
char *remote;
assert(fd >= 0);
#ifdef HAVE_LIBWRAP
if (sa->sa_family != AF_UNIX) {
char *hostaddr = sockaddr_to_string(sa, sa_length, NULL);
const char *progname = g_get_prgname();
struct request_info req;
request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
fromhost(&req);
if (!hosts_access(&req)) {
/* tcp wrappers says no */
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"libwrap refused connection (libwrap=%s) from %s",
progname, hostaddr);
g_free(hostaddr);
close(fd);
return;
}
g_free(hostaddr);
}
#endif /* HAVE_WRAP */
if (client_list_is_full()) {
g_warning("Max Connections Reached!");
close(fd);
return;
}
client = g_new0(struct client, 1);
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
#else
client->channel = g_io_channel_win32_new_socket(fd);
#endif
/* GLib is responsible for closing the file descriptor */
g_io_channel_set_close_on_unref(client->channel, true);
/* NULL encoding means the stream is binary safe; the MPD
protocol is UTF-8 only, but we are doing this call anyway
to prevent GLib from messing around with the stream */
g_io_channel_set_encoding(client->channel, NULL, NULL);
/* we prefer to do buffering */
g_io_channel_set_buffered(client->channel, false);
client->source_id = g_io_add_watch(client->channel,
G_IO_IN|G_IO_ERR|G_IO_HUP,
client_in_event, client);
client->input = fifo_buffer_new(4096);
client->permission = getDefaultPermissions();
client->uid = uid;
client->last_activity = g_timer_new();
client->cmd_list = NULL;
client->cmd_list_OK = -1;
client->cmd_list_size = 0;
client->deferred_send = g_queue_new();
client->deferred_bytes = 0;
client->num = next_client_num++;
client->send_buf_used = 0;
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
client_list_add(client);
remote = sockaddr_to_string(sa, sa_length, NULL);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] opened from %s", client->num, remote);
g_free(remote);
}
static void
deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
struct deferred_buffer *buffer = data;
g_free(buffer);
}
void
client_close(struct client *client)
{
client_list_remove(client);
client_set_expired(client);
g_timer_destroy(client->last_activity);
if (client->cmd_list) {
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
}
g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
g_queue_free(client->deferred_send);
fifo_buffer_free(client->input);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
"[%u] closed", client->num);
g_free(client);
}
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "client_internal.h"
#include <string.h>
#define CLIENT_LIST_MODE_BEGIN "command_list_begin"
#define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin"
#define CLIENT_LIST_MODE_END "command_list_end"
static enum command_return
client_process_command_list(struct client *client, bool list_ok, GSList *list)
{
enum command_return ret = COMMAND_RETURN_OK;
unsigned num = 0;
for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
char *cmd = cur->data;
g_debug("command_process_list: process command \"%s\"",
cmd);
ret = command_process(client, num++, cmd);
g_debug("command_process_list: command returned %i", ret);
if (ret != COMMAND_RETURN_OK || client_is_expired(client))
break;
else if (list_ok)
client_puts(client, "list_OK\n");
}
return ret;
}
enum command_return
client_process_line(struct client *client, char *line)
{
enum command_return ret;
if (strcmp(line, "noidle") == 0) {
if (client->idle_waiting) {
/* send empty idle response and leave idle mode */
client->idle_waiting = false;
command_success(client);
client_write_output(client);
}
/* do nothing if the client wasn't idling: the client
has already received the full idle response from
client_idle_notify(), which he can now evaluate */
return COMMAND_RETURN_OK;
} else if (client->idle_waiting) {
/* during idle mode, clients must not send anything
except "noidle" */
g_warning("[%u] command \"%s\" during idle",
client->num, line);
return COMMAND_RETURN_CLOSE;
}
if (client->cmd_list_OK >= 0) {
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
g_debug("[%u] process command list",
client->num);
/* for scalability reasons, we have prepended
each new command; now we have to reverse it
to restore the correct order */
client->cmd_list = g_slist_reverse(client->cmd_list);
ret = client_process_command_list(client,
client->cmd_list_OK,
client->cmd_list);
g_debug("[%u] process command "
"list returned %i", client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
client_write_output(client);
free_cmd_list(client->cmd_list);
client->cmd_list = NULL;
client->cmd_list_OK = -1;
} else {
size_t len = strlen(line) + 1;
client->cmd_list_size += len;
if (client->cmd_list_size >
client_max_command_list_size) {
g_warning("[%u] command list size (%lu) "
"is larger than the max (%lu)",
client->num,
(unsigned long)client->cmd_list_size,
(unsigned long)client_max_command_list_size);
return COMMAND_RETURN_CLOSE;
}
new_cmd_list_ptr(client, line);
ret = COMMAND_RETURN_OK;
}
} else {
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
client->cmd_list_OK = 0;
ret = COMMAND_RETURN_OK;
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
client->cmd_list_OK = 1;
ret = COMMAND_RETURN_OK;
} else {
g_debug("[%u] process command \"%s\"",
client->num, line);
ret = command_process(client, 0, line);
g_debug("[%u] command returned %i",
client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
client_is_expired(client))
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
client_write_output(client);
}
}
return ret;
}
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.
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.
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.
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