Compare commits

..

42 Commits

Author SHA1 Message Date
Avuton Olrich
9ad862d3a6 mpd version 0.16.1 2011-01-09 18:00:12 -08:00
Yuriy Kaminskiy
77d71c4ee6 Makefile.am: resolve modplug vs. libsndfile cflags/headers conflict
A bit of automake magic (see info automake "Per-Object Flags").
Compile-tested.
2011-01-09 18:21:27 +01:00
Max Kellermann
8c0afd8557 Merge branch 'v0.15.x' into v0.16.x
Conflicts:
	NEWS
	configure.ac
	src/directory.h
2011-01-07 23:50:23 +01:00
Max Kellermann
2a56300f7b player_thread: discard empty chunks while cross-fading
When a music_chunk to be crossfaded consists only of a tag,
cross-fading is not possible, and led to an assertion failure.  This
patch just discards those, as if cross-fading was not enabled.
2011-01-07 23:45:51 +01:00
Max Kellermann
5f06999686 output_thread: fix double lock
During the whole output thread, the audio_output object is locked, and
it is only unlocked while waiting for the GCond and while running a
plugin method.  The error handler in ao_play_chunk() attempted to lock
the object again, which was code from MPD 0.15.x which should have
been removed a long time ago.
2011-01-07 23:08:18 +01:00
Max Kellermann
4c09aeb5a1 player_thread: fix assertion failure due to early seek
Until the decoder plugin has called decoder_initialized(), the player
may not submit seek commands.  This however could occur with a slow
decoder and a CUE file with a virtual song offset.  This patch adds
another check.
2011-01-07 22:52:50 +01:00
Max Kellermann
af892e7e80 player_thread: make variables more local 2011-01-07 22:33:10 +01:00
Max Kellermann
0022fb100b encoder/lame: explicitly configure the output sample rate
When you don't explicitly set an output sample rate, liblame tries to
guess an output sample rate from the input sample rate.  You would
think that this "guessing" consists of just setting both equal, but
that is not the case.  For 44.1kHz at 96kbit/s, liblame chooses
32kHz.  This patch explicitly configures the output sample rate, to
stop the bad guessing.
2011-01-07 19:37:39 +01:00
Max Kellermann
4f2d67dfb0 output/httpd: define G_LOG_DOMAIN in httpd_client.c 2011-01-07 18:00:12 +01:00
Max Kellermann
b5645ab29f output/osx: fix up audio format first, then apply it to device
This is a MPD 0.16 regression: when playing a 24 bit file, the switch
to 16 bit was made only partially, after mBytesPerPacket and
mBytesPerFrame had already been applied.

That means mBytesPerFrame referred to 24 bit, and mBitsPerChannel
referred to 16 bits.  Of course, that cannot work.
2011-01-07 17:31:36 +01:00
Max Kellermann
3149d1abf9 configure.ac: eliminate bashism "echo -n"
Use "printf" instead.
2011-01-07 17:31:30 +01:00
Max Kellermann
59a417fc84 configure.ac: avoid GNU extension in "expr match" call 2011-01-07 17:29:19 +01:00
Max Kellermann
b75d53413d configure.ac: use AC_LANG_SOURCE
Fixes autotools warnings.
2011-01-07 17:25:52 +01:00
Max Kellermann
c44a744c0b fix version number in NEWS 2011-01-07 17:25:25 +01:00
Max Kellermann
76cddfab90 configure.ac: disable the FFADO plugin by default
It is known to crash instantly.
2010-12-22 07:31:17 +01:00
Max Kellermann
60b4f6b3eb directory: fix warning "comparison between signed and unsigned"
Cast the constant to dev_t, not to unsigned.
2010-12-21 20:21:22 +01:00
Max Kellermann
546232b1c0 zeroconf-bonjour: use g_htons() instead of htons()
Fixes the gcc warning "implicit declaration of function 'htons'".
2010-12-21 20:21:20 +01:00
Max Kellermann
42c5788de3 Modify version string to post-release version 0.15.16~git 2010-12-21 20:19:49 +01:00
Max Kellermann
fb00e7fddc add void casts to suppress "result unused" warnings (clang) 2010-12-21 08:06:02 +01:00
Alex Viskovatoff
41fdcf328c decoder/mad: work around build failure on Solaris
Rename the "version" struct, because it seems to be a reserved name on
Solaris:

 "src/decoder/mad_decoder_plugin.c", line 550: (enum) tag redeclared: version
 cc: acomp failed for src/decoder/mad_decoder_plugin.c
