Compare commits

...

75 Commits

Author SHA1 Message Date
Avuton Olrich
e2950a7e4d mpd version 0.16.3 2011-06-04 07:37:33 -07:00
Max Kellermann
4b4aa64261 directory: allow directories with just playlists
Keep those when scanning for empty directories.  The check in
playlist_vector_is_empty() was missing.
2011-05-09 21:37:43 +02:00
Max Kellermann
26735390ff playlist_song: fix playlist files in base music directory
g_path_get_dirname() returns "." when there is no directory name in
the given path.  This patch adds a workaround for that.
2011-05-09 18:05:11 +02:00
Max Kellermann
9402b23dd5 playlist_song: fix NULL pointer dereference 2011-05-09 18:03:54 +02:00
Max Kellermann
246db3d565 decoder/ffmpeg: use avcodec_decode_audio3() if available
avcodec_decode_audio3() has been added in libavformat 52.25.0, and the
predecessor avcodec_decode_audio2() has been deprecated.
2011-05-09 09:24:17 +02:00
Max Kellermann
eaf414cbc8 decoder/ffmpeg: make variables more local 2011-05-09 09:24:15 +02:00
Anton Khirnov
327d41c00f decoder/ffmpeg: don't use deprecated CODEC_TYPE_AUDIO with new lavc
fixes build with lavc 53.
2011-05-09 08:00:45 +02:00
Max Kellermann
05d8ce3bcd decoder/ffmpeg: define fallback macro AV_VERSION_INT()
For ffmpeg < 0.5.  Copied from libavutil 0.5.
2011-05-09 08:00:45 +02:00
Max Kellermann
def2fe8805 Merge branch 'v0.15.x' into v0.16.x
Conflicts:
	NEWS
	configure.ac
	src/listen.c
2011-04-12 07:39:01 +02:00
Max Kellermann
f680b0a431 decoder/flac: fix enum mismatch in flac_tell_cb()
Fix clang warning.
2011-03-23 22:31:40 +01:00
Max Kellermann
d4b00ff11c listen: suppress "unused variable" warning 2011-03-23 22:27:31 +01:00
Max Kellermann
532f94a187 audio_parser: fix assertion failure in audio format mask parser
Use audio_format_mask_valid() to verify a mask.
2011-03-23 22:22:51 +01:00
Max Kellermann
87ad2f8542 command: fix return value of handle_currentsong()
Thanks to clang for complaining.
2011-03-23 22:16:46 +01:00
Simon Kagstrom
a8f891efcd configure.ac: Enable HAVE_OGG_COMMON when using libtremor
Otherwise OGGs can't be played.
2011-03-23 22:09:58 +01:00
Avuton Olrich
b5fc2419e8 Modify version string to post-release version 0.16.3~git 2011-03-18 17:43:11 -07:00
Avuton Olrich
fe588a255b mpd version 0.16.2 2011-03-18 17:43:11 -07:00
Max Kellermann
1fc571088b command: print playlist load error
Call print_playlist_result() instead of casting the enum implicitly.
2011-03-18 19:45:59 +01:00
Max Kellermann
8d83914f05 output/httpd: include sys/socket.h only when building with libwrap
Fixes build failure on WIN32.
2011-03-18 19:44:12 +01:00
Max Kellermann
0fdcd381bc update_walk: ignore parameter "mode" on WIN32
Fix compiler warning.
2011-03-18 19:43:26 +01:00
Max Kellermann
4f293ecd6f audio_format, output_thread: add more audio_format_valid() assertions 2011-03-16 23:37:41 +01:00
Max Kellermann
b6303313f0 encoder/vorbis: reset the Ogg stream after flush
Without the ogg_stream_reset() call, the "e_o_s" flag never gets
reset, and libogg writes EOS packets over and over.
2011-03-16 19:16:06 +01:00
Max Kellermann
a28449a123 encoder/vorbis: reset the Ogg stream after flush
Without the ogg_stream_reset() call, the "e_o_s" flag never gets
reset, and libogg writes EOS packets over and over.
2011-03-16 19:13:46 +01:00
Max Kellermann
6dcec36621 Merge release 0.15.16 into v0.16.x
Conflicts:
	NEWS
	configure.ac
	src/output/jack_plugin.c
	src/update.c
