release v0.21.24

-----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAl7hRzQQHG1heEBtdXNp
 Y3BkLm9yZwAKCRAjbopYxttFEp7dD/483fkBEyipufQyQT9nntP8R/vqKlUT+M9V
 8LyoJBtNtbNkO4bdrgAQCzElYzkDnd8VR++ZDEcqSEF49y5wafsiuufJNW2/s02Y
 ygAqgTCpcuS58F7aK92CW99bhZcKC6zoQovkenSMaZ39gorE1ZuO1JyrFR1LWOat
 ELekougQe0JbX2YX0YOH0Qags5fv9joxYJYsx8ZC60sCRNc+h8CBQjpyhZqzk7wC
 EPviLeMDw5lXkoSI3C045QmwJRFG8GMEyZ/4E/mnibxoTXBJmsm4ArKrfJznrUUs
 r8Mkf4G7sVqbsRMyMFBpzw+lsRDpVWI26mhdah9Y1zuUYPaEMe7OVKuEsASJQ/oK
 33wRSBVZc7EPhV3m8f7U8NAJI0/XaaPKGgP2OrtnOfxD+OyAze4vNLZ/GJCYSsh+
 wN1grmJw1mTP52xBicN2AITqXSVVpuvznn+p0g9MBB9Nw8/vbeWaqGPQhMHGHW6a
 JIL9yUTWwhkvkhav1bT7zKaeZv3qfgO7fjkgJqQFYt/q4FZwPFHJme55mVGq9d1y
 FNR8BMh+0A8hRhhQVwVycLxr4+NJja3vaqx+uVG2kov1g3eQBSgnEYCiEV7uls4V
 Pr4sIEeU/QQ+0jsMoJCaW65bNm4tOGKibpjSHrwJ+gxwKn3N7sJaAOo7PgGsUZyS
 30cBC5zkjw==
 =XHev
 -----END PGP SIGNATURE-----

Merge tag 'v0.21.24'

release v0.21.24
This commit is contained in:
Max Kellermann 2020-06-10 22:58:14 +02:00
commit c6a7f6dabc
16 changed files with 88 additions and 36 deletions

9
NEWS
View File

@ -40,7 +40,7 @@ ver 0.22 (not yet released)
* switch to C++17 * switch to C++17
- GCC 7 or clang 4 (or newer) recommended - GCC 7 or clang 4 (or newer) recommended
ver 0.21.24 (not yet released) ver 0.21.24 (2020/06/10)
* protocol * protocol
- "tagtypes" requires no permissions - "tagtypes" requires no permissions
* database * database
@ -49,11 +49,14 @@ ver 0.21.24 (not yet released)
- modplug: fix Windows build failure - modplug: fix Windows build failure
- wildmidi: attempt to detect WildMidi using pkg-config - wildmidi: attempt to detect WildMidi using pkg-config
- wildmidi: fix Windows build failure - wildmidi: fix Windows build failure
* player
- don't restart current song if seeking beyond end
* Android * Android
- enable the decoder plugins ModPlug and WildMidi - enable the decoder plugins GME, ModPlug and WildMidi
- fix build failure with Android NDK r21 - fix build failure with Android NDK r21
* Windows * Windows
- enable the decoder plugins ModPlug and WildMidi - fix stream playback
- enable the decoder plugins GME, ModPlug and WildMidi
- work around Meson bug breaking the Windows build with GCC 10 - work around Meson bug breaking the Windows build with GCC 10
* fix unit test failure * fix unit test failure

View File

