From 2aae9799e1d96d827ad156aeafa549deabd51e5d Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sun, 27 Jan 2019 17:02:40 +0100 Subject: Re-instate instrument v1.0 support. --- drumgizmo/Makefile.am | 18 ++++++++- drumgizmo/dgvalidator.cc | 90 ++++++++++++++++++++++++++++++++++++++++++++ src/DGDOM.h | 20 +++++++++- src/dgxmlparser.cc | 46 ++++++++++++++++++++--- src/domloader.cc | 29 ++++++++++++++ src/inputprocessor.cc | 4 +- src/instrument.cc | 39 +++++++++++++++---- src/instrument.h | 7 +++- src/rangemap.h | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sample.cc | 6 +-- src/sample.h | 8 ++-- 11 files changed, 339 insertions(+), 26 deletions(-) create mode 100644 drumgizmo/dgvalidator.cc create mode 100644 src/rangemap.h diff --git a/drumgizmo/Makefile.am b/drumgizmo/Makefile.am index 5455726..40bddc9 100644 --- a/drumgizmo/Makefile.am +++ b/drumgizmo/Makefile.am @@ -2,7 +2,7 @@ DISTDIRS = input output if ENABLE_CLI -bin_PROGRAMS = drumgizmo +bin_PROGRAMS = drumgizmo dgvalidator drumgizmo_LDADD = $(JACK_LIBS) $(top_srcdir)/src/libdg.la @@ -101,4 +101,20 @@ EXTRA_DIST = \ output/oss.h \ output/wavfile.h +dgvalidator_LDADD = $(JACK_LIBS) $(top_srcdir)/src/libdg.la + +dgvalidator_LDFLAGS = + +dgvalidator_CXXFLAGS = \ + -I$(top_srcdir)/src -I$(top_srcdir)/getoptpp \ + -I$(top_srcdir)/hugin -DWITH_HUG_MUTEX -DWITH_HUG_FILTER \ + $(SSEFLAGS) + +dgvalidator_CFLAGS = -DWITH_HUG_MUTEX -DWITH_HUG_FILTER + +dgvalidator_SOURCES = \ + dgvalidator.cc \ + $(top_srcdir)/hugin/hugin.c \ + $(top_srcdir)/hugin/hugin_filter.c + endif # ENABLE_CLI diff --git a/drumgizmo/dgvalidator.cc b/drumgizmo/dgvalidator.cc new file mode 100644 index 0000000..d0cef8d --- /dev/null +++ b/drumgizmo/dgvalidator.cc @@ -0,0 +1,90 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * dgvalidator.cc + * + * Sun Jan 27 10:44:44 CET 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. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +int main(int argc, char* argv[]) +{ + std::string edited_filename = argv[1]; + DrumkitDOM drumkitdom; + std::vector instrumentdoms; + std::string path = getPath(edited_filename); + bool parseerror = false; + bool ret = parseDrumkitFile(edited_filename, drumkitdom); + if(!ret) + { + WARN(drumkitloader, "Drumkit file parser error: '%s'", + edited_filename.data()); + } + + parseerror |= !ret; + + for(const auto& ref : drumkitdom.instruments) + { + instrumentdoms.emplace_back(); + bool ret = parseInstrumentFile(path + "/" + ref.file, instrumentdoms.back()); + if(!ret) + { + WARN(drumkitloader, "Instrument file parser error: '%s'", + edited_filename.data()); + } + + parseerror |= !ret; + } + + if(parseerror) + { + return 1; + } + + Settings settings; + Random rand; + DrumKit kit; + + DOMLoader domloader(settings, rand); + ret = domloader.loadDom(path, drumkitdom, instrumentdoms, kit); + if(!ret) + { + WARN(drumkitloader, "DOMLoader error"); + return 1; + } + parseerror |= !ret; + if(parseerror) + { + ERR(drumgizmo, "Drumkit parser failed: %s\n", edited_filename.c_str()); + return 1; + } + + return 0; +} diff --git a/src/DGDOM.h b/src/DGDOM.h index df03515..933c250 100644 --- a/src/DGDOM.h +++ b/src/DGDOM.h @@ -31,6 +31,21 @@ #include "channel.h" +// v1.0 velocity groups + +struct SampleRefDOM +{ + double probability; + std::string name; +}; + +struct VelocityDOM +{ + double upper; + double lower; + std::vector samplerefs; +}; + // Instrument DOM: struct AudioFileDOM @@ -43,7 +58,7 @@ struct AudioFileDOM struct SampleDOM { std::string name; - double power; + double power; // >= v2.0 only std::vector audiofiles; }; @@ -60,6 +75,9 @@ struct InstrumentDOM std::string description; std::vector samples; std::vector instrument_channels; + + // v1.0 only + std::vector velocities; }; diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc index 9d3249e..fc5dbb9 100644 --- a/src/dgxmlparser.cc +++ b/src/dgxmlparser.cc @@ -179,20 +179,56 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom) res &= attrcpy(dom.instrument_channels.back().main, channel, "main", true); } + INFO(dgxmlparser, "XML version: %s\n", dom.version.data()); + pugi::xml_node samples = instrument.child("samples"); 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().power, sample, "power"); + + // Power only part of >= v2.0 instruments. + if(dom.version == "1.0") + { + dom.samples.back().power = 0.0; + } + else + { + res &= attrcpy(dom.samples.back().power, sample, "power"); + } 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"); - res &= attrcpy(dom.samples.back().audiofiles.back().file, audiofile, "file"); - dom.samples.back().audiofiles.back().filechannel = 1; // Defaults to channel 1 in mono (1-based) - res &= attrcpy(dom.samples.back().audiofiles.back().filechannel, audiofile, "filechannel", true); + res &= attrcpy(dom.samples.back().audiofiles.back().instrument_channel, + audiofile, "channel"); + res &= attrcpy(dom.samples.back().audiofiles.back().file, + audiofile, "file"); + // 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); + } + } + + // Velocity groups are only part of v1.0 instruments. + if(dom.version == "1.0") + { + pugi::xml_node velocities = instrument.child("velocities"); + for(pugi::xml_node velocity: velocities.children("velocity")) + { + dom.velocities.emplace_back(); + + res &= attrcpy(dom.velocities.back().lower, velocity, "lower"); + res &= attrcpy(dom.velocities.back().upper, velocity, "upper"); + for(pugi::xml_node sampleref: velocity.children("sampleref")) + { + dom.velocities.back().samplerefs.emplace_back(); + res &= attrcpy(dom.velocities.back().samplerefs.back().probability, + sampleref, "probability"); + res &= attrcpy(dom.velocities.back().samplerefs.back().name, + sampleref, "name"); + } } } diff --git a/src/domloader.cc b/src/domloader.cc index 663ef75..8a133fa 100644 --- a/src/domloader.cc +++ b/src/domloader.cc @@ -165,6 +165,35 @@ bool DOMLoader::loadDom(const std::string& basepath, } } + if(instrument->version == VersionStr(1,0,0)) + { + // Version 1.0 use velocity groups + for(auto& velocity : instrumentdom.velocities) + { + for(const auto& sampleref : velocity.samplerefs) + { + bool found_sample{false}; + for(const auto& sample : instrument->samplelist) + { + // TODO: What should be done with the probability?? + if(sample->name == sampleref.name) + { + instrument->addSample(velocity.lower, velocity.upper, sample); + found_sample = true; + break; + } + } + if(!found_sample) + { + ERR(kitparser, + "Missing sample '%s' from sampleref in instrument '%s'\n", + sampleref.name.data(), instrument->getName().data()); + return false; + } + } + } + } + instrument->finalise(); // Transfer ownership to the DrumKit object. diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index c004933..1570afe 100644 --- a/src/inputprocessor.cc +++ b/src/inputprocessor.cc @@ -147,7 +147,7 @@ bool InputProcessor::processOnset(event_t& event, } } - Sample* sample = instr->sample(event.velocity, event.offset + pos); + const auto sample = instr->sample(event.velocity, event.offset + pos); if(sample == nullptr) { @@ -162,7 +162,7 @@ bool InputProcessor::processOnset(event_t& event, for(Channel& ch: kit.channels) { - AudioFile* af = sample->getAudioFile(ch); + const auto af = sample->getAudioFile(ch); if(af == nullptr || !af->isValid()) { //DEBUG(inputprocessor, "Missing AudioFile.\n"); diff --git a/src/instrument.cc b/src/instrument.cc index 05ac17d..29619d8 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -54,21 +54,44 @@ bool Instrument::isValid() const return this == magic; } -Sample* Instrument::sample(level_t level, size_t pos) +const Sample* Instrument::sample(level_t level, size_t pos) { - return powerlist.get(level * mod); + if(version >= VersionStr("2.0")) + { + // Version 2.0 + return powerlist.get(level * mod); + } + else + { + // Version 1.0 + auto s = samples.get(level * mod); + if(s.size() == 0) + { + return nullptr; + } + + return rand.choose(s); + } +} + +void Instrument::addSample(level_t a, level_t b, const Sample* s) +{ + samples.insert(a, b, s); } void Instrument::finalise() { - std::vector::iterator s = samplelist.begin(); - while(s != samplelist.end()) + if(version >= VersionStr("2.0")) { - powerlist.add(*s); - s++; + std::vector::iterator s = samplelist.begin(); + while(s != samplelist.end()) + { + powerlist.add(*s); + s++; + } + + powerlist.finalise(); } - - powerlist.finalise(); } const std::string& Instrument::getName() const diff --git a/src/instrument.h b/src/instrument.h index ff33d6e..6e59681 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -31,6 +31,7 @@ #include #include +#include "rangemap.h" // for v1.0 kits #include "powerlist.h" #include "sample.h" @@ -45,7 +46,7 @@ public: Instrument(Settings& settings, Random& rand); ~Instrument(); - Sample* sample(level_t level, size_t pos); + const Sample* sample(level_t level, size_t pos); const std::string& getName() const; const std::string& getDescription() const; @@ -84,7 +85,9 @@ private: VersionStr version; - void addSample(level_t a, level_t b, Sample* s); + RangeMap samples; + + void addSample(level_t a, level_t b, const Sample* s); void finalise(); ///< Signal instrument that no more samples will be added. std::vector samplelist; diff --git a/src/rangemap.h b/src/rangemap.h new file mode 100644 index 0000000..e53cbe8 --- /dev/null +++ b/src/rangemap.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * rangemap.h + * + * Wed Sep 22 19:17:49 CEST 2010 + * Copyright 2010 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 +#include + +template class RangeMap +{ +public: + void insert(T1 from, T1 to, T2 value); + std::vector get(T1 from, T1 to); + std::vector get(T1 at); + +private: + friend class InstrumentParserTest; + std::multimap, T2> values; +}; + +template +void RangeMap::insert(T1 from, T1 to, T2 value) +{ + if(from < to) + { + values.insert(std::make_pair(std::make_pair(from, to), value)); + } + else + { + values.insert(std::make_pair(std::make_pair(to, from), value)); + } +} + +template +std::vector RangeMap::get(T1 from, T1 to) +{ + std::vector res; + + typename std::multimap, T2>::iterator i = values.begin(); + while(i != values.end()) + { + T1 a = i->first.first; + T1 b = i->first.second; + if((from >= a && to <= b) || // inside + (from <= a && to >= b) || // containing + (from <= a && to >= a && to <= b) || // overlapping lower + (from >= a && from <= b && to >= b) // overlapping upper + ) + { + res.push_back(i->second); + } + i++; + } + + return res; +} + +template std::vector RangeMap::get(T1 at) +{ + std::vector res; + + typename std::multimap, T2>::iterator i = values.begin(); + while(i != values.end()) + { + T1 a = i->first.first; + T1 b = i->first.second; + if(at >= a && at <= b) + { + res.push_back(i->second); + } + i++; + } + + return res; +} diff --git a/src/sample.cc b/src/sample.cc index c1795e8..4d0443d 100644 --- a/src/sample.cc +++ b/src/sample.cc @@ -28,7 +28,7 @@ #include -Sample::Sample(const std::string& name, float power) +Sample::Sample(const std::string& name, double power) : name{name} , power{power} , audiofiles{} @@ -44,7 +44,7 @@ void Sample::addAudioFile(InstrumentChannel* c, AudioFile* a) audiofiles[c] = a; } -AudioFile* Sample::getAudioFile(const Channel& channel) +AudioFile* Sample::getAudioFile(const Channel& channel) const { /* if(audiofiles.find(c) == audiofiles.end()) return nullptr; @@ -63,7 +63,7 @@ AudioFile* Sample::getAudioFile(const Channel& channel) return nullptr; } -float Sample::getPower() const +double Sample::getPower() const { return power; } diff --git a/src/sample.h b/src/sample.h index 5e41e9e..48f94ac 100644 --- a/src/sample.h +++ b/src/sample.h @@ -37,12 +37,12 @@ using AudioFiles = std::map; class Sample { public: - Sample(const std::string& name, float power); + Sample(const std::string& name, double power); ~Sample(); - AudioFile* getAudioFile(const Channel& channel); + AudioFile* getAudioFile(const Channel& channel) const; - float getPower() const; + double getPower() const; private: friend class DOMLoader; @@ -55,6 +55,6 @@ private: AudioFile* audio_file); std::string name; - float power; + double power; AudioFiles audiofiles; }; -- cgit v1.2.3