2011-03-16 18:08:54 +01:00
Avuton Olrich
84d0fd39a3 Modify version string to post-release version 0.15.17~git 2011-03-13 20:27:33 -07:00
Avuton Olrich
4d4b7e3de0 mpd version 0.15.16 2011-03-13 20:27:33 -07:00
Ulrich Spörlein
e2aea6bce5 output/httpd: include sys/socket.h for AF_UNIX 2011-03-09 19:53:48 +01:00
Ulrich Spörlein
5779146a7f configure.ac: fix bashism in tremor test
This makes FreeBSD detect libogg correctly. The '==' operator is an
undocumented GNU extension to test(1) and cannot be relied upon to
exist and do the right thing. POSIX mandates string comparisons to be
done using "test foo = bar".
2011-03-09 19:50:54 +01:00
Max Kellermann
a1d1c2beaa output/oss: disable 24 bit playback on FreeBSD
See code comment.
2011-02-28 00:09:45 +01:00
Max Kellermann
ee9c60fad4 output/oss: AFMT_S24_PACKED is little-endian
According to the Solaris dsp manpage, AFMT_S24_PACKED is
little-endian:

 http://download.oracle.com/docs/cd/E19963-01/821-1475/6nmf5baot/index.html

The Minix soundcard.h header says the same.
2011-02-28 00:00:41 +01:00
Max Kellermann
1674a4ec82 output/jack: fix crash with mono playback
With mono sound, jack_sample_size is smaller than frame_size (4 vs 2
bytes), and "space/jack_sample_size==0".  That means mpd_jack_play()
will return 0, although no error has occurred.
2011-02-27 23:26:50 +01:00
Max Kellermann
ce370bee60 output/jack: rename variable sample_size to jack_sample_size 2011-02-25 10:46:44 +01:00
Max Kellermann
e257484870 Makefile.am: distribute test/stdbin.h 2011-02-18 08:19:37 +01:00
Christopher Brannon
2a1f4539f6 Insure proper initialization of stack-allocated struct.
Version 1.0.0 of the libao library added a new field to the
ao_sample_format struct.  It is a char * named matrix.  When
an ao_sample_format is allocated on the stack, this field contains
garbage.  The proper course is to insure that is initialized to NULL.
NULL indicates that we do not want any mapping.
The struct is now initialized using a static initializer, and this
technique is compatible with all known versions of libao.
2011-02-15 12:16:25 +01:00
Max Kellermann
906efdd320 Makefile.am: compile test/run_encoder with ENCODER_CFLAGS 2011-02-13 23:22:57 +01:00
Thomas Jansen
948b8f35e6 general: whitespace cleanup
Remove trailing whitespace found by this command:
find -name '*.[ch]' | xargs grep "[[:space:]]$"
2011-02-09 22:42:31 +01:00
Thomas Jansen
e776c605ad output/httpd: initialize unflushed_input
This fixes the following valgrind warning occuring on the first call of
httpd_output_read_page:
==20124== Conditional jump or move depends on uninitialised value(s)
==20124==    at 0x425E65: httpd_output_read_page (httpd_output_plugin.c:240)
==20124==    by 0x426087: httpd_output_open (httpd_output_plugin.c:279)
==20124==    by 0x41D862: ao_open (output_plugin.h:206)
==20124==    by 0x41E133: audio_output_task (output_thread.c:590)
2011-02-09 22:41:36 +01:00
Tony Miller
8b2f4fc823 Set fadeout in gme_decoder_plugin. Due to the nature of the gme library,
this needs to be done for the end of songs to be detected.
2011-02-03 00:25:35 +01:00
Max Kellermann
03018611f8 update: log all file permission problems 2011-01-31 09:39:24 +01:00
Max Kellermann
8f99c954ad NEWS: fix 0.16.1 release year 2011-01-28 21:12:17 +01:00
Max Kellermann
5735c9efc1 configure.ac: fix tremor configure test
When the configure options were moved around for 0.16, the order was
changed, and the Tremor check broke.
2011-01-28 12:21:03 +01:00
Andreas Wiese
e6c3acaa6f Fix NDEBUG test
<stdbool.h> needs to be included unconditionally from definition of
NDEBUG, since »bool« doesn't get defined otherwise.

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

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

 "src/decoder/mad_decoder_plugin.c", line 550: (enum) tag redeclared: version
 cc: acomp failed for src/decoder/mad_decoder_plugin.c