2010-12-21 07:57:07 +01:00
Alex Viskovatoff
144ad7992e output/solaris: add missing parameter to open_cloexec() call 2010-12-21 07:31:08 +01:00
Alex Viskovatoff
a0dd1a1b8b audio_check: fix parameter in prototype 2010-12-21 07:29:58 +01:00
Max Kellermann
c360e69162 Modify version string to post-release version 0.16.1~git 2010-12-21 07:29:31 +01:00
Avuton Olrich
da01c6ef5b mpd version 0.16 2010-12-11 04:19:49 -08:00
Max Kellermann
fcd2355f4f Merge branch 'master' of git://git.musicpd.org/avuton/mpd 2010-12-07 18:18:19 +01:00
Max Kellermann
748a8a6f42 tag_id3: support multiple values
Loop over all frames with a specific id, and import all of them - not
just the first one (index 0).
2010-12-07 18:05:44 +01:00
Anton Khirnov
cb9965bab5 command: don't error when sticker list is run on song with no stickers
this is inconsistent with other commands (e.g. find) and seems wrong --
a song with no stickers attached is a perfectly valid state and an empty
list of stickers is also perfectly valid.
2010-12-07 17:32:52 +01:00
Max Kellermann
429ed24c99 tag_ape: support multiple values
One APE tag may contain more than one value, separated by null bytes.
2010-11-24 08:59:04 +01:00
Max Kellermann
1ab46472ab decoder_thread: load APE replay gain from music files 2010-11-18 23:02:30 +01:00
Max Kellermann
f6bbe1332f replay_gain_ape: parse replay gain from APE tags
Based on the APE reader.
2010-11-18 22:26:06 +01:00
Max Kellermann
11613347be tag_ape: move code to ape.c
Generic library for scanning APE tags.  Eliminated one "goto"!
2010-11-18 21:44:24 +01:00
Max Kellermann
8f46f1520c timer: fix integer overflow in timer_delay()
Fixes a regression: for output_plugin.delay(), we added a method to
the timer class which returns the delay in milliseconds.  This fails
to detect negative values, because the unsigned integer is divided by
1000, and then casted to signed.
2010-11-18 21:29:03 +01:00
Avuton Olrich
f2893b0d0f Modify version string to post-release version 0.16~git 2010-11-08 18:57:09 -08:00
Avuton Olrich
c7265f9689 mpd version 0.16~alpha4 2010-11-08 18:57:09 -08:00
Max Kellermann
46ab8d18e2 playlist_song: calculate duration of last CUE track 2010-11-08 20:16:26 +01:00
Max Kellermann
f384f8da93 Merge release 0.15.15 from branch 'v0.15.x'
Conflicts:
	NEWS
	configure.ac
2010-11-08 18:50:22 +01:00
Max Kellermann
23cd8a74be mpd version 0.15.15 2010-11-08 18:48:28 +01:00
Max Kellermann
cc1debc948 output/shout: artist comes first in stream title
After popular demand, I've switched the order of "artist" and "title"
in the stream title.  There is no standard, and there is no reliable
way to parse those from the stream title.
2010-11-08 18:46:14 +01:00
Max Kellermann
5a3aa1262a update_walk: explicitly check for permission problems
Call access() and print an extra error message when EACCES is
returned.  Hopefully this will reduce the number of support requests
due to wrong file permissions.
2010-11-08 18:24:19 +01:00
Max Kellermann
ad52eb236d input/rewind: fix assertion failure
The assertion added in MPD 0.15.14 was too much, it failed when the
MIME type of a stream was NULL.
2010-11-08 10:37:09 +01:00
Avuton Olrich
d2c2cbd0ae Modify version string to post-release version 0.16~git 2010-11-07 06:39:31 -08:00
Avuton Olrich
462bba8e2f Modify version string to post-release version 0.15.15~git 2010-11-06 14:42:03 -07:00
29 changed files with 537 additions and 220 deletions

@@ -7,6 +7,8 @@ AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"'
bin_PROGRAMS = src/mpd
noinst_LIBRARIES =
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
$(LIBWRAP_CFLAGS) \
@@ -34,6 +36,7 @@ mpd_headers = \
src/check.h \
src/notify.h \
src/ack.h \
src/ape.h \
src/audio.h \
src/audio_format.h \
src/audio_check.h \
@@ -186,6 +189,7 @@ mpd_headers = \
src/refcount.h \
src/replay_gain_config.h \
src/replay_gain_info.h \
src/replay_gain_ape.h \
src/sig_handlers.h \
src/song.h \
src/song_print.h \
@@ -412,6 +416,8 @@ TAG_LIBS = \
$(ID3TAG_LIBS)
TAG_SRC = \
src/ape.c \
src/replay_gain_ape.c \
src/tag_ape.c
if HAVE_ID3TAG
@@ -428,7 +434,6 @@ DECODER_CFLAGS = \
$(SNDFILE_CFLAGS) \
$(AUDIOFILE_CFLAGS) \
$(LIBMIKMOD_CFLAGS) \
$(MODPLUG_CFLAGS) \
$(GME_CFLAGS) \
$(SIDPLAY_CFLAGS) \
$(FLUIDSYNTH_CFLAGS) \
@@ -444,7 +449,6 @@ DECODER_LIBS = \
$(FLAC_LIBS) \
$(SNDFILE_LIBS) \
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
$(MODPLUG_LIBS) \
$(GME_LIBS) \
$(SIDPLAY_LIBS) \
$(FLUIDSYNTH_LIBS) \
@@ -517,7 +521,11 @@ DECODER_SRC += src/decoder/mikmod_decoder_plugin.c
endif
if HAVE_MODPLUG
DECODER_SRC += src/decoder/modplug_decoder_plugin.c
libmodplug_decoder_plugin_a_SOURCES = src/decoder/modplug_decoder_plugin.c
libmodplug_decoder_plugin_a_CFLAGS = $(src_mpd_CFLAGS) $(MODPLUG_CFLAGS)
libmodplug_decoder_plugin_a_CPPFLAGS = $(src_mpd_CPPFLAGS)
noinst_LIBRARIES += libmodplug_decoder_plugin.a
DECODER_LIBS += libmodplug_decoder_plugin.a $(MODPLUG_LIBS)
endif
if ENABLE_SIDPLAY

