summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/audioinputenginemidi.cc73
-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.cc24
-rw-r--r--src/midimapper.cc56
-rw-r--r--src/midimapper.h20
-rw-r--r--src/parsecurvemap.cc60
-rw-r--r--src/parsecurvemap.h33
-rw-r--r--src/powermapfilter.h4
10 files changed, 328 insertions, 100 deletions
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/src/parsecurvemap.h b/src/parsecurvemap.h
new file mode 100644
index 0000000..a6f0429
--- /dev/null
+++ b/src/parsecurvemap.h
@@ -0,0 +1,33 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * parsecurvemap.h
+ *
+ * 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.
+ */
+#pragma once
+
+#include <pugixml.hpp>
+#include "curvemap.h"
+
+// 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;
};