diff options
-rw-r--r-- | plugin/Makefile.mingw32.in | 3 | ||||
-rw-r--r-- | plugingui/powerwidget.cc | 1 | ||||
-rw-r--r-- | plugingui/powerwidget.h | 4 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/audioinputenginemidi.cc | 73 | ||||
-rw-r--r-- | src/curvemap.cc (renamed from src/powermap.cc) | 87 | ||||
-rw-r--r-- | src/curvemap.h (renamed from src/powermap.h) | 65 | ||||
-rw-r--r-- | src/midimapparser.cc | 24 | ||||
-rw-r--r-- | src/midimapper.cc | 56 | ||||
-rw-r--r-- | src/midimapper.h | 20 | ||||
-rw-r--r-- | src/parsecurvemap.cc | 60 | ||||
-rw-r--r-- | src/parsecurvemap.h (renamed from test/powermaptest.cc) | 36 | ||||
-rw-r--r-- | src/powermapfilter.h | 4 | ||||
-rw-r--r-- | test/Makefile.am | 17 | ||||
-rw-r--r-- | test/curvemaptest.cc | 237 | ||||
-rw-r--r-- | test/dgreftest/midiinputengine.cc | 13 | ||||
-rw-r--r-- | test/midimapparsertest.cc | 50 | ||||
-rw-r--r-- | test/midimappertest.cc | 77 | ||||
-rw-r--r-- | test/translationtest_resource_data.cc | 72 | ||||
-rwxr-xr-x | tools/add_file | 4 |
20 files changed, 735 insertions, 174 deletions
diff --git a/plugin/Makefile.mingw32.in b/plugin/Makefile.mingw32.in index ad47bcc..e932b9b 100644 --- a/plugin/Makefile.mingw32.in +++ b/plugin/Makefile.mingw32.in @@ -39,7 +39,8 @@ DG_SRC = \ @top_srcdir@/src/midimapper.cc \ @top_srcdir@/src/path.cc \ @top_srcdir@/src/powerlist.cc \ - @top_srcdir@/src/powermap.cc \ + @top_srcdir@/src/curvemap.cc \ + @top_srcdir@/src/parsecurvemap.cc \ @top_srcdir@/src/powermapfilter.cc \ @top_srcdir@/src/random.cc \ @top_srcdir@/src/sample.cc \ diff --git a/plugingui/powerwidget.cc b/plugingui/powerwidget.cc index 9be0c48..db37561 100644 --- a/plugingui/powerwidget.cc +++ b/plugingui/powerwidget.cc @@ -31,7 +31,6 @@ #include <notifier.h> #include <settings.h> -#include <powermap.h> #include <hugin.hpp> #include <cmath> diff --git a/plugingui/powerwidget.h b/plugingui/powerwidget.h index 3a7bb8e..3d09e6b 100644 --- a/plugingui/powerwidget.h +++ b/plugingui/powerwidget.h @@ -34,7 +34,7 @@ #include <dggui/label.h> #include <dggui/font.h> -#include <powermap.h> +#include <curvemap.h> struct Settings; class SettingsNotifier; @@ -75,7 +75,7 @@ private: virtual void mouseLeaveEvent() override; private: - Powermap power_map; + CurveMap power_map; void parameterChangedFloat(float); void parameterChangedBool(bool); diff --git a/src/Makefile.am b/src/Makefile.am index a8bdb59..9c83aab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,7 +67,8 @@ libdg_la_SOURCES = \ $(top_srcdir)/src/midimapper.cc \ $(top_srcdir)/src/path.cc \ $(top_srcdir)/src/powerlist.cc \ - $(top_srcdir)/src/powermap.cc \ + $(top_srcdir)/src/curvemap.cc \ + $(top_srcdir)/src/parsecurvemap.cc \ $(top_srcdir)/src/powermapfilter.cc \ $(top_srcdir)/src/random.cc \ $(top_srcdir)/src/sample.cc \ @@ -126,7 +127,8 @@ EXTRA_DIST = \ path.h \ platform.h \ powerlist.h \ - powermap.h \ + curvemap.h \ + parsecurvemap.h \ powermapfilter.h \ random.h \ range.h \ diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc index 2e794f4..f0e79ed 100644 --- a/src/audioinputenginemidi.cc +++ b/src/audioinputenginemidi.cc @@ -108,42 +108,57 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer, return; } - auto key = midi_buffer[1]; // NOLINT - span - auto velocity = midi_buffer[2]; // NOLINT - span - auto instrument_idx = mmap.lookup(key); - auto instruments = mmap.lookup(key); - for(const auto& instrument_idx : instruments) + switch(midi_buffer[0] & NoteMask) // NOLINT - span { - switch(midi_buffer[0] & NoteMask) // NOLINT - span - { - case NoteOff: - // Ignore for now - break; + case NoteOff: + // Ignore for now + break; - case NoteOn: - if(velocity != 0) + case NoteOn: + { + auto key = midi_buffer[1]; // NOLINT - span + auto velocity = midi_buffer[2]; // NOLINT - span + auto map_entries = mmap.lookup(key); + for(const auto& entry : map_entries) { - constexpr float lower_offset{0.5f}; - constexpr float midi_velocity_max{127.0f}; - // maps velocities to [.5/127, 126.5/127] - assert(velocity <= 127); // MIDI only support up to 127 - auto centered_velocity = - (static_cast<float>(velocity) - lower_offset) / midi_velocity_max; - events.push_back({EventType::OnSet, (std::size_t)instrument_idx, - offset, centered_velocity}); + auto instrument_idx = mmap.lookup_instrument(entry.instrument_name); + if(instrument_idx >= 0 && velocity != 0) + { + constexpr float lower_offset{0.5f}; + constexpr float midi_velocity_max{127.0f}; + // maps velocities to [.5/127, 126.5/127] + assert(velocity <= 127); // MIDI only support up to 127 + auto centered_velocity = + (static_cast<float>(velocity) - lower_offset) / midi_velocity_max; + if (entry.maybe_curve_map) + { + centered_velocity = entry.maybe_curve_map->map(centered_velocity); + } + events.push_back({EventType::OnSet, (std::size_t)instrument_idx, + offset, centered_velocity}); + } } - break; + } + break; - case NoteAftertouch: - if(velocity > 0) + case NoteAftertouch: + { + auto key = midi_buffer[1]; // NOLINT - span + auto velocity = midi_buffer[2]; // NOLINT - span + auto map_entries = mmap.lookup(key); + for(const auto& entry : map_entries) { - events.push_back({EventType::Choke, (std::size_t)instrument_idx, - offset, .0f}); + auto instrument_idx = mmap.lookup_instrument(entry.instrument_name); + if(instrument_idx >= 0 && velocity > 0) + { + events.push_back({EventType::Choke, (std::size_t)instrument_idx, + offset, .0f}); + } } - break; - - default: - break; } + break; + + default: + break; } } diff --git a/src/powermap.cc b/src/curvemap.cc index 2bb45b7..858c7b8 100644 --- a/src/powermap.cc +++ b/src/curvemap.cc @@ -24,7 +24,7 @@ * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include "powermap.h" +#include "curvemap.h" #include <cassert> #include <cmath> @@ -32,31 +32,31 @@ namespace { -using Power = Powermap::Power; -using PowerPair = Powermap::PowerPair; +using CurveValue = CurveMap::CurveValue; +using CurveValuePair = CurveMap::CurveValuePair; -Power h00(Power x) +CurveValue h00(CurveValue x) { return (1 + 2 * x) * pow(1 - x, 2); } -Power h10(Power x) +CurveValue h10(CurveValue x) { return x * pow(1 - x, 2); } -Power h01(Power x) +CurveValue h01(CurveValue x) { return x * x * (3 - 2 * x); } -Power h11(Power x) +CurveValue h11(CurveValue x) { return x * x * (x - 1); } -Power computeValue(const Power x, const PowerPair& P0, const PowerPair& P1, - const Power m0, const Power m1) +CurveValue computeValue(const CurveValue x, const CurveValuePair& P0, const CurveValuePair& P1, + const CurveValue m0, const CurveValue m1) { const auto x0 = P0.in; const auto x1 = P1.in; @@ -74,21 +74,25 @@ Power computeValue(const Power x, const PowerPair& P0, const PowerPair& P1, } // end anonymous namespace -Powermap::Powermap() +CurveMap::CurveMap() { reset(); } -Power Powermap::map(Power in) +CurveValue CurveMap::map(CurveValue in) { assert(in >= 0. && in <= 1.); + if (invert) + { + in = 1.0 - in; + } if (spline_needs_update) { updateSpline(); } - Power out; + CurveValue out; if (in < fixed[0].in) { out = shelf ? fixed[0].out @@ -113,18 +117,19 @@ Power Powermap::map(Power in) return out; } -void Powermap::reset() +void CurveMap::reset() { setFixed0({eps, eps}); setFixed1({.5, .5}); setFixed2({1 - eps, 1 - eps}); // FIXME: better false? shelf = true; + invert = false; updateSpline(); } -void Powermap::setFixed0(PowerPair new_value) +void CurveMap::setFixed0(CurveValuePair new_value) { if (fixed[0] != new_value) { @@ -134,7 +139,7 @@ void Powermap::setFixed0(PowerPair new_value) } } -void Powermap::setFixed1(PowerPair new_value) +void CurveMap::setFixed1(CurveValuePair new_value) { if (fixed[1] != new_value) { @@ -144,7 +149,7 @@ void Powermap::setFixed1(PowerPair new_value) } } -void Powermap::setFixed2(PowerPair new_value) +void CurveMap::setFixed2(CurveValuePair new_value) { if (fixed[2] != new_value) { @@ -154,7 +159,12 @@ void Powermap::setFixed2(PowerPair new_value) } } -void Powermap::setShelf(bool enable) +void CurveMap::setInvert(bool enable) +{ + invert = enable; +} + +void CurveMap::setShelf(bool enable) { if (shelf != enable) { @@ -163,34 +173,42 @@ void Powermap::setShelf(bool enable) } } -PowerPair Powermap::getFixed0() const +CurveValuePair CurveMap::getFixed0() const { return fixed[0]; } -PowerPair Powermap::getFixed1() const +CurveValuePair CurveMap::getFixed1() const { return fixed[1]; } -PowerPair Powermap::getFixed2() const +CurveValuePair CurveMap::getFixed2() const { return fixed[2]; } +bool CurveMap::getInvert() const { + return invert; +} + +bool CurveMap::getShelf() const { + return shelf; +} + // This mostly followes the wikipedia article for monotone cubic splines: // https://en.wikipedia.org/wiki/Monotone_cubic_interpolation -void Powermap::updateSpline() +void CurveMap::updateSpline() { assert(0. <= fixed[0].in && fixed[0].in < fixed[1].in && fixed[1].in < fixed[2].in && fixed[2].in <= 1.); assert(0. <= fixed[0].out && fixed[0].out <= fixed[1].out && fixed[1].out <= fixed[2].out && fixed[2].out <= 1.); - Powers X = shelf ? Powers{fixed[0].in, fixed[1].in, fixed[2].in} - : Powers{0., fixed[0].in, fixed[1].in, fixed[2].in, 1.}; - Powers Y = shelf ? Powers{fixed[0].out, fixed[1].out, fixed[2].out} - : Powers{0., fixed[0].out, fixed[1].out, fixed[2].out, 1.}; + CurveValues X = shelf ? CurveValues{fixed[0].in, fixed[1].in, fixed[2].in} + : CurveValues{0., fixed[0].in, fixed[1].in, fixed[2].in, 1.}; + CurveValues Y = shelf ? CurveValues{fixed[0].out, fixed[1].out, fixed[2].out} + : CurveValues{0., fixed[0].out, fixed[1].out, fixed[2].out, 1.}; auto slopes = calcSlopes(X, Y); @@ -215,12 +233,12 @@ void Powermap::updateSpline() // This follows the monotone cubic spline algorithm of Steffen, from: // "A Simple Method for Monotonic Interpolation in One Dimension" -std::vector<float> Powermap::calcSlopes(const Powers& X, const Powers& Y) +std::vector<float> CurveMap::calcSlopes(const CurveValues& X, const CurveValues& Y) { - Powers m(X.size()); + CurveValues m(X.size()); - Powers d(X.size() - 1); - Powers h(X.size() - 1); + CurveValues d(X.size() - 1); + CurveValues h(X.size() - 1); for (std::size_t i = 0; i < d.size(); ++i) { h[i] = X[i + 1] - X[i]; @@ -245,7 +263,16 @@ std::vector<float> Powermap::calcSlopes(const Powers& X, const Powers& Y) return m; } -Power Powermap::clamp(Power in, Power min, Power max) const +CurveValue CurveMap::clamp(CurveValue in, CurveValue min, CurveValue max) const { return std::max(min, std::min(in, max)); } + +bool CurveMap::operator==(const CurveMap& other) const +{ + return getFixed0() == other.getFixed0() && + getFixed1() == other.getFixed1() && + getFixed2() == other.getFixed2() && + getShelf() == other.getShelf() && + getInvert() == other.getInvert(); +} diff --git a/src/powermap.h b/src/curvemap.h index 3a406cc..c058bab 100644 --- a/src/powermap.h +++ b/src/curvemap.h @@ -1,6 +1,6 @@ /* -*- Mode: c++ -*- */ /*************************************************************************** - * powermap.h + * curvemap.h * * Fri Apr 17 23:06:12 CEST 2020 * Copyright 2020 André Nusser @@ -29,48 +29,71 @@ #include <array> #include <vector> -class Powermap +class CurveMapTestAccessor; + +class CurveMap { + friend class CurveMapTestAccessor; public: - using Power = float; - using Powers = std::vector<Power>; - struct PowerPair + using CurveValue = float; + using CurveValues = std::vector<CurveValue>; + + bool operator==(const CurveMap& other) const; + + struct CurveValuePair { - Power in; - Power out; + CurveValue in; + CurveValue out; - bool operator!=(const PowerPair& other) + bool operator==(const CurveValuePair& other) + { + return in == other.in || out == other.out; + } + bool operator!=(const CurveValuePair& other) { - return in != other.in || out != other.out; + return !(*this == other); } }; - Powermap(); + CurveMap(); - Power map(Power in); + CurveValue map(CurveValue in); void reset(); - void setFixed0(PowerPair new_value); - void setFixed1(PowerPair new_value); - void setFixed2(PowerPair new_value); + void setFixed0(CurveValuePair new_value); + void setFixed1(CurveValuePair new_value); + void setFixed2(CurveValuePair new_value); void setShelf(bool enable); - PowerPair getFixed0() const; - PowerPair getFixed1() const; - PowerPair getFixed2() const; + //! If enabled, inversion inverts (1 - x) the input value before mapping + //! it through the curve. + void setInvert(bool enable); + + CurveValuePair getFixed0() const; + CurveValuePair getFixed1() const; + CurveValuePair getFixed2() const; + bool getShelf() const; + bool getInvert() const; private: // input parameters (state of this class) - std::array<PowerPair, 3> fixed; + std::array<CurveValuePair, 3> fixed; bool shelf; + bool invert; // spline parameters (deterministically computed from the input parameters) bool spline_needs_update; std::array<float, 5> m; - const Power eps = 1e-4; + static constexpr CurveValue eps = 1e-4; void updateSpline(); - std::vector<float> calcSlopes(const Powers& X, const Powers& P); + std::vector<float> calcSlopes(const CurveValues& X, const CurveValues& P); + + CurveValue clamp(CurveValue in, CurveValue min, CurveValue max) const; +}; - Power clamp(Power in, Power min, Power max) const; +class CurveMapTestAccessor +{ +public: + static constexpr CurveMap::CurveValue eps = CurveMap::eps; }; diff --git a/src/midimapparser.cc b/src/midimapparser.cc index 363e1d5..70dfbc0 100644 --- a/src/midimapparser.cc +++ b/src/midimapparser.cc @@ -25,10 +25,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "midimapparser.h" +#include "parsecurvemap.h" #include <pugixml.hpp> #include <hugin.hpp> +#include <cpp11fix.h> + bool MidiMapParser::parseFile(const std::string& filename) { pugi::xml_document doc; @@ -42,15 +45,26 @@ bool MidiMapParser::parseFile(const std::string& filename) pugi::xml_node midimap_node = doc.child("midimap"); for(pugi::xml_node map_node : midimap_node.children("map")) { - constexpr int bad_value = 10000; - auto note = map_node.attribute("note").as_int(bad_value); - auto instr = map_node.attribute("instr").as_string(); - if(std::string(instr) == "" || note == bad_value) + constexpr int default_int = 10000; + auto note = map_node.attribute("note").as_int(default_int); + auto instr = std::string(map_node.attribute("instr").as_string()); + + std::unique_ptr<CurveMap> maybe_curve; + auto maybe_curve_node = map_node.child("curve"); + if (maybe_curve_node) { + CurveMap curve; + if (parse_curve_map (maybe_curve_node, curve)) { + maybe_curve = std::make_unique<CurveMap>(); + *maybe_curve = curve; + } + } + + if(std::string(instr) == "" || note == default_int) { continue; } - MidimapEntry entry{note, instr}; + MidimapEntry entry(note, instr, maybe_curve.get()); midimap.push_back(entry); } diff --git a/src/midimapper.cc b/src/midimapper.cc index 345ce2f..8b44dde 100644 --- a/src/midimapper.cc +++ b/src/midimapper.cc @@ -25,26 +25,64 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "midimapper.h" +#include <set> -std::vector<int> MidiMapper::lookup(int note_id) +#include <cpp11fix.h> + +MidimapEntry::MidimapEntry(int note_id, + std::string instrument_name, + CurveMap *maybe_curve_map) : + note_id(note_id) + , instrument_name(instrument_name) +{ + if (maybe_curve_map) + { + this->maybe_curve_map = std::make_unique<CurveMap>(*maybe_curve_map); + } +} + +MidimapEntry::MidimapEntry(const MidimapEntry& other) +{ + *this = other; +} + +MidimapEntry &MidimapEntry::operator=(const MidimapEntry& other) +{ + note_id = other.note_id; + instrument_name = other.instrument_name; + if (other.maybe_curve_map) + { + maybe_curve_map = std::make_unique<CurveMap>(*other.maybe_curve_map); + } + + return *this; +} + +int MidiMapper::lookup_instrument(std::string name) { + const std::lock_guard<std::mutex> guard(mutex); + auto instrmap_it = instrmap.find(name); + if(instrmap_it != instrmap.end()) + { + return instrmap_it->second; + } + return -1; +} + +std::vector<MidimapEntry> MidiMapper::lookup(int note_id) { - std::vector<int> instruments; + std::vector<MidimapEntry> rval; const std::lock_guard<std::mutex> guard(mutex); for(const auto& map_entry : midimap) { - if(map_entry.note_id == note_id) + if(note_id == map_entry.note_id) { - auto instrmap_it = instrmap.find(map_entry.instrument_name); - if(instrmap_it != instrmap.end()) - { - instruments.push_back(instrmap_it->second); - } + rval.push_back(map_entry); } } - return instruments; + return rval; } void MidiMapper::swap(instrmap_t& instrmap, midimap_t& midimap) diff --git a/src/midimapper.h b/src/midimapper.h index 94781d4..ee1e8d0 100644 --- a/src/midimapper.h +++ b/src/midimapper.h @@ -30,11 +30,24 @@ #include <string> #include <mutex> #include <vector> +#include <memory> + +#include "curvemap.h" struct MidimapEntry { int note_id; std::string instrument_name; + + //! An optional curve map which will map the given velocity + //! or CC value to a new value. + std::unique_ptr<CurveMap> maybe_curve_map; + + MidimapEntry &operator=(const MidimapEntry& other); + MidimapEntry(const MidimapEntry& other); + MidimapEntry(int note_id, + std::string instrument_name, + CurveMap *maybe_curve_map = nullptr); }; using midimap_t = std::vector<MidimapEntry>; @@ -43,8 +56,11 @@ using instrmap_t = std::map<std::string, int>; class MidiMapper { public: - //! Lookup note in map and returns the corresponding instrument index list. - std::vector<int> lookup(int note_id); + //! Lookup midi map entries matching the given note. + std::vector<MidimapEntry> lookup(int note_id); + + //! Lookup instrument by name. + int lookup_instrument(std::string name); //! Set new map sets. void swap(instrmap_t& instrmap, midimap_t& midimap); diff --git a/src/parsecurvemap.cc b/src/parsecurvemap.cc new file mode 100644 index 0000000..f6862e1 --- /dev/null +++ b/src/parsecurvemap.cc @@ -0,0 +1,60 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * parsecurvemap.cc + * + * Wed Jul 24 12:55:00 CEST 2024 + * Copyright 2024 Sander Vocke + * + ****************************************************************************/ + +/* + * 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 "parsecurvemap.h" + +bool parse_curve_map(pugi::xml_node curvemap_node, CurveMap &out) +{ + CurveMap rval; + + if(curvemap_node.attribute("shelf")) { + rval.setShelf(curvemap_node.attribute("shelf").as_bool(true)); + } + if(curvemap_node.attribute("invert")) { + rval.setInvert(curvemap_node.attribute("invert").as_bool(false)); + } + if(curvemap_node.attribute("in2") && curvemap_node.attribute("out2")) { + rval.setFixed2( CurveMap::CurveValuePair{ + curvemap_node.attribute("in2").as_float(0.0f), + curvemap_node.attribute("out2").as_float(0.0f) + }); + } + if(curvemap_node.attribute("in1") && curvemap_node.attribute("out1")) { + rval.setFixed1( CurveMap::CurveValuePair{ + curvemap_node.attribute("in1").as_float(0.0f), + curvemap_node.attribute("out1").as_float(0.0f) + }); + } + if(curvemap_node.attribute("in0") && curvemap_node.attribute("out0")) { + rval.setFixed0( CurveMap::CurveValuePair{ + curvemap_node.attribute("in0").as_float(0.0f), + curvemap_node.attribute("out0").as_float(0.0f) + }); + } + + out = rval; + return true; +}
\ No newline at end of file diff --git a/test/powermaptest.cc b/src/parsecurvemap.h index bef5bdc..a6f0429 100644 --- a/test/powermaptest.cc +++ b/src/parsecurvemap.h @@ -1,10 +1,10 @@ /* -*- Mode: c++ -*- */ /*************************************************************************** - * powermaptest.cc + * parsecurvemap.h + * + * Wed Jul 24 12:55:00 CEST 2024 + * Copyright 2024 Sander Vocke * - * Sun Apr 19 23:23:37 CEST 2020 - * Copyright 2020 André Nusser - * andre.nusser@googlemail.com ****************************************************************************/ /* @@ -24,28 +24,10 @@ * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <uunit.h> - -#include "../src/powermap.h" - -class test_powermaptest - : public uUnit -{ -public: - test_powermaptest() - { - uUNIT_TEST(test_powermaptest::check_values); - } - - void check_values() - { - Powermap powermap; +#pragma once - // TODO - // std::cout << powermap.map(.8) << std::endl; - // uUNIT_ASSERT_EQUAL(powermap.map(.8), .8); - } -}; +#include <pugixml.hpp> +#include "curvemap.h" -// Registers the fixture into the 'registry' -static test_powermaptest test; +// Returns true if success (out is replaced), false if failure (out unchanged) +bool parse_curve_map(pugi::xml_node curvemap_node, CurveMap &out);
\ No newline at end of file diff --git a/src/powermapfilter.h b/src/powermapfilter.h index 263f809..b9dfe5b 100644 --- a/src/powermapfilter.h +++ b/src/powermapfilter.h @@ -27,7 +27,7 @@ #pragma once #include "inputfilter.h" -#include "powermap.h" +#include "curvemap.h" struct Settings; @@ -43,5 +43,5 @@ public: private: Settings& settings; - Powermap powermap; + CurveMap powermap; }; diff --git a/test/Makefile.am b/test/Makefile.am index 15ceb7d..5550816 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -8,7 +8,7 @@ TESTS = resource enginetest paintertest configfile audiocache \ randomtest atomictest syncedsettingstest imagecachetest \ semaphoretest drumkitcreatortest bytesizeparsertest notifiertest \ dgxmlparsertest domloadertest configparsertest midimapparsertest \ - eventsdstest powermaptest midimappertest + eventsdstest curvemaptest midimappertest if WITH_NLS TESTS += translationtest @@ -314,6 +314,9 @@ midimapparsertest_LDFLAGS = midimapparsertest_SOURCES = \ $(top_srcdir)/hugin/hugin.c \ $(top_srcdir)/src/midimapparser.cc \ + $(top_srcdir)/src/curvemap.cc \ + $(top_srcdir)/src/parsecurvemap.cc \ + $(top_srcdir)/src/midimapper.cc \ $(top_srcdir)/pugixml/src/pugixml.cpp \ scopedfile.cc \ midimapparsertest.cc \ @@ -329,14 +332,14 @@ eventsdstest_SOURCES = \ eventsdstest.cc \ uunit/uunit.cc -powermaptest_CXXFLAGS = \ - -I$(top_srcdir)/test/uunit -DOUTPUT=\"powermaptest\" \ +curvemaptest_CXXFLAGS = \ + -I$(top_srcdir)/test/uunit -DOUTPUT=\"curvemaptest\" \ $(DEBUG_FLAGS) \ -I$(top_srcdir)/src -powermaptest_LDFLAGS = -powermaptest_SOURCES = \ - $(top_srcdir)/src/powermap.cc \ - powermaptest.cc \ +curvemaptest_LDFLAGS = +curvemaptest_SOURCES = \ + $(top_srcdir)/src/curvemap.cc \ + curvemaptest.cc \ uunit/uunit.cc midimappertest_CXXFLAGS = \ diff --git a/test/curvemaptest.cc b/test/curvemaptest.cc new file mode 100644 index 0000000..8bbfa10 --- /dev/null +++ b/test/curvemaptest.cc @@ -0,0 +1,237 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * curvemaptest.cc + * + * Sun Apr 19 23:23:37 CEST 2020 + * Copyright 2020 André Nusser + * andre.nusser@googlemail.com + ****************************************************************************/ + +/* + * 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 <uunit.h> +#include <map> + +#include "../src/curvemap.h" +#include <iostream> +#include <iomanip> + +class test_curvemaptest + : public uUnit +{ +public: + test_curvemaptest() + { + uUNIT_TEST(test_curvemaptest::check_default); + uUNIT_TEST(test_curvemaptest::check_default_invert); + uUNIT_TEST(test_curvemaptest::check_default_disabled_shelf); + uUNIT_TEST(test_curvemaptest::check_default_disabled_shelf_invert); + uUNIT_TEST(test_curvemaptest::check_fixed1_075_025); + uUNIT_TEST(test_curvemaptest::check_fixed1_075_025_invert); + uUNIT_TEST(test_curvemaptest::check_shelf_060); + uUNIT_TEST(test_curvemaptest::check_shelf_060_invert); + // uUNIT_TEST(test_curvemaptest::check_fixed0_075_025_invert); + } + + static constexpr auto eps = CurveMapTestAccessor::eps; + + const std::map<double, double> default_map = { + {0.0, eps}, + {eps, eps}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.7}, + {0.8, 0.8}, + {0.9, 0.9}, + {1.0 - eps, 1.0 - eps}, + {1.0, 1.0 - eps} + }; + + const std::map<double, double> identity_map = { + {0.0, 0.0}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.7}, + {0.8, 0.8}, + {0.9, 0.9}, + {1.0, 1.0} + }; + + const std::map<double, double> fixed1_075_025_map = { + {0.0, eps}, + {eps, eps}, + {0, 0.0001}, + {0.1, 0.0295469705015421}, + {0.2, 0.0536915548145771}, + {0.3, 0.0760560110211372}, + {0.4, 0.100195862352848}, + {0.5, 0.129666686058044}, + {0.6, 0.168024003505707}, + {0.7, 0.218823373317719}, + {0.8, 0.325357049703598}, + {0.9, 0.64416378736496}, + {1.0 - eps, 1.0 - eps}, + {1.0, 1.0 - eps} + }; + + const std::map<double, double> shelf_060_map = { + {0.0, eps}, + {eps, eps}, + {0.1, 0.1}, + {0.2, 0.2}, + {0.3, 0.3}, + {0.4, 0.4}, + {0.5, 0.5}, + {0.6, 0.6}, + {0.7, 0.6}, + {0.8, 0.6}, + {0.9, 0.6}, + {1.0, 0.6} + }; + + void check_default() + { + auto dataset = this->default_map; + + CurveMap map; + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_invert() + { + auto dataset = this->default_map; + + CurveMap map; + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_disabled_shelf() + { + auto dataset = this->identity_map; + + CurveMap map; + map.setShelf(false); + map.setFixed2({0.6, 0.6}); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + // std::cout << "{" << in << ", " << std::setprecision (15) << map.map(in) << "}," << std::endl; // FIXME + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_default_disabled_shelf_invert() + { + auto dataset = this->identity_map; + + CurveMap map; + map.setShelf(false); + map.setFixed2({0.6, 0.6}); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_fixed1_075_025() + { + auto dataset = this->fixed1_075_025_map; + + CurveMap map; + map.setFixed1({0.75, 0.25}); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + // std::cout << "{" << in << ", " << std::setprecision (15) << map.map(in) << "}," << std::endl; // FIXME + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_fixed1_075_025_invert() + { + auto dataset = this->fixed1_075_025_map; + + CurveMap map; + map.setFixed1({0.75, 0.25}); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_shelf_060() + { + auto dataset = this->shelf_060_map; + + CurveMap map; + map.setFixed2({0.6, 0.6}); + map.setShelf(true); + + for (auto& entry : dataset) { + auto in = entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } + + void check_shelf_060_invert() + { + auto dataset = this->shelf_060_map; + + CurveMap map; + map.setFixed2({0.6, 0.6}); + map.setShelf(true); + map.setInvert(true); + + for (auto& entry : dataset) { + auto in = 1.0 - entry.first; + auto expect = entry.second; + uASSERT_EQUAL(expect, map.map(in)); + } + } +}; + +// Registers the fixture into the 'registry' +static test_curvemaptest test; diff --git a/test/dgreftest/midiinputengine.cc b/test/dgreftest/midiinputengine.cc index dbffec9..4827e5f 100644 --- a/test/dgreftest/midiinputengine.cc +++ b/test/dgreftest/midiinputengine.cc @@ -155,9 +155,14 @@ void MidifileInputEngine::run(size_t pos, size_t len, std::vector<event_t>& even int key = current_event->midi_buffer[1]; int velocity = current_event->midi_buffer[2]; - auto instruments = mmap.lookup(key); - for(const auto& instrument_idx : instruments) + auto entries = mmap.lookup(key); + for(const auto& entry : entries) { + auto instrument_idx = mmap.lookup_instrument(entry.instrument_name); + if (instrument_idx < 0) + { + continue; + } events.emplace_back(); auto& event = events.back(); event.type = EventType::OnSet; @@ -165,6 +170,10 @@ void MidifileInputEngine::run(size_t pos, size_t len, std::vector<event_t>& even event.offset = evpos - pos; event.instrument = instrument_idx; event.velocity = velocity / 127.0; + if (entry.maybe_curve_map) + { + event.velocity = entry.maybe_curve_map->map(event.velocity); + } } } } diff --git a/test/midimapparsertest.cc b/test/midimapparsertest.cc index 3e77c44..0b892e9 100644 --- a/test/midimapparsertest.cc +++ b/test/midimapparsertest.cc @@ -27,6 +27,7 @@ #include <uunit.h> #include <midimapparser.h> +#include <curvemap.h> #include "scopedfile.h" @@ -36,11 +37,12 @@ class MidimapParserTest public: MidimapParserTest() { - uTEST(MidimapParserTest::test); - uTEST(MidimapParserTest::invalid); + uTEST(MidimapParserTest::test_basic); + uTEST(MidimapParserTest::test_curve); + uTEST(MidimapParserTest::test_invalid); } - void test() + void test_basic() { ScopedFile scoped_file( "<?xml version='1.0' encoding='UTF-8'?>\n" \ @@ -80,7 +82,47 @@ public: uASSERT_EQUAL(std::string("Hihat_closed"), midimap[5].instrument_name); } - void invalid() + void test_curve() + { + ScopedFile scoped_file( + "<?xml version='1.0' encoding='UTF-8'?>\n" \ + "<midimap>\n" \ + " <map note=\"56\" instr=\"Hihat_closed\"/>\n" \ + " <map note=\"40\" instr=\"Kick\">\n" \ + " <curve in0=\"0.1\" out0=\"0.2\" in1=\"0.5\" out1=\"0.6\" in2=\"0.8\" out2=\"0.9\" invert=\"true\" shelf=\"false\"/>\n" \ + " </map>\n" \ + " <map note=\"41\" instr=\"Snare\">\n" \ + " <curve/>\n" \ + " </map>\n" \ + "</midimap>"); + + MidiMapParser parser; + uASSERT(parser.parseFile(scoped_file.filename())); + + const auto& midimap = parser.midimap; + uASSERT_EQUAL(3u, midimap.size()); + + uASSERT_EQUAL(56, midimap[0].note_id); + uASSERT(!midimap[0].maybe_curve_map); + + uASSERT_EQUAL(40, midimap[1].note_id); + uASSERT(midimap[1].maybe_curve_map.get()); + uASSERT_EQUAL(true, midimap[1].maybe_curve_map->getInvert()); + uASSERT_EQUAL(false, midimap[1].maybe_curve_map->getShelf()); + uASSERT_EQUAL(0.1, midimap[1].maybe_curve_map->getFixed0().in); + uASSERT_EQUAL(0.2, midimap[1].maybe_curve_map->getFixed0().out); + uASSERT_EQUAL(0.5, midimap[1].maybe_curve_map->getFixed1().in); + uASSERT_EQUAL(0.6, midimap[1].maybe_curve_map->getFixed1().out); + uASSERT_EQUAL(0.8, midimap[1].maybe_curve_map->getFixed2().in); + uASSERT_EQUAL(0.9, midimap[1].maybe_curve_map->getFixed2().out); + + uASSERT_EQUAL(41, midimap[2].note_id); + CurveMap reference_map; + uASSERT(midimap[2].maybe_curve_map.get()); + uASSERT(reference_map == *midimap[2].maybe_curve_map); + } + + void test_invalid() { ScopedFile scoped_file( "<?xml version='1.0' encoding='UTF-8'?>\n" \ diff --git a/test/midimappertest.cc b/test/midimappertest.cc index 703c646..bf80266 100644 --- a/test/midimappertest.cc +++ b/test/midimappertest.cc @@ -26,6 +26,7 @@ #include <uunit.h> #include <algorithm> +#include <vector> #include <midimapper.h> @@ -45,12 +46,12 @@ public: { midimap_t midimap { - { 54, "Crash_left_tip" }, - { 60, "Crash_left_whisker" }, - { 55, "Crash_right_tip" }, - { 62, "Crash_right_whisker" }, - { 62, "Hihat_closed" }, - { 56, "Hihat_closed" }, + MidimapEntry(54, "Crash_left_tip"), + MidimapEntry(60, "Crash_left_whisker"), + MidimapEntry(55, "Crash_right_tip"), + MidimapEntry(62, "Crash_right_whisker"), + MidimapEntry(62, "Hihat_closed"), + MidimapEntry(56, "Hihat_closed"), }; instrmap_t instrmap @@ -66,35 +67,48 @@ public: mapper.swap(instrmap, midimap); { - auto is = mapper.lookup(54); - uASSERT_EQUAL(1u, is.size()); - uASSERT_EQUAL(0, is[0]); + auto es = mapper.lookup(54); + uASSERT_EQUAL(1u, es.size()); + + auto i = mapper.lookup_instrument(es[0].instrument_name); + uASSERT_EQUAL(0, i); } { - auto is = mapper.lookup(60); - uASSERT_EQUAL(1u, is.size()); - uASSERT_EQUAL(1, is[0]); + auto es = mapper.lookup(60); + uASSERT_EQUAL(1u, es.size()); + + auto i = mapper.lookup_instrument(es[0].instrument_name); + uASSERT_EQUAL(1, i); } { - auto is = mapper.lookup(55); - uASSERT_EQUAL(1u, is.size()); - uASSERT_EQUAL(2, is[0]); + auto es = mapper.lookup(55); + uASSERT_EQUAL(1u, es.size()); + + auto i = mapper.lookup_instrument(es[0].instrument_name); + uASSERT_EQUAL(2, i); } { - auto is = mapper.lookup(62); - uASSERT_EQUAL(2u, is.size()); + auto es = mapper.lookup(62); + uASSERT_EQUAL(2u, es.size()); + + std::vector<int> is; + is.push_back(mapper.lookup_instrument(es[0].instrument_name)); + is.push_back(mapper.lookup_instrument(es[1].instrument_name)); + // We don't care about the order, so just count the instances uASSERT_EQUAL(1u, std::count(is.begin(), is.end(), 3)); uASSERT_EQUAL(1u, std::count(is.begin(), is.end(), 4)); } { - auto is = mapper.lookup(56); - uASSERT_EQUAL(1u, is.size()); - uASSERT_EQUAL(4, is[0]); + auto es = mapper.lookup(56); + uASSERT_EQUAL(1u, es.size()); + + auto i = mapper.lookup_instrument(es[0].instrument_name); + uASSERT_EQUAL(4, i); } } @@ -102,12 +116,12 @@ public: { midimap_t midimap { - { 54, "Crash_left_tip" }, - { 60, "Crash_left_whisker_MISSING" }, - { 55, "Crash_right_tip" }, - { 62, "Crash_right_whisker" }, - { 62, "Hihat_closed" }, - { 56, "Hihat_closed" }, + MidimapEntry(54, "Crash_left_tip" ), + MidimapEntry(60, "Crash_left_whisker_MISSING" ), + MidimapEntry(55, "Crash_right_tip" ), + MidimapEntry(62, "Crash_right_whisker" ), + MidimapEntry(62, "Hihat_closed" ), + MidimapEntry(56, "Hihat_closed" ), }; instrmap_t instrmap @@ -124,14 +138,17 @@ public: // no such note id { - auto is = mapper.lookup(42); - uASSERT_EQUAL(0u, is.size()); + auto es = mapper.lookup(42); + uASSERT_EQUAL(0u, es.size()); } // no such instrument { - auto is = mapper.lookup(60); - uASSERT_EQUAL(0u, is.size()); + auto es = mapper.lookup(60); + uASSERT_EQUAL(1u, es.size()); + + auto is = mapper.lookup_instrument(es[0].instrument_name); + uASSERT_EQUAL(-1, is); } } }; diff --git a/test/translationtest_resource_data.cc b/test/translationtest_resource_data.cc new file mode 100644 index 0000000..7e25737 --- /dev/null +++ b/test/translationtest_resource_data.cc @@ -0,0 +1,72 @@ +/* This file is autogenerated by rcgen. Do not modify! */ +#include <dggui/resource_data.h> + +const rc_data_t rc_dataX[] = +{ + { + ":locale/da.mo", 948, + "\336\22\4\225\0\0\0\0\15\0\0\0\34\0\0\0" + "\204\0\0\0\21\0\0\0\354\0\0\0\0\0\0\0" + "\60\1\0\0\5\0\0\0\61\1\0\0\15\0\0\0" + "\67\1\0\0\16\0\0\0\105\1\0\0\13\0\0\0" + "\124\1\0\0\7\0\0\0\140\1\0\0\4\0\0\0" + "\150\1\0\0\12\0\0\0\155\1\0\0\20\0\0\0" + "\170\1\0\0\6\0\0\0\211\1\0\0\20\0\0\0" + "\220\1\0\0\22\0\0\0\241\1\0\0\12\0\0\0" + "\264\1\0\0\141\1\0\0\277\1\0\0\2\0\0\0" + "\41\3\0\0\17\0\0\0\44\3\0\0\16\0\0\0" + "\64\3\0\0\1\0\0\0\103\3\0\0\11\0\0\0" + "\105\3\0\0\11\0\0\0\117\3\0\0\12\0\0\0" + "\131\3\0\0\21\0\0\0\144\3\0\0\10\0\0\0" + "\166\3\0\0\17\0\0\0\177\3\0\0\26\0\0\0" + "\217\3\0\0\15\0\0\0\246\3\0\0\1\0\0\0" + "\14\0\0\0\0\0\0\0\15\0\0\0\11\0\0\0" + "\0\0\0\0\5\0\0\0\0\0\0\0\7\0\0\0" + "\2\0\0\0\12\0\0\0\0\0\0\0\3\0\0\0" + "\10\0\0\0\6\0\0\0\13\0\0\0\4\0\0\0" + "\0\101\142\157\165\164\0\102\154\145\145\144\40\103\157\156" + "\164\162\157\154\0\104\151\163\153\40\123\164\162\145\141\155" + "\151\156\147\0\104\162\165\155\107\151\172\155\157\40\166\0" + "\104\162\165\155\153\151\164\0\115\141\151\156\0\122\145\163" + "\141\155\160\154\151\156\147\0\123\141\155\160\154\145\40\123" + "\145\154\145\143\164\151\157\156\0\123\164\141\164\165\163\0" + "\124\151\155\151\156\147\40\110\165\155\141\156\151\172\145\162" + "\0\126\145\154\157\143\151\164\171\40\110\165\155\141\156\151" + "\172\145\162\0\126\151\163\165\141\154\151\172\145\162\0\120" + "\162\157\152\145\143\164\55\111\144\55\126\145\162\163\151\157" + "\156\72\40\144\162\165\155\147\151\172\155\157\40\60\56\71" + "\56\61\67\12\122\145\160\157\162\164\55\115\163\147\151\144" + "\55\102\165\147\163\55\124\157\72\40\12\120\117\124\55\103" + "\162\145\141\164\151\157\156\55\104\141\164\145\72\40\62\60" + "\61\71\55\60\71\55\61\63\40\62\61\72\60\67\53\60" + "\62\60\60\12\120\117\55\122\145\166\151\163\151\157\156\55" + "\104\141\164\145\72\40\62\60\61\71\55\60\71\55\61\63" + "\40\61\71\72\64\62\53\60\62\60\60\12\114\141\163\164" + "\55\124\162\141\156\163\154\141\164\157\162\72\40\101\165\164" + "\157\155\141\164\151\143\141\154\154\171\40\147\145\156\145\162" + "\141\164\145\144\12\114\141\156\147\165\141\147\145\55\124\145" + "\141\155\72\40\156\157\156\145\12\114\141\156\147\165\141\147" + "\145\72\40\144\141\12\115\111\115\105\55\126\145\162\163\151" + "\157\156\72\40\61\56\60\12\103\157\156\164\145\156\164\55" + "\124\171\160\145\72\40\164\145\170\164\57\160\154\141\151\156" + "\73\40\143\150\141\162\163\145\164\75\111\123\117\55\70\70" + "\65\71\55\61\12\103\157\156\164\145\156\164\55\124\162\141" + "\156\163\146\145\162\55\105\156\143\157\144\151\156\147\72\40" + "\70\142\151\164\12\120\154\165\162\141\154\55\106\157\162\155" + "\163\72\40\156\160\154\165\162\141\154\163\75\62\73\40\160" + "\154\165\162\141\154\75\50\156\40\41\75\40\61\51\73\12" + "\0\117\155\0\117\166\145\162\150\370\162\40\153\157\156\164" + "\162\157\154\0\104\151\163\153\40\163\164\162\145\141\155\151" + "\156\147\0\130\0\124\162\157\155\155\145\163\346\164\0\110" + "\157\166\145\144\146\141\156\145\0\122\145\163\141\155\160\154" + "\151\156\147\0\114\171\144\142\151\144\40\165\144\166\346\154" + "\147\145\154\163\145\0\124\151\154\163\164\141\156\144\0\124" + "\151\144\163\150\165\155\141\156\151\163\141\164\157\162\0\123" + "\154\141\147\163\164\171\162\153\145\40\150\165\155\141\156\151" + "\163\141\164\157\162\0\126\151\163\165\141\154\151\163\145\162" + "\151\156\147\0" + }, + { "", 0, 0 } +}; + +const rc_data_t* rc_data = rc_dataX; diff --git a/tools/add_file b/tools/add_file index a704029..be232fb 100755 --- a/tools/add_file +++ b/tools/add_file @@ -26,6 +26,10 @@ function allfile() { then NAME="Goran Mekić"; EMAIL="meka@tilda.center" fi + if [ "$USER" == "savo" ] + then + NAME="Sander Vocke"; EMAIL="sandervocke@gmail.com" + fi echo "/* -*- Mode: c++ -*- */" > $1; echo "/***************************************************************************" >> $1; |