33
NEWS

@@ -1,4 +1,20 @@
ver 0.16 (20??/??/??)
ver 0.16.1 (2010/01/09)
* audio_check: fix parameter in prototype
* add void casts to suppress "result unused" warnings (clang)
* input:
- ffado: disable by default
* decoder:
- mad: work around build failure on Solaris
- resolve modplug vs. libsndfile cflags/headers conflict
* output:
- solaris: add missing parameter to open_cloexec() cal
- osx: fix up audio format first, then apply it to device
* player_thread: discard empty chunks while cross-fading
* player_thread: fix assertion failure due to early seek
* output_thread: fix double lock
ver 0.16 (2010/12/11)
* protocol:
- send song modification time to client
- added "update" idle event
@@ -20,7 +36,9 @@ ver 0.16 (20??/??/??)
* tags:
- added tags "ArtistSort", "AlbumArtistSort"
- id3: revised "performer" tag support
- id3: support multiple values
- ape: MusicBrainz tags
- ape: support multiple values
* decoders:
- don't try a plugin twice (MIME type & suffix)
- don't fall back to "mad" unless no plugin matches
@@ -88,6 +106,7 @@ ver 0.16 (20??/??/??)
- fall back to track gain if album gain is unavailable
- optionally use hardware mixer to apply replay gain
- added mode "auto"
- parse replay gain from APE tags
* log unused/unknown block parameters
* removed the deprecated "error_file" option
* save state when stopped
@@ -109,6 +128,18 @@ ver 0.16 (20??/??/??)
* make single mode 'sticky'
ver 0.15.16 (2010/??/??)
* encoders:
- lame: explicitly configure the output sample rate
ver 0.15.15 (2010/11/08)
* input:
- rewind: fix assertion failure
* output:
- shout: artist comes first in stream title
ver 0.15.14 (2010/11/06)
* player_thread: fix assertion failure due to wrong music pipe on seek
* output_thread: fix assertion failure due to race condition in OPEN

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.16~alpha3, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.16.1, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
AM_CONFIG_HEADER(config.h)
@@ -13,6 +13,7 @@ dnl Programs
dnl ---------------------------------------------------------------------------
AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_RANLIB
HAVE_CXX=yes
if test x$CXX = xg++; then
@@ -157,7 +158,7 @@ AC_ARG_ENABLE(documentation,
AC_ARG_ENABLE(ffado,
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
[enable_ffado=auto])
[enable_ffado=no])
AC_ARG_ENABLE(ffmpeg,
AS_HELP_STRING([--enable-ffmpeg],
@@ -849,15 +850,6 @@ if test x$enable_modplug = xyes; then
fi
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
dnl --------------------------- sndfile/modplug test --------------------------
if test x$enable_sndfile = xauto && test x$enable_modplug = xyes; then
dnl If modplug is enabled, enable sndfile only if explicitly
dnl requested - modplug's modplug/sndfile.h is known to
dnl conflict with libsndfile's sndfile.h.
AC_MSG_NOTICE([disabling libsndfile auto-detection, because the modplug decoder is enabled])
enable_sndfile=no
fi
dnl -------------------------------- libsndfile -------------------------------
dnl See above test, which may disable this.
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
@@ -1495,23 +1487,23 @@ dnl ---------------------------------------------------------------------------
echo ''
echo '########### MPD CONFIGURATION ############'
echo -ne '\nArchive support:\n\t'
printf '\nArchive support:\n\t'
results(bzip2,[bzip2])
results(iso9660,[ISO9660])
results(zzip,[ZIP])
if test x$with_zeroconf != xno; then
echo -ne '\nAutodiscovery support:\n\t'
printf '\nAutodiscovery support:\n\t'
results(avahi, [Avahi])
results(bonjour, [Bonjour])
fi
echo -ne '\nClient support:\n\t'
printf '\nClient support:\n\t'
results(ipv6, "IPv6")
results(tcp, "TCP")
results(un,[UNIX Domain Sockets])
echo -ne '\nFile format support:\n\t'
printf '\nFile format support:\n\t'
results(aac, [AAC])
results(sidplay, [C64 SID])
results(ffmpeg, [FFMPEG])
@@ -1519,7 +1511,7 @@ results(flac, [FLAC])
results(fluidsynth, [FluidSynth])
results(gme, [GME])
results(sndfile, [libsndfile])
echo -ne '\n\t'
printf '\n\t'
results(mikmod, [MikMod])
results(modplug, [MODPLUG])
results(mad, [MAD])
@@ -1527,23 +1519,23 @@ results(mpg123, [MPG123])
results(mp4, [MP4])
results(mpc, [Musepack])
results(oggflac, [OggFLAC], flac)
echo -ne '\n\t'
printf '\n\t'
results(tremor, [OggTremor])
results(vorbis, [OggVorbis])
results(audiofile, [WAVE])
results(wavpack, [WavPack])
results(wildmidi, [WildMidi])
echo -en '\nOther features:\n\t'
printf '\nOther features:\n\t'
results(lsr, [libsamplerate])
results(inotify, [inotify])
results(sqlite, [SQLite])
echo -en '\nMetadata support:\n\t'
printf '\nMetadata support:\n\t'
results(cue,[cue])
results(id3,[ID3])
echo -en '\nPlayback support:\n\t'
printf '\nPlayback support:\n\t'
results(alsa,ALSA)
results(ffado,FFADO)
results(fifo,FIFO)
@@ -1552,14 +1544,14 @@ results(httpd_output,[HTTP Daemon])
results(jack,[JACK])
results(ao,[libao])
results(oss,[OSS])
echo -ne '\n\t'
printf '\n\t'
results(openal,[OpenAL])
results(osx, [OS X])
results(pipe_output, [Pipeline])
results(pulse, [PulseAudio])
results(mvp, [Media MVP])
results(shout, [SHOUTcast])
echo -ne '\n\t'
printf '\n\t'
results(solaris, [Solaris])
results(winmm_output, [WinMM])
@@ -1567,7 +1559,7 @@ if
test x$enable_shout = xyes ||
test x$enable_recorder = xyes ||
test x$enable_httpd_output = xyes; then
echo -en '\nStreaming encoder support:\n\t'
printf '\nStreaming encoder support:\n\t'
results(flac_encoder, [FLAC])
results(lame_encoder, [LAME])
results(vorbis_encoder, [Ogg Vorbis])
@@ -1575,19 +1567,14 @@ if
results(wave_encoder, [WAVE])
fi
echo -en '\nStreaming support:\n\t'
printf '\nStreaming support:\n\t'
results(curl,[CURL])
results(lastfm,[Last.FM])
results(mms,[MMS])
echo -ne '\n\n##########################################\n\n'
printf '\n\n##########################################\n\n'
if test x$enable_sndfile = xyes && test x$enable_modplug = xyes; then
AC_MSG_WARN([compilation may fail, because libmodplug conflicts with libsndfile])
AC_MSG_WARN([libmodplug ships modplug/sndfile.h, which hides libsndfile's sndfile.h])
fi
echo -ne 'Generating files needed for compilation\n'
echo 'Generating files needed for compilation'
dnl ---------------------------------------------------------------------------
dnl Generate files

@@ -58,7 +58,7 @@ if test x$enable_aac = xyes; then
fi
if test x$enable_aac = xyes; then
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
int main() {
@@ -82,9 +82,9 @@ int main() {
return 0;
}
],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
AC_MSG_CHECKING(that FAAD2 can even be used)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
int main() {
@@ -113,7 +113,7 @@ int main() {
return 0;
}
],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
])
fi
if test x$enable_aac = xyes; then
@@ -136,7 +136,7 @@ if test x$enable_aac = xyes; then
CPPFLAGS=$CFLAGS
AC_MSG_CHECKING(for broken libfaad headers)
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
#include <stddef.h>
#include <stdint.h>
@@ -148,7 +148,7 @@ int main() {
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
return 0;
}
],
])],
[AC_MSG_RESULT(correct)],
[AC_MSG_RESULT(broken);
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])

