aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INSTALL2
-rw-r--r--NEWS12
-rw-r--r--configure.ac12
-rw-r--r--src/conf.c1
-rw-r--r--src/decoder/_flac_common.c124
-rw-r--r--src/decoder/_flac_common.h20
-rw-r--r--src/decoder/flac_plugin.c28
-rw-r--r--src/decoder/oggflac_plugin.c34
-rw-r--r--src/decoder_list.c2
-rw-r--r--src/fd_util.c34
-rw-r--r--src/fd_util.h6
-rw-r--r--src/glib_compat.h62
-rw-r--r--src/input/curl_input_plugin.c1
-rw-r--r--src/input/file_input_plugin.c2
-rw-r--r--src/input_stream.h4
-rw-r--r--src/listen.c1
-rw-r--r--src/log.c2
-rw-r--r--src/main.c2
-rw-r--r--src/mixer/oss_mixer_plugin.c2
-rw-r--r--src/output/alsa_plugin.c50
-rw-r--r--src/output/fifo_output_plugin.c4
-rw-r--r--src/output/httpd_client.c1
-rw-r--r--src/output/mvp_plugin.c4
-rw-r--r--src/output/oss_plugin.c4
-rw-r--r--src/output/recorder_output_plugin.c3
-rw-r--r--src/output_all.c9
-rw-r--r--src/output_all.h6
-rw-r--r--src/output_control.c9
-rw-r--r--src/output_control.h3
-rw-r--r--src/output_internal.h7
-rw-r--r--src/output_thread.c13
-rw-r--r--src/player_control.h4
-rw-r--r--src/player_thread.c6
-rw-r--r--src/playlist/lastfm_playlist_plugin.c11
-rw-r--r--src/playlist_list.c12
-rw-r--r--src/state_file.c1
-rw-r--r--src/sticker.c4
-rw-r--r--test/read_mixer.c7
38 files changed, 353 insertions, 156 deletions
diff --git a/INSTALL b/INSTALL
index 82cb0c712..cf087ef83 100644
--- a/INSTALL
+++ b/INSTALL
@@ -13,7 +13,7 @@ Dependencies
gcc - http://gcc.gnu.org/
Any other C99 compliant compiler should also work.
-GLib 2.16 - http://www.gtk.org/
+GLib 2.12 - http://www.gtk.org/
General-purpose utility library.
diff --git a/NEWS b/NEWS
index 96fcfb1f7..fa668f1bf 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,7 @@ ver 0.16 (20??/??/??)
* output:
- recorder: new output plugin for recording radio streams
- alsa: don't recover on CANCEL
+ - alsa: fill period buffer with silence before draining
- openal: new output plugin
- pulse: announce "media.role=music"
- pulse: renamed context to "Music Player Daemon"
@@ -43,6 +44,8 @@ ver 0.16 (20??/??/??)
- httpd: bind port when output is enabled
- wildcards allowed in audio_format configuration
- consistently lock audio output objects
+* player:
+ - drain audio outputs at the end of the playlist
* mixers:
- removed support for legacy mixer configuration
- reimplemented software volume as mixer+filter plugin
@@ -65,14 +68,21 @@ ver 0.16 (20??/??/??)
* set the close-on-exec flag on all file descriptors
* obey $(sysconfdir) for default mpd.conf location
* build with large file support by default
-* require GLib 2.16
+* require GLib 2.12
ver 0.15.6 (2009/??/??)
+* input:
+ - lastfm: fixed variable name in GLib<2.16 code path
+ - input/mms: require libmms 0.4
+* archive:
+ - zzip: require libzzip 0.13
* decoders:
- ffmpeg: convert metadata
* output_thread: check again if output is open on PAUSE
* update: delete ignored symlinks from database
+* database: increased maximum line length to 32 kB
+* sticker: added fallback for sqlite3_prepare_v2()
ver 0.15.5 (2009/10/18)
diff --git a/configure.ac b/configure.ac
index bfa21b9d4..2efff82a6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,8 +141,8 @@ dnl
dnl mandatory libraries
dnl
-PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16 gthread-2.0],,
- [AC_MSG_ERROR([glib-2.16 is required])])
+PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12 gthread-2.0],,
+ [AC_MSG_ERROR([GLib 2.12 is required])])
dnl
@@ -322,7 +322,7 @@ AC_ARG_ENABLE(mms,
[enable the MMS protocol with libmms]),,
[enable_mms=auto])
-MPD_AUTO_PKG(mms, MMS, [libmms],
+MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
[libmms mms:// protocol support], [libmms not found])
if test x$enable_mms = xyes; then
AC_DEFINE(ENABLE_MMS, 1,
@@ -358,7 +358,7 @@ AC_ARG_ENABLE(zip,
[enable zip archive support (default: disabled)]),,
enable_zip=no)
-MPD_AUTO_PKG(zip, ZZIP, [zziplib],
+MPD_AUTO_PKG(zip, ZZIP, [zziplib >= 0.13],
[libzzip archive library], [libzzip not found])
AM_CONDITIONAL(HAVE_ZIP, test x$enable_zip = xyes)
@@ -410,8 +410,8 @@ dnl
AC_ARG_ENABLE(audiofile,
AS_HELP_STRING([--enable-audiofile],
- [enable audiofile support, disables wave support]),,
- enable_audiofile=yes)
+ [enable audiofile support (WAV and others)]),,
+ enable_audiofile=auto)
MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.1.7],
[audiofile decoder plugin], [libaudiofile not found])
diff --git a/src/conf.c b/src/conf.c
index 4da44c775..647ddec38 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -21,6 +21,7 @@
#include "utils.h"
#include "tokenizer.h"
#include "path.h"
+#include "glib_compat.h"
#include <glib.h>
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index d8802a6a3..9b3272cf2 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -31,6 +31,8 @@ void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream)
{
+ pcm_buffer_init(&data->buffer);
+
data->time = 0;
data->position = 0;
data->bit_rate = 0;
@@ -40,6 +42,18 @@ flac_data_init(struct flac_data *data, struct decoder * decoder,
data->tag = NULL;
}
+void
+flac_data_deinit(struct flac_data *data)
+{
+ pcm_buffer_deinit(&data->buffer);
+
+ if (data->replay_gain_info != NULL)
+ replay_gain_info_free(data->replay_gain_info);
+
+ if (data->tag != NULL)
+ tag_free(data->tag);
+}
+
static void
flac_find_float_comment(const FLAC__StreamMetadata *block,
const char *cmnt, float *fl, bool *found_r)
@@ -183,15 +197,31 @@ flac_parse_comment(struct tag *tag, const char *char_tnum,
return;
}
-void
+static void
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata *block)
+ const FLAC__StreamMetadata_VorbisComment *comment)
{
- FLAC__StreamMetadata_VorbisComment_Entry *comments =
- block->data.vorbis_comment.comments;
+ for (unsigned i = 0; i < comment->num_comments; ++i)
+ flac_parse_comment(tag, char_tnum, &comment->comments[i]);
+}
- for (unsigned i = block->data.vorbis_comment.num_comments; i > 0; --i)
- flac_parse_comment(tag, char_tnum, comments++);
+void
+flac_tag_apply_metadata(struct tag *tag, const char *track,
+ const FLAC__StreamMetadata *block)
+{
+ switch (block->type) {
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ flac_vorbis_comments_to_tag(tag, track,
+ &block->data.vorbis_comment);
+ break;
+
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ tag->time = flac_duration(&block->data.stream_info);
+ break;
+
+ default:
+ break;
+ }
}
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
@@ -209,7 +239,8 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
flac_parse_replay_gain(block, data);
if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL, block);
+ flac_vorbis_comments_to_tag(data->tag, NULL,
+ &block->data.vorbis_comment);
default:
break;
@@ -290,14 +321,14 @@ flac_convert_8(int8_t *dest,
*dest++ = buf[c_chan][position];
}
-static void flac_convert(unsigned char *dest,
- unsigned int num_channels,
- unsigned int bytes_per_sample,
- const FLAC__int32 * const buf[],
- unsigned int position, unsigned int end)
+static void
+flac_convert(void *dest,
+ unsigned int num_channels, unsigned sample_format,
+ const FLAC__int32 *const buf[],
+ unsigned int position, unsigned int end)
{
- switch (bytes_per_sample) {
- case 2:
+ switch (sample_format) {
+ case 16:
if (num_channels == 2)
flac_convert_stereo16((int16_t*)dest, buf,
position, end);
@@ -306,12 +337,13 @@ static void flac_convert(unsigned char *dest,
position, end);
break;
- case 4:
+ case 24:
+ case 32:
flac_convert_32((int32_t*)dest, num_channels, buf,
position, end);
break;
- case 1:
+ case 8:
flac_convert_8((int8_t*)dest, num_channels, buf,
position, end);
break;
@@ -322,49 +354,31 @@ FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[])
{
- unsigned int c_samp;
- const unsigned int num_channels = frame->header.channels;
- const unsigned int bytes_per_sample =
- audio_format_sample_size(&data->audio_format);
- const unsigned int bytes_per_channel =
- bytes_per_sample * frame->header.channels;
- const unsigned int max_samples = FLAC_CHUNK_SIZE / bytes_per_channel;
- unsigned int num_samples;
enum decoder_command cmd;
+ size_t buffer_size = frame->header.blocksize *
+ audio_format_frame_size(&data->audio_format);
+ void *buffer;
+
+ buffer = pcm_buffer_get(&data->buffer, buffer_size);
+
+ flac_convert(buffer, data->audio_format.channels,
+ data->audio_format.bits, buf,
+ 0, frame->header.blocksize);
+
+ cmd = decoder_data(data->decoder, data->input_stream,
+ buffer, buffer_size,
+ data->time, data->bit_rate,
+ data->replay_gain_info);
+ switch (cmd) {
+ case DECODE_COMMAND_NONE:
+ case DECODE_COMMAND_START:
+ break;
- if (bytes_per_sample != 1 && bytes_per_sample != 2 &&
- bytes_per_sample != 4)
- /* exotic unsupported bit rate */
+ case DECODE_COMMAND_STOP:
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
- for (c_samp = 0; c_samp < frame->header.blocksize;
- c_samp += num_samples) {
- num_samples = frame->header.blocksize - c_samp;
- if (num_samples > max_samples)
- num_samples = max_samples;
-
- flac_convert(data->chunk,
- num_channels, bytes_per_sample, buf,
- c_samp, c_samp + num_samples);
-
- cmd = decoder_data(data->decoder, data->input_stream,
- data->chunk,
- num_samples * bytes_per_channel,
- data->time, data->bit_rate,
- data->replay_gain_info);
- switch (cmd) {
- case DECODE_COMMAND_NONE:
- case DECODE_COMMAND_START:
- break;
-
- case DECODE_COMMAND_STOP:
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
-
- case DECODE_COMMAND_SEEK:
- return
- FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
- }
+ case DECODE_COMMAND_SEEK:
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
diff --git a/src/decoder/_flac_common.h b/src/decoder/_flac_common.h
index 68de7e969..5f83acc70 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/_flac_common.h
@@ -24,7 +24,8 @@
#ifndef MPD_FLAC_COMMON_H
#define MPD_FLAC_COMMON_H
-#include "../decoder_api.h"
+#include "decoder_api.h"
+#include "pcm_buffer.h"
#include "config.h"
#include <glib.h>
@@ -145,7 +146,8 @@ typedef size_t flac_read_status_size_t;
#define FLAC_CHUNK_SIZE 4080
struct flac_data {
- unsigned char chunk[FLAC_CHUNK_SIZE];
+ struct pcm_buffer buffer;
+
float time;
unsigned int bit_rate;
struct audio_format audio_format;
@@ -157,11 +159,21 @@ struct flac_data {
struct tag *tag;
};
+static inline unsigned
+flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
+{
+ return (stream_info->total_samples + stream_info->sample_rate - 1) /
+ stream_info->sample_rate;
+}
+
/* initializes a given FlacData struct */
void
flac_data_init(struct flac_data *data, struct decoder * decoder,
struct input_stream *input_stream);
+void
+flac_data_deinit(struct flac_data *data);
+
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
@@ -170,8 +182,8 @@ void flac_error_common_cb(const char *plugin,
struct flac_data *data);
void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata *block);
+flac_tag_apply_metadata(struct tag *tag, const char *track,
+ const FLAC__StreamMetadata *block);
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
diff --git a/src/decoder/flac_plugin.c b/src/decoder/flac_plugin.c
index 9692ba49f..ee80d667a 100644
--- a/src/decoder/flac_plugin.c
+++ b/src/decoder/flac_plugin.c
@@ -268,12 +268,8 @@ flac_tag_load(const char *file, const char *char_tnum)
block = FLAC__metadata_simple_iterator_get_block(it);
if (!block)
break;
- if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
- flac_vorbis_comments_to_tag(tag, char_tnum, block);
- } else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
- tag->time = ((float)block->data.stream_info.total_samples) /
- block->data.stream_info.sample_rate + 0.5;
- }
+
+ flac_tag_apply_metadata(tag, char_tnum, block);
FLAC__metadata_object_delete(block);
} while (FLAC__metadata_simple_iterator_next(it));
@@ -485,10 +481,7 @@ flac_decode_internal(struct decoder * decoder,
}
fail:
- if (data.replay_gain_info)
- replay_gain_info_free(data.replay_gain_info);
-
- tag_free(data.tag);
+ flac_data_deinit(&data);
if (flac_dec)
flac_delete(flac_dec);
@@ -670,8 +663,7 @@ fail:
if (pathname)
g_free(pathname);
- if (data.replay_gain_info)
- replay_gain_info_free(data.replay_gain_info);
+ flac_data_deinit(&data);
if (flac_dec)
flac_delete(flac_dec);
@@ -793,8 +785,7 @@ flac_filedecode_internal(struct decoder* decoder,
}
fail:
- if (data.replay_gain_info)
- replay_gain_info_free(data.replay_gain_info);
+ flac_data_deinit(&data);
if (flac_dec)
flac_delete(flac_dec);
@@ -853,13 +844,8 @@ oggflac_tag_dup(const char *file)
do {
if (!(block = FLAC__metadata_iterator_get_block(it)))
break;
- if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
- flac_vorbis_comments_to_tag(ret, NULL, block);
- } else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
- ret->time = ((float)block->data.stream_info.
- total_samples) /
- block->data.stream_info.sample_rate + 0.5;
- }
+
+ flac_tag_apply_metadata(ret, NULL, block);
} while (FLAC__metadata_iterator_next(it));
FLAC__metadata_iterator_delete(it);
diff --git a/src/decoder/oggflac_plugin.c b/src/decoder/oggflac_plugin.c
index fedfdcb48..dff76be51 100644
--- a/src/decoder/oggflac_plugin.c
+++ b/src/decoder/oggflac_plugin.c
@@ -29,11 +29,8 @@
#include <assert.h>
#include <unistd.h>
-static void oggflac_cleanup(struct flac_data *data,
- OggFLAC__SeekableStreamDecoder * decoder)
+static void oggflac_cleanup(OggFLAC__SeekableStreamDecoder * decoder)
{
- if (data->replay_gain_info)
- replay_gain_info_free(data->replay_gain_info);
if (decoder)
OggFLAC__seekable_stream_decoder_delete(decoder);
}
@@ -173,17 +170,7 @@ static void of_metadata_dup_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecode
assert(data->tag != NULL);
- switch (block->type) {
- case FLAC__METADATA_TYPE_STREAMINFO:
- data->tag->time = ((float)block->data.stream_info.
- total_samples) /
- block->data.stream_info.sample_rate + 0.5;
- return;
- case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- flac_vorbis_comments_to_tag(data->tag, NULL, block);
- default:
- break;
- }
+ flac_tag_apply_metadata(data->tag, NULL, block);
}
/* used by decode */
@@ -264,6 +251,7 @@ oggflac_tag_dup(const char *file)
struct input_stream input_stream;
OggFLAC__SeekableStreamDecoder *decoder;
struct flac_data data;
+ struct tag *tag;
if (!input_stream_open(&input_stream, file))
return NULL;
@@ -280,15 +268,18 @@ oggflac_tag_dup(const char *file)
* data.tag will be set or unset, that's all we care about */
decoder = full_decoder_init_and_read_metadata(&data, 1);
- oggflac_cleanup(&data, decoder);
+ oggflac_cleanup(decoder);
input_stream_close(&input_stream);
- if (!tag_is_defined(data.tag)) {
- tag_free(data.tag);
+ if (tag_is_defined(data.tag)) {
+ tag = data.tag;
data.tag = NULL;
- }
+ } else
+ tag = NULL;
+
+ flac_data_deinit(&data);
- return data.tag;
+ return tag;
}
static void
@@ -344,7 +335,8 @@ oggflac_decode(struct decoder * mpd_decoder, struct input_stream *input_stream)
}
fail:
- oggflac_cleanup(&data, decoder);
+ oggflac_cleanup(decoder);
+ flac_data_deinit(&data);
}
static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
diff --git a/src/decoder_list.c b/src/decoder_list.c
index 7ac9b5b3a..2ee4f3ec9 100644
--- a/src/decoder_list.c
+++ b/src/decoder_list.c
@@ -157,7 +157,7 @@ decoder_plugin_from_mime_type(const char *mimeType, unsigned int next)
for (; decoder_plugins[i] != NULL; ++i) {
const struct decoder_plugin *plugin = decoder_plugins[i];
if (decoder_plugins_enabled[i] &&
- decoder_plugin_supports_suffix(plugin, mimeType)) {
+ decoder_plugin_supports_mime_type(plugin, mimeType)) {
++i;
return plugin;
}
diff --git a/src/fd_util.c b/src/fd_util.c
index 8f142e34a..f78b8ed8b 100644
--- a/src/fd_util.c
+++ b/src/fd_util.c
@@ -101,7 +101,7 @@ fd_set_nonblock(int fd)
}
int
-open_cloexec(const char *path_fs, int flags)
+open_cloexec(const char *path_fs, int flags, int mode)
{
int fd;
@@ -113,30 +113,34 @@ open_cloexec(const char *path_fs, int flags)
flags |= O_NOCTTY;
#endif
- fd = open(path_fs, flags, 0666);
+ fd = open(path_fs, flags, mode);
fd_set_cloexec(fd, true);
return fd;
}
int
-creat_cloexec(const char *path_fs, int mode)
+pipe_cloexec(int fd[2])
{
- int flags = O_CREAT|O_WRONLY|O_TRUNC;
- int fd;
-
-#ifdef O_CLOEXEC
- flags |= O_CLOEXEC;
-#endif
+#ifdef WIN32
+ return _pipe(event_pipe, 512, _O_BINARY);
+#else
+ int ret;
-#ifdef O_NOCTTY
- flags |= O_NOCTTY;
+#ifdef HAVE_PIPE2
+ ret = pipe2(fd, O_CLOEXEC);
+ if (ret >= 0 || errno != ENOSYS)
+ return ret;
#endif
- fd = open(path_fs, flags, mode);
- fd_set_cloexec(fd, true);
+ ret = pipe(fd);
+ if (ret >= 0) {
+ fd_set_cloexec(fd[0], true);
+ fd_set_cloexec(fd[1], true);
+ }
- return fd;
+ return ret;
+#endif
}
int
@@ -158,10 +162,8 @@ pipe_cloexec_nonblock(int fd[2])
fd_set_cloexec(fd[0], true);
fd_set_cloexec(fd[1], true);
-#ifndef WIN32
fd_set_nonblock(fd[0]);
fd_set_nonblock(fd[1]);
-#endif
}
return ret;
diff --git a/src/fd_util.h b/src/fd_util.h
index 68a7d86ab..57ad63288 100644
--- a/src/fd_util.h
+++ b/src/fd_util.h
@@ -46,14 +46,14 @@ struct sockaddr;
* supported by the OS).
*/
int
-open_cloexec(const char *path_fs, int flags);
+open_cloexec(const char *path_fs, int flags, int mode);
/**
- * Wrapper for creat(), which sets the CLOEXEC flag (atomically if
+ * Wrapper for pipe(), which sets the CLOEXEC flag (atomically if
* supported by the OS).
*/
int
-creat_cloexec(const char *path_fs, int mode);
+pipe_cloexec(int fd[2]);
/**
* Wrapper for pipe(), which sets the CLOEXEC flag (atomically if
diff --git a/src/glib_compat.h b/src/glib_compat.h
new file mode 100644
index 000000000..641fef99a
--- /dev/null
+++ b/src/glib_compat.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Compatibility with older GLib versions. Some of this isn't
+ * implemented properly, just "good enough" to allow users with older
+ * operating systems to run MPD.
+ */
+
+#ifndef MPD_GLIB_COMPAT_H
+#define MPD_GLIB_COMPAT_H
+
+#include <glib.h>
+
+#if !GLIB_CHECK_VERSION(2,14,0)
+
+#define g_queue_clear(q) do { g_queue_free(q); q = g_queue_new(); } while (0)
+
+static inline guint
+g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
+{
+ return g_timeout_add(interval * 1000, function, data);
+}
+
+#endif /* !2.14 */
+
+#if !GLIB_CHECK_VERSION(2,16,0)
+
+static inline void
+g_propagate_prefixed_error(GError **dest_r, GError *src,
+ G_GNUC_UNUSED const gchar *format, ...)
+{
+ g_propagate_error(dest_r, src);
+}
+
+static inline char *
+g_uri_escape_string(const char *unescaped,
+ G_GNUC_UNUSED const char *reserved_chars_allowed,
+ G_GNUC_UNUSED gboolean allow_utf8)
+{
+ return g_strdup(unescaped);
+}
+
+#endif /* !2.16 */
+
+#endif
diff --git a/src/input/curl_input_plugin.c b/src/input/curl_input_plugin.c
index c3928a09b..b8e9435a0 100644
--- a/src/input/curl_input_plugin.c
+++ b/src/input/curl_input_plugin.c
@@ -23,6 +23,7 @@
#include "config.h"
#include "tag.h"
#include "icy_metadata.h"
+#include "glib_compat.h"
#include <assert.h>
diff --git a/src/input/file_input_plugin.c b/src/input/file_input_plugin.c
index 55b495f38..8561d04e7 100644
--- a/src/input/file_input_plugin.c
+++ b/src/input/file_input_plugin.c
@@ -51,7 +51,7 @@ input_file_open(struct input_stream *is, const char *filename)
*slash = '\0';
}
- fd = open_cloexec(pathname, O_RDONLY);
+ fd = open_cloexec(pathname, O_RDONLY, 0);
if (fd < 0) {
is->error = errno;
g_debug("Failed to open \"%s\": %s",
diff --git a/src/input_stream.h b/src/input_stream.h
index 9e4a526ef..edecb8460 100644
--- a/src/input_stream.h
+++ b/src/input_stream.h
@@ -26,6 +26,10 @@
#include <stdbool.h>
#include <sys/types.h>
+#if !GLIB_CHECK_VERSION(2,14,0)
+typedef gint64 goffset;
+#endif
+
struct input_stream {
/**
* the plugin which implements this input stream
diff --git a/src/listen.c b/src/listen.c
index 668a8077c..c1611d4f0 100644
--- a/src/listen.c
+++ b/src/listen.c
@@ -22,6 +22,7 @@
#include "client.h"
#include "conf.h"
#include "fd_util.h"
+#include "glib_compat.h"
#include "config.h"
#include <sys/types.h>
diff --git a/src/log.c b/src/log.c
index b2de391bd..bb1f1f112 100644
--- a/src/log.c
+++ b/src/log.c
@@ -129,7 +129,7 @@ open_log_file(void)
{
assert(out_filename != NULL);
- return open_cloexec(out_filename, O_CREAT | O_WRONLY | O_APPEND);
+ return open_cloexec(out_filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
}
static void
diff --git a/src/main.c b/src/main.c
index 70166fefb..2c970ba0a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -111,8 +111,10 @@ glue_mapper_init(void)
const char *music_dir, *playlist_dir;
music_dir = config_get_path(CONF_MUSIC_DIR);
+#if GLIB_CHECK_VERSION(2,14,0)
if (music_dir == NULL)
music_dir = g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
+#endif
playlist_dir = config_get_path(CONF_PLAYLIST_DIR);
diff --git a/src/mixer/oss_mixer_plugin.c b/src/mixer/oss_mixer_plugin.c
index 028786ae9..631107b70 100644
--- a/src/mixer/oss_mixer_plugin.c
+++ b/src/mixer/oss_mixer_plugin.c
@@ -123,7 +123,7 @@ oss_mixer_open(struct mixer *data, GError **error_r)
{
struct oss_mixer *om = (struct oss_mixer *) data;
- om->device_fd = open_cloexec(om->device, O_RDONLY);
+ om->device_fd = open_cloexec(om->device, O_RDONLY, 0);
if (om->device_fd < 0) {
g_set_error(error_r, oss_mixer_quark(), errno,
"failed to open %s: %s",
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index 48a40fb9b..64a8127ba 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -69,6 +69,16 @@ struct alsa_data {
/** the size of one audio frame */
size_t frame_size;
+
+ /**
+ * The size of one period, in number of frames.
+ */
+ snd_pcm_uframes_t period_frames;
+
+ /**
+ * The number of frames written in the current period.
+ */
+ snd_pcm_uframes_t period_position;
};
/**
@@ -413,6 +423,9 @@ configure_hw:
g_debug("buffer_size=%u period_size=%u",
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
+ ad->period_frames = alsa_period_size;
+ ad->period_position = 0;
+
return true;
error:
@@ -479,6 +492,7 @@ alsa_recover(struct alsa_data *ad, int err)
/* fall-through to snd_pcm_prepare: */
case SND_PCM_STATE_SETUP:
case SND_PCM_STATE_XRUN:
+ ad->period_position = 0;
err = snd_pcm_prepare(ad->pcm);
break;
case SND_PCM_STATE_DISCONNECTED:
@@ -500,8 +514,33 @@ alsa_drain(void *data)
{
struct alsa_data *ad = data;
- if (snd_pcm_state(ad->pcm) == SND_PCM_STATE_RUNNING)
- snd_pcm_drain(ad->pcm);
+ if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
+ return;
+
+ if (ad->period_position > 0) {
+ /* generate some silence to finish the partial
+ period */
+ snd_pcm_uframes_t nframes =
+ ad->period_frames - ad->period_position;
+ size_t nbytes = nframes * ad->frame_size;
+ void *buffer = g_malloc(nbytes);
+ snd_pcm_hw_params_t *params;
+ snd_pcm_format_t format;
+ unsigned channels;
+
+ snd_pcm_hw_params_alloca(&params);
+ snd_pcm_hw_params_current(ad->pcm, params);
+ snd_pcm_hw_params_get_format(params, &format);
+ snd_pcm_hw_params_get_channels(params, &channels);
+
+ snd_pcm_format_set_silence(format, buffer, nframes * channels);
+ ad->writei(ad->pcm, buffer, nframes);
+ g_free(buffer);
+ }
+
+ snd_pcm_drain(ad->pcm);
+
+ ad->period_position = 0;
}
static void
@@ -509,6 +548,8 @@ alsa_cancel(void *data)
{
struct alsa_data *ad = data;
+ ad->period_position = 0;
+
snd_pcm_drop(ad->pcm);
}
@@ -529,8 +570,11 @@ alsa_play(void *data, const void *chunk, size_t size, GError **error)
while (true) {
snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
- if (ret > 0)
+ if (ret > 0) {
+ ad->period_position = (ad->period_position + ret)
+ % ad->period_frames;
return ret * ad->frame_size;
+ }
if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
alsa_recover(ad, ret) < 0) {
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index 0217c2675..b5e6f5314 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -153,7 +153,7 @@ fifo_open(struct fifo_data *fd, GError **error)
if (!fifo_check(fd, error))
return false;
- fd->input = open_cloexec(fd->path, O_RDONLY|O_NONBLOCK);
+ fd->input = open_cloexec(fd->path, O_RDONLY|O_NONBLOCK, 0);
if (fd->input < 0) {
g_set_error(error, fifo_output_quark(), errno,
"Could not open FIFO \"%s\" for reading: %s",
@@ -162,7 +162,7 @@ fifo_open(struct fifo_data *fd, GError **error)
return false;
}
- fd->output = open_cloexec(fd->path, O_WRONLY|O_NONBLOCK);
+ fd->output = open_cloexec(fd->path, O_WRONLY|O_NONBLOCK, 0);
if (fd->output < 0) {
g_set_error(error, fifo_output_quark(), errno,
"Could not open FIFO \"%s\" for writing: %s",
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
index 8157ebb44..62ede81be 100644
--- a/src/output/httpd_client.c
+++ b/src/output/httpd_client.c
@@ -22,6 +22,7 @@
#include "fifo_buffer.h"
#include "page.h"
#include "icy_server.h"
+#include "glib_compat.h"
#include <stdbool.h>
#include <assert.h>
diff --git a/src/output/mvp_plugin.c b/src/output/mvp_plugin.c
index f5fbadbee..7e6dd6d31 100644
--- a/src/output/mvp_plugin.c
+++ b/src/output/mvp_plugin.c
@@ -116,7 +116,7 @@ mvp_output_test_default_device(void)
{
int fd;
- fd = open_cloexec("/dev/adec_pcm", O_WRONLY);
+ fd = open_cloexec("/dev/adec_pcm", O_WRONLY, 0);
if (fd >= 0) {
close(fd);
@@ -231,7 +231,7 @@ mvp_output_open(void *data, struct audio_format *audio_format, GError **error)
int mix[5] = { 0, 2, 7, 1, 0 };
bool success;
- md->fd = open_cloexec("/dev/adec_pcm", O_RDWR | O_NONBLOCK);
+ md->fd = open_cloexec("/dev/adec_pcm", O_RDWR | O_NONBLOCK, 0);
if (md->fd < 0) {
g_set_error(error, mvp_output_quark(), errno,
"Error opening /dev/adec_pcm: %s",
diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c
index f308c293e..6518c3f49 100644
--- a/src/output/oss_plugin.c
+++ b/src/output/oss_plugin.c
@@ -344,7 +344,7 @@ oss_output_test_default_device(void)
int fd, i;
for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
- fd = open_cloexec(default_devices[i], O_WRONLY);
+ fd = open_cloexec(default_devices[i], O_WRONLY, 0);
if (fd >= 0) {
close(fd);
@@ -519,7 +519,7 @@ oss_open(struct oss_data *od, GError **error)
{
bool success;
- od->fd = open_cloexec(od->device, O_WRONLY);
+ od->fd = open_cloexec(od->device, O_WRONLY, 0);
if (od->fd < 0) {
g_set_error(error, oss_output_quark(), errno,
"Error opening OSS device \"%s\": %s",
diff --git a/src/output/recorder_output_plugin.c b/src/output/recorder_output_plugin.c
index 202b56073..11dd6c041 100644
--- a/src/output/recorder_output_plugin.c
+++ b/src/output/recorder_output_plugin.c
@@ -157,7 +157,8 @@ recorder_output_open(void *data, struct audio_format *audio_format,
/* create the output file */
- recorder->fd = creat_cloexec(recorder->path, 0666);
+ recorder->fd = open_cloexec(recorder->path, O_CREAT|O_WRONLY|O_TRUNC,
+ 0666);
if (recorder->fd < 0) {
g_set_error(error_r, recorder_output_quark(), 0,
"Failed to create '%s': %s",
diff --git a/src/output_all.c b/src/output_all.c
index 5b7cc4908..29590abf0 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -496,6 +496,15 @@ audio_output_all_pause(void)
}
void
+audio_output_all_drain(void)
+{
+ for (unsigned i = 0; i < num_audio_outputs; ++i)
+ audio_output_drain_async(&audio_outputs[i]);
+
+ audio_output_wait_all();
+}
+
+void
audio_output_all_cancel(void)
{
unsigned int i;
diff --git a/src/output_all.h b/src/output_all.h
index 91b6f3f5d..6ff45fb79 100644
--- a/src/output_all.h
+++ b/src/output_all.h
@@ -130,6 +130,12 @@ void
audio_output_all_pause(void);
/**
+ * Drain all audio outputs.
+ */
+void
+audio_output_all_drain(void);
+
+/**
* Try to cancel data which may still be in the device's buffers.
*/
void
diff --git a/src/output_control.c b/src/output_control.c
index 842b89030..795d04a8c 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -229,6 +229,15 @@ void audio_output_pause(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
}
+void
+audio_output_drain_async(struct audio_output *ao)
+{
+ g_mutex_lock(ao->mutex);
+ if (audio_output_is_open(ao))
+ ao_command_async(ao, AO_COMMAND_DRAIN);
+ g_mutex_unlock(ao->mutex);
+}
+
void audio_output_cancel(struct audio_output *ao)
{
g_mutex_lock(ao->mutex);
diff --git a/src/output_control.h b/src/output_control.h
index b2e48fa8d..692c11676 100644
--- a/src/output_control.h
+++ b/src/output_control.h
@@ -67,6 +67,9 @@ audio_output_play(struct audio_output *ao);
void audio_output_pause(struct audio_output *ao);
+void
+audio_output_drain_async(struct audio_output *ao);
+
void audio_output_cancel(struct audio_output *ao);
void audio_output_close(struct audio_output *ao);
void audio_output_finish(struct audio_output *ao);
diff --git a/src/output_internal.h b/src/output_internal.h
index 6b81bbc78..de1b15c29 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -40,6 +40,13 @@ enum audio_output_command {
AO_COMMAND_CLOSE,
AO_COMMAND_PAUSE,
+
+ /**
+ * Drains the internal (hardware) buffers of the device. This
+ * operation may take a while to complete.
+ */
+ AO_COMMAND_DRAIN,
+
AO_COMMAND_CANCEL,
AO_COMMAND_KILL
};
diff --git a/src/output_thread.c b/src/output_thread.c
index 40906d82f..fb1701591 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -450,6 +450,19 @@ static gpointer audio_output_task(gpointer arg)
the new command first */
continue;
+ case AO_COMMAND_DRAIN:
+ if (ao->open) {
+ assert(ao->chunk == NULL);
+ assert(music_pipe_peek(ao->pipe) == NULL);
+
+ g_mutex_unlock(ao->mutex);
+ ao_plugin_drain(ao->plugin, ao->data);
+ g_mutex_lock(ao->mutex);
+ }
+
+ ao_command_finished(ao);
+ continue;
+
case AO_COMMAND_CANCEL:
ao->chunk = NULL;
if (ao->open)
diff --git a/src/player_control.h b/src/player_control.h
index 02c04fda8..e279067d5 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -107,9 +107,9 @@ struct player_control {
struct audio_format audio_format;
float total_time;
float elapsed_time;
- struct song *volatile next_song;
+ struct song *next_song;
const struct song *errored_song;
- volatile double seek_where;
+ double seek_where;
float cross_fade_seconds;
double total_play_time;
};
diff --git a/src/player_thread.c b/src/player_thread.c
index 5cb73a3ee..c527c57b6 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -874,8 +874,12 @@ static void do_play(struct decoder_control *dc)
/* check the size of the pipe again, because
the decoder thread may have added something
since we last checked */
- if (music_pipe_size(player.pipe) == 0)
+ if (music_pipe_size(player.pipe) == 0) {
+ /* wait for the hardware to finish
+ playback */
+ audio_output_all_drain();
break;
+ }
} else {
/* the decoder is too busy and hasn't provided
new PCM data in time: send silence (if the
diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/lastfm_playlist_plugin.c
index d7de25375..70e51c2fc 100644
--- a/src/playlist/lastfm_playlist_plugin.c
+++ b/src/playlist/lastfm_playlist_plugin.c
@@ -24,6 +24,7 @@
#include "uri.h"
#include "song.h"
#include "input_stream.h"
+#include "glib_compat.h"
#include <glib.h>
@@ -55,11 +56,7 @@ lastfm_init(const struct config_param *param)
return false;
}
-#if GLIB_CHECK_VERSION(2,16,0)
lastfm_config.user = g_uri_escape_string(user, NULL, false);
-#else
- lastfm_config.user = g_strdup(user);
-#endif
#if GLIB_CHECK_VERSION(2,16,0)
if (strlen(passwd) != 32)
@@ -178,11 +175,9 @@ lastfm_open_uri(const char *uri)
return NULL;
}
-#if GLIB_CHECK_VERSION(2,16,0)
q = g_uri_escape_string(session, NULL, false);
g_free(session);
session = q;
-#endif
g_debug("session='%s'", session);
@@ -191,11 +186,7 @@ lastfm_open_uri(const char *uri)
if (strlen(uri) > 9) {
char *escaped_uri;
-#if GLIB_CHECK_VERSION(2,16,0)
escaped_uri = g_uri_escape_string(uri, NULL, false);
-#else
- escaped_uri = g_strdup(uri);
-#endif
p = g_strconcat("http://ws.audioscrobbler.com/radio/adjust.php?"
"session=", session, "&url=", escaped_uri, "&debug=0",
diff --git a/src/playlist_list.c b/src/playlist_list.c
index 2c6237ed4..d6359463e 100644
--- a/src/playlist_list.c
+++ b/src/playlist_list.c
@@ -104,6 +104,18 @@ playlist_list_global_finish(void)
playlist_plugin_finish(playlist_plugins[i]);
}
+/* g_uri_parse_scheme() was introduced in GLib 2.16 */
+#if !GLIB_CHECK_VERSION(2,16,0)
+static char *
+g_uri_parse_scheme(const char *uri)
+{
+ const char *end = strstr(uri, "://");
+ if (end == NULL)
+ return NULL;
+ return g_strndup(uri, end - uri);
+}
+#endif
+
struct playlist_provider *
playlist_list_open_uri(const char *uri)
{
diff --git a/src/state_file.c b/src/state_file.c
index aa66720c1..4c7dab426 100644
--- a/src/state_file.c
+++ b/src/state_file.c
@@ -22,6 +22,7 @@
#include "playlist.h"
#include "playlist_state.h"
#include "volume.h"
+#include "glib_compat.h"
#include <glib.h>
#include <assert.h>
diff --git a/src/sticker.c b/src/sticker.c
index 4cccd9399..4135e6293 100644
--- a/src/sticker.c
+++ b/src/sticker.c
@@ -27,6 +27,10 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "sticker"
+#if SQLITE_VERSION_NUMBER < 3003009
+#define sqlite3_prepare_v2 sqlite3_prepare
+#endif
+
struct sticker {
GHashTable *table;
};
diff --git a/test/read_mixer.c b/test/read_mixer.c
index 0272ec67a..208503618 100644
--- a/test/read_mixer.c
+++ b/test/read_mixer.c
@@ -21,8 +21,8 @@
#include "mixer_list.h"
#include "filter_registry.h"
#include "pcm_volume.h"
-#include "output/pulse_output_plugin.h"
#include "event_pipe.h"
+#include "config.h"
#include <glib.h>
@@ -30,6 +30,9 @@
#include <string.h>
#include <unistd.h>
+#ifdef HAVE_PULSE
+#include "output/pulse_output_plugin.h"
+
void
pulse_output_set_mixer(G_GNUC_UNUSED struct pulse_output *po,
G_GNUC_UNUSED struct pulse_mixer *pm)
@@ -50,6 +53,8 @@ pulse_output_set_volume(G_GNUC_UNUSED struct pulse_output *po,
return false;
}
+#endif
+
void
event_pipe_emit(G_GNUC_UNUSED enum pipe_event event)
{