summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2019-07-25 17:49:16 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2019-09-28 20:37:47 +0200
commit9d1ea1e7cfa256c7f5cac027382d92f658734ccb (patch)
tree39ec7cdaa8ed571137430ccbbc0a391bb27427a3
parentcd9af98abdce1fe3759e786d6b010a7fe3395e2c (diff)
Add generic logger interface for propagating messages while loading drumkits to the commandline
and/or plugin UI.
-rw-r--r--drumgizmo/dgvalidator.cc42
-rw-r--r--plugingui/statusframecontent.cc20
-rw-r--r--plugingui/statusframecontent.h7
-rw-r--r--src/Makefile.am1
-rw-r--r--src/audiofile.cc17
-rw-r--r--src/audiofile.h3
-rw-r--r--src/dgxmlparser.cc166
-rw-r--r--src/dgxmlparser.h10
-rw-r--r--src/domloader.cc10
-rw-r--r--src/domloader.h4
-rw-r--r--src/drumkitloader.cc33
-rw-r--r--src/drumkitloader.h2
-rw-r--r--src/logger.h38
-rw-r--r--src/powerlist.cc2
-rw-r--r--src/settings.h10
-rw-r--r--test/audiocachefiletest.cc2
-rw-r--r--test/audiocachetest.cc4
-rwxr-xr-xtestdist.sh2
18 files changed, 292 insertions, 81 deletions
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 <random.h>
#include <settings.h>
#include <drumkit.h>
-
+#include <iostream>
#include <string>
#include <hugin.hpp>
+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 <drumkit>|-h|--help\n", prog);
@@ -65,7 +82,7 @@ int main(int argc, char* argv[])
std::vector<InstrumentDOM> 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<std::mutex> 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<std::size_t>::max());
+ void load(LogFunction logger, std::size_t sample_limit = std::numeric_limits<std::size_t>::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<typename T>
-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<typename T>
-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 <string>
+#include <functional>
#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<InstrumentDOM>& 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 <vector>
#include <string>
+#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<InstrumentDOM>& 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<InstrumentDOM> 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<std::size_t>::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 <functional>
+
+enum class LogLevel
+{
+ Info,
+ Warning,
+ Error,
+};
+
+using LogFunction = std::function<void (LogLevel, const std::string&)>;
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<std::size_t> audition_counter{0};
Atomic<std::string> audition_instrument;
Atomic<float> audition_velocity;
+
+ // Notify UI about load errors
+ Atomic<std::string> load_status_text;
};
//! Settings getter class.
@@ -206,6 +209,8 @@ struct SettingsGetter
SettingRef<std::string> audition_instrument;
SettingRef<float> audition_velocity;
+ SettingRef<std::string> 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<std::string> audition_instrument;
Notifier<int> audition_velocity;
+ Notifier<std::string> 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<sample_t> 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