Merge remote-tracking branches 'neheb/patch-2', 'neheb/con', 'neheb/cons', 'neheb/guruhg', 'neheb/r12R3', 'neheb/fefgheh' and 'neheb/rhgerg3453'

This commit is contained in:
Max Kellermann 2020-02-05 19:36:01 +01:00
63 changed files with 230 additions and 121 deletions

6
NEWS
View File

@ -35,6 +35,12 @@ 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.20 (not yet released)
* decoder
- audiofile, ffmpeg, sndfile: handle MIME type "audio/wav"
- ffmpeg: fix playback of AIFF and TTA
- vorbis, opus: fix seeking in small files
ver 0.21.19 (2020/01/17) ver 0.21.19 (2020/01/17)
* configuration * configuration
- allow overriding top-level settings in includes - allow overriding top-level settings in includes

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="42" android:versionCode="43"
android:versionName="0.21.19"> android:versionName="0.21.20">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>

View File

@ -297,7 +297,7 @@ public:
explicit ConfigLoader(ConfigData &_config) noexcept explicit ConfigLoader(ConfigData &_config) noexcept
:config(_config) {} :config(_config) {}
bool TryFile(const Path path); bool TryFile(Path path);
bool TryFile(const AllocatedPath &base_path, Path path); bool TryFile(const AllocatedPath &base_path, Path path);
}; };

View File