@ -170,6 +170,7 @@ thirdparty_libs = [
libid3tag, libid3tag,
libmodplug, libmodplug,
wildmidi, wildmidi,
gme,
ffmpeg, ffmpeg,
curl, curl,
libexpat, libexpat,

View File

@ -485,7 +485,8 @@ Querying :program:`MPD`'s status
- ``songs``: number of songs - ``songs``: number of songs
- ``uptime``: daemon uptime in seconds - ``uptime``: daemon uptime in seconds
- ``db_playtime``: sum of all song times in the database in seconds - ``db_playtime``: sum of all song times in the database in seconds
- ``db_update``: last db update in UNIX time - ``db_update``: last db update in UNIX time (seconds since
1970-01-01 UTC)
- ``playtime``: time length of music played - ``playtime``: time length of music played
Playback options Playback options

View File

@ -135,6 +135,18 @@ wildmidi = CmakeProject(
version='0.4.3', version='0.4.3',
) )
gme = CmakeProject(
'https://bitbucket.org/mpyne/game-music-emu/downloads/game-music-emu-0.6.3.tar.xz',
'aba34e53ef0ec6a34b58b84e28bf8cfbccee6585cebca25333604c35db3e051d',
'lib/libgme.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DENABLE_UBSAN=OFF',
'-DZLIB_INCLUDE_DIR=OFF',
'-DSDL2_DIR=OFF',
],
)
ffmpeg = FfmpegProject( ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz', 'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
'9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31', '9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',

View File

@ -38,15 +38,19 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <stdexcept>
#include <string.h> #include <string.h>
DecoderBridge::DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending, DecoderBridge::DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending,
bool _initial_seek_essential,
std::unique_ptr<Tag> _tag) noexcept std::unique_ptr<Tag> _tag) noexcept
:dc(_dc), :dc(_dc),
initial_seek_pending(_initial_seek_pending), initial_seek_pending(_initial_seek_pending),
initial_seek_essential(_initial_seek_essential),
song_tag(std::move(_tag)) {} song_tag(std::move(_tag)) {}
DecoderBridge::~DecoderBridge() noexcept DecoderBridge::~DecoderBridge() noexcept
{ {
/* caller must flush the chunk */ /* caller must flush the chunk */
@ -364,6 +368,10 @@ DecoderBridge::SeekError() noexcept
/* d'oh, we can't seek to the sub-song start position, /* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */ what now? - no idea, ignoring the problem for now. */
initial_seek_running = false; initial_seek_running = false;
if (initial_seek_essential)
error = std::make_exception_ptr(std::runtime_error("Decoder failed to seek"));
return; return;
} }

View File

@ -64,6 +64,11 @@ private:
*/ */
bool initial_seek_pending; bool initial_seek_pending;
/**
* Are initial seek failures fatal?
*/
const bool initial_seek_essential;
/** /**
* Is the initial seek currently running? During this time, * Is the initial seek currently running? During this time,
* the decoder command is SEEK. This flag is set by * the decoder command is SEEK. This flag is set by
@ -112,6 +117,7 @@ private:
public: public:
DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending, DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending,
bool _initial_seek_essential,
std::unique_ptr<Tag> _tag) noexcept; std::unique_ptr<Tag> _tag) noexcept;
~DecoderBridge() noexcept; ~DecoderBridge() noexcept;

View File

@ -80,6 +80,7 @@ void
DecoderControl::Start(std::unique_lock<Mutex> &lock, DecoderControl::Start(std::unique_lock<Mutex> &lock,
std::unique_ptr<DetachedSong> _song, std::unique_ptr<DetachedSong> _song,
SongTime _start_time, SongTime _end_time, SongTime _start_time, SongTime _end_time,
bool _initial_seek_essential,
MusicBuffer &_buffer, MusicBuffer &_buffer,
std::shared_ptr<MusicPipe> _pipe) noexcept std::shared_ptr<MusicPipe> _pipe) noexcept
{ {
@ -89,6 +90,7 @@ DecoderControl::Start(std::unique_lock<Mutex> &lock,
song = std::move(_song); song = std::move(_song);
start_time = _start_time; start_time = _start_time;
end_time = _end_time; end_time = _end_time;
initial_seek_essential = _initial_seek_essential;
buffer = &_buffer; buffer = &_buffer;
pipe = std::move(_pipe); pipe = std::move(_pipe);

View File

@ -112,6 +112,12 @@ private:
public: public:
bool seek_error; bool seek_error;
bool seekable; bool seekable;
/**
* @see #DecoderBridge::initial_seek_essential
*/
bool initial_seek_essential;
SongTime seek_time; SongTime seek_time;
private: private:
@ -383,12 +389,15 @@ public:
* owned and freed by the decoder * owned and freed by the decoder
* @param start_time see #DecoderControl * @param start_time see #DecoderControl
* @param end_time see #DecoderControl * @param end_time see #DecoderControl
* @param initial_seek_essential see
* #DecoderBridge::initial_seek_essential
* @param pipe the pipe which receives the decoded chunks (owned by * @param pipe the pipe which receives the decoded chunks (owned by
* the caller) * the caller)
*/ */
void Start(std::unique_lock<Mutex> &lock, void Start(std::unique_lock<Mutex> &lock,
std::unique_ptr<DetachedSong> song, std::unique_ptr<DetachedSong> song,
SongTime start_time, SongTime end_time, SongTime start_time, SongTime end_time,
bool initial_seek_essential,
MusicBuffer &buffer, MusicBuffer &buffer,
std::shared_ptr<MusicPipe> pipe) noexcept; std::shared_ptr<MusicPipe> pipe) noexcept;

View File

@ -22,7 +22,7 @@
#include "util/Compiler.h" #include "util/Compiler.h"
#include <forward_list> #include <forward_list> // IWYU pragma: export
struct ConfigBlock; struct ConfigBlock;
class InputStream; class InputStream;

View File

