Commit 1a421a5e authored by Led's avatar Led

Merge commit '0.14.1' into alt-git

parents 067629d7 e8c148ab
......@@ -33,3 +33,6 @@ mkinstalldirs
mpd
stamp-h1
tags
*~
.stgit*
doc/protocol.html
......@@ -162,8 +162,9 @@ Run
$ mpd <config file>
First default is ~/.mpdconf then /etc/mpd.conf. If neither of these exist
a mpd configuration file must be specified at runtime. A
First default is ~/.mpdconf then ~/.mpd/mpd.conf then /etc/mpd.conf. If
neither of these exist a mpd configuration file must be specified at
runtime.
A sample config file is included with the source of MPD, mpdconf.example.
......
ver 0.15 - (200?/??/??)
* Rewritten mixer code to support multiple mixers
* Add audio archive extraction support:
- bzip2
- iso9660
- zip
* Add RVA2 tag support
* the option "error_file" was removed, all messages are logged into
"log_file"
* support logging to syslog
* fall back to XDG music directory if no music_directory is configured
* failure to read the state file is non-fatal
* added Icy-Metadata support
* --create-db starts the MPD daemon instead of exiting
ver 0.14.1 (2009/01/17)
* decoders:
- mp4: support the writer/composer tag
- id3: strip leading and trailing whitespace from ID3 tags
- oggvorbis: fix tremor support
- oggvorbis: disable seeking on remote files
* audio outputs:
- jack: allocate default port names (fixes a crash)
* update:
- refresh stats after update
- save the database even if it is empty
* input_curl:
- use select() to eliminate busy loop during connect
- honour http_proxy_* config directives
- fix assertion failure on "connection refused"
- fix assertion failure with empty HTTP responses
* corrected the sample calculation in the fallback resampler
* log: automatically append newline
* fix setenv() conflict on Solaris
* configure.ac: check for pkg-config before using it
* fix minor memory leak in decoder_tag()
* fix cross-fading bug: it used to play some chunks of the new song twice
* playlist
- fix assertion failure during playlist load
- implement Fisher-Yates shuffle properly
- safely search the playlist for deleted song
* use custom PRNG for volume dithering (speedup)
* detect libid3tag without pkg-config
ver 0.14 (2008/12/25)
* audio outputs:
- wait 10 seconds before reopening a failed device
......
......@@ -26,7 +26,7 @@ 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 and exit.
Force (re)creation of database.
.TP
.BI --no-create-db
Do not create database, even if it doesn't exist.
......
......@@ -3,8 +3,8 @@
mpd.conf \- Music Player Daemon configuration file
.SH DESCRIPTION
\fBmpd.conf\fP is the configuration file for mpd(1). If not specified on the
command line, MPD first searches for it at \fB~/.mpdconf\fP and then in
\fB/etc/mpd.conf\fP.
command line, MPD first searches for it at \fB~/.mpdconf\fP then at
\fB~/.mpd/mpd.conf\fP and then in \fB/etc/mpd.conf\fP.
Lines beginning with a "#" character are comments. All other non-empty lines
specify parameters and their values. These lines contain the parameter name
......@@ -51,9 +51,7 @@ This specifies where the db file will be stored.
.TP
.B log_file <file>
This specifies where the log file should be located.
.TP
.B error_file <file>
This specifies where the error file should be located.
The special value "syslog" makes MPD use the local syslog daemon.
.SH OPTIONAL PARAMETERS
.TP
.B pid_file <file>
......@@ -159,18 +157,23 @@ Linear interpolator, very fast, poor quality.
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 <oss, alsa or software>
This specifies which mixer to use. The default depends on what audio output
support mpd was built with.
.B mixer_type <alsa, oss, software or hardware>
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".
default for alsa is "default". This 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".
for OSS is "Pcm", and an example for alsa is "PCM". This 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>
If specified, mpd will adjust the volume of songs played using ReplayGain tags
......@@ -278,6 +281,15 @@ whatever audio format is passed to the audio output.
.B device <dev>
This specifies the device to use for audio output. The default is "default".
.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".
.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".
.TP
.B use_mmap <yes or no>
Setting this allows you to use memory-mapped I/O. Certain hardware setups may
benefit from this, but most do not. Most users do not need to set this. The
......
......@@ -25,8 +25,11 @@ db_file "~/.mpd/database"
# These logs are great for troubleshooting, depending on your log_level
# settings.
#
# The special value "syslog" makes MPD use the local syslog daemon.
# On most systems, log messages will appear in /var/log/daemon.log
# then.
#
log_file "~/.mpd/log"
error_file "~/.mpd/error-log"
###############################################################################
......@@ -159,6 +162,8 @@ error_file "~/.mpd/error-log"
# name "My ALSA Device"
# device "hw:0,0" # optional
# format "44100:16:2" # optional
# mixer_device "default" # optional
# mixer_control "PCM" # optional
#}
#
# An example of an OSS output:
......@@ -168,6 +173,8 @@ error_file "~/.mpd/error-log"
# name "My OSS Device"
# device "/dev/dsp" # optional
# format "44100:16:2" # optional
# mixer_device "/dev/mixer" # optional
# mixer_control "PCM" # optional
#}
#
# An example of a shout output (for streaming to Icecast):
......@@ -229,17 +236,9 @@ error_file "~/.mpd/error-log"
# specified it may be autodetected at startup, depending on the dependencies
# which were compiled into the server.
#
# An example for controlling an ALSA mixer:
#
#mixer_type "alsa"
#mixer_device "default"
#mixer_control "PCM"
#
# An example for controlling an OSS mixer:
# An example for controlling an ALSA or OSS mixer:
#
#mixer_type "oss"
#mixer_device "/dev/mixer"
#mixer_control "PCM"
#mixer_type "hardware"
#
# This example is a general volume control mixer, it is used to adjust the
# volume of the audio sent to the audio output, and will work with all outputs.
......
# codeset.m4 serial AM1 (gettext-0.10.40)
dnl Copyright (C) 2000-2002 Free Software Foundation, Inc.
dnl This file is free software, distributed under the terms of the GNU
dnl General Public License. As a special exception to the GNU General
dnl Public License, this file may be distributed as part of a program
dnl that contains a configuration script generated by Autoconf, under
dnl the same distribution terms as the rest of that program.
dnl From Bruno Haible.
AC_DEFUN([AM_LANGINFO_CODESET],
[
AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
[AC_TRY_LINK([#include <langinfo.h>],
[char* cs = nl_langinfo(CODESET);],
am_cv_langinfo_codeset=yes,
am_cv_langinfo_codeset=no)
])
if test $am_cv_langinfo_codeset = yes; then
AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
[Define if you have <langinfo.h> and nl_langinfo(CODESET).])
fi
])
bin_PROGRAMS = mpd
mpd_CFLAGS = $(MPD_CFLAGS)
mpd_CPPFLAGS = \
$(SQLITE_CFLAGS) \
$(CURL_CFLAGS) \
$(AO_CFLAGS) $(ALSA_CFLAGS) \
$(SHOUT_CFLAGS) \
$(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(AUDIOFILE_CFLAGS) $(LIBMIKMOD_CFLAGS) \
$(MODPLUG_CFLAGS) \
$(ID3TAG_CFLAGS) \
$(MAD_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(GLIB_CFLAGS)
mpd_LDADD = $(MPD_LIBS) \
$(SQLITE_LIBS) \
$(CURL_LIBS) \
$(AO_LIBS) $(ALSA_LIBS) \
$(SHOUT_LIBS) \
$(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
$(MODPLUG_LIBS) \
$(ID3TAG_LIBS) \
$(MAD_LIBS) \
$(MP4FF_LIBS) \
$(FFMPEG_LIBS) \
$(GLIB_LIBS)
mpd_headers = \
notify.h \
ack.h \
......@@ -15,7 +43,7 @@ mpd_headers = \
buffer2array.h \
command.h \
idle.h \
condition.h \
cmdline.h \
conf.h \
crossfade.h \
dbUtils.h \
......@@ -36,23 +64,29 @@ mpd_headers = \
input_stream.h \
input_file.h \
input_curl.h \
icy_metadata.h \
client.h \
list.h \
dlist.h \
listen.h \
log.h \
ls.h \
main_notify.h \
main.h \
mixer_api.h \
event_pipe.h \
daemon.h \
normalize.h \
compress.h \
os_compat.h \
pipe.h \
path.h \
mapper.h \
pcm_utils.h \
pcm_convert.h \
pcm_volume.h \
pcm_mix.h \
pcm_channels.h \
pcm_format.h \
pcm_resample.h \
pcm_dither.h \
pcm_prng.h \
permission.h \
player_thread.h \
player_control.h \
......@@ -60,7 +94,6 @@ mpd_headers = \
playlist.h \
playlist_save.h \
replay_gain.h \
signal_check.h \
sig_handlers.h \
song.h \
song_print.h \
......@@ -77,11 +110,13 @@ mpd_headers = \
strset.h \
utils.h \
volume.h \
ioops.h \
zeroconf.h \
zeroconf.h zeroconf-internal.h \
locate.h \
stored_playlist.h \
timer.h
timer.h \
archive_api.h \
archive_list.h \
input_archive.h
mpd_SOURCES = \
......@@ -98,7 +133,7 @@ mpd_SOURCES = \
buffer2array.c \
command.c \
idle.c \
condition.c \
cmdline.c \
conf.c \
crossfade.c \
dbUtils.c \
......@@ -115,20 +150,23 @@ mpd_SOURCES = \
input_stream.c \
input_file.c \
client.c \
ioops.c \
list.c \
listen.c \
log.c \
ls.c \
main.c \
main_notify.c \
event_pipe.c \
daemon.c \
mixer_api.c \
normalize.c \
compress.c \
pipe.c \
path.c \
mapper.c \
pcm_utils.c \
pcm_convert.c \
pcm_volume.c \
pcm_mix.c \
pcm_channels.c \
pcm_format.c \
pcm_resample.c \
pcm_dither.c \
permission.c \
......@@ -139,7 +177,6 @@ mpd_SOURCES = \
playlist_save.c \
replay_gain.c \
sig_handlers.c \
signal_check.c \
song.c \
song_print.c \
song_save.c \
......@@ -167,6 +204,27 @@ if HAVE_ID3TAG
mpd_SOURCES += tag_id3.c
endif
# archive plugins
if HAVE_BZ2
mpd_SOURCES += archive/bz2_plugin.c
endif
if HAVE_ZIP
mpd_SOURCES += archive/zip_plugin.c
endif
if HAVE_ISO
mpd_SOURCES += archive/iso_plugin.c
endif
if ENABLE_ARCHIVE
mpd_SOURCES += \
archive_api.c \
archive_list.c \
input_archive.c
endif
# decoder plugins
......@@ -218,6 +276,10 @@ if HAVE_MIKMOD
mpd_SOURCES += decoder/mod_plugin.c
endif
if HAVE_MODPLUG
mpd_SOURCES += decoder/modplug_plugin.c
endif
if HAVE_FFMPEG
mpd_SOURCES += decoder/ffmpeg_plugin.c
endif
......@@ -225,16 +287,25 @@ endif
if HAVE_ZEROCONF
mpd_SOURCES += zeroconf.c
if HAVE_AVAHI
mpd_SOURCES += zeroconf-avahi.c
endif
if HAVE_BONJOUR
mpd_SOURCES += zeroconf-bonjour.c
endif
endif
if HAVE_CURL
mpd_SOURCES += input_curl.c
mpd_SOURCES += input_curl.c icy_metadata.c
endif
if HAVE_ALSA
mpd_SOURCES += output/alsa_plugin.c
mpd_SOURCES += mixer/alsa_mixer.c
endif
if HAVE_AO
......@@ -255,6 +326,7 @@ endif
if HAVE_OSS
mpd_SOURCES += output/oss_plugin.c
mpd_SOURCES += mixer/oss_mixer.c
endif
if HAVE_OSX
......@@ -277,31 +349,6 @@ if HAVE_SHOUT_OGG
mpd_SOURCES += output/shout_ogg.c
endif
mpd_CFLAGS = $(MPD_CFLAGS)
mpd_CPPFLAGS = \
$(CURL_CFLAGS) \
$(AO_CFLAGS) $(ALSA_CFLAGS) \
$(SHOUT_CFLAGS) \
$(OGGVORBIS_CFLAGS) $(VORBISENC_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
$(AUDIOFILE_CFLAGS) $(LIBMIKMOD_CFLAGS) \
$(ID3TAG_CFLAGS) \
$(MAD_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(GLIB_CFLAGS)
mpd_LDADD = $(MPD_LIBS) \
$(CURL_LIBS) \
$(AO_LIBS) $(ALSA_LIBS) \
$(SHOUT_LIBS) \
$(OGGVORBIS_LIBS) $(VORBISENC_LIBS) $(FLAC_LIBS) \
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
$(ID3TAG_LIBS) \
$(MAD_LIBS) \
$(MP4FF_LIBS) \
$(FFMPEG_LIBS) \
$(GLIB_LIBS)
# sparse is a semantic parser
# URL: git://www.kernel.org/pub/scm/devel/sparse/sparse.git
SPARSE = sparse
......
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* single bz2 archive handling (requires libbz2)
*/
#include "archive_api.h"
#include "input_stream.h"
#include "config.h"
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <glib.h>
#include <bzlib.h>
#ifdef HAVE_OLDER_BZIP2
#define BZ2_bzDecompressInit bzDecompressInit
#define BZ2_bzDecompress bzDecompress
#endif
#define BZ_BUFSIZE 5000
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;
static const struct input_plugin bz2_inputplugin;
/* single archive handling allocation helpers */
static bool
bz2_alloc(bz2_context *data)
{
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);
g_free(data);
return false;
}
data->last_bz_result = BZ_OK;
data->last_parent_result = 0;
return true;
}
static void
bz2_destroy(bz2_context *data)
{
BZ2_bzDecompressEnd(&data->bzstream);
g_free(data->buffer);
}
/* archive open && listing routine */
static struct archive_file *
bz2_open(char * pathname)
{
bz2_context *context;
char *name;
int len;
context = g_malloc(sizeof(bz2_context));
if (!context) {
return NULL;
}
//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);
g_free(context);
return NULL;
}
context->name = g_strdup(name+1);
//remove suffix
len = strlen(context->name);
if (len > 4) {
context->name[len-4] = 0; //remove .bz2 suffix
}
return (struct archive_file *) context;
}
static void
bz2_scan_reset(struct archive_file *file)
{
bz2_context *context = (bz2_context *) file;
context->reset = true;
}
static char *
bz2_scan_next(struct archive_file *file)
{
bz2_context *context = (bz2_context *) 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;
if (context->name)
g_free(context->name);
input_stream_close(&context->istream);
g_free(context);
}
/* single archive handling */
static void
bz2_setup_stream(struct archive_file *file, struct input_stream *is)
{
bz2_context *context = (bz2_context *) file;
//setup file ops
is->plugin = &bz2_inputplugin;
//insert back reference
is->archive = context;
is->seekable = false;
}
static bool
bz2_is_open(struct input_stream *is, G_GNUC_UNUSED const char *url)
{
bz2_context *context = (bz2_context *) is->archive;
if (!bz2_alloc(context)) {
g_warning("alloc bz2 failed\n");
return false;
}
return true;
}
static void
bz2_is_close(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->archive;
bz2_destroy(context);
is->data = NULL;
}
static int
bz2_fillbuffer(bz2_context *context,
size_t numBytes)
{
size_t count;
bz_stream *bzstream;
bzstream = &context->bzstream;
if (bzstream->avail_in > 0)
return 0;
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;
}
return 0;
}
static size_t
bz2_is_read(struct input_stream *is, void *ptr, size_t size)
{
bz2_context *context = (bz2_context *) is->archive;
bz_stream *bzstream;
int bz_result;
size_t numBytes = size;
size_t bytesRead = 0;
if (context->last_bz_result != BZ_OK)
return 0;
if (context->last_parent_result != 0)
return 0;
bzstream = &context->bzstream;
bzstream->next_out = ptr;
bzstream->avail_out = numBytes;
while (bzstream->avail_out != 0) {
if (bz2_fillbuffer(context, numBytes) != 0)
break;
bz_result = BZ2_bzDecompress(bzstream);
if (context->last_bz_result != BZ_OK
&& bzstream->avail_out == numBytes) {
context->last_bz_result = bz_result;
break;
}
if (bz_result == BZ_STREAM_END) {
context->last_bz_result = bz_result;
break;
}
}
bytesRead = numBytes - bzstream->avail_out;
is->offset += bytesRead;
return bytesRead;
}
static bool
bz2_is_eof(struct input_stream *is)
{
bz2_context *context = (bz2_context *) is->archive;
if (context->last_bz_result == BZ_STREAM_END) {
return true;
}
return false;
}
static bool
bz2_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
static int
bz2_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */
static const char *const bz2_extensions[] = {
"bz2",
NULL
};
static const struct input_plugin bz2_inputplugin = {
.open = bz2_is_open,
.close = bz2_is_close,
.read = bz2_is_read,
.eof = bz2_is_eof,
.seek = bz2_is_seek,
.buffer = bz2_is_buffer
};
const struct archive_plugin bz2_plugin = {
.name = "bz2",
.open = bz2_open,
.scan_reset = bz2_scan_reset,
.scan_next = bz2_scan_next,
.setup_stream = bz2_setup_stream,
.close = bz2_close,
.suffixes = bz2_extensions
};
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* iso archive handling (requires cdio, and iso9660)
*/
#include "archive_api.h"
#include "input_stream.h"
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <glib.h>
#include <string.h>
#define CEILING(x, y) ((x+(y-1))/y)
typedef struct {
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 iso_inputplugin;
/* archive open && listing routine */
static void
listdir_recur(const char *psz_path, iso_context *context)
{
iso9660_t *iso = context->iso;
CdioList_t *entlist;
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
if (!entlist) {
return;
}
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
_CDIO_LIST_FOREACH (entnode, entlist) {
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
strcpy(pathname, psz_path);
strcat(pathname, statbuf->filename);
if (_STAT_DIR == statbuf->type ) {
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
strcat(pathname, "/");
listdir_recur(pathname, context);
}
} else {
//remove leading /
context->list = g_slist_prepend( context->list,
g_strdup(pathname + 1));
}
}
_cdio_list_free (entlist, true);
}
static struct archive_file *
iso_open(char * pathname)
{
iso_context *context = g_malloc(sizeof(iso_context));
context->list = NULL;
/* open archive */
context->iso = iso9660_open (pathname);
if (context->iso == NULL) {
g_warning("iso %s open failed\n", pathname);
return NULL;
}
listdir_recur("/", context);
return (struct archive_file *)context;
}
static void
iso_scan_reset(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
//reset iterator
context->iter = context->list;
}
static char *
iso_scan_next(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
iso_close(struct archive_file *file)
{
iso_context *context = (iso_context *) file;
GSList *tmp;
if (context->list) {
//free list
for (tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
iso9660_close(context->iso);
context->iso = NULL;
}
/* single archive handling */
static void
iso_setup_stream(struct archive_file *file, struct input_stream *is)
{
iso_context *context = (iso_context *) file;
//setup file ops
is->plugin = &iso_inputplugin;
//insert back reference
is->archive = context;
//we are not seekable
is->seekable = false;
}
static bool
iso_is_open(struct input_stream *is, const char *pathname)
{
iso_context *context = (iso_context *) is->archive;
context->statbuf = iso9660_ifs_stat_translate (context->iso, pathname);
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;
}
static void
iso_is_close(struct input_stream *is)
{
iso_context *context = (iso_context *) is->archive;
g_free(context->statbuf);
}
static size_t
iso_is_read(struct input_stream *is, void *ptr, size_t size)
{
iso_context *context = (iso_context *) is->archive;
int toread, readed = 0;
int no_blocks, cur_block;
size_t left_bytes = context->statbuf->size - context->cur_ofs;
size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
if (left_bytes < size) {
toread = left_bytes;
no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
} else {
toread = size;
no_blocks = toread / ISO_BLOCKSIZE;
}
if (no_blocks > 0) {
cur_block = context->cur_ofs / ISO_BLOCKSIZE;
readed = iso9660_iso_seek_read (context->iso, ptr,
context->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;
}
if (left_bytes < size) {
readed = left_bytes;
}
context->cur_ofs += readed;
}
return readed;
}
static bool
iso_is_eof(struct input_stream *is)
{
iso_context *context = (iso_context *) is->archive;
return (context->cur_ofs == context->statbuf->size);
}
static bool
iso_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
static int
iso_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */
static const char *const iso_extensions[] = {
"iso",
NULL
};
static const struct input_plugin iso_inputplugin = {
.open = iso_is_open,
.close = iso_is_close,
.read = iso_is_read,
.eof = iso_is_eof,
.seek = iso_is_seek,
.buffer = iso_is_buffer
};
const struct archive_plugin iso_plugin = {
.name = "iso",
.open = iso_open,
.scan_reset = iso_scan_reset,
.scan_next = iso_scan_next,
.setup_stream = iso_setup_stream,
.close = iso_close,
.suffixes = iso_extensions
};
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* zip archive handling (requires zziplib)
*/
#include "archive_api.h"
#include "archive_api.h"
#include "input_stream.h"
#include <zzip/zzip.h>
#include <glib.h>
#include <string.h>
typedef struct {
ZZIP_DIR *dir;
ZZIP_FILE *file;
size_t length;
GSList *list;
GSList *iter;
} zip_context;
static const struct input_plugin zip_inputplugin;
/* archive open && listing routine */
static struct archive_file *
zip_open(char * pathname)
{
zip_context *context = g_malloc(sizeof(zip_context));
ZZIP_DIRENT dirent;
// open archive
context->list = NULL;
context->dir = zzip_dir_open(pathname, 0);
if (context->dir == NULL) {
g_warning("zipfile %s open failed\n", pathname);
return NULL;
}
while (zzip_dir_read(context->dir, &dirent)) {
//add only files
if (dirent.st_size > 0) {
context->list = g_slist_prepend(context->list,
g_strdup(dirent.d_name));
}
}
return (struct archive_file *)context;
}
static void
zip_scan_reset(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
//reset iterator
context->iter = context->list;
}
static char *
zip_scan_next(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
char *data = NULL;
if (context->iter != NULL) {
///fetch data and goto next
data = context->iter->data;
context->iter = g_slist_next(context->iter);
}
return data;
}
static void
zip_close(struct archive_file *file)
{
zip_context *context = (zip_context *) file;
if (context->list) {
//free list
for (GSList *tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
g_free(tmp->data);
g_slist_free(context->list);
}
//close archive
zzip_dir_close (context->dir);
context->dir = NULL;
}
/* single archive handling */
static void
zip_setup_stream(struct archive_file *file, struct input_stream *is)
{
zip_context *context = (zip_context *) file;
//setup file ops
is->plugin = &zip_inputplugin;
//insert back reference
is->archive = context;
//we are not seekable
is->seekable = false;
}
static bool
zip_is_open(struct input_stream *is, const char *pathname)
{
zip_context *context = (zip_context *) is->archive;
ZZIP_STAT z_stat;
context->file = zzip_file_open(context->dir, pathname, 0);
if (!context->file) {
g_warning("file %s not found in the zipfile\n", pathname);
return false;
}
zzip_file_stat(context->file, &z_stat);
context->length = z_stat.st_size;
return true;
}
static void
zip_is_close(struct input_stream *is)
{
zip_context *context = (zip_context *) is->archive;
zzip_file_close (context->file);
}
static size_t
zip_is_read(struct input_stream *is, void *ptr, size_t size)
{
zip_context *context = (zip_context *) is->archive;
int ret;
ret = zzip_file_read(context->file, ptr, size);
if (ret < 0) {
g_warning("error %d reading zipfile\n", ret);
return 0;
}
return ret;
}
static bool
zip_is_eof(struct input_stream *is)
{
zip_context *context = (zip_context *) is->archive;
return ((size_t) zzip_tell(context->file) == context->length);
}
static bool
zip_is_seek(G_GNUC_UNUSED struct input_stream *is,
G_GNUC_UNUSED off_t offset, G_GNUC_UNUSED int whence)
{
return false;
}
static int
zip_is_buffer(G_GNUC_UNUSED struct input_stream *is)
{
return 0;
}
/* exported structures */
static const char *const zip_extensions[] = {
"zip",
NULL
};
static const struct input_plugin zip_inputplugin = {
.open = zip_is_open,
.close = zip_is_close,
.read = zip_is_read,
.eof = zip_is_eof,
.seek = zip_is_seek,
.buffer = zip_is_buffer
};
const struct archive_plugin zip_plugin = {
.name = "zip",
.open = zip_open,
.scan_reset = zip_scan_reset,
.scan_next = zip_scan_next,
.setup_stream = zip_setup_stream,
.close = zip_close,
.suffixes = zip_extensions
};
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include "archive_api.h"
/**
*
* archive_lookup is used to determine if part of pathname refers to an regular
* file (archive). If so then its also used to split pathname into archive file
* and path used to locate file in archive. It also returns suffix of the file.
* How it works:
* We do stat of the parent of input pathname as long as we find an regular file
* Normally this should never happen. When routine returns true pathname modified
* and split into archive, inpath and suffix. Otherwise nothing happens
*
* For example:
*
* /music/path/Talco.zip/Talco - Combat Circus/12 - A la pachenka.mp3
* is split into archive: /music/path/Talco.zip
* inarchive pathname: Talco - Combat Circus/12 - A la pachenka.mp3
* and suffix: zip
*/
bool archive_lookup(char *pathname, char **archive, char **inpath, char **suffix)
{
char *pathdupe;
int len, idx;
struct stat st_info;
bool ret = false;
*archive = NULL;
*inpath = NULL;
*suffix = NULL;
pathdupe = g_strdup(pathname);
len = idx = strlen(pathname);
while (idx > 0) {
//try to stat if its real directory
if (stat(pathdupe, &st_info) == -1) {
if (errno != ENOTDIR) {
g_warning("stat %s failed (errno=%d)\n", pathdupe, errno);
break;
}
} else {
//is something found ins original path (is not an archive)
if (idx == len) {
break;
}
//its a file ?
if (S_ISREG(st_info.st_mode)) {
//so the upper should be file
pathname[idx] = 0;
ret = true;
*archive = pathname;
*inpath = pathname + idx+1;
//try to get suffix
*suffix = NULL;
while (idx > 0) {
if (pathname[idx] == '.') {
*suffix = pathname + idx + 1;
break;
}
idx--;
}
break;
} else {
g_warning("not a regular file %s\n", pathdupe);
break;
}
}
//find one dir up
while (idx > 0) {
if (pathdupe[idx] == '/') {
pathdupe[idx] = 0;
break;
}
idx--;
}
}
g_free(pathdupe);
return ret;
}
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MPD_ARCHIVE_API_H
#define MPD_ARCHIVE_API_H
/*
* This is the public API which is used by archive plugins to
* provide transparent archive decompression layer for mpd
*
*/
#include "archive_internal.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 *);
/**
* this is used to setup input stream handle, to be able to read
* from archive. open method of inputstream can be the used to
* extract particular file
*/
void (*setup_stream)(struct archive_file *, struct input_stream *is);
/**
* 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
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>
* Copyright (C) 2008 Eric Wong <normalperson@yhbt.net>
*
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -18,23 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MPD_MAIN_NOTIFY_H
#define MPD_MAIN_NOTIFY_H
#include <pthread.h>
extern pthread_t main_task;
void init_main_notify(void);
void deinit_main_notify(void);
void wakeup_main_task(void);
void wait_main_task(void);
void main_notify_lock(void);
#ifndef MPD_ARCHIVE_INTERNAL_H
#define MPD_ARCHIVE_INTERNAL_H
void main_notify_unlock(void);
struct archive_file {
int placeholder;
};
#endif /* MAIN_NOTIFY_H */
#endif
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "archive_list.h"
#include "archive_api.h"
#include "utils.h"
#include "config.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,
#endif
#ifdef HAVE_ZIP
&zip_plugin,
#endif
#ifdef HAVE_ISO
&iso_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];
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) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
stringFoundInStringArray(plugin->suffixes, suffix)) {
++i;
return plugin;
}
}
return NULL;
}
const struct archive_plugin *
archive_plugin_from_name(const char *name)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] &&
strcmp(plugin->name, name) == 0)
return plugin;
}
return NULL;
}
void archive_plugin_print_all_suffixes(FILE * fp)
{
const char *const*suffixes;
for (unsigned i = 0; i < num_archive_plugins; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (!archive_plugins_enabled[i])
continue;
suffixes = plugin->suffixes;
while (suffixes && *suffixes) {
fprintf(fp, "%s ", *suffixes);
suffixes++;
}
}
fprintf(fp, "\n");
fflush(fp);
}
void archive_plugin_init_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (plugin->init == NULL || archive_plugins[i]->init())
archive_plugins_enabled[i] = true;
}
}
void archive_plugin_deinit_all(void)
{
for (unsigned i = 0; i < num_archive_plugins; ++i) {
const struct archive_plugin *plugin = archive_plugins[i];
if (archive_plugins_enabled[i] && plugin->finish != NULL)
archive_plugins[i]->finish();
}
}
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MPD_ARCHIVE_LIST_H
#define MPD_ARCHIVE_LIST_H
#include "archive_api.h"
#include <stdio.h>
struct archive_plugin;
/* interface for using plugins */
const struct archive_plugin *
archive_plugin_from_suffix(const char *suffix);
const struct archive_plugin *
archive_plugin_from_name(const char *name);
void archive_plugin_print_all_suffixes(FILE * fp);
/* this is where we "load" all the "plugins" ;-) */
void archive_plugin_init_all(void);
/* this is where we "unload" all the "plugins" */
void archive_plugin_deinit_all(void);
#endif
......@@ -24,11 +24,13 @@
#include "path.h"
#include "client.h"
#include "idle.h"
#include "utils.h"
#include "os_compat.h"
#include "mixer_api.h"
#include <glib.h>
#include <assert.h>
#include <stdlib.h>
#define AUDIO_DEVICE_STATE "audio_device_state:"
#define AUDIO_BUFFER_SIZE 2*MPD_PATH_MAX
......@@ -38,12 +40,12 @@ static struct audio_format input_audio_format;
static struct audio_output *audioOutputArray;
static unsigned int audioOutputArraySize;
static unsigned int audio_output_count(void)
unsigned int audio_output_count(void)
{
unsigned int nr = 0;
ConfigParam *param = NULL;
struct config_param *param = NULL;
while ((param = getNextConfigParam(CONF_AUDIO_OUTPUT, param)))
while ((param = config_get_next_param(CONF_AUDIO_OUTPUT, param)))
nr++;
if (!nr)
nr = 1; /* we'll always have at least one device */
......@@ -53,18 +55,20 @@ static unsigned int audio_output_count(void)
/* make sure initPlayerData is called before this function!! */
void initAudioDriver(void)
{
ConfigParam *param = NULL;
struct config_param *param = NULL;
unsigned int i;
notify_init(&audio_output_client_notify);
audioOutputArraySize = audio_output_count();
audioOutputArray = xmalloc(sizeof(struct audio_output) * audioOutputArraySize);
audioOutputArray = g_new(struct audio_output, audioOutputArraySize);
for (i = 0; i < audioOutputArraySize; i++)
{
struct audio_output *output = &audioOutputArray[i];
unsigned int j;
param = getNextConfigParam(CONF_AUDIO_OUTPUT, param);
param = config_get_next_param(CONF_AUDIO_OUTPUT, param);
/* only allow param to be NULL if there just one audioOutput */
assert(param || (audioOutputArraySize == 1));
......@@ -102,7 +106,7 @@ void getOutputAudioFormat(const struct audio_format *inAudioFormat,
void initAudioConfig(void)
{
ConfigParam *param = getConfigParam(CONF_AUDIO_OUTPUT_FORMAT);
struct config_param *param = config_get_param(CONF_AUDIO_OUTPUT_FORMAT);
if (NULL == param || NULL == param->value)
return;
......@@ -182,6 +186,8 @@ void finishAudioDriver(void)
free(audioOutputArray);
audioOutputArray = NULL;
audioOutputArraySize = 0;
notify_deinit(&audio_output_client_notify);
}
bool
......@@ -392,9 +398,11 @@ void readAudioDevicesState(FILE *fp)
assert(audioOutputArraySize != 0);
while (myFgets(buffer, AUDIO_BUFFER_SIZE, fp)) {
while (fgets(buffer, sizeof(buffer), fp)) {
char *c, *name;
g_strchomp(buffer);
if (!g_str_has_prefix(buffer, AUDIO_DEVICE_STATE))
continue;
......@@ -421,3 +429,57 @@ errline:
}
}
bool mixer_control_setvol(unsigned int device, int volume, int rel)
{
struct audio_output *output;
if (device >= audioOutputArraySize)
return false;
output = &audioOutputArray[device];
if (output->plugin && output->plugin->control) {
if (rel) {
int cur_volume;
if (!output->plugin->control(output->data, AC_MIXER_GETVOL, &cur_volume)) {
return false;
}
volume = volume + cur_volume;
}
if (volume > 100)
volume = 100;
else if (volume < 0)
volume = 0;
return output->plugin->control(output->data, AC_MIXER_SETVOL, &volume);
}
return false;
}
bool mixer_control_getvol(unsigned int device, int *volume)
{
struct audio_output *output;
if (device >= audioOutputArraySize)
return false;
output = &audioOutputArray[device];
if (output->plugin && output->plugin->control) {
return output->plugin->control(output->data, AC_MIXER_GETVOL, volume);
}
return false;
}
bool mixer_configure_legacy(char *name, struct config_param *param)
{
unsigned i;
struct audio_output *output;
for (i = 0; i < audioOutputArraySize; ++i) {
output = &audioOutputArray[i];
if (output && output->plugin && !strcmp(name, output->plugin->name)) {
if (output->plugin->control) {
g_debug("reconfiguring %s mixer\n", name);
return output->plugin->control(output->data, AC_MIXER_CONFIGURE, param);
}
}
}
return false;
}
......@@ -27,6 +27,9 @@
struct audio_format;
struct tag;
struct client;
struct config_param;
unsigned int audio_output_count(void);
void getOutputAudioFormat(const struct audio_format *inFormat,
struct audio_format *outFormat);
......@@ -68,4 +71,8 @@ void readAudioDevicesState(FILE *fp);
void saveAudioDevicesState(FILE *fp);
bool mixer_control_setvol(unsigned int device, int volume, int rel);
bool mixer_control_getvol(unsigned int device, int *volume);
bool mixer_configure_legacy(char *name, struct config_param *param);
#endif
......@@ -17,14 +17,10 @@
*/
#include "buffer2array.h"
#include "os_compat.h"
static inline
int
isWhiteSpace(char c)
{
return (c == ' ' || c == '\t');
}
#include <glib.h>
#include <string.h>
int buffer2array(char *buffer, char *array[], const int max)
{
......@@ -44,19 +40,20 @@ int buffer2array(char *buffer, char *array[], const int max)
}
}
} else {
while (isWhiteSpace(*c))
++c;
array[i++] = c++;
c = g_strchug(c);
if (*c == '\0')
return i;
while (!isWhiteSpace(*c) && *c != '\0')
array[i++] = c++;
while (!g_ascii_isspace(*c) && *c != '\0')
++c;
}
if (*c == '\0')
return i;
*(c++) = '\0';
while (isWhiteSpace(*c))
++c;
c = g_strchug(c);
}
return i;
}
......
......@@ -23,14 +23,12 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
#include <sys/socket.h>
struct client;
struct sockaddr;
void client_manager_init(void);
void client_manager_deinit(void);
int client_manager_io(void);
void client_manager_expire(void);
void client_new(int fd, const struct sockaddr *addr, int uid);
......
/*
* Copyright (C) 2003-2008 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cmdline.h"
#include "path.h"
#include "conf.h"
#include "decoder_list.h"
#include "config.h"
#include "audioOutput.h"
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
#endif
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#define SYSTEM_CONFIG_FILE_LOCATION "/etc/mpd.conf"
#define USER_CONFIG_FILE_LOCATION1 ".mpdconf"
#define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf"
G_GNUC_NORETURN
static void version(void)
{
puts(PACKAGE " (MPD: Music Player Daemon) " VERSION " \n"
"\n"
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
"Copyright (C) 2008 Max Kellermann <max@duempel.org>\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n"
"Supported formats:\n");
decoder_plugin_init_all();
decoder_plugin_print_all_suffixes(stdout);
puts("\n"
"Supported decoders:\n");
decoder_plugin_print_all_decoders(stdout);
puts("\n"
"Supported outputs:\n");
printAllOutputPluginTypes(stdout);
#ifdef ENABLE_ARCHIVE
puts("\n"
"Supported archives:\n");
archive_plugin_init_all();
archive_plugin_print_all_suffixes(stdout);
#endif
exit(EXIT_SUCCESS);
}
#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 12)
static const char *summary =
"Music Player Daemon - a daemon for playing music.";
#endif
void parseOptions(int argc, char **argv, Options *options)
{
GError *error = NULL;
GOptionContext *context;
bool ret;
static gboolean option_version,
option_create_db, option_no_create_db, option_no_daemon;
const GOptionEntry entries[] = {
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
"print version number", NULL },
{ "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill,
"kill the currently running mpd session", NULL },
{ "create-db", 0, 0, G_OPTION_ARG_NONE, &option_create_db,
"force (re)creation of database", NULL },
{ "no-create-db", 0, 0, G_OPTION_ARG_NONE, &option_no_create_db,
"don't create database, even if it doesn't exist", NULL },
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon,
"don't detach from console", NULL },
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->stdOutput,
"print messages to stderr", NULL },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose,
"verbose logging", NULL },
{ .long_name = NULL }
};
options->kill = false;
options->daemon = true;
options->stdOutput = false;
options->verbose = false;
options->createDB = 0;
context = g_option_context_new("[path/to/mpd.conf]");
g_option_context_add_main_entries(context, entries, NULL);
#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 12)
g_option_context_set_summary(context, summary);
#endif
ret = g_option_context_parse(context, &argc, &argv, &error);
g_option_context_free(context);
if (!ret) {
g_error("option parsing failed: %s\n", error->message);
exit(1);
}
if (option_version)
version();
if (option_create_db && option_no_create_db)
g_error("Cannot use both --create-db and --no-create-db\n");
if (option_no_create_db)
options->createDB = -1;
else if (option_create_db)
options->createDB = 1;
options->daemon = !option_no_daemon;
if (argc <= 1) {
/* default configuration file path */
char *path1;
char *path2;
path1 = g_build_filename(g_get_home_dir(),
USER_CONFIG_FILE_LOCATION1, NULL);
path2 = g_build_filename(g_get_home_dir(),
USER_CONFIG_FILE_LOCATION2, NULL);
if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
config_read_file(path1);
else if (g_file_test(path2, G_FILE_TEST_IS_REGULAR))
config_read_file(path2);
else if (g_file_test(SYSTEM_CONFIG_FILE_LOCATION,
G_FILE_TEST_IS_REGULAR))
config_read_file(SYSTEM_CONFIG_FILE_LOCATION);
g_free(path1);
g_free(path2);
} else if (argc == 2) {
/* specified configuration file */
config_read_file(argv[1]);
} else
g_error("too many arguments");
}
/*
* Copyright (C) 2003-2008 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CMDLINE_H
#define CMDLINE_H
#include <glib.h>
typedef struct _Options {
gboolean kill;
gboolean daemon;
gboolean stdOutput;
gboolean verbose;
int createDB;
} Options;
void parseOptions(int argc, char **argv, Options *options);
#endif
......@@ -29,7 +29,6 @@
#include "permission.h"
#include "buffer2array.h"
#include "log.h"
#include "utils.h"
#include "stored_playlist.h"
#include "ack.h"
#include "audio.h"
......@@ -38,8 +37,13 @@
#include "client.h"
#include "tag_print.h"
#include "path.h"
#include "os_compat.h"
#include "idle.h"
#include "config.h"
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#define COMMAND_STATUS_VOLUME "volume"
#define COMMAND_STATUS_STATE "state"
......@@ -75,6 +79,7 @@ struct command {
/* this should really be "need a non-negative integer": */
static const char need_positive[] = "need a positive integer"; /* no-op */
static const char need_range[] = "need a range";
/* FIXME: redundant error messages */
static const char check_integer[] = "\"%s\" is not a integer";
......@@ -156,6 +161,77 @@ check_int(struct client *client, int *value_r,
return true;
}
static bool G_GNUC_PRINTF(5, 6)
check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
const char *s, const char *fmt, ...)
{
char *test, *test2;
long value;
value = strtol(s, &test, 10);
if (*test != '\0' && *test != ':') {
va_list args;
va_start(args, fmt);
command_error_v(client, ACK_ERROR_ARG, fmt, args);
va_end(args);
return false;
}
if (value == -1 && *test == 0) {
/* compatibility with older MPD versions: specifying
"-1" makes MPD display the whole list */
*value_r1 = 0;
*value_r2 = UINT_MAX;
return true;
}
if (value < 0) {
command_error(client, ACK_ERROR_ARG,
"Number is negative: %s", s);
return false;
}
#if LONG_MAX > UINT_MAX
if (value > UINT_MAX) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
}
#endif
*value_r1 = (unsigned)value;
if (*test == ':') {
value = strtol(++test, &test2, 10);
if (*test2 != '\0' || test == test2) {
va_list args;
va_start(args, fmt);
command_error_v(client, ACK_ERROR_ARG, fmt, args);
va_end(args);
return false;
}
if (value < 0) {
command_error(client, ACK_ERROR_ARG,
"Number is negative: %s", s);
return false;
}
#if LONG_MAX > UINT_MAX
if (value > UINT_MAX) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
}
#endif
*value_r2 = (unsigned)value;
} else {
*value_r2 = (unsigned)value + 1;
}
return true;
}
static bool
check_unsigned(struct client *client, unsigned *value_r, const char *s)
{
......@@ -257,14 +333,21 @@ print_spl_list(struct client *client, GPtrArray *list)
struct stored_playlist_info *playlist =
g_ptr_array_index(list, i);
time_t t;
#ifndef WIN32
struct tm tm;
#endif
char timestamp[32];
client_printf(client, "playlist: %s\n", playlist->name);
t = playlist->mtime;
strftime(timestamp, sizeof(timestamp), "%FT%TZ",
gmtime_r(&t, &tm));
#ifdef WIN32
gmtime(&t)
#else
gmtime_r(&t, &tm)
#endif
);
client_printf(client, "Last-Modified: %s\n", timestamp);
}
}
......@@ -275,7 +358,7 @@ handle_urlhandlers(struct client *client,
{
if (client_get_uid(client) > 0)
client_puts(client, "handler: file://\n");
printRemoteUrlHandlers(client);
print_supported_uri_schemes(client);
return COMMAND_RETURN_OK;
}
......@@ -330,7 +413,7 @@ handle_currentsong(struct client *client,
if (song < 0)
return COMMAND_RETURN_OK;
result = playlistInfo(client, song);
result = playlistInfo(client, song, song + 1);
return print_playlist_result(client, result);
}
......@@ -379,7 +462,7 @@ handle_status(struct client *client,
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
COMMAND_STATUS_CROSSFADE ": %i\n"
COMMAND_STATUS_STATE ": %s\n",
getVolumeLevel(),
volume_level_get(),
getPlaylistRepeatStatus(),
getPlaylistRandomStatus(),
getPlaylistVersion(),
......@@ -438,25 +521,30 @@ handle_close(G_GNUC_UNUSED struct client *client,
static enum command_return
handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *path = argv[1];
char *uri = argv[1];
enum playlist_result result;
if (strncmp(path, "file:///", 8) == 0) {
result = playlist_append_file(path + 7, client_get_uid(client),
if (strncmp(uri, "file:///", 8) == 0) {
#ifdef WIN32
result = PLAYLIST_RESULT_DENIED;
#else
result = playlist_append_file(uri + 7, client_get_uid(client),
NULL);
#endif
return print_playlist_result(client, result);
}
if (isRemoteUrl(path))
return addToPlaylist(path, NULL);
if (uri_has_scheme(uri)) {
if (!uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported URI scheme");
return COMMAND_RETURN_ERROR;
}
if (uri_has_scheme(path)) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported URI scheme");
return COMMAND_RETURN_ERROR;
return addToPlaylist(uri, NULL);
}
result = addAllIn(path);
result = addAllIn(uri);
if (result == (enum playlist_result)-1) {
command_error(client, ACK_ERROR_NO_EXIST,
"directory or file not found");
......@@ -469,15 +557,27 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
static enum command_return
handle_addid(struct client *client, int argc, char *argv[])
{
char *uri = argv[1];
unsigned added_id;
enum playlist_result result;
if (strncmp(argv[1], "file:///", 8) == 0)
result = playlist_append_file(argv[1] + 7,
if (strncmp(uri, "file:///", 8) == 0) {
#ifdef WIN32
result = PLAYLIST_RESULT_DENIED;
#else
result = playlist_append_file(uri + 7,
client_get_uid(client),
&added_id);
else
result = addToPlaylist(argv[1], &added_id);
#endif
} else {
if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported URI scheme");
return COMMAND_RETURN_ERROR;
}
result = addToPlaylist(uri, &added_id);
}
if (result != PLAYLIST_RESULT_SUCCESS)
return print_playlist_result(client, result);
......@@ -564,7 +664,7 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
enum playlist_result result;
result = loadPlaylist(client, argv[1]);
result = loadPlaylist(argv[1]);
return print_playlist_result(client, result);
}
......@@ -596,13 +696,16 @@ handle_listplaylistinfo(struct client *client,
static enum command_return
handle_lsinfo(struct client *client, int argc, char *argv[])
{
const char *path = "";
const char *uri;
const struct directory *directory;
if (argc == 2)
path = argv[1];
uri = argv[1];
else
/* default is root directory */
uri = "";
directory = db_get_directory(path);
directory = db_get_directory(uri);
if (directory == NULL) {
command_error(client, ACK_ERROR_NO_EXIST,
"directory not found");
......@@ -611,7 +714,7 @@ handle_lsinfo(struct client *client, int argc, char *argv[])
directory_print(client, directory);
if (isRootDirectory(path)) {
if (isRootDirectory(uri)) {
GPtrArray *list = spl_list();
if (list != NULL) {
print_spl_list(client, list);
......@@ -663,13 +766,14 @@ handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[
static enum command_return
handle_playlistinfo(struct client *client, int argc, char *argv[])
{
int song = -1;
unsigned start = 0, end = UINT_MAX;
enum playlist_result result;
if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
if (argc == 2 && !check_range(client, &start, &end,
argv[1], need_range))
return COMMAND_RETURN_ERROR;
result = playlistInfo(client, song);
result = playlistInfo(client, start, end);
return print_playlist_result(client, result);
}
......@@ -893,7 +997,7 @@ handle_volume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &change, argv[1], need_integer))
return COMMAND_RETURN_ERROR;
ret = changeVolumeLevel(change, 1);
ret = volume_level_change(change, 1);
if (ret == -1)
command_error(client, ACK_ERROR_SYSTEM,
"problems setting volume");
......@@ -909,7 +1013,7 @@ handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &level, argv[1], need_integer))
return COMMAND_RETURN_ERROR;
ret = changeVolumeLevel(level, 0);
ret = volume_level_change(level, 0);
if (ret == -1)
command_error(client, ACK_ERROR_SYSTEM,
"problems setting volume");
......@@ -1224,17 +1328,19 @@ static enum command_return
handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *playlist = argv[1];
char *path = argv[2];
char *uri = argv[2];
enum playlist_result result;
if (isRemoteUrl(path))
result = spl_append_uri(path, playlist);
else if (uri_has_scheme(path)) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported URI scheme");
return COMMAND_RETURN_ERROR;
if (uri_has_scheme(uri)) {
if (!uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
"unsupported URI scheme");
return COMMAND_RETURN_ERROR;
}
result = spl_append_uri(uri, playlist);
} else
result = addAllInToStoredPlaylist(path, playlist);
result = addAllInToStoredPlaylist(uri, playlist);
if (result == (enum playlist_result)-1) {
command_error(client, ACK_ERROR_NO_EXIST,
......
......@@ -20,9 +20,12 @@
*
*/
#include "os_compat.h"
#include "compress.h"
#include "utils.h"
#include <glib.h>
#include <stdint.h>
#include <string.h>
#ifdef USE_X
#include <X11/Xlib.h>
......@@ -64,7 +67,7 @@ void CompressCfg(int show_mon, int anticlip, int target, int gainmax,
prefs.buckets = buckets;
/* Allocate the peak structure */
peaks = xrealloc(peaks, sizeof(int)*prefs.buckets);
peaks = g_realloc(peaks, sizeof(int)*prefs.buckets);
if (prefs.buckets > lastsize)
memset(peaks + lastsize, 0, sizeof(int)*(prefs.buckets
......@@ -163,8 +166,7 @@ void CompressFree(void)
}
#endif
if (peaks)
free(peaks);
g_free(peaks);
}
void CompressDo(void *data, unsigned int length)
......
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "condition.h"
#include "utils.h"
#include "log.h"
#include <sys/time.h>
#include <string.h>
void cond_init(struct condition *cond)
{
xpthread_mutex_init(&cond->mutex, NULL);
xpthread_cond_init(&cond->cond, NULL);
}
void cond_enter(struct condition *cond)
{
pthread_mutex_lock(&cond->mutex);
}
void cond_leave(struct condition *cond)
{
pthread_mutex_unlock(&cond->mutex);
}
void cond_wait(struct condition *cond)
{
pthread_cond_wait(&cond->cond, &cond->mutex);
}
static struct timespec * ts_timeout(struct timespec *ts, const long sec)
{
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec + sec;
ts->tv_nsec = tv.tv_usec * 1000;
return ts;
}
int cond_timedwait(struct condition *cond, const long sec)
{
struct timespec ts;
int ret = pthread_cond_timedwait(&cond->cond, &cond->mutex,
ts_timeout(&ts, sec));
if (!ret || ret == ETIMEDOUT)
return ret;
FATAL("cond_timedwait: %s\n", strerror(ret));
return ret;
}
int cond_signal_async(struct condition *cond)
{
if (!pthread_mutex_trylock(&cond->mutex)) {
pthread_cond_signal(&cond->cond);
pthread_mutex_unlock(&cond->mutex);
return 0;
}
return EBUSY;
}
void cond_signal_sync(struct condition *cond)
{
pthread_cond_signal(&cond->cond);
}
void cond_destroy(struct condition *cond)
{
xpthread_cond_destroy(&cond->cond);
xpthread_mutex_destroy(&cond->mutex);
}
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MPD_CONDITION_H
#define MPD_CONDITION_H
#include <pthread.h>
struct condition {
pthread_mutex_t mutex;
pthread_cond_t cond;
};
void cond_init(struct condition *cond);
/**
* The thread which shall be notified by this object must call this
* function before any cond_wait() invocation. It locks the mutex.
*/
void cond_enter(struct condition *cond);
/**
* Neutralize cond_leave().
*/
void cond_leave(struct condition *cond);
/**
* Wait for a conditio. Return immediately if we have already
* been notified since we last returned from cond_wait().
*/
void cond_wait(struct condition *cond);
/**
* Wait for a condition with timeout
*
* @param sec number of seconds to wait for (subject to change)
*
* @return ETIMEDOUT if timed out, 0 if notification was received
*/
int cond_timedwait(struct condition *cond, const long sec);
/**
* Notify the thread there is a waiter. This function never blocks.
*
* @return EBUSY if it was unable to lock the mutex, 0 on success
*/
int cond_signal_async(struct condition *cond);
/**
* Notify the thread synchronously, i.e. wait until it can deliver
* the notification.
*/
void cond_signal_sync(struct condition *cond);
/**
* cond_destroy - destroy the cond and internal structures
*/
void cond_destroy(struct condition *cond);
#endif /* CONDITION_H */
......@@ -20,6 +20,7 @@
#define MPD_CONF_H
#include <stdbool.h>
#include <glib.h>
#define CONF_MUSIC_DIR "music_directory"
#define CONF_PLAYLIST_DIR "playlist_directory"
......@@ -67,40 +68,57 @@
#define CONF_BOOL_UNSET -1
#define CONF_BOOL_INVALID -2
typedef struct _BlockParam {
struct block_param {
char *name;
char *value;
int line;
} BlockParam;
};
typedef struct _ConfigParam {
struct config_param {
char *value;
unsigned int line;
BlockParam *blockParams;
int numberOfBlockParams;
} ConfigParam;
void initConf(void);
void finishConf(void);
struct block_param *block_params;
int num_block_params;
};
void readConf(const char *file);
void config_global_init(void);
void config_global_finish(void);
void config_read_file(const char *file);
/* don't free the returned value
set _last_ to NULL to get first entry */
ConfigParam *getNextConfigParam(const char *name, ConfigParam * last);
#define getConfigParam(name) getNextConfigParam(name, NULL)
struct config_param *
config_get_next_param(const char *name, struct config_param *last);
char *getConfigParamValue(const char *name);
static inline struct config_param *
config_get_param(const char *name)
{
return config_get_next_param(name, NULL);
}
BlockParam *getBlockParam(ConfigParam * param, const char *name);
const char *
config_get_string(const char *name, const char *default_value);
ConfigParam *parseConfigFilePath(const char *name, int force);
struct block_param *
getBlockParam(struct config_param *param, const char *name);
int getBoolConfigParam(const char *name, int force);
struct config_param *
parseConfigFilePath(const char *name, int force);
bool config_get_bool(const char *name, bool default_value);
int getBoolBlockParam(ConfigParam *param, const char *name, int force);
bool
config_get_block_bool(struct config_param *param, const char *name,
bool default_value);
struct config_param *
newConfigParam(const char *value, int line);
void config_param_free(gpointer data, gpointer user_data);
void
addBlockParam(struct config_param *param, const char *name, const char *value, int line);
#endif
......@@ -19,7 +19,7 @@
#include "crossfade.h"
#include "audio.h"
#include "pcm_utils.h"
#include "pcm_mix.h"
#include "pipe.h"
#include "audio_format.h"
#include "tag.h"
......
/*
* Copyright (C) 2003-2008 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "daemon.h"
#include "conf.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void
daemonize_close_stdin(void)
{
int fd = open("/dev/null", O_RDONLY);
if (fd < 0)
close(STDIN_FILENO);
else if (fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
}
void
daemonize(Options *options)
{
#ifndef WIN32
FILE *fp = NULL;
struct config_param *pidFileParam =
parseConfigFilePath(CONF_PID_FILE, 0);
if (pidFileParam) {
/* do this before daemon'izing so we can fail gracefully if we can't
* write to the pid file */
g_debug("opening pid file");
fp = fopen(pidFileParam->value, "w+");
if (!fp) {
g_error("could not open %s \"%s\" (at line %i) for writing: %s",
CONF_PID_FILE, pidFileParam->value,
pidFileParam->line, strerror(errno));
}
}
if (options->daemon) {
int pid;
fflush(NULL);
pid = fork();
if (pid > 0)
_exit(EXIT_SUCCESS);
else if (pid < 0) {
g_error("problems fork'ing for daemon!");
}
if (chdir("/") < 0) {
g_error("problems changing to root directory");
}
setsid();
g_debug("daemonized!");
}
if (pidFileParam) {
g_debug("writing pid file");
fprintf(fp, "%lu\n", (unsigned long)getpid());
fclose(fp);
}
#else
/* no daemonization on WIN32 */
(void)options;
#endif
}
/*
* Copyright (C) 2003-2008 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DAEMON_H
#define DAEMON_H
#include "cmdline.h"
/**
* Close stdin (fd 0) and re-open it as /dev/null.
*/
void
daemonize_close_stdin(void);
void
daemonize(Options *options);
#endif
......@@ -22,18 +22,22 @@
#include "directory_save.h"
#include "song.h"
#include "conf.h"
#include "log.h"
#include "ls.h"
#include "path.h"
#include "stats.h"
#include "utils.h"
#include "dbUtils.h"
#include "update.h"
#include "main_notify.h"
#include "config.h"
#include <glib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <glib.h>
#include <stdlib.h>
#include <errno.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "database"
static struct directory *music_root;
......@@ -42,21 +46,7 @@ static time_t directory_dbModTime;
void
db_init(void)
{
unsigned ret;
music_root = directory_new("", NULL);
ret = directory_update_init(NULL);
if (ret == 0)
FATAL("directory update failed\n");
do {
wait_main_task();
reap_update_task();
} while (isUpdatingDB());
stats.numberOfSongs = countSongsIn(NULL);
stats.dbPlayTime = sumSongTimesIn(NULL);
}
void
......@@ -88,10 +78,10 @@ db_get_song(const char *file)
struct song *song = NULL;
struct directory *directory;
char *dir = NULL;
char *duplicated = xstrdup(file);
char *duplicated = g_strdup(file);
char *shortname = strrchr(duplicated, '/');
DEBUG("get song: %s\n", file);
g_debug("get song: %s", file);
if (!shortname) {
shortname = duplicated;
......@@ -133,7 +123,7 @@ db_walk(const char *name,
static char *
db_get_file(void)
{
ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1);
struct config_param *param = parseConfigFilePath(CONF_DB_FILE, 1);
assert(param);
assert(param->value);
......@@ -141,7 +131,7 @@ db_get_file(void)
return param->value;
}
int
bool
db_check(void)
{
struct stat st;
......@@ -157,74 +147,74 @@ db_check(void)
/* Check that the parent part of the path is a directory */
if (stat(dirPath, &st) < 0) {
g_free(dirPath);
ERROR("Couldn't stat parent directory of db file "
"\"%s\": %s\n", dbFile, strerror(errno));
return -1;
g_warning("Couldn't stat parent directory of db file "
"\"%s\": %s", dbFile, strerror(errno));
return false;
}
if (!S_ISDIR(st.st_mode)) {
g_free(dirPath);
ERROR("Couldn't create db file \"%s\" because the "
"parent path is not a directory\n", dbFile);
return -1;
g_warning("Couldn't create db file \"%s\" because the "
"parent path is not a directory", dbFile);
return false;
}
/* Check if we can write to the directory */
if (access(dirPath, R_OK | W_OK)) {
ERROR("Can't create db file in \"%s\": %s\n", dirPath,
strerror(errno));
g_warning("Can't create db file in \"%s\": %s",
dirPath, strerror(errno));
g_free(dirPath);
return -1;
return false;
}
g_free(dirPath);
return 0;
return true;
}
/* Path exists, now check if it's a regular file */
if (stat(dbFile, &st) < 0) {
ERROR("Couldn't stat db file \"%s\": %s\n", dbFile,
strerror(errno));
return -1;
g_warning("Couldn't stat db file \"%s\": %s",
dbFile, strerror(errno));
return false;
}
if (!S_ISREG(st.st_mode)) {
ERROR("db file \"%s\" is not a regular file\n", dbFile);
return -1;
g_warning("db file \"%s\" is not a regular file", dbFile);
return false;
}
/* And check that we can write to it */
if (access(dbFile, R_OK | W_OK)) {
ERROR("Can't open db file \"%s\" for reading/writing: %s\n",
dbFile, strerror(errno));
return -1;
g_warning("Can't open db file \"%s\" for reading/writing: %s",
dbFile, strerror(errno));
return false;
}
return 0;
return true;
}
int
bool
db_save(void)
{
FILE *fp;
char *dbFile = db_get_file();
struct stat st;
DEBUG("removing empty directories from DB\n");
g_debug("removing empty directories from DB");
directory_prune_empty(music_root);
DEBUG("sorting DB\n");
g_debug("sorting DB");
directory_sort(music_root);
DEBUG("writing DB\n");
g_debug("writing DB");
fp = fopen(dbFile, "w");
if (!fp) {
ERROR("unable to write to db file \"%s\": %s\n",
dbFile, strerror(errno));
return -1;
g_warning("unable to write to db file \"%s\": %s",
dbFile, strerror(errno));
return false;
}
/* block signals when writing the db so we don't get a corrupted db */
......@@ -234,10 +224,10 @@ db_save(void)
fprintf(fp, "%s\n", DIRECTORY_INFO_END);
if (directory_save(fp, music_root) < 0) {
ERROR("Failed to write to database file: %s\n",
strerror(errno));
g_warning("Failed to write to database file: %s",
strerror(errno));
while (fclose(fp) && errno == EINTR);
return -1;
return false;
}
while (fclose(fp) && errno == EINTR);
......@@ -245,82 +235,87 @@ db_save(void)
if (stat(dbFile, &st) == 0)
directory_dbModTime = st.st_mtime;
return 0;
return true;
}
int
bool
db_load(void)
{
FILE *fp = NULL;
char *dbFile = db_get_file();
struct stat st;
char buffer[100];
int foundFsCharset = 0;
int foundVersion = 0;
bool foundFsCharset = false, foundVersion = false;
assert(music_root != NULL);
if (!music_root)
music_root = directory_new("", NULL);
while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ;
if (fp == NULL) {
ERROR("unable to open db file \"%s\": %s\n",
dbFile, strerror(errno));
return -1;
g_warning("unable to open db file \"%s\": %s",
dbFile, strerror(errno));
return false;
}
/* get initial info */
if (!myFgets(buffer, sizeof(buffer), fp))
FATAL("Error reading db, fgets\n");
if (!fgets(buffer, sizeof(buffer), fp))
g_error("Error reading db, fgets");
g_strchomp(buffer);
if (0 != strcmp(DIRECTORY_INFO_BEGIN, buffer)) {
ERROR("db info not found in db file\n");
ERROR("you should recreate the db using --create-db\n");
g_warning("db info not found in db file; "
"you should recreate the db using --create-db");
while (fclose(fp) && errno == EINTR) ;
return -1;
return false;
}
while (myFgets(buffer, sizeof(buffer), fp) &&
0 != strcmp(DIRECTORY_INFO_END, buffer)) {
while (fgets(buffer, sizeof(buffer), fp) &&
!g_str_has_prefix(buffer, DIRECTORY_INFO_END)) {
g_strchomp(buffer);
if (g_str_has_prefix(buffer, DIRECTORY_MPD_VERSION)) {
if (foundVersion)
FATAL("already found version in db\n");
foundVersion = 1;
g_error("already found version in db");
foundVersion = true;
} else if (g_str_has_prefix(buffer, DIRECTORY_FS_CHARSET)) {
char *fsCharset;
char *tempCharset;
const char *tempCharset;
if (foundFsCharset)
FATAL("already found fs charset in db\n");
g_error("already found fs charset in db");
foundFsCharset = 1;
foundFsCharset = true;
fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]);
if ((tempCharset = getConfigParamValue(CONF_FS_CHARSET))
tempCharset = config_get_string(CONF_FS_CHARSET, NULL);
if (tempCharset != NULL
&& strcmp(fsCharset, tempCharset)) {
WARNING("Using \"%s\" for the "
"filesystem charset "
"instead of \"%s\"\n",
g_message("Using \"%s\" for the "
"filesystem charset "
"instead of \"%s\"; "
"maybe you need to "
"recreate the db?",
fsCharset, tempCharset);
WARNING("maybe you need to "
"recreate the db?\n");
path_set_fs_charset(fsCharset);
}
} else
FATAL("directory: unknown line in db info: %s\n",
buffer);
g_error("unknown line in db info: %s",
buffer);
}
DEBUG("reading DB\n");
g_debug("reading DB");
directory_load(fp, music_root);
while (fclose(fp) && errno == EINTR) ;
stats.numberOfSongs = countSongsIn(NULL);
stats.dbPlayTime = sumSongTimesIn(NULL);
stats_update();
if (stat(dbFile, &st) == 0)
directory_dbModTime = st.st_mtime;
return 0;
return true;
}
time_t
......
......@@ -21,15 +21,29 @@
#define MPD_DATABASE_H
#include <sys/time.h>
#include <stdbool.h>
struct directory;
/**
* Initialize the database library.
*/
void
db_init(void);
void
db_finish(void);
/**
* Clear the database.
*/
static inline void
db_clear(void)
{
db_finish();
db_init();
}
struct directory *
db_get_root(void);
......@@ -43,16 +57,27 @@ int db_walk(const char *name,
int (*forEachSong)(struct song *, void *),
int (*forEachDir)(struct directory *, void *), void *data);
int
bool
db_check(void);
int
bool
db_save(void);
int
bool
db_load(void);
time_t
db_get_mtime(void);
/**
* Returns true if there is a valid database file on the disk.
*/
static inline bool
db_exists(void)
{
/* mtime is set only if the database file was loaded or saved
successfully */
return db_get_mtime() > 0;
}
#endif
......@@ -21,7 +21,6 @@
#include "directory.h"
#include "database.h"
#include "client.h"
#include "utils.h"
#include "playlist.h"
#include "song.h"
#include "song_print.h"
......@@ -32,6 +31,8 @@
#include <glib.h>
#include <stdlib.h>
typedef struct _ListCommandItem {
int8_t tagType;
int numConditionals;
......@@ -63,9 +64,10 @@ static int
printDirectoryInDirectory(struct directory *directory, void *data)
{
struct client *client = data;
if (!isRootDirectory(directory->path)) {
if (!directory_is_root(directory))
client_printf(client, "directory: %s\n", directory_get_path(directory));
}
return 0;
}
......@@ -100,7 +102,7 @@ int searchForSongsIn(struct client *client, const char *name,
int ret;
int i;
char **originalNeedles = xmalloc(numItems * sizeof(char *));
char **originalNeedles = g_new(char *, numItems);
struct search_data data;
for (i = 0; i < numItems; i++) {
......@@ -275,7 +277,7 @@ unsigned long sumSongTimesIn(const char *name)
static ListCommandItem *newListCommandItem(int tagType, int numConditionals,
LocateTagItem * conditionals)
{
ListCommandItem *item = xmalloc(sizeof(ListCommandItem));
ListCommandItem *item = g_new(ListCommandItem, 1);
item->tagType = tagType;
item->numConditionals = numConditionals;
......@@ -373,7 +375,7 @@ sumSavedFilenameMemoryInDirectory(struct directory *dir, void *data)
{
int *sum = data;
if (isRootDirectory(dir->path))
if (directory_is_root(dir))
return 0;
*sum += (strlen(directory_get_path(dir)) + 1
......
......@@ -21,23 +21,29 @@
#include "_flac_common.h"
#include <glib.h>
#include <FLAC/format.h>
#include <FLAC/metadata.h>
void init_FlacData(FlacData * data, struct decoder * decoder,
struct input_stream *inStream)
#include <assert.h>
void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream)
{
data->time = 0;
data->position = 0;
data->bitRate = 0;
data->bit_rate = 0;
data->decoder = decoder;
data->inStream = inStream;
data->replayGainInfo = NULL;
data->input_stream = input_stream;
data->replay_gain_info = NULL;
data->tag = NULL;
}
static int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block,
const char *cmnt, float *fl)
static bool
flac_find_float_comment(const FLAC__StreamMetadata *block,
const char *cmnt, float *fl)
{
int offset =
FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, cmnt);
......@@ -55,101 +61,120 @@ static int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block,
*fl = (float)atof((char *)p);
p[len] = tmp;
return 1;
return true;
}
}
return 0;
return false;
}
/* replaygain stuff by AliasMrJones */
static void flacParseReplayGain(const FLAC__StreamMetadata * block,
FlacData * data)
static void
flac_parse_replay_gain(const FLAC__StreamMetadata *block,
struct flac_data *data)
{
int found = 0;
bool found;
if (data->replayGainInfo)
replay_gain_info_free(data->replayGainInfo);
if (data->replay_gain_info)
replay_gain_info_free(data->replay_gain_info);
data->replayGainInfo = replay_gain_info_new();
data->replay_gain_info = replay_gain_info_new();
found |= flacFindVorbisCommentFloat(block, "replaygain_album_gain",
&data->replayGainInfo->tuples[REPLAY_GAIN_ALBUM].gain);
found |= flacFindVorbisCommentFloat(block, "replaygain_album_peak",
&data->replayGainInfo->tuples[REPLAY_GAIN_ALBUM].peak);
found |= flacFindVorbisCommentFloat(block, "replaygain_track_gain",
&data->replayGainInfo->tuples[REPLAY_GAIN_TRACK].gain);
found |= flacFindVorbisCommentFloat(block, "replaygain_track_peak",
&data->replayGainInfo->tuples[REPLAY_GAIN_TRACK].peak);
found = flac_find_float_comment(block, "replaygain_album_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain) ||
flac_find_float_comment(block, "replaygain_album_peak",
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak) ||
flac_find_float_comment(block, "replaygain_track_gain",
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain) ||
flac_find_float_comment(block, "replaygain_track_peak",
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak);
if (!found) {
replay_gain_info_free(data->replayGainInfo);
data->replayGainInfo = NULL;
replay_gain_info_free(data->replay_gain_info);
data->replay_gain_info = NULL;
}
}
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
* tag names match */
static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber";
static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static unsigned int commentMatchesAddToTag(const
FLAC__StreamMetadata_VorbisComment_Entry
* entry, unsigned int itemType,
struct tag ** tag)
/**
* Checks if the specified name matches the entry's name, and if yes,
* returns the comment value (not null-temrinated).
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, size_t *length_r)
{
const char *str;
size_t slen;
int vlen;
switch (itemType) {
case TAG_ITEM_TRACK:
str = VORBIS_COMMENT_TRACK_KEY;
break;
case TAG_ITEM_DISC:
str = VORBIS_COMMENT_DISC_KEY;
break;
default:
str = mpdTagItemKeys[itemType];
size_t name_length = strlen(name);
const char *comment = (const char*)entry->entry;
if (entry->length > name_length &&
g_ascii_strncasecmp(comment, name, name_length) == 0 &&
comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
slen = strlen(str);
vlen = entry->length - slen - 1;
if ((vlen > 0) && (0 == strncasecmp(str, (char *)entry->entry, slen))
&& (*(entry->entry + slen) == '=')) {
if (!*tag)
*tag = tag_new();
return NULL;
}
tag_add_item_n(*tag, itemType,
(char *)(entry->entry + slen + 1), vlen);
/**
* Check if the comment's name equals the passed name, and if so, copy
* the comment value into the tag.
*/
static bool
flac_copy_comment(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type)
{
const char *value;
size_t value_length;
return 1;
value = flac_comment_value(entry, name, &value_length);
if (value != NULL) {
tag_add_item_n(tag, tag_type, value, value_length);
return true;
}
return 0;
return false;
}
struct tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
struct tag * tag)
/* tracknumber is used in VCs, MPD uses "track" ..., all the other
* tag names match */
static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber";
static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static void
flac_parse_comment(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment_Entry *entry)
{
unsigned int i, j;
FLAC__StreamMetadata_VorbisComment_Entry *comments;
assert(tag != NULL);
if (flac_copy_comment(tag, entry, VORBIS_COMMENT_TRACK_KEY,
TAG_ITEM_TRACK) ||
flac_copy_comment(tag, entry, VORBIS_COMMENT_DISC_KEY,
TAG_ITEM_DISC) ||
flac_copy_comment(tag, entry, "album artist",
TAG_ITEM_ALBUM_ARTIST))
return;
comments = block->data.vorbis_comment.comments;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(tag, entry,
mpdTagItemKeys[i], i))
return;
}
for (i = block->data.vorbis_comment.num_comments; i != 0; --i) {
for (j = TAG_NUM_OF_ITEM_TYPES; j--;) {
if (commentMatchesAddToTag(comments, j, &tag))
break;
}
comments++;
}
void
flac_vorbis_comments_to_tag(struct tag *tag,
const FLAC__StreamMetadata *block)
{
FLAC__StreamMetadata_VorbisComment_Entry *comments =
block->data.vorbis_comment.comments;
return tag;
for (unsigned i = block->data.vorbis_comment.num_comments; i > 0; --i)
flac_parse_comment(tag, comments++);
}
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
FlacData * data)
struct flac_data *data)
{
const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info);
......@@ -161,7 +186,7 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
data->total_time = ((float)si->total_samples) / (si->sample_rate);
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
flacParseReplayGain(block, data);
flac_parse_replay_gain(block, data);
default:
break;
}
......@@ -169,7 +194,7 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
void flac_error_common_cb(const char *plugin,
const FLAC__StreamDecoderErrorStatus status,
mpd_unused FlacData * data)
struct flac_data *data)
{
if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
return;
......@@ -270,7 +295,7 @@ static void flac_convert(unsigned char *dest,
}
FLAC__StreamDecoderWriteStatus
flac_common_write(FlacData *data, const FLAC__Frame * frame,
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[])
{
unsigned int c_samp;
......@@ -298,11 +323,11 @@ flac_common_write(FlacData *data, const FLAC__Frame * frame,
num_channels, bytes_per_sample, buf,
c_samp, c_samp + num_samples);
cmd = decoder_data(data->decoder, data->inStream,
cmd = decoder_data(data->decoder, data->input_stream,
data->chunk,
num_samples * bytes_per_channel,
data->time, data->bitRate,
data->replayGainInfo);
data->time, data->bit_rate,
data->replay_gain_info);
switch (cmd) {
case DECODE_COMMAND_NONE:
case DECODE_COMMAND_START:
......
......@@ -141,33 +141,37 @@ typedef size_t flac_read_status_size_t;
#define FLAC_CHUNK_SIZE 4080
typedef struct {
struct flac_data {
unsigned char chunk[FLAC_CHUNK_SIZE];
float time;
unsigned int bitRate;
unsigned int bit_rate;
struct audio_format audio_format;
float total_time;
FLAC__uint64 position;
struct decoder *decoder;
struct input_stream *inStream;
struct replay_gain_info *replayGainInfo;
struct input_stream *input_stream;
struct replay_gain_info *replay_gain_info;
struct tag *tag;
} FlacData;
};
/* initializes a given FlacData struct */
void init_FlacData(FlacData * data, struct decoder * decoder,
struct input_stream *inStream);
void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream);
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
FlacData * data);
struct flac_data *data);
void flac_error_common_cb(const char *plugin,
FLAC__StreamDecoderErrorStatus status,
FlacData * data);
struct flac_data *data);
struct tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
struct tag *tag);
void
flac_vorbis_comments_to_tag(struct tag *tag,
const FLAC__StreamMetadata *block);
FLAC__StreamDecoderWriteStatus
flac_common_write(FlacData *data, const FLAC__Frame * frame,
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[]);
#endif /* _FLAC_COMMON_H */
......@@ -17,6 +17,7 @@
*/
#include "../decoder_api.h"
#include "config.h"
#define AAC_MAX_CHANNELS 6
......@@ -442,19 +443,18 @@ aac_stream_decode(struct decoder *mpd_decoder, struct input_stream *inStream)
static struct tag *aacTagDup(const char *file)
{
struct tag *ret = NULL;
int file_time = getAacTotalTime(file);
struct tag *tag;
if (file_time >= 0) {
if ((ret = tag_id3_load(file)) == NULL)
ret = tag_new();
ret->time = file_time;
} else {
if (file_time < 0) {
g_debug("aacTagDup: Failed to get total song time from: %s\n",
file);
return NULL;
}
return ret;
tag = tag_new();
tag->time = file_time;
return tag;
}
static const char *const aac_suffixes[] = { "aac", NULL };
......
......@@ -20,8 +20,9 @@
#include "../decoder_api.h"
#include <sys/stat.h>
#include <audiofile.h>
#include <af_vfs.h>
#include <assert.h>
#include <glib.h>
#undef G_LOG_DOMAIN
......@@ -44,27 +45,79 @@ static int getAudiofileTotalTime(const char *file)
return total_time;
}
static ssize_t
audiofile_file_read(AFvirtualfile *vfile, void *data, size_t nbytes)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
return input_stream_read(is, data, nbytes);
}
static long
audiofile_file_length(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
return is->size;
}
static long
audiofile_file_tell(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
return is->offset;
}
static void
audiofile_decode(struct decoder *decoder, const char *path)
audiofile_file_destroy(AFvirtualfile *vfile)
{
assert(vfile->closure != NULL);
vfile->closure = NULL;
}
static long
audiofile_file_seek(AFvirtualfile *vfile, long offset, int is_relative)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
if (input_stream_seek(is, offset, whence)) {
return is->offset;
} else {
return -1;
}
}
static AFvirtualfile *
setup_virtual_fops(struct input_stream *stream)
{
AFvirtualfile *vf = g_malloc(sizeof(AFvirtualfile));
vf->closure = stream;
vf->write = NULL;
vf->read = audiofile_file_read;
vf->length = audiofile_file_length;
vf->destroy = audiofile_file_destroy;
vf->seek = audiofile_file_seek;
vf->tell = audiofile_file_tell;
return vf;
}
static void
audiofile_streamdecode(struct decoder * decoder, struct input_stream *inStream)
{
AFvirtualfile *vf;
int fs, frame_count;
AFfilehandle af_fp;
int bits;
struct audio_format audio_format;
float total_time;
uint16_t bitRate;
struct stat st;
int ret, current = 0;
char chunk[CHUNK_SIZE];
if (stat(path, &st) < 0) {
g_warning("failed to stat: %s\n", path);
return;
}
vf = setup_virtual_fops(inStream);
af_fp = afOpenFile(path, "r", NULL);
af_fp = afOpenVirtualFile(vf, "r", NULL);
if (af_fp == AF_NULL_FILEHANDLE) {
g_warning("failed to open: %s\n", path);
g_warning("failed to input stream\n");
return;
}
......@@ -89,7 +142,7 @@ audiofile_decode(struct decoder *decoder, const char *path)
total_time = ((float)frame_count / (float)audio_format.sample_rate);
bitRate = (uint16_t)(st.st_size * 8.0 / total_time / 1000.0 + 0.5);
bitRate = (uint16_t)(inStream->size * 8.0 / total_time / 1000.0 + 0.5);
fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1);
......@@ -118,7 +171,7 @@ audiofile_decode(struct decoder *decoder, const char *path)
afCloseFile(af_fp);
}
static struct tag *audiofileTagDup(const char *file)
static struct tag *audiofile_tag_dup(const char *file)
{
struct tag *ret = NULL;
int total_time = getAudiofileTotalTime(file);
......@@ -134,13 +187,20 @@ static struct tag *audiofileTagDup(const char *file)
return ret;
}
static const char *const audiofileSuffixes[] = {
static const char *const audiofile_suffixes[] = {
"wav", "au", "aiff", "aif", NULL
};
static const char *const audiofile_mime_types[] = {
"audio/x-wav",
"audio/x-aiff",
NULL
};
const struct decoder_plugin audiofilePlugin = {
.name = "audiofile",
.file_decode = audiofile_decode,
.tag_dup = audiofileTagDup,
.suffixes = audiofileSuffixes,
.stream_decode = audiofile_streamdecode,
.tag_dup = audiofile_tag_dup,
.suffixes = audiofile_suffixes,
.mime_types = audiofile_mime_types,
};
......@@ -17,6 +17,9 @@
*/
#include "../decoder_api.h"
#include "config.h"
#include <glib.h>
#include <assert.h>
#include <stdio.h>
......@@ -26,7 +29,6 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#ifdef OLD_FFMPEG_INCLUDES
#include <avcodec.h>
......@@ -73,7 +75,7 @@ static struct ffmpeg_stream *url_to_struct(const char *url)
}
static int mpd_ffmpeg_open(URLContext *h, const char *filename,
mpd_unused int flags)
G_GNUC_UNUSED int flags)
{
struct ffmpeg_stream *stream = url_to_struct(filename);
h->priv_data = stream;
......@@ -402,6 +404,7 @@ static const char *const ffmpeg_mime_types[] = {
"application/x-ms-wmz",
"application/x-ms-wmd",
"audio/mpeg",
"audio/x-wav",
NULL
};
......
......@@ -17,8 +17,6 @@
*/
#include "../decoder_api.h"
#include "../utils.h"
#include "../log.h"
#include <glib.h>
#include <mikmod.h>
......@@ -108,8 +106,8 @@ static bool mod_initMikMod(void)
DMODE_16BITS);
if (MikMod_Init(params)) {
ERROR("Could not init MikMod: %s\n",
MikMod_strerror(MikMod_errno));
g_warning("Could not init MikMod: %s\n",
MikMod_strerror(MikMod_errno));
return false;
}
......@@ -123,7 +121,7 @@ static void mod_finishMikMod(void)
typedef struct _mod_Data {
MODULE *moduleHandle;
SBYTE *audio_buffer;
SBYTE audio_buffer[MIKMOD_FRAME_SIZE];
} mod_Data;
static mod_Data *mod_open(const char *path)
......@@ -142,9 +140,7 @@ static mod_Data *mod_open(const char *path)
/* Prevent module from looping forever */
moduleHandle->loop = 0;
data = xmalloc(sizeof(mod_Data));
data->audio_buffer = xmalloc(MIKMOD_FRAME_SIZE);
data = g_new(mod_Data, 1);
data->moduleHandle = moduleHandle;
Player_Start(data->moduleHandle);
......@@ -156,7 +152,6 @@ static void mod_close(mod_Data * data)
{
Player_Stop();
Player_Free(data->moduleHandle);
free(data->audio_buffer);
free(data);
}
......@@ -171,7 +166,7 @@ mod_decode(struct decoder *decoder, const char *path)
enum decoder_command cmd = DECODE_COMMAND_NONE;
if (!(data = mod_open(path))) {
ERROR("failed to open mod: %s\n", path);
g_warning("failed to open mod: %s\n", path);
MikMod_Exit();
return;
}
......@@ -211,7 +206,7 @@ static struct tag *modTagDup(const char *file)
g_free(path2);
if (moduleHandle == NULL) {
DEBUG("modTagDup: Failed to open file: %s\n", file);
g_debug("modTagDup: Failed to open file: %s\n", file);
MikMod_Exit();
return NULL;
......@@ -223,7 +218,7 @@ static struct tag *modTagDup(const char *file)
ret->time = 0;
path2 = g_strdup(file);
title = xstrdup(Player_LoadTitle(path2));
title = g_strdup(Player_LoadTitle(path2));
g_free(path2);
if (title)
tag_add_item(ret, TAG_ITEM_TITLE, title);
......
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../decoder_api.h"
#include "../utils.h"
#include "../log.h"
#include <glib.h>
#include <modplug.h>
#define MODPLUG_FRAME_SIZE (4096)
#define MODPLUG_PREALLOC_BLOCK (256*1024)
#define MODPLUG_READ_BLOCK (128*1024)
#define MODPLUG_FILE_LIMIT (1024*1024*100)
static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is)
{
unsigned char *data;
GByteArray *bdatas;
int total_len;
int ret;
//known/unknown size, preallocate array, lets read in chunks
if (is->size) {
if (is->size > MODPLUG_FILE_LIMIT) {
g_warning("file too large\n");
return NULL;
}
bdatas = g_byte_array_sized_new(is->size);
} else {
bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK);
}
data = g_malloc(MODPLUG_READ_BLOCK);
total_len = 0;
do {
if (decoder) {
ret = decoder_read(decoder, is, data, MODPLUG_READ_BLOCK);
} else {
ret = input_stream_read(is, data, MODPLUG_READ_BLOCK);
}
if (ret > 0) {
g_byte_array_append(bdatas, data, ret);
total_len += ret;
} else {
//end of file, or read error
break;
}
if (total_len > MODPLUG_FILE_LIMIT) {
g_warning("stream too large\n");
g_free(data);
g_byte_array_free(bdatas, TRUE);
return NULL;
}
} while (input_stream_eof(is));
g_free(data);
return bdatas;
}
static void
mod_decode(struct decoder *decoder, struct input_stream *is)
{
ModPlugFile *f;
ModPlug_Settings settings;
GByteArray *bdatas;
struct audio_format audio_format;
float total_time = 0.0;
int ret, current;
char audio_buffer[MODPLUG_FRAME_SIZE];
float sec_perbyte;
enum decoder_command cmd = DECODE_COMMAND_NONE;
bdatas = mod_loadfile(decoder, is);
if (!bdatas) {
g_warning("could not load stream\n");
return;
}
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
if (!f) {
g_warning("could not decode stream\n");
return;
}
ModPlug_GetSettings(&settings);
/* alter setting */
settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */
settings.mChannels = 2;
settings.mBits = 16;
settings.mFrequency = 44100;
/* insert more setting changes here */
ModPlug_SetSettings(&settings);
audio_format.bits = 16;
audio_format.sample_rate = 44100;
audio_format.channels = 2;
sec_perbyte =
1.0 / ((audio_format.bits * audio_format.channels / 8.0) *
(float)audio_format.sample_rate);
total_time = ModPlug_GetLength(f) / 1000;
decoder_initialized(decoder, &audio_format,
is->seekable, total_time);
total_time = 0;
do {
ret = ModPlug_Read(f, audio_buffer, MODPLUG_FRAME_SIZE);
if (ret == 0) {
break;
}
total_time += ret * sec_perbyte;
cmd = decoder_data(decoder, NULL,
audio_buffer, ret,
total_time, 0, NULL);
if (cmd == DECODE_COMMAND_SEEK) {
total_time = decoder_seek_where(decoder);
current = total_time * 1000;
ModPlug_Seek(f, current);
decoder_command_finished(decoder);
}
} while (cmd != DECODE_COMMAND_STOP);
ModPlug_Unload(f);
}
static struct tag *mod_tagdup(const char *file)
{
ModPlugFile *f;
struct tag *ret = NULL;
GByteArray *bdatas;
char *title;
struct input_stream is;
if (!input_stream_open(&is, file)) {
g_warning("cant open file %s\n", file);
return NULL;
}
bdatas = mod_loadfile(NULL, &is);
if (!bdatas) {
g_warning("cant load file %s\n", file);
return NULL;
}
f = ModPlug_Load(bdatas->data, bdatas->len);
g_byte_array_free(bdatas, TRUE);
if (!f) {
g_warning("could not decode file %s\n", file);
return NULL;
}
ret = tag_new();
ret->time = 0;
title = g_strdup(ModPlug_GetName(f));
if (title)
tag_add_item(ret, TAG_ITEM_TITLE, title);
g_free(title);
ModPlug_Unload(f);
input_stream_close(&is);
return ret;
}
static const char *const mod_suffixes[] = {
"669", "amf", "ams", "dbm", "dfm", "dsm", "far", "it",
"med", "mdl", "mod", "mtm", "mt2", "okt", "s3m", "stm",
"ult", "umx", "xm",
NULL
};
const struct decoder_plugin modplug_plugin = {
.name = "modplug",
.stream_decode = mod_decode,
.tag_dup = mod_tagdup,
.suffixes = mod_suffixes,
};
......@@ -18,6 +18,8 @@
#include "../decoder_api.h"
#include "../conf.h"
#include "config.h"
#include "tag_id3.h"
#include <assert.h>
#include <unistd.h>
......@@ -91,10 +93,8 @@ mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth,
static bool mp3_plugin_init(void)
{
int ret = getBoolConfigParam(CONF_GAPLESS_MP3_PLAYBACK, true);
gapless_playback = ret != CONF_BOOL_UNSET
? !!ret
: DEFAULT_GAPLESS_MP3_PLAYBACK;
gapless_playback = config_get_bool(CONF_GAPLESS_MP3_PLAYBACK,
DEFAULT_GAPLESS_MP3_PLAYBACK);
return true;
}
......@@ -203,6 +203,95 @@ mp3_fill_buffer(struct mp3_data *data)
}
#ifdef HAVE_ID3TAG
/* Parse mp3 RVA2 frame. Shamelessly stolen from madplay. */
static int parse_rva2(struct id3_tag * tag, struct replay_gain_info * replay_gain_info)
{
struct id3_frame const * frame;
id3_latin1_t const *id;
id3_byte_t const *data;
id3_length_t length;
int found;
enum {
CHANNEL_OTHER = 0x00,
CHANNEL_MASTER_VOLUME = 0x01,
CHANNEL_FRONT_RIGHT = 0x02,
CHANNEL_FRONT_LEFT = 0x03,
CHANNEL_BACK_RIGHT = 0x04,
CHANNEL_BACK_LEFT = 0x05,
CHANNEL_FRONT_CENTRE = 0x06,
CHANNEL_BACK_CENTRE = 0x07,
CHANNEL_SUBWOOFER = 0x08
};
found = 0;
/* relative volume adjustment information */
frame = id3_tag_findframe(tag, "RVA2", 0);
if (!frame) return 0;
id = id3_field_getlatin1(id3_frame_field(frame, 0));
data = id3_field_getbinarydata(id3_frame_field(frame, 1),
&length);
if (!id || !data) return 0;
/*
* "The 'identification' string is used to identify the
* situation and/or device where this adjustment should apply.
* The following is then repeated for every channel
*
* Type of channel $xx
* Volume adjustment $xx xx
* Bits representing peak $xx
* Peak volume $xx (xx ...)"
*/
while (length >= 4) {
unsigned int peak_bytes;
peak_bytes = (data[3] + 7) / 8;
if (4 + peak_bytes > length)
break;
if (data[0] == CHANNEL_MASTER_VOLUME) {
signed int voladj_fixed;
double voladj_float;
/*
* "The volume adjustment is encoded as a fixed
* point decibel value, 16 bit signed integer
* representing (adjustment*512), giving +/- 64
* dB with a precision of 0.001953125 dB."
*/
voladj_fixed = (data[1] << 8) | (data[2] << 0);
voladj_fixed |= -(voladj_fixed & 0x8000);
voladj_float = (double) voladj_fixed / 512;
replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak = voladj_float;
replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak = voladj_float;
g_debug("parseRVA2: Relative Volume "
"%+.1f dB adjustment (%s)\n",
voladj_float, id);
found = 1;
break;
}
data += 4 + peak_bytes;
length -= 4 + peak_bytes;
}
return found;
}
#endif
#ifdef HAVE_ID3TAG
static struct replay_gain_info *
parse_id3_replay_gain_info(struct id3_tag *tag)
{
......@@ -244,6 +333,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag)
free(value);
}
if (!found) {
/* fall back on RVA2 if no replaygain tags found */
found = parse_rva2(tag, replay_gain_info);
}
if (found)
return replay_gain_info;
replay_gain_info_free(replay_gain_info);
......@@ -1095,22 +1189,19 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
static struct tag *mp3_tag_dup(const char *file)
{
struct tag *ret = NULL;
struct tag *tag;
int total_time;
ret = tag_id3_load(file);
total_time = mp3_total_file_time(file);
if (total_time >= 0) {
if (!ret)
ret = tag_new();
ret->time = total_time;
} else {
if (total_time < 0) {
g_debug("mp3_tag_dup: Failed to get total song time from: %s\n",
file);
return NULL;
}
return ret;
tag = tag_new();
tag->time = total_time;
return tag;
}
static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL };
......
/* the Music Player Daemon (MPD)
* Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
* This project's homepage is: 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
......@@ -17,6 +17,7 @@
*/
#include "../decoder_api.h"
#include "config.h"
#include "mp4ff.h"
......@@ -387,6 +388,8 @@ mp4_load_tag(const char *file)
tag_add_item(ret, TAG_ITEM_GENRE, value);
} else if (0 == strcasecmp("date", item)) {
tag_add_item(ret, TAG_ITEM_DATE, value);
} else if (0 == strcasecmp("writer", item)) {
tag_add_item(ret, TAG_ITEM_COMPOSER, value);
}
free(item);
......@@ -402,21 +405,7 @@ mp4_load_tag(const char *file)
static struct tag *
mp4_tag_dup(const char *file)
{
struct tag *ret = NULL;
ret = mp4_load_tag(file);
if (!ret)
return NULL;
if (tag_is_empty(ret)) {
struct tag *temp = tag_id3_load(file);
if (temp) {
temp->time = ret->time;
tag_free(ret);
ret = temp;
}
}
return ret;
return mp4_load_tag(file);
}
static const char *const mp4_suffixes[] = { "m4a", "mp4", NULL };
......
......@@ -17,6 +17,7 @@
*/
#include "../decoder_api.h"
#include "config.h"
#include <mpcdec/mpcdec.h>
#include <glib.h>
......@@ -248,8 +249,8 @@ static float mpcGetTime(const char *file)
static struct tag *mpcTagDup(const char *file)
{
struct tag *ret = NULL;
float total_time = mpcGetTime(file);
struct tag *tag;
if (total_time < 0) {
g_debug("mpcTagDup: Failed to get Songlength of file: %s\n",
......@@ -257,14 +258,9 @@ static struct tag *mpcTagDup(const char *file)
return NULL;
}
ret = tag_ape_load(file);
if (!ret)
ret = tag_id3_load(file);
if (!ret)
ret = tag_new();
ret->time = total_time;
return ret;
tag = tag_new();
tag->time = total_time;
return tag;
}
static const char *const mpcSuffixes[] = { "mpc", NULL };
......
......@@ -24,7 +24,11 @@
#include <wavpack/wavpack.h>
#include <glib.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
#define CHUNK_SIZE 1020
......@@ -111,7 +115,7 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
* This function converts floating point sample data to 24-bit integer.
*/
static void
format_samples_float(mpd_unused int bytes_per_sample, void *buffer,
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
uint32_t count)
{
int32_t *dst = buffer;
......@@ -451,8 +455,7 @@ static bool
wavpack_open_wvc(struct decoder *decoder, struct input_stream *is_wvc,
struct wavpack_input *wpi)
{
char tmp[MPD_PATH_MAX];
const char *utf8url;
char *utf8url;
char *wvc_url = NULL;
bool ret;
char first_byte;
......@@ -462,12 +465,14 @@ wavpack_open_wvc(struct decoder *decoder, struct input_stream *is_wvc,
* As we use dc->utf8url, this function will be bad for
* single files. utf8url is not absolute file path :/
*/
utf8url = decoder_get_url(decoder, tmp);
utf8url = decoder_get_uri(decoder);
if (utf8url == NULL) {
return false;
}
wvc_url = g_strconcat(utf8url, "c", NULL);
g_free(utf8url);
ret = input_stream_open(is_wvc, wvc_url);
g_free(wvc_url);
......
......@@ -27,23 +27,24 @@
#include "normalize.h"
#include "pipe.h"
#include <assert.h>
#include <glib.h>
void decoder_initialized(struct decoder * decoder,
#include <assert.h>
#include <stdlib.h>
void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
const struct audio_format *audio_format,
bool seekable, float total_time)
{
assert(dc.state == DECODE_STATE_START);
assert(decoder != NULL);
assert(!decoder->stream_tag_sent);
assert(decoder->stream_tag == NULL);
assert(decoder->decoder_tag == NULL);
assert(!decoder->seeking);
assert(audio_format != NULL);
assert(audio_format_defined(audio_format));
assert(audio_format_valid(audio_format));
pcm_convert_init(&decoder->conv_state);
dc.in_audio_format = *audio_format;
getOutputAudioFormat(audio_format, &dc.out_audio_format);
......@@ -54,10 +55,9 @@ void decoder_initialized(struct decoder * decoder,
notify_signal(&pc.notify);
}
const char *decoder_get_url(G_GNUC_UNUSED struct decoder * decoder,
char * buffer)
char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder)
{
return song_get_url(dc.current_song, buffer);
return song_get_uri(dc.current_song);
}
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
......@@ -133,33 +133,6 @@ size_t decoder_read(struct decoder *decoder,
}
/**
* Add the tag items from the input stream (meta_name, meta_title) to
* a duplicate of the specified tag. The return value has to be freed
* with tag_free(). If this function returns NULL, then there are no
* tags provided by the stream.
*/
static struct tag *
tag_add_stream_tags(const struct tag *src_tag, const struct input_stream *is)
{
struct tag *tag;
assert(src_tag != NULL);
assert(is != NULL);
if ((is->meta_name == NULL || tag_has_type(src_tag, TAG_ITEM_NAME)) &&
(is->meta_title == NULL || tag_has_type(src_tag, TAG_ITEM_TITLE)))
return NULL;
tag = tag_dup(src_tag);
if (is->meta_name != NULL && !tag_has_type(src_tag, TAG_ITEM_NAME))
tag_add_item(tag, TAG_ITEM_NAME, is->meta_name);
if (is->meta_title != NULL && !tag_has_type(src_tag, TAG_ITEM_TITLE))
tag_add_item(tag, TAG_ITEM_TITLE, is->meta_title);
return tag;
}
/**
* All chunks are full of decoded data; wait for the player to free
* one.
*/
......@@ -174,109 +147,132 @@ need_chunks(struct input_stream *is, bool wait)
notify_wait(&dc.notify);
notify_signal(&pc.notify);
if (dc.command != DECODE_COMMAND_STOP)
return dc.command;
return dc.command;
}
return DECODE_COMMAND_NONE;
}
static enum decoder_command
do_send_tag(struct input_stream *is, const struct tag *tag)
{
while (!music_pipe_tag(tag)) {
enum decoder_command cmd = need_chunks(is, true);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
}
return DECODE_COMMAND_NONE;
}
static bool
update_stream_tag(struct decoder *decoder, struct input_stream *is)
{
struct tag *tag;
if (is == NULL)
return false;
tag = input_stream_tag(is);
if (tag == NULL)
return false;
if (decoder->stream_tag != NULL)
tag_free(decoder->stream_tag);
decoder->stream_tag = tag;
return true;
}
enum decoder_command
decoder_data(struct decoder *decoder,
struct input_stream *is,
void *_data, size_t length,
const void *_data, size_t length,
float data_time, uint16_t bitRate,
struct replay_gain_info *replay_gain_info)
{
static char *conv_buffer;
static size_t conv_buffer_size;
size_t nbytes;
char *data;
const char *data = _data;
assert(dc.state == DECODE_STATE_DECODE);
assert(length % audio_format_frame_size(&dc.in_audio_format) == 0);
if (dc.command == DECODE_COMMAND_STOP ||
dc.command == DECODE_COMMAND_SEEK ||
length == 0)
return dc.command;
if (is != NULL && !decoder->stream_tag_sent) {
const struct tag *src;
struct tag *tag1, *tag2;
/* base is the current song's tag, or an empty new
tag if the song has no tag */
src = dc.current_song->tag;
if (src == NULL)
src = tag1 = tag_new();
else
tag1 = NULL;
tag2 = tag_add_stream_tags(src, is);
if (tag1 != NULL)
/* free the empty tag created by tag_new(), we
aren't going to send it */
tag_free(tag1);
if (tag2 != NULL)
/* use the composite tag returned by
tag_add_stream_tags() */
src = tag2;
if (src != NULL) {
music_pipe_tag(src);
if (tag2 != NULL)
tag_free(tag2);
}
/* send stream tags */
decoder->stream_tag_sent = true;
}
if (update_stream_tag(decoder, is)) {
enum decoder_command cmd;
if (audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) {
data = _data;
} else {
size_t out_length =
pcm_convert_size(&dc.in_audio_format, length,
&dc.out_audio_format);
if (out_length > conv_buffer_size) {
if (conv_buffer != NULL)
free(conv_buffer);
conv_buffer = xmalloc(out_length);
conv_buffer_size = out_length;
}
if (decoder->decoder_tag != NULL) {
/* merge with tag from decoder plugin */
struct tag *tag;
data = conv_buffer;
length = pcm_convert(&dc.in_audio_format, _data,
length, &dc.out_audio_format,
data, &decoder->conv_state);
tag = tag_merge(decoder->stream_tag,
decoder->decoder_tag);
cmd = do_send_tag(is, tag);
tag_free(tag);
} else
/* send only the stream tag */
cmd = do_send_tag(is, decoder->stream_tag);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
}
if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) {
data = pcm_convert(&decoder->conv_state,
&dc.in_audio_format, data, length,
&dc.out_audio_format, &length);
/* under certain circumstances, pcm_convert() may
return an empty buffer - this condition should be
investigated further, but for now, do this check as
a workaround: */
if (length == 0)
if (data == NULL)
return DECODE_COMMAND_NONE;
}
if (replay_gain_info != NULL && (replay_gain_mode != REPLAY_GAIN_OFF))
replay_gain_apply(replay_gain_info, data, length,
&dc.out_audio_format);
else if (normalizationEnabled)
normalizeData(data, length, &dc.out_audio_format);
while (length > 0) {
nbytes = music_pipe_append(data, length,
&dc.out_audio_format,
data_time, bitRate);
length -= nbytes;
data += nbytes;
if (length > 0) {
enum decoder_command cmd =
need_chunks(is, nbytes == 0);
size_t nbytes;
char *dest = music_pipe_write(&dc.out_audio_format,
data_time, bitRate,
&nbytes);
if (dest == NULL) {
/* the music pipe is full: wait for more
room */
enum decoder_command cmd = need_chunks(is, true);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
continue;
}
assert(nbytes > 0);
if (nbytes > length)
nbytes = length;
/* copy the buffer */
memcpy(dest, data, nbytes);
/* apply replay gain or normalization */
if (replay_gain_info != NULL &&
replay_gain_mode != REPLAY_GAIN_OFF)
replay_gain_apply(replay_gain_info, dest, nbytes,
&dc.out_audio_format);
else if (normalizationEnabled)
normalizeData(dest, nbytes, &dc.out_audio_format);
/* expand the music pipe chunk */
music_pipe_expand(&dc.out_audio_format, nbytes);
data += nbytes;
length -= nbytes;
}
return DECODE_COMMAND_NONE;
......@@ -286,23 +282,33 @@ enum decoder_command
decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
const struct tag *tag)
{
struct tag *tag2 = is != NULL ? tag_add_stream_tags(tag, is) : NULL;
enum decoder_command cmd;
assert(dc.state == DECODE_STATE_DECODE);
assert(tag != NULL);
if (tag2 != NULL)
tag = tag2;
/* save the tag */
while (!music_pipe_tag(tag)) {
enum decoder_command cmd = need_chunks(is, true);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
}
if (decoder->decoder_tag != NULL)
tag_free(decoder->decoder_tag);
decoder->decoder_tag = tag_dup(tag);
if (tag2 != NULL)
tag_free(tag2);
/* check for a new stream tag */
decoder->stream_tag_sent = true;
update_stream_tag(decoder, is);
return DECODE_COMMAND_NONE;
/* send tag to music pipe */
if (decoder->stream_tag != NULL) {
/* merge with tag from input stream */
struct tag *merged;
merged = tag_merge(decoder->stream_tag, decoder->decoder_tag);
cmd = do_send_tag(is, merged);
tag_free(merged);
} else
/* send only the decoder tag */
cmd = do_send_tag(is, tag);
return cmd;
}
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