@ -91,7 +91,7 @@ class ZzipInputStream final : public InputStream {
ZZIP_FILE *const file; ZZIP_FILE *const file;
public: public:
ZzipInputStream(const std::shared_ptr<ZzipDir> _dir, const char *_uri, ZzipInputStream(const std::shared_ptr<ZzipDir>& _dir, const char *_uri,
Mutex &_mutex, Mutex &_mutex,
ZZIP_FILE *_file) ZZIP_FILE *_file)
:InputStream(_uri, _mutex), :InputStream(_uri, _mutex),

View File

@ -82,7 +82,7 @@ ToAck(DatabaseErrorCode code) noexcept
gcc_pure gcc_pure
static enum ack static enum ack
ToAck(std::exception_ptr ep) noexcept ToAck(const std::exception_ptr& ep) noexcept
{ {
try { try {
std::rethrow_exception(ep); std::rethrow_exception(ep);
@ -113,7 +113,7 @@ ToAck(std::exception_ptr ep) noexcept
} }
void void
PrintError(Response &r, std::exception_ptr ep) PrintError(Response &r, const std::exception_ptr& ep)
{ {
LogError(ep); LogError(ep);
r.Error(ToAck(ep), GetFullMessage(ep).c_str()); r.Error(ToAck(ep), GetFullMessage(ep).c_str());

View File

@ -28,6 +28,6 @@ class Response;
* Send the exception to the client. * Send the exception to the client.
*/ */
void void
PrintError(Response &r, std::exception_ptr ep); PrintError(Response &r, const std::exception_ptr& ep);
#endif #endif

View File

@ -174,7 +174,7 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
COMMAND_STATUS_BITRATE ": %u\n", COMMAND_STATUS_BITRATE ": %u\n",
player_status.elapsed_time.RoundS(), player_status.elapsed_time.RoundS(),
player_status.total_time.IsNegative() player_status.total_time.IsNegative()
? 0u ? 0U
: unsigned(player_status.total_time.RoundS()), : unsigned(player_status.total_time.RoundS()),
player_status.elapsed_time.ToDoubleS(), player_status.elapsed_time.ToDoubleS(),
player_status.bit_rate); player_status.bit_rate);

View File

@ -50,7 +50,7 @@ template<typename T>
static auto static auto
Append(std::forward_list<T> &list, T &&src) Append(std::forward_list<T> &list, T &&src)
{ {
return list.emplace_after(FindLast(list), std::move(src)); return list.emplace_after(FindLast(list), std::forward<T>(src));
} }
void void

View File

@ -39,7 +39,6 @@ search_add_to_playlist(const Database &db, const Storage *storage,
const DatabaseSelection &selection) const DatabaseSelection &selection)
{ {
using namespace std::placeholders; using namespace std::placeholders;
const auto f = std::bind(AddSong, storage, const auto f = [=](auto && arg1) { return AddSong(storage, playlist_path_utf8, arg1); };
playlist_path_utf8, _1);
db.Visit(selection, f); db.Visit(selection, f);
} }

View File

@ -50,8 +50,9 @@
#include <mpd/async.h> #include <mpd/async.h>
#include <cassert> #include <cassert>
#include <string>
#include <list> #include <list>
#include <string>
#include <utility>
class LibmpdclientError final : public std::runtime_error { class LibmpdclientError final : public std::runtime_error {
enum mpd_error code; enum mpd_error code;
@ -447,7 +448,7 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
listener(_listener), listener(_listener),
host(block.GetBlockValue("host", "")), host(block.GetBlockValue("host", "")),
password(block.GetBlockValue("password", "")), password(block.GetBlockValue("password", "")),
port(block.GetBlockValue("port", 0u)), port(block.GetBlockValue("port", 0U)),
keepalive(block.GetBlockValue("keepalive", false)) keepalive(block.GetBlockValue("keepalive", false))
{ {
} }
@ -516,7 +517,7 @@ ProxyDatabase::Connect()
(void)keepalive; (void)keepalive;
#endif #endif
idle_received = ~0u; idle_received = ~0U;
is_idle = false; is_idle = false;
SocketMonitor::Open(SocketDescriptor(mpd_async_get_fd(mpd_connection_get_async(connection)))); SocketMonitor::Open(SocketDescriptor(mpd_async_get_fd(mpd_connection_get_async(connection))));
@ -674,15 +675,15 @@ ProxyDatabase::ReturnSong(const LightSong *_song) const noexcept
static void static void
Visit(struct mpd_connection *connection, const char *uri, Visit(struct mpd_connection *connection, const char *uri,
bool recursive, const SongFilter *filter, bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song, const VisitDirectory& visit_directory, const VisitSong& visit_song,
VisitPlaylist visit_playlist); const VisitPlaylist& visit_playlist);
static void static void
Visit(struct mpd_connection *connection, Visit(struct mpd_connection *connection,
bool recursive, const SongFilter *filter, bool recursive, const SongFilter *filter,
const struct mpd_directory *directory, const struct mpd_directory *directory,
VisitDirectory visit_directory, VisitSong visit_song, const VisitDirectory& visit_directory, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const VisitPlaylist& visit_playlist)
{ {
const char *path = mpd_directory_get_path(directory); const char *path = mpd_directory_get_path(directory);
@ -697,7 +698,7 @@ Visit(struct mpd_connection *connection,
if (recursive) if (recursive)
Visit(connection, path, recursive, filter, Visit(connection, path, recursive, filter,
visit_directory, visit_song, visit_playlist); visit_directory, std::move(visit_song), std::move(visit_playlist));
} }
gcc_pure gcc_pure
@ -710,7 +711,7 @@ Match(const SongFilter *filter, const LightSong &song) noexcept
static void static void
Visit(const SongFilter *filter, Visit(const SongFilter *filter,
const mpd_song *_song, const mpd_song *_song,
VisitSong visit_song) const VisitSong& visit_song)
{ {
if (!visit_song) if (!visit_song)
return; return;
@ -722,7 +723,7 @@ Visit(const SongFilter *filter,
static void static void
Visit(const struct mpd_playlist *playlist, Visit(const struct mpd_playlist *playlist,
VisitPlaylist visit_playlist) const VisitPlaylist& visit_playlist)
{ {
if (!visit_playlist) if (!visit_playlist)
return; return;
@ -778,8 +779,8 @@ ReceiveEntities(struct mpd_connection *connection) noexcept
static void static void
Visit(struct mpd_connection *connection, const char *uri, Visit(struct mpd_connection *connection, const char *uri,
bool recursive, const SongFilter *filter, bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song, const VisitDirectory& visit_directory, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const VisitPlaylist& visit_playlist)
{ {
if (!mpd_send_list_meta(connection, uri)) if (!mpd_send_list_meta(connection, uri))
ThrowError(connection); ThrowError(connection);
@ -813,7 +814,7 @@ Visit(struct mpd_connection *connection, const char *uri,
static void static void
SearchSongs(struct mpd_connection *connection, SearchSongs(struct mpd_connection *connection,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitSong visit_song) const VisitSong& visit_song)
try { try {
assert(selection.recursive); assert(selection.recursive);
assert(visit_song); assert(visit_song);

View File

@ -220,8 +220,8 @@ Directory::Sort() noexcept
void void
Directory::Walk(bool recursive, const SongFilter *filter, Directory::Walk(bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song, const VisitDirectory& visit_directory, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const const VisitPlaylist& visit_playlist) const
{ {
if (IsMount()) { if (IsMount()) {
assert(IsEmpty()); assert(IsEmpty());

View File

@ -284,8 +284,8 @@ public:
* Caller must lock #db_mutex. * Caller must lock #db_mutex.
*/ */
void Walk(bool recursive, const SongFilter *match, void Walk(bool recursive, const SongFilter *match,
VisitDirectory visit_directory, VisitSong visit_song, const VisitDirectory& visit_directory, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const; const VisitPlaylist& visit_playlist) const;
gcc_pure gcc_pure
LightDirectory Export() const noexcept; LightDirectory Export() const noexcept;

View File

@ -41,6 +41,7 @@
#include "util/SplitString.hxx" #include "util/SplitString.hxx"
#include <string> #include <string>
#include <utility>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -107,9 +108,9 @@ private:
void VisitServer(const ContentDirectoryService &server, void VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string> &&vpath, std::forward_list<std::string> &&vpath,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitDirectory visit_directory, const VisitDirectory& visit_directory,
VisitSong visit_song, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const; const VisitPlaylist& visit_playlist) const;
/** /**
* Run an UPnP search according to MPD parameters, and * Run an UPnP search according to MPD parameters, and
@ -118,7 +119,7 @@ private:
void SearchSongs(const ContentDirectoryService &server, void SearchSongs(const ContentDirectoryService &server,
const char *objid, const char *objid,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitSong visit_song) const; const VisitSong& visit_song) const;
UPnPDirContent SearchSongs(const ContentDirectoryService &server, UPnPDirContent SearchSongs(const ContentDirectoryService &server,
const char *objid, const char *objid,
@ -311,7 +312,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
static void static void
visitSong(const UPnPDirObject &meta, const char *path, visitSong(const UPnPDirObject &meta, const char *path,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitSong visit_song) const VisitSong& visit_song)
{ {
if (!visit_song) if (!visit_song)
return; return;
@ -339,7 +340,7 @@ void
UpnpDatabase::SearchSongs(const ContentDirectoryService &server, UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
const char *objid, const char *objid,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitSong visit_song) const const VisitSong& visit_song) const
{ {
if (!visit_song) if (!visit_song)
return; return;
@ -440,13 +441,13 @@ UpnpDatabase::Namei(const ContentDirectoryService &server,
static void static void
VisitItem(const UPnPDirObject &object, const char *uri, VisitItem(const UPnPDirObject &object, const char *uri,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitSong visit_song, VisitPlaylist visit_playlist) const VisitSong& visit_song, const VisitPlaylist& visit_playlist)
{ {
assert(object.type == UPnPDirObject::Type::ITEM); assert(object.type == UPnPDirObject::Type::ITEM);
switch (object.item_class) { switch (object.item_class) {
case UPnPDirObject::ItemClass::MUSIC: case UPnPDirObject::ItemClass::MUSIC:
visitSong(object, uri, selection, visit_song); visitSong(object, uri, selection, std::move(visit_song));
break; break;
case UPnPDirObject::ItemClass::PLAYLIST: case UPnPDirObject::ItemClass::PLAYLIST:
@ -469,9 +470,9 @@ VisitItem(const UPnPDirObject &object, const char *uri,
static void static void
VisitObject(const UPnPDirObject &object, const char *uri, VisitObject(const UPnPDirObject &object, const char *uri,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitDirectory visit_directory, const VisitDirectory& visit_directory,
VisitSong visit_song, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const VisitPlaylist& visit_playlist)
{ {
switch (object.type) { switch (object.type) {
case UPnPDirObject::Type::UNKNOWN: case UPnPDirObject::Type::UNKNOWN:
@ -486,7 +487,7 @@ VisitObject(const UPnPDirObject &object, const char *uri,
case UPnPDirObject::Type::ITEM: case UPnPDirObject::Type::ITEM:
VisitItem(object, uri, selection, VisitItem(object, uri, selection,
visit_song, visit_playlist); std::move(visit_song), std::move(visit_playlist));
break; break;
} }
} }
@ -497,9 +498,9 @@ void
UpnpDatabase::VisitServer(const ContentDirectoryService &server, UpnpDatabase::VisitServer(const ContentDirectoryService &server,
std::forward_list<std::string> &&vpath, std::forward_list<std::string> &&vpath,
const DatabaseSelection &selection, const DatabaseSelection &selection,
VisitDirectory visit_directory, const VisitDirectory& visit_directory,
VisitSong visit_song, const VisitSong& visit_song,
VisitPlaylist visit_playlist) const const VisitPlaylist& visit_playlist) const
{ {
/* If the path begins with rootid, we know that this is a /* If the path begins with rootid, we know that this is a
song, not a directory (because that's how we set things song, not a directory (because that's how we set things

View File

@ -41,7 +41,7 @@ adplug_init(const ConfigBlock &block)
FormatDebug(adplug_domain, "adplug %s", FormatDebug(adplug_domain, "adplug %s",
CAdPlug::get_version().c_str()); CAdPlug::get_version().c_str());
sample_rate = block.GetPositiveValue("sample_rate", 48000u); sample_rate = block.GetPositiveValue("sample_rate", 48000U);
CheckSampleRate(sample_rate); CheckSampleRate(sample_rate);
return true; return true;

View File

@ -200,7 +200,7 @@ audiofile_stream_decode(DecoderClient &client, InputStream &is)
AudioFileInputStream afis{&client, is}; AudioFileInputStream afis{&client, is};
AFvirtualfile *const vf = setup_virtual_fops(afis); AFvirtualfile *const vf = setup_virtual_fops(afis);
const AFfilehandle fh = afOpenVirtualFile(vf, "r", nullptr); auto fh = afOpenVirtualFile(vf, "r", nullptr);
if (fh == AF_NULL_FILEHANDLE) if (fh == AF_NULL_FILEHANDLE)
return; return;
@ -269,6 +269,8 @@ static const char *const audiofile_suffixes[] = {
}; };
static const char *const audiofile_mime_types[] = { static const char *const audiofile_mime_types[] = {
"audio/wav",
"audio/aiff",
"audio/x-wav", "audio/x-wav",
"audio/x-aiff", "audio/x-aiff",
nullptr nullptr

View File

@ -150,6 +150,5 @@ dsdlib_tag_id3(InputStream &is, TagHandler &handler,
scan_id3_tag(id3_tag, handler); scan_id3_tag(id3_tag, handler);
id3_tag_delete(id3_tag); id3_tag_delete(id3_tag);
return;
} }
#endif #endif

View File

@ -213,7 +213,6 @@ dsdiff_handle_native_tag(DecoderClient *client, InputStream &is,
return; return;
handler.OnTag(type, {label, length}); handler.OnTag(type, {label, length});
return;
} }
/** /**

View File

@ -231,7 +231,7 @@ faad_song_duration(DecoderBuffer &buffer, InputStream &is)
static NeAACDecHandle static NeAACDecHandle
faad_decoder_new() faad_decoder_new()
{ {
const NeAACDecHandle decoder = NeAACDecOpen(); auto decoder = NeAACDecOpen();
NeAACDecConfigurationPtr config = NeAACDecConfigurationPtr config =
NeAACDecGetCurrentConfiguration(decoder); NeAACDecGetCurrentConfiguration(decoder);
@ -324,7 +324,7 @@ faad_get_file_time(InputStream &is)
static void static void
faad_stream_decode(DecoderClient &client, InputStream &is, faad_stream_decode(DecoderClient &client, InputStream &is,
DecoderBuffer &buffer, const NeAACDecHandle decoder) DecoderBuffer &buffer, NeAACDecHandle decoder)
{ {
const auto total_time = faad_song_duration(buffer, is); const auto total_time = faad_song_duration(buffer, is);
@ -406,7 +406,7 @@ faad_stream_decode(DecoderClient &client, InputStream &is)
/* create the libfaad decoder */ /* create the libfaad decoder */
const NeAACDecHandle decoder = faad_decoder_new(); auto decoder = faad_decoder_new();
AtScopeExit(decoder) { NeAACDecClose(decoder); }; AtScopeExit(decoder) { NeAACDecClose(decoder); };
faad_stream_decode(client, is, buffer, decoder); faad_stream_decode(client, is, buffer, decoder);

View File

@ -698,7 +698,7 @@ static const char *const ffmpeg_mime_types[] = {
"audio/aac", "audio/aac",
"audio/aacp", "audio/aacp",
"audio/ac3", "audio/ac3",
"audio/aiff" "audio/aiff",
"audio/amr", "audio/amr",
"audio/basic", "audio/basic",
"audio/flac", "audio/flac",
@ -711,12 +711,13 @@ static const char *const ffmpeg_mime_types[] = {
"audio/qcelp", "audio/qcelp",
"audio/vorbis", "audio/vorbis",
"audio/vorbis+ogg", "audio/vorbis+ogg",
"audio/wav",
"audio/x-8svx", "audio/x-8svx",
"audio/x-16sv", "audio/x-16sv",
"audio/x-aac", "audio/x-aac",
"audio/x-ac3", "audio/x-ac3",
"audio/x-adx", "audio/x-adx",
"audio/x-aiff" "audio/x-aiff",
"audio/x-alaw", "audio/x-alaw",
"audio/x-au", "audio/x-au",
"audio/x-dca", "audio/x-dca",
@ -736,7 +737,7 @@ static const char *const ffmpeg_mime_types[] = {
"audio/x-pn-realaudio", "audio/x-pn-realaudio",
"audio/x-pn-multirate-realaudio", "audio/x-pn-multirate-realaudio",
"audio/x-speex", "audio/x-speex",
"audio/x-tta" "audio/x-tta",
"audio/x-voc", "audio/x-voc",
"audio/x-wav", "audio/x-wav",
"audio/x-wma", "audio/x-wma",

View File

@ -77,7 +77,7 @@ fluidsynth_mpd_log_function(int level,
static bool static bool
fluidsynth_init(const ConfigBlock &block) fluidsynth_init(const ConfigBlock &block)
{ {
sample_rate = block.GetPositiveValue("sample_rate", 48000u); sample_rate = block.GetPositiveValue("sample_rate", 48000U);
CheckSampleRate(sample_rate); CheckSampleRate(sample_rate);
soundfont_path = block.GetBlockValue("soundfont", soundfont_path = block.GetBlockValue("soundfont",

View File

@ -186,7 +186,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
client.Ready(result.first, true, duration); client.Ready(result.first, true, duration);
frame_size = result.first.GetFrameSize(); frame_size = result.first.GetFrameSize();
kbit_rate = frame_size * result.first.sample_rate / kbit_rate = frame_size * result.first.sample_rate /
(1024u / 8u); (1024U / 8U);
total_frames = result.second / frame_size; total_frames = result.second / frame_size;
} catch (UnsupportedFile) { } catch (UnsupportedFile) {
/* not a Hybrid-DSD file; let the next decoder plugin /* not a Hybrid-DSD file; let the next decoder plugin
@ -236,7 +236,7 @@ HybridDsdDecode(DecoderClient &client, InputStream &input)
/* fill the buffer */ /* fill the buffer */
auto w = buffer.Write(); auto w = buffer.Write();
if (!w.empty()) { if (!w.empty()) {
if (remaining_bytes < (1<<30ull) && if (remaining_bytes < (1<<30ULL) &&
w.size > size_t(remaining_bytes)) w.size > size_t(remaining_bytes))
w.size = remaining_bytes; w.size = remaining_bytes;

View File

@ -760,7 +760,7 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept
if (max_frames > 8 * 1024 * 1024) { if (max_frames > 8 * 1024 * 1024) {
FormatWarning(mad_domain, FormatWarning(mad_domain,
"mp3 file header indicates too many frames: %lu", "mp3 file header indicates too many frames: %zu",
max_frames); max_frames);
return false; return false;
} }

View File

@ -108,7 +108,7 @@ mikmod_decoder_init(const ConfigBlock &block)
static char params[] = ""; static char params[] = "";
mikmod_loop = block.GetBlockValue("loop", false); mikmod_loop = block.GetBlockValue("loop", false);
mikmod_sample_rate = block.GetPositiveValue("sample_rate", 44100u); mikmod_sample_rate = block.GetPositiveValue("sample_rate", 44100U);
if (!audio_valid_sample_rate(mikmod_sample_rate)) if (!audio_valid_sample_rate(mikmod_sample_rate))
throw FormatRuntimeError("Invalid sample rate in line %d: %u", throw FormatRuntimeError("Invalid sample rate in line %d: %u",
block.line, mikmod_sample_rate); block.line, mikmod_sample_rate);

View File

@ -45,8 +45,12 @@ OggDecoder::LoadEndPacket(ogg_packet &packet) const
DecoderReader reader(client, input_stream); DecoderReader reader(client, input_stream);
OggSyncState sync2(reader); OggSyncState sync2(reader);
OggStreamState stream2(GetSerialNo()); OggStreamState stream2(GetSerialNo());
/* passing synced=false because we're inside an
OggVisitor callback, and our InputStream may be in
the middle of an Ogg packet */
result = OggSeekFindEOS(sync2, stream2, packet, result = OggSeekFindEOS(sync2, stream2, packet,
input_stream); input_stream, false);
} }
/* restore the previous file position */ /* restore the previous file position */

View File

@ -127,7 +127,7 @@ SidplayGlobal::SidplayGlobal(const ConfigBlock &block)
if (!database_path.IsNull()) if (!database_path.IsNull())
songlength_database = sidplay_load_songlength_db(database_path); songlength_database = sidplay_load_songlength_db(database_path);
default_songlength = block.GetPositiveValue("default_songlength", 0u); default_songlength = block.GetPositiveValue("default_songlength", 0U);
default_genre = block.GetBlockValue("default_genre", ""); default_genre = block.GetBlockValue("default_genre", "");
@ -403,7 +403,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
const unsigned timebase = player.timebase(); const unsigned timebase = player.timebase();
#endif #endif
const unsigned end = duration.IsNegative() const unsigned end = duration.IsNegative()
? 0u ? 0U
: duration.ToScale<uint64_t>(timebase); : duration.ToScale<uint64_t>(timebase);
DecoderCommand cmd; DecoderCommand cmd;

View File

@ -323,6 +323,8 @@ static const char *const sndfile_suffixes[] = {
}; };
static const char *const sndfile_mime_types[] = { static const char *const sndfile_mime_types[] = {
"audio/wav",
"audio/aiff",
"audio/x-wav", "audio/x-wav",
"audio/x-aiff", "audio/x-aiff",

View File

@ -93,7 +93,7 @@ public:
}; };
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block) PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
:compression(block.GetBlockValue("compression", 5u)) :compression(block.GetBlockValue("compression", 5U))
{ {
} }

View File

@ -105,7 +105,7 @@ PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
throw std::runtime_error("Invalid bit rate"); throw std::runtime_error("Invalid bit rate");
} }
complexity = block.GetBlockValue("complexity", 10u); complexity = block.GetBlockValue("complexity", 10U);
if (complexity > 10) if (complexity > 10)
throw std::runtime_error("Invalid complexity"); throw std::runtime_error("Invalid complexity");

View File

@ -30,11 +30,13 @@
#include <nfsc/libnfs-raw-nfs.h> #include <nfsc/libnfs-raw-nfs.h>
#endif #endif
#include <utility>
bool bool
IsFileNotFound(std::exception_ptr ep) noexcept IsFileNotFound(std::exception_ptr ep) noexcept
{ {
try { try {
std::rethrow_exception(ep); std::rethrow_exception(std::move(ep));
} catch (const std::system_error &e) { } catch (const std::system_error &e) {
return IsFileNotFound(e); return IsFileNotFound(e);
#ifdef ENABLE_CURL #ifdef ENABLE_CURL

View File

@ -112,7 +112,7 @@ input_cdio_init(EventLoop &, const ConfigBlock &block)
throw FormatRuntimeError("Unrecognized 'default_byte_order' setting: %s", throw FormatRuntimeError("Unrecognized 'default_byte_order' setting: %s",
value); value);
} }
speed = block.GetBlockValue("speed",0u); speed = block.GetBlockValue("speed",0U);
} }
struct CdioUri { struct CdioUri {

View File

@ -364,7 +364,7 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block)
http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK"); http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK");
proxy = block.GetBlockValue("proxy"); proxy = block.GetBlockValue("proxy");
proxy_port = block.GetBlockValue("proxy_port", 0u); proxy_port = block.GetBlockValue("proxy_port", 0U);
proxy_user = block.GetBlockValue("proxy_user"); proxy_user = block.GetBlockValue("proxy_user");
proxy_password = block.GetBlockValue("proxy_password"); proxy_password = block.GetBlockValue("proxy_password");
@ -409,9 +409,9 @@ CurlInputStream::InitEasy()
request = new CurlRequest(**curl_init, GetURI(), *this); request = new CurlRequest(**curl_init, GetURI(), *this);
request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases); request->SetOption(CURLOPT_HTTP200ALIASES, http_200_aliases);
request->SetOption(CURLOPT_FOLLOWLOCATION, 1l); request->SetOption(CURLOPT_FOLLOWLOCATION, 1L);
request->SetOption(CURLOPT_MAXREDIRS, 5l); request->SetOption(CURLOPT_MAXREDIRS, 5L);
request->SetOption(CURLOPT_FAILONERROR, 1l); request->SetOption(CURLOPT_FAILONERROR, 1L);
if (proxy != nullptr) if (proxy != nullptr)
request->SetOption(CURLOPT_PROXY, proxy); request->SetOption(CURLOPT_PROXY, proxy);
@ -424,8 +424,8 @@ CurlInputStream::InitEasy()
StringFormat<1024>("%s:%s", proxy_user, StringFormat<1024>("%s:%s", proxy_user,
proxy_password).c_str()); proxy_password).c_str());
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l); request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1L : 0L);
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l); request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L);
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get()); request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
} }

View File

@ -65,7 +65,7 @@ public:
} }
private: private:
void Failed(std::exception_ptr e) { void Failed(const std::exception_ptr& e) {
SetInput(std::make_unique<FailingInputStream>(GetURI(), e, SetInput(std::make_unique<FailingInputStream>(GetURI(), e,
mutex)); mutex));
} }

View File

@ -35,6 +35,7 @@
#include "Log.hxx" #include "Log.hxx"
#include <memory> #include <memory>
#include <utility>
static constexpr Domain tidal_domain("tidal"); static constexpr Domain tidal_domain("tidal");
@ -77,7 +78,7 @@ public:
} }
private: private:
void Failed(std::exception_ptr e) { void Failed(const std::exception_ptr& e) {
SetInput(std::make_unique<FailingInputStream>(GetURI(), e, SetInput(std::make_unique<FailingInputStream>(GetURI(), e,
mutex)); mutex));
} }
@ -133,7 +134,7 @@ static bool
IsInvalidSession(std::exception_ptr e) noexcept IsInvalidSession(std::exception_ptr e) noexcept
{ {
try { try {
std::rethrow_exception(e); std::rethrow_exception(std::move(e));
} catch (const TidalError &te) { } catch (const TidalError &te) {
return te.IsInvalidSession(); return te.IsInvalidSession();
} catch (...) { } catch (...) {

View File

@ -56,7 +56,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);
easy.SetOption(CURLOPT_NETRC, 1l); easy.SetOption(CURLOPT_NETRC, 1L);
easy.SetErrorBuffer(error_buffer); easy.SetErrorBuffer(error_buffer);
easy.SetNoProgress(); easy.SetNoProgress();
easy.SetNoSignal(); easy.SetNoSignal();

View File

@ -167,7 +167,7 @@ ParseObjects(ODBus::ReadMessageIter &&i,
ForEachInterface(std::move(i), [&callback](const char *path, auto &&j){ ForEachInterface(std::move(i), [&callback](const char *path, auto &&j){
Object o(path); Object o(path);
ParseObject(o, std::move(j)); ParseObject(o, std::forward<decltype(j)>(j));
if (o.IsValid()) if (o.IsValid())
callback(std::move(o)); callback(std::move(o));
}); });

View File

@ -58,7 +58,7 @@ try {
if (u.IsNull()) if (u.IsNull())
return AllocatedString<>::Duplicate(src); return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u); AllocatedArray<UChar> folded(u.size() * 2U);
UErrorCode error_code = U_ZERO_ERROR; UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(), size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),

View File

@ -37,7 +37,7 @@ ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device,
m_modelName(device.modelName), m_modelName(device.modelName),
m_rdreqcnt(200) m_rdreqcnt(200)
{ {
if (!m_modelName.compare("MediaTomb")) { if (m_modelName == "MediaTomb") {
// Readdir by 200 entries is good for most, but MediaTomb likes // Readdir by 200 entries is good for most, but MediaTomb likes
// them really big. Actually 1000 is better but I don't dare // them really big. Actually 1000 is better but I don't dare
m_rdreqcnt = 500; m_rdreqcnt = 500;

View File

@ -57,13 +57,14 @@ OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is,
bool bool
OggSeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet, OggSeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
InputStream &is) InputStream &is, bool synced)
{ {
if (!is.KnownSize()) if (!is.KnownSize())
return false; return false;
if (is.GetRest() < 65536) if (is.GetRest() < 65536)
return OggFindEOS(oy, os, packet); return (synced || oy.ExpectPageSeekIn(os)) &&
OggFindEOS(oy, os, packet);
if (!is.CheapSeeking()) if (!is.CheapSeeking())
return false; return false;

View File

@ -47,10 +47,13 @@ OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is,
* Try to find the end-of-stream (EOS) packet. Seek to the end of the * Try to find the end-of-stream (EOS) packet. Seek to the end of the
* file if necessary. * file if necessary.
* *
* @param synced is the #OggSyncState currently synced? If not, then
* we need to use ogg_sync_pageseek() instead of ogg_sync_pageout(),
* which is more expensive
* @return true if the EOS packet was found * @return true if the EOS packet was found
*/ */
bool bool
OggSeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet, OggSeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
InputStream &is); InputStream &is, bool synced=true);
#endif #endif

View File

@ -43,7 +43,7 @@ OggSyncState::ExpectPage(ogg_page &page)
if (r != 0) { if (r != 0) {
if (r > 0) { if (r > 0) {
start_offset = offset; start_offset = offset;
offset += r; offset += page.header_len + page.body_len;
} }
return r > 0; return r > 0;
} }

View File

@ -231,7 +231,7 @@ UdisksNeighborExplorer::HandleMessage(DBusConnection *, DBusMessage *message) no
dbus_message_has_signature(message, InterfacesAddedType::value)) { dbus_message_has_signature(message, InterfacesAddedType::value)) {
RecurseInterfaceDictEntry(ReadMessageIter(*message), [this](const char *path, auto &&i){ RecurseInterfaceDictEntry(ReadMessageIter(*message), [this](const char *path, auto &&i){
UDisks2::Object o(path); UDisks2::Object o(path);
UDisks2::ParseObject(o, std::move(i)); UDisks2::ParseObject(o, std::forward<decltype(i)>(i));
if (o.IsValid()) if (o.IsValid())
this->Insert(std::move(o)); this->Insert(std::move(o));
}); });

View File

@ -116,7 +116,7 @@ AudioOutputControl::GetMixer() const noexcept
return output ? output->mixer : nullptr; return output ? output->mixer : nullptr;
} }
const std::map<std::string, std::string> std::map<std::string, std::string>
AudioOutputControl::GetAttributes() const noexcept AudioOutputControl::GetAttributes() const noexcept
{ {
return output return output

View File

@ -357,7 +357,7 @@ public:
void BeginDestroy() noexcept; void BeginDestroy() noexcept;
const std::map<std::string, std::string> GetAttributes() const noexcept; std::map<std::string, std::string> GetAttributes() const noexcept;
void SetAttribute(std::string &&name, std::string &&value); void SetAttribute(std::string &&name, std::string &&value);
/** /**

View File

@ -39,7 +39,7 @@ FilteredAudioOutput::SupportsPause() const noexcept
return output->SupportsPause(); return output->SupportsPause();
} }
const std::map<std::string, std::string> std::map<std::string, std::string>
FilteredAudioOutput::GetAttributes() const noexcept FilteredAudioOutput::GetAttributes() const noexcept
{ {
return output->GetAttributes(); return output->GetAttributes();

View File

@ -170,7 +170,7 @@ public:
gcc_pure gcc_pure
bool SupportsPause() const noexcept; bool SupportsPause() const noexcept;
const std::map<std::string, std::string> GetAttributes() const noexcept; std::map<std::string, std::string> GetAttributes() const noexcept;
void SetAttribute(std::string &&name, std::string &&value); void SetAttribute(std::string &&name, std::string &&value);
/** /**

View File

@ -64,7 +64,7 @@ public:
* *
* This method must be thread-safe. * This method must be thread-safe.
*/ */
virtual const std::map<std::string, std::string> GetAttributes() const noexcept { virtual std::map<std::string, std::string> GetAttributes() const noexcept {
return {}; return {};
} }

View File

@ -228,7 +228,7 @@ public:
} }
private: private:
const std::map<std::string, std::string> GetAttributes() const noexcept override; std::map<std::string, std::string> GetAttributes() const noexcept override;
void SetAttribute(std::string &&name, std::string &&value) override; void SetAttribute(std::string &&name, std::string &&value) override;
void Enable() override; void Enable() override;
@ -404,7 +404,7 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
#endif #endif
buffer_time(block.GetPositiveValue("buffer_time", buffer_time(block.GetPositiveValue("buffer_time",
MPD_ALSA_BUFFER_TIME_US)), MPD_ALSA_BUFFER_TIME_US)),
period_time(block.GetPositiveValue("period_time", 0u)) period_time(block.GetPositiveValue("period_time", 0U))
{ {
#ifdef SND_PCM_NO_AUTO_RESAMPLE #ifdef SND_PCM_NO_AUTO_RESAMPLE
if (!block.GetBlockValue("auto_resample", true)) if (!block.GetBlockValue("auto_resample", true))
@ -427,7 +427,7 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string); allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string);
} }
const std::map<std::string, std::string> std::map<std::string, std::string>
AlsaOutput::GetAttributes() const noexcept AlsaOutput::GetAttributes() const noexcept
{ {
const std::lock_guard<Mutex> lock(attributes_mutex); const std::lock_guard<Mutex> lock(attributes_mutex);

View File

@ -101,7 +101,7 @@ MakeAoError()
AoOutput::AoOutput(const ConfigBlock &block) AoOutput::AoOutput(const ConfigBlock &block)
:AudioOutput(0), :AudioOutput(0),
write_size(block.GetPositiveValue("write_size", 1024u)) write_size(block.GetPositiveValue("write_size", 1024U))
{ {
const char *value = block.GetBlockValue("driver", "default"); const char *value = block.GetBlockValue("driver", "default");
if (StringIsEqual(value, "default")) if (StringIsEqual(value, "default"))

View File

@ -247,7 +247,7 @@ JackOutput::JackOutput(const ConfigBlock &block)
num_source_ports, num_destination_ports, num_source_ports, num_destination_ports,
block.line); block.line);
ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768u); ringbuffer_size = block.GetPositiveValue("ringbuffer_size", 32768U);
} }
inline jack_nframes_t inline jack_nframes_t

View File

@ -99,7 +99,7 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
{ {
const char *host = require_block_string(block, "host"); const char *host = require_block_string(block, "host");
const char *mount = require_block_string(block, "mount"); const char *mount = require_block_string(block, "mount");
unsigned port = block.GetBlockValue("port", 0u); unsigned port = block.GetBlockValue("port", 0U);
if (port == 0) if (port == 0)
throw std::runtime_error("shout port must be configured"); throw std::runtime_error("shout port must be configured");

View File

@ -50,11 +50,11 @@ HttpdOutput::HttpdOutput(EventLoop &_loop, const ConfigBlock &block)
genre = block.GetBlockValue("genre", "Set genre in config"); genre = block.GetBlockValue("genre", "Set genre in config");
website = block.GetBlockValue("website", "Set website in config"); website = block.GetBlockValue("website", "Set website in config");
clients_max = block.GetBlockValue("max_clients", 0u); clients_max = block.GetBlockValue("max_clients", 0U);
/* set up bind_to_address */ /* set up bind_to_address */
ServerSocketAddGeneric(*this, block.GetBlockValue("bind_to_address"), block.GetBlockValue("port", 8000u)); ServerSocketAddGeneric(*this, block.GetBlockValue("bind_to_address"), block.GetBlockValue("port", 8000U));
/* determine content type */ /* determine content type */
content_type = prepared_encoder->GetMimeType(); content_type = prepared_encoder->GetMimeType();

View File

@ -94,5 +94,5 @@ DsdToDopConverter::Convert(ConstBuffer<uint8_t> src) noexcept
{ {
using namespace std::placeholders; using namespace std::placeholders;
return rest_buffer.Process<uint32_t>(buffer, src, 2 * channels, return rest_buffer.Process<uint32_t>(buffer, src, 2 * channels,
std::bind(DsdToDop, _1, _2, _3, channels)); [=](auto && arg1, auto && arg2, auto && arg3) { return DsdToDop(arg1, arg2, arg3, channels); });
} }

View File

@ -65,5 +65,5 @@ Dsd16Converter::Convert(ConstBuffer<uint8_t> src) noexcept
{ {
using namespace std::placeholders; using namespace std::placeholders;
return rest_buffer.Process<uint16_t>(buffer, src, channels, return rest_buffer.Process<uint16_t>(buffer, src, channels,
std::bind(Dsd8To16, _1, _2, _3, channels)); [=](auto && arg1, auto && arg2, auto && arg3) { return Dsd8To16(arg1, arg2, arg3, channels); });
} }

View File

@ -67,5 +67,5 @@ Dsd32Converter::Convert(ConstBuffer<uint8_t> src) noexcept
{ {
using namespace std::placeholders; using namespace std::placeholders;
return rest_buffer.Process<uint32_t>(buffer, src, channels, return rest_buffer.Process<uint32_t>(buffer, src, channels,
std::bind(Dsd8To32, _1, _2, _3, channels)); [=](auto && arg1, auto && arg2, auto && arg3) { return Dsd8To32(arg1, arg2, arg3, channels); });
} }

View File

@ -261,8 +261,8 @@ public:
CommonExpatParser(ExpatNamespaceSeparator{'|'}) CommonExpatParser(ExpatNamespaceSeparator{'|'})
{ {
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND"); request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
request.SetOption(CURLOPT_FOLLOWLOCATION, 1l); request.SetOption(CURLOPT_FOLLOWLOCATION, 1L);
request.SetOption(CURLOPT_MAXREDIRS, 1l); request.SetOption(CURLOPT_MAXREDIRS, 1L);
request_headers.Append(StringFormat<40>("depth: %u", depth)); request_headers.Append(StringFormat<40>("depth: %u", depth));

View File

@ -38,7 +38,7 @@ struct ApeFooter {
}; };
bool bool
tag_ape_scan(InputStream &is, ApeTagCallback callback) tag_ape_scan(InputStream &is, const ApeTagCallback& callback)
try { try {
std::unique_lock<Mutex> lock(is.mutex); std::unique_lock<Mutex> lock(is.mutex);

View File

@ -37,6 +37,6 @@ typedef std::function<bool(unsigned long flags, const char *key,
* present * present
*/ */
bool bool
tag_ape_scan(InputStream &is, ApeTagCallback callback); tag_ape_scan(InputStream &is, const ApeTagCallback& callback);
#endif #endif

View File

@ -54,14 +54,14 @@ tag_ape_import_item(unsigned long flags,
return false; return false;
if (handler.WantPair()) if (handler.WantPair())
for (const auto &i : IterableSplitString(value, '\0')) for (const auto i : IterableSplitString(value, '\0'))
handler.OnPair(key, i); handler.OnPair(key, i);
TagType type = tag_ape_name_parse(key); TagType type = tag_ape_name_parse(key);
if (type == TAG_NUM_OF_ITEM_TYPES) if (type == TAG_NUM_OF_ITEM_TYPES)
return false; return false;
for (const auto &i : IterableSplitString(value, '\0')) for (const auto i : IterableSplitString(value, '\0'))
handler.OnTag(type, i); handler.OnTag(type, i);
return true; return true;

View File

@ -204,7 +204,7 @@ struct CheckSequenceUTF8 {
}; };
template<> template<>
struct CheckSequenceUTF8<0u> { struct CheckSequenceUTF8<0U> {
constexpr bool operator()(gcc_unused const char *p) const noexcept { constexpr bool operator()(gcc_unused const char *p) const noexcept {
return true; return true;
} }
@ -217,7 +217,7 @@ InnerSequenceLengthUTF8(const char *p) noexcept
{ {
return CheckSequenceUTF8<L>()(p) return CheckSequenceUTF8<L>()(p)
? L + 1 ? L + 1
: 0u; : 0U;
} }
size_t size_t

View File

@ -28,15 +28,15 @@
void void
DumpDecoderClient::Ready(const AudioFormat audio_format, DumpDecoderClient::Ready(const AudioFormat audio_format,
gcc_unused bool seekable, bool seekable,
SignedSongTime duration) noexcept SignedSongTime duration) noexcept
{ {
assert(!initialized); assert(!initialized);
assert(audio_format.IsValid()); assert(audio_format.IsValid());
fprintf(stderr, "audio_format=%s duration=%f\n", fprintf(stderr, "audio_format=%s duration=%f seekable=%d\n",
ToString(audio_format).c_str(), ToString(audio_format).c_str(),
duration.ToDoubleS()); duration.ToDoubleS(), seekable);
initialized = true; initialized = true;
} }
@ -101,7 +101,7 @@ DumpDecoderClient::SubmitData(gcc_unused InputStream *is,
} }
gcc_unused ssize_t nbytes = write(STDOUT_FILENO, data, datalen); gcc_unused ssize_t nbytes = write(STDOUT_FILENO, data, datalen);
return DecoderCommand::NONE; return GetCommand();
} }
DecoderCommand DecoderCommand
@ -113,7 +113,7 @@ DumpDecoderClient::SubmitTag(gcc_unused InputStream *is,
for (const auto &i : tag) for (const auto &i : tag)
fprintf(stderr, " %s=%s\n", tag_item_names[i.type], i.value); fprintf(stderr, " %s=%s\n", tag_item_names[i.type], i.value);
return DecoderCommand::NONE; return GetCommand();
} }
static void static void

View File

@ -27,7 +27,7 @@
* A #DecoderClient implementation which dumps metadata to stderr and * A #DecoderClient implementation which dumps metadata to stderr and
* decoded data to stdout. * decoded data to stdout.
*/ */
class DumpDecoderClient final : public DecoderClient { class DumpDecoderClient : public DecoderClient {
bool initialized = false; bool initialized = false;
uint16_t prev_kbit_rate = 0; uint16_t prev_kbit_rate = 0;

View File

@ -6,10 +6,14 @@ if compiler.get_id() == 'gcc'
gtest_compile_args += [ gtest_compile_args += [
'-Wno-suggest-attribute=format', '-Wno-suggest-attribute=format',
'-Wno-suggest-attribute=noreturn', '-Wno-suggest-attribute=noreturn',
'-Wno-missing-declarations', ]
endif
# needed on Jessie for gtest's IsNullLiteralHelper if compiler.get_id() == 'clang' and compiler.version().version_compare('>=9')
'-Wno-conversion-null', gtest_compile_args += [
# work around clang warning caused by GTest's wrong "-lpthread"
# compiler flag
'-Wno-unused-command-line-argument',
] ]
endif endif

View File

@ -47,16 +47,20 @@ struct CommandLine {
Path config_path = nullptr; Path config_path = nullptr;
bool verbose = false; bool verbose = false;
SongTime seek_where{};
}; };
enum Option { enum Option {
OPTION_CONFIG, OPTION_CONFIG,
OPTION_VERBOSE, OPTION_VERBOSE,
OPTION_SEEK,
}; };
static constexpr OptionDef option_defs[] = { static constexpr OptionDef option_defs[] = {
{"config", 0, true, "Load a MPD configuration file"}, {"config", 0, true, "Load a MPD configuration file"},
{"verbose", 'v', false, "Verbose logging"}, {"verbose", 'v', false, "Verbose logging"},
{"seek", 0, true, "Seek to this position"},
}; };
static CommandLine static CommandLine
@ -74,6 +78,10 @@ ParseCommandLine(int argc, char **argv)
case OPTION_VERBOSE: case OPTION_VERBOSE:
c.verbose = true; c.verbose = true;
break; break;
case OPTION_SEEK:
c.seek_where = SongTime::FromS(strtod(o.value, nullptr));
break;
} }
} }
@ -102,6 +110,85 @@ public:
} }
}; };
class MyDecoderClient final : public DumpDecoderClient {
SongTime seek_where;
unsigned sample_rate;
bool seekable, seek_error = false;
public:
explicit MyDecoderClient(SongTime _seek_where) noexcept
:seek_where(_seek_where) {}
void Finish() {
if (!IsInitialized())
throw "Unrecognized file";
if (seek_error)
throw "Seek error";
if (seek_where != SongTime{}) {
if (!seekable)
throw "Not seekable";
throw "Did not seek";
}
}
/* virtual methods from DecoderClient */
void Ready(AudioFormat audio_format,
bool _seekable, SignedSongTime duration) noexcept override {
assert(!IsInitialized());
DumpDecoderClient::Ready(audio_format, _seekable, duration);
sample_rate = audio_format.sample_rate;
seekable = _seekable;
}
DecoderCommand GetCommand() noexcept override {
assert(IsInitialized());
if (seek_where != SongTime{}) {
if (!seekable)
return DecoderCommand::STOP;
return DecoderCommand::SEEK;
} else if (seek_error)
return DecoderCommand::STOP;
else
return DumpDecoderClient::GetCommand();
}
void CommandFinished() noexcept override {
assert(!seek_error);
if (seek_where != SongTime{})
seek_where = {};
else
DumpDecoderClient::CommandFinished();
}
SongTime GetSeekTime() noexcept override {
assert(seek_where != SongTime{});
return seek_where;
}
uint64_t GetSeekFrame() noexcept override {
assert(seek_where != SongTime{});
return GetSeekTime().ToScale<uint64_t>(sample_rate);
}
void SeekError() noexcept override {
assert(seek_where != SongTime{});
seek_error = true;
seek_where = {};
}
};
int main(int argc, char **argv) int main(int argc, char **argv)
try { try {
const auto c = ParseCommandLine(argc, argv); const auto c = ParseCommandLine(argc, argv);
@ -115,7 +202,7 @@ try {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
DumpDecoderClient client; MyDecoderClient client(c.seek_where);
if (plugin->file_decode != nullptr) { if (plugin->file_decode != nullptr) {
try { try {
plugin->FileDecode(client, Path::FromFS(c.uri)); plugin->FileDecode(client, Path::FromFS(c.uri));
@ -132,10 +219,7 @@ try {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!client.IsInitialized()) { client.Finish();
fprintf(stderr, "Decoding failed\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} catch (...) { } catch (...) {