2010-12-21 07:57:07 +01:00
Alex Viskovatoff
144ad7992e output/solaris: add missing parameter to open_cloexec() call 2010-12-21 07:31:08 +01:00
Alex Viskovatoff
a0dd1a1b8b audio_check: fix parameter in prototype 2010-12-21 07:29:58 +01:00
Max Kellermann
c360e69162 Modify version string to post-release version 0.16.1~git 2010-12-21 07:29:31 +01:00
Avuton Olrich
da01c6ef5b mpd version 0.16 2010-12-11 04:19:49 -08:00
Max Kellermann
fcd2355f4f Merge branch 'master' of git://git.musicpd.org/avuton/mpd 2010-12-07 18:18:19 +01:00
Max Kellermann
748a8a6f42 tag_id3: support multiple values
Loop over all frames with a specific id, and import all of them - not
just the first one (index 0).
2010-12-07 18:05:44 +01:00
Anton Khirnov
cb9965bab5 command: don't error when sticker list is run on song with no stickers
this is inconsistent with other commands (e.g. find) and seems wrong --
a song with no stickers attached is a perfectly valid state and an empty
list of stickers is also perfectly valid.
2010-12-07 17:32:52 +01:00
Max Kellermann
429ed24c99 tag_ape: support multiple values
One APE tag may contain more than one value, separated by null bytes.
2010-11-24 08:59:04 +01:00
Max Kellermann
1ab46472ab decoder_thread: load APE replay gain from music files 2010-11-18 23:02:30 +01:00
Max Kellermann
f6bbe1332f replay_gain_ape: parse replay gain from APE tags
Based on the APE reader.
2010-11-18 22:26:06 +01:00
Max Kellermann
11613347be tag_ape: move code to ape.c
Generic library for scanning APE tags.  Eliminated one "goto"!
2010-11-18 21:44:24 +01:00
Max Kellermann
8f46f1520c timer: fix integer overflow in timer_delay()
Fixes a regression: for output_plugin.delay(), we added a method to
the timer class which returns the delay in milliseconds.  This fails
to detect negative values, because the unsigned integer is divided by
1000, and then casted to signed.
2010-11-18 21:29:03 +01:00
Avuton Olrich
f2893b0d0f Modify version string to post-release version 0.16~git 2010-11-08 18:57:09 -08:00
48 changed files with 697 additions and 294 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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])])

View File

@@ -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"
]) ])

View File

@@ -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"
]) ])

View File

@@ -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
View 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
View 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

View File

@@ -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);

View File

@@ -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));
} }
/** /**

View File

@@ -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;
} }

View File

@@ -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) {

View File

@@ -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 = {

View File

@@ -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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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 *

View File

@@ -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;

View File

@@ -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");

View File

@@ -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;
} }

View File

@@ -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,

View File

@@ -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;
} }

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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/
*/ */

View File

@@ -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:

View File

@@ -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,

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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
View 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
View 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

View File

@@ -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;
} }

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);