@ -422,6 +422,7 @@ decoder_run_song(DecoderControl &dc,
dc.start_time = dc.seek_time; dc.start_time = dc.seek_time;
DecoderBridge bridge(dc, dc.start_time.IsPositive(), DecoderBridge bridge(dc, dc.start_time.IsPositive(),
dc.initial_seek_essential,
/* pass the song tag only if it's /* pass the song tag only if it's
authoritative, i.e. if it's a local authoritative, i.e. if it's a local
file - tags on "stream" songs are just file - tags on "stream" songs are just

View File

@ -27,11 +27,11 @@
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx" #include "fs/FileSystem.hxx"
#include "fs/NarrowPath.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include "util/StringFormat.hxx" #include "util/StringFormat.hxx"
#include "util/StringView.hxx" #include "util/StringView.hxx"
#include "util/UriExtract.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -40,7 +40,6 @@
#include <cassert> #include <cassert>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#define SUBTUNE_PREFIX "tune_" #define SUBTUNE_PREFIX "tune_"
@ -83,11 +82,10 @@ gcc_pure
static unsigned static unsigned
ParseSubtuneName(const char *base) noexcept ParseSubtuneName(const char *base) noexcept
{ {
if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0) base = StringAfterPrefix(base, SUBTUNE_PREFIX);
if (base == nullptr)
return 0; return 0;
base += sizeof(SUBTUNE_PREFIX) - 1;
char *endptr; char *endptr;
auto track = strtoul(base, &endptr, 10); auto track = strtoul(base, &endptr, 10);
if (endptr == base || *endptr != '.') if (endptr == base || *endptr != '.')
@ -106,41 +104,46 @@ ParseContainerPath(Path path_fs)
const Path base = path_fs.GetBase(); const Path base = path_fs.GetBase();
unsigned track; unsigned track;
if (base.IsNull() || if (base.IsNull() ||
(track = ParseSubtuneName(base.c_str())) < 1) (track = ParseSubtuneName(NarrowPath(base))) < 1)
return { AllocatedPath(path_fs), 0 }; return { AllocatedPath(path_fs), 0 };
return { path_fs.GetDirectoryName(), track - 1 }; return { path_fs.GetDirectoryName(), track - 1 };
} }
static AllocatedPath
ReplaceSuffix(Path src,
const PathTraitsFS::const_pointer new_suffix) noexcept
{
const auto *old_suffix = src.GetSuffix();
if (old_suffix == nullptr)
return nullptr;
PathTraitsFS::string s(src.c_str(), old_suffix);
s += new_suffix;
return AllocatedPath::FromFS(std::move(s));
}
static Music_Emu* static Music_Emu*
LoadGmeAndM3u(const GmeContainerPath& container) { LoadGmeAndM3u(const GmeContainerPath& container) {
const char *path = container.path.c_str();
const char *suffix = uri_get_suffix(path);
Music_Emu *emu; Music_Emu *emu;
const char *gme_err = const char *gme_err =
gme_open_file(path, &emu, GME_SAMPLE_RATE); gme_open_file(NarrowPath(container.path), &emu, GME_SAMPLE_RATE);
if (gme_err != nullptr) { if (gme_err != nullptr) {
LogWarning(gme_domain, gme_err); LogWarning(gme_domain, gme_err);
return nullptr; return nullptr;
} }
if(suffix == nullptr) { const auto m3u_path = ReplaceSuffix(container.path,
return emu; PATH_LITERAL("m3u"));
}
std::string m3u_path(path,suffix);
m3u_path += "m3u";
/* /*
* Some GME formats lose metadata if you attempt to * Some GME formats lose metadata if you attempt to
* load a non-existant M3U file, so check that one * load a non-existant M3U file, so check that one
* exists before loading. * exists before loading.
*/ */
if(FileExists(Path::FromFS(m3u_path.c_str()))) { if (!m3u_path.IsNull() && FileExists(m3u_path))
gme_load_m3u(emu,m3u_path.c_str()); gme_load_m3u(emu, NarrowPath(m3u_path));
}
return emu; return emu;
} }
@ -320,7 +323,7 @@ gme_container_scan(Path path_fs)
if (num_songs < 2) if (num_songs < 2)
return list; return list;
const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); const auto *subtune_suffix = path_fs.GetSuffix();
TagBuilder tag_builder; TagBuilder tag_builder;

View File

@ -57,7 +57,7 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
easy.SetUserAgent("Music Player Daemon " VERSION); easy.SetUserAgent("Music Player Daemon " VERSION);
easy.SetHeaderFunction(_HeaderFunction, this); easy.SetHeaderFunction(_HeaderFunction, this);
easy.SetWriteFunction(WriteFunction, this); easy.SetWriteFunction(WriteFunction, this);
#ifndef ANDROID #if !defined(ANDROID) && !defined(_WIN32)
easy.SetOption(CURLOPT_NETRC, 1L); easy.SetOption(CURLOPT_NETRC, 1L);
#endif #endif
easy.SetErrorBuffer(error_buffer); easy.SetErrorBuffer(error_buffer);

View File

@ -20,8 +20,8 @@
#ifndef MPD_AUDIO_FORMAT_HXX #ifndef MPD_AUDIO_FORMAT_HXX
#define MPD_AUDIO_FORMAT_HXX #define MPD_AUDIO_FORMAT_HXX
#include "pcm/SampleFormat.hxx" #include "pcm/SampleFormat.hxx" // IWYU pragma: export
#include "pcm/ChannelDefs.hxx" #include "pcm/ChannelDefs.hxx" // IWYU pragma: export
#include "util/Compiler.h" #include "util/Compiler.h"
#include <cstddef> #include <cstddef>

View File

@ -224,7 +224,8 @@ private:
* Caller must lock the mutex. * Caller must lock the mutex.
*/ */
void StartDecoder(std::unique_lock<Mutex> &lock, void StartDecoder(std::unique_lock<Mutex> &lock,
std::shared_ptr<MusicPipe> pipe) noexcept; std::shared_ptr<MusicPipe> pipe,
bool initial_seek_essential) noexcept;
/** /**
* The decoder has acknowledged the "START" command (see * The decoder has acknowledged the "START" command (see
@ -367,7 +368,8 @@ public:
void void
Player::StartDecoder(std::unique_lock<Mutex> &lock, Player::StartDecoder(std::unique_lock<Mutex> &lock,
std::shared_ptr<MusicPipe> _pipe) noexcept std::shared_ptr<MusicPipe> _pipe,
bool initial_seek_essential) noexcept
{ {
assert(queued || pc.command == PlayerCommand::SEEK); assert(queued || pc.command == PlayerCommand::SEEK);
assert(pc.next_song != nullptr); assert(pc.next_song != nullptr);
@ -379,6 +381,7 @@ Player::StartDecoder(std::unique_lock<Mutex> &lock,
dc.Start(lock, std::make_unique<DetachedSong>(*pc.next_song), dc.Start(lock, std::make_unique<DetachedSong>(*pc.next_song),
start_time, pc.next_song->GetEndTime(), start_time, pc.next_song->GetEndTime(),
initial_seek_essential,
buffer, std::move(_pipe)); buffer, std::move(_pipe));
} }
@ -636,7 +639,7 @@ Player::SeekDecoder(std::unique_lock<Mutex> &lock) noexcept
pipe->Clear(); pipe->Clear();
/* re-start the decoder */ /* re-start the decoder */
StartDecoder(lock, pipe); StartDecoder(lock, pipe, true);
ActivateDecoder(); ActivateDecoder();
pc.seeking = true; pc.seeking = true;
@ -714,7 +717,8 @@ Player::ProcessCommand(std::unique_lock<Mutex> &lock) noexcept
pc.CommandFinished(); pc.CommandFinished();
if (dc.IsIdle()) if (dc.IsIdle())
StartDecoder(lock, std::make_shared<MusicPipe>()); StartDecoder(lock, std::make_shared<MusicPipe>(),
false);
break; break;
@ -985,7 +989,7 @@ Player::Run() noexcept
std::unique_lock<Mutex> lock(pc.mutex); std::unique_lock<Mutex> lock(pc.mutex);
StartDecoder(lock, pipe); StartDecoder(lock, pipe, true);
ActivateDecoder(); ActivateDecoder();
pc.state = PlayerState::PLAY; pc.state = PlayerState::PLAY;
@ -1025,7 +1029,8 @@ Player::Run() noexcept
assert(dc.pipe == nullptr || dc.pipe == pipe); assert(dc.pipe == nullptr || dc.pipe == pipe);
StartDecoder(lock, std::make_shared<MusicPipe>()); StartDecoder(lock, std::make_shared<MusicPipe>(),
false);
} }
if (/* no cross-fading if MPD is going to pause at the if (/* no cross-fading if MPD is going to pause at the

View File

@ -30,7 +30,7 @@
#ifndef STRING_FORMAT_HXX #ifndef STRING_FORMAT_HXX
#define STRING_FORMAT_HXX #define STRING_FORMAT_HXX
#include "StringBuffer.hxx" #include "StringBuffer.hxx" // IWYU pragma: export
#include <stdio.h> #include <stdio.h>

View File

@ -98,6 +98,7 @@ thirdparty_libs = [
liblame, liblame,
libmodplug, libmodplug,
wildmidi, wildmidi,
gme,
ffmpeg, ffmpeg,
curl, curl,
libexpat, libexpat,