From cbf79769d396449cfc9e5555c7419cc3f117e8d4 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 8 Nov 2014 19:21:42 +0100 Subject: db/Count: include cleanup --- src/db/Count.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/db/Count.cxx b/src/db/Count.cxx index b58ac53e9..e5e244a6a 100644 --- a/src/db/Count.cxx +++ b/src/db/Count.cxx @@ -23,7 +23,7 @@ #include "Interface.hxx" #include "client/Client.hxx" #include "LightSong.hxx" -#include "tag/Set.hxx" +#include "tag/Tag.hxx" #include #include -- cgit v1.2.3 From b5ba94f1de06c621da937241eedfcfb100f26a09 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 8 Nov 2014 19:25:01 +0100 Subject: tag/Set: do AlbumArtist/Artist fallback only if AlbumArtist is not disabled On "list albumartist", songs that have no AlbumArtist tag will use the Artist tag. However, if AlbumArtist is disabled via "metadata_to_use", the TagBuilder::AddItem() call is ignored, and PrintUniqueTag() attempts to print a nullptr string. This commit fixes the problem by attempting the fallback only if AlbumArtist is not disabled. --- src/tag/Set.cxx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/tag/Set.cxx b/src/tag/Set.cxx index 47a8423bf..6a55a450f 100644 --- a/src/tag/Set.cxx +++ b/src/tag/Set.cxx @@ -19,6 +19,7 @@ #include "Set.hxx" #include "TagBuilder.hxx" +#include "TagSettings.h" #include @@ -109,6 +110,7 @@ TagSet::InsertUnique(const Tag &tag, if (!CheckUnique(type, tag, type, group_mask) && (type != TAG_ALBUM_ARTIST || + ignore_tag_items[TAG_ALBUM_ARTIST] || /* fall back to "Artist" if no "AlbumArtist" was found */ !CheckUnique(type, tag, TAG_ARTIST, group_mask))) InsertUnique(tag, type, nullptr, group_mask); -- cgit v1.2.3 From ff6f1655f0441f2fa8a1947bf242ea2cce6ba7a7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 10 Nov 2014 08:45:19 +0100 Subject: input/curl: ignore ResponseBoundary() while seeking While seeking, metadata must not be updated. ResponseBoundary() was added in MPD 0.19.1, but I forgot to add the IsSeeking() check there. This caused the "seekable" flag to reset. --- src/input/plugins/CurlInputPlugin.cxx | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index 1e1a46108..abb7e312c 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -610,6 +610,10 @@ CurlInputStream::ResponseBoundary() /* undo all effects of HeaderReceived() because the previous response was not applicable for this stream */ + if (IsSeekPending()) + /* don't update metadata while seeking */ + return; + seekable = false; size = UNKNOWN_SIZE; ClearMimeType(); -- cgit v1.2.3 From 8ff0d99092acfe8e80eba9709c2e93727f9e47b9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 10 Nov 2014 09:00:50 +0100 Subject: decoder/audiofile: fix bit rate calculation --- src/decoder/plugins/AudiofileDecoderPlugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/decoder/plugins/AudiofileDecoderPlugin.cxx b/src/decoder/plugins/AudiofileDecoderPlugin.cxx index 4b347817e..a0ef71e49 100644 --- a/src/decoder/plugins/AudiofileDecoderPlugin.cxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.cxx @@ -209,7 +209,7 @@ audiofile_stream_decode(Decoder &decoder, InputStream &is) const auto total_time = audiofile_get_duration(fh); const uint16_t kbit_rate = (uint16_t) - (is.GetSize() * uint64_t(8000) / total_time.ToMS()); + (is.GetSize() * uint64_t(8) / total_time.ToMS()); const unsigned frame_size = (unsigned) afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); -- cgit v1.2.3 From 8d036c4b7c3a09bd55bdc86a4fb7b5d525a805c2 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 10 Nov 2014 22:52:17 +0100 Subject: pcm/SoxrResampler: round output buffer size up The old formula calculates the output buffer size with "regular" rounding (to the nearest integer), however sometimes, that is insufficient and the last sample cannot be resampled. This causes audible distortions. By changing the formula to consider the worst case (always round up), this problem is eliminated. --- src/pcm/SoxrResampler.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx index 56b9760d5..b9d6fc099 100644 --- a/src/pcm/SoxrResampler.cxx +++ b/src/pcm/SoxrResampler.cxx @@ -147,7 +147,8 @@ SoxrPcmResampler::Resample(ConstBuffer src, Error &error) const size_t n_frames = src.size / frame_size; - const size_t o_frames = size_t(n_frames * ratio + 0.5); + /* always round up: worst case output buffer size */ + const size_t o_frames = size_t(n_frames * ratio) + 1; float *output_buffer = (float *)buffer.Get(o_frames * frame_size); -- cgit v1.2.3 From 134cb6a0171192b7d621697f84196ce670a3ce21 Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Mon, 10 Nov 2014 19:38:22 -0800 Subject: Main: fix compilation on OS X using non-Apple compilers Commit d42c0f1dc5063d50a62817b63a1c2a4507c46071 added an OS X-specific method of calling mpd_main_after_fork(), which uses Grand Central Dispatch. Since this uses a block literal, it breaks compilation on compilers which don't support the block extension, e.g. non-Apple compilers. This affects users on older OS X releases with GCD (which depend on older Clang releases, or Apple GCCs, which don't support the C++11 features MPD needs); or which don't support GCD at all (10.5 and lower). This patch changes the #ifdef so that the non-GCD code is used as it was on OS X before this patch if blocks aren't available, via checking __BLOCKS__ macro. --- src/Main.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Main.cxx b/src/Main.cxx index 2719c05e0..26d4e7ae4 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -114,7 +114,7 @@ #include #endif -#ifdef __APPLE__ +#ifdef __BLOCKS__ #include #endif @@ -517,7 +517,7 @@ int mpd_main(int argc, char *argv[]) daemonize_begin(options.daemon); #endif -#ifdef __APPLE__ +#ifdef __BLOCKS__ /* Runs the OS X native event loop in the main thread, and runs the rest of mpd_main on a new thread. This lets CoreAudio receive route change notifications (e.g. plugging or unplugging headphones). -- cgit v1.2.3 From 4a04f73434ed6953ae1403d6f1eb33a3807e01d7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 07:45:31 +0100 Subject: decoder/opus: add constexpr output_buffer_frames --- src/decoder/plugins/OpusDecoderPlugin.cxx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 97c81984c..9599f7bcf 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -40,6 +40,12 @@ static constexpr opus_int32 opus_sample_rate = 48000; +/** + * Allocate an output buffer for 16 bit PCM samples big enough to hold + * a quarter second, larger than 120ms required by libopus. + */ +static constexpr unsigned opus_output_buffer_frames = opus_sample_rate / 4; + gcc_pure static bool IsOpusHead(const ogg_packet &packet) @@ -70,7 +76,6 @@ class MPDOpusDecoder { OpusDecoder *opus_decoder; opus_int16 *output_buffer; - unsigned output_size; bool os_initialized; bool found_opus; @@ -86,7 +91,7 @@ public: InputStream &_input_stream) :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), - output_buffer(nullptr), output_size(0), + output_buffer(nullptr), os_initialized(false), found_opus(false) {} ~MPDOpusDecoder(); @@ -264,11 +269,8 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) eos_granulepos > 0, duration); frame_size = audio_format.GetFrameSize(); - /* allocate an output buffer for 16 bit PCM samples big enough - to hold a quarter second, larger than 120ms required by - libopus */ - output_size = audio_format.sample_rate / 4; - output_buffer = new opus_int16[output_size * audio_format.channels]; + output_buffer = new opus_int16[opus_output_buffer_frames + * audio_format.channels]; return decoder_get_command(decoder); } @@ -304,7 +306,7 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) int nframes = opus_decode(opus_decoder, (const unsigned char*)packet.packet, packet.bytes, - output_buffer, output_size, + output_buffer, opus_output_buffer_frames, 0); if (nframes < 0) { LogError(opus_domain, opus_strerror(nframes)); -- cgit v1.2.3 From 466b6a23cdce42143a25f87ce2234e045e963bdf Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 08:30:11 +0100 Subject: decoder/opus: eliminate flag "found_opus" Check opus_decoder!=nullptr instead. --- src/decoder/plugins/OpusDecoderPlugin.cxx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 9599f7bcf..cf102a9b5 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -78,7 +78,6 @@ class MPDOpusDecoder { opus_int16 *output_buffer; bool os_initialized; - bool found_opus; int opus_serialno; @@ -92,7 +91,7 @@ public: :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), output_buffer(nullptr), - os_initialized(false), found_opus(false) {} + os_initialized(false) {} ~MPDOpusDecoder(); bool ReadFirstPage(OggSyncState &oy); @@ -168,7 +167,7 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) if (packet.b_o_s) return HandleBOS(packet); - else if (!found_opus) + else if (opus_decoder == nullptr) return DecoderCommand::STOP; if (IsOpusTags(packet)) @@ -230,7 +229,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) { assert(packet.b_o_s); - if (found_opus || !IsOpusHead(packet)) + if (opus_decoder != nullptr || !IsOpusHead(packet)) return DecoderCommand::STOP; unsigned channels; @@ -242,7 +241,6 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) assert(output_buffer == nullptr); opus_serialno = os.serialno; - found_opus = true; /* TODO: parse attributes from the OpusHead (sample rate, channels, ...) */ -- cgit v1.2.3 From 7886a14b74f58896670bb8c0573646cd1a947d05 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 11:18:51 +0100 Subject: decoder/opus: fix mistyped LoadEOSPacket() return value --- src/decoder/plugins/OpusDecoderPlugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index cf102a9b5..a4be8eb60 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -188,7 +188,7 @@ LoadEOSPacket(InputStream &is, Decoder *decoder, int serialno, /* we do this for local files only, because seeking around remote files is expensive and not worth the troubl */ - return -1; + return false; const auto old_offset = is.GetOffset(); -- cgit v1.2.3 From 23465ad9855f4878fd7210bf076fed938a409b7d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 10:56:36 +0100 Subject: decoder/opus: improved error logging --- src/decoder/plugins/OpusDecoderPlugin.cxx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index a4be8eb60..c3ac96687 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -167,8 +167,10 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) if (packet.b_o_s) return HandleBOS(packet); - else if (opus_decoder == nullptr) + else if (opus_decoder == nullptr) { + LogDebug(opus_domain, "BOS packet expected"); return DecoderCommand::STOP; + } if (IsOpusTags(packet)) return HandleTags(packet); @@ -229,13 +231,17 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) { assert(packet.b_o_s); - if (opus_decoder != nullptr || !IsOpusHead(packet)) + if (opus_decoder != nullptr || !IsOpusHead(packet)) { + LogDebug(opus_domain, "BOS packet must be OpusHead"); return DecoderCommand::STOP; + } unsigned channels; if (!ScanOpusHeader(packet.packet, packet.bytes, channels) || - !audio_valid_channel_count(channels)) + !audio_valid_channel_count(channels)) { + LogDebug(opus_domain, "Malformed BOS packet"); return DecoderCommand::STOP; + } assert(opus_decoder == nullptr); assert(output_buffer == nullptr); @@ -307,7 +313,8 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) output_buffer, opus_output_buffer_frames, 0); if (nframes < 0) { - LogError(opus_domain, opus_strerror(nframes)); + FormatError(opus_domain, "libopus error: %s", + opus_strerror(nframes)); return DecoderCommand::STOP; } -- cgit v1.2.3 From ba6f2b0467d85e5d855c7bc9649a09cfe973e4b9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 08:36:22 +0100 Subject: decoder/opus: move code to HandleEOS() --- src/decoder/plugins/OpusDecoderPlugin.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index c3ac96687..30ce06708 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -100,6 +100,7 @@ public: DecoderCommand HandlePackets(); DecoderCommand HandlePacket(const ogg_packet &packet); DecoderCommand HandleBOS(const ogg_packet &packet); + DecoderCommand HandleEOS(); DecoderCommand HandleTags(const ogg_packet &packet); DecoderCommand HandleAudio(const ogg_packet &packet); @@ -163,7 +164,7 @@ inline DecoderCommand MPDOpusDecoder::HandlePacket(const ogg_packet &packet) { if (packet.e_o_s) - return DecoderCommand::STOP; + return HandleEOS(); if (packet.b_o_s) return HandleBOS(packet); @@ -279,6 +280,12 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) return decoder_get_command(decoder); } +inline DecoderCommand +MPDOpusDecoder::HandleEOS() +{ + return DecoderCommand::STOP; +} + inline DecoderCommand MPDOpusDecoder::HandleTags(const ogg_packet &packet) { -- cgit v1.2.3 From c98cb1d6f9704e1fefcc28938bcedb8a4d84a7e5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 07:41:01 +0100 Subject: decoder/opus: support chained streams --- src/decoder/plugins/OpusDecoderPlugin.cxx | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 30ce06708..b6b2e0465 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -77,6 +77,14 @@ class MPDOpusDecoder { OpusDecoder *opus_decoder; opus_int16 *output_buffer; + /** + * If non-zero, then a previous Opus stream has been found + * already with this number of channels. If opus_decoder is + * nullptr, then its end-of-stream packet has been found + * already. + */ + unsigned previous_channels; + bool os_initialized; int opus_serialno; @@ -91,6 +99,7 @@ public: :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), output_buffer(nullptr), + previous_channels(0), os_initialized(false) {} ~MPDOpusDecoder(); @@ -245,7 +254,14 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) } assert(opus_decoder == nullptr); - assert(output_buffer == nullptr); + assert((previous_channels == 0) == (output_buffer == nullptr)); + + if (previous_channels != 0 && channels != previous_channels) { + FormatWarning(opus_domain, + "Next stream has different channels (%u -> %u)", + previous_channels, channels); + return DecoderCommand::STOP; + } opus_serialno = os.serialno; @@ -261,6 +277,13 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) return DecoderCommand::STOP; } + if (previous_channels != 0) { + /* decoder was already initialized by the previous + stream; skip the rest of this method */ + LogDebug(opus_domain, "Found another stream"); + return decoder_get_command(decoder); + } + eos_granulepos = LoadEOSGranulePos(input_stream, &decoder, opus_serialno); const auto duration = eos_granulepos >= 0 @@ -268,6 +291,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) opus_sample_rate) : SignedSongTime::Negative(); + previous_channels = channels; const AudioFormat audio_format(opus_sample_rate, SampleFormat::S16, channels); decoder_initialized(decoder, audio_format, @@ -283,6 +307,17 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) inline DecoderCommand MPDOpusDecoder::HandleEOS() { + if (eos_granulepos < 0 && previous_channels != 0) { + /* allow chaining of (unseekable) streams */ + assert(opus_decoder != nullptr); + assert(output_buffer != nullptr); + + opus_decoder_destroy(opus_decoder); + opus_decoder = nullptr; + + return decoder_get_command(decoder); + } + return DecoderCommand::STOP; } -- cgit v1.2.3