@@ -4,9 +4,9 @@ AC_DEFUN([MPD_CHECK_FLAG],[
[mpd_check_cflag_$var],[
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_COMPILE_IFELSE([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main(void) { return 0; }
], [ eval "mpd_check_cflag_$var=yes"
])], [ eval "mpd_check_cflag_$var=yes"
], [ eval "mpd_check_cflag_$var=no" ])
CFLAGS="$save_CFLAGS"
])

@@ -1,19 +1,19 @@
AC_DEFUN([results], [
dnl This is a hack to allow "with" names, otherwise "enable".
num=`expr match $1 'with'`
num=`expr $1 : 'with'`
if test "$num" != "0"; then
var="`echo '$'$1`"
else
var="`echo '$'enable_$1`"
fi
echo -n '('
printf '('
if eval "test x$var = xyes"; then
echo -n '+'
printf '+'
elif test -n "$3" && eval "test x$var = x$3"; then
echo -n '+'
printf '+'
else
echo -n '-'
printf '-'
fi
echo -n "$2) "
printf '%s) ' "$2"
])

113
src/ape.c Normal file

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "ape.h"
#include <glib.h>
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
struct ape_footer {
unsigned char id[8];
uint32_t version;
uint32_t length;
uint32_t count;
unsigned char flags[4];
unsigned char reserved[8];
};
static bool
ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
{
/* determine if file has an apeV2 tag */
struct ape_footer footer;
if (fseek(fp, -(long)sizeof(footer), SEEK_END) ||
fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) ||
memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
GUINT32_FROM_LE(footer.version) != 2000)
return false;
/* find beginning of ape tag */
size_t remaining = GUINT32_FROM_LE(footer.length);
if (remaining <= sizeof(footer) + 10 ||
/* refuse to load more than one megabyte of tag data */
remaining > 1024 * 1024 ||
fseek(fp, -(long)remaining, SEEK_END))
return false;
/* read tag into buffer */
remaining -= sizeof(footer);
assert(remaining > 10);
char *buffer = g_malloc(remaining);
if (fread(buffer, 1, remaining, fp) != remaining)
return false;
/* read tags */
unsigned n = GUINT32_FROM_LE(footer.count);
const char *p = buffer;
while (n-- && remaining > 10) {
size_t size = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
remaining -= 4;
unsigned long flags = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
remaining -= 4;
/* get the key */
const char *key = p;
while (remaining > size && *p != '\0') {
p++;
remaining--;
}
p++;
remaining--;
/* get the value */
if (remaining < size)
break;
if (!callback(flags, key, p, size, ctx))
break;
p += size;
remaining -= size;
}
g_free(buffer);
return true;
}
bool
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx)
{
FILE *fp;
fp = fopen(path_fs, "rb");
if (fp == NULL)
return false;
bool success = ape_scan_internal(fp, callback, ctx);
fclose(fp);
return success;
}

