Compare commits

..

79 Commits

Author SHA1 Message Date
Avuton Olrich
ffb3a9f526 mpd version 0.17.3 2013-01-06 16:47:09 -08:00
Max Kellermann
9761abf3b5 cue_parser: fix CUE files with only one track
Track whether _finish() has been called, and deliver all partial
results then.  Fixes Mantis ticket 0003621.
2013-01-03 21:58:20 +01:00
Max Kellermann
599a562170 cue_parser: add code comments 2013-01-03 21:38:38 +01:00
Max Kellermann
d29a251547 .gitignore: add more debug programs 2013-01-03 21:21:32 +01:00
Max Kellermann
31da4bc566 cue_parser: fix memory leak 2013-01-03 21:02:59 +01:00
Denis Krjuchkov
0f1a180e15 mpd_auto.m4: Pass libraries to AC_CHECK_LIB in MPD_AUTO_PKG_LIB
Rationale: vanilla libid3tag does not have any pkg-config stuff
and fails to detect because symbols from libz are not found.
2013-01-03 19:59:41 +01:00
Denis Krjuchkov
01a45a53aa cmdline: bunch of fixes related to config file selection
- fix potential memory leak of system_path

  'Potential' because currently g_get_system_config_dirs()
  returns single entry on Windows, but that might change.

- remove incorrect g_free() call

  It's not required at all because
  g_get_system_config_dirs() returns GLib owned memory.

- remove extra semicolon
2013-01-03 19:45:51 +01:00
John
a9a5907a0f mpd.service: depend on network.target
Since some configurations use the "bind_to_address" option in their
/etc/mpd.conf, the systemd service file must wait for the
network.target or else mpd will start before it and thus fail due to
no iface.
2012-11-21 17:26:23 +01:00
Max Kellermann
8fb20fcdf8 playlist_song: fix potential charset bug in apply_song_metadata()
The song's URI must be UTF-8, not filesystem character set.
2012-10-05 17:01:04 +02:00
Max Kellermann
72bf226608 playlist_save: use temp2 instead of temp
Fixes minor Windows compatibility problem.
2012-10-05 16:55:30 +02:00
Max Kellermann
d4b5699403 decoder/ffmpeg: support planar audio
Implements Mantis feature request 3582.
2012-10-05 16:40:25 +02:00
Max Kellermann
1dc27be015 decoder/ffmpeg: fix playback of planar PCM data
Interleaving was completely wrong.  This code was never used, so it
didn't have an effect.
2012-10-05 16:38:55 +02:00
Max Kellermann
230a3eb400 decoder/ffmpeg: move code to copy_interleave_frame2() 2012-10-05 16:37:07 +02:00
Max Kellermann
e39382dedd decoder/ffmpeg: ignore negative time stamps
Works around assertion failure in decoder_timestamp().
2012-10-05 16:37:07 +02:00
Max Kellermann
fd016f4507 decoder/ffmpeg: show unsupported sample format name
Use av_get_sample_fmt_string() to obtain a human-readable string.
2012-10-05 15:24:53 +02:00
Max Kellermann
9d728b365d decoder/ffmpeg: pass AVSampleFormat to ffmpeg_sample_format()
API simplification.
2012-10-05 15:14:57 +02:00
Max Kellermann
ddc0283339 decoder/ffmpeg: remove duplicate sample format error message 2012-10-05 14:52:30 +02:00
Gregory Smith
03a401e477 OSX: Set mDataByteSize correctly on AudioBuffers during render. 2012-10-02 17:27:52 +02:00
Max Kellermann
9994521b8c test/dump_playlist: add missing newline to error message 2012-10-02 17:27:47 +02:00
Max Kellermann
adbe8c409a output/{recorder,shout}: call encoder_read() in a loop
This is necessary for Ogg packets that span more than one page.
2012-10-02 00:26:40 +02:00
Max Kellermann
58e600f408 output/recorder: move code to _write_to_file() 2012-10-02 00:26:40 +02:00
Max Kellermann
d34e55c370 output/recorder: fix write() error check
We can only check for negative values if the variable is signed.
2012-10-02 00:20:42 +02:00
Max Kellermann
fbcbcdc001 output/recorder: make variables more local 2012-10-02 00:20:32 +02:00
Max Kellermann
4227a325a5 output/httpd: make variables more local 2012-10-02 00:20:13 +02:00
Max Kellermann
d115507502 encoder/vorbis: make variables more local 2012-10-02 00:20:01 +02:00
Max Kellermann
43d8252050 output/recorder, test/*: invoke encoder_read() after _open()
Make sure the file header gets written at the beginning, before
_write() gets called.
2012-10-02 00:18:18 +02:00
Max Kellermann
674b4ab647 output/shout: eliminate struct shout_buffer
Move the raw buffer to struct shout_data.
2012-10-02 00:18:04 +02:00
Max Kellermann
fe8fc1081a output/shout: remove shout_buffer.len
Make it a local variable instead.
2012-10-02 00:17:53 +02:00
Max Kellermann
c7748fedab output/shout: fix memory leak in error handler 2012-10-02 00:17:27 +02:00
Max Kellermann
c392efb481 output/shout: make variables more local 2012-10-02 00:17:17 +02:00
Max Kellermann
1ddd9dd52a test/run_encoder: fix encoder_open() call 2012-10-02 00:17:08 +02:00
Avuton Olrich
f672e4016f Modify version string to post-release version 0.17.3~git 2012-09-30 03:27:38 -07:00
Avuton Olrich
76e3dec723 mpd version 0.17.2 2012-09-30 03:27:38 -07:00
Max Kellermann
ba6ef53ef9 decoder_control: remove MixRamp debug messages
These are confusing, and since MixRamp development has ceased, not
useful to anybody.
2012-09-25 11:08:16 +02:00
Max Kellermann
c93a28c641 configure.ac: don't auto-detect the Vorbis encoder when Tremor is enabled
libvorbisidec and libvorbis export the same symbols, which is a
dangerous thing.  Since libvorbisenc depends on libvorbis, this can
get nasty, so let's disable the Vorbis encoder unless the user
explicitly wants it.
2012-09-25 10:41:39 +02:00
Max Kellermann
7088a679a2 decoder/wavpack: support all APEv2 tags
WavPack tags are always APEv2, by definition.  Reuse the tag_table
from tag_ape.c, instead of rolling our own.
2012-09-25 09:37:16 +02:00
Max Kellermann
04c02a1eb8 locate: cast enum tag_type to int before comparing with integer
Avoids clang pickiness.  This code is not correct, but we'll fix that
another day.
2012-09-22 09:48:27 +02:00
Max Kellermann
41487426f5 decoder/_ogg_common: fix buffer size check
Fixes potential access to uninitialised memory.
2012-09-04 11:22:15 +02:00
Max Kellermann
0d24250aa7 decoder/_ogg_common: simplify the large "if" expression 2012-09-04 11:22:05 +02:00
Wieland Hoffmann
2050e2f886 mpd.conf(5): Use the correct default value for max_playlist_length 2012-09-03 22:49:51 +02:00
Max Kellermann
013e8479af AudioCompress: abort on out-of-memory
This library crashes on out-of-memory (NULL pointer dereference).
There's not much useful MPD can do in such a situation, so let's
explicitly abort instead, just like GLib does.
2012-09-03 22:45:33 +02:00
Max Kellermann
27535a7f78 update_walk: fix unsafe readlink() usage 2012-09-03 22:41:04 +02:00
Max Kellermann
acaa725478 playlist/cue: map "PERFORMER" to "artist" or "album artist"
Implements Mantis ticket 0003549.
2012-08-25 09:56:14 +02:00
Max Kellermann
f351550534 player_thread: disable cross-fading in "single" mode
This commit reimplements the core of the "single" mode.  Instead of
doing the detection in the playlist code from the outside, it is moved
to the player thread, which gets a new option called "border_pause".
It will now pause playback exactly at the beginning of the new song,
making the feature more reliable.

Now that the player thread knows what will happen, it can suppress
cross-fading.

Fixes mantis tickets 0003055 and 0003166.
2012-08-25 09:38:41 +02:00
Max Kellermann
66ecf39efe command: make "single" a bool 2012-08-21 19:38:08 +02:00
Max Kellermann
5ad21d7e98 queue_save: save song priorities 2012-08-21 19:17:14 +02:00
Max Kellermann
ef5125f8f4 playlist_print: fix memory leak 2012-08-16 00:01:01 +02:00
Max Kellermann
bf2e07074b playlist_song: pass const song to _check_load_song() 2012-08-16 00:00:50 +02:00
Max Kellermann
20695ef369 playlist_song: fix user-after-free bug 2012-08-16 00:00:21 +02:00
Max Kellermann
9374e0f445 player_thread: add local variable "start_ms"
Just in case "song" becomes invalid at some point.
2012-08-15 22:51:48 +02:00
Max Kellermann
19ed233118 playlist: fix unprotected player_control access 2012-08-15 22:47:08 +02:00
Max Kellermann
faa4fff4dd filter/volume: include cleanup 2012-08-15 22:45:03 +02:00
Max Kellermann
2276e7677b mapper: fix potential crash in file permission check 2012-08-15 22:44:13 +02:00
Max Kellermann
93f9c2ab6b doc/user: add wildmidi documentation 2012-08-15 01:03:16 +02:00
Max Kellermann
4a993cd79e decoder/fluidsynth: add "sample_rate" setting 2012-08-15 00:57:32 +02:00
Max Kellermann
02325d2ede decoder/fluidsynth: add "soundfont" setting
Replaces the old global "soundfont" which never worked.
2012-08-15 00:51:45 +02:00
Max Kellermann
9c83464b95 configure.ac: auto-detect libfluidsynth
Now that the libfluidsynth API was sanitized, we can enable the plugin
automatically if libfluidsynth is installed.
2012-08-15 00:48:52 +02:00
Max Kellermann
b1bbd70f0f decoder/fluidsynth: stop playback at end of file
Use libfluidsynth's new function fluid_player_get_status().
2012-08-15 00:47:10 +02:00
Max Kellermann
c31d11bfe0 decoder/fluidsynth: don't duplicate path
The libfluidsynth now accepts const strings.
2012-08-15 00:39:22 +02:00
Max Kellermann
c8ec85d649 decoder/fluidsynth: check if file is really a MIDI
Use fluid_is_midifile() to verify the file format.
2012-08-15 00:36:04 +02:00
Max Kellermann
e291f3d257 decoder/fluidsynth: remove throttle (requires libfluidsynth 1.1)
The libfluidsynth API is now sane, and does not require real-time
decoding.
2012-08-15 00:29:38 +02:00
Max Kellermann
dc22846d58 log: store duplicated path string
Don't free the string right after calling log_init_file().  Add a new
function log_deinit() that frees the string on shutdown.

This fixes cycling the log file after SIGHUP (Mantis ticket 0003524).
2012-08-14 23:16:46 +02:00
Max Kellermann
c9aaabb5d4 output/jack: implement method delay()
Eliminate the g_usleep() call.
2012-08-14 22:47:25 +02:00
Max Kellermann
335d5d5d72 output/pulse: implement method delay()
Reduce command latency while paused.
2012-08-14 22:30:46 +02:00
Max Kellermann
51d793bec1 output/pulse: simplify _wait_stream()
One large loop and only one pa_stream_get_state() call.
2012-08-14 22:22:55 +02:00
Max Kellermann
249dcd967e output/httpd: move delay from _pause() to _delay() 2012-08-14 21:54:47 +02:00
Max Kellermann
302972e9fc output/httpd: fix throttling bug after resuming playback
Reset the timer when paused and no client is connected.

This fixes Mantis ticket 0003527.
2012-08-14 21:39:33 +02:00
Max Kellermann
31b380b266 output/httpd: move code to _has_clients() 2012-08-14 20:22:32 +02:00
Max Kellermann
a869dfea85 timer: use monotonic clock if available 2012-08-14 19:07:31 +02:00
Max Kellermann
12838c6294 input/ffmpeg: remove fallback AV_VERSION_INT definition
This is part of libavutil.
2012-08-14 19:07:27 +02:00
Wieland Hoffmann
49c7102547 mpd.conf(5): Document the existence of musicbrainz_ tags
Additionally, update mpdconf.example to refer to mpd.conf(5) for the
complete list of tags instead of trying to repeat it.
2012-08-14 09:43:18 +02:00
Max Kellermann
1ae8972859 mapper: fix non-UTF8 music directory name
Duplicate the music_dir variable: one encoded in UTF-8, and another
one using the configured filesystem character set.  This fixes an
ancient MPD bug.
2012-08-14 02:28:04 +02:00
Max Kellermann
adcd2c8eac playlist_song: use map_to_relative_path() 2012-08-14 02:25:19 +02:00
Max Kellermann
45ff355835 playlist_song: improve const-correctness 2012-08-14 02:24:16 +02:00
Max Kellermann
f8bf3afeae playlist_song: move code to playlist_check_load_song() 2012-08-14 02:17:25 +02:00
Max Kellermann
f703da1516 valgrind.suppressions: suppressions for GStaticMutex and more 2012-08-14 01:58:17 +02:00
Max Kellermann
a582deee2c input_stream, main: remove obsolete GLib version checks
MPD requires GLib 2.16.
2012-08-14 01:57:53 +02:00
Anton Khirnov
12be9e818f client_file: remove pure attribute from client_allow_file().
That function is not pure, it writes to error.

When marked as pure, the compiler is allowed to assume it does not do
anything to error, so it can remain NULL, which would result in an
invalid read in print_error().
2012-08-13 07:55:40 +02:00
Avuton Olrich
281cd7c057 Modify version string to post-release version 0.17.2~git 2012-07-31 19:05:52 -07:00
65 changed files with 988 additions and 517 deletions

4
.gitignore vendored
View File

@@ -67,3 +67,7 @@ test/run_ntp_server
test/run_resolver
test/run_tcp_connect
test/test_pcm
test/dump_rva2
test/dump_text_file
test/test_byte_reverse
test/test_vorbis_encoder

View File

@@ -103,7 +103,7 @@ libsidplay2 - http://sidplay2.sourceforge.net/
For C64 SID support.
libfluidsynth - http://fluidsynth.resonance.org/
For MIDI support (DO NOT USE - use libwildmidi instead)
For MIDI support.
libwildmidi - http://wildmidi.sourceforge.net/
For MIDI support.

View File

@@ -227,6 +227,7 @@ src_mpd_SOURCES = \
$(OUTPUT_API_SRC) \
$(MIXER_API_SRC) \
src/glib_socket.h \
src/clock.c src/clock.h \
src/notify.c \
src/audio_config.c src/audio_config.h \
src/audio_check.c \
@@ -1069,7 +1070,6 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \
src/audio_check.c src/pcm_buffer.c \
src/text_input_stream.c src/fifo_buffer.c \
src/cue/cue_parser.c src/cue/cue_parser.h \
src/timer.c \
src/fd_util.c
if HAVE_FLAC
@@ -1096,7 +1096,6 @@ test_run_decoder_SOURCES = test/run_decoder.c \
src/fd_util.c \
src/audio_check.c \
src/audio_format.c \
src/timer.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(TAG_SRC) \
@@ -1118,7 +1117,6 @@ test_read_tags_SOURCES = test/read_tags.c \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
src/timer.c \
$(DECODER_SRC)
if HAVE_ID3TAG
@@ -1240,7 +1238,7 @@ test_run_output_SOURCES = test/run_output.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/timer.c \
src/timer.c src/clock.c \
src/tag.c src/tag_pool.c \
src/fifo_buffer.c src/growing_fifo.c \
src/page.c \

37
NEWS
View File

@@ -1,3 +1,40 @@
ver 0.17.3 (2013/01/06)
* output:
- osx: fix pops during playback
- recorder: fix I/O error check
- shout: fix memory leak in error handler
- recorder, shout: support Ogg packets that span more than one page
* decoder:
- ffmpeg: ignore negative time stamps
- ffmpeg: support planar audio
* playlist:
- cue: fix memory leak
- cue: fix CUE files with only one track
ver 0.17.2 (2012/09/30)
* protocol:
- fix crash in local file check
* decoder:
- fluidsynth: remove throttle (requires libfluidsynth 1.1)
- fluidsynth: stop playback at end of file
- fluidsynth: check MIDI file format while scanning
- fluidsynth: add sample rate setting
- wavpack: support all APEv2 tags
* output:
- httpd: use monotonic clock, avoid hiccups after system clock adjustment
- httpd: fix throttling bug after resuming playback
* playlist:
- cue: map "PERFORMER" to "artist" or "album artist"
* mapper: fix non-UTF8 music directory name
* mapper: fix potential crash in file permission check
* playlist: fix use-after-free bug
* playlist: fix memory leak
* state_file: save song priorities
* player: disable cross-fading in "single" mode
* update: fix unsafe readlink() usage
* configure.ac:
- don't auto-detect the vorbis encoder when Tremor is enabled
ver 0.17.1 (2012/07/31)
* protocol:
- require appropriate permissions for searchadd{,pl}

View File

@@ -1,6 +1,6 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.17.1, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.17.3, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=17
@@ -217,8 +217,8 @@ AC_ARG_ENABLE(flac,
AC_ARG_ENABLE(fluidsynth,
AS_HELP_STRING([--enable-fluidsynth],
[enable MIDI support via fluidsynth (default: disable)]),,
enable_fluidsynth=no)
[enable MIDI support via fluidsynth (default: auto)]),,
enable_fluidsynth=auto)
AC_ARG_ENABLE(gme,
AS_HELP_STRING([--enable-gme],
@@ -845,7 +845,7 @@ enable_flac_encoder=$enable_flac
dnl -------------------------------- FluidSynth -------------------------------
if test x$enable_fluidsynth = xyes; then
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth],
PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth >= 1.1],
AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]),
enable_fluidsynth=no)
fi
@@ -999,6 +999,11 @@ if test x$enable_tremor = xyes; then
AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."])
fi
enable_vorbis=no
if test x$enable_vorbis_encoder = xauto; then
AC_MSG_WARN([OggTremor detected, disabling the Vorbis encoder plugin.])
enable_vorbis_encoder=no
fi
fi
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg],

View File

@@ -216,7 +216,7 @@ default is 5.
.TP
.B max_playlist_length <number>
This specifies the maximum number of songs that can be in the playlist. The
default is 4096.
default is 16384.
.TP
.B max_command_list_size <size in KiB>
This specifies the maximum size a command list can be. The default is 2048.
@@ -252,11 +252,12 @@ when saving playlists. The default is "no".
This specifies the tag types that will be scanned for and made available to
clients. Note that you must recreate (not update) your database for changes to
this parameter to take effect. Possible values are artist, album, title,
track, name, genre, date, composer, performer, comment, and disc. Multiple
tags may be specified as a comma separated list. An example value is
"artist,album,title,track". The special value "none" may be used alone to
disable all metadata. The default is to use all known tag types except for
comments.
track, name, genre, date, composer, performer, comment, disc,
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
An example value is "artist,album,title,track". The special value "none" may
be used alone to disable all metadata. The default is to use all known tag
types except for comments and those starting with "musicbrainz".
.TP
.B auto_update <yes or no>
This specifies the wheter to support automatic update of music database when

View File

@@ -113,10 +113,9 @@
#
#save_absolute_paths_in_playlists "no"
#
# This setting defines a list of tag types that will be extracted during the
# audio file discovery process. Optionally, 'comment' can be added to this
# list.
#
# This setting defines a list of tag types that will be extracted during the
# audio file discovery process. The complete list of possible values can be
# found in the mpd.conf man page.
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in

View File

@@ -818,6 +818,46 @@ systemctl start mpd.socket</programlisting>
</section>
<section>
<title><varname>fluidsynth</varname></title>
<para>
MIDI decoder based on libfluidsynth.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>sample_rate</varname>
</entry>
<entry>
The sample rate that shall be synthesized by the
plugin. Defaults to 48000.
</entry>
</row>
<row>
<entry>
<varname>soundfont</varname>
</entry>
<entry>
The absolute path of the soundfont file. Defaults
to
<filename>/usr/share/sounds/sf2/FluidR3_GM.sf2</filename>.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>mikmod</varname></title>
@@ -847,6 +887,37 @@ systemctl start mpd.socket</programlisting>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>wildmidi</varname></title>
<para>
MIDI decoder based on libwildmidi.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>config_file</varname>
</entry>
<entry>
The absolute path of the timidity config file. Defaults
to
<filename>/etc/timidity/timidity.cfg</filename>.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section>
<section>

View File

@@ -73,7 +73,8 @@ AC_DEFUN([MPD_AUTO_PKG_LIB], [
[eval "found_$1=yes"],
AC_CHECK_LIB($4, $5,
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
[eval "found_$1=no"]))
[eval "found_$1=no"],
[$6]))
fi
MPD_AUTO_RESULT([$1], [$8], [$9])

View File

@@ -1,6 +1,6 @@
[Unit]
Description=Music Player Daemon
After=sound.target
After=network.target sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon

View File

@@ -33,6 +33,9 @@ struct Compressor {
struct Compressor *Compressor_new(unsigned int history)
{
struct Compressor *obj = malloc(sizeof(struct Compressor));
if (obj == NULL)
/* out of memory, not much we can do */
abort();
obj->prefs.target = TARGET;
obj->prefs.maxgain = GAINMAX;
@@ -61,6 +64,10 @@ void Compressor_delete(struct Compressor *obj)
static int *resizeArray(int *data, int newsz, int oldsz)
{
data = realloc(data, newsz*sizeof(int));
if (data == NULL)
/* out of memory, not much we can do */
abort();
if (newsz > oldsz)
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
return data;

View File

@@ -35,7 +35,6 @@ struct client;
* @param path_fs the absolute path name in filesystem encoding
* @return true if access is allowed
*/
G_GNUC_PURE
bool
client_allow_file(const struct client *client, const char *path_fs,
GError **error_r);

95
src/clock.c Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "clock.h"
#ifdef WIN32
#include <windows.h>
#elif defined(__APPLE__)
#include <mach/mach_time.h>
#else
#include <time.h>
#endif
unsigned
monotonic_clock_ms(void)
{
#ifdef WIN32
return GetTickCount();
#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */
static mach_timebase_info_data_t base;
if (base.denom == 0)
(void)mach_timebase_info(&base);
return (unsigned)((mach_absolute_time() * base.numer)
/ (1000000 * base.denom));
#elif defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
#else
/* we have no monotonic clock, fall back to gettimeofday() */
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}
uint64_t
monotonic_clock_us(void)
{
#ifdef WIN32
LARGE_INTEGER l_value, l_frequency;
if (!QueryPerformanceCounter(&l_value) ||
!QueryPerformanceFrequency(&l_frequency))
return 0;
uint64_t value = l_value.QuadPart;
uint64_t frequency = l_frequency.QuadPart;
if (frequency > 1000000) {
value *= 10000;
value /= frequency / 100;
} else if (frequency < 1000000) {
value *= 10000;
value /= frequency;
value *= 100;
}
return value;
#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */
static mach_timebase_info_data_t base;
if (base.denom == 0)
(void)mach_timebase_info(&base);
return ((uint64_t)mach_absolute_time() * (uint64_t)base.numer)
/ (1000 * (uint64_t)base.denom);
#elif defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000 + (uint64_t)(ts.tv_nsec / 1000);
#else
/* we have no monotonic clock, fall back to gettimeofday() */
struct timeval tv;
gettimeofday(&tv, 0);
return (uint64_t)tv.tv_sec * 1000 + (uint64_t)(tv.tv_usec) / 1000(;
#endif
}

41
src/clock.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_CLOCK_H
#define MPD_CLOCK_H
#include <glib.h>
#include <stdint.h>
/**
* Returns the value of a monotonic clock in milliseconds.
*/
G_GNUC_PURE
unsigned
monotonic_clock_ms(void);
/**
* Returns the value of a monotonic clock in microseconds.
*/
G_GNUC_PURE
uint64_t
monotonic_clock_us(void);
#endif

View File

@@ -213,12 +213,12 @@ parse_cmdline(int argc, char **argv, struct options *options,
if(g_file_test(system_path,
G_FILE_TEST_IS_REGULAR)) {
ret = config_read_file(system_path,error_r);
g_free(system_path);
break;
}
++i;;
} else
g_free(system_path);
++i;
}
g_free(system_path);
g_free(&system_config_dirs);
}
#else /* G_OS_WIN32 */
char *path2;

View File

@@ -1069,7 +1069,7 @@ handle_next(G_GNUC_UNUSED struct client *client,
{
/* single mode is not considered when this is user who
* wants to change song. */
int single = g_playlist.queue.single;
const bool single = g_playlist.queue.single;
g_playlist.queue.single = false;
playlist_next(&g_playlist, client->player_control);
@@ -1544,7 +1544,7 @@ handle_config(struct client *client,
return COMMAND_RETURN_ERROR;
}
const char *path = mapper_get_music_directory();
const char *path = mapper_get_music_directory_utf8();
if (path != NULL)
client_printf(client, "music_directory: %s\n", path);

View File

@@ -58,9 +58,35 @@ struct cue_parser {
char *filename;
struct song *current, *previous, *finished;
/**
* The song currently being edited.
*/
struct song *current;
/**
* The previous song. It is remembered because its end_time
* will be set to the current song's start time.
*/
struct song *previous;
/**
* A song that is completely finished and can be returned to
* the caller via cue_parser_get().
*/
struct song *finished;
/**
* Set to true after previous.end_time has been updated to the
* start time of the current song.
*/
bool last_updated;
/**
* Tracks whether cue_parser_finish() has been called. If
* true, then all remaining (partial) results will be
* delivered by cue_parser_get().
*/
bool end;
};
struct cue_parser *
@@ -73,6 +99,7 @@ cue_parser_new(void)
parser->current = NULL;
parser->previous = NULL;
parser->finished = NULL;
parser->end = false;
return parser;
}
@@ -85,6 +112,9 @@ cue_parser_free(struct cue_parser *parser)
if (parser->current != NULL)
song_free(parser->current);
if (parser->previous != NULL)
song_free(parser->previous);
if (parser->finished != NULL)
song_free(parser->finished);
@@ -201,10 +231,32 @@ cue_parse_position(const char *p)
return minutes * 60000 + seconds * 1000 + frames * 1000 / 75;
}
/**
* Commit the current song. It will be moved to "previous", so the
* next song may soon edit its end time (using the next song's start
* time).
*/
static void
cue_parser_commit(struct cue_parser *parser)
{
/* the caller of this library must call cue_parser_get() often
enough */
assert(parser->finished == NULL);
assert(!parser->end);
if (parser->current == NULL)
return;
parser->finished = parser->previous;
parser->previous = parser->current;
parser->current = NULL;
}
static void
cue_parser_feed2(struct cue_parser *parser, char *p)
{
assert(parser != NULL);
assert(!parser->end);
assert(p != NULL);
const char *command = cue_next_token(&p);
@@ -216,16 +268,26 @@ cue_parser_feed2(struct cue_parser *parser, char *p)
if (tag != NULL)
cue_parse_rem(p, tag);
} else if (strcmp(command, "PERFORMER") == 0) {
/* MPD knows a "performer" tag, but it is not a good
match for this CUE tag; from the Hydrogenaudio
Knowledgebase: "At top-level this will specify the
CD artist, while at track-level it specifies the
track artist." */
enum tag_type type = parser->state == TRACK
? TAG_ARTIST
: TAG_ALBUM_ARTIST;
struct tag *tag = cue_current_tag(parser);
if (tag != NULL)
cue_add_tag(tag, TAG_PERFORMER, p);
cue_add_tag(tag, type, p);
} else if (strcmp(command, "TITLE") == 0) {
if (parser->state == HEADER)
cue_add_tag(parser->tag, TAG_ALBUM, p);
else if (parser->state == TRACK)
cue_add_tag(parser->current->tag, TAG_TITLE, p);
} else if (strcmp(command, "FILE") == 0) {
cue_parser_finish(parser);
cue_parser_commit(parser);
const char *filename = cue_next_value(&p);
if (filename == NULL)
@@ -248,7 +310,7 @@ cue_parser_feed2(struct cue_parser *parser, char *p)
} else if (parser->state == IGNORE_FILE) {
return;
} else if (strcmp(command, "TRACK") == 0) {
cue_parser_finish(parser);
cue_parser_commit(parser);
const char *nr = cue_next_token(&p);
if (nr == NULL)
@@ -300,6 +362,7 @@ void
cue_parser_feed(struct cue_parser *parser, const char *line)
{
assert(parser != NULL);
assert(!parser->end);
assert(line != NULL);
char *allocated = g_strdup(line);
@@ -310,12 +373,12 @@ cue_parser_feed(struct cue_parser *parser, const char *line)
void
cue_parser_finish(struct cue_parser *parser)
{
if (parser->finished != NULL)
song_free(parser->finished);
if (parser->end)
/* has already been called, ignore */
return;
parser->finished = parser->previous;
parser->previous = parser->current;
parser->current = NULL;
cue_parser_commit(parser);
parser->end = true;
}
struct song *
@@ -323,6 +386,15 @@ cue_parser_get(struct cue_parser *parser)
{
assert(parser != NULL);
if (parser->finished == NULL && parser->end) {
/* cue_parser_finish() has been called already:
deliver all remaining (partial) results */
assert(parser->current == NULL);
parser->finished = parser->previous;
parser->previous = NULL;
}
struct song *song = parser->finished;
parser->finished = NULL;
return song;

View File

@@ -124,11 +124,10 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) {
g_debug("setting mixramp_tags");
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block))
decoder_mixramp(data->decoder, replay_gain_db,
mixramp_start, mixramp_end);
}
if (data->tag != NULL)
flac_vorbis_comments_to_tag(data->tag, NULL,

View File

@@ -33,12 +33,14 @@ ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
size_t r;
r = decoder_read(NULL, inStream, buf, sizeof(buf));
if (r >= 32 && memcmp(buf, "OggS", 4) == 0 && (
(memcmp(buf+29, "FLAC", 4) == 0
&& memcmp(buf+37, "fLaC", 4) == 0)
|| (memcmp(buf+28, "FLAC", 4) == 0)
|| (memcmp(buf+28, "fLaC", 4) == 0))) {
if (r < sizeof(buf) || memcmp(buf, "OggS", 4) != 0)
return VORBIS;
if ((memcmp(buf + 29, "FLAC", 4) == 0 &&
memcmp(buf + 37, "fLaC", 4) == 0) ||
memcmp(buf + 28, "FLAC", 4) == 0 ||
memcmp(buf + 28, "fLaC", 4) == 0)
return FLAC;
}
return VORBIS;
}

View File

@@ -234,6 +234,21 @@ time_to_ffmpeg(double t, const AVRational time_base)
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
static void
copy_interleave_frame2(uint8_t *dest, uint8_t **src,
unsigned nframes, unsigned nchannels,
unsigned sample_size)
{
for (unsigned frame = 0; frame < nframes; ++frame) {
for (unsigned channel = 0; channel < nchannels; ++channel) {
memcpy(dest, src[channel] + frame * sample_size,
sample_size);
dest += sample_size;
}
}
}
/**
* Copy PCM data from a AVFrame to an interleaved buffer.
*/
@@ -254,11 +269,10 @@ copy_interleave_frame(const AVCodecContext *codec_context,
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
codec_context->channels > 1) {
for (int i = 0, channels = codec_context->channels;
i < channels; i++) {
memcpy(buffer, frame->extended_data[i], plane_size);
buffer += plane_size;
}
copy_interleave_frame2(buffer, frame->extended_data,
frame->nb_samples,
codec_context->channels,
av_get_bytes_per_sample(codec_context->sample_fmt));
} else {
memcpy(buffer, frame->extended_data[0], data_size);
}
@@ -273,7 +287,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
AVCodecContext *codec_context,
const AVRational *time_base)
{
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE)
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts, *time_base));
@@ -352,12 +366,20 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
return cmd;
}
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1)
#define AVSampleFormat SampleFormat
#endif
G_GNUC_CONST
static enum sample_format
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
{
switch (codec_context->sample_fmt) {
switch (sample_fmt) {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S16:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S16P:
#endif
#else
case SAMPLE_FMT_S16:
#endif
@@ -365,16 +387,30 @@ ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S32:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S32P:
#endif
#else
case SAMPLE_FMT_S32:
#endif
return SAMPLE_FORMAT_S32;
default:
g_warning("Unsupported libavcodec SampleFormat value: %d",
codec_context->sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
break;
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
char buffer[64];
const char *name = av_get_sample_fmt_string(buffer, sizeof(buffer),
sample_fmt);
if (name != NULL)
g_warning("Unsupported libavcodec SampleFormat value: %s (%d)",
name, sample_fmt);
else
#endif
g_warning("Unsupported libavcodec SampleFormat value: %d",
sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
}
static AVInputFormat *
@@ -485,11 +521,16 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
return;
}
const enum sample_format sample_format =
ffmpeg_sample_format(codec_context->sample_fmt);
if (sample_format == SAMPLE_FORMAT_UNDEFINED)
return;
GError *error = NULL;
struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format,
codec_context->sample_rate,
ffmpeg_sample_format(codec_context),
sample_format,
codec_context->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,18 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* WARNING! This plugin suffers from major shortcomings in the
* libfluidsynth API, which render it practically unusable. For a
* discussion, see the post on the fluidsynth mailing list:
*
* http://www.mail-archive.com/fluid-dev@nongnu.org/msg01099.html
*
*/
#include "config.h"
#include "decoder_api.h"
#include "timer.h"
#include "audio_check.h"
#include "conf.h"
#include <glib.h>
@@ -38,6 +29,9 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "fluidsynth"
static unsigned sample_rate;
static const char *soundfont_path;
/**
* Convert a fluidsynth log level to a GLib log level.
*/
@@ -75,8 +69,21 @@ fluidsynth_mpd_log_function(int level, char *message, G_GNUC_UNUSED void *data)
}
static bool
fluidsynth_init(G_GNUC_UNUSED const struct config_param *param)
fluidsynth_init(const struct config_param *param)
{
GError *error = NULL;
sample_rate = config_get_block_unsigned(param, "sample_rate", 48000);
if (!audio_check_sample_rate(sample_rate, &error)) {
g_warning("%s\n", error->message);
g_error_free(error);
return false;
}
soundfont_path =
config_get_block_string(param, "soundfont",
"/usr/share/sounds/sf2/FluidR3_GM.sf2");
fluid_set_log_function(LAST_LOG_LEVEL,
fluidsynth_mpd_log_function, NULL);
@@ -86,36 +93,24 @@ fluidsynth_init(G_GNUC_UNUSED const struct config_param *param)
static void
fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
{
static const struct audio_format audio_format = {
.sample_rate = 48000,
.format = SAMPLE_FORMAT_S16,
.channels = 2,
};
char setting_sample_rate[] = "synth.sample-rate";
/*
char setting_verbose[] = "synth.verbose";
char setting_yes[] = "yes";
*/
const char *soundfont_path;
fluid_settings_t *settings;
fluid_synth_t *synth;
fluid_player_t *player;
char *path_dup;
int ret;
struct timer *timer;
enum decoder_command cmd;
soundfont_path =
config_get_string("soundfont",
"/usr/share/sounds/sf2/FluidR3_GM.sf2");
/* set up fluid settings */
settings = new_fluid_settings();
if (settings == NULL)
return;
fluid_settings_setnum(settings, setting_sample_rate, 48000);
fluid_settings_setnum(settings, setting_sample_rate, sample_rate);
/*
fluid_settings_setstr(settings, setting_verbose, setting_yes);
@@ -146,11 +141,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
return;
}
/* temporarily duplicate the path_fs string, because
fluidsynth wants a writable string */
path_dup = g_strdup(path_fs);
ret = fluid_player_add(player, path_dup);
g_free(path_dup);
ret = fluid_player_add(player, path_fs);
if (ret != 0) {
g_warning("fluid_player_add() failed");
delete_fluid_player(player);
@@ -170,47 +161,34 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
return;
}
/* set up a timer for synchronization; fluidsynth always
decodes in real time, which forces us to synchronize */
/* XXX is there any way to switch off real-time decoding? */
timer = timer_new(&audio_format);
timer_start(timer);
/* initialization complete - announce the audio format to the
MPD core */
struct audio_format audio_format;
audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
decoder_initialized(decoder, &audio_format, false, -1);
do {
while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) {
int16_t buffer[2048];
const unsigned max_frames = G_N_ELEMENTS(buffer) / 2;
/* synchronize with the fluid player */
timer_add(timer, sizeof(buffer));
timer_sync(timer);
/* read samples from fluidsynth and send them to the
MPD core */
ret = fluid_synth_write_s16(synth, max_frames,
buffer, 0, 2,
buffer, 1, 2);
/* XXX how do we see whether the player is done? We
can't access the private attribute
player->status */
if (ret != 0)
break;
cmd = decoder_data(decoder, NULL, buffer, sizeof(buffer),
0);
} while (cmd == DECODE_COMMAND_NONE);
if (cmd != DECODE_COMMAND_NONE)
break;
}
/* clean up */
timer_free(timer);
fluid_player_stop(player);
fluid_player_join(player);
@@ -224,10 +202,7 @@ fluidsynth_scan_file(const char *file,
G_GNUC_UNUSED const struct tag_handler *handler,
G_GNUC_UNUSED void *handler_ctx)
{
/* to be implemented */
(void)file;
return true;
return fluid_is_midifile(file);
}
static const char *const fluidsynth_suffixes[] = {

View File

@@ -365,11 +365,10 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
data->found_replay_gain = true;
}
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) {
g_debug("setting mixramp_tags");
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
decoder_mixramp(data->decoder, replay_gain_db,
mixramp_start, mixramp_end);
}
}
id3_tag_delete(id3_tag);

View File

@@ -24,6 +24,7 @@
#include "utils.h"
#include "tag_table.h"
#include "tag_handler.h"
#include "tag_ape.h"
#include <wavpack/wavpack.h>
#include <glib.h>
@@ -38,21 +39,6 @@
#define ERRORLEN 80
static const struct tag_table wavpack_tags[] = {
{ "artist", TAG_ARTIST },
{ "album", TAG_ALBUM },
{ "title", TAG_TITLE },
{ "track", TAG_TRACK },
{ "name", TAG_NAME },
{ "genre", TAG_GENRE },
{ "date", TAG_DATE },
{ "composer", TAG_COMPOSER },
{ "performer", TAG_PERFORMER },
{ "comment", TAG_COMMENT },
{ "disc", TAG_DISC },
{ NULL, TAG_NUM_OF_ITEM_TYPES }
};
/** A pointer type for format converter function. */
typedef void (*format_samples_t)(
int bytes_per_sample,
@@ -321,7 +307,17 @@ wavpack_scan_file(const char *fname,
WavpackGetNumSamples(wpc) /
WavpackGetSampleRate(wpc));
for (const struct tag_table *i = wavpack_tags; i->name != NULL; ++i)
/* the WavPack format implies APEv2 tags, which means we can
reuse the mapping from tag_ape.c */
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
const char *name = tag_item_names[i];
if (name != NULL)
wavpack_scan_tag_item(wpc, name, (enum tag_type)i,
handler, handler_ctx);
}
for (const struct tag_table *i = ape_tags; i->name != NULL; ++i)
wavpack_scan_tag_item(wpc, i->name, i->type,
handler, handler_ctx);

View File

@@ -169,7 +169,6 @@ dc_mixramp_start(struct decoder_control *dc, char *mixramp_start)
g_free(dc->mixramp_start);
dc->mixramp_start = mixramp_start;
g_debug("mixramp_start = %s", mixramp_start ? mixramp_start : "NULL");
}
void
@@ -179,7 +178,6 @@ dc_mixramp_end(struct decoder_control *dc, char *mixramp_end)
g_free(dc->mixramp_end);
dc->mixramp_end = mixramp_end;
g_debug("mixramp_end = %s", mixramp_end ? mixramp_end : "NULL");
}
void
@@ -189,5 +187,4 @@ dc_mixramp_prev_end(struct decoder_control *dc, char *mixramp_prev_end)
g_free(dc->mixramp_prev_end);
dc->mixramp_prev_end = mixramp_prev_end;
g_debug("mixramp_prev_end = %s", mixramp_prev_end ? mixramp_prev_end : "NULL");
}

View File

@@ -468,7 +468,6 @@ decoder_task(gpointer arg)
switch (dc->command) {
case DECODE_COMMAND_START:
g_debug("clearing mixramp tags");
dc_mixramp_start(dc, NULL);
dc_mixramp_prev_end(dc, dc->mixramp_end);
dc->mixramp_end = NULL; /* Don't free, it's copied above. */

View File

@@ -65,13 +65,11 @@ static bool
vorbis_encoder_configure(struct vorbis_encoder *encoder,
const struct config_param *param, GError **error)
{
const char *value;
char *endptr;
value = config_get_block_string(param, "quality", NULL);
const char *value = config_get_block_string(param, "quality", NULL);
if (value != NULL) {
/* a quality was configured (VBR) */
char *endptr;
encoder->quality = g_ascii_strtod(value, &endptr);
if (*endptr != '\0' || encoder->quality < -1.0 ||
@@ -103,8 +101,9 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
}
encoder->quality = -2.0;
encoder->bitrate = g_ascii_strtoll(value, &endptr, 10);
char *endptr;
encoder->bitrate = g_ascii_strtoll(value, &endptr, 10);
if (*endptr != '\0' || encoder->bitrate <= 0) {
g_set_error(error, vorbis_encoder_quark(), 0,
"bitrate at line %i should be a positive integer",
@@ -119,9 +118,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
static struct encoder *
vorbis_encoder_init(const struct config_param *param, GError **error)
{
struct vorbis_encoder *encoder;
encoder = g_new(struct vorbis_encoder, 1);
struct vorbis_encoder *encoder = g_new(struct vorbis_encoder, 1);
encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
/* load configuration from "param" */
@@ -211,14 +208,12 @@ vorbis_encoder_open(struct encoder *_encoder,
GError **error)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
bool ret;
audio_format->format = SAMPLE_FORMAT_S16;
encoder->audio_format = *audio_format;
ret = vorbis_encoder_reinit(encoder, error);
if (!ret)
if (!vorbis_encoder_reinit(encoder, error))
return false;
vorbis_encoder_send_header(encoder);
@@ -251,11 +246,10 @@ static void
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
{
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
ogg_packet packet;
vorbis_analysis(&encoder->vb, NULL);
vorbis_bitrate_addblock(&encoder->vb);
ogg_packet packet;
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
ogg_stream_packetin(&encoder->os, &packet);
}
@@ -344,9 +338,9 @@ vorbis_encoder_write(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
unsigned num_frames;
num_frames = length / audio_format_frame_size(&encoder->audio_format);
unsigned num_frames = length
/ audio_format_frame_size(&encoder->audio_format);
/* this is for only 16-bit audio */
@@ -364,12 +358,10 @@ static size_t
vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
ogg_page page;
int ret;
unsigned char *dest = _dest;
size_t nbytes;
ret = ogg_stream_pageout(&encoder->os, &page);
ogg_page page;
int ret = ogg_stream_pageout(&encoder->os, &page);
if (ret == 0 && encoder->flush) {
encoder->flush = false;
ret = ogg_stream_flush(&encoder->os, &page);
@@ -381,7 +373,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
assert(page.header_len > 0 || page.body_len > 0);
nbytes = (size_t)page.header_len + (size_t)page.body_len;
size_t nbytes = (size_t)page.header_len + (size_t)page.body_len;
if (nbytes > length)
/* XXX better error handling */

View File

@@ -119,6 +119,10 @@ encoder_finish(struct encoder *encoder)
* Before you free it, you must call encoder_close(). You may open
* and close (reuse) one encoder any number of times.
*
* After this function returns successfully and before the first
* encoder_write() call, you should invoke encoder_read() to obtain
* the file header.
*
* @param encoder the encoder
* @param audio_format the encoder's input audio format; the plugin
* may modify the struct to adapt it to its abilities
@@ -291,6 +295,8 @@ encoder_write(struct encoder *encoder, const void *data, size_t length,
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param encoder the encoder
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer

View File

@@ -26,7 +26,6 @@
#include "pcm_buffer.h"
#include "pcm_volume.h"
#include "audio_format.h"
#include "player_control.h"
#include <assert.h>
#include <string.h>

View File

@@ -266,7 +266,7 @@ mpd_inotify_callback(int wd, unsigned mask,
(mask & IN_ISDIR) != 0) {
/* a sub directory was changed: register those in
inotify */
const char *root = mapper_get_music_directory();
const char *root = mapper_get_music_directory_fs();
const char *path_fs;
char *allocated = NULL;
@@ -308,7 +308,7 @@ mpd_inotify_init(unsigned max_depth)
g_debug("initializing inotify");
const char *path = mapper_get_music_directory();
const char *path = mapper_get_music_directory_fs();
if (path == NULL) {
g_debug("no music directory configured");
return;

View File

@@ -22,16 +22,13 @@
#include "input_internal.h"
#include "input_plugin.h"
#include <libavutil/avutil.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_ffmpeg"
#ifndef AV_VERSION_INT
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
#endif
struct input_ffmpeg {
struct input_stream base;

View File

@@ -29,10 +29,6 @@
#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

View File

@@ -127,7 +127,7 @@ locate_tag_search(const struct song *song, enum tag_type type, const char *str)
{
bool ret = false;
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
char *uri = song_get_uri(song);
char *p = g_utf8_casefold(uri, -1);
g_free(uri);
@@ -147,7 +147,7 @@ locate_tag_search(const struct song *song, enum tag_type type, const char *str)
for (unsigned i = 0; i < song->tag->num_items && !ret; i++) {
visited_types[song->tag->items[i]->type] = true;
if (type != LOCATE_TAG_ANY_TYPE &&
if ((int)type != LOCATE_TAG_ANY_TYPE &&
song->tag->items[i]->type != type) {
continue;
}
@@ -185,7 +185,7 @@ locate_song_search(const struct song *song,
static bool
locate_tag_match(const struct song *song, enum tag_type type, const char *str)
{
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
char *uri = song_get_uri(song);
bool matches = strcmp(str, uri) == 0;
g_free(uri);
@@ -205,7 +205,7 @@ locate_tag_match(const struct song *song, enum tag_type type, const char *str)
for (unsigned i = 0; i < song->tag->num_items; i++) {
visited_types[song->tag->items[i]->type] = true;
if (type != LOCATE_TAG_ANY_TYPE &&
if ((int)type != LOCATE_TAG_ANY_TYPE &&
song->tag->items[i]->type != type) {
continue;
}

View File

@@ -55,7 +55,7 @@ static const char *log_charset;
static bool stdout_mode = true;
static int out_fd;
static const char *out_filename;
static char *out_filename;
static void redirect_logs(int fd)
{
@@ -134,14 +134,15 @@ open_log_file(void)
}
static bool
log_init_file(const char *path, unsigned line, GError **error_r)
log_init_file(unsigned line, GError **error_r)
{
out_filename = path;
assert(out_filename != NULL);
out_fd = open_log_file();
if (out_fd < 0) {
g_set_error(error_r, log_quark(), errno,
"failed to open log file \"%s\" (config line %u): %s",
path, line, g_strerror(errno));
out_filename, line, g_strerror(errno));
return false;
}
@@ -271,22 +272,33 @@ log_init(bool verbose, bool use_stdout, GError **error_r)
return true;
#endif
} else {
GError *error = NULL;
char *path = config_dup_path(CONF_LOG_FILE, &error);
if (path == NULL) {
assert(error != NULL);
g_propagate_error(error_r, error);
return false;
}
bool success = log_init_file(path, param->line,
error_r);
g_free(path);
return success;
out_filename = config_dup_path(CONF_LOG_FILE, error_r);
return out_filename != NULL &&
log_init_file(param->line, error_r);
}
}
}
static void
close_log_files(void)
{
if (stdout_mode)
return;
#ifdef HAVE_SYSLOG
if (out_filename == NULL)
closelog();
#endif
}
void
log_deinit(void)
{
close_log_files();
g_free(out_filename);
}
void setup_log_output(bool use_stdout)
{
fflush(NULL);
@@ -327,15 +339,3 @@ int cycle_log_files(void)
g_debug("Done cycling log files\n");
return 0;
}
void close_log_files(void)
{
if (stdout_mode)
return;
#ifdef HAVE_SYSLOG
if (out_filename == NULL)
closelog();
#endif
}

View File

@@ -44,10 +44,11 @@ log_early_init(bool verbose);
bool
log_init(bool verbose, bool use_stdout, GError **error_r);
void
log_deinit(void);
void setup_log_output(bool use_stdout);
int cycle_log_files(void);
void close_log_files(void);
#endif /* LOG_H */

View File

@@ -135,10 +135,8 @@ glue_mapper_init(GError **error_r)
return false;
}
#if GLIB_CHECK_VERSION(2,14,0)
if (music_dir == NULL)
music_dir = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
#endif
mapper_init(music_dir, playlist_dir);
@@ -538,6 +536,6 @@ int mpd_main(int argc, char *argv[])
WSACleanup();
#endif
close_log_files();
log_deinit();
return EXIT_SUCCESS;
}

View File

@@ -36,10 +36,24 @@
#include <errno.h>
#include <dirent.h>
static char *music_dir;
static size_t music_dir_length;
/**
* The absolute path of the music directory encoded in UTF-8.
*/
static char *music_dir_utf8;
static size_t music_dir_utf8_length;
static char *playlist_dir;
/**
* The absolute path of the music directory encoded in the filesystem
* character set.
*/
static char *music_dir_fs;
static size_t music_dir_fs_length;
/**
* The absolute path of the playlist directory encoded in the
* filesystem character set.
*/
static char *playlist_dir_fs;
/**
* Duplicate a string, chop all trailing slashes.
@@ -79,27 +93,28 @@ check_directory(const char *path)
#endif
DIR *dir = opendir(path);
if (dir == NULL && errno == EACCES)
g_warning("No permission to read directory: %s", path);
else
if (dir != NULL)
closedir(dir);
else if (errno == EACCES)
g_warning("No permission to read directory: %s", path);
}
static void
mapper_set_music_dir(const char *path)
mapper_set_music_dir(const char *path_utf8)
{
check_directory(path);
music_dir_utf8 = strdup_chop_slash(path_utf8);
music_dir_utf8_length = strlen(music_dir_utf8);
music_dir = strdup_chop_slash(path);
music_dir_length = strlen(music_dir);
music_dir_fs = utf8_to_fs_charset(music_dir_utf8);
check_directory(music_dir_fs);
music_dir_fs_length = strlen(music_dir_fs);
}
static void
mapper_set_playlist_dir(const char *path)
mapper_set_playlist_dir(const char *path_utf8)
{
check_directory(path);
playlist_dir = g_strdup(path);
playlist_dir_fs = utf8_to_fs_charset(path_utf8);
check_directory(playlist_dir_fs);
}
void mapper_init(const char *_music_dir, const char *_playlist_dir)
@@ -113,23 +128,31 @@ void mapper_init(const char *_music_dir, const char *_playlist_dir)
void mapper_finish(void)
{
g_free(music_dir);
g_free(playlist_dir);
g_free(music_dir_utf8);
g_free(music_dir_fs);
g_free(playlist_dir_fs);
}
const char *
mapper_get_music_directory(void)
mapper_get_music_directory_utf8(void)
{
return music_dir;
return music_dir_utf8;
}
const char *
mapper_get_music_directory_fs(void)
{
return music_dir_fs;
}
const char *
map_to_relative_path(const char *path_utf8)
{
return music_dir != NULL &&
memcmp(path_utf8, music_dir, music_dir_length) == 0 &&
G_IS_DIR_SEPARATOR(path_utf8[music_dir_length])
? path_utf8 + music_dir_length + 1
return music_dir_utf8 != NULL &&
memcmp(path_utf8, music_dir_utf8,
music_dir_utf8_length) == 0 &&
G_IS_DIR_SEPARATOR(path_utf8[music_dir_utf8_length])
? path_utf8 + music_dir_utf8_length + 1
: path_utf8;
}
@@ -141,14 +164,14 @@ map_uri_fs(const char *uri)
assert(uri != NULL);
assert(*uri != '/');
if (music_dir == NULL)
if (music_dir_fs == NULL)
return NULL;
uri_fs = utf8_to_fs_charset(uri);
if (uri_fs == NULL)
return NULL;
path_fs = g_build_filename(music_dir, uri_fs, NULL);
path_fs = g_build_filename(music_dir_fs, uri_fs, NULL);
g_free(uri_fs);
return path_fs;
@@ -157,10 +180,11 @@ map_uri_fs(const char *uri)
char *
map_directory_fs(const struct directory *directory)
{
assert(music_dir != NULL);
assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL);
if (directory_is_root(directory))
return g_strdup(music_dir);
return g_strdup(music_dir_fs);
return map_uri_fs(directory_get_path(directory));
}
@@ -168,9 +192,10 @@ map_directory_fs(const struct directory *directory)
char *
map_directory_child_fs(const struct directory *directory, const char *name)
{
char *name_fs, *parent_fs, *path;
assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL);
assert(music_dir != NULL);
char *name_fs, *parent_fs, *path;
/* check for invalid or unauthorized base names */
if (*name == 0 || strchr(name, '/') != NULL ||
@@ -208,11 +233,11 @@ map_song_fs(const struct song *song)
char *
map_fs_to_utf8(const char *path_fs)
{
if (music_dir != NULL &&
strncmp(path_fs, music_dir, music_dir_length) == 0 &&
G_IS_DIR_SEPARATOR(path_fs[music_dir_length]))
if (music_dir_fs != NULL &&
strncmp(path_fs, music_dir_fs, music_dir_fs_length) == 0 &&
G_IS_DIR_SEPARATOR(path_fs[music_dir_fs_length]))
/* remove musicDir prefix */
path_fs += music_dir_length + 1;
path_fs += music_dir_fs_length + 1;
else if (G_IS_DIR_SEPARATOR(path_fs[0]))
/* not within musicDir */
return NULL;
@@ -226,7 +251,7 @@ map_fs_to_utf8(const char *path_fs)
const char *
map_spl_path(void)
{
return playlist_dir;
return playlist_dir_fs;
}
char *
@@ -234,7 +259,7 @@ map_spl_utf8_to_fs(const char *name)
{
char *filename_utf8, *filename_fs, *path;
if (playlist_dir == NULL)
if (playlist_dir_fs == NULL)
return NULL;
filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL);
@@ -243,7 +268,7 @@ map_spl_utf8_to_fs(const char *name)
if (filename_fs == NULL)
return NULL;
path = g_build_filename(playlist_dir, filename_fs, NULL);
path = g_build_filename(playlist_dir_fs, filename_fs, NULL);
g_free(filename_fs);
return path;

View File

@@ -36,9 +36,20 @@ void mapper_init(const char *_music_dir, const char *_playlist_dir);
void mapper_finish(void);
/**
* Return the absolute path of the music directory encoded in UTF-8.
*/
G_GNUC_CONST
const char *
mapper_get_music_directory(void);
mapper_get_music_directory_utf8(void);
/**
* Return the absolute path of the music directory encoded in the
* filesystem character set.
*/
G_GNUC_CONST
const char *
mapper_get_music_directory_fs(void);
/**
* Returns true if a music directory was configured.
@@ -47,7 +58,7 @@ G_GNUC_CONST
static inline bool
mapper_has_music_directory(void)
{
return mapper_get_music_directory() != NULL;
return mapper_get_music_directory_utf8() != NULL;
}
/**

View File

@@ -53,6 +53,31 @@ httpd_output_quark(void)
return g_quark_from_static_string("httpd_output");
}
/**
* Check whether there is at least one client.
*
* Caller must lock the mutex.
*/
G_GNUC_PURE
static bool
httpd_output_has_clients(const struct httpd_output *httpd)
{
return httpd->clients != NULL;
}
/**
* Check whether there is at least one client.
*/
G_GNUC_PURE
static bool
httpd_output_lock_has_clients(const struct httpd_output *httpd)
{
g_mutex_lock(httpd->mutex);
bool result = httpd_output_has_clients(httpd);
g_mutex_unlock(httpd->mutex);
return result;
}
static void
httpd_listen_in_event(int fd, const struct sockaddr *address,
size_t address_length, int uid, void *ctx);
@@ -89,10 +114,6 @@ httpd_output_init(const struct config_param *param,
return NULL;
}
const char *encoder_name, *bind_to_address;
const struct encoder_plugin *encoder_plugin;
guint port;
/* read configuration */
httpd->name =
config_get_block_string(param, "name", "Set name in config");
@@ -101,10 +122,12 @@ httpd_output_init(const struct config_param *param,
httpd->website =
config_get_block_string(param, "website", "Set website in config");
port = config_get_block_unsigned(param, "port", 8000);
guint port = config_get_block_unsigned(param, "port", 8000);
encoder_name = config_get_block_string(param, "encoder", "vorbis");
encoder_plugin = encoder_plugin_get(encoder_name);
const char *encoder_name =
config_get_block_string(param, "encoder", "vorbis");
const struct encoder_plugin *encoder_plugin =
encoder_plugin_get(encoder_name);
if (encoder_plugin == NULL) {
g_set_error(error, httpd_output_quark(), 0,
"No such encoder: %s", encoder_name);
@@ -119,7 +142,7 @@ httpd_output_init(const struct config_param *param,
httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
bind_to_address =
const char *bind_to_address =
config_get_block_string(param, "bind_to_address", NULL);
bool success = bind_to_address != NULL &&
strcmp(bind_to_address, "any") != 0
@@ -250,8 +273,6 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
static struct page *
httpd_output_read_page(struct httpd_output *httpd)
{
size_t size = 0, nbytes;
if (httpd->unflushed_input >= 65536) {
/* we have fed a lot of input into the encoder, but it
didn't give anything back yet - flush now to avoid
@@ -260,9 +281,11 @@ httpd_output_read_page(struct httpd_output *httpd)
httpd->unflushed_input = 0;
}
size_t size = 0;
do {
nbytes = encoder_read(httpd->encoder, httpd->buffer + size,
sizeof(httpd->buffer) - size);
size_t nbytes = encoder_read(httpd->encoder,
httpd->buffer + size,
sizeof(httpd->buffer) - size);
if (nbytes == 0)
break;
@@ -282,10 +305,7 @@ httpd_output_encoder_open(struct httpd_output *httpd,
struct audio_format *audio_format,
GError **error)
{
bool success;
success = encoder_open(httpd->encoder, audio_format, error);
if (!success)
if (!encoder_open(httpd->encoder, audio_format, error))
return false;
/* we have to remember the encoder header, i.e. the first
@@ -319,14 +339,12 @@ httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
GError **error)
{
struct httpd_output *httpd = (struct httpd_output *)ao;
bool success;
g_mutex_lock(httpd->mutex);
/* open the encoder */
success = httpd_output_encoder_open(httpd, audio_format, error);
if (!success) {
if (!httpd_output_encoder_open(httpd, audio_format, error)) {
g_mutex_unlock(httpd->mutex);
return false;
}
@@ -397,6 +415,19 @@ httpd_output_delay(struct audio_output *ao)
{
struct httpd_output *httpd = (struct httpd_output *)ao;
if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
/* if there's no client and this output is paused,
then httpd_output_pause() will not do anything, it
will not fill the buffer and it will not update the
timer; therefore, we reset the timer here */
timer_reset(httpd->timer);
/* some arbitrary delay that is long enough to avoid
consuming too much CPU, and short enough to notice
new clients quickly enough */
return 1000;
}
return httpd->timer->started
? timer_delay(httpd->timer)
: 0;
@@ -457,10 +488,7 @@ static bool
httpd_output_encode_and_play(struct httpd_output *httpd,
const void *chunk, size_t size, GError **error)
{
bool success;
success = encoder_write(httpd->encoder, chunk, size, error);
if (!success)
if (!encoder_write(httpd->encoder, chunk, size, error))
return false;
httpd->unflushed_input += size;
@@ -472,21 +500,12 @@ httpd_output_encode_and_play(struct httpd_output *httpd,
static size_t
httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
GError **error)
GError **error_r)
{
struct httpd_output *httpd = (struct httpd_output *)ao;
bool has_clients;
g_mutex_lock(httpd->mutex);
has_clients = httpd->clients != NULL;
g_mutex_unlock(httpd->mutex);
if (has_clients) {
bool success;
success = httpd_output_encode_and_play(httpd, chunk, size,
error);
if (!success)
if (httpd_output_lock_has_clients(httpd)) {
if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
return 0;
}
@@ -502,16 +521,11 @@ httpd_output_pause(struct audio_output *ao)
{
struct httpd_output *httpd = (struct httpd_output *)ao;
g_mutex_lock(httpd->mutex);
bool has_clients = httpd->clients != NULL;
g_mutex_unlock(httpd->mutex);
if (has_clients) {
if (httpd_output_lock_has_clients(httpd)) {
static const char silence[1020];
return httpd_output_play(ao, silence, sizeof(silence),
NULL) > 0;
} else {
g_usleep(100000);
return true;
}
}
@@ -534,7 +548,6 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
if (httpd->encoder->plugin->tag != NULL) {
/* embed encoder tags */
struct page *page;
/* flush the current stream, and end it */
@@ -550,7 +563,7 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
used as the new "header" page, which is sent to all
new clients */
page = httpd_output_read_page(httpd);
struct page *page = httpd_output_read_page(httpd);
if (page != NULL) {
if (httpd->header != NULL)
page_unref(httpd->header);

View File

@@ -608,6 +608,16 @@ mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao)
mpd_jack_stop(jd);
}
static unsigned
mpd_jack_delay(struct audio_output *ao)
{
struct jack_data *jd = (struct jack_data *)ao;
return jd->base.pause && jd->pause && !jd->shutdown
? 1000
: 0;
}
static inline jack_default_audio_sample_t
sample_16_to_jack(int16_t sample)
{
@@ -727,10 +737,6 @@ mpd_jack_pause(struct audio_output *ao)
jd->pause = true;
/* due to a MPD API limitation, we have to sleep a little bit
here, to avoid hogging the CPU */
g_usleep(50000);
return true;
}
@@ -742,6 +748,7 @@ const struct audio_output_plugin jack_output_plugin = {
.enable = mpd_jack_enable,
.disable = mpd_jack_disable,
.open = mpd_jack_open,
.delay = mpd_jack_delay,
.play = mpd_jack_play,
.pause = mpd_jack_pause,
.close = mpd_jack_close,

View File

@@ -228,9 +228,13 @@ osx_render(void *vdata,
g_cond_signal(od->condition);
g_mutex_unlock(od->mutex);
if (nbytes < buffer_size)
memset((unsigned char*)buffer->mData + nbytes, 0,
buffer_size - nbytes);
buffer->mDataByteSize = nbytes;
unsigned i;
for (i = 1; i < buffer_list->mNumberBuffers; ++i) {
buffer = &buffer_list->mBuffers[i];
buffer->mDataByteSize = 0;
}
return 0;
}

View File

@@ -681,35 +681,6 @@ pulse_output_close(struct audio_output *ao)
pa_threaded_mainloop_unlock(po->mainloop);
}
/**
* Check if the stream is (already) connected, and waits for a signal
* if not. The mainloop must be locked before calling this function.
*
* @return the current stream state
*/
static pa_stream_state_t
pulse_output_check_stream(struct pulse_output *po)
{
pa_stream_state_t state = pa_stream_get_state(po->stream);
assert(po->mainloop != NULL);
switch (state) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
case PA_STREAM_UNCONNECTED:
break;
case PA_STREAM_CREATING:
pa_threaded_mainloop_wait(po->mainloop);
state = pa_stream_get_state(po->stream);
break;
}
return state;
}
/**
* Check if the stream is (already) connected, and waits if not. The
* mainloop must be locked before calling this function.
@@ -719,35 +690,25 @@ pulse_output_check_stream(struct pulse_output *po)
static bool
pulse_output_wait_stream(struct pulse_output *po, GError **error_r)
{
pa_stream_state_t state = pa_stream_get_state(po->stream);
while (true) {
switch (pa_stream_get_state(po->stream)) {
case PA_STREAM_READY:
return true;
switch (state) {
case PA_STREAM_READY:
return true;
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
case PA_STREAM_UNCONNECTED:
g_set_error(error_r, pulse_output_quark(),
pa_context_errno(po->context),
"failed to connect the stream: %s",
pa_strerror(pa_context_errno(po->context)));
return false;
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
case PA_STREAM_UNCONNECTED:
g_set_error(error_r, pulse_output_quark(), 0,
"disconnected");
return false;
case PA_STREAM_CREATING:
break;
case PA_STREAM_CREATING:
pa_threaded_mainloop_wait(po->mainloop);
break;
}
}
do {
state = pulse_output_check_stream(po);
} while (state == PA_STREAM_CREATING);
if (state != PA_STREAM_READY) {
g_set_error(error_r, pulse_output_quark(), 0,
"failed to connect the stream: %s",
pa_strerror(pa_context_errno(po->context)));
return false;
}
return true;
}
/**
@@ -801,6 +762,24 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause,
return true;
}
static unsigned
pulse_output_delay(struct audio_output *ao)
{
struct pulse_output *po = (struct pulse_output *)ao;
unsigned result = 0;
pa_threaded_mainloop_lock(po->mainloop);
if (po->base.pause && pulse_output_stream_is_paused(po) &&
pa_stream_get_state(po->stream) == PA_STREAM_READY)
/* idle while paused */
result = 1000;
pa_threaded_mainloop_unlock(po->mainloop);
return result;
}
static size_t
pulse_output_play(struct audio_output *ao, const void *chunk, size_t size,
GError **error_r)
@@ -928,13 +907,8 @@ pulse_output_pause(struct audio_output *ao)
/* cork the stream */
if (pulse_output_stream_is_paused(po)) {
/* already paused; due to a MPD API limitation, we
have to sleep a little bit here, to avoid hogging
the CPU */
g_usleep(50000);
} else if (!pulse_output_stream_pause(po, true, &error)) {
if (!pulse_output_stream_is_paused(po) &&
!pulse_output_stream_pause(po, true, &error)) {
pa_threaded_mainloop_unlock(po->mainloop);
g_warning("%s", error->message);
g_error_free(error);
@@ -971,6 +945,7 @@ const struct audio_output_plugin pulse_output_plugin = {
.enable = pulse_output_enable,
.disable = pulse_output_disable,
.open = pulse_output_open,
.delay = pulse_output_delay,
.play = pulse_output_play,
.cancel = pulse_output_cancel,
.pause = pulse_output_pause,

View File

@@ -77,13 +77,12 @@ recorder_output_init(const struct config_param *param, GError **error_r)
return NULL;
}
const char *encoder_name;
const struct encoder_plugin *encoder_plugin;
/* read configuration */
encoder_name = config_get_block_string(param, "encoder", "vorbis");
encoder_plugin = encoder_plugin_get(encoder_name);
const char *encoder_name =
config_get_block_string(param, "encoder", "vorbis");
const struct encoder_plugin *encoder_plugin =
encoder_plugin_get(encoder_name);
if (encoder_plugin == NULL) {
g_set_error(error_r, recorder_output_quark(), 0,
"No such encoder: %s", encoder_name);
@@ -121,33 +120,22 @@ recorder_output_finish(struct audio_output *ao)
g_free(recorder);
}
/**
* Writes pending data from the encoder to the output file.
*/
static bool
recorder_output_encoder_to_file(struct recorder_output *recorder,
GError **error_r)
recorder_write_to_file(struct recorder_output *recorder,
const void *_data, size_t length,
GError **error_r)
{
size_t size = 0, position, nbytes;
assert(length > 0);
assert(recorder->fd >= 0);
const int fd = recorder->fd;
/* read from the encoder */
const uint8_t *data = (const uint8_t *)_data, *end = data + length;
size = encoder_read(recorder->encoder, recorder->buffer,
sizeof(recorder->buffer));
if (size == 0)
return true;
/* write everything into the file */
position = 0;
while (true) {
nbytes = write(recorder->fd, recorder->buffer + position,
size - position);
ssize_t nbytes = write(fd, data, end - data);
if (nbytes > 0) {
position += (size_t)nbytes;
if (position >= size)
data += nbytes;
if (data == end)
return true;
} else if (nbytes == 0) {
/* shouldn't happen for files */
@@ -163,13 +151,37 @@ recorder_output_encoder_to_file(struct recorder_output *recorder,
}
}
/**
* Writes pending data from the encoder to the output file.
*/
static bool
recorder_output_encoder_to_file(struct recorder_output *recorder,
GError **error_r)
{
assert(recorder->fd >= 0);
while (true) {
/* read from the encoder */
size_t size = encoder_read(recorder->encoder, recorder->buffer,
sizeof(recorder->buffer));
if (size == 0)
return true;
/* write everything into the file */
if (!recorder_write_to_file(recorder, recorder->buffer, size,
error_r))
return false;
}
}
static bool
recorder_output_open(struct audio_output *ao,
struct audio_format *audio_format,
GError **error_r)
{
struct recorder_output *recorder = (struct recorder_output *)ao;
bool success;
/* create the output file */
@@ -185,8 +197,14 @@ recorder_output_open(struct audio_output *ao,
/* open the encoder */
success = encoder_open(recorder->encoder, audio_format, error_r);
if (!success) {
if (!encoder_open(recorder->encoder, audio_format, error_r)) {
close(recorder->fd);
unlink(recorder->path);
return false;
}
if (!recorder_output_encoder_to_file(recorder, error_r)) {
encoder_close(recorder->encoder);
close(recorder->fd);
unlink(recorder->path);
return false;

View File

@@ -36,11 +36,6 @@
#define DEFAULT_CONN_TIMEOUT 2
struct shout_buffer {
unsigned char data[32768];
size_t len;
};
struct shout_data {
struct audio_output base;
@@ -54,7 +49,7 @@ struct shout_data {
int timeout;
struct shout_buffer buf;
uint8_t buffer[32768];
};
static int shout_init_count;
@@ -114,24 +109,7 @@ static struct audio_output *
my_shout_init_driver(const struct config_param *param,
GError **error)
{
struct shout_data *sd;
char *test;
unsigned port;
char *host;
char *mount;
char *passwd;
const char *encoding;
const struct encoder_plugin *encoder_plugin;
unsigned shout_format;
unsigned protocol;
const char *user;
char *name;
const char *value;
const struct block_param *block_param;
int public;
sd = new_shout_data();
struct shout_data *sd = new_shout_data();
if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
free_shout_data(sd);
return NULL;
@@ -152,13 +130,14 @@ my_shout_init_driver(const struct config_param *param,
shout_init_count++;
const struct block_param *block_param;
check_block_param("host");
host = block_param->value;
char *host = block_param->value;
check_block_param("mount");
mount = block_param->value;
char *mount = block_param->value;
port = config_get_block_unsigned(param, "port", 0);
unsigned port = config_get_block_unsigned(param, "port", 0);
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
@@ -166,17 +145,18 @@ my_shout_init_driver(const struct config_param *param,
}
check_block_param("password");
passwd = block_param->value;
const char *passwd = block_param->value;
check_block_param("name");
name = block_param->value;
const char *name = block_param->value;
public = config_get_block_bool(param, "public", false);
bool public = config_get_block_bool(param, "public", false);
user = config_get_block_string(param, "user", "source");
const char *user = config_get_block_string(param, "user", "source");
value = config_get_block_string(param, "quality", NULL);
const char *value = config_get_block_string(param, "quality", NULL);
if (value != NULL) {
char *test;
sd->quality = strtod(value, &test);
if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
@@ -201,6 +181,7 @@ my_shout_init_driver(const struct config_param *param,
goto failure;
}
char *test;
sd->bitrate = strtol(value, &test, 10);
if (*test != '\0' || sd->bitrate <= 0) {
@@ -210,8 +191,10 @@ my_shout_init_driver(const struct config_param *param,
}
}
encoding = config_get_block_string(param, "encoding", "ogg");
encoder_plugin = shout_encoder_plugin_get(encoding);
const char *encoding = config_get_block_string(param, "encoding",
"ogg");
const struct encoder_plugin *encoder_plugin =
shout_encoder_plugin_get(encoding);
if (encoder_plugin == NULL) {
g_set_error(error, shout_output_quark(), 0,
"couldn't find shout encoder plugin \"%s\"",
@@ -223,11 +206,13 @@ my_shout_init_driver(const struct config_param *param,
if (sd->encoder == NULL)
goto failure;
unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
shout_format = SHOUT_FORMAT_MP3;
else
shout_format = SHOUT_FORMAT_OGG;
unsigned protocol;
value = config_get_block_string(param, "protocol", NULL);
if (value != NULL) {
if (0 == strcmp(value, "shoutcast") &&
@@ -355,26 +340,24 @@ handle_shout_error(struct shout_data *sd, int err, GError **error)
static bool
write_page(struct shout_data *sd, GError **error)
{
int err;
assert(sd->encoder != NULL);
sd->buf.len = encoder_read(sd->encoder,
sd->buf.data, sizeof(sd->buf.data));
if (sd->buf.len == 0)
return true;
while (true) {
size_t nbytes = encoder_read(sd->encoder,
sd->buffer, sizeof(sd->buffer));
if (nbytes == 0)
return true;
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
if (!handle_shout_error(sd, err, error))
return false;
int err = shout_send(sd->shout_conn, sd->buffer, nbytes);
if (!handle_shout_error(sd, err, error))
return false;
}
return true;
}
static void close_shout_conn(struct shout_data * sd)
{
sd->buf.len = 0;
if (sd->encoder != NULL) {
if (encoder_end(sd->encoder, NULL))
write_page(sd, NULL);
@@ -425,10 +408,7 @@ my_shout_close_device(struct audio_output *ao)
static bool
shout_connect(struct shout_data *sd, GError **error)
{
int state;
state = shout_open(sd->shout_conn);
switch (state) {
switch (shout_open(sd->shout_conn)) {
case SHOUTERR_SUCCESS:
case SHOUTERR_CONNECTED:
return true;
@@ -448,17 +428,17 @@ my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format,
GError **error)
{
struct shout_data *sd = (struct shout_data *)ao;
bool ret;
ret = shout_connect(sd, error);
if (!ret)
if (!shout_connect(sd, error))
return false;
sd->buf.len = 0;
if (!encoder_open(sd->encoder, audio_format, error)) {
shout_close(sd->shout_conn);
return false;
}
ret = encoder_open(sd->encoder, audio_format, error) &&
write_page(sd, error);
if (!ret) {
if (!write_page(sd, error)) {
encoder_close(sd->encoder);
shout_close(sd->shout_conn);
return false;
}
@@ -528,32 +508,27 @@ static void my_shout_set_tag(struct audio_output *ao,
const struct tag *tag)
{
struct shout_data *sd = (struct shout_data *)ao;
bool ret;
GError *error = NULL;
if (sd->encoder->plugin->tag != NULL) {
/* encoder plugin supports stream tags */
ret = encoder_pre_tag(sd->encoder, &error);
if (!ret) {
if (!encoder_pre_tag(sd->encoder, &error)) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
ret = write_page(sd, NULL);
if (!ret)
if (!write_page(sd, NULL))
return;
ret = encoder_tag(sd->encoder, tag, &error);
if (!ret) {
if (!encoder_tag(sd->encoder, tag, &error)) {
g_warning("%s", error->message);
g_error_free(error);
}
} else {
/* no stream tag support: fall back to icy-metadata */
char song[1024];
shout_tag_to_metadata(tag, song, sizeof(song));
shout_metadata_add(sd->shout_meta, "song", song);

View File

@@ -210,6 +210,14 @@ pc_set_pause(struct player_control *pc, bool pause_flag)
player_unlock(pc);
}
void
pc_set_border_pause(struct player_control *pc, bool border_pause)
{
player_lock(pc);
pc->border_pause = border_pause;
player_unlock(pc);
}
void
pc_get_status(struct player_control *pc, struct player_status *status)
{

View File

@@ -115,6 +115,15 @@ struct player_control {
float mixramp_db;
float mixramp_delay_seconds;
double total_play_time;
/**
* If this flag is set, then the player will be auto-paused at
* the end of the song, before the next song starts to play.
*
* This is a copy of the queue's "single" flag most of the
* time.
*/
bool border_pause;
};
struct player_control *
@@ -207,6 +216,12 @@ pc_set_pause(struct player_control *pc, bool pause_flag);
void
pc_pause(struct player_control *pc);
/**
* Set the player's #border_pause flag.
*/
void
pc_set_border_pause(struct player_control *pc, bool border_pause);
void
pc_kill(struct player_control *pc);

View File

@@ -450,6 +450,8 @@ static bool player_seek_decoder(struct player *player)
assert(pc->next_song != NULL);
const unsigned start_ms = song->start_ms;
if (decoder_current_song(dc) != song) {
/* the decoder is already decoding the "next" song -
stop it and start the previous song again */
@@ -498,7 +500,7 @@ static bool player_seek_decoder(struct player *player)
if (where < 0.0)
where = 0.0;
if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
if (!dc_seek(dc, where + start_ms / 1000.0)) {
/* decoder failure */
player_command_finished(pc);
return false;
@@ -840,6 +842,16 @@ player_song_border(struct player *player)
if (!player_wait_for_decoder(player))
return false;
struct player_control *const pc = player->pc;
player_lock(pc);
if (pc->border_pause) {
player->paused = true;
pc->state = PLAYER_STATE_PAUSE;
}
player_unlock(pc);
return true;
}
@@ -955,7 +967,10 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
player_dc_start(&player, music_pipe_new());
}
if (player_dc_at_next_song(&player) &&
if (/* no cross-fading if MPD is going to pause at the
end of the current song */
!pc->border_pause &&
player_dc_at_next_song(&player) &&
player.xfade == XFADE_UNKNOWN &&
!decoder_lock_is_starting(dc)) {
/* enable cross fading in this song? if yes,

View File

@@ -109,11 +109,6 @@ playlist_song_started(struct playlist *playlist, struct player_control *pc)
playlist->current = playlist->queued;
playlist->queued = -1;
/* Pause if we are in single mode. */
if(playlist->queue.single && !playlist->queue.repeat) {
pc_set_pause(pc, true);
}
if(playlist->queue.consume)
playlist_delete(playlist, pc,
queue_order_to_position(&playlist->queue,
@@ -239,9 +234,13 @@ playlist_sync(struct playlist *playlist, struct player_control *pc)
if (pc_next_song == NULL && playlist->queued != -1)
playlist_song_started(playlist, pc);
player_lock(pc);
pc_next_song = pc->next_song;
player_unlock(pc);
/* make sure the queued song is always set (if
possible) */
if (pc->next_song == NULL && playlist->queued < 0)
if (pc_next_song == NULL && playlist->queued < 0)
playlist_update_queued_song(playlist, pc, NULL);
}
}
@@ -306,7 +305,11 @@ playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
if (status == playlist->queue.repeat)
return;
playlist->queue.repeat = status;
struct queue *queue = &playlist->queue;
queue->repeat = status;
pc_set_border_pause(pc, queue->single && !queue->repeat);
/* if the last song is currently being played, the "next song"
might change when repeat mode is toggled */
@@ -334,7 +337,11 @@ playlist_set_single(struct playlist *playlist, struct player_control *pc,
if (status == playlist->queue.single)
return;
playlist->queue.single = status;
struct queue *queue = &playlist->queue;
queue->single = status;
pc_set_border_pause(pc, queue->single && !queue->repeat);
/* if the last song is currently being played, the "next song"
might change when single mode is toggled */

View File

@@ -58,12 +58,10 @@ lastfm_init(const struct config_param *param)
lastfm_config.user = g_uri_escape_string(user, NULL, false);
#if GLIB_CHECK_VERSION(2,16,0)
if (strlen(passwd) != 32)
lastfm_config.md5 = g_compute_checksum_for_string(G_CHECKSUM_MD5,
passwd, strlen(passwd));
else
#endif
lastfm_config.md5 = g_strdup(passwd);
return true;

View File

@@ -83,7 +83,7 @@ playlist_append_song(struct playlist *playlist, struct player_control *pc,
queued = playlist_get_queued_song(playlist);
id = queue_append(&playlist->queue, song);
id = queue_append(&playlist->queue, song, 0);
if (playlist->queue.random) {
/* shuffle the new song into the list of remaining

View File

@@ -163,6 +163,9 @@ playlist_provider_print(struct client *client, const char *uri,
song_print_info(client, song);
else
song_print_uri(client, song);
if (!song_in_database(song))
song_free(song);
}
g_free(base_uri);

View File

@@ -137,7 +137,8 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
*p = '/';
p++;
}
if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
if ((playlist_append_uri(playlist, pc, temp2,
NULL)) != PLAYLIST_RESULT_SUCCESS) {
g_warning("can't add file \"%s\"", temp2);
}
g_free(temp2);

View File

@@ -23,6 +23,7 @@
#include "mapper.h"
#include "song.h"
#include "uri.h"
#include "path.h"
#include "ls.h"
#include "tag.h"
@@ -62,14 +63,19 @@ apply_song_metadata(struct song *dest, const struct song *src)
if (path_fs == NULL)
return dest;
tmp = song_file_new(path_fs, NULL);
g_free(path_fs);
char *path_utf8 = fs_charset_to_utf8(path_fs);
if (path_utf8 != NULL)
g_free(path_fs);
else
path_utf8 = path_fs;
tmp = song_file_new(path_utf8, NULL);
g_free(path_utf8);
merge_song_metadata(tmp, dest, src);
} else {
tmp = song_file_new(dest->uri, NULL);
merge_song_metadata(tmp, dest, src);
song_free(dest);
}
if (dest->tag != NULL && dest->tag->time > 0 &&
@@ -80,20 +86,42 @@ apply_song_metadata(struct song *dest, const struct song *src)
(e.g. last track on a CUE file); fix it up here */
tmp->tag->time = dest->tag->time - src->start_ms / 1000;
if (!song_in_database(dest))
song_free(dest);
return tmp;
}
static struct song *
playlist_check_load_song(const struct song *song, const char *uri, bool secure)
{
struct song *dest;
if (uri_has_scheme(uri)) {
dest = song_remote_new(uri);
} else if (g_path_is_absolute(uri) && secure) {
dest = song_file_load(uri, NULL);
if (dest == NULL)
return NULL;
} else {
dest = db_get_song(uri);
if (dest == NULL)
/* not found in database */
return NULL;
}
return apply_song_metadata(dest, song);
}
struct song *
playlist_check_translate_song(struct song *song, const char *base_uri,
bool secure)
{
struct song *dest;
if (song_in_database(song))
/* already ok */
return song;
char *uri = song->uri;
const char *uri = song->uri;
if (uri_has_scheme(uri)) {
if (uri_supported_scheme(uri))
@@ -115,11 +143,11 @@ playlist_check_translate_song(struct song *song, const char *base_uri,
if (g_path_is_absolute(uri)) {
/* XXX fs_charset vs utf8? */
const char *prefix = mapper_get_music_directory();
const char *suffix = map_to_relative_path(uri);
assert(suffix != NULL);
if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
uri[strlen(prefix)] == '/')
uri += strlen(prefix) + 1;
if (suffix != uri)
uri = suffix;
else if (!secure) {
/* local files must be relative to the music
directory when "secure" is enabled */
@@ -130,32 +158,12 @@ playlist_check_translate_song(struct song *song, const char *base_uri,
base_uri = NULL;
}
char *allocated = NULL;
if (base_uri != NULL)
uri = g_build_filename(base_uri, uri, NULL);
else
uri = g_strdup(uri);
uri = allocated = g_build_filename(base_uri, uri, NULL);
if (uri_has_scheme(uri)) {
dest = song_remote_new(uri);
g_free(uri);
} else if (g_path_is_absolute(uri) && secure) {
dest = song_file_load(uri, NULL);
if (dest == NULL) {
song_free(song);
return NULL;
}
} else {
dest = db_get_song(uri);
g_free(uri);
if (dest == NULL) {
/* not found in database */
song_free(song);
return dest;
}
}
dest = apply_song_metadata(dest, song);
struct song *dest = playlist_check_load_song(song, uri, secure);
song_free(song);
g_free(allocated);
return dest;
}

View File

@@ -96,7 +96,7 @@ queue_modify_all(struct queue *queue)
}
unsigned
queue_append(struct queue *queue, struct song *song)
queue_append(struct queue *queue, struct song *song, uint8_t priority)
{
unsigned id = queue_generate_id(queue);
@@ -106,7 +106,7 @@ queue_append(struct queue *queue, struct song *song)
.song = song,
.id = id,
.version = queue->version,
.priority = 0,
.priority = priority,
};
queue->order[queue->length] = queue->length;

View File

@@ -280,9 +280,11 @@ queue_modify_all(struct queue *queue);
*
* If a song is not in the database (determined by
* song_in_database()), it is freed when removed from the queue.
*
* @param priority the priority of this new queue item
*/
unsigned
queue_append(struct queue *queue, struct song *song);
queue_append(struct queue *queue, struct song *song, uint8_t priority);
/**
* Swaps two songs, addressed by their position.

View File

@@ -24,9 +24,12 @@
#include "uri.h"
#include "database.h"
#include "song_save.h"
#include "text_file.h"
#include <stdlib.h>
#define PRIO_LABEL "Prio: "
static void
queue_save_database_song(FILE *fp, int idx, const struct song *song)
{
@@ -54,8 +57,13 @@ queue_save_song(FILE *fp, int idx, const struct song *song)
void
queue_save(FILE *fp, const struct queue *queue)
{
for (unsigned i = 0; i < queue_length(queue); i++)
for (unsigned i = 0; i < queue_length(queue); i++) {
uint8_t prio = queue_get_priority_at_position(queue, i);
if (prio != 0)
fprintf(fp, PRIO_LABEL "%u\n", prio);
queue_save_song(fp, i, queue_get(queue, i));
}
}
static struct song *
@@ -75,6 +83,15 @@ queue_load_song(FILE *fp, GString *buffer, const char *line,
if (queue_is_full(queue))
return;
uint8_t priority = 0;
if (g_str_has_prefix(line, PRIO_LABEL)) {
priority = strtoul(line + sizeof(PRIO_LABEL) - 1, NULL, 10);
line = read_text_line(fp, buffer);
if (line == NULL)
return;
}
if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *uri = line + sizeof(SONG_BEGIN) - 1;
if (!uri_has_scheme(uri) && !g_path_is_absolute(uri))
@@ -102,5 +119,5 @@ queue_load_song(FILE *fp, GString *buffer, const char *line,
return;
}
queue_append(queue, song);
queue_append(queue, song, priority);
}

View File

@@ -24,7 +24,7 @@
#include "tag_handler.h"
#include "ape.h"
static const struct tag_table ape_tags[] = {
const struct tag_table ape_tags[] = {
{ "album artist", TAG_ALBUM_ARTIST },
{ "year", TAG_DATE },
{ NULL, TAG_NUM_OF_ITEM_TYPES }

View File

@@ -20,10 +20,14 @@
#ifndef MPD_TAG_APE_H
#define MPD_TAG_APE_H
#include "tag_table.h"
#include <stdbool.h>
struct tag_handler;
extern const struct tag_table ape_tags[];
/**
* Scan the APE tags of a file.
*

View File

@@ -20,23 +20,14 @@
#include "config.h"
#include "timer.h"
#include "audio_format.h"
#include "clock.h"
#include <glib.h>
#include <assert.h>
#include <limits.h>
#include <sys/time.h>
#include <stddef.h>
static uint64_t now(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((uint64_t)tv.tv_sec * 1000000) + tv.tv_usec;
}
struct timer *timer_new(const struct audio_format *af)
{
struct timer *timer = g_new(struct timer, 1);
@@ -54,7 +45,7 @@ void timer_free(struct timer *timer)
void timer_start(struct timer *timer)
{
timer->time = now();
timer->time = monotonic_clock_us();
timer->started = 1;
}
@@ -74,7 +65,7 @@ void timer_add(struct timer *timer, int size)
unsigned
timer_delay(const struct timer *timer)
{
int64_t delay = (int64_t)(timer->time - now()) / 1000;
int64_t delay = (int64_t)(timer->time - monotonic_clock_us()) / 1000;
if (delay < 0)
return 0;
@@ -90,7 +81,7 @@ void timer_sync(struct timer *timer)
assert(timer->started);
sleep_duration = timer->time - now();
sleep_duration = timer->time - monotonic_clock_us();
if (sleep_duration > 0)
g_usleep(sleep_duration);
}

View File

@@ -283,12 +283,20 @@ skip_symlink(const struct directory *directory, const char *utf8_name)
return true;
char buffer[MPD_PATH_MAX];
ssize_t ret = readlink(path_fs, buffer, sizeof(buffer));
ssize_t length = readlink(path_fs, buffer, sizeof(buffer));
g_free(path_fs);
if (ret < 0)
if (length < 0)
/* don't skip if this is not a symlink */
return errno != EINVAL;
if ((size_t)length >= sizeof(buffer))
/* skip symlinks when the buffer is too small for the
link target */
return true;
/* null-terminate the buffer, because readlink() will not */
buffer[length] = 0;
if (!follow_inside_symlinks && !follow_outside_symlinks) {
/* ignore all symlinks */
return true;

View File

@@ -20,7 +20,6 @@
#include "config.h"
#include "volume.h"
#include "conf.h"
#include "player_control.h"
#include "idle.h"
#include "pcm_volume.h"
#include "output_all.h"

View File

@@ -161,7 +161,7 @@ int main(int argc, char **argv)
config_global_init();
success = config_read_file(argv[1], &error);
if (!success) {
g_printerr("%s:", error->message);
g_printerr("%s\n", error->message);
g_error_free(error);
return 1;
}

View File

@@ -99,14 +99,15 @@ int main(int argc, char **argv)
}
}
ret = encoder_open(encoder, &audio_format, &error);
if (encoder == NULL) {
if (!encoder_open(encoder, &audio_format, &error)) {
g_printerr("Failed to open encoder: %s\n",
error->message);
g_error_free(error);
return 1;
}
encoder_to_stdout(encoder);
/* do it */
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {

View File

@@ -41,7 +41,7 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
queue_init(&queue, 32);
for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
queue_append(&queue, &songs[i]);
queue_append(&queue, &songs[i], 0);
assert(queue_length(&queue) == G_N_ELEMENTS(songs));

View File

@@ -67,6 +67,8 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
success = encoder_open(encoder, &audio_format, NULL);
assert(success);
encoder_to_stdout(encoder);
/* write a block of data */
success = encoder_write(encoder, zero, sizeof(zero), NULL);

View File

@@ -39,6 +39,14 @@
fun:g_main_loop_run
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:*alloc
...
fun:g_once_init_leave
}
{
g_log
Memcheck:Leak
@@ -57,6 +65,14 @@
fun:g_slice_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:*alloc
...
fun:g_static_mutex_get_mutex_impl
}
{
g_private
Memcheck:Leak
@@ -483,3 +499,24 @@
fun:?alloc
fun:snd1_dlobj_cache_get
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:_Znwm
...
obj:*/libjack.so*
fun:call_init
fun:_dl_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
fun:*alloc
fun:_dl_allocate_tls
...
obj:*/libffado.so*
fun:call_init
fun:_dl_init
}