Compare commits
75 Commits
v0.16_alph
...
v0.16.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e2950a7e4d | ||
![]() |
4b4aa64261 | ||
![]() |
26735390ff | ||
![]() |
9402b23dd5 | ||
![]() |
246db3d565 | ||
![]() |
eaf414cbc8 | ||
![]() |
327d41c00f | ||
![]() |
05d8ce3bcd | ||
![]() |
def2fe8805 | ||
![]() |
f680b0a431 | ||
![]() |
d4b00ff11c | ||
![]() |
532f94a187 | ||
![]() |
87ad2f8542 | ||
![]() |
a8f891efcd | ||
![]() |
b5fc2419e8 | ||
![]() |
fe588a255b | ||
![]() |
1fc571088b | ||
![]() |
8d83914f05 | ||
![]() |
0fdcd381bc | ||
![]() |
4f293ecd6f | ||
![]() |
b6303313f0 | ||
![]() |
a28449a123 | ||
![]() |
6dcec36621 | ||
![]() |
84d0fd39a3 | ||
![]() |
4d4b7e3de0 | ||
![]() |
e2aea6bce5 | ||
![]() |
5779146a7f | ||
![]() |
a1d1c2beaa | ||
![]() |
ee9c60fad4 | ||
![]() |
1674a4ec82 | ||
![]() |
ce370bee60 | ||
![]() |
e257484870 | ||
![]() |
2a1f4539f6 | ||
![]() |
906efdd320 | ||
![]() |
948b8f35e6 | ||
![]() |
e776c605ad | ||
![]() |
8b2f4fc823 | ||
![]() |
03018611f8 | ||
![]() |
8f99c954ad | ||
![]() |
5735c9efc1 | ||
![]() |
e6c3acaa6f | ||
![]() |
44b4b50949 | ||
![]() |
9ad862d3a6 | ||
![]() |
77d71c4ee6 | ||
![]() |
8c0afd8557 | ||
![]() |
2a56300f7b | ||
![]() |
5f06999686 | ||
![]() |
4c09aeb5a1 | ||
![]() |
af892e7e80 | ||
![]() |
0022fb100b | ||
![]() |
4f2d67dfb0 | ||
![]() |
b5645ab29f | ||
![]() |
3149d1abf9 | ||
![]() |
59a417fc84 | ||
![]() |
b75d53413d | ||
![]() |
c44a744c0b | ||
![]() |
76cddfab90 | ||
![]() |
60b4f6b3eb | ||
![]() |
546232b1c0 | ||
![]() |
42c5788de3 | ||
![]() |
fb00e7fddc | ||
![]() |
41fdcf328c | ||
![]() |
144ad7992e | ||
![]() |
a0dd1a1b8b | ||
![]() |
c360e69162 | ||
![]() |
da01c6ef5b | ||
![]() |
fcd2355f4f | ||
![]() |
748a8a6f42 | ||
![]() |
cb9965bab5 | ||
![]() |
429ed24c99 | ||
![]() |
1ab46472ab | ||
![]() |
f6bbe1332f | ||
![]() |
11613347be | ||
![]() |
8f46f1520c | ||
![]() |
f2893b0d0f |
23
Makefile.am
23
Makefile.am
@@ -7,6 +7,8 @@ AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"'
|
|||||||
|
|
||||||
bin_PROGRAMS = src/mpd
|
bin_PROGRAMS = src/mpd
|
||||||
|
|
||||||
|
noinst_LIBRARIES =
|
||||||
|
|
||||||
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
|
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
|
||||||
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||||
$(LIBWRAP_CFLAGS) \
|
$(LIBWRAP_CFLAGS) \
|
||||||
@@ -34,6 +36,7 @@ mpd_headers = \
|
|||||||
src/check.h \
|
src/check.h \
|
||||||
src/notify.h \
|
src/notify.h \
|
||||||
src/ack.h \
|
src/ack.h \
|
||||||
|
src/ape.h \
|
||||||
src/audio.h \
|
src/audio.h \
|
||||||
src/audio_format.h \
|
src/audio_format.h \
|
||||||
src/audio_check.h \
|
src/audio_check.h \
|
||||||
@@ -186,6 +189,7 @@ mpd_headers = \
|
|||||||
src/refcount.h \
|
src/refcount.h \
|
||||||
src/replay_gain_config.h \
|
src/replay_gain_config.h \
|
||||||
src/replay_gain_info.h \
|
src/replay_gain_info.h \
|
||||||
|
src/replay_gain_ape.h \
|
||||||
src/sig_handlers.h \
|
src/sig_handlers.h \
|
||||||
src/song.h \
|
src/song.h \
|
||||||
src/song_print.h \
|
src/song_print.h \
|
||||||
@@ -412,6 +416,8 @@ TAG_LIBS = \
|
|||||||
$(ID3TAG_LIBS)
|
$(ID3TAG_LIBS)
|
||||||
|
|
||||||
TAG_SRC = \
|
TAG_SRC = \
|
||||||
|
src/ape.c \
|
||||||
|
src/replay_gain_ape.c \
|
||||||
src/tag_ape.c
|
src/tag_ape.c
|
||||||
|
|
||||||
if HAVE_ID3TAG
|
if HAVE_ID3TAG
|
||||||
@@ -428,7 +434,6 @@ DECODER_CFLAGS = \
|
|||||||
$(SNDFILE_CFLAGS) \
|
$(SNDFILE_CFLAGS) \
|
||||||
$(AUDIOFILE_CFLAGS) \
|
$(AUDIOFILE_CFLAGS) \
|
||||||
$(LIBMIKMOD_CFLAGS) \
|
$(LIBMIKMOD_CFLAGS) \
|
||||||
$(MODPLUG_CFLAGS) \
|
|
||||||
$(GME_CFLAGS) \
|
$(GME_CFLAGS) \
|
||||||
$(SIDPLAY_CFLAGS) \
|
$(SIDPLAY_CFLAGS) \
|
||||||
$(FLUIDSYNTH_CFLAGS) \
|
$(FLUIDSYNTH_CFLAGS) \
|
||||||
@@ -444,7 +449,6 @@ DECODER_LIBS = \
|
|||||||
$(FLAC_LIBS) \
|
$(FLAC_LIBS) \
|
||||||
$(SNDFILE_LIBS) \
|
$(SNDFILE_LIBS) \
|
||||||
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
|
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
|
||||||
$(MODPLUG_LIBS) \
|
|
||||||
$(GME_LIBS) \
|
$(GME_LIBS) \
|
||||||
$(SIDPLAY_LIBS) \
|
$(SIDPLAY_LIBS) \
|
||||||
$(FLUIDSYNTH_LIBS) \
|
$(FLUIDSYNTH_LIBS) \
|
||||||
@@ -517,7 +521,11 @@ DECODER_SRC += src/decoder/mikmod_decoder_plugin.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_MODPLUG
|
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
|
endif
|
||||||
|
|
||||||
if ENABLE_SIDPLAY
|
if ENABLE_SIDPLAY
|
||||||
@@ -851,6 +859,7 @@ test_run_input_LDADD = $(MPD_LIBS) \
|
|||||||
$(INPUT_LIBS) \
|
$(INPUT_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_input_SOURCES = test/run_input.c \
|
test_run_input_SOURCES = test/run_input.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c \
|
src/conf.c src/tokenizer.c src/utils.c \
|
||||||
src/tag.c src/tag_pool.c src/tag_save.c \
|
src/tag.c src/tag_pool.c src/tag_save.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
@@ -898,6 +907,7 @@ test_run_decoder_LDADD = $(MPD_LIBS) \
|
|||||||
$(INPUT_LIBS) $(DECODER_LIBS) \
|
$(INPUT_LIBS) $(DECODER_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_decoder_SOURCES = test/run_decoder.c \
|
test_run_decoder_SOURCES = test/run_decoder.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
src/replay_gain_info.c \
|
src/replay_gain_info.c \
|
||||||
@@ -938,6 +948,7 @@ test_run_filter_LDADD = $(MPD_LIBS) \
|
|||||||
$(SAMPLERATE_LIBS) \
|
$(SAMPLERATE_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_filter_SOURCES = test/run_filter.c \
|
test_run_filter_SOURCES = test/run_filter.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/filter_plugin.c \
|
src/filter_plugin.c \
|
||||||
src/filter_registry.c \
|
src/filter_registry.c \
|
||||||
src/conf.c src/tokenizer.c src/utils.c \
|
src/conf.c src/tokenizer.c src/utils.c \
|
||||||
@@ -960,6 +971,7 @@ endif
|
|||||||
if ENABLE_ENCODER
|
if ENABLE_ENCODER
|
||||||
noinst_PROGRAMS += test/run_encoder
|
noinst_PROGRAMS += test/run_encoder
|
||||||
test_run_encoder_SOURCES = test/run_encoder.c \
|
test_run_encoder_SOURCES = test/run_encoder.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c \
|
src/conf.c src/tokenizer.c \
|
||||||
src/utils.c \
|
src/utils.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
@@ -967,12 +979,15 @@ test_run_encoder_SOURCES = test/run_encoder.c \
|
|||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
$(ENCODER_SRC)
|
$(ENCODER_SRC)
|
||||||
|
test_run_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||||
|
$(ENCODER_CFLAGS)
|
||||||
test_run_encoder_LDADD = $(MPD_LIBS) \
|
test_run_encoder_LDADD = $(MPD_LIBS) \
|
||||||
$(ENCODER_LIBS) \
|
$(ENCODER_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
test_software_volume_SOURCES = test/software_volume.c \
|
test_software_volume_SOURCES = test/software_volume.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/pcm_volume.c
|
src/pcm_volume.c
|
||||||
@@ -980,6 +995,7 @@ test_software_volume_LDADD = \
|
|||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
|
|
||||||
test_run_normalize_SOURCES = test/run_normalize.c \
|
test_run_normalize_SOURCES = test/run_normalize.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/AudioCompress/compress.c
|
src/AudioCompress/compress.c
|
||||||
@@ -1017,6 +1033,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
|
|||||||
$(OUTPUT_LIBS) \
|
$(OUTPUT_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_output_SOURCES = test/run_output.c \
|
test_run_output_SOURCES = test/run_output.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
|
61
NEWS
61
NEWS
@@ -1,4 +1,44 @@
|
|||||||
ver 0.16 (20??/??/??)
|
ver 0.16.3 (2011/06/04)
|
||||||
|
* fix assertion failure in audio format mask parser
|
||||||
|
* fix NULL pointer dereference in playlist parser
|
||||||
|
* fix playlist files in base music directory
|
||||||
|
* database: allow directories with just playlists
|
||||||
|
* decoder:
|
||||||
|
- ffmpeg: support libavcodec 0.7
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.2 (2011/03/18)
|
||||||
|
* configure.ac:
|
||||||
|
- fix bashism in tremor test
|
||||||
|
* decoder:
|
||||||
|
- tremor: fix configure test
|
||||||
|
- gme: detect end of song
|
||||||
|
* encoder:
|
||||||
|
- vorbis: reset the Ogg stream after flush
|
||||||
|
* output:
|
||||||
|
- httpd: fix uninitialized variable
|
||||||
|
- httpd: include sys/socket.h
|
||||||
|
- oss: AFMT_S24_PACKED is little-endian
|
||||||
|
- oss: disable 24 bit playback on FreeBSD
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.1 (2011/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:
|
* protocol:
|
||||||
- send song modification time to client
|
- send song modification time to client
|
||||||
- added "update" idle event
|
- added "update" idle event
|
||||||
@@ -20,7 +60,9 @@ ver 0.16 (20??/??/??)
|
|||||||
* tags:
|
* tags:
|
||||||
- added tags "ArtistSort", "AlbumArtistSort"
|
- added tags "ArtistSort", "AlbumArtistSort"
|
||||||
- id3: revised "performer" tag support
|
- id3: revised "performer" tag support
|
||||||
|
- id3: support multiple values
|
||||||
- ape: MusicBrainz tags
|
- ape: MusicBrainz tags
|
||||||
|
- ape: support multiple values
|
||||||
* decoders:
|
* decoders:
|
||||||
- don't try a plugin twice (MIME type & suffix)
|
- don't try a plugin twice (MIME type & suffix)
|
||||||
- don't fall back to "mad" unless no plugin matches
|
- don't fall back to "mad" unless no plugin matches
|
||||||
@@ -88,6 +130,7 @@ ver 0.16 (20??/??/??)
|
|||||||
- fall back to track gain if album gain is unavailable
|
- fall back to track gain if album gain is unavailable
|
||||||
- optionally use hardware mixer to apply replay gain
|
- optionally use hardware mixer to apply replay gain
|
||||||
- added mode "auto"
|
- added mode "auto"
|
||||||
|
- parse replay gain from APE tags
|
||||||
* log unused/unknown block parameters
|
* log unused/unknown block parameters
|
||||||
* removed the deprecated "error_file" option
|
* removed the deprecated "error_file" option
|
||||||
* save state when stopped
|
* save state when stopped
|
||||||
@@ -109,6 +152,22 @@ ver 0.16 (20??/??/??)
|
|||||||
* make single mode 'sticky'
|
* make single mode 'sticky'
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.17 (2011/??/??)
|
||||||
|
* encoder:
|
||||||
|
- vorbis: reset the Ogg stream after flush
|
||||||
|
* decoders:
|
||||||
|
- vorbis: fix tremor support
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.16 (2011/03/13)
|
||||||
|
* output:
|
||||||
|
- ao: initialize the ao_sample_format struct
|
||||||
|
- jack: fix crash with mono playback
|
||||||
|
* encoders:
|
||||||
|
- lame: explicitly configure the output sample rate
|
||||||
|
* update: log all file permission problems
|
||||||
|
|
||||||
|
|
||||||
ver 0.15.15 (2010/11/08)
|
ver 0.15.15 (2010/11/08)
|
||||||
* input:
|
* input:
|
||||||
- rewind: fix assertion failure
|
- rewind: fix assertion failure
|
||||||
|
63
configure.ac
63
configure.ac
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
AC_INIT(mpd, 0.16~alpha4, musicpd-dev-team@lists.sourceforge.net)
|
AC_INIT(mpd, 0.16.3, musicpd-dev-team@lists.sourceforge.net)
|
||||||
AC_CONFIG_SRCDIR([src/main.c])
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
||||||
AM_CONFIG_HEADER(config.h)
|
AM_CONFIG_HEADER(config.h)
|
||||||
@@ -13,6 +13,7 @@ dnl Programs
|
|||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
AC_PROG_CC_C99
|
AC_PROG_CC_C99
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
|
||||||
HAVE_CXX=yes
|
HAVE_CXX=yes
|
||||||
if test x$CXX = xg++; then
|
if test x$CXX = xg++; then
|
||||||
@@ -157,7 +158,7 @@ AC_ARG_ENABLE(documentation,
|
|||||||
|
|
||||||
AC_ARG_ENABLE(ffado,
|
AC_ARG_ENABLE(ffado,
|
||||||
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
|
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
|
||||||
[enable_ffado=auto])
|
[enable_ffado=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE(ffmpeg,
|
AC_ARG_ENABLE(ffmpeg,
|
||||||
AS_HELP_STRING([--enable-ffmpeg],
|
AS_HELP_STRING([--enable-ffmpeg],
|
||||||
@@ -634,7 +635,7 @@ fi
|
|||||||
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
||||||
|
|
||||||
dnl ---------------------------------- libogg ---------------------------------
|
dnl ---------------------------------- libogg ---------------------------------
|
||||||
if test x$with_tremor == xno || test -z $with_tremor; then
|
if test x$with_tremor = xno || test -z $with_tremor; then
|
||||||
PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no)
|
PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -849,15 +850,6 @@ if test x$enable_modplug = xyes; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
|
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 -------------------------------- libsndfile -------------------------------
|
||||||
dnl See above test, which may disable this.
|
dnl See above test, which may disable this.
|
||||||
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
|
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
|
||||||
@@ -942,13 +934,19 @@ if test x$enable_tremor = xyes; then
|
|||||||
ac_save_LIBS="$LIBS"
|
ac_save_LIBS="$LIBS"
|
||||||
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
||||||
LIBS="$LIBS $TREMOR_LIBS"
|
LIBS="$LIBS $TREMOR_LIBS"
|
||||||
AC_CHECK_LIB(vorbisidec,ov_read,enable_vorbis=yes,enable_vorbis=no;
|
AC_CHECK_LIB(vorbisidec,ov_read,,enable_tremor=no;
|
||||||
AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
||||||
CFLAGS="$ac_save_CFLAGS"
|
CFLAGS="$ac_save_CFLAGS"
|
||||||
LIBS="$ac_save_LIBS"
|
LIBS="$ac_save_LIBS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test x$enable_tremor = xyes; then
|
||||||
AC_DEFINE(HAVE_TREMOR,1,
|
AC_DEFINE(HAVE_TREMOR,1,
|
||||||
[Define to use tremor (libvorbisidec) for ogg support])
|
[Define to use tremor (libvorbisidec) for ogg support])
|
||||||
|
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]),
|
||||||
|
else
|
||||||
|
TREMOR_CFLAGS=
|
||||||
|
TREMOR_LIBS=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_SUBST(TREMOR_CFLAGS)
|
AC_SUBST(TREMOR_CFLAGS)
|
||||||
@@ -988,7 +986,7 @@ if test x$enable_vorbis = xyes; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes)
|
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
|
||||||
|
|
||||||
dnl --------------------------------- sidplay ---------------------------------
|
dnl --------------------------------- sidplay ---------------------------------
|
||||||
found_sidplay=$HAVE_CXX
|
found_sidplay=$HAVE_CXX
|
||||||
@@ -1083,7 +1081,7 @@ if
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
||||||
test x$enable_vorbis = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
|
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
|
||||||
|
|
||||||
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
||||||
test x$enable_flac = xyes || test x$enable_oggflac = xyes)
|
test x$enable_flac = xyes || test x$enable_oggflac = xyes)
|
||||||
@@ -1495,23 +1493,23 @@ dnl ---------------------------------------------------------------------------
|
|||||||
echo ''
|
echo ''
|
||||||
echo '########### MPD CONFIGURATION ############'
|
echo '########### MPD CONFIGURATION ############'
|
||||||
|
|
||||||
echo -ne '\nArchive support:\n\t'
|
printf '\nArchive support:\n\t'
|
||||||
results(bzip2,[bzip2])
|
results(bzip2,[bzip2])
|
||||||
results(iso9660,[ISO9660])
|
results(iso9660,[ISO9660])
|
||||||
results(zzip,[ZIP])
|
results(zzip,[ZIP])
|
||||||
|
|
||||||
if test x$with_zeroconf != xno; then
|
if test x$with_zeroconf != xno; then
|
||||||
echo -ne '\nAutodiscovery support:\n\t'
|
printf '\nAutodiscovery support:\n\t'
|
||||||
results(avahi, [Avahi])
|
results(avahi, [Avahi])
|
||||||
results(bonjour, [Bonjour])
|
results(bonjour, [Bonjour])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -ne '\nClient support:\n\t'
|
printf '\nClient support:\n\t'
|
||||||
results(ipv6, "IPv6")
|
results(ipv6, "IPv6")
|
||||||
results(tcp, "TCP")
|
results(tcp, "TCP")
|
||||||
results(un,[UNIX Domain Sockets])
|
results(un,[UNIX Domain Sockets])
|
||||||
|
|
||||||
echo -ne '\nFile format support:\n\t'
|
printf '\nFile format support:\n\t'
|
||||||
results(aac, [AAC])
|
results(aac, [AAC])
|
||||||
results(sidplay, [C64 SID])
|
results(sidplay, [C64 SID])
|
||||||
results(ffmpeg, [FFMPEG])
|
results(ffmpeg, [FFMPEG])
|
||||||
@@ -1519,7 +1517,7 @@ results(flac, [FLAC])
|
|||||||
results(fluidsynth, [FluidSynth])
|
results(fluidsynth, [FluidSynth])
|
||||||
results(gme, [GME])
|
results(gme, [GME])
|
||||||
results(sndfile, [libsndfile])
|
results(sndfile, [libsndfile])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(mikmod, [MikMod])
|
results(mikmod, [MikMod])
|
||||||
results(modplug, [MODPLUG])
|
results(modplug, [MODPLUG])
|
||||||
results(mad, [MAD])
|
results(mad, [MAD])
|
||||||
@@ -1527,23 +1525,23 @@ results(mpg123, [MPG123])
|
|||||||
results(mp4, [MP4])
|
results(mp4, [MP4])
|
||||||
results(mpc, [Musepack])
|
results(mpc, [Musepack])
|
||||||
results(oggflac, [OggFLAC], flac)
|
results(oggflac, [OggFLAC], flac)
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(tremor, [OggTremor])
|
results(tremor, [OggTremor])
|
||||||
results(vorbis, [OggVorbis])
|
results(vorbis, [OggVorbis])
|
||||||
results(audiofile, [WAVE])
|
results(audiofile, [WAVE])
|
||||||
results(wavpack, [WavPack])
|
results(wavpack, [WavPack])
|
||||||
results(wildmidi, [WildMidi])
|
results(wildmidi, [WildMidi])
|
||||||
|
|
||||||
echo -en '\nOther features:\n\t'
|
printf '\nOther features:\n\t'
|
||||||
results(lsr, [libsamplerate])
|
results(lsr, [libsamplerate])
|
||||||
results(inotify, [inotify])
|
results(inotify, [inotify])
|
||||||
results(sqlite, [SQLite])
|
results(sqlite, [SQLite])
|
||||||
|
|
||||||
echo -en '\nMetadata support:\n\t'
|
printf '\nMetadata support:\n\t'
|
||||||
results(cue,[cue])
|
results(cue,[cue])
|
||||||
results(id3,[ID3])
|
results(id3,[ID3])
|
||||||
|
|
||||||
echo -en '\nPlayback support:\n\t'
|
printf '\nPlayback support:\n\t'
|
||||||
results(alsa,ALSA)
|
results(alsa,ALSA)
|
||||||
results(ffado,FFADO)
|
results(ffado,FFADO)
|
||||||
results(fifo,FIFO)
|
results(fifo,FIFO)
|
||||||
@@ -1552,14 +1550,14 @@ results(httpd_output,[HTTP Daemon])
|
|||||||
results(jack,[JACK])
|
results(jack,[JACK])
|
||||||
results(ao,[libao])
|
results(ao,[libao])
|
||||||
results(oss,[OSS])
|
results(oss,[OSS])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(openal,[OpenAL])
|
results(openal,[OpenAL])
|
||||||
results(osx, [OS X])
|
results(osx, [OS X])
|
||||||
results(pipe_output, [Pipeline])
|
results(pipe_output, [Pipeline])
|
||||||
results(pulse, [PulseAudio])
|
results(pulse, [PulseAudio])
|
||||||
results(mvp, [Media MVP])
|
results(mvp, [Media MVP])
|
||||||
results(shout, [SHOUTcast])
|
results(shout, [SHOUTcast])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(solaris, [Solaris])
|
results(solaris, [Solaris])
|
||||||
results(winmm_output, [WinMM])
|
results(winmm_output, [WinMM])
|
||||||
|
|
||||||
@@ -1567,7 +1565,7 @@ if
|
|||||||
test x$enable_shout = xyes ||
|
test x$enable_shout = xyes ||
|
||||||
test x$enable_recorder = xyes ||
|
test x$enable_recorder = xyes ||
|
||||||
test x$enable_httpd_output = xyes; then
|
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(flac_encoder, [FLAC])
|
||||||
results(lame_encoder, [LAME])
|
results(lame_encoder, [LAME])
|
||||||
results(vorbis_encoder, [Ogg Vorbis])
|
results(vorbis_encoder, [Ogg Vorbis])
|
||||||
@@ -1575,19 +1573,14 @@ if
|
|||||||
results(wave_encoder, [WAVE])
|
results(wave_encoder, [WAVE])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -en '\nStreaming support:\n\t'
|
printf '\nStreaming support:\n\t'
|
||||||
results(curl,[CURL])
|
results(curl,[CURL])
|
||||||
results(lastfm,[Last.FM])
|
results(lastfm,[Last.FM])
|
||||||
results(mms,[MMS])
|
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
|
echo 'Generating files needed for compilation'
|
||||||
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'
|
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
dnl Generate files
|
dnl Generate files
|
||||||
|
12
m4/faad.m4
12
m4/faad.m4
@@ -58,7 +58,7 @@ if test x$enable_aac = xyes; then
|
|||||||
fi
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -82,9 +82,9 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
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_MSG_CHECKING(that FAAD2 can even be used)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -113,7 +113,7 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
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
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
@@ -136,7 +136,7 @@ if test x$enable_aac = xyes; then
|
|||||||
CPPFLAGS=$CFLAGS
|
CPPFLAGS=$CFLAGS
|
||||||
|
|
||||||
AC_MSG_CHECKING(for broken libfaad headers)
|
AC_MSG_CHECKING(for broken libfaad headers)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -148,7 +148,7 @@ int main() {
|
|||||||
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],
|
])],
|
||||||
[AC_MSG_RESULT(correct)],
|
[AC_MSG_RESULT(correct)],
|
||||||
[AC_MSG_RESULT(broken);
|
[AC_MSG_RESULT(broken);
|
||||||
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
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],[
|
[mpd_check_cflag_$var],[
|
||||||
save_CFLAGS="$CFLAGS"
|
save_CFLAGS="$CFLAGS"
|
||||||
CFLAGS="$CFLAGS $1"
|
CFLAGS="$CFLAGS $1"
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
int main(void) { return 0; }
|
int main(void) { return 0; }
|
||||||
], [ eval "mpd_check_cflag_$var=yes"
|
])], [ eval "mpd_check_cflag_$var=yes"
|
||||||
], [ eval "mpd_check_cflag_$var=no" ])
|
], [ eval "mpd_check_cflag_$var=no" ])
|
||||||
CFLAGS="$save_CFLAGS"
|
CFLAGS="$save_CFLAGS"
|
||||||
])
|
])
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
AC_DEFUN([results], [
|
AC_DEFUN([results], [
|
||||||
dnl This is a hack to allow "with" names, otherwise "enable".
|
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
|
if test "$num" != "0"; then
|
||||||
var="`echo '$'$1`"
|
var="`echo '$'$1`"
|
||||||
else
|
else
|
||||||
var="`echo '$'enable_$1`"
|
var="`echo '$'enable_$1`"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -n '('
|
printf '('
|
||||||
if eval "test x$var = xyes"; then
|
if eval "test x$var = xyes"; then
|
||||||
echo -n '+'
|
printf '+'
|
||||||
elif test -n "$3" && eval "test x$var = x$3"; then
|
elif test -n "$3" && eval "test x$var = x$3"; then
|
||||||
echo -n '+'
|
printf '+'
|
||||||
else
|
else
|
||||||
echo -n '-'
|
printf '-'
|
||||||
fi
|
fi
|
||||||
echo -n "$2) "
|
printf '%s) ' "$2"
|
||||||
])
|
])
|
||||||
|
@@ -16,16 +16,16 @@
|
|||||||
struct Compressor {
|
struct Compressor {
|
||||||
//! The compressor's preferences
|
//! The compressor's preferences
|
||||||
struct CompressorConfig prefs;
|
struct CompressorConfig prefs;
|
||||||
|
|
||||||
//! History of the peak values
|
//! History of the peak values
|
||||||
int *peaks;
|
int *peaks;
|
||||||
|
|
||||||
//! History of the gain values
|
//! History of the gain values
|
||||||
int *gain;
|
int *gain;
|
||||||
|
|
||||||
//! History of clip amounts
|
//! History of clip amounts
|
||||||
int *clipped;
|
int *clipped;
|
||||||
|
|
||||||
unsigned int pos;
|
unsigned int pos;
|
||||||
unsigned int bufsz;
|
unsigned int bufsz;
|
||||||
};
|
};
|
||||||
@@ -41,9 +41,9 @@ struct Compressor *Compressor_new(unsigned int history)
|
|||||||
obj->peaks = obj->gain = obj->clipped = NULL;
|
obj->peaks = obj->gain = obj->clipped = NULL;
|
||||||
obj->bufsz = 0;
|
obj->bufsz = 0;
|
||||||
obj->pos = 0;
|
obj->pos = 0;
|
||||||
|
|
||||||
Compressor_setHistory(obj, history);
|
Compressor_setHistory(obj, history);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ void Compressor_setHistory(struct Compressor *obj, unsigned int history)
|
|||||||
{
|
{
|
||||||
if (!history)
|
if (!history)
|
||||||
history = BUCKETS;
|
history = BUCKETS;
|
||||||
|
|
||||||
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
|
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
|
||||||
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
|
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
|
||||||
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
|
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
|
||||||
@@ -82,7 +82,7 @@ struct CompressorConfig *Compressor_getConfig(struct Compressor *obj)
|
|||||||
return &obj->prefs;
|
return &obj->prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||||
unsigned int count)
|
unsigned int count)
|
||||||
{
|
{
|
||||||
struct CompressorConfig *prefs = Compressor_getConfig(obj);
|
struct CompressorConfig *prefs = Compressor_getConfig(obj);
|
||||||
@@ -97,7 +97,7 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
|||||||
int *clipped = obj->clipped + slot;
|
int *clipped = obj->clipped + slot;
|
||||||
unsigned int ramp = count;
|
unsigned int ramp = count;
|
||||||
int delta;
|
int delta;
|
||||||
|
|
||||||
ap = audio;
|
ap = audio;
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -124,15 +124,15 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
|||||||
|
|
||||||
//! Determine target gain
|
//! Determine target gain
|
||||||
newGain = (1 << 10)*prefs->target/peakVal;
|
newGain = (1 << 10)*prefs->target/peakVal;
|
||||||
|
|
||||||
//! Adjust the gain with inertia from the previous gain value
|
//! Adjust the gain with inertia from the previous gain value
|
||||||
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
|
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
|
||||||
>> prefs->smooth;
|
>> prefs->smooth;
|
||||||
|
|
||||||
//! Make sure it's no more than the maximum gain value
|
//! Make sure it's no more than the maximum gain value
|
||||||
if (newGain > (prefs->maxgain << 10))
|
if (newGain > (prefs->maxgain << 10))
|
||||||
newGain = prefs->maxgain << 10;
|
newGain = prefs->maxgain << 10;
|
||||||
|
|
||||||
//! Make sure it's no less than 1:1
|
//! Make sure it's no less than 1:1
|
||||||
if (newGain < (1 << 10))
|
if (newGain < (1 << 10))
|
||||||
newGain = 1 << 10;
|
newGain = 1 << 10;
|
||||||
@@ -144,7 +144,7 @@ void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
|||||||
//! Truncate the ramp time
|
//! Truncate the ramp time
|
||||||
ramp = peakPos;
|
ramp = peakPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Record the new gain
|
//! Record the new gain
|
||||||
obj->gain[slot] = newGain;
|
obj->gain[slot] = newGain;
|
||||||
|
|
||||||
|
113
src/ape.c
Normal file
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
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);
|
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
audio_check_sample_format(unsigned sample_format, GError **error_r);
|
audio_check_sample_format(enum sample_format, GError **error_r);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
audio_check_channel_count(unsigned sample_format, GError **error_r);
|
audio_check_channel_count(unsigned sample_format, GError **error_r);
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
enum sample_format {
|
enum sample_format {
|
||||||
SAMPLE_FORMAT_UNDEFINED = 0,
|
SAMPLE_FORMAT_UNDEFINED = 0,
|
||||||
@@ -219,6 +220,9 @@ static inline void
|
|||||||
audio_format_mask_apply(struct audio_format *af,
|
audio_format_mask_apply(struct audio_format *af,
|
||||||
const struct audio_format *mask)
|
const struct audio_format *mask)
|
||||||
{
|
{
|
||||||
|
assert(audio_format_valid(af));
|
||||||
|
assert(audio_format_mask_valid(mask));
|
||||||
|
|
||||||
if (mask->sample_rate != 0)
|
if (mask->sample_rate != 0)
|
||||||
af->sample_rate = mask->sample_rate;
|
af->sample_rate = mask->sample_rate;
|
||||||
|
|
||||||
@@ -227,6 +231,8 @@ audio_format_mask_apply(struct audio_format *af,
|
|||||||
|
|
||||||
if (mask->channels != 0)
|
if (mask->channels != 0)
|
||||||
af->channels = mask->channels;
|
af->channels = mask->channels;
|
||||||
|
|
||||||
|
assert(audio_format_valid(af));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -192,6 +192,8 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
audio_format_init(dest, rate, sample_format, channels);
|
audio_format_init(dest, rate, sample_format, channels);
|
||||||
|
assert(mask ? audio_format_mask_valid(dest)
|
||||||
|
: audio_format_valid(dest));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -464,7 +464,7 @@ handle_currentsong(struct client *client,
|
|||||||
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
||||||
{
|
{
|
||||||
playlist_print_current(client, &g_playlist);
|
playlist_print_current(client, &g_playlist);
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return COMMAND_RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum command_return
|
static enum command_return
|
||||||
@@ -749,7 +749,7 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
|
|||||||
|
|
||||||
result = playlist_open_into_queue(argv[1], &g_playlist);
|
result = playlist_open_into_queue(argv[1], &g_playlist);
|
||||||
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
|
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
|
||||||
return result;
|
return print_playlist_result(client, result);
|
||||||
|
|
||||||
result = playlist_load_spl(&g_playlist, argv[1]);
|
result = playlist_load_spl(&g_playlist, argv[1]);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(client, result);
|
||||||
@@ -1715,15 +1715,11 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
sticker = sticker_song_get(song);
|
sticker = sticker_song_get(song);
|
||||||
if (NULL == sticker) {
|
if (sticker) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
sticker_print(client, sticker);
|
||||||
"no stickers found");
|
sticker_free(sticker);
|
||||||
return COMMAND_RETURN_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sticker_print(client, sticker);
|
|
||||||
sticker_free(sticker);
|
|
||||||
|
|
||||||
return COMMAND_RETURN_OK;
|
return COMMAND_RETURN_OK;
|
||||||
/* set song song_id id key */
|
/* set song song_id id key */
|
||||||
} else if (argc == 6 && strcmp(argv[1], "set") == 0) {
|
} else if (argc == 6 && strcmp(argv[1], "set") == 0) {
|
||||||
|
@@ -244,7 +244,7 @@ static const char *const audiofile_suffixes[] = {
|
|||||||
static const char *const audiofile_mime_types[] = {
|
static const char *const audiofile_mime_types[] = {
|
||||||
"audio/x-wav",
|
"audio/x-wav",
|
||||||
"audio/x-aiff",
|
"audio/x-aiff",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct decoder_plugin audiofile_decoder_plugin = {
|
const struct decoder_plugin audiofile_decoder_plugin = {
|
||||||
|
@@ -81,6 +81,10 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
|
|||||||
|
|
||||||
#endif /* !OLD_FFMPEG_INCLUDES */
|
#endif /* !OLD_FFMPEG_INCLUDES */
|
||||||
|
|
||||||
|
#ifndef AV_VERSION_INT
|
||||||
|
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mpd_ffmpeg_stream {
|
struct mpd_ffmpeg_stream {
|
||||||
struct decoder *decoder;
|
struct decoder *decoder;
|
||||||
struct input_stream *input;
|
struct input_stream *input;
|
||||||
@@ -102,13 +106,11 @@ static int64_t
|
|||||||
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
||||||
{
|
{
|
||||||
struct mpd_ffmpeg_stream *stream = opaque;
|
struct mpd_ffmpeg_stream *stream = opaque;
|
||||||
bool ret;
|
|
||||||
|
|
||||||
if (whence == AVSEEK_SIZE)
|
if (whence == AVSEEK_SIZE)
|
||||||
return stream->input->size;
|
return stream->input->size;
|
||||||
|
|
||||||
ret = input_stream_seek(stream->input, pos, whence, NULL);
|
if (!input_stream_seek(stream->input, pos, whence, NULL))
|
||||||
if (!ret)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return stream->input->offset;
|
return stream->input->offset;
|
||||||
@@ -156,7 +158,11 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
|
|||||||
{
|
{
|
||||||
for (unsigned i = 0; i < format_context->nb_streams; ++i)
|
for (unsigned i = 0; i < format_context->nb_streams; ++i)
|
||||||
if (format_context->streams[i]->codec->codec_type ==
|
if (format_context->streams[i]->codec->codec_type ==
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
|
||||||
|
AVMEDIA_TYPE_AUDIO)
|
||||||
|
#else
|
||||||
CODEC_TYPE_AUDIO)
|
CODEC_TYPE_AUDIO)
|
||||||
|
#endif
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@@ -183,30 +189,40 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
AVCodecContext *codec_context,
|
AVCodecContext *codec_context,
|
||||||
const AVRational *time_base)
|
const AVRational *time_base)
|
||||||
{
|
{
|
||||||
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
|
||||||
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
|
|
||||||
int16_t *aligned_buffer;
|
|
||||||
size_t buffer_size;
|
|
||||||
int len, audio_size;
|
|
||||||
uint8_t *packet_data;
|
|
||||||
int packet_size;
|
|
||||||
|
|
||||||
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
|
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
|
||||||
decoder_timestamp(decoder,
|
decoder_timestamp(decoder,
|
||||||
av_rescale_q(packet->pts, *time_base,
|
av_rescale_q(packet->pts, *time_base,
|
||||||
(AVRational){1, 1}));
|
(AVRational){1, 1}));
|
||||||
|
|
||||||
packet_data = packet->data;
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
packet_size = packet->size;
|
AVPacket packet2 = *packet;
|
||||||
|
#else
|
||||||
|
const uint8_t *packet_data = packet->data;
|
||||||
|
int packet_size = packet->size;
|
||||||
|
#endif
|
||||||
|
|
||||||
buffer_size = sizeof(audio_buf);
|
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
|
||||||
aligned_buffer = align16(audio_buf, &buffer_size);
|
size_t buffer_size = sizeof(audio_buf);
|
||||||
|
int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
|
||||||
|
|
||||||
while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
|
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
||||||
audio_size = buffer_size;
|
while (
|
||||||
len = avcodec_decode_audio2(codec_context,
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
aligned_buffer, &audio_size,
|
packet2.size > 0 &&
|
||||||
packet_data, packet_size);
|
#else
|
||||||
|
packet_size > 0 &&
|
||||||
|
#endif
|
||||||
|
cmd == DECODE_COMMAND_NONE) {
|
||||||
|
int audio_size = buffer_size;
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
|
int len = avcodec_decode_audio3(codec_context,
|
||||||
|
aligned_buffer, &audio_size,
|
||||||
|
&packet2);
|
||||||
|
#else
|
||||||
|
int len = avcodec_decode_audio2(codec_context,
|
||||||
|
aligned_buffer, &audio_size,
|
||||||
|
packet_data, packet_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
/* if error, we skip the frame */
|
/* if error, we skip the frame */
|
||||||
@@ -214,8 +230,13 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
|
packet2.data += len;
|
||||||
|
packet2.size -= len;
|
||||||
|
#else
|
||||||
packet_data += len;
|
packet_data += len;
|
||||||
packet_size -= len;
|
packet_size -= len;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (audio_size <= 0)
|
if (audio_size <= 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -230,7 +251,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
static enum sample_format
|
static enum sample_format
|
||||||
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(51, 41, 0)
|
||||||
switch (codec_context->sample_fmt) {
|
switch (codec_context->sample_fmt) {
|
||||||
case SAMPLE_FMT_S16:
|
case SAMPLE_FMT_S16:
|
||||||
return SAMPLE_FORMAT_S16;
|
return SAMPLE_FORMAT_S16;
|
||||||
@@ -299,12 +320,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *format_context;
|
|
||||||
AVCodecContext *codec_context;
|
|
||||||
AVCodec *codec;
|
|
||||||
int audio_stream;
|
|
||||||
|
|
||||||
//ffmpeg works with ours "fileops" helper
|
//ffmpeg works with ours "fileops" helper
|
||||||
|
AVFormatContext *format_context;
|
||||||
if (av_open_input_stream(&format_context, stream->io, input->uri,
|
if (av_open_input_stream(&format_context, stream->io, input->uri,
|
||||||
input_format, NULL) != 0) {
|
input_format, NULL) != 0) {
|
||||||
g_warning("Open failed\n");
|
g_warning("Open failed\n");
|
||||||
@@ -319,7 +336,7 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_stream = ffmpeg_find_audio_stream(format_context);
|
int audio_stream = ffmpeg_find_audio_stream(format_context);
|
||||||
if (audio_stream == -1) {
|
if (audio_stream == -1) {
|
||||||
g_warning("No audio stream inside\n");
|
g_warning("No audio stream inside\n");
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
@@ -327,11 +344,12 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
codec_context = format_context->streams[audio_stream]->codec;
|
AVCodecContext *codec_context =
|
||||||
|
format_context->streams[audio_stream]->codec;
|
||||||
if (codec_context->codec_name[0] != 0)
|
if (codec_context->codec_name[0] != 0)
|
||||||
g_debug("codec '%s'", codec_context->codec_name);
|
g_debug("codec '%s'", codec_context->codec_name);
|
||||||
|
|
||||||
codec = avcodec_find_decoder(codec_context->codec_id);
|
AVCodec *codec = avcodec_find_decoder(codec_context->codec_id);
|
||||||
|
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
g_warning("Unsupported audio codec\n");
|
g_warning("Unsupported audio codec\n");
|
||||||
|
@@ -81,7 +81,7 @@ flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
|
|||||||
struct flac_data *data = (struct flac_data *) fdata;
|
struct flac_data *data = (struct flac_data *) fdata;
|
||||||
|
|
||||||
if (!data->input_stream->seekable)
|
if (!data->input_stream->seekable)
|
||||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
|
||||||
|
|
||||||
*offset = (long)(data->input_stream->offset);
|
*offset = (long)(data->input_stream->offset);
|
||||||
|
|
||||||
|
@@ -153,6 +153,9 @@ gme_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
if((gme_err = gme_start_track(emu, song_num)) != NULL)
|
if((gme_err = gme_start_track(emu, song_num)) != NULL)
|
||||||
g_warning("%s", gme_err);
|
g_warning("%s", gme_err);
|
||||||
|
|
||||||
|
if(ti->length > 0)
|
||||||
|
gme_set_fade(emu, ti->length);
|
||||||
|
|
||||||
/* play */
|
/* play */
|
||||||
do {
|
do {
|
||||||
gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
|
gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
|
||||||
|
@@ -547,14 +547,14 @@ enum {
|
|||||||
XING_SCALE = 0x00000008L
|
XING_SCALE = 0x00000008L
|
||||||
};
|
};
|
||||||
|
|
||||||
struct version {
|
struct lame_version {
|
||||||
unsigned major;
|
unsigned major;
|
||||||
unsigned minor;
|
unsigned minor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lame {
|
struct lame {
|
||||||
char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */
|
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 peak; /* replaygain peak */
|
||||||
float track_gain; /* replaygain track gain */
|
float track_gain; /* replaygain track gain */
|
||||||
float album_gain; /* replaygain album gain */
|
float album_gain; /* replaygain album gain */
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include "decoder_list.h"
|
#include "decoder_list.h"
|
||||||
#include "decoder_plugin.h"
|
#include "decoder_plugin.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
|
#include "replay_gain_ape.h"
|
||||||
#include "input_stream.h"
|
#include "input_stream.h"
|
||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
@@ -297,6 +298,18 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
|
|||||||
return success;
|
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.
|
* Try decoding a file.
|
||||||
*/
|
*/
|
||||||
@@ -312,6 +325,8 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
|
|||||||
|
|
||||||
decoder_unlock(dc);
|
decoder_unlock(dc);
|
||||||
|
|
||||||
|
decoder_load_replay_gain(decoder, path_fs);
|
||||||
|
|
||||||
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
|
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
|
||||||
if (plugin->file_decode != NULL) {
|
if (plugin->file_decode != NULL) {
|
||||||
decoder_lock(dc);
|
decoder_lock(dc);
|
||||||
|
@@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
#define DIRECTORY_DIR "directory: "
|
#define DIRECTORY_DIR "directory: "
|
||||||
|
|
||||||
#define DEVICE_INARCHIVE (dev_t)(-1)
|
#define DEVICE_INARCHIVE (dev_t)(-1)
|
||||||
#define DEVICE_CONTAINER (dev_t)(-2)
|
#define DEVICE_CONTAINER (dev_t)(-2)
|
||||||
|
|
||||||
struct directory {
|
struct directory {
|
||||||
struct dirvec children;
|
struct dirvec children;
|
||||||
@@ -62,7 +62,8 @@ directory_free(struct directory *directory);
|
|||||||
static inline bool
|
static inline bool
|
||||||
directory_is_empty(const struct directory *directory)
|
directory_is_empty(const struct directory *directory)
|
||||||
{
|
{
|
||||||
return directory->children.nr == 0 && directory->songs.nr == 0;
|
return directory->children.nr == 0 && directory->songs.nr == 0 &&
|
||||||
|
playlist_vector_is_empty(&directory->playlists);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const char *
|
static inline const char *
|
||||||
|
@@ -55,7 +55,7 @@ static bool
|
|||||||
flac_encoder_configure(struct flac_encoder *encoder,
|
flac_encoder_configure(struct flac_encoder *encoder,
|
||||||
const struct config_param *param, G_GNUC_UNUSED GError **error)
|
const struct config_param *param, G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
encoder->compression = config_get_block_unsigned(param,
|
encoder->compression = config_get_block_unsigned(param,
|
||||||
"compression", 5);
|
"compression", 5);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -218,7 +218,7 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
|
|||||||
|
|
||||||
if (init_status != FLAC__STREAM_ENCODER_OK) {
|
if (init_status != FLAC__STREAM_ENCODER_OK) {
|
||||||
g_set_error(error, flac_encoder_quark(), 0,
|
g_set_error(error, flac_encoder_quark(), 0,
|
||||||
"failed to initialize encoder: %s\n",
|
"failed to initialize encoder: %s\n",
|
||||||
FLAC__StreamEncoderStateString[init_status]);
|
FLAC__StreamEncoderStateString[init_status]);
|
||||||
flac_encoder_close(_encoder);
|
flac_encoder_close(_encoder);
|
||||||
return false;
|
return false;
|
||||||
@@ -234,7 +234,7 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
|
|||||||
|
|
||||||
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
|
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
|
||||||
g_set_error(error, flac_encoder_quark(), 0,
|
g_set_error(error, flac_encoder_quark(), 0,
|
||||||
"failed to initialize encoder: %s\n",
|
"failed to initialize encoder: %s\n",
|
||||||
FLAC__StreamEncoderInitStatusString[init_status]);
|
FLAC__StreamEncoderInitStatusString[init_status]);
|
||||||
flac_encoder_close(_encoder);
|
flac_encoder_close(_encoder);
|
||||||
return false;
|
return false;
|
||||||
|
@@ -170,6 +170,13 @@ lame_encoder_setup(struct lame_encoder *encoder, GError **error)
|
|||||||
return false;
|
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)) {
|
if (0 > lame_init_params(encoder->gfp)) {
|
||||||
g_set_error(error, lame_encoder_quark(), 0,
|
g_set_error(error, lame_encoder_quark(), 0,
|
||||||
"error initializing lame params");
|
"error initializing lame params");
|
||||||
|
@@ -276,6 +276,8 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
|
|||||||
vorbis_analysis_init(&encoder->vd, &encoder->vi);
|
vorbis_analysis_init(&encoder->vd, &encoder->vi);
|
||||||
vorbis_block_init(&encoder->vd, &encoder->vb);
|
vorbis_block_init(&encoder->vd, &encoder->vb);
|
||||||
|
|
||||||
|
ogg_stream_reset(&encoder->os);
|
||||||
|
|
||||||
encoder->flush = true;
|
encoder->flush = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ wave_encoder_quark(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fill_wave_header(struct wave_header *header, int channels, int bits,
|
fill_wave_header(struct wave_header *header, int channels, int bits,
|
||||||
int freq, int block_size)
|
int freq, int block_size)
|
||||||
{
|
{
|
||||||
int data_size = 0x0FFFFFFF;
|
int data_size = 0x0FFFFFFF;
|
||||||
@@ -142,7 +142,7 @@ wave_encoder_open(struct encoder *_encoder,
|
|||||||
buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
|
buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
|
||||||
|
|
||||||
/* create PCM wave header in initial buffer */
|
/* create PCM wave header in initial buffer */
|
||||||
fill_wave_header((struct wave_header *) buffer,
|
fill_wave_header((struct wave_header *) buffer,
|
||||||
audio_format->channels,
|
audio_format->channels,
|
||||||
encoder->bits,
|
encoder->bits,
|
||||||
audio_format->sample_rate,
|
audio_format->sample_rate,
|
||||||
|
@@ -58,11 +58,11 @@ winmm_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
|
|||||||
G_GNUC_UNUSED GError **error_r)
|
G_GNUC_UNUSED GError **error_r)
|
||||||
{
|
{
|
||||||
assert(ao != NULL);
|
assert(ao != NULL);
|
||||||
|
|
||||||
struct winmm_mixer *wm = g_new(struct winmm_mixer, 1);
|
struct winmm_mixer *wm = g_new(struct winmm_mixer, 1);
|
||||||
mixer_init(&wm->base, &winmm_mixer_plugin);
|
mixer_init(&wm->base, &winmm_mixer_plugin);
|
||||||
wm->output = (struct winmm_output *) ao;
|
wm->output = (struct winmm_output *) ao;
|
||||||
|
|
||||||
return &wm->base;
|
return &wm->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,13 +79,13 @@ winmm_mixer_get_volume(struct mixer *mixer, GError **error_r)
|
|||||||
DWORD volume;
|
DWORD volume;
|
||||||
HWAVEOUT handle = winmm_output_get_handle(wm->output);
|
HWAVEOUT handle = winmm_output_get_handle(wm->output);
|
||||||
MMRESULT result = waveOutGetVolume(handle, &volume);
|
MMRESULT result = waveOutGetVolume(handle, &volume);
|
||||||
|
|
||||||
if (result != MMSYSERR_NOERROR) {
|
if (result != MMSYSERR_NOERROR) {
|
||||||
g_set_error(error_r, 0, winmm_mixer_quark(),
|
g_set_error(error_r, 0, winmm_mixer_quark(),
|
||||||
"Failed to get winmm volume");
|
"Failed to get winmm volume");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return winmm_volume_decode(volume);
|
return winmm_volume_decode(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ winmm_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
|
|||||||
"Failed to set winmm volume");
|
"Failed to set winmm volume");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,6 +26,9 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "ao"
|
#define G_LOG_DOMAIN "ao"
|
||||||
|
|
||||||
|
/* An ao_sample_format, with all fields set to zero: */
|
||||||
|
static const ao_sample_format OUR_AO_FORMAT_INITIALIZER;
|
||||||
|
|
||||||
static unsigned ao_output_ref;
|
static unsigned ao_output_ref;
|
||||||
|
|
||||||
struct ao_data {
|
struct ao_data {
|
||||||
@@ -167,7 +170,7 @@ static bool
|
|||||||
ao_output_open(void *data, struct audio_format *audio_format,
|
ao_output_open(void *data, struct audio_format *audio_format,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
ao_sample_format format;
|
ao_sample_format format = OUR_AO_FORMAT_INITIALIZER;
|
||||||
struct ao_data *ad = (struct ao_data *)data;
|
struct ao_data *ad = (struct ao_data *)data;
|
||||||
|
|
||||||
switch (audio_format->format) {
|
switch (audio_format->format) {
|
||||||
|
@@ -29,6 +29,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#undef G_LOG_DOMAIN
|
||||||
|
#define G_LOG_DOMAIN "httpd_output"
|
||||||
|
|
||||||
struct httpd_client {
|
struct httpd_client {
|
||||||
/**
|
/**
|
||||||
* The httpd output object this client is connected to.
|
* The httpd output object this client is connected to.
|
||||||
|
@@ -111,7 +111,7 @@ struct httpd_output {
|
|||||||
char buffer[32768];
|
char buffer[32768];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum and current number of clients connected
|
* The maximum and current number of clients connected
|
||||||
* at the same time.
|
* at the same time.
|
||||||
*/
|
*/
|
||||||
guint clients_max, clients_cnt;
|
guint clients_max, clients_cnt;
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#ifdef HAVE_LIBWRAP
|
#ifdef HAVE_LIBWRAP
|
||||||
|
#include <sys/socket.h> /* needed for AF_UNIX */
|
||||||
#include <tcpd.h>
|
#include <tcpd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -123,6 +124,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
|
|
||||||
/* initialize metadata */
|
/* initialize metadata */
|
||||||
httpd->metadata = NULL;
|
httpd->metadata = NULL;
|
||||||
|
httpd->unflushed_input = 0;
|
||||||
|
|
||||||
/* initialize encoder */
|
/* initialize encoder */
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ enum {
|
|||||||
MAX_PORTS = 16,
|
MAX_PORTS = 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const size_t sample_size = sizeof(jack_default_audio_sample_t);
|
static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t);
|
||||||
|
|
||||||
struct jack_data {
|
struct jack_data {
|
||||||
/**
|
/**
|
||||||
@@ -103,9 +103,9 @@ mpd_jack_available(const struct jack_data *jd)
|
|||||||
min = current;
|
min = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(min % sample_size == 0);
|
assert(min % jack_sample_size == 0);
|
||||||
|
|
||||||
return min / sample_size;
|
return min / jack_sample_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -123,7 +123,7 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
const jack_nframes_t available = mpd_jack_available(jd);
|
const jack_nframes_t available = mpd_jack_available(jd);
|
||||||
for (unsigned i = 0; i < jd->audio_format.channels; ++i)
|
for (unsigned i = 0; i < jd->audio_format.channels; ++i)
|
||||||
jack_ringbuffer_read_advance(jd->ringbuffer[i],
|
jack_ringbuffer_read_advance(jd->ringbuffer[i],
|
||||||
available * sample_size);
|
available * jack_sample_size);
|
||||||
|
|
||||||
/* generate silence while MPD is paused */
|
/* generate silence while MPD is paused */
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
for (unsigned i = 0; i < jd->audio_format.channels; ++i) {
|
for (unsigned i = 0; i < jd->audio_format.channels; ++i) {
|
||||||
out = jack_port_get_buffer(jd->ports[i], nframes);
|
out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||||
jack_ringbuffer_read(jd->ringbuffer[i],
|
jack_ringbuffer_read(jd->ringbuffer[i],
|
||||||
(char *)out, available * sample_size);
|
(char *)out, available * jack_sample_size);
|
||||||
|
|
||||||
for (jack_nframes_t f = available; f < nframes; ++f)
|
for (jack_nframes_t f = available; f < nframes; ++f)
|
||||||
/* ringbuffer underrun, fill with silence */
|
/* ringbuffer underrun, fill with silence */
|
||||||
@@ -675,7 +675,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
space = space1;
|
space = space1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (space >= frame_size)
|
if (space >= jack_sample_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* XXX do something more intelligent to
|
/* XXX do something more intelligent to
|
||||||
@@ -683,7 +683,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
g_usleep(1000);
|
g_usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
space /= sample_size;
|
space /= jack_sample_size;
|
||||||
if (space < size)
|
if (space < size)
|
||||||
size = space;
|
size = space;
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Media MVP audio output based on code from MVPMC project:
|
* Media MVP audio output based on code from MVPMC project:
|
||||||
* http://mvpmc.sourceforge.net/
|
* http://mvpmc.sourceforge.net/
|
||||||
*/
|
*/
|
||||||
|
@@ -41,6 +41,15 @@
|
|||||||
# include <sys/soundcard.h>
|
# include <sys/soundcard.h>
|
||||||
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
||||||
|
|
||||||
|
/* We got bug reports from FreeBSD users who said that the two 24 bit
|
||||||
|
formats generate white noise on FreeBSD, but 32 bit works. This is
|
||||||
|
a workaround until we know what exactly is expected by the kernel
|
||||||
|
audio drivers. */
|
||||||
|
#ifndef __linux__
|
||||||
|
#undef AFMT_S24_PACKED
|
||||||
|
#undef AFMT_S24_NE
|
||||||
|
#endif
|
||||||
|
|
||||||
struct oss_data {
|
struct oss_data {
|
||||||
int fd;
|
int fd;
|
||||||
const char *device;
|
const char *device;
|
||||||
@@ -347,7 +356,7 @@ oss_setup_sample_rate(int fd, struct audio_format *audio_format,
|
|||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
if (!audio_valid_sample_rate(sample_rate))
|
if (!audio_valid_sample_rate(sample_rate))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
audio_format->sample_rate = sample_rate;
|
audio_format->sample_rate = sample_rate;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -461,6 +470,12 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
audio_format->format = mpd_format;
|
audio_format->format = mpd_format;
|
||||||
|
|
||||||
|
#ifdef AFMT_S24_PACKED
|
||||||
|
if (oss_format == AFMT_S24_PACKED)
|
||||||
|
audio_format->reverse_endian =
|
||||||
|
G_BYTE_ORDER != G_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
@@ -502,6 +517,12 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
audio_format->format = mpd_format;
|
audio_format->format = mpd_format;
|
||||||
|
|
||||||
|
#ifdef AFMT_S24_PACKED
|
||||||
|
if (oss_format == AFMT_S24_PACKED)
|
||||||
|
audio_format->reverse_endian =
|
||||||
|
G_BYTE_ORDER != G_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
|
@@ -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.mSampleRate = audio_format->sample_rate;
|
||||||
stream_description.mFormatID = kAudioFormatLinearPCM;
|
stream_description.mFormatID = kAudioFormatLinearPCM;
|
||||||
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
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) {
|
switch (audio_format->format) {
|
||||||
case SAMPLE_FORMAT_S8:
|
case SAMPLE_FORMAT_S8:
|
||||||
@@ -239,6 +230,16 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
|
|||||||
break;
|
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,
|
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
|
||||||
kAudioUnitScope_Input, 0,
|
kAudioUnitScope_Input, 0,
|
||||||
&stream_description,
|
&stream_description,
|
||||||
|
@@ -93,7 +93,7 @@ solaris_output_open(void *data, struct audio_format *audio_format,
|
|||||||
|
|
||||||
/* open the device in non-blocking mode */
|
/* 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) {
|
if (so->fd < 0) {
|
||||||
g_set_error(error, solaris_output_quark(), errno,
|
g_set_error(error, solaris_output_quark(), errno,
|
||||||
"Failed to open %s: %s",
|
"Failed to open %s: %s",
|
||||||
|
@@ -115,6 +115,7 @@ audio_output_open(struct audio_output *ao,
|
|||||||
{
|
{
|
||||||
bool open;
|
bool open;
|
||||||
|
|
||||||
|
assert(audio_format_valid(audio_format));
|
||||||
assert(mp != NULL);
|
assert(mp != NULL);
|
||||||
|
|
||||||
if (ao->fail_timer != NULL) {
|
if (ao->fail_timer != NULL) {
|
||||||
|
@@ -95,6 +95,8 @@ ao_filter_open(struct audio_output *ao,
|
|||||||
struct audio_format *audio_format,
|
struct audio_format *audio_format,
|
||||||
GError **error_r)
|
GError **error_r)
|
||||||
{
|
{
|
||||||
|
assert(audio_format_valid(audio_format));
|
||||||
|
|
||||||
/* the replay_gain filter cannot fail here */
|
/* the replay_gain filter cannot fail here */
|
||||||
if (ao->replay_gain_filter != NULL)
|
if (ao->replay_gain_filter != NULL)
|
||||||
filter_open(ao->replay_gain_filter, audio_format, error_r);
|
filter_open(ao->replay_gain_filter, audio_format, error_r);
|
||||||
@@ -136,6 +138,7 @@ ao_open(struct audio_output *ao)
|
|||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
assert(ao->pipe != NULL);
|
assert(ao->pipe != NULL);
|
||||||
assert(ao->chunk == NULL);
|
assert(ao->chunk == NULL);
|
||||||
|
assert(audio_format_valid(&ao->in_audio_format));
|
||||||
|
|
||||||
if (ao->fail_timer != NULL) {
|
if (ao->fail_timer != NULL) {
|
||||||
/* this can only happen when this
|
/* this can only happen when this
|
||||||
@@ -164,6 +167,8 @@ ao_open(struct audio_output *ao)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(audio_format_valid(filter_audio_format));
|
||||||
|
|
||||||
ao->out_audio_format = *filter_audio_format;
|
ao->out_audio_format = *filter_audio_format;
|
||||||
audio_format_mask_apply(&ao->out_audio_format,
|
audio_format_mask_apply(&ao->out_audio_format,
|
||||||
&ao->config_audio_format);
|
&ao->config_audio_format);
|
||||||
@@ -303,7 +308,7 @@ ao_wait(struct audio_output *ao)
|
|||||||
GTimeVal tv;
|
GTimeVal tv;
|
||||||
g_get_current_time(&tv);
|
g_get_current_time(&tv);
|
||||||
g_time_val_add(&tv, delay * 1000);
|
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)
|
if (ao->command != AO_COMMAND_NONE)
|
||||||
return false;
|
return false;
|
||||||
@@ -463,12 +468,9 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
|
|||||||
|
|
||||||
/* don't automatically reopen this device for
|
/* don't automatically reopen this device for
|
||||||
10 seconds */
|
10 seconds */
|
||||||
g_mutex_lock(ao->mutex);
|
|
||||||
|
|
||||||
assert(ao->fail_timer == NULL);
|
assert(ao->fail_timer == NULL);
|
||||||
ao->fail_timer = g_timer_new();
|
ao->fail_timer = g_timer_new();
|
||||||
|
|
||||||
g_mutex_unlock(ao->mutex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ const int16_t *pcm_byteswap_16(struct pcm_buffer *buffer,
|
|||||||
|
|
||||||
static inline uint32_t swab32(uint32_t x)
|
static inline uint32_t swab32(uint32_t x)
|
||||||
{
|
{
|
||||||
return (x << 24) |
|
return (x << 24) |
|
||||||
((x & 0xff00) << 8) |
|
((x & 0xff00) << 8) |
|
||||||
((x & 0xff0000) >> 8) |
|
((x & 0xff0000) >> 8) |
|
||||||
(x >> 24);
|
(x >> 24);
|
||||||
|
@@ -20,9 +20,9 @@
|
|||||||
#ifndef MPD_PIPE_H
|
#ifndef MPD_PIPE_H
|
||||||
#define MPD_PIPE_H
|
#define MPD_PIPE_H
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
struct audio_format;
|
struct audio_format;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -356,16 +356,9 @@ player_check_decoder_startup(struct player *player)
|
|||||||
static bool
|
static bool
|
||||||
player_send_silence(struct player *player)
|
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));
|
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) {
|
if (chunk == NULL) {
|
||||||
g_warning("Failed to allocate silence buffer");
|
g_warning("Failed to allocate silence buffer");
|
||||||
return false;
|
return false;
|
||||||
@@ -375,6 +368,12 @@ player_send_silence(struct player *player)
|
|||||||
chunk->audio_format = player->play_audio_format;
|
chunk->audio_format = player->play_audio_format;
|
||||||
#endif
|
#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->times = -1.0; /* undefined time stamp */
|
||||||
chunk->length = num_frames * frame_size;
|
chunk->length = num_frames * frame_size;
|
||||||
memset(chunk->data, 0, chunk->length);
|
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 song *song = pc.next_song;
|
||||||
struct decoder_control *dc = player->dc;
|
struct decoder_control *dc = player->dc;
|
||||||
double where;
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
|
|
||||||
@@ -413,8 +410,7 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
|
|
||||||
/* re-start the decoder */
|
/* re-start the decoder */
|
||||||
player_dc_start(player, player->pipe);
|
player_dc_start(player, player->pipe);
|
||||||
ret = player_wait_for_decoder(player);
|
if (!player_wait_for_decoder(player)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -435,8 +431,7 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
/* wait for the decoder to complete initialization */
|
/* wait for the decoder to complete initialization */
|
||||||
|
|
||||||
while (player->decoder_starting) {
|
while (player->decoder_starting) {
|
||||||
ret = player_check_decoder_startup(player);
|
if (!player_check_decoder_startup(player)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -445,14 +440,13 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
|
|
||||||
/* send the SEEK command */
|
/* send the SEEK command */
|
||||||
|
|
||||||
where = pc.seek_where;
|
double where = pc.seek_where;
|
||||||
if (where > pc.total_time)
|
if (where > pc.total_time)
|
||||||
where = pc.total_time - 0.1;
|
where = pc.total_time - 0.1;
|
||||||
if (where < 0.0)
|
if (where < 0.0)
|
||||||
where = 0.0;
|
where = 0.0;
|
||||||
|
|
||||||
ret = dc_seek(dc, where + song->start_ms / 1000.0);
|
if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -583,14 +577,12 @@ static void player_process_command(struct player *player)
|
|||||||
static void
|
static void
|
||||||
update_song_tag(struct song *song, const struct tag *new_tag)
|
update_song_tag(struct song *song, const struct tag *new_tag)
|
||||||
{
|
{
|
||||||
struct tag *old_tag;
|
|
||||||
|
|
||||||
if (song_is_file(song))
|
if (song_is_file(song))
|
||||||
/* don't update tags of local files, only remote
|
/* don't update tags of local files, only remote
|
||||||
streams may change tags dynamically */
|
streams may change tags dynamically */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
old_tag = song->tag;
|
struct tag *old_tag = song->tag;
|
||||||
song->tag = tag_dup(new_tag);
|
song->tag = tag_dup(new_tag);
|
||||||
|
|
||||||
if (old_tag != NULL)
|
if (old_tag != NULL)
|
||||||
@@ -648,15 +640,14 @@ static bool
|
|||||||
play_next_chunk(struct player *player)
|
play_next_chunk(struct player *player)
|
||||||
{
|
{
|
||||||
struct decoder_control *dc = player->dc;
|
struct decoder_control *dc = player->dc;
|
||||||
struct music_chunk *chunk = NULL;
|
|
||||||
unsigned cross_fade_position;
|
|
||||||
bool success;
|
|
||||||
|
|
||||||
if (!audio_output_all_wait(64))
|
if (!audio_output_all_wait(64))
|
||||||
/* the output pipe is still large enough, don't send
|
/* the output pipe is still large enough, don't send
|
||||||
another chunk */
|
another chunk */
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
unsigned cross_fade_position;
|
||||||
|
struct music_chunk *chunk = NULL;
|
||||||
if (player->xfade == XFADE_ENABLED &&
|
if (player->xfade == XFADE_ENABLED &&
|
||||||
player_dc_at_next_song(player) &&
|
player_dc_at_next_song(player) &&
|
||||||
(cross_fade_position = music_pipe_size(player->pipe))
|
(cross_fade_position = music_pipe_size(player->pipe))
|
||||||
@@ -694,6 +685,19 @@ play_next_chunk(struct player *player)
|
|||||||
chunk->mix_ratio = nan("");
|
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;
|
chunk->other = other_chunk;
|
||||||
} else {
|
} else {
|
||||||
/* there are not enough decoded chunks yet */
|
/* there are not enough decoded chunks yet */
|
||||||
@@ -732,9 +736,7 @@ play_next_chunk(struct player *player)
|
|||||||
|
|
||||||
/* play the current chunk */
|
/* play the current chunk */
|
||||||
|
|
||||||
success = play_chunk(player->song, chunk, &player->play_audio_format);
|
if (!play_chunk(player->song, chunk, &player->play_audio_format)) {
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
music_buffer_return(player_buffer, chunk);
|
music_buffer_return(player_buffer, chunk);
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
@@ -776,11 +778,9 @@ play_next_chunk(struct player *player)
|
|||||||
static bool
|
static bool
|
||||||
player_song_border(struct player *player)
|
player_song_border(struct player *player)
|
||||||
{
|
{
|
||||||
char *uri;
|
|
||||||
|
|
||||||
player->xfade = XFADE_UNKNOWN;
|
player->xfade = XFADE_UNKNOWN;
|
||||||
|
|
||||||
uri = song_get_uri(player->song);
|
char *uri = song_get_uri(player->song);
|
||||||
g_message("played \"%s\"", uri);
|
g_message("played \"%s\"", uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
@@ -875,16 +875,17 @@ static void do_play(struct decoder_control *dc)
|
|||||||
|
|
||||||
if (player.decoder_starting) {
|
if (player.decoder_starting) {
|
||||||
/* wait until the decoder is initialized completely */
|
/* wait until the decoder is initialized completely */
|
||||||
bool success;
|
|
||||||
const struct song *song;
|
|
||||||
|
|
||||||
success = player_check_decoder_startup(&player);
|
if (!player_check_decoder_startup(&player))
|
||||||
if (!success)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* seek to the beginning of the range */
|
/* 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 &&
|
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))
|
!dc_seek(dc, song->start_ms / 1000.0))
|
||||||
player_dc_stop(&player);
|
player_dc_stop(&player);
|
||||||
|
|
||||||
@@ -1092,10 +1093,9 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
|||||||
|
|
||||||
void player_create(void)
|
void player_create(void)
|
||||||
{
|
{
|
||||||
GError *e = NULL;
|
|
||||||
|
|
||||||
assert(pc.thread == NULL);
|
assert(pc.thread == NULL);
|
||||||
|
|
||||||
|
GError *e = NULL;
|
||||||
pc.thread = g_thread_create(player_task, NULL, true, &e);
|
pc.thread = g_thread_create(player_task, NULL, true, &e);
|
||||||
if (pc.thread == NULL)
|
if (pc.thread == NULL)
|
||||||
MPD_ERROR("Failed to spawn player task: %s", e->message);
|
MPD_ERROR("Failed to spawn player task: %s", e->message);
|
||||||
|
@@ -105,6 +105,13 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (base_uri != NULL && strcmp(base_uri, ".") == 0)
|
||||||
|
/* g_path_get_dirname() returns "." when there is no
|
||||||
|
directory name in the given path; clear that now,
|
||||||
|
because it would break the database lookup
|
||||||
|
functions */
|
||||||
|
base_uri = NULL;
|
||||||
|
|
||||||
if (g_path_is_absolute(uri)) {
|
if (g_path_is_absolute(uri)) {
|
||||||
/* XXX fs_charset vs utf8? */
|
/* XXX fs_charset vs utf8? */
|
||||||
char *prefix = base_uri != NULL
|
char *prefix = base_uri != NULL
|
||||||
@@ -129,7 +136,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
|
|||||||
else
|
else
|
||||||
uri = g_strdup(uri);
|
uri = g_strdup(uri);
|
||||||
|
|
||||||
if (uri_has_scheme(base_uri)) {
|
if (uri_has_scheme(uri)) {
|
||||||
dest = song_remote_new(uri);
|
dest = song_remote_new(uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -51,6 +51,12 @@ playlist_vector_init(struct playlist_vector *pv)
|
|||||||
void
|
void
|
||||||
playlist_vector_deinit(struct playlist_vector *pv);
|
playlist_vector_deinit(struct playlist_vector *pv);
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
playlist_vector_is_empty(const struct playlist_vector *pv)
|
||||||
|
{
|
||||||
|
return pv->head == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct playlist_metadata *
|
struct playlist_metadata *
|
||||||
playlist_vector_find(struct playlist_vector *pv, const char *name);
|
playlist_vector_find(struct playlist_vector *pv, const char *name);
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ poison_noaccess(void *p, size_t length)
|
|||||||
memset(p, 0x01, length);
|
memset(p, 0x01, length);
|
||||||
|
|
||||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||||
VALGRIND_MAKE_MEM_NOACCESS(p, length);
|
(void)VALGRIND_MAKE_MEM_NOACCESS(p, length);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ poison_undefined(void *p, size_t length)
|
|||||||
memset(p, 0x02, length);
|
memset(p, 0x02, length);
|
||||||
|
|
||||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||||
VALGRIND_MAKE_MEM_UNDEFINED(p, length);
|
(void)VALGRIND_MAKE_MEM_UNDEFINED(p, length);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
78
src/replay_gain_ape.c
Normal file
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
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
|
128
src/tag_ape.c
128
src/tag_ape.c
@@ -21,11 +21,7 @@
|
|||||||
#include "tag_ape.h"
|
#include "tag_ape.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
|
#include "ape.h"
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
|
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
|
||||||
[TAG_ALBUM_ARTIST] = "album artist",
|
[TAG_ALBUM_ARTIST] = "album artist",
|
||||||
@@ -56,101 +52,45 @@ tag_ape_import_item(struct tag *tag, unsigned long flags,
|
|||||||
|
|
||||||
if (tag == NULL)
|
if (tag == NULL)
|
||||||
tag = tag_new();
|
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;
|
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 *
|
struct tag *
|
||||||
tag_ape_load(const char *file)
|
tag_ape_load(const char *file)
|
||||||
{
|
{
|
||||||
struct tag *ret = NULL;
|
struct tag_ape_ctx ctx = { .tag = NULL };
|
||||||
FILE *fp;
|
|
||||||
int tagCount;
|
|
||||||
char *buffer = NULL;
|
|
||||||
char *p;
|
|
||||||
size_t tagLen;
|
|
||||||
size_t size;
|
|
||||||
unsigned long flags;
|
|
||||||
char *key;
|
|
||||||
|
|
||||||
struct {
|
tag_ape_scan(file, tag_ape_callback, &ctx);
|
||||||
unsigned char id[8];
|
return ctx.tag;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@@ -126,17 +126,16 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
|
|||||||
* - string list
|
* - string list
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
|
||||||
enum tag_type type)
|
const struct id3_frame *frame,
|
||||||
|
enum tag_type type)
|
||||||
{
|
{
|
||||||
struct id3_frame const *frame;
|
|
||||||
id3_ucs4_t const *ucs4;
|
id3_ucs4_t const *ucs4;
|
||||||
id3_utf8_t *utf8;
|
id3_utf8_t *utf8;
|
||||||
union id3_field const *field;
|
union id3_field const *field;
|
||||||
unsigned int nstrings, i;
|
unsigned int nstrings, i;
|
||||||
|
|
||||||
frame = id3_tag_findframe(tag, id, 0);
|
if (frame->nfields != 2)
|
||||||
if (frame == NULL || frame->nfields != 2)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* check the encoding field */
|
/* 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
|
* Import a "Comment frame" (ID3v2.4.0 section 4.10). It
|
||||||
* contains 4 fields:
|
* 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)
|
* - full string (we use this one)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
|
tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
|
||||||
enum tag_type type)
|
const struct id3_frame *frame,
|
||||||
|
enum tag_type type)
|
||||||
{
|
{
|
||||||
struct id3_frame const *frame;
|
|
||||||
id3_ucs4_t const *ucs4;
|
id3_ucs4_t const *ucs4;
|
||||||
id3_utf8_t *utf8;
|
id3_utf8_t *utf8;
|
||||||
union id3_field const *field;
|
union id3_field const *field;
|
||||||
|
|
||||||
frame = id3_tag_findframe(tag, id, 0);
|
if (frame->nfields != 4)
|
||||||
if (frame == NULL || frame->nfields != 4)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* for now I only read the 4th field, with the fullstring */
|
/* 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);
|
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.
|
* 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.
|
* 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
|
unsigned
|
||||||
timer_delay(const Timer *timer)
|
timer_delay(const Timer *timer)
|
||||||
{
|
{
|
||||||
int64_t delay = (timer->time - now()) / 1000;
|
int64_t delay = (int64_t)(timer->time - now()) / 1000;
|
||||||
if (delay < 0)
|
if (delay < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@@ -300,6 +300,9 @@ stat_directory(const struct directory *directory, struct stat *st)
|
|||||||
if (path_fs == NULL)
|
if (path_fs == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
ret = stat(path_fs, st);
|
ret = stat(path_fs, st);
|
||||||
|
if (ret < 0)
|
||||||
|
g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
|
||||||
|
|
||||||
g_free(path_fs);
|
g_free(path_fs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -316,6 +319,9 @@ stat_directory_child(const struct directory *parent, const char *name,
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = stat(path_fs, st);
|
ret = stat(path_fs, st);
|
||||||
|
if (ret < 0)
|
||||||
|
g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
|
||||||
|
|
||||||
g_free(path_fs);
|
g_free(path_fs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -557,6 +563,7 @@ directory_child_access(const struct directory *directory,
|
|||||||
/* access() is useless on WIN32 */
|
/* access() is useless on WIN32 */
|
||||||
(void)directory;
|
(void)directory;
|
||||||
(void)name;
|
(void)name;
|
||||||
|
(void)mode;
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
char *path = map_directory_child_fs(directory, name);
|
char *path = map_directory_child_fs(directory, name);
|
||||||
|
Reference in New Issue
Block a user