42
src/ape.h Normal file

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_APE_H
#define MPD_APE_H
#include "check.h"
#include <stdbool.h>
#include <stddef.h>
typedef bool (*tag_ape_callback_t)(unsigned long flags, const char *key,
const char *value, size_t value_length,
void *ctx);
/**
* Scans the APE tag values from a file.
*
* @param path_fs the path of the file in filesystem encoding
* @return false if the file could not be opened or if no APE tag is
* present
*/
bool
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx);
#endif

@@ -38,7 +38,7 @@ bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
bool
audio_check_sample_format(unsigned sample_format, GError **error_r);
audio_check_sample_format(enum sample_format, GError **error_r);
bool
audio_check_channel_count(unsigned sample_format, GError **error_r);

@@ -1715,15 +1715,11 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
}
sticker = sticker_song_get(song);
if (NULL == sticker) {
command_error(client, ACK_ERROR_NO_EXIST,
"no stickers found");
return COMMAND_RETURN_ERROR;
if (sticker) {
sticker_print(client, sticker);
sticker_free(sticker);
}
sticker_print(client, sticker);
sticker_free(sticker);
return COMMAND_RETURN_OK;
/* set song song_id id key */
} else if (argc == 6 && strcmp(argv[1], "set") == 0) {

@@ -547,14 +547,14 @@ enum {
XING_SCALE = 0x00000008L
};
struct version {
struct lame_version {
unsigned major;
unsigned minor;
};
struct lame {
char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */
struct version version; /* struct containing just the version */
struct lame_version version; /* struct containing just the version */
float peak; /* replaygain peak */
float track_gain; /* replaygain track gain */
float album_gain; /* replaygain album gain */

@@ -24,6 +24,7 @@
#include "decoder_list.h"
#include "decoder_plugin.h"
#include "decoder_api.h"
#include "replay_gain_ape.h"
#include "input_stream.h"
#include "player_control.h"
#include "pipe.h"
@@ -297,6 +298,18 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
return success;
}
/**
* Attempt to load replay gain data, and pass it to
* decoder_replay_gain().
*/
static void
decoder_load_replay_gain(struct decoder *decoder, const char *path_fs)
{
struct replay_gain_info info;
if (replay_gain_ape_read(path_fs, &info))
decoder_replay_gain(decoder, &info);
}
/**
* Try decoding a file.
*/
@@ -312,6 +325,8 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
decoder_unlock(dc);
decoder_load_replay_gain(decoder, path_fs);
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
if (plugin->file_decode != NULL) {
decoder_lock(dc);

@@ -30,8 +30,8 @@
#define DIRECTORY_DIR "directory: "
#define DEVICE_INARCHIVE (dev_t)(-1)
#define DEVICE_CONTAINER (dev_t)(-2)
#define DEVICE_INARCHIVE (dev_t)(-1)
#define DEVICE_CONTAINER (dev_t)(-2)
struct directory {
struct dirvec children;

@@ -170,6 +170,13 @@ lame_encoder_setup(struct lame_encoder *encoder, GError **error)
return false;
}
if (0 != lame_set_out_samplerate(encoder->gfp,
encoder->audio_format.sample_rate)) {
g_set_error(error, lame_encoder_quark(), 0,
"error setting lame out sample rate");
return false;
}
if (0 > lame_init_params(encoder->gfp)) {
g_set_error(error, lame_encoder_quark(), 0,
"error initializing lame params");

@@ -81,7 +81,7 @@ copy_attributes(struct input_rewind *r)
const struct input_stream *src = r->input;
assert(dest != src);
assert(dest->mime != src->mime);
assert(src->mime == NULL || dest->mime != src->mime);
dest->ready = src->ready;
dest->seekable = src->seekable;

@@ -29,6 +29,9 @@
#include <assert.h>
#include <string.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "httpd_output"
struct httpd_client {
/**
* The httpd output object this client is connected to.

@@ -214,15 +214,6 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
stream_description.mSampleRate = audio_format->sample_rate;
stream_description.mFormatID = kAudioFormatLinearPCM;
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
#if G_BYTE_ORDER == G_BIG_ENDIAN
stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
#endif
stream_description.mBytesPerPacket =
audio_format_frame_size(audio_format);
stream_description.mFramesPerPacket = 1;
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
stream_description.mChannelsPerFrame = audio_format->channels;
switch (audio_format->format) {
case SAMPLE_FORMAT_S8:
@@ -239,6 +230,16 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
break;
}
#if G_BYTE_ORDER == G_BIG_ENDIAN
stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
#endif
stream_description.mBytesPerPacket =
audio_format_frame_size(audio_format);
stream_description.mFramesPerPacket = 1;
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
stream_description.mChannelsPerFrame = audio_format->channels;
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0,
&stream_description,

@@ -494,7 +494,7 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
}
}
snprintf(dest, size, "%s - %s", title, artist);
snprintf(dest, size, "%s - %s", artist, title);
}
static void my_shout_set_tag(void *data,

@@ -93,7 +93,7 @@ solaris_output_open(void *data, struct audio_format *audio_format,
/* open the device in non-blocking mode */
so->fd = open_cloexec(so->device, O_WRONLY|O_NONBLOCK);
so->fd = open_cloexec(so->device, O_WRONLY|O_NONBLOCK, 0);
if (so->fd < 0) {
g_set_error(error, solaris_output_quark(), errno,
"Failed to open %s: %s",

@@ -303,7 +303,7 @@ ao_wait(struct audio_output *ao)
GTimeVal tv;
g_get_current_time(&tv);
g_time_val_add(&tv, delay * 1000);
g_cond_timed_wait(ao->cond, ao->mutex, &tv);
(void)g_cond_timed_wait(ao->cond, ao->mutex, &tv);
if (ao->command != AO_COMMAND_NONE)
return false;
@@ -463,12 +463,9 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
/* don't automatically reopen this device for
10 seconds */
g_mutex_lock(ao->mutex);
assert(ao->fail_timer == NULL);
ao->fail_timer = g_timer_new();
g_mutex_unlock(ao->mutex);
return false;
}

@@ -356,16 +356,9 @@ player_check_decoder_startup(struct player *player)
static bool
player_send_silence(struct player *player)
{
struct music_chunk *chunk;
size_t frame_size =
audio_format_frame_size(&player->play_audio_format);
/* this formula ensures that we don't send
partial frames */
unsigned num_frames = sizeof(chunk->data) / frame_size;
assert(audio_format_defined(&player->play_audio_format));
chunk = music_buffer_allocate(player_buffer);
struct music_chunk *chunk = music_buffer_allocate(player_buffer);
if (chunk == NULL) {
g_warning("Failed to allocate silence buffer");
return false;
@@ -375,6 +368,12 @@ player_send_silence(struct player *player)
chunk->audio_format = player->play_audio_format;
#endif
size_t frame_size =
audio_format_frame_size(&player->play_audio_format);
/* this formula ensures that we don't send
partial frames */
unsigned num_frames = sizeof(chunk->data) / frame_size;
chunk->times = -1.0; /* undefined time stamp */
chunk->length = num_frames * frame_size;
memset(chunk->data, 0, chunk->length);
@@ -396,8 +395,6 @@ static bool player_seek_decoder(struct player *player)
{
struct song *song = pc.next_song;
struct decoder_control *dc = player->dc;
double where;
bool ret;
assert(pc.next_song != NULL);
@@ -413,8 +410,7 @@ static bool player_seek_decoder(struct player *player)
/* re-start the decoder */
player_dc_start(player, player->pipe);
ret = player_wait_for_decoder(player);
if (!ret) {
if (!player_wait_for_decoder(player)) {
/* decoder failure */
player_command_finished();
return false;
@@ -435,8 +431,7 @@ static bool player_seek_decoder(struct player *player)
/* wait for the decoder to complete initialization */
while (player->decoder_starting) {
ret = player_check_decoder_startup(player);
if (!ret) {
if (!player_check_decoder_startup(player)) {
/* decoder failure */
player_command_finished();
return false;
@@ -445,14 +440,13 @@ static bool player_seek_decoder(struct player *player)
/* send the SEEK command */
where = pc.seek_where;
double where = pc.seek_where;
if (where > pc.total_time)
where = pc.total_time - 0.1;
if (where < 0.0)
where = 0.0;
ret = dc_seek(dc, where + song->start_ms / 1000.0);
if (!ret) {
if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
/* decoder failure */
player_command_finished();
return false;
@@ -583,14 +577,12 @@ static void player_process_command(struct player *player)
static void
update_song_tag(struct song *song, const struct tag *new_tag)
{
struct tag *old_tag;
if (song_is_file(song))
/* don't update tags of local files, only remote
streams may change tags dynamically */
return;
old_tag = song->tag;
struct tag *old_tag = song->tag;
song->tag = tag_dup(new_tag);
if (old_tag != NULL)
@@ -648,15 +640,14 @@ static bool
play_next_chunk(struct player *player)
{
struct decoder_control *dc = player->dc;
struct music_chunk *chunk = NULL;
unsigned cross_fade_position;
bool success;
if (!audio_output_all_wait(64))
/* the output pipe is still large enough, don't send
another chunk */
return true;
unsigned cross_fade_position;
struct music_chunk *chunk = NULL;
if (player->xfade == XFADE_ENABLED &&
player_dc_at_next_song(player) &&
(cross_fade_position = music_pipe_size(player->pipe))
@@ -694,6 +685,19 @@ play_next_chunk(struct player *player)
chunk->mix_ratio = nan("");
}
if (music_chunk_is_empty(other_chunk)) {
/* the "other" chunk was a music_chunk
which had only a tag, but no music
data - we cannot cross-fade that;
but since this happens only at the
beginning of the new song, we can
easily recover by throwing it away
now */
music_buffer_return(player_buffer,
other_chunk);
other_chunk = NULL;
}
chunk->other = other_chunk;
} else {
/* there are not enough decoded chunks yet */
@@ -732,9 +736,7 @@ play_next_chunk(struct player *player)
/* play the current chunk */
success = play_chunk(player->song, chunk, &player->play_audio_format);
if (!success) {
if (!play_chunk(player->song, chunk, &player->play_audio_format)) {
music_buffer_return(player_buffer, chunk);
player_lock();
@@ -776,11 +778,9 @@ play_next_chunk(struct player *player)
static bool
player_song_border(struct player *player)
{
char *uri;
player->xfade = XFADE_UNKNOWN;
uri = song_get_uri(player->song);
char *uri = song_get_uri(player->song);
g_message("played \"%s\"", uri);
g_free(uri);
@@ -875,16 +875,17 @@ static void do_play(struct decoder_control *dc)
if (player.decoder_starting) {
/* wait until the decoder is initialized completely */
bool success;
const struct song *song;
success = player_check_decoder_startup(&player);
if (!success)
if (!player_check_decoder_startup(&player))
break;
/* seek to the beginning of the range */
song = decoder_current_song(dc);
const struct song *song = decoder_current_song(dc);
if (song != NULL && song->start_ms > 0 &&
/* we must not send a seek command until
the decoder is initialized
completely */
!player.decoder_starting &&
!dc_seek(dc, song->start_ms / 1000.0))
player_dc_stop(&player);
@@ -1092,10 +1093,9 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
void player_create(void)
{
GError *e = NULL;
assert(pc.thread == NULL);
GError *e = NULL;
pc.thread = g_thread_create(player_task, NULL, true, &e);
if (pc.thread == NULL)
MPD_ERROR("Failed to spawn player task: %s", e->message);

@@ -72,6 +72,14 @@ apply_song_metadata(struct song *dest, const struct song *src)
song_free(dest);
}
if (dest->tag != NULL && dest->tag->time > 0 &&
src->start_ms > 0 && src->end_ms == 0 &&
src->start_ms / 1000 < (unsigned)dest->tag->time)
/* the range is open-ended, and the playlist plugin
did not know the total length of the song file
(e.g. last track on a CUE file); fix it up here */
tmp->tag->time = dest->tag->time - src->start_ms / 1000;
return tmp;
}

@@ -47,7 +47,7 @@ poison_noaccess(void *p, size_t length)
memset(p, 0x01, length);
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_NOACCESS(p, length);
(void)VALGRIND_MAKE_MEM_NOACCESS(p, length);
#endif
#endif
}
@@ -68,7 +68,7 @@ poison_undefined(void *p, size_t length)
memset(p, 0x02, length);
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_UNDEFINED(p, length);
(void)VALGRIND_MAKE_MEM_UNDEFINED(p, length);
#endif
#endif
}

78
src/replay_gain_ape.c Normal file

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "replay_gain_ape.h"
#include "replay_gain_info.h"
#include "ape.h"
#include <glib.h>
#include <string.h>
#include <stdlib.h>
struct rg_ape_ctx {
struct replay_gain_info *info;
bool found;
};
static bool
replay_gain_ape_callback(unsigned long flags, const char *key,
const char *_value, size_t value_length, void *_ctx)
{
struct rg_ape_ctx *ctx = _ctx;
/* we only care about utf-8 text tags */
if ((flags & (0x3 << 1)) != 0)
return true;
char value[16];
if (value_length >= sizeof(value))
return true;
memcpy(value, _value, value_length);
value[value_length] = 0;
if (g_ascii_strcasecmp(key, "replaygain_track_gain") == 0) {
ctx->info->tuples[REPLAY_GAIN_TRACK].gain = atof(value);
ctx->found = true;
} else if (g_ascii_strcasecmp(key, "replaygain_album_gain") == 0) {
ctx->info->tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
ctx->found = true;
} else if (g_ascii_strcasecmp(key, "replaygain_track_peak") == 0) {
ctx->info->tuples[REPLAY_GAIN_TRACK].peak = atof(value);
ctx->found = true;
} else if (g_ascii_strcasecmp(key, "replaygain_album_peak") == 0) {
ctx->info->tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
ctx->found = true;
}
return true;
}
bool
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info)
{
struct rg_ape_ctx ctx = {
.info = info,
.found = false,
};
return tag_ape_scan(path_fs, replay_gain_ape_callback, &ctx) &&
ctx.found;
}

32
src/replay_gain_ape.h Normal file

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_REPLAY_GAIN_APE_H
#define MPD_REPLAY_GAIN_APE_H
#include "check.h"
#include <stdbool.h>
struct replay_gain_info;
bool
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info);
#endif

@@ -21,11 +21,7 @@
#include "tag_ape.h"
#include "tag.h"
#include "tag_table.h"
#include <glib.h>
#include <assert.h>
#include <stdio.h>
#include "ape.h"
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_ALBUM_ARTIST] = "album artist",
@@ -56,101 +52,45 @@ tag_ape_import_item(struct tag *tag, unsigned long flags,
if (tag == NULL)
tag = tag_new();
tag_add_item_n(tag, type, value, value_length);
const char *end = value + value_length;
while (true) {
/* multiple values are separated by null bytes */
const char *n = memchr(value, 0, end - value);
if (n != NULL) {
if (n > value)
tag_add_item_n(tag, type, value, n - value);
value = n + 1;
} else {
if (end > value)
tag_add_item_n(tag, type, value, end - value);
break;
}
}
return tag;
}
struct tag_ape_ctx {
struct tag *tag;
};
static bool
tag_ape_callback(unsigned long flags, const char *key,
const char *value, size_t value_length, void *_ctx)
{
struct tag_ape_ctx *ctx = _ctx;
ctx->tag = tag_ape_import_item(ctx->tag, flags, key,
value, value_length);
return true;
}
struct tag *
tag_ape_load(const char *file)
{
struct tag *ret = NULL;
FILE *fp;
int tagCount;
char *buffer = NULL;
char *p;
size_t tagLen;
size_t size;
unsigned long flags;
char *key;
struct tag_ape_ctx ctx = { .tag = NULL };
struct {
unsigned char id[8];
uint32_t version;
uint32_t length;
uint32_t tagCount;
unsigned char flags[4];
unsigned char reserved[8];
} footer;
fp = fopen(file, "rb");
if (!fp)
return NULL;
/* determine if file has an apeV2 tag */
if (fseek(fp, 0, SEEK_END))
goto fail;
size = (size_t)ftell(fp);
if (fseek(fp, size - sizeof(footer), SEEK_SET))
goto fail;
if (fread(&footer, 1, sizeof(footer), fp) != sizeof(footer))
goto fail;
if (memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0)
goto fail;
if (GUINT32_FROM_LE(footer.version) != 2000)
goto fail;
/* find beginning of ape tag */
tagLen = GUINT32_FROM_LE(footer.length);
if (tagLen <= sizeof(footer) + 10)
goto fail;
if (tagLen > 1024 * 1024)
/* refuse to load more than one megabyte of tag data */
goto fail;
if (fseek(fp, size - tagLen, SEEK_SET))
goto fail;
/* read tag into buffer */
tagLen -= sizeof(footer);
assert(tagLen > 10);
buffer = g_malloc(tagLen);
if (fread(buffer, 1, tagLen, fp) != tagLen)
goto fail;
/* read tags */
tagCount = GUINT32_FROM_LE(footer.tagCount);
p = buffer;
while (tagCount-- && tagLen > 10) {
size = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
tagLen -= 4;
flags = GUINT32_FROM_LE(*(const uint32_t *)p);
p += 4;
tagLen -= 4;
/* get the key */
key = p;
while (tagLen > size && *p != '\0') {
p++;
tagLen--;
}
p++;
tagLen--;
/* get the value */
if (tagLen < size)
goto fail;
ret = tag_ape_import_item(ret, flags, key, p, size);
p += size;
tagLen -= size;
}
fail:
if (fp)
fclose(fp);
g_free(buffer);
return ret;
tag_ape_scan(file, tag_ape_callback, &ctx);
return ctx.tag;
}

@@ -126,17 +126,16 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
* - string list
*/
static void
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
enum tag_type type)
tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
const struct id3_frame *frame,
enum tag_type type)
{
struct id3_frame const *frame;
id3_ucs4_t const *ucs4;
id3_utf8_t *utf8;
union id3_field const *field;
unsigned int nstrings, i;
frame = id3_tag_findframe(tag, id, 0);
if (frame == NULL || frame->nfields != 2)
if (frame->nfields != 2)
return;
/* check the encoding field */
@@ -170,6 +169,20 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
}
}
/**
* Import all text frames with the specified id (ID3v2.4.0 section
* 4.2). This is a wrapper for tag_id3_import_text_frame().
*/
static void
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
enum tag_type type)
{
const struct id3_frame *frame;
for (unsigned i = 0;
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
tag_id3_import_text_frame(dest, tag, frame, type);
}
/**
* Import a "Comment frame" (ID3v2.4.0 section 4.10). It
* contains 4 fields:
@@ -180,16 +193,15 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
* - full string (we use this one)
*/
static void
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
enum tag_type type)
tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
const struct id3_frame *frame,
enum tag_type type)
{
struct id3_frame const *frame;
id3_ucs4_t const *ucs4;
id3_utf8_t *utf8;
union id3_field const *field;
frame = id3_tag_findframe(tag, id, 0);
if (frame == NULL || frame->nfields != 4)
if (frame->nfields != 4)
return;
/* for now I only read the 4th field, with the fullstring */
@@ -209,6 +221,20 @@ tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
g_free(utf8);
}
/**
* Import all comment frames (ID3v2.4.0 section 4.10). This is a
* wrapper for tag_id3_import_comment_frame().
*/
static void
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
enum tag_type type)
{
const struct id3_frame *frame;
for (unsigned i = 0;
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
tag_id3_import_comment_frame(dest, tag, frame, type);
}
/**
* Parse a TXXX name, and convert it to a tag_type enum value.
* Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood.

@@ -74,7 +74,7 @@ void timer_add(Timer *timer, int size)
unsigned
timer_delay(const Timer *timer)
{
int64_t delay = (timer->time - now()) / 1000;
int64_t delay = (int64_t)(timer->time - now()) / 1000;
if (delay < 0)
return 0;

@@ -546,6 +546,31 @@ update_container_file( struct directory* directory,
return true;
}
/**
* Checks if the given permissions on the mapped file are given.
*/
static bool
directory_child_access(const struct directory *directory,
const char *name, int mode)
{
#ifdef WIN32
/* access() is useless on WIN32 */
(void)directory;
(void)name;
return true;
#else
char *path = map_directory_child_fs(directory, name);
if (path == NULL)
/* something went wrong, but that isn't a permission
problem */
return true;
bool success = access(path, mode) == 0 || errno != EACCES;
g_free(path);
return success;
#endif
}
static void
update_regular_file(struct directory *directory,
const char *name, const struct stat *st)
@@ -562,6 +587,14 @@ update_regular_file(struct directory *directory,
{
struct song* song = songvec_find(&directory->songs, name);
if (!directory_child_access(directory, name, R_OK)) {
g_warning("no read permissions on %s/%s",
directory_get_path(directory), name);
if (song != NULL)
delete_song(directory, song);
return;
}
if (!(song != NULL && st->st_mtime == song->mtime &&
!walk_discard) &&
plugin->container_scan != NULL)