diff --git a/NEWS b/NEWS index 0ba8476c4..9517b8345 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,16 @@ ver 0.21 (not yet released) * mixer - sndio: new mixer plugin +ver 0.20.12 (not yet released) +* input + - curl: fix seeking +* decoder + - vorbis: fix Tremor support +* player + - log message when decoder is too slow +* output + - fix hanging playback with soxr resampler + ver 0.20.11 (2017/10/18) * storage - curl: support Content-Type application/xml diff --git a/src/decoder/plugins/VorbisDecoderPlugin.cxx b/src/decoder/plugins/VorbisDecoderPlugin.cxx index be557a353..fbbc0264a 100644 --- a/src/decoder/plugins/VorbisDecoderPlugin.cxx +++ b/src/decoder/plugins/VorbisDecoderPlugin.cxx @@ -178,6 +178,20 @@ VorbisDecoder::SubmitInit() client.Ready(audio_format, eos_granulepos > 0, duration); } +#ifdef HAVE_TREMOR +static inline int16_t tremor_clip_sample(int32_t x) +{ + x >>= 9; + + if (x < INT16_MIN) + return INT16_MIN; + if (x > INT16_MAX) + return INT16_MAX; + + return x; +} +#endif + bool VorbisDecoder::SubmitSomePcm() { @@ -197,7 +211,7 @@ VorbisDecoder::SubmitSomePcm() auto *dest = &buffer[c]; for (size_t i = 0; i < n_frames; ++i) { - *dest = *src++; + *dest = tremor_clip_sample(*src++); dest += channels; } } diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index a1be25d90..6ab7e34ee 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -66,7 +66,6 @@ static const size_t CURL_RESUME_AT = 384 * 1024; struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler { /* some buffers which were passed to libcurl, which we have too free */ - char range[32]; CurlSlist request_headers; CurlRequest *request = nullptr; @@ -89,8 +88,19 @@ struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler { static InputStream *Open(const char *url, Mutex &mutex, Cond &cond); + /** + * Create and initialize a new #CurlRequest instance. After + * this, you may add more request headers and set options. To + * actually start the request, call StartRequest(). + */ void InitEasy(); + /** + * Start the request after having called InitEasy(). After + * this, you must not set any CURL options. + */ + void StartRequest(); + /** * Frees the current "libcurl easy" handle, and everything * associated with it. @@ -365,6 +375,11 @@ CurlInputStream::InitEasy() request_headers.Clear(); request_headers.Append("Icy-Metadata: 1"); +} + +void +CurlInputStream::StartRequest() +{ request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get()); request->Start(); @@ -391,6 +406,7 @@ CurlInputStream::SeekInternal(offset_type new_offset) /* send the "Range" header */ if (offset > 0) { + char range[32]; #ifdef WIN32 // TODO: what can we use on Windows to format 64 bit? sprintf(range, "%lu-", (long)offset); @@ -399,6 +415,8 @@ CurlInputStream::SeekInternal(offset_type new_offset) #endif request->SetOption(CURLOPT_RANGE, range); } + + StartRequest(); } void @@ -422,6 +440,7 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond) try { BlockingCall(c->GetEventLoop(), [c](){ c->InitEasy(); + c->StartRequest(); }); } catch (...) { delete c; diff --git a/src/output/Thread.cxx b/src/output/Thread.cxx index 5acf0da0b..ed2feac82 100644 --- a/src/output/Thread.cxx +++ b/src/output/Thread.cxx @@ -245,16 +245,15 @@ try { inline bool AudioOutputControl::PlayChunk() noexcept { - if (tags) { - const auto *tag = source.ReadTag(); - if (tag != nullptr) { - const ScopeUnlock unlock(mutex); - try { - output->SendTag(*tag); - } catch (const std::runtime_error &e) { - FormatError(e, "Failed to send tag to %s", - GetLogName()); - } + // ensure pending tags are flushed in all cases + const auto *tag = source.ReadTag(); + if (tags && tag != nullptr) { + const ScopeUnlock unlock(mutex); + try { + output->SendTag(*tag); + } catch (const std::runtime_error &e) { + FormatError(e, "Failed to send tag to %s", + GetLogName()); } } diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx index a6d2b3d4b..90431ef90 100644 --- a/src/player/Thread.cxx +++ b/src/player/Thread.cxx @@ -32,6 +32,7 @@ #include "output/MultipleOutputs.hxx" #include "tag/Tag.hxx" #include "Idle.hxx" +#include "system/PeriodClock.hxx" #include "util/Domain.hxx" #include "thread/Name.hxx" #include "Log.hxx" @@ -146,6 +147,8 @@ class Player { */ SongTime elapsed_time; + PeriodClock throttle_silence_log; + public: Player(PlayerControl &_pc, DecoderControl &_dc, MusicBuffer &_buffer) @@ -934,6 +937,8 @@ Player::SongBorder() { FormatDefault(player_domain, "played \"%s\"", song->GetURI()); + throttle_silence_log.Reset(); + ReplacePipe(dc.pipe); pc.outputs.SongBorder(); @@ -1095,6 +1100,10 @@ Player::Run() /* the decoder is too busy and hasn't provided new PCM data in time: send silence (if the output pipe is empty) */ + + if (throttle_silence_log.CheckUpdate(std::chrono::seconds(5))) + FormatWarning(player_domain, "Decoder is too slow; playing silence to avoid xrun"); + if (!SendSilence()) break; }