From 9d1ea1e7cfa256c7f5cac027382d92f658734ccb Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 25 Jul 2019 17:49:16 +0200 Subject: Add generic logger interface for propagating messages while loading drumkits to the commandline and/or plugin UI. --- src/dgxmlparser.cc | 166 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 50 deletions(-) (limited to 'src/dgxmlparser.cc') diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc index ae0842a..7f62a64 100644 --- a/src/dgxmlparser.cc +++ b/src/dgxmlparser.cc @@ -33,16 +33,34 @@ #include "nolocale.h" -bool probeDrumkitFile(const std::string& filename) +static int getLineNumberFromOffset(const std::string& filename, ptrdiff_t offset) +{ + FILE* fp = fopen(filename.data(), "rt"); + if(!fp) + { + return 0; + } + + int lineno{1}; + char c = 0; + while((c = fgetc(fp)) != EOF && offset--) + { + lineno += c == '\n' ? 1 : 0; + } + fclose(fp); + return lineno; +} + +bool probeDrumkitFile(const std::string& filename, LogFunction logger) { DrumkitDOM d; - return parseDrumkitFile(filename, d); + return parseDrumkitFile(filename, d, logger); } -bool probeInstrumentFile(const std::string& filename) +bool probeInstrumentFile(const std::string& filename, LogFunction logger) { InstrumentDOM d; - return parseInstrumentFile(filename, d); + return parseInstrumentFile(filename, d, logger); } static bool assign(double& dest, const std::string& val) @@ -77,7 +95,7 @@ static bool assign(main_state_t& dest, const std::string& val) } template -static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, bool opt = false) +static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, LogFunction logger, const std::string& filename, bool opt = false) { const char* val = src.attribute(attr.c_str()).as_string(nullptr); if(!val) @@ -86,6 +104,12 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, { ERR(dgxmlparser, "Attribute %s not found in %s, offset %d\n", attr.data(), src.path().data(), (int)src.offset_debug()); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, src.offset_debug()); + logger(LogLevel::Error, "Missing attribute '" + attr + + "' at line " + std::to_string(lineno)); + } } return opt; } @@ -94,6 +118,12 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, { ERR(dgxmlparser, "Attribute %s could not be assigned, offset %d\n", attr.data(), (int)src.offset_debug()); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, src.offset_debug()); + logger(LogLevel::Error, "Attribute '" + attr + + "' could not be assigned at line " + std::to_string(lineno)); + } return false; } @@ -101,7 +131,7 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, } template -static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, bool opt = false) +static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, LogFunction logger, const std::string& filename, bool opt = false) { auto val = src.child(node.c_str()); if(val == pugi::xml_node()) @@ -109,7 +139,13 @@ static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, if(!opt) { ERR(dgxmlparser, "Node %s not found in %s, offset %d\n", - node.data(), src.path().data(), (int)src.offset_debug()); + node.data(), filename.data(), (int)src.offset_debug()); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, src.offset_debug()); + logger(LogLevel::Error, "Node '" + node + + "' not found at line " + std::to_string(lineno)); + } } return opt; } @@ -118,16 +154,27 @@ static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, { ERR(dgxmlparser, "Attribute %s could not be assigned, offset %d\n", node.data(), (int)src.offset_debug()); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, src.offset_debug()); + logger(LogLevel::Error, "Node '" + node + + "' could not be assigned at line " + std::to_string(lineno)); + } return false; } return true; } -bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) +bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom, LogFunction logger) { bool res = true; + if(logger) + { + logger(LogLevel::Info, "Loading " + filename); + } + pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(filename.c_str()); res &= !result.status; @@ -135,49 +182,56 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) { ERR(dgxmlparser, "XML parse error: '%s' %d", filename.data(), (int) result.offset); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, result.offset); + logger(LogLevel::Error, "XML parse error in '" + filename + + "': " + result.description() + " at line " + + std::to_string(lineno)); + } return false; } pugi::xml_node drumkit = doc.child("drumkit"); dom.version = "1.0"; - res &= attrcpy(dom.version, drumkit, "version", true); + res &= attrcpy(dom.version, drumkit, "version", logger, filename, true); dom.samplerate = 44100.0; - res &= attrcpy(dom.samplerate, drumkit, "samplerate", true); + res &= attrcpy(dom.samplerate, drumkit, "samplerate", logger, filename, true); // Use the old name and description attributes on the drumkit node as fallback - res &= attrcpy(dom.metadata.title, drumkit, "name", true); - res &= attrcpy(dom.metadata.description, drumkit, "description", true); + res &= attrcpy(dom.metadata.title, drumkit, "name", logger, filename, true); + res &= attrcpy(dom.metadata.description, drumkit, "description", logger, filename, true); pugi::xml_node metadata = drumkit.child("metadata"); if(metadata != pugi::xml_node()) { auto& meta = dom.metadata; - res &= nodecpy(meta.version, metadata, "version", true); - res &= nodecpy(meta.title, metadata, "title", true); + res &= nodecpy(meta.version, metadata, "version", logger, filename, true); + res &= nodecpy(meta.title, metadata, "title", logger, filename, true); pugi::xml_node logo = metadata.child("logo"); if(logo != pugi::xml_node()) { - res &= attrcpy(meta.logo, logo, "src", true); + res &= attrcpy(meta.logo, logo, "src", logger, filename, true); } - res &= nodecpy(meta.description, metadata, "description", true); - res &= nodecpy(meta.license, metadata, "license", true); - res &= nodecpy(meta.notes, metadata, "notes", true); - res &= nodecpy(meta.author, metadata, "author", true); - res &= nodecpy(meta.email, metadata, "email", true); - res &= nodecpy(meta.website, metadata, "website", true); + res &= nodecpy(meta.description, metadata, "description", logger, filename, true); + res &= nodecpy(meta.license, metadata, "license", logger, filename, true); + res &= nodecpy(meta.notes, metadata, "notes", logger, filename, true); + res &= nodecpy(meta.author, metadata, "author", logger, filename, true); + res &= nodecpy(meta.email, metadata, "email", logger, filename, true); + res &= nodecpy(meta.website, metadata, "website", logger, filename, true); pugi::xml_node image = metadata.child("image"); if(image != pugi::xml_node()) { - res &= attrcpy(meta.image, image, "src", true); - res &= attrcpy(meta.image_map, image, "map", true); + res &= attrcpy(meta.image, image, "src", logger, filename, true); + res &= attrcpy(meta.image_map, image, "map", logger, filename, true); for(auto clickmap : image.children("clickmap")) { meta.clickmaps.emplace_back(); res &= attrcpy(meta.clickmaps.back().instrument, - clickmap, "instrument", true); + clickmap, "instrument", logger, filename, true); res &= attrcpy(meta.clickmaps.back().colour, - clickmap, "colour", true); + clickmap, "colour", logger, filename, true); } } } @@ -186,7 +240,7 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) for(pugi::xml_node channel: channels.children("channel")) { dom.channels.emplace_back(); - res &= attrcpy(dom.channels.back().name, channel, "name"); + res &= attrcpy(dom.channels.back().name, channel, "name", logger, filename); } pugi::xml_node instruments = doc.child("drumkit").child("instruments"); @@ -194,18 +248,18 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) { dom.instruments.emplace_back(); auto& instrument_ref = dom.instruments.back(); - res &= attrcpy(instrument_ref.name, instrument, "name"); - res &= attrcpy(instrument_ref.file, instrument, "file"); - res &= attrcpy(instrument_ref.group, instrument, "group", true); + res &= attrcpy(instrument_ref.name, instrument, "name", logger, filename); + res &= attrcpy(instrument_ref.file, instrument, "file", logger, filename); + res &= attrcpy(instrument_ref.group, instrument, "group", logger, filename, true); for(pugi::xml_node cmap: instrument.children("channelmap")) { instrument_ref.channel_map.emplace_back(); auto& channel_map_ref = instrument_ref.channel_map.back(); - res &= attrcpy(channel_map_ref.in, cmap, "in"); - res &= attrcpy(channel_map_ref.out, cmap, "out"); + res &= attrcpy(channel_map_ref.in, cmap, "in", logger, filename); + res &= attrcpy(channel_map_ref.out, cmap, "out", logger, filename); channel_map_ref.main = main_state_t::unset; - res &= attrcpy(channel_map_ref.main, cmap, "main", true); + res &= attrcpy(channel_map_ref.main, cmap, "main", logger, filename, true); } auto num_chokes = std::distance(instrument.children("chokes").begin(), @@ -223,9 +277,9 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) { instrument_ref.chokes.emplace_back(); auto& choke_ref = instrument_ref.chokes.back(); - res &= attrcpy(choke_ref.instrument, choke, "instrument"); + res &= attrcpy(choke_ref.instrument, choke, "instrument", logger, filename); choke_ref.choketime = 68; // default to 68 ms - res &= attrcpy(choke_ref.choketime, choke, "choketime", true); + res &= attrcpy(choke_ref.choketime, choke, "choketime", logger, filename, true); } } } @@ -233,32 +287,44 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom) return res; } -bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom) +bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom, LogFunction logger) { bool res = true; + if(logger) + { + logger(LogLevel::Info, "Loading " + filename); + } + pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(filename.data()); res &= !result.status; if(!res) { - ERR(dgxmlparser, "XML parse error: '%s'", filename.data()); + WARN(dgxmlparser, "XML parse error: '%s'", filename.data()); + if(logger) + { + auto lineno = getLineNumberFromOffset(filename, result.offset); + logger(LogLevel::Warning, "XML parse error in '" + filename + + "': " + result.description() + " at line " + + std::to_string(lineno)); + } } //TODO: handle version pugi::xml_node instrument = doc.child("instrument"); - res &= attrcpy(dom.name, instrument, "name"); + res &= attrcpy(dom.name, instrument, "name", logger, filename); dom.version = "1.0"; - res &= attrcpy(dom.version, instrument, "version", true); - res &= attrcpy(dom.description, instrument, "description", true); + res &= attrcpy(dom.version, instrument, "version", logger, filename, true); + res &= attrcpy(dom.description, instrument, "description", logger, filename, true); pugi::xml_node channels = instrument.child("channels"); for(pugi::xml_node channel : channels.children("channel")) { dom.instrument_channels.emplace_back(); - res &= attrcpy(dom.instrument_channels.back().name, channel, "name"); + res &= attrcpy(dom.instrument_channels.back().name, channel, "name", logger, filename); dom.instrument_channels.back().main = main_state_t::unset; - res &= attrcpy(dom.instrument_channels.back().main, channel, "main", true); + res &= attrcpy(dom.instrument_channels.back().main, channel, "main", logger, filename, true); } INFO(dgxmlparser, "XML version: %s\n", dom.version.data()); @@ -267,7 +333,7 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom) for(pugi::xml_node sample: samples.children("sample")) { dom.samples.emplace_back(); - res &= attrcpy(dom.samples.back().name, sample, "name"); + res &= attrcpy(dom.samples.back().name, sample, "name", logger, filename); // Power only part of >= v2.0 instruments. if(dom.version == "1.0") @@ -276,20 +342,20 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom) } else { - res &= attrcpy(dom.samples.back().power, sample, "power"); + res &= attrcpy(dom.samples.back().power, sample, "power", logger, filename); } for(pugi::xml_node audiofile: sample.children("audiofile")) { dom.samples.back().audiofiles.emplace_back(); res &= attrcpy(dom.samples.back().audiofiles.back().instrument_channel, - audiofile, "channel"); + audiofile, "channel", logger, filename); res &= attrcpy(dom.samples.back().audiofiles.back().file, - audiofile, "file"); + audiofile, "file", logger, filename); // Defaults to channel 1 in mono (1-based) dom.samples.back().audiofiles.back().filechannel = 1; res &= attrcpy(dom.samples.back().audiofiles.back().filechannel, - audiofile, "filechannel", true); + audiofile, "filechannel", logger, filename, true); } } @@ -301,14 +367,14 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom) { dom.velocities.emplace_back(); - res &= attrcpy(dom.velocities.back().lower, velocity, "lower"); - res &= attrcpy(dom.velocities.back().upper, velocity, "upper"); + res &= attrcpy(dom.velocities.back().lower, velocity, "lower", logger, filename); + res &= attrcpy(dom.velocities.back().upper, velocity, "upper", logger, filename); for(auto sampleref : velocity.children("sampleref")) { dom.velocities.back().samplerefs.emplace_back(); auto& sref = dom.velocities.back().samplerefs.back(); - res &= attrcpy(sref.probability, sampleref, "probability"); - res &= attrcpy(sref.name, sampleref, "name"); + res &= attrcpy(sref.probability, sampleref, "probability", logger, filename); + res &= attrcpy(sref.name, sampleref, "name", logger, filename); } } } -- cgit v1.2.3