tag/Handler: convert to class with virtual methods

This commit is contained in:
Max Kellermann 2018-07-05 19:07:05 +02:00
parent 09d4176210
commit 3d3a1232b1
49 changed files with 414 additions and 496 deletions

View File

@ -25,7 +25,7 @@
bool
tag_archive_scan(ArchiveFile &archive, const char *path_utf8,
const TagHandler &handler, void *handler_ctx) noexcept
TagHandler &handler) noexcept
try {
Mutex mutex;
@ -33,7 +33,7 @@ try {
if (!is)
return false;
return tag_stream_scan(*is, handler, handler_ctx);
return tag_stream_scan(*is, handler);
} catch (const std::exception &e) {
return false;
}

View File

@ -23,7 +23,7 @@
#include "check.h"
class ArchiveFile;
struct TagHandler;
class TagHandler;
class TagBuilder;
/**
@ -36,7 +36,7 @@ class TagBuilder;
*/
bool
tag_archive_scan(ArchiveFile &archive, const char *path_utf8,
const TagHandler &handler, void *handler_ctx) noexcept;
TagHandler &handler) noexcept;
/**
* Scan the tags of a song file inside an archive. Invokes matching

View File

@ -36,21 +36,20 @@ class TagFileScan {
const Path path_fs;
const char *const suffix;
const TagHandler &handler;
void *handler_ctx;
TagHandler &handler;
Mutex mutex;
InputStreamPtr is;
public:
TagFileScan(Path _path_fs, const char *_suffix,
const TagHandler &_handler, void *_handler_ctx) noexcept
TagHandler &_handler) noexcept
:path_fs(_path_fs), suffix(_suffix),
handler(_handler), handler_ctx(_handler_ctx) ,
handler(_handler),
is(nullptr) {}
bool ScanFile(const DecoderPlugin &plugin) noexcept {
return plugin.ScanFile(path_fs, handler, handler_ctx);
return plugin.ScanFile(path_fs, handler);
}
bool ScanStream(const DecoderPlugin &plugin) noexcept {
@ -72,7 +71,7 @@ public:
}
/* now try the stream_tag() method */
return plugin.ScanStream(*is, handler, handler_ctx);
return plugin.ScanStream(*is, handler);
}
bool Scan(const DecoderPlugin &plugin) noexcept {
@ -82,8 +81,7 @@ public:
};
bool
tag_file_scan(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
tag_file_scan(Path path_fs, TagHandler &handler) noexcept
{
assert(!path_fs.IsNull());
@ -95,7 +93,7 @@ tag_file_scan(Path path_fs,
const auto suffix_utf8 = Path::FromFS(suffix).ToUTF8();
TagFileScan tfs(path_fs, suffix_utf8.c_str(), handler, handler_ctx);
TagFileScan tfs(path_fs, suffix_utf8.c_str(), handler);
return decoder_plugins_try([&](const DecoderPlugin &plugin){
return tfs.Scan(plugin);
});
@ -104,11 +102,13 @@ tag_file_scan(Path path_fs,
bool
tag_file_scan(Path path, TagBuilder &builder) noexcept
{
if (!tag_file_scan(path, full_tag_handler, &builder))
FullTagHandler h(builder);
if (!tag_file_scan(path, h))
return false;
if (builder.empty())
ScanGenericTags(path, full_tag_handler, &builder);
ScanGenericTags(path, h);
return true;
}

View File

@ -23,7 +23,7 @@
#include "check.h"
class Path;
struct TagHandler;
class TagHandler;
class TagBuilder;
/**
@ -34,8 +34,7 @@ class TagBuilder;
* found)
*/
bool
tag_file_scan(Path path,
const TagHandler &handler, void *handler_ctx) noexcept;
tag_file_scan(Path path, TagHandler &handler) noexcept;
/**
* Scan the tags of a song file. Invokes matching decoder plugins,

View File

@ -46,7 +46,7 @@ CheckDecoderPlugin(const DecoderPlugin &plugin,
}
bool
tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx)
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept
{
assert(is.IsReady());
@ -62,44 +62,46 @@ tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx)
mime = (mime_base = GetMimeTypeBase(mime)).c_str();
return decoder_plugins_try([suffix, mime, &is,
&handler, ctx](const DecoderPlugin &plugin){
&handler](const DecoderPlugin &plugin){
try {
is.LockRewind();
} catch (...) {
}
return CheckDecoderPlugin(plugin, suffix, mime) &&
plugin.ScanStream(is, handler, ctx);
plugin.ScanStream(is, handler);
});
}
bool
tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx)
tag_stream_scan(const char *uri, TagHandler &handler) noexcept
try {
Mutex mutex;
auto is = InputStream::OpenReady(uri, mutex);
return tag_stream_scan(*is, handler, ctx);
return tag_stream_scan(*is, handler);
} catch (const std::exception &e) {
return false;
}
bool
tag_stream_scan(InputStream &is, TagBuilder &builder)
tag_stream_scan(InputStream &is, TagBuilder &builder) noexcept
{
assert(is.IsReady());
if (!tag_stream_scan(is, full_tag_handler, &builder))
FullTagHandler h(builder);
if (!tag_stream_scan(is, h))
return false;
if (builder.empty())
ScanGenericTags(is, full_tag_handler, &builder);
ScanGenericTags(is, h);
return true;
}
bool
tag_stream_scan(const char *uri, TagBuilder &builder)
tag_stream_scan(const char *uri, TagBuilder &builder) noexcept
try {
Mutex mutex;

View File

@ -23,7 +23,7 @@
#include "check.h"
class InputStream;
struct TagHandler;
class TagHandler;
class TagBuilder;
/**
@ -34,10 +34,10 @@ class TagBuilder;
* found)
*/
bool
tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx);
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept;
bool
tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx);
tag_stream_scan(const char *uri, TagHandler &handler) noexcept;
/**
* Scan the tags of an #InputStream. Invokes matching decoder
@ -48,9 +48,9 @@ tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx);
* found)
*/
bool
tag_stream_scan(InputStream &is, TagBuilder &builder);
tag_stream_scan(InputStream &is, TagBuilder &builder) noexcept;
bool
tag_stream_scan(const char *uri, TagBuilder &builder);
tag_stream_scan(const char *uri, TagBuilder &builder) noexcept;
#endif

View File

@ -137,25 +137,24 @@ IsValidValue(const char *p) noexcept
return true;
}
static void
print_pair(const char *key, const char *value, void *ctx)
{
auto &r = *(Response *)ctx;
class PrintCommentHandler final : public NullTagHandler {
Response &response;
if (IsValidName(key) && IsValidValue(value))
r.Format("%s: %s\n", key, value);
}
public:
explicit PrintCommentHandler(Response &_response) noexcept
:NullTagHandler(WANT_PAIR), response(_response) {}
static constexpr TagHandler print_comment_handler = {
nullptr,
nullptr,
print_pair,
void OnPair(const char *key, const char *value) noexcept override {
if (IsValidName(key) && IsValidValue(value))
response.Format("%s: %s\n", key, value);
}
};
static CommandResult
read_stream_comments(Response &r, const char *uri)
{
if (!tag_stream_scan(uri, print_comment_handler, &r)) {
PrintCommentHandler h(r);
if (!tag_stream_scan(uri, h)) {
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
return CommandResult::ERROR;
}
@ -167,12 +166,13 @@ read_stream_comments(Response &r, const char *uri)
static CommandResult
read_file_comments(Response &r, const Path path_fs)
{
if (!tag_file_scan(path_fs, print_comment_handler, &r)) {
PrintCommentHandler h(r);
if (!tag_file_scan(path_fs, h)) {
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
return CommandResult::ERROR;
}
ScanGenericTags(path_fs, print_comment_handler, &r);
ScanGenericTags(path_fs, h);
return CommandResult::OK;

View File

@ -93,15 +93,6 @@ handle_kill(gcc_unused Client &client, gcc_unused Request request,
return CommandResult::KILL;
}
static void
print_tag(TagType type, const char *value, void *ctx)
{
auto &r = *(Response *)ctx;
if (r.GetClient().tag_mask.Test(type))
tag_print(r, type, value);
}
CommandResult
handle_listfiles(Client &client, Request args, Response &r)
{
@ -149,16 +140,24 @@ handle_listfiles(Client &client, Request args, Response &r)
gcc_unreachable();
}
static constexpr TagHandler print_tag_handler = {
nullptr,
print_tag,
nullptr,
class PrintTagHandler final : public NullTagHandler {
Response &response;
public:
explicit PrintTagHandler(Response &_response) noexcept
:NullTagHandler(WANT_TAG), response(_response) {}
void OnTag(TagType type, const char *value) noexcept override {
if (response.GetClient().tag_mask.Test(type))
tag_print(response, type, value);
}
};
static CommandResult
handle_lsinfo_absolute(Response &r, const char *uri)
{
if (!tag_stream_scan(uri, print_tag_handler, &r)) {
PrintTagHandler h(r);
if (!tag_stream_scan(uri, h)) {
r.Error(ACK_ERROR_NO_EXIST, "No such file");
return CommandResult::ERROR;
}

View File

@ -26,7 +26,7 @@
struct ConfigBlock;
class InputStream;
struct TagHandler;
class TagHandler;
class Path;
class DecoderClient;
class DetachedSong;
@ -71,18 +71,14 @@ struct DecoderPlugin {
*
* @return false if the operation has failed
*/
bool (*scan_file)(Path path_fs,
const TagHandler &handler,
void *handler_ctx) noexcept;
bool (*scan_file)(Path path_fs, TagHandler &handler) noexcept;
/**
* Scan metadata of a file.
*
* @return false if the operation has failed
*/
bool (*scan_stream)(InputStream &is,
const TagHandler &handler,
void *handler_ctx) noexcept;
bool (*scan_stream)(InputStream &is, TagHandler &handler) noexcept;
/**
* @brief Return a "virtual" filename for subtracks in
@ -139,20 +135,18 @@ struct DecoderPlugin {
* Read the tag of a file.
*/
template<typename P>
bool ScanFile(P path_fs,
const TagHandler &handler, void *handler_ctx) const noexcept {
bool ScanFile(P path_fs, TagHandler &handler) const noexcept {
return scan_file != nullptr
? scan_file(path_fs, handler, handler_ctx)
? scan_file(path_fs, handler)
: false;
}
/**
* Read the tag of a stream.
*/
bool ScanStream(InputStream &is,
const TagHandler &handler, void *handler_ctx) const noexcept {
bool ScanStream(InputStream &is, TagHandler &handler) const noexcept {
return scan_stream != nullptr
? scan_stream(is, handler, handler_ctx)
? scan_stream(is, handler)
: false;
}

View File

@ -83,16 +83,14 @@ adplug_file_decode(DecoderClient &client, Path path_fs)
static void
adplug_scan_tag(TagType type, const std::string &value,
const TagHandler &handler, void *handler_ctx) noexcept
TagHandler &handler) noexcept
{
if (!value.empty())
tag_handler_invoke_tag(handler, handler_ctx,
type, value.c_str());
handler.OnTag(type, value.c_str());
}
static bool
adplug_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
adplug_scan_file(Path path_fs, TagHandler &handler) noexcept
{
CEmuopl opl(sample_rate, true, true);
opl.init();
@ -101,16 +99,15 @@ adplug_scan_file(Path path_fs,
if (player == nullptr)
return false;
tag_handler_invoke_duration(handler, handler_ctx,
SongTime::FromMS(player->songlength()));
handler.OnDuration(SongTime::FromMS(player->songlength()));
if (handler.tag != nullptr) {
if (handler.WantTag()) {
adplug_scan_tag(TAG_TITLE, player->gettitle(),
handler, handler_ctx);
handler);
adplug_scan_tag(TAG_ARTIST, player->getauthor(),
handler, handler_ctx);
handler);
adplug_scan_tag(TAG_COMMENT, player->getdesc(),
handler, handler_ctx);
handler);
}
delete player;

View File

@ -256,14 +256,13 @@ audiofile_get_duration(InputStream &is) noexcept
}
static bool
audiofile_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
audiofile_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
const auto duration = audiofile_get_duration(is);
if (duration.IsNegative())
return false;
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
handler.OnDuration(SongTime(duration));
return true;
}

View File

@ -115,9 +115,8 @@ dsdlib_valid_freq(uint32_t samplefreq) noexcept
#ifdef ENABLE_ID3TAG
void
dsdlib_tag_id3(InputStream &is,
const TagHandler &handler,
void *handler_ctx, offset_type tagoffset)
dsdlib_tag_id3(InputStream &is, TagHandler &handler,
offset_type tagoffset)
{
if (tagoffset == 0 || !is.KnownSize())
return;
@ -150,7 +149,7 @@ dsdlib_tag_id3(InputStream &is,
if (id3_tag == nullptr)
return;
scan_id3_tag(id3_tag, handler, handler_ctx);
scan_id3_tag(id3_tag, handler);
id3_tag_delete(id3_tag);
return;

View File

@ -26,7 +26,7 @@
#include <stdint.h>
struct TagHandler;
class TagHandler;
class DecoderClient;
class InputStream;
@ -79,8 +79,7 @@ dsdlib_valid_freq(uint32_t samplefreq) noexcept;
* DSF and DSDIFF files are imported
*/
void
dsdlib_tag_id3(InputStream &is,
const TagHandler &handler,
void *handler_ctx, offset_type tagoffset);
dsdlib_tag_id3(InputStream &is, TagHandler &handler,
offset_type tagoffset);
#endif

View File

@ -186,8 +186,8 @@ dsdiff_read_prop(DecoderClient *client, InputStream &is,
static void
dsdiff_handle_native_tag(InputStream &is,
const TagHandler &handler,
void *handler_ctx, offset_type tagoffset,
TagHandler &handler,
offset_type tagoffset,
TagType type)
{
if (!dsdlib_skip_to(nullptr, is, tagoffset))
@ -212,7 +212,7 @@ dsdiff_handle_native_tag(InputStream &is,
return;
string[length] = '\0';
tag_handler_invoke_tag(handler, handler_ctx, type, label);
handler.OnTag(type, label);
return;
}
@ -228,8 +228,7 @@ static bool
dsdiff_read_metadata_extra(DecoderClient *client, InputStream &is,
DsdiffMetaData *metadata,
DsdiffChunkHeader *chunk_header,
const TagHandler &handler,
void *handler_ctx)
TagHandler &handler)
{
/* skip from DSD data to next chunk header */
@ -286,17 +285,17 @@ dsdiff_read_metadata_extra(DecoderClient *client, InputStream &is,
if (id3_offset != 0) {
/* a ID3 tag has preference over the other tags, do not process
other tags if we have one */
dsdlib_tag_id3(is, handler, handler_ctx, id3_offset);
dsdlib_tag_id3(is, handler, id3_offset);
return true;
}
#endif
if (artist_offset != 0)
dsdiff_handle_native_tag(is, handler, handler_ctx,
dsdiff_handle_native_tag(is, handler,
artist_offset, TAG_ARTIST);
if (title_offset != 0)
dsdiff_handle_native_tag(is, handler, handler_ctx,
dsdiff_handle_native_tag(is, handler,
title_offset, TAG_TITLE);
return true;
}
@ -449,9 +448,7 @@ dsdiff_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
dsdiff_scan_stream(InputStream &is,
gcc_unused const TagHandler &handler,
gcc_unused void *handler_ctx) noexcept
dsdiff_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
DsdiffMetaData metadata;
DsdiffChunkHeader chunk_header;
@ -469,11 +466,11 @@ dsdiff_scan_stream(InputStream &is,
uint64_t n_frames = metadata.chunk_size / metadata.channels;
auto songtime = SongTime::FromScale<uint64_t>(n_frames,
sample_rate);
tag_handler_invoke_duration(handler, handler_ctx, songtime);
handler.OnDuration(songtime);
/* Read additional metadata and created tags if available */
dsdiff_read_metadata_extra(nullptr, is, &metadata, &chunk_header,
handler, handler_ctx);
handler);
return true;
}

View File

@ -325,9 +325,7 @@ dsf_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
dsf_scan_stream(InputStream &is,
gcc_unused const TagHandler &handler,
gcc_unused void *handler_ctx) noexcept
dsf_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
/* check DSF metadata */
DsfMetaData metadata;
@ -342,11 +340,11 @@ dsf_scan_stream(InputStream &is,
const auto n_blocks = metadata.n_blocks;
auto songtime = SongTime::FromScale<uint64_t>(n_blocks * DSF_BLOCK_SIZE,
sample_rate);
tag_handler_invoke_duration(handler, handler_ctx, songtime);
handler.OnDuration(songtime);
#ifdef ENABLE_ID3TAG
/* Add available tags from the ID3 tag */
dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
dsdlib_tag_id3(is, handler, metadata.id3_offset);
#endif
return true;
}

View File

@ -414,16 +414,14 @@ faad_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
faad_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
faad_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
auto result = faad_get_file_time(is);
if (!result.first)
return false;
if (!result.second.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx,
SongTime(result.second));
handler.OnDuration(SongTime(result.second));
return true;
}

View File

@ -572,21 +572,20 @@ FfmpegParseMetaData(DecoderClient &client,
}
static void
FfmpegScanMetadata(const AVStream &stream,
const TagHandler &handler, void *handler_ctx)
FfmpegScanMetadata(const AVStream &stream, TagHandler &handler) noexcept
{
FfmpegScanDictionary(stream.metadata, handler, handler_ctx);
FfmpegScanDictionary(stream.metadata, handler);
}
static void
FfmpegScanMetadata(const AVFormatContext &format_context, int audio_stream,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
assert(audio_stream >= 0);
FfmpegScanDictionary(format_context.metadata, handler, handler_ctx);
FfmpegScanDictionary(format_context.metadata, handler);
FfmpegScanMetadata(*format_context.streams[audio_stream],
handler, handler_ctx);
handler);
}
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
@ -595,8 +594,8 @@ static void
FfmpegScanTag(const AVFormatContext &format_context, int audio_stream,
TagBuilder &tag)
{
FfmpegScanMetadata(format_context, audio_stream,
full_tag_handler, &tag);
FullTagHandler h(tag);
FfmpegScanMetadata(format_context, audio_stream, h);
}
/**
@ -828,7 +827,7 @@ ffmpeg_decode(DecoderClient &client, InputStream &input)
static bool
FfmpegScanStream(AVFormatContext &format_context,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const int find_result =
avformat_find_stream_info(&format_context, nullptr);
@ -841,22 +840,19 @@ FfmpegScanStream(AVFormatContext &format_context,
const AVStream &stream = *format_context.streams[audio_stream];
if (stream.duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(stream.duration,
stream.time_base));
handler.OnDuration(FromFfmpegTime(stream.duration,
stream.time_base));
else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(format_context.duration,
AV_TIME_BASE_Q));
handler.OnDuration(FromFfmpegTime(format_context.duration,
AV_TIME_BASE_Q));
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);
FfmpegScanMetadata(format_context, audio_stream, handler);
return true;
}
static bool
ffmpeg_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
ffmpeg_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
AVInputFormat *input_format = ffmpeg_probe(nullptr, is);
if (input_format == nullptr)
@ -877,7 +873,7 @@ ffmpeg_scan_stream(InputStream &is,
avformat_close_input(&f);
};
return FfmpegScanStream(*f, handler, handler_ctx);
return FfmpegScanStream(*f, handler);
}
/**

View File

@ -43,46 +43,42 @@ static constexpr struct tag_table ffmpeg_tags[] = {
static void
FfmpegScanTag(TagType type,
AVDictionary *m, const char *name,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
AVDictionaryEntry *mt = nullptr;
while ((mt = av_dict_get(m, name, mt, 0)) != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
type, mt->value);
handler.OnTag(type, mt->value);
}
static void
FfmpegScanPairs(AVDictionary *dict,
const TagHandler &handler, void *handler_ctx)
FfmpegScanPairs(AVDictionary *dict, TagHandler &handler) noexcept
{
AVDictionaryEntry *i = nullptr;
while ((i = av_dict_get(dict, "", i, AV_DICT_IGNORE_SUFFIX)) != nullptr)
tag_handler_invoke_pair(handler, handler_ctx,
i->key, i->value);
handler.OnPair(i->key, i->value);
}
void
FfmpegScanDictionary(AVDictionary *dict,
const TagHandler &handler, void *handler_ctx)
FfmpegScanDictionary(AVDictionary *dict, TagHandler &handler) noexcept
{
if (handler.tag != nullptr) {
if (handler.WantTag()) {
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
FfmpegScanTag(TagType(i), dict, tag_item_names[i],
handler, handler_ctx);
handler);
for (const struct tag_table *i = ffmpeg_tags;
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
handler);
for (const struct tag_table *i = musicbrainz_txxx_tags;
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
handler);
}
if (handler.pair != nullptr)
FfmpegScanPairs(dict, handler, handler_ctx);
if (handler.WantPair())
FfmpegScanPairs(dict, handler);
}

View File

@ -21,10 +21,9 @@
#define MPD_FFMPEG_METADATA_HXX
struct AVDictionary;
struct TagHandler;
class TagHandler;
void
FfmpegScanDictionary(AVDictionary *dict,
const TagHandler &handler, void *handler_ctx);
FfmpegScanDictionary(AVDictionary *dict, TagHandler &handler) noexcept;
#endif

View File

@ -69,8 +69,7 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
}
static bool
flac_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
flac_scan_file(Path path_fs, TagHandler &handler) noexcept
{
FlacMetadataChain chain;
if (!chain.Read(NarrowPath(path_fs))) {
@ -80,13 +79,12 @@ flac_scan_file(Path path_fs,
return false;
}
chain.Scan(handler, handler_ctx);
chain.Scan(handler);
return true;
}
static bool
flac_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
flac_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
FlacMetadataChain chain;
if (!chain.Read(is)) {
@ -96,7 +94,7 @@ flac_scan_stream(InputStream &is,
return false;
}
chain.Scan(handler, handler_ctx);
chain.Scan(handler);
return true;
}
@ -315,8 +313,7 @@ oggflac_init(gcc_unused const ConfigBlock &block)
}
static bool
oggflac_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
oggflac_scan_file(Path path_fs, TagHandler &handler) noexcept
{
FlacMetadataChain chain;
if (!chain.ReadOgg(NarrowPath(path_fs))) {
@ -326,13 +323,12 @@ oggflac_scan_file(Path path_fs,
return false;
}
chain.Scan(handler, handler_ctx);
chain.Scan(handler);
return true;
}
static bool
oggflac_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
oggflac_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
FlacMetadataChain chain;
if (!chain.ReadOgg(is)) {
@ -342,7 +338,7 @@ oggflac_scan_stream(InputStream &is,
return false;
}
chain.Scan(handler, handler_ctx);
chain.Scan(handler);
return true;
}

View File

@ -79,11 +79,11 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static bool
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, TagType tag_type,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const char *value = flac_comment_value(entry, name);
if (value != nullptr) {
tag_handler_invoke_tag(handler, handler_ctx, tag_type, value);
handler.OnTag(tag_type, value);
return true;
}
@ -92,36 +92,33 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static void
flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
if (handler.pair != nullptr) {
if (handler.WantPair()) {
const char *comment = (const char *)entry->entry;
const DivideString split(comment, '=');
if (split.IsDefined() && !split.empty())
tag_handler_invoke_pair(handler, handler_ctx,
split.GetFirst(),
split.GetSecond());
handler.OnPair(split.GetFirst(), split.GetSecond());
}
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
if (flac_copy_comment(entry, i->name, i->type,
handler, handler_ctx))
if (flac_copy_comment(entry, i->name, i->type, handler))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(entry,
tag_item_names[i], (TagType)i,
handler, handler_ctx))
handler))
return;
}
static void
flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
for (unsigned i = 0; i < comment->num_comments; ++i)
flac_scan_comment(&comment->comments[i],
handler, handler_ctx);
handler);
}
gcc_pure
@ -136,18 +133,17 @@ flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info) noexcept
void
flac_scan_metadata(const FLAC__StreamMetadata *block,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
flac_scan_comments(&block->data.vorbis_comment,
handler, handler_ctx);
handler);
break;
case FLAC__METADATA_TYPE_STREAMINFO:
if (block->data.stream_info.sample_rate > 0)
tag_handler_invoke_duration(handler, handler_ctx,
flac_duration(&block->data.stream_info));
handler.OnDuration(flac_duration(&block->data.stream_info));
break;
default:
@ -159,12 +155,13 @@ Tag
flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment)
{
TagBuilder tag_builder;
flac_scan_comments(comment, add_tag_handler, &tag_builder);
AddTagHandler h(tag_builder);
flac_scan_comments(comment, h);
return tag_builder.Commit();
}
void
FlacMetadataChain::Scan(const TagHandler &handler, void *handler_ctx)
FlacMetadataChain::Scan(TagHandler &handler) noexcept
{
FLACMetadataIterator iterator(*this);
@ -173,6 +170,6 @@ FlacMetadataChain::Scan(const TagHandler &handler, void *handler_ctx)
if (block == nullptr)
break;
flac_scan_metadata(block, handler, handler_ctx);
flac_scan_metadata(block, handler);
} while (iterator.Next());
}

View File

@ -25,7 +25,7 @@
#include <FLAC/metadata.h>
struct TagHandler;
class TagHandler;
class MixRampInfo;
class FlacMetadataChain {
@ -82,7 +82,7 @@ public:
return FLAC__Metadata_ChainStatusString[GetStatus()];
}
void Scan(const TagHandler &handler, void *handler_ctx);
void Scan(TagHandler &handler) noexcept;
};
class FLACMetadataIterator {
@ -126,6 +126,6 @@ flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment);
void
flac_scan_metadata(const FLAC__StreamMetadata *block,
const TagHandler &handler, void *handler_ctx);
TagHandler &handler) noexcept;
#endif

View File

@ -193,8 +193,7 @@ fluidsynth_file_decode(DecoderClient &client, Path path_fs)
static bool
fluidsynth_scan_file(Path path_fs,
gcc_unused const TagHandler &handler,
gcc_unused void *handler_ctx) noexcept
gcc_unused TagHandler &handler) noexcept
{
return fluid_is_midifile(path_fs.c_str());
}

View File

@ -215,15 +215,13 @@ gme_file_decode(DecoderClient &client, Path path_fs)
static void
ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
if (info.play_length > 0)
tag_handler_invoke_duration(handler, handler_ctx,
SongTime::FromMS(info.play_length));
handler.OnDuration(SongTime::FromMS(info.play_length));
if (track_count > 1)
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
StringFormat<16>("%u", song_num + 1));
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1));
if (info.song != nullptr) {
if (track_count > 1) {
@ -232,33 +230,26 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
StringFormat<1024>("%s (%u/%d)",
info.song, song_num + 1,
track_count);
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, tag_title);
handler.OnTag(TAG_TITLE, tag_title);
} else
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, info.song);
handler.OnTag(TAG_TITLE, info.song);
}
if (info.author != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_ARTIST, info.author);
handler.OnTag(TAG_ARTIST, info.author);
if (info.game != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_ALBUM, info.game);
handler.OnTag(TAG_ALBUM, info.game);
if (info.comment != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_COMMENT, info.comment);
handler.OnTag(TAG_COMMENT, info.comment);
if (info.copyright != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_DATE, info.copyright);
handler.OnTag(TAG_DATE, info.copyright);
}
static bool
ScanMusicEmu(Music_Emu *emu, unsigned song_num,
const TagHandler &handler, void *handler_ctx)
ScanMusicEmu(Music_Emu *emu, unsigned song_num, TagHandler &handler) noexcept
{
gme_info_t *ti;
const char *gme_err = gme_track_info(emu, &ti, song_num);
@ -271,14 +262,12 @@ ScanMusicEmu(Music_Emu *emu, unsigned song_num,
AtScopeExit(ti) { gme_free_info(ti); };
ScanGmeInfo(*ti, song_num, gme_track_count(emu),
handler, handler_ctx);
ScanGmeInfo(*ti, song_num, gme_track_count(emu), handler);
return true;
}
static bool
gme_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
gme_scan_file(Path path_fs, TagHandler &handler) noexcept
{
const auto container = ParseContainerPath(path_fs);
@ -289,7 +278,7 @@ gme_scan_file(Path path_fs,
AtScopeExit(emu) { gme_delete(emu); };
return ScanMusicEmu(emu, container.track, handler, handler_ctx);
return ScanMusicEmu(emu, container.track, handler);
}
static std::forward_list<DetachedSong>
@ -316,8 +305,8 @@ gme_container_scan(Path path_fs)
auto tail = list.before_begin();
for (unsigned i = 0; i < num_songs; ++i) {
ScanMusicEmu(emu, i,
add_tag_handler, &tag_builder);
AddTagHandler h(tag_builder);
ScanMusicEmu(emu, i, h);
const auto track_name =
StringFormat<64>(SUBTUNE_PREFIX "%03u.%s", i+1,

View File

@ -1062,16 +1062,14 @@ mp3_decode(DecoderClient &client, InputStream &input_stream)
}
static bool
mad_decoder_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
mad_decoder_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
const auto result = mad_decoder_total_file_time(is);
if (!result.first)
return false;
if (!result.second.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx,
SongTime(result.second));
handler.OnDuration(SongTime(result.second));
return true;
}

View File

@ -185,8 +185,7 @@ mikmod_decoder_file_decode(DecoderClient &client, Path path_fs)
}
static bool
mikmod_decoder_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
mikmod_decoder_scan_file(Path path_fs, TagHandler &handler) noexcept
{
/* deconstify the path because libmikmod wants a non-const
string pointer */
@ -204,8 +203,7 @@ mikmod_decoder_scan_file(Path path_fs,
char *title = Player_LoadTitle(path2);
if (title != nullptr) {
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, title);
handler.OnTag(TAG_TITLE, title);
#if (LIBMIKMOD_VERSION >= 0x030200)
MikMod_free(title);
#else

View File

@ -175,20 +175,17 @@ mod_decode(DecoderClient &client, InputStream &is)
}
static bool
modplug_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
modplug_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
ModPlugFile *f = LoadModPlugFile(nullptr, is);
if (f == nullptr)
return false;
tag_handler_invoke_duration(handler, handler_ctx,
SongTime::FromMS(ModPlug_GetLength(f)));
handler.OnDuration(SongTime::FromMS(ModPlug_GetLength(f)));
const char *title = ModPlug_GetName(f);
if (title != nullptr)
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, title);
handler.OnTag(TAG_TITLE, title);
ModPlug_Unload(f);

View File

@ -257,14 +257,13 @@ mpcdec_get_file_duration(InputStream &is)
}
static bool
mpcdec_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
mpcdec_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
const auto duration = mpcdec_get_file_duration(is);
if (duration.IsNegative())
return false;
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
handler.OnDuration(SongTime(duration));
return true;
}

View File

@ -279,8 +279,7 @@ mpd_mpg123_file_decode(DecoderClient &client, Path path_fs)
}
static bool
mpd_mpg123_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
mpd_mpg123_scan_file(Path path_fs, TagHandler &handler) noexcept
{
int error;
mpg123_handle *const handle = mpg123_new(nullptr, &error);
@ -316,7 +315,7 @@ mpd_mpg123_scan_file(Path path_fs,
SongTime::FromScale<uint64_t>(num_samples,
audio_format.sample_rate);
tag_handler_invoke_duration(handler, handler_ctx, duration);
handler.OnDuration(duration);
return true;
}

View File

@ -207,10 +207,9 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
rgi.Clear();
TagBuilder tag_builder;
AddTagHandler h(tag_builder);
if (ScanOpusTags(packet.packet, packet.bytes,
&rgi,
add_tag_handler, &tag_builder) &&
if (ScanOpusTags(packet.packet, packet.bytes, &rgi, h) &&
!tag_builder.empty()) {
client.SubmitReplayGain(&rgi);
@ -314,7 +313,7 @@ ReadAndParseOpusHead(OggSyncState &sync, OggStreamState &stream,
static bool
ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler)
{
ogg_packet packet;
@ -322,12 +321,12 @@ ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
IsOpusTags(packet) &&
ScanOpusTags(packet.packet, packet.bytes,
nullptr,
handler, handler_ctx);
handler);
}
static void
VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler)
{
ogg_packet packet;
@ -335,13 +334,12 @@ VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
const auto duration =
SongTime::FromScale<uint64_t>(packet.granulepos,
opus_sample_rate);
tag_handler_invoke_duration(handler, handler_ctx, duration);
handler.OnDuration(duration);
}
}
static bool
mpd_opus_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
InputStreamReader reader(is);
OggSyncState oy(reader);
@ -354,10 +352,10 @@ mpd_opus_scan_stream(InputStream &is,
unsigned channels;
if (!ReadAndParseOpusHead(oy, os, channels) ||
!ReadAndVisitOpusTags(oy, os, handler, handler_ctx))
!ReadAndVisitOpusTags(oy, os, handler))
return false;
VisitOpusDuration(is, oy, os, handler, handler_ctx);
VisitOpusDuration(is, oy, os, handler);
return true;
}

View File

@ -43,7 +43,7 @@ ParseOpusTagName(const char *name) noexcept
static void
ScanOneOpusTag(const char *name, const char *value,
ReplayGainInfo *rgi,
const TagHandler &handler, void *ctx)
TagHandler &handler) noexcept
{
if (rgi != nullptr && strcmp(name, "R128_TRACK_GAIN") == 0) {
/* R128_TRACK_GAIN is a Q7.8 fixed point number in
@ -63,25 +63,25 @@ ScanOneOpusTag(const char *name, const char *value,
rgi->album.gain = double(l) / 256.;
}
tag_handler_invoke_pair(handler, ctx, name, value);
handler.OnPair(name, value);
if (handler.tag != nullptr) {
if (handler.WantTag()) {
TagType t = ParseOpusTagName(name);
if (t != TAG_NUM_OF_ITEM_TYPES)
tag_handler_invoke_tag(handler, ctx, t, value);
handler.OnTag(t, value);
}
}
bool
ScanOpusTags(const void *data, size_t size,
ReplayGainInfo *rgi,
const TagHandler &handler, void *ctx)
TagHandler &handler) noexcept
{
OpusReader r(data, size);
if (!r.Expect("OpusTags", 8))
return false;
if (handler.pair == nullptr && handler.tag == nullptr)
if (!handler.WantPair() && !handler.WantTag())
return true;
if (!r.SkipString())
@ -100,7 +100,7 @@ ScanOpusTags(const void *data, size_t size,
if (eq != nullptr && eq > p) {
*eq = 0;
ScanOneOpusTag(p, eq + 1, rgi, handler, ctx);
ScanOneOpusTag(p, eq + 1, rgi, handler);
}
delete[] p;

View File

@ -25,11 +25,11 @@
#include <stddef.h>
struct ReplayGainInfo;
struct TagHandler;
class TagHandler;
bool
ScanOpusTags(const void *data, size_t size,
ReplayGainInfo *rgi,
const TagHandler &handler, void *ctx);
TagHandler &handler) noexcept;
#endif

View File

@ -405,7 +405,7 @@ GetInfoString(const SidTuneInfo &info, unsigned i) noexcept
static void
ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
/* title */
const char *title = GetInfoString(info, 0);
@ -416,31 +416,26 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
const auto tag_title =
StringFormat<1024>("%s (%u/%u)",
title, track, n_tracks);
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, tag_title);
handler.OnTag(TAG_TITLE, tag_title);
} else
tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title);
handler.OnTag(TAG_TITLE, title);
/* artist */
const char *artist = GetInfoString(info, 1);
if (artist != nullptr)
tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST,
artist);
handler.OnTag(TAG_ARTIST, artist);
/* date */
const char *date = GetInfoString(info, 2);
if (date != nullptr)
tag_handler_invoke_tag(handler, handler_ctx, TAG_DATE,
date);
handler.OnTag(TAG_DATE, date);
/* track */
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
StringFormat<16>("%u", track));
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track));
}
static bool
sidplay_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
sidplay_scan_file(Path path_fs, TagHandler &handler) noexcept
{
const auto container = ParseContainerPath(path_fs);
const unsigned song_num = container.track;
@ -463,13 +458,12 @@ sidplay_scan_file(Path path_fs,
const unsigned n_tracks = info.songs;
#endif
ScanSidTuneInfo(info, song_num, n_tracks, handler, handler_ctx);
ScanSidTuneInfo(info, song_num, n_tracks, handler);
/* time */
const auto duration = get_song_length(tune);
if (!duration.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx,
SongTime(duration));
handler.OnDuration(SongTime(duration));
return true;
}
@ -506,8 +500,8 @@ sidplay_container_scan(Path path_fs)
for (unsigned i = 1; i <= n_tracks; ++i) {
tune.selectSong(i);
ScanSidTuneInfo(info, i, n_tracks,
add_tag_handler, &tag_builder);
AddTagHandler h(tag_builder);
ScanSidTuneInfo(info, i, n_tracks, h);
char track_name[32];
/* Construct container/tune path names, eg.

View File

@ -240,11 +240,11 @@ sndfile_stream_decode(DecoderClient &client, InputStream &is)
static void
sndfile_handle_tag(SNDFILE *sf, int str, TagType tag,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const char *value = sf_get_string(sf, str);
if (value != nullptr)
tag_handler_invoke_tag(handler, handler_ctx, tag, value);
handler.OnTag(tag, value);
}
static constexpr struct {
@ -261,8 +261,7 @@ static constexpr struct {
};
static bool
sndfile_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
sndfile_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
SF_INFO info;
@ -280,11 +279,10 @@ sndfile_scan_stream(InputStream &is,
return false;
}
tag_handler_invoke_duration(handler, handler_ctx,
sndfile_duration(info));
handler.OnDuration(sndfile_duration(info));
for (auto i : sndfile_tags)
sndfile_handle_tag(sf, i.str, i.tag, handler, handler_ctx);
sndfile_handle_tag(sf, i.str, i.tag, handler);
sf_close(sf);

View File

@ -348,7 +348,7 @@ static void
VisitVorbisDuration(InputStream &is,
OggSyncState &sync, OggStreamState &stream,
unsigned sample_rate,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
ogg_packet packet;
@ -358,12 +358,11 @@ VisitVorbisDuration(InputStream &is,
const auto duration =
SongTime::FromScale<uint64_t>(packet.granulepos,
sample_rate);
tag_handler_invoke_duration(handler, handler_ctx, duration);
handler.OnDuration(duration);
}
static bool
vorbis_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
vorbis_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
/* initialize libogg */
@ -397,12 +396,11 @@ vorbis_scan_stream(InputStream &is,
/* visit the Vorbis comments we just read */
vorbis_comments_scan(vc.user_comments,
handler, handler_ctx);
vorbis_comments_scan(vc.user_comments, handler);
/* check the song duration by locating the e_o_s packet */
VisitVorbisDuration(is, sync, stream, vi.rate, handler, handler_ctx);
VisitVorbisDuration(is, sync, stream, vi.rate, handler);
return true;
}

View File

@ -578,8 +578,7 @@ wavpack_filedecode(DecoderClient &client, Path path_fs)
* Reads metainfo from the specified file.
*/
static bool
wavpack_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
wavpack_scan_file(Path path_fs, TagHandler &handler) noexcept
{
WavpackContext *wpc;
@ -595,14 +594,13 @@ wavpack_scan_file(Path path_fs,
const auto duration = GetDuration(wpc);
if (!duration.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
handler.OnDuration(SongTime(duration));
return true;
}
static bool
wavpack_scan_stream(InputStream &is,
const TagHandler &handler, void *handler_ctx) noexcept
wavpack_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
WavpackInput isp(nullptr, is);
@ -620,7 +618,7 @@ wavpack_scan_stream(InputStream &is,
const auto duration = GetDuration(wpc);
if (!duration.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
handler.OnDuration(SongTime(duration));
return true;
}

View File

@ -126,8 +126,7 @@ wildmidi_file_decode(DecoderClient &client, Path path_fs)
}
static bool
wildmidi_scan_file(Path path_fs,
const TagHandler &handler, void *handler_ctx) noexcept
wildmidi_scan_file(Path path_fs, TagHandler &handler) noexcept
{
midi *wm = WildMidi_Open(path_fs.c_str());
if (wm == nullptr)
@ -142,7 +141,7 @@ wildmidi_scan_file(Path path_fs,
const auto duration =
SongTime::FromScale<uint64_t>(info->approx_total_samples,
WILDMIDI_SAMPLE_RATE);
tag_handler_invoke_duration(handler, handler_ctx, duration);
handler.OnDuration(duration);
WildMidi_Close(wm);

View File

@ -53,13 +53,13 @@ vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept
static bool
vorbis_copy_comment(const char *comment,
const char *name, TagType tag_type,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const char *value;
value = vorbis_comment_value(comment, name);
if (value != nullptr) {
tag_handler_invoke_tag(handler, handler_ctx, tag_type, value);
handler.OnTag(tag_type, value);
return true;
}
@ -67,36 +67,31 @@ vorbis_copy_comment(const char *comment,
}
static void
vorbis_scan_comment(const char *comment,
const TagHandler &handler, void *handler_ctx)
vorbis_scan_comment(const char *comment, TagHandler &handler) noexcept
{
if (handler.pair != nullptr) {
if (handler.WantPair()) {
const DivideString split(comment, '=');
if (split.IsDefined() && !split.empty())
tag_handler_invoke_pair(handler, handler_ctx,
split.GetFirst(),
split.GetSecond());
handler.OnPair(split.GetFirst(), split.GetSecond());
}
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
if (vorbis_copy_comment(comment, i->name, i->type,
handler, handler_ctx))
handler))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (vorbis_copy_comment(comment,
tag_item_names[i], TagType(i),
handler, handler_ctx))
handler))
return;
}
void
vorbis_comments_scan(char **comments,
const TagHandler &handler, void *handler_ctx)
vorbis_comments_scan(char **comments, TagHandler &handler) noexcept
{
while (*comments)
vorbis_scan_comment(*comments++,
handler, handler_ctx);
vorbis_scan_comment(*comments++, handler);
}
@ -104,7 +99,8 @@ std::unique_ptr<Tag>
vorbis_comments_to_tag(char **comments) noexcept
{
TagBuilder tag_builder;
vorbis_comments_scan(comments, add_tag_handler, &tag_builder);
AddTagHandler h(tag_builder);
vorbis_comments_scan(comments, h);
return tag_builder.empty()
? nullptr
: tag_builder.CommitNew();

View File

@ -25,15 +25,14 @@
#include <memory>
struct ReplayGainInfo;
struct TagHandler;
class TagHandler;
struct Tag;
bool
vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept;
void
vorbis_comments_scan(char **comments,
const TagHandler &handler, void *handler_ctx);
vorbis_comments_scan(char **comments, TagHandler &handler) noexcept;
std::unique_ptr<Tag>
vorbis_comments_to_tag(char **comments) noexcept;

View File

@ -71,22 +71,23 @@ public:
virtual std::unique_ptr<DetachedSong> NextSong() override;
};
static void
embcue_tag_pair(const char *name, const char *value, void *ctx)
{
EmbeddedCuePlaylist *playlist = (EmbeddedCuePlaylist *)ctx;
class ExtractCuesheetTagHandler final : public NullTagHandler {
public:
std::string cuesheet;
if (playlist->cuesheet.empty() &&
StringEqualsCaseASCII(name, "cuesheet"))
playlist->cuesheet = value;
}
ExtractCuesheetTagHandler() noexcept:NullTagHandler(WANT_PAIR) {}
static constexpr TagHandler embcue_tag_handler = {
nullptr,
nullptr,
embcue_tag_pair,
void OnPair(const char *key, const char *value) noexcept override;
};
void
ExtractCuesheetTagHandler::OnPair(const char *name, const char *value) noexcept
{
if (cuesheet.empty() &&
StringEqualsCaseASCII(name, "cuesheet"))
cuesheet = value;
}
static std::unique_ptr<SongEnumerator>
embcue_playlist_open_uri(const char *uri,
gcc_unused Mutex &mutex)
@ -97,18 +98,21 @@ embcue_playlist_open_uri(const char *uri,
const auto path_fs = AllocatedPath::FromUTF8Throw(uri);
auto playlist = std::make_unique<EmbeddedCuePlaylist>();
ExtractCuesheetTagHandler extract_cuesheet;
tag_file_scan(path_fs, extract_cuesheet);
if (extract_cuesheet.cuesheet.empty())
ScanGenericTags(path_fs, extract_cuesheet);
tag_file_scan(path_fs, embcue_tag_handler, playlist.get());
if (playlist->cuesheet.empty())
ScanGenericTags(path_fs, embcue_tag_handler, playlist.get());
if (playlist->cuesheet.empty())
if (extract_cuesheet.cuesheet.empty())
/* no "CUESHEET" tag found */
return nullptr;
auto playlist = std::make_unique<EmbeddedCuePlaylist>();
playlist->filename = PathTraitsUTF8::GetBase(uri);
playlist->cuesheet = std::move(extract_cuesheet.cuesheet);
playlist->next = &playlist->cuesheet[0];
playlist->parser = new CueParser();

View File

@ -76,7 +76,7 @@ ForEachValue(const char *value, const char *end, C &&callback)
static bool
tag_ape_import_item(unsigned long flags,
const char *key, StringView value,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
/* we only care about utf-8 text tags */
if ((flags & (0x3 << 1)) != 0)
@ -85,36 +85,31 @@ tag_ape_import_item(unsigned long flags,
const auto begin = value.begin();
const auto end = value.end();
if (handler.pair != nullptr)
ForEachValue(begin, end, [handler, handler_ctx,
key](const char *_value) {
handler.pair(key, _value, handler_ctx);
if (handler.WantPair())
ForEachValue(begin, end, [&handler, key](const char *_value) {
handler.OnPair(key, _value);
});
TagType type = tag_ape_name_parse(key);
if (type == TAG_NUM_OF_ITEM_TYPES)
return false;
ForEachValue(begin, end, [handler, handler_ctx,
type](const char *_value) {
tag_handler_invoke_tag(handler, handler_ctx,
type, _value);
ForEachValue(begin, end, [&handler, type](const char *_value) {
handler.OnTag(type, _value);
});
return true;
}
bool
tag_ape_scan2(InputStream &is,
const TagHandler &handler, void *handler_ctx)
tag_ape_scan2(InputStream &is, TagHandler &handler) noexcept
{
bool recognized = false;
auto callback = [handler, handler_ctx, &recognized]
auto callback = [&handler, &recognized]
(unsigned long flags, const char *key,
StringView value) {
recognized |= tag_ape_import_item(flags, key, value,
handler, handler_ctx);
recognized |= tag_ape_import_item(flags, key, value, handler);
return true;
};

View File

@ -20,10 +20,8 @@
#ifndef MPD_APE_TAG_HXX
#define MPD_APE_TAG_HXX
#include "Table.hxx"
class InputStream;
struct TagHandler;
class TagHandler;
/**
* Scan the APE tags of a stream.
@ -31,7 +29,6 @@ struct TagHandler;
* @param path_fs the path of the file in filesystem encoding
*/
bool
tag_ape_scan2(InputStream &is,
const TagHandler &handler, void *handler_ctx);
tag_ape_scan2(InputStream &is, TagHandler &handler) noexcept;
#endif

View File

@ -30,9 +30,9 @@
#include <exception>
bool
ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx)
ScanGenericTags(InputStream &is, TagHandler &handler) noexcept
{
if (tag_ape_scan2(is, handler, ctx))
if (tag_ape_scan2(is, handler))
return true;
#ifdef ENABLE_ID3TAG
@ -42,19 +42,19 @@ ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx)
return false;
}
return tag_id3_scan(is, handler, ctx);
return tag_id3_scan(is, handler);
#else
return false;
#endif
}
bool
ScanGenericTags(Path path, const TagHandler &handler, void *ctx)
ScanGenericTags(Path path, TagHandler &handler) noexcept
try {
Mutex mutex;
auto is = OpenLocalInputStream(path, mutex);
return ScanGenericTags(*is, handler, ctx);
return ScanGenericTags(*is, handler);
} catch (...) {
LogError(std::current_exception());
return false;

View File

@ -22,7 +22,7 @@
#include "check.h"
struct TagHandler;
class TagHandler;
class InputStream;
class Path;
@ -31,12 +31,12 @@ class Path;
* stream does not need to be rewound.
*/
bool
ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx);
ScanGenericTags(InputStream &is, TagHandler &handler) noexcept;
/**
* Attempts to scan APE or ID3 tags from the specified file.
*/
bool
ScanGenericTags(Path path, const TagHandler &handler, void *ctx);
ScanGenericTags(Path path, TagHandler &handler) noexcept;
#endif

View File

@ -25,19 +25,15 @@
#include <stdlib.h>
static void
add_tag_duration(SongTime duration, void *ctx)
void
AddTagHandler::OnDuration(SongTime duration) noexcept
{
TagBuilder &tag = *(TagBuilder *)ctx;
tag.SetDuration(duration);
}
static void
add_tag_tag(TagType type, const char *value, void *ctx)
void
AddTagHandler::OnTag(TagType type, const char *value) noexcept
{
TagBuilder &tag = *(TagBuilder *)ctx;
if (type == TAG_TRACK || type == TAG_DISC) {
/* filter out this extra data and leading zeroes */
char *end;
@ -48,24 +44,10 @@ add_tag_tag(TagType type, const char *value, void *ctx)
tag.AddItem(type, value);
}
const TagHandler add_tag_handler = {
add_tag_duration,
add_tag_tag,
nullptr,
};
static void
full_tag_pair(const char *name, gcc_unused const char *value, void *ctx)
void
FullTagHandler::OnPair(const char *name, gcc_unused const char *value) noexcept
{
TagBuilder &tag = *(TagBuilder *)ctx;
if (StringEqualsCaseASCII(name, "cuesheet"))
tag.SetHasPlaylist(true);
}
const TagHandler full_tag_handler = {
add_tag_duration,
add_tag_tag,
full_tag_pair,
};

View File

@ -23,19 +23,45 @@
#include "check.h"
#include "Type.h"
#include "Chrono.hxx"
#include "Compiler.h"
#include <assert.h>
class TagBuilder;
/**
* A callback table for receiving metadata of a song.
* An interface for receiving metadata of a song.
*/
struct TagHandler {
class TagHandler {
const unsigned want_mask;
public:
static constexpr unsigned WANT_DURATION = 0x1;
static constexpr unsigned WANT_TAG = 0x2;
static constexpr unsigned WANT_PAIR = 0x4;
explicit TagHandler(unsigned _want_mask) noexcept
:want_mask(_want_mask) {}
TagHandler(const TagHandler &) = delete;
TagHandler &operator=(const TagHandler &) = delete;
bool WantDuration() const noexcept {
return want_mask & WANT_DURATION;
}
bool WantTag() const noexcept {
return want_mask & WANT_TAG;
}
bool WantPair() const noexcept {
return want_mask & WANT_PAIR;
}
/**
* Declare the duration of a song. Do not call
* this when the duration could not be determined, because
* there is no magic value for "unknown duration".
*/
void (*duration)(SongTime duration, void *ctx);
virtual void OnDuration(SongTime duration) noexcept = 0;
/**
* A tag has been read.
@ -43,56 +69,57 @@ struct TagHandler {
* @param the value of the tag; the pointer will become
* invalid after returning
*/
void (*tag)(TagType type, const char *value, void *ctx);
virtual void OnTag(TagType type, const char *value) noexcept = 0;
/**
* A name-value pair has been read. It is the codec specific
* representation of tags.
*/
void (*pair)(const char *key, const char *value, void *ctx);
virtual void OnPair(const char *key, const char *value) noexcept = 0;
};
static inline void
tag_handler_invoke_duration(const TagHandler &handler, void *ctx,
SongTime duration) noexcept
{
if (handler.duration != nullptr)
handler.duration(duration, ctx);
}
class NullTagHandler : public TagHandler {
public:
explicit NullTagHandler(unsigned _want_mask) noexcept
:TagHandler(_want_mask) {}
static inline void
tag_handler_invoke_tag(const TagHandler &handler, void *ctx,
TagType type, const char *value) noexcept
{
assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES);
assert(value != nullptr);
if (handler.tag != nullptr)
handler.tag(type, value, ctx);
}
static inline void
tag_handler_invoke_pair(const TagHandler &handler, void *ctx,
const char *name, const char *value) noexcept
{
assert(name != nullptr);
assert(value != nullptr);
if (handler.pair != nullptr)
handler.pair(name, value, ctx);
}
void OnDuration(gcc_unused SongTime duration) noexcept override {}
void OnTag(gcc_unused TagType type,
gcc_unused const char *value) noexcept override {}
void OnPair(gcc_unused const char *key,
gcc_unused const char *value) noexcept override {}
};
/**
* This #TagHandler implementation adds tag values to a #TagBuilder object
* (casted from the context pointer).
* This #TagHandler implementation adds tag values to a #TagBuilder
* object.
*/
extern const TagHandler add_tag_handler;
class AddTagHandler : public NullTagHandler {
protected:
TagBuilder &tag;
AddTagHandler(unsigned _want_mask, TagBuilder &_builder) noexcept
:NullTagHandler(_want_mask), tag(_builder) {}
public:
explicit AddTagHandler(TagBuilder &_builder) noexcept
:AddTagHandler(WANT_DURATION|WANT_TAG, _builder) {}
void OnDuration(SongTime duration) noexcept override;
void OnTag(TagType type, const char *value) noexcept override;
};
/**
* This #TagHandler implementation adds tag values to a #TagBuilder object
* (casted from the context pointer), and supports the has_playlist
* attribute.
*/
extern const TagHandler full_tag_handler;
class FullTagHandler : public AddTagHandler {
public:
explicit FullTagHandler(TagBuilder &_builder) noexcept
:AddTagHandler(WANT_DURATION|WANT_TAG|WANT_PAIR, _builder) {}
void OnPair(const char *key, const char *value) noexcept override;
};
#endif

View File

@ -100,7 +100,7 @@ import_id3_string(const id3_ucs4_t *ucs4)
static void
tag_id3_import_text_frame(const struct id3_frame *frame,
TagType type,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
if (frame->nfields != 2)
return;
@ -133,8 +133,7 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char *)utf8);
handler.OnTag(type, (const char *)utf8);
}
}
@ -144,13 +143,13 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
*/
static void
tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const struct id3_frame *frame;
for (unsigned i = 0;
(frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i)
tag_id3_import_text_frame(frame, type,
handler, handler_ctx);
handler);
}
/**
@ -164,8 +163,7 @@ tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type,
*/
static void
tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
const TagHandler &handler,
void *handler_ctx)
TagHandler &handler) noexcept
{
if (frame->nfields != 4)
return;
@ -185,7 +183,7 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
handler.OnTag(type, (const char *)utf8);
}
/**
@ -194,13 +192,13 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
*/
static void
tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
const struct id3_frame *frame;
for (unsigned i = 0;
(frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i)
tag_id3_import_comment_frame(frame, type,
handler, handler_ctx);
handler);
}
/**
@ -220,8 +218,7 @@ tag_id3_parse_txxx_name(const char *name) noexcept
*/
static void
tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
const TagHandler &handler,
void *handler_ctx)
TagHandler &handler) noexcept
{
for (unsigned i = 0;; ++i) {
const id3_frame *frame = id3_tag_findframe(id3_tag, "TXXX", i);
@ -240,15 +237,12 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
AtScopeExit(value) { free(value); };
tag_handler_invoke_pair(handler, handler_ctx,
(const char *)name,
(const char *)value);
handler.OnPair((const char *)name, (const char *)value);
TagType type = tag_id3_parse_txxx_name((const char*)name);
if (type != TAG_NUM_OF_ITEM_TYPES)
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char*)value);
handler.OnTag(type, (const char*)value);
}
}
@ -257,7 +251,7 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
*/
static void
tag_id3_import_ufid(struct id3_tag *id3_tag,
const TagHandler &handler, void *handler_ctx)
TagHandler &handler) noexcept
{
for (unsigned i = 0;; ++i) {
const id3_frame *frame = id3_tag_findframe(id3_tag, "UFID", i);
@ -284,65 +278,63 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
continue;
std::string p((const char *)value, length);
tag_handler_invoke_tag(handler, handler_ctx,
TAG_MUSICBRAINZ_TRACKID, p.c_str());
handler.OnTag(TAG_MUSICBRAINZ_TRACKID, p.c_str());
}
}
void
scan_id3_tag(struct id3_tag *tag,
const TagHandler &handler, void *handler_ctx)
scan_id3_tag(struct id3_tag *tag, TagHandler &handler) noexcept
{
tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST,
TAG_ALBUM_ARTIST, handler, handler_ctx);
TAG_ALBUM_ARTIST, handler);
tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT,
TAG_ARTIST_SORT, handler, handler_ctx);
TAG_ARTIST_SORT, handler);
tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler, handler_ctx);
tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler);
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT,
TAG_ALBUM_ARTIST_SORT, handler, handler_ctx);
TAG_ALBUM_ARTIST_SORT, handler);
tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_ORIGINAL_RELEASE_DATE, TAG_ORIGINAL_DATE,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, "TPE3", TAG_PERFORMER,
handler, handler_ctx);
tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx);
handler);
tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler);
tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT,
handler, handler_ctx);
handler);
tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC,
handler, handler_ctx);
handler);
tag_id3_import_musicbrainz(tag, handler, handler_ctx);
tag_id3_import_ufid(tag, handler, handler_ctx);
tag_id3_import_musicbrainz(tag, handler);
tag_id3_import_ufid(tag, handler);
}
std::unique_ptr<Tag>
tag_id3_import(struct id3_tag *tag)
{
TagBuilder tag_builder;
scan_id3_tag(tag, add_tag_handler, &tag_builder);
AddTagHandler h(tag_builder);
scan_id3_tag(tag, h);
return tag_builder.empty()
? nullptr
: tag_builder.CommitNew();
}
bool
tag_id3_scan(InputStream &is,
const TagHandler &handler, void *handler_ctx)
tag_id3_scan(InputStream &is, TagHandler &handler) noexcept
{
UniqueId3Tag tag;
@ -355,6 +347,6 @@ tag_id3_scan(InputStream &is,
return false;
}
scan_id3_tag(tag.get(), handler, handler_ctx);
scan_id3_tag(tag.get(), handler);
return true;
}

View File

@ -25,13 +25,12 @@
#include <memory>
class InputStream;
struct TagHandler;
class TagHandler;
struct Tag;
struct id3_tag;
bool
tag_id3_scan(InputStream &is,
const TagHandler &handler, void *handler_ctx);
tag_id3_scan(InputStream &is, TagHandler &handler) noexcept;
std::unique_ptr<Tag>
tag_id3_import(id3_tag *);
@ -41,7 +40,6 @@ tag_id3_import(id3_tag *);
*
*/
void
scan_id3_tag(id3_tag *tag,
const TagHandler &handler, void *handler_ctx);
scan_id3_tag(id3_tag *tag, TagHandler &handler) noexcept;
#endif

View File

@ -40,31 +40,29 @@
#include <locale.h>
#endif
static bool empty = true;
class DumpTagHandler final : public NullTagHandler {
bool empty = true;
static void
print_duration(SongTime duration, gcc_unused void *ctx)
{
printf("duration=%f\n", duration.ToDoubleS());
}
public:
DumpTagHandler() noexcept
:NullTagHandler(WANT_DURATION|WANT_TAG|WANT_PAIR) {}
static void
print_tag(TagType type, const char *value, gcc_unused void *ctx)
{
printf("[%s]=%s\n", tag_item_names[type], value);
empty = false;
}
bool IsEmpty() const noexcept {
return empty;
}
static void
print_pair(const char *name, const char *value, gcc_unused void *ctx)
{
printf("\"%s\"=%s\n", name, value);
}
void OnDuration(SongTime duration) noexcept override {
printf("duration=%f\n", duration.ToDoubleS());
}
static constexpr TagHandler print_handler = {
print_duration,
print_tag,
print_pair,
void OnTag(TagType type, const char *value) noexcept override {
printf("[%s]=%s\n", tag_item_names[type], value);
empty = false;
}
void OnPair(const char *key, const char *value) noexcept override {
printf("\"%s\"=%s\n", key, value);
}
};
int main(int argc, char **argv)
@ -100,9 +98,10 @@ try {
return EXIT_FAILURE;
}
DumpTagHandler h;
bool success;
try {
success = plugin->ScanFile(path, print_handler, nullptr);
success = plugin->ScanFile(path, h);
} catch (const std::exception &e) {
LogError(e);
success = false;
@ -113,7 +112,7 @@ try {
if (!success && plugin->scan_stream != NULL) {
is = InputStream::OpenReady(path.c_str(), mutex);
success = plugin->ScanStream(*is, print_handler, nullptr);
success = plugin->ScanStream(*is, h);
}
if (!success) {
@ -121,11 +120,11 @@ try {
return EXIT_FAILURE;
}
if (empty) {
if (h.IsEmpty()) {
if (is)
ScanGenericTags(*is, print_handler, nullptr);
ScanGenericTags(*is, h);
else
ScanGenericTags(path, print_handler, nullptr);
ScanGenericTags(path, h);
}
return 0;