diff options
Diffstat (limited to '')
34 files changed, 329 insertions, 147 deletions
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> @@ -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(¶ms); + 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; }; |