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. --- drumgizmo/dgvalidator.cc | 42 ++++++++-- plugingui/statusframecontent.cc | 20 +++-- plugingui/statusframecontent.h | 7 +- src/Makefile.am | 1 + src/audiofile.cc | 17 +++- src/audiofile.h | 3 +- src/dgxmlparser.cc | 166 ++++++++++++++++++++++++++++------------ src/dgxmlparser.h | 10 ++- src/domloader.cc | 10 ++- src/domloader.h | 4 +- src/drumkitloader.cc | 33 +++++++- src/drumkitloader.h | 2 + src/logger.h | 38 +++++++++ src/powerlist.cc | 2 +- src/settings.h | 10 +++ test/audiocachefiletest.cc | 2 +- test/audiocachetest.cc | 4 +- testdist.sh | 2 +- 18 files changed, 292 insertions(+), 81 deletions(-) create mode 100644 src/logger.h diff --git a/drumgizmo/dgvalidator.cc b/drumgizmo/dgvalidator.cc index d7557aa..c8c192a 100644 --- a/drumgizmo/dgvalidator.cc +++ b/drumgizmo/dgvalidator.cc @@ -30,10 +30,27 @@ #include #include #include - +#include #include #include +void logger(LogLevel level, const std::string& message) +{ + switch(level) + { + case LogLevel::Info: + std::cout << "[Info]"; + break; + case LogLevel::Warning: + std::cout << "[Warning]"; + break; + case LogLevel::Error: + std::cout << "[Error]"; + break; + } + std::cout << " " << message << std::endl; +} + void printUsage(const char* prog, bool full = true) { printf("Usage: %s |-h|--help\n", prog); @@ -65,7 +82,7 @@ int main(int argc, char* argv[]) std::vector instrumentdoms; std::string path = getPath(edited_filename); bool parseerror = false; - bool ret = parseDrumkitFile(edited_filename, drumkitdom); + bool ret = parseDrumkitFile(edited_filename, drumkitdom, logger); if(!ret) { WARN(drumkitloader, "Drumkit file parser error: '%s'", @@ -77,7 +94,8 @@ int main(int argc, char* argv[]) for(const auto& ref : drumkitdom.instruments) { instrumentdoms.emplace_back(); - bool ret = parseInstrumentFile(path + "/" + ref.file, instrumentdoms.back()); + bool ret = parseInstrumentFile(path + "/" + ref.file, instrumentdoms.back(), + logger); if(!ret) { WARN(drumkitloader, "Instrument file parser error: '%s'", @@ -97,16 +115,18 @@ int main(int argc, char* argv[]) DrumKit kit; DOMLoader domloader(settings, rand); - ret = domloader.loadDom(path, drumkitdom, instrumentdoms, kit); + ret = domloader.loadDom(path, drumkitdom, instrumentdoms, kit, logger); if(!ret) { WARN(drumkitloader, "DOMLoader error"); + logger(LogLevel::Error, "Validator found errors."); return 1; } parseerror |= !ret; if(parseerror) { ERR(drumgizmo, "Drumkit parser failed: %s\n", edited_filename.c_str()); + logger(LogLevel::Error, "Validator found errors."); return 1; } @@ -115,15 +135,27 @@ int main(int argc, char* argv[]) { for(auto& audiofile: instrument->audiofiles) { - audiofile->load(1); + audiofile->load(logger, 1); if(!audiofile->isLoaded()) { WARN(drumkitloader, "Instrument file load error: '%s'", audiofile->filename.data()); + logger(LogLevel::Warning, "Error loading audio file '" + + audiofile->filename + "' in the '" + instrument->getName() + + "' instrument"); parseerror = true; } } } + if(parseerror) + { + logger(LogLevel::Warning, "Validator found errors."); + } + else + { + logger(LogLevel::Info, "Validator finished without errors."); + } + return parseerror ? 1 : 0; } diff --git a/plugingui/statusframecontent.cc b/plugingui/statusframecontent.cc index 34903e8..d457dee 100644 --- a/plugingui/statusframecontent.cc +++ b/plugingui/statusframecontent.cc @@ -29,9 +29,9 @@ namespace GUI { -StatusframeContent::StatusframeContent( - Widget* parent, SettingsNotifier& settings_notifier) - : Widget(parent), settings_notifier(settings_notifier) +StatusframeContent::StatusframeContent(Widget* parent, + SettingsNotifier& settings_notifier) + : Widget(parent), settings_notifier(settings_notifier) { CONNECT(this, settings_notifier.drumkit_load_status, this, &StatusframeContent::updateDrumkitLoadStatus); @@ -48,6 +48,9 @@ StatusframeContent::StatusframeContent( CONNECT(this, settings_notifier.number_of_underruns, this, &StatusframeContent::updateNumberOfUnderruns); + CONNECT(this, settings_notifier.load_status_text, + this, &StatusframeContent::loadStatusTextChanged); + text_field.move(0, 0); text_field.setReadOnly(true); @@ -95,21 +98,21 @@ void StatusframeContent::updateDrumkitLoadStatus(LoadStatus load_status) updateContent(); } -void StatusframeContent::updateDrumkitName(std::string const& drumkit_name) +void StatusframeContent::updateDrumkitName(const std::string& drumkit_name) { this->drumkit_name = drumkit_name; updateContent(); } -void StatusframeContent::updateDrumkitDescription(std::string const& drumkit_description) +void StatusframeContent::updateDrumkitDescription(const std::string& drumkit_description) { this->drumkit_description = drumkit_description; updateContent(); } -void StatusframeContent::updateDrumkitVersion(std::string const& drumkit_version) +void StatusframeContent::updateDrumkitVersion(const std::string& drumkit_version) { this->drumkit_version = drumkit_version; @@ -151,4 +154,9 @@ void StatusframeContent::updateNumberOfUnderruns(std::size_t number_of_underruns updateContent(); } +void StatusframeContent::loadStatusTextChanged(const std::string& text) +{ + text_field.setText(text); +} + } // GUI:: diff --git a/plugingui/statusframecontent.h b/plugingui/statusframecontent.h index 4ca4f63..dd44907 100644 --- a/plugingui/statusframecontent.h +++ b/plugingui/statusframecontent.h @@ -46,12 +46,13 @@ public: void updateContent(); void updateDrumkitLoadStatus(LoadStatus load_status); - void updateDrumkitName(std::string const& drumkit_name); - void updateDrumkitDescription(std::string const& drumkit_description); - void updateDrumkitVersion(std::string const& drumkit_version); + void updateDrumkitName(const std::string& drumkit_name); + void updateDrumkitDescription(const std::string& drumkit_description); + void updateDrumkitVersion(const std::string& drumkit_version); void updateMidimapLoadStatus(LoadStatus load_status); void updateBufferSize(std::size_t buffer_size); void updateNumberOfUnderruns(std::size_t number_of_underruns); + void loadStatusTextChanged(const std::string& text); private: TextEdit text_field{this}; diff --git a/src/Makefile.am b/src/Makefile.am index b85e1d3..56d44ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,7 @@ EXTRA_DIST = \ inputprocessor.h \ instrument.h \ latencyfilter.h \ + logger.h \ midimapparser.h \ midimapper.h \ nolocale.h \ diff --git a/src/audiofile.cc b/src/audiofile.cc index c2c5cf9..2d61eb5 100644 --- a/src/audiofile.cc +++ b/src/audiofile.cc @@ -73,7 +73,7 @@ void AudioFile::unload() #define BUFFER_SIZE 4096 -void AudioFile::load(std::size_t sample_limit) +void AudioFile::load(LogFunction logger, std::size_t sample_limit) { // Make sure we don't unload the object while loading it... std::lock_guard guard(mutex); @@ -89,12 +89,22 @@ void AudioFile::load(std::size_t sample_limit) { ERR(audiofile,"SNDFILE Error (%s): %s\n", filename.c_str(), sf_strerror(fh)); + if(logger) + { + logger(LogLevel::Warning, "Could not load '" + filename + + "': " + sf_strerror(fh)); + } return; } if(sf_info.channels < 1) { // This should never happen but lets check just in case. + if(logger) + { + logger(LogLevel::Warning, "Could not load '" + filename + + "': no audio channels available."); + } return; } @@ -116,6 +126,11 @@ void AudioFile::load(std::size_t sample_limit) // check filechannel exists if(filechannel >= (std::size_t)sf_info.channels) { + if(logger) + { + logger(LogLevel::Warning, "Audio file '" + filename + + "' does no have " + std::to_string(filechannel + 1) + " channels."); + } filechannel = sf_info.channels - 1; } diff --git a/src/audiofile.h b/src/audiofile.h index 5a5cc3a..e20d91f 100644 --- a/src/audiofile.h +++ b/src/audiofile.h @@ -36,6 +36,7 @@ #include "audio.h" #include "channel.h" +#include "logger.h" class InstrumentChannel; @@ -46,7 +47,7 @@ public: InstrumentChannel* instrument_channel = nullptr); ~AudioFile(); - void load(std::size_t sample_limit = std::numeric_limits::max()); + void load(LogFunction logger, std::size_t sample_limit = std::numeric_limits::max()); void unload(); bool isLoaded() const; 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); } } } diff --git a/src/dgxmlparser.h b/src/dgxmlparser.h index 1bc580d..21d045b 100644 --- a/src/dgxmlparser.h +++ b/src/dgxmlparser.h @@ -27,11 +27,13 @@ #pragma once #include +#include #include "DGDOM.h" +#include "logger.h" -bool probeDrumkitFile(const std::string& filename); -bool probeInstrumentFile(const std::string& filename); +bool probeDrumkitFile(const std::string& filename, LogFunction logger = nullptr); +bool probeInstrumentFile(const std::string& filename, LogFunction logger = nullptr); -bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom); -bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom); +bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom, LogFunction logger = nullptr); +bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom, LogFunction logger = nullptr); diff --git a/src/domloader.cc b/src/domloader.cc index 0e06239..a718ade 100644 --- a/src/domloader.cc +++ b/src/domloader.cc @@ -52,7 +52,7 @@ DOMLoader::DOMLoader(Settings& settings, Random& random) bool DOMLoader::loadDom(const std::string& basepath, const DrumkitDOM& dom, const std::vector& instrumentdoms, - DrumKit& drumkit) + DrumKit& drumkit, LogFunction logger) { settings.has_bleed_control.store(false); @@ -165,6 +165,9 @@ bool DOMLoader::loadDom(const std::string& basepath, { ERR(kitparser, "Missing channel '%s' in instrument '%s'\n", instrument_channel.name.c_str(), instrument->getName().c_str()); + logger(LogLevel::Warning, "Missing channel '" + + instrument_channel.name + "' in the '" + + instrument->getName() + "' instrument."); } } @@ -191,6 +194,9 @@ bool DOMLoader::loadDom(const std::string& basepath, ERR(kitparser, "Missing sample '%s' from sampleref in instrument '%s'\n", sampleref.name.data(), instrument->getName().data()); + logger(LogLevel::Warning, "Missing sample '" + + sampleref.name + "' in the '" + + instrument->getName() + "' instrument."); return false; } } @@ -209,6 +215,8 @@ bool DOMLoader::loadDom(const std::string& basepath, if(!found) { ERR(domloader, "No instrument with name '%s'", instrumentref.name.data()); + logger(LogLevel::Warning, "No instrument with name '" + + instrumentref.name + "'."); return false; } } diff --git a/src/domloader.h b/src/domloader.h index 2901560..103de27 100644 --- a/src/domloader.h +++ b/src/domloader.h @@ -29,6 +29,8 @@ #include #include +#include "logger.h" + struct DrumkitDOM; struct InstrumentDOM; class DrumKit; @@ -45,7 +47,7 @@ public: bool loadDom(const std::string& basepath, const DrumkitDOM& dom, const std::vector& instrumentdoms, - DrumKit& drumkit); + DrumKit& drumkit, LogFunction logger = nullptr); private: static InstrumentChannel* addOrGetChannel(Instrument& instrument, diff --git a/src/drumkitloader.cc b/src/drumkitloader.cc index 933e415..d9f65f0 100644 --- a/src/drumkitloader.cc +++ b/src/drumkitloader.cc @@ -50,6 +50,28 @@ DrumKitLoader::DrumKitLoader(Settings& settings, DrumKit& kit, , rand(rand) , audio_cache(audio_cache) { + logger = + [&](LogLevel level, const std::string& msg) + { + std::string message; + switch(level) + { + case LogLevel::Info: + //message = "[Info]"; + //break; + return; // Ignore info level messages + case LogLevel::Warning: + message = "[Warning]"; + break; + case LogLevel::Error: + message = "[Error]"; + break; + } + message += " " + msg + "\n"; + std::string status = settings.load_status_text.load(); + status += message; + settings.load_status_text.store(status); + }; } DrumKitLoader::~DrumKitLoader() @@ -108,6 +130,8 @@ bool DrumKitLoader::loadkit(const std::string& file) // Delete all Channels, Instruments, Samples and AudioFiles. kit.clear(); + settings.load_status_text.store(""); + settings.drumkit_load_status.store(LoadStatus::Loading); // Parse drumkit and instrument xml @@ -134,7 +158,7 @@ bool DrumKitLoader::loadkit(const std::string& file) std::vector instrumentdoms; std::string path = getPath(edited_filename); bool parseerror = false; - bool ret = parseDrumkitFile(edited_filename, drumkitdom); + bool ret = parseDrumkitFile(edited_filename, drumkitdom, logger); if(!ret) { WARN(drumkitloader, "Drumkit file parser error: '%s'", @@ -146,7 +170,8 @@ bool DrumKitLoader::loadkit(const std::string& file) for(const auto& ref : drumkitdom.instruments) { instrumentdoms.emplace_back(); - bool ret = parseInstrumentFile(path + "/" + ref.file, instrumentdoms.back()); + bool ret = parseInstrumentFile(path + "/" + ref.file, instrumentdoms.back(), + logger); if(!ret) { WARN(drumkitloader, "Instrument file parser error: '%s'", @@ -157,7 +182,7 @@ bool DrumKitLoader::loadkit(const std::string& file) } DOMLoader domloader(settings, rand); - ret = domloader.loadDom(path, drumkitdom, instrumentdoms, kit); + ret = domloader.loadDom(path, drumkitdom, instrumentdoms, kit, logger); if(!ret) { WARN(drumkitloader, "DOMLoader error"); @@ -339,7 +364,7 @@ void DrumKitLoader::thread_main() filename = audiofile->filename; try { - audiofile->load(preload_samples); + audiofile->load(logger, preload_samples); } catch(std::bad_alloc&) { diff --git a/src/drumkitloader.h b/src/drumkitloader.h index a2531d5..0dc6cca 100644 --- a/src/drumkitloader.h +++ b/src/drumkitloader.h @@ -95,4 +95,6 @@ protected: Random& rand; AudioCache& audio_cache; std::size_t preload_samples{std::numeric_limits::max()}; + + LogFunction logger; }; diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..bd5719e --- /dev/null +++ b/src/logger.h @@ -0,0 +1,38 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * logger.h + * + * Wed Jul 24 19:42:25 CEST 2019 + * Copyright 2019 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include + +enum class LogLevel +{ + Info, + Warning, + Error, +}; + +using LogFunction = std::function; diff --git a/src/powerlist.cc b/src/powerlist.cc index c78b071..23d9795 100644 --- a/src/powerlist.cc +++ b/src/powerlist.cc @@ -81,7 +81,7 @@ const Channel* PowerList::getMasterChannel() const Channel* c = pair.first; AudioFile* af = pair.second; - af->load(LOAD_SIZE); + af->load(nullptr, LOAD_SIZE); float silence{0.f}; std::size_t silence_length{4u}; diff --git a/src/settings.h b/src/settings.h index 64d27b7..97b1cf1 100644 --- a/src/settings.h +++ b/src/settings.h @@ -145,6 +145,9 @@ struct Settings Atomic audition_counter{0}; Atomic audition_instrument; Atomic audition_velocity; + + // Notify UI about load errors + Atomic load_status_text; }; //! Settings getter class. @@ -206,6 +209,8 @@ struct SettingsGetter SettingRef audition_instrument; SettingRef audition_velocity; + SettingRef load_status_text; + SettingsGetter(Settings& settings) : drumkit_file(settings.drumkit_file) , drumkit_load_status(settings.drumkit_load_status) @@ -250,6 +255,7 @@ struct SettingsGetter , audition_counter{settings.audition_counter} , audition_instrument{settings.audition_instrument} , audition_velocity{settings.audition_velocity} + , load_status_text{settings.load_status_text} { } }; @@ -312,6 +318,8 @@ public: Notifier audition_instrument; Notifier audition_velocity; + Notifier load_status_text; + void evaluate() { #define EVAL(x) if(settings.x.hasChanged()) { x(settings.x.getValue()); } @@ -369,6 +377,8 @@ public: EVAL(audition_counter); EVAL(audition_instrument); EVAL(audition_velocity); + + EVAL(load_status_text); } SettingsNotifier(Settings& settings) diff --git a/test/audiocachefiletest.cc b/test/audiocachefiletest.cc index 267d787..f1473de 100644 --- a/test/audiocachefiletest.cc +++ b/test/audiocachefiletest.cc @@ -99,7 +99,7 @@ public: for(size_t c = 0; c < 13; ++c) { ref_file[c] = new AudioFile(filename, c); - ref_file[c]->load(); + ref_file[c]->load(nullptr); } std::vector read_buffer; diff --git a/test/audiocachetest.cc b/test/audiocachetest.cc index 1ad863e..74c7df4 100644 --- a/test/audiocachetest.cc +++ b/test/audiocachetest.cc @@ -62,12 +62,12 @@ public: // Reference file: AudioFile audio_file_ref(filename, channel); printf("audio_file_ref.load\n"); - audio_file_ref.load(); + audio_file_ref.load(nullptr); // Input file: AudioFile audio_file(filename, channel); printf("audio_file.load\n"); - audio_file.load(4096); + audio_file.load(nullptr, 4096); Settings settings; AudioCache audio_cache(settings); diff --git a/testdist.sh b/testdist.sh index 288b072..46dce08 100755 --- a/testdist.sh +++ b/testdist.sh @@ -24,7 +24,7 @@ make dist && ( ./configure --enable-lv2 --enable-cli --enable-vst \ --with-vst-sources="$VST_BASE" --prefix=/usr \ --with-test --with-lv2dir=$PWD/tst/install/lv2 - make + V=0 make -j12 DESTDIR=$PWD/tst/install make install (cd plugin; make install) make check -- cgit v1.2.3