Merge tag 'v0.21.24'
release v0.21.24
This commit is contained in:
		
							
								
								
									
										9
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								NEWS
									
									
									
									
									
								
							@@ -40,7 +40,7 @@ ver 0.22 (not yet released)
 | 
			
		||||
* switch to C++17
 | 
			
		||||
  - GCC 7 or clang 4 (or newer) recommended
 | 
			
		||||
 | 
			
		||||
ver 0.21.24 (not yet released)
 | 
			
		||||
ver 0.21.24 (2020/06/10)
 | 
			
		||||
* protocol
 | 
			
		||||
  - "tagtypes" requires no permissions
 | 
			
		||||
* database
 | 
			
		||||
@@ -49,11 +49,14 @@ ver 0.21.24 (not yet released)
 | 
			
		||||
  - modplug: fix Windows build failure
 | 
			
		||||
  - wildmidi: attempt to detect WildMidi using pkg-config
 | 
			
		||||
  - wildmidi: fix Windows build failure
 | 
			
		||||
* player
 | 
			
		||||
  - don't restart current song if seeking beyond end
 | 
			
		||||
* Android
 | 
			
		||||
  - enable the decoder plugins ModPlug and WildMidi
 | 
			
		||||
  - enable the decoder plugins GME, ModPlug and WildMidi
 | 
			
		||||
  - fix build failure with Android NDK r21
 | 
			
		||||
* 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
 | 
			
		||||
* fix unit test failure
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -170,6 +170,7 @@ thirdparty_libs = [
 | 
			
		||||
    libid3tag,
 | 
			
		||||
    libmodplug,
 | 
			
		||||
    wildmidi,
 | 
			
		||||
    gme,
 | 
			
		||||
    ffmpeg,
 | 
			
		||||
    curl,
 | 
			
		||||
    libexpat,
 | 
			
		||||
 
 | 
			
		||||
@@ -485,7 +485,8 @@ Querying :program:`MPD`'s status
 | 
			
		||||
    - ``songs``: number of songs
 | 
			
		||||
    - ``uptime``: daemon uptime 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
 | 
			
		||||
 | 
			
		||||
Playback options
 | 
			
		||||
 
 | 
			
		||||
@@ -135,6 +135,18 @@ wildmidi = CmakeProject(
 | 
			
		||||
    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(
 | 
			
		||||
    'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
 | 
			
		||||
    '9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',
 | 
			
		||||
 
 | 
			
		||||
@@ -38,15 +38,19 @@
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
DecoderBridge::DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending,
 | 
			
		||||
			     bool _initial_seek_essential,
 | 
			
		||||
			     std::unique_ptr<Tag> _tag) noexcept
 | 
			
		||||
	:dc(_dc),
 | 
			
		||||
	 initial_seek_pending(_initial_seek_pending),
 | 
			
		||||
	 initial_seek_essential(_initial_seek_essential),
 | 
			
		||||
	 song_tag(std::move(_tag)) {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DecoderBridge::~DecoderBridge() noexcept
 | 
			
		||||
{
 | 
			
		||||
	/* caller must flush the chunk */
 | 
			
		||||
@@ -364,6 +368,10 @@ DecoderBridge::SeekError() noexcept
 | 
			
		||||
		/* d'oh, we can't seek to the sub-song start position,
 | 
			
		||||
		   what now? - no idea, ignoring the problem for now. */
 | 
			
		||||
		initial_seek_running = false;
 | 
			
		||||
 | 
			
		||||
		if (initial_seek_essential)
 | 
			
		||||
			error = std::make_exception_ptr(std::runtime_error("Decoder failed to seek"));
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,11 @@ private:
 | 
			
		||||
	 */
 | 
			
		||||
	bool initial_seek_pending;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Are initial seek failures fatal?
 | 
			
		||||
	 */
 | 
			
		||||
	const bool initial_seek_essential;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Is the initial seek currently running?  During this time,
 | 
			
		||||
	 * the decoder command is SEEK.  This flag is set by
 | 
			
		||||
@@ -112,6 +117,7 @@ private:
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending,
 | 
			
		||||
		      bool _initial_seek_essential,
 | 
			
		||||
		      std::unique_ptr<Tag> _tag) noexcept;
 | 
			
		||||
 | 
			
		||||
	~DecoderBridge() noexcept;
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@ void
 | 
			
		||||
DecoderControl::Start(std::unique_lock<Mutex> &lock,
 | 
			
		||||
		      std::unique_ptr<DetachedSong> _song,
 | 
			
		||||
		      SongTime _start_time, SongTime _end_time,
 | 
			
		||||
		      bool _initial_seek_essential,
 | 
			
		||||
		      MusicBuffer &_buffer,
 | 
			
		||||
		      std::shared_ptr<MusicPipe> _pipe) noexcept
 | 
			
		||||
{
 | 
			
		||||
@@ -89,6 +90,7 @@ DecoderControl::Start(std::unique_lock<Mutex> &lock,
 | 
			
		||||
	song = std::move(_song);
 | 
			
		||||
	start_time = _start_time;
 | 
			
		||||
	end_time = _end_time;
 | 
			
		||||
	initial_seek_essential = _initial_seek_essential;
 | 
			
		||||
	buffer = &_buffer;
 | 
			
		||||
	pipe = std::move(_pipe);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,12 @@ private:
 | 
			
		||||
public:
 | 
			
		||||
	bool seek_error;
 | 
			
		||||
	bool seekable;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @see #DecoderBridge::initial_seek_essential
 | 
			
		||||
	 */
 | 
			
		||||
	bool initial_seek_essential;
 | 
			
		||||
 | 
			
		||||
	SongTime seek_time;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
@@ -383,12 +389,15 @@ public:
 | 
			
		||||
	 * owned and freed by the decoder
 | 
			
		||||
	 * @param start_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
 | 
			
		||||
	 * the caller)
 | 
			
		||||
	 */
 | 
			
		||||
	void Start(std::unique_lock<Mutex> &lock,
 | 
			
		||||
		   std::unique_ptr<DetachedSong> song,
 | 
			
		||||
		   SongTime start_time, SongTime end_time,
 | 
			
		||||
		   bool initial_seek_essential,
 | 
			
		||||
		   MusicBuffer &buffer,
 | 
			
		||||
		   std::shared_ptr<MusicPipe> pipe) noexcept;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
#include "util/Compiler.h"
 | 
			
		||||
 | 
			
		||||
#include <forward_list>
 | 
			
		||||
#include <forward_list>  // IWYU pragma: export
 | 
			
		||||
 | 
			
		||||
struct ConfigBlock;
 | 
			
		||||
class InputStream;
 | 
			
		||||
 
 | 
			
		||||
@@ -422,6 +422,7 @@ decoder_run_song(DecoderControl &dc,
 | 
			
		||||
		dc.start_time = dc.seek_time;
 | 
			
		||||
 | 
			
		||||
	DecoderBridge bridge(dc, dc.start_time.IsPositive(),
 | 
			
		||||
			     dc.initial_seek_essential,
 | 
			
		||||
			     /* pass the song tag only if it's
 | 
			
		||||
				authoritative, i.e. if it's a local
 | 
			
		||||
				file - tags on "stream" songs are just
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,11 @@
 | 
			
		||||
#include "fs/Path.hxx"
 | 
			
		||||
#include "fs/AllocatedPath.hxx"
 | 
			
		||||
#include "fs/FileSystem.hxx"
 | 
			
		||||
#include "fs/NarrowPath.hxx"
 | 
			
		||||
#include "util/ScopeExit.hxx"
 | 
			
		||||
#include "util/StringCompare.hxx"
 | 
			
		||||
#include "util/StringFormat.hxx"
 | 
			
		||||
#include "util/StringView.hxx"
 | 
			
		||||
#include "util/UriExtract.hxx"
 | 
			
		||||
#include "util/Domain.hxx"
 | 
			
		||||
#include "Log.hxx"
 | 
			
		||||
 | 
			
		||||
@@ -40,7 +40,6 @@
 | 
			
		||||
#include <cassert>
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#define SUBTUNE_PREFIX "tune_"
 | 
			
		||||
 | 
			
		||||
@@ -83,11 +82,10 @@ gcc_pure
 | 
			
		||||
static unsigned
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	base += sizeof(SUBTUNE_PREFIX) - 1;
 | 
			
		||||
 | 
			
		||||
	char *endptr;
 | 
			
		||||
	auto track = strtoul(base, &endptr, 10);
 | 
			
		||||
	if (endptr == base || *endptr != '.')
 | 
			
		||||
@@ -106,41 +104,46 @@ ParseContainerPath(Path path_fs)
 | 
			
		||||
	const Path base = path_fs.GetBase();
 | 
			
		||||
	unsigned track;
 | 
			
		||||
	if (base.IsNull() ||
 | 
			
		||||
	    (track = ParseSubtuneName(base.c_str())) < 1)
 | 
			
		||||
	    (track = ParseSubtuneName(NarrowPath(base))) < 1)
 | 
			
		||||
		return { AllocatedPath(path_fs), 0 };
 | 
			
		||||
 | 
			
		||||
	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*
 | 
			
		||||
LoadGmeAndM3u(const GmeContainerPath& container) {
 | 
			
		||||
 | 
			
		||||
	const char *path = container.path.c_str();
 | 
			
		||||
	const char *suffix = uri_get_suffix(path);
 | 
			
		||||
 | 
			
		||||
	Music_Emu *emu;
 | 
			
		||||
	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) {
 | 
			
		||||
		LogWarning(gme_domain, gme_err);
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(suffix == nullptr) {
 | 
			
		||||
		return emu;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string m3u_path(path,suffix);
 | 
			
		||||
	m3u_path += "m3u";
 | 
			
		||||
 | 
			
		||||
	const auto m3u_path = ReplaceSuffix(container.path,
 | 
			
		||||
					    PATH_LITERAL("m3u"));
 | 
			
		||||
    /*
 | 
			
		||||
     * Some GME formats lose metadata if you attempt to
 | 
			
		||||
     * load a non-existant M3U file, so check that one
 | 
			
		||||
     * exists before loading.
 | 
			
		||||
     */
 | 
			
		||||
	if(FileExists(Path::FromFS(m3u_path.c_str()))) {
 | 
			
		||||
		gme_load_m3u(emu,m3u_path.c_str());
 | 
			
		||||
	}
 | 
			
		||||
	if (!m3u_path.IsNull() && FileExists(m3u_path))
 | 
			
		||||
		gme_load_m3u(emu, NarrowPath(m3u_path));
 | 
			
		||||
 | 
			
		||||
	return emu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -320,7 +323,7 @@ gme_container_scan(Path path_fs)
 | 
			
		||||
	if (num_songs < 2)
 | 
			
		||||
		return list;
 | 
			
		||||
 | 
			
		||||
	const char *subtune_suffix = uri_get_suffix(path_fs.c_str());
 | 
			
		||||
	const auto *subtune_suffix = path_fs.GetSuffix();
 | 
			
		||||
 | 
			
		||||
	TagBuilder tag_builder;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
 | 
			
		||||
	easy.SetUserAgent("Music Player Daemon " VERSION);
 | 
			
		||||
	easy.SetHeaderFunction(_HeaderFunction, this);
 | 
			
		||||
	easy.SetWriteFunction(WriteFunction, this);
 | 
			
		||||
#ifndef ANDROID
 | 
			
		||||
#if !defined(ANDROID) && !defined(_WIN32)
 | 
			
		||||
	easy.SetOption(CURLOPT_NETRC, 1L);
 | 
			
		||||
#endif
 | 
			
		||||
	easy.SetErrorBuffer(error_buffer);
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
#ifndef MPD_AUDIO_FORMAT_HXX
 | 
			
		||||
#define MPD_AUDIO_FORMAT_HXX
 | 
			
		||||
 | 
			
		||||
#include "pcm/SampleFormat.hxx"
 | 
			
		||||
#include "pcm/ChannelDefs.hxx"
 | 
			
		||||
#include "pcm/SampleFormat.hxx" // IWYU pragma: export
 | 
			
		||||
#include "pcm/ChannelDefs.hxx" // IWYU pragma: export
 | 
			
		||||
#include "util/Compiler.h"
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 
 | 
			
		||||
@@ -224,7 +224,8 @@ private:
 | 
			
		||||
	 * Caller must lock the mutex.
 | 
			
		||||
	 */
 | 
			
		||||
	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
 | 
			
		||||
@@ -367,7 +368,8 @@ public:
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
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(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),
 | 
			
		||||
		 start_time, pc.next_song->GetEndTime(),
 | 
			
		||||
		 initial_seek_essential,
 | 
			
		||||
		 buffer, std::move(_pipe));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -636,7 +639,7 @@ Player::SeekDecoder(std::unique_lock<Mutex> &lock) noexcept
 | 
			
		||||
		pipe->Clear();
 | 
			
		||||
 | 
			
		||||
		/* re-start the decoder */
 | 
			
		||||
		StartDecoder(lock, pipe);
 | 
			
		||||
		StartDecoder(lock, pipe, true);
 | 
			
		||||
		ActivateDecoder();
 | 
			
		||||
 | 
			
		||||
		pc.seeking = true;
 | 
			
		||||
@@ -714,7 +717,8 @@ Player::ProcessCommand(std::unique_lock<Mutex> &lock) noexcept
 | 
			
		||||
		pc.CommandFinished();
 | 
			
		||||
 | 
			
		||||
		if (dc.IsIdle())
 | 
			
		||||
			StartDecoder(lock, std::make_shared<MusicPipe>());
 | 
			
		||||
			StartDecoder(lock, std::make_shared<MusicPipe>(),
 | 
			
		||||
				     false);
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
@@ -985,7 +989,7 @@ Player::Run() noexcept
 | 
			
		||||
 | 
			
		||||
	std::unique_lock<Mutex> lock(pc.mutex);
 | 
			
		||||
 | 
			
		||||
	StartDecoder(lock, pipe);
 | 
			
		||||
	StartDecoder(lock, pipe, true);
 | 
			
		||||
	ActivateDecoder();
 | 
			
		||||
 | 
			
		||||
	pc.state = PlayerState::PLAY;
 | 
			
		||||
@@ -1025,7 +1029,8 @@ Player::Run() noexcept
 | 
			
		||||
 | 
			
		||||
			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
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
#ifndef STRING_FORMAT_HXX
 | 
			
		||||
#define STRING_FORMAT_HXX
 | 
			
		||||
 | 
			
		||||
#include "StringBuffer.hxx"
 | 
			
		||||
#include "StringBuffer.hxx" // IWYU pragma: export
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,7 @@ thirdparty_libs = [
 | 
			
		||||
    liblame,
 | 
			
		||||
    libmodplug,
 | 
			
		||||
    wildmidi,
 | 
			
		||||
    gme,
 | 
			
		||||
    ffmpeg,
 | 
			
		||||
    curl,
 | 
			
		||||
    libexpat,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user