summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DGDOM.h4
-rw-r--r--src/audioinputenginemidi.cc99
-rw-r--r--src/audioinputenginemidi.h1
-rw-r--r--src/dgxmlparser.cc22
-rw-r--r--src/domloader.cc3
-rw-r--r--src/drumgizmo.cc4
-rw-r--r--src/engineevent.h12
-rw-r--r--src/events.h4
-rw-r--r--src/inputprocessor.cc111
-rw-r--r--src/inputprocessor.h8
-rw-r--r--src/instrument.cc12
-rw-r--r--src/instrument.h7
-rw-r--r--src/instrumentstate.h40
-rw-r--r--src/midimapparser.cc44
-rw-r--r--src/midimapper.cc65
-rw-r--r--src/midimapper.h42
-rw-r--r--src/powermapfilter.cc6
-rw-r--r--src/sample.cc9
-rw-r--r--src/sample.h5
-rw-r--r--src/sample_selection.cc15
-rw-r--r--src/sample_selection.h2
-rw-r--r--src/settings.h18
-rw-r--r--src/staminafilter.cc2
-rw-r--r--src/velocityfilter.cc4
24 files changed, 441 insertions, 98 deletions
diff --git a/src/DGDOM.h b/src/DGDOM.h
index 474b29c..d179268 100644
--- a/src/DGDOM.h
+++ b/src/DGDOM.h
@@ -58,7 +58,8 @@ struct AudioFileDOM
struct SampleDOM
{
std::string name;
- double power; // >= v2.0 only
+ double power; // >= v2.0 only
+ double openness; // >= v2.0 only
bool normalized; // >= v2.0 only
std::vector<AudioFileDOM> audiofiles;
};
@@ -76,6 +77,7 @@ struct InstrumentDOM
std::string description;
std::vector<SampleDOM> samples;
std::vector<InstrumentChannelDOM> instrument_channels;
+ float openness_choke_threshold {0.0f}; // <= 0 means disabled
// v1.0 only
std::vector<VelocityDOM> velocities;
diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc
index 2e794f4..fa2fc71 100644
--- a/src/audioinputenginemidi.cc
+++ b/src/audioinputenginemidi.cc
@@ -26,6 +26,7 @@
*/
#include "audioinputenginemidi.h"
+#include "instrument.h"
#include "midimapparser.h"
#include <cassert>
@@ -75,8 +76,8 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& midimap_file,
mmap.swap(instrmap, midimap_parser.midimap);
midimap = file;
+ reloaded = true;
is_valid = true;
-
return true;
}
@@ -94,6 +95,7 @@ bool AudioInputEngineMidi::isValid() const
constexpr std::uint8_t NoteOff{0x80};
constexpr std::uint8_t NoteOn{0x90};
constexpr std::uint8_t NoteAftertouch{0xA0};
+constexpr std::uint8_t ControlChange{0xB0};
// Note type mask:
constexpr std::uint8_t NoteMask{0xF0};
@@ -103,47 +105,86 @@ void AudioInputEngineMidi::processNote(const std::uint8_t* midi_buffer,
std::size_t offset,
std::vector<event_t>& events)
{
+ if(reloaded)
+ {
+ events.push_back({EventType::ResetInstrumentStates, 0, 0, InstrumentStateKind::NoneOrAny, 0.0f});
+ reloaded = false;
+ }
+
if(midi_buffer_length < 3)
{
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, MapFrom::Note, MapTo::PlayInstrument);
+ 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;
+ events.push_back({EventType::OnSet, (std::size_t)instrument_idx,
+ offset, InstrumentStateKind::NoneOrAny, 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, MapFrom::Note, MapTo::PlayInstrument);
+ 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, InstrumentStateKind::NoneOrAny, .0f});
+ }
}
- break;
+ }
+ break;
+
+ case ControlChange:
+ {
+ auto controller_number = midi_buffer[1]; // NOLINT - span
+ auto value = midi_buffer[2]; // NOLINT - span
- default:
- break;
+ auto map_entries = mmap.lookup(controller_number,
+ MapFrom::CC,
+ MapTo::InstrumentState);
+ for(const auto& entry : map_entries)
+ {
+ auto instrument_idx = mmap.lookup_instrument(entry.instrument_name);
+ if (instrument_idx >= 0) {
+ constexpr float lower_offset{0.5f};
+ constexpr float cc_value_max{127.0f};
+ auto centered_value =
+ (static_cast<float>(value) - lower_offset) / cc_value_max;
+
+ events.push_back({EventType::ChangeInstrumentState,
+ (std::size_t)instrument_idx, 0,
+ entry.maybe_instrument_state_kind,
+ centered_value});
+ }
+ }
}
}
}
diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h
index 386a055..a71fda9 100644
--- a/src/audioinputenginemidi.h
+++ b/src/audioinputenginemidi.h
@@ -69,6 +69,7 @@ protected:
private:
std::string midimap;
bool is_valid{false};
+ bool reloaded{true};
ConfigFile refs{REFSFILE};
};
diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc
index 0d3cdcd..180d4cd 100644
--- a/src/dgxmlparser.cc
+++ b/src/dgxmlparser.cc
@@ -351,16 +351,20 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom, LogFun
dom.samples.emplace_back();
res &= attrcpy(dom.samples.back().name, sample, "name", logger, filename);
- // Power only part of >= v2.0 instruments.
- if(dom.version == "1.0")
+ // Settings which are only part of >= v2.0 instruments.
+ dom.samples.back().power = 0.0;
+ dom.samples.back().normalized = false; // optional - defaults to false
+ dom.samples.back().openness = 0.0; // optional - defaults to 0.0
+ if(dom.version != "1.0")
{
- dom.samples.back().power = 0.0;
- }
- else
- {
- res &= attrcpy(dom.samples.back().power, sample, "power", logger, filename);
- dom.samples.back().normalized = false;
- res &= attrcpy(dom.samples.back().normalized, sample, "normalized", logger, filename, true);
+ res &= attrcpy(dom.samples.back().power, sample, "power",
+ logger, filename);
+ res &= attrcpy(dom.samples.back().normalized, sample, "normalized",
+ logger, filename, true);
+ res &= attrcpy(dom.samples.back().openness, sample, "openness",
+ logger, filename, true);
+ dom.samples.back().openness =
+ std::min(1.0, std::max(dom.samples.back().openness, 0.0));
}
for(pugi::xml_node audiofile: sample.children("audiofile"))
diff --git a/src/domloader.cc b/src/domloader.cc
index c78ed75..c972f08 100644
--- a/src/domloader.cc
+++ b/src/domloader.cc
@@ -89,7 +89,7 @@ bool DOMLoader::loadDom(const std::string& basepath,
continue;
}
- auto instrument = std::make_unique<Instrument>(settings, random);
+ auto instrument = std::make_unique<Instrument>(settings, random, instrumentdom.openness_choke_threshold);
instrument->setGroup(instrumentref.group);
instrument->_name = instrumentdom.name;
instrument->version = instrumentdom.version;
@@ -99,6 +99,7 @@ bool DOMLoader::loadDom(const std::string& basepath,
for(const auto& sampledom : instrumentdom.samples)
{
auto sample = new Sample(sampledom.name, sampledom.power,
+ sampledom.openness,
sampledom.normalized);
for(const auto& audiofiledom : sampledom.audiofiles)
{
diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc
index 85624ca..7ccf6b7 100644
--- a/src/drumgizmo.cc
+++ b/src/drumgizmo.cc
@@ -26,11 +26,9 @@
*/
#include "drumgizmo.h"
-#include <cmath>
#include <cstdio>
#include <cassert>
#include <cstring>
-#include <mutex>
#include "audiotypes.h"
#include <config.h>
@@ -174,7 +172,7 @@ bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples)
}
}
- events.push_back({EventType::OnSet, instrument_index, 0, velocity});
+ events.push_back({EventType::OnSet, instrument_index, 0, InstrumentStateKind::NoneOrAny, velocity});
}
bool active_events_left =
diff --git a/src/engineevent.h b/src/engineevent.h
index 9c60a4a..ff9b8ba 100644
--- a/src/engineevent.h
+++ b/src/engineevent.h
@@ -27,6 +27,7 @@
#pragma once
#include <cstddef>
+#include "instrumentstate.h"
//! Event types
enum class EventType
@@ -34,13 +35,16 @@ enum class EventType
OnSet,
Choke,
Stop,
+ ChangeInstrumentState,
+ ResetInstrumentStates
};
//! POD datatype for input event transport.
struct event_t
{
- EventType type; //!< The type of the event.
- std::size_t instrument; //!< The instrument number.
- std::size_t offset; //!< The offset position in the input buffer
- float velocity; //!< The velocity if the type is a note on [0; 1]
+ EventType type; //!< The type of the event.
+ std::size_t instrument; //!< The instrument number.
+ std::size_t offset; //!< The offset position in the input buffer
+ InstrumentStateKind state_kind; //!< For instrument state changes: the state to change
+ float velocity_or_state; //!< State value, or the velocity if the type is a note on [0; 1]
};
diff --git a/src/events.h b/src/events.h
index 538127b..0ced567 100644
--- a/src/events.h
+++ b/src/events.h
@@ -71,7 +71,7 @@ class SampleEvent
{
public:
SampleEvent(channel_t ch, float g, AudioFile* af, const std::string& grp,
- std::size_t instrument_id)
+ std::size_t instrument_id, float openness)
: Event(Event::Type::SampleEvent, ch)
, cache_id(CACHE_NOID)
, gain(g)
@@ -81,6 +81,7 @@ public:
, rampdown_count(-1)
, ramp_length(0)
, instrument_id(instrument_id)
+ , openness(openness)
{
}
@@ -105,4 +106,5 @@ public:
std::size_t rampdown_offset{0};
float scale{1.0f};
std::size_t instrument_id;
+ float openness; //< Openness related to this sample.
};
diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc
index c0c0e92..6e5d205 100644
--- a/src/inputprocessor.cc
+++ b/src/inputprocessor.cc
@@ -3,7 +3,7 @@
* inputprocessor.cc
*
* Sat Apr 23 20:39:30 CEST 2016
- * Copyright 2016 André Nusser
+ * Copyright 2016 Andr� Nusser
* andre.nusser@googlemail.com
****************************************************************************/
@@ -36,7 +36,6 @@
#include "powermapfilter.h"
#include "staminafilter.h"
#include "velocityfilter.h"
-
#include "cpp11fix.h"
class VelocityStorer
@@ -50,7 +49,7 @@ public:
bool filter(event_t& event, std::size_t pos) override
{
- original_velocity = event.velocity;
+ original_velocity = event.velocity_or_state;
return true;
}
@@ -70,7 +69,7 @@ public:
bool filter(event_t& event, std::size_t pos) override
{
- settings.velocity_modifier_current.store(event.velocity / original_velocity);
+ settings.velocity_modifier_current.store(event.velocity_or_state / original_velocity);
return true;
}
@@ -94,6 +93,10 @@ InputProcessor::InputProcessor(Settings& settings,
filters.emplace_back(std::make_unique<LatencyFilter>(settings, random));
filters.emplace_back(std::make_unique<VelocityFilter>(settings, random));
filters.emplace_back(std::make_unique<Reporter>(settings, original_velocity));
+
+ // To prevent needing run-time allocation for first-time instruments
+ constexpr size_t reserved_n_instruments = 256;
+ instrument_states.reserve(reserved_n_instruments);
}
bool InputProcessor::process(std::vector<event_t>& events,
@@ -118,6 +121,22 @@ bool InputProcessor::process(std::vector<event_t>& events,
}
}
+ if(event.type == EventType::ChangeInstrumentState)
+ {
+ if(!processStateChange(event, pos))
+ {
+ continue;
+ }
+ }
+
+ if(event.type == EventType::ResetInstrumentStates)
+ {
+ if(!processResetStates())
+ {
+ continue;
+ }
+ }
+
if(!processStop(event))
{
return false;
@@ -207,6 +226,77 @@ void InputProcessor::applyDirectedChoke(Settings& settings, DrumKit& kit,
}
}
+bool InputProcessor::processResetStates()
+{
+ instrument_states.clear();
+ return true;
+}
+
+bool InputProcessor::processOpennessChange(event_t& event, Instrument &inst, float openness, size_t pos)
+{
+ auto &state = instrument_states[event.instrument]; // Constructs if necessary
+ auto threshold = inst.getOpennessChokeThreshold();
+
+ if(threshold > 0.0f &&
+ state.openness > threshold && openness <= threshold)
+ {
+ // We crossed the openness threshold and should choke all running samples that have
+ // higher openness.
+ for(const auto& ch : kit.channels)
+ {
+ if(ch.num >= NUM_CHANNELS) // kit may have more channels than the engine
+ {
+ continue;
+ }
+
+ for(auto& event_sample : events_ds.iterateOver<SampleEvent>(ch.num))
+ {
+ if(event_sample.instrument_id == event.instrument && // Only applies to self
+ event_sample.openness > threshold && // Only samples that are more open than the threshold
+ event_sample.rampdown_count == -1) // Only if not already ramping
+ {
+ // Fixed group rampdown time of 68ms, independent of samplerate
+ applyChoke(settings, event_sample, 68, event.offset, pos);
+ }
+ }
+ }
+ }
+
+ state.openness = event.velocity_or_state;
+ return true;
+}
+
+bool InputProcessor::processStateChange(event_t& event, size_t pos)
+{
+ if(!kit.isValid())
+ {
+ return false;
+ }
+
+ std::size_t instrument_id = event.instrument;
+ Instrument* instr = nullptr;
+
+ if(instrument_id < kit.instruments.size())
+ {
+ instr = kit.instruments[instrument_id].get();
+ }
+
+ if(instr == nullptr || !instr->isValid())
+ {
+ ERR(inputprocessor, "Missing Instrument %d.\n", (int)instrument_id);
+ return false;
+ }
+
+ switch (event.state_kind)
+ {
+ case InstrumentStateKind::Openness:
+ return processOpennessChange(event, *instr, event.velocity_or_state, pos);
+ default:
+ ERR(inputprocessor, "Unsupported state change");
+ return false;
+ }
+}
+
bool InputProcessor::processOnset(event_t& event, std::size_t pos,
double resample_ratio)
{
@@ -229,7 +319,7 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
return false;
}
- original_velocity = event.velocity;
+ original_velocity = event.velocity_or_state;
for(auto& filter : filters)
{
// This line might change the 'event' variable
@@ -250,8 +340,11 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
auto const power_max = instr->getMaxPower();
auto const power_min = instr->getMinPower();
float const power_span = power_max - power_min;
- float const instrument_level = power_min + event.velocity*power_span;
- const auto sample = instr->sample(instrument_level, event.offset + pos);
+ float const instrument_level = power_min + event.velocity_or_state*power_span;
+
+ auto state_it = instrument_states.find(instrument_id);
+ auto openness = (state_it != instrument_states.end()) ? state_it->second.openness : 0.0f;
+ const auto sample = instr->sample(instrument_level, openness, event.offset + pos);
if(sample == nullptr)
{
@@ -292,12 +385,12 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos,
auto& event_sample =
events_ds.emplace<SampleEvent>(ch.num, ch.num, 1.0, af,
- instr->getGroup(), instrument_id);
+ instr->getGroup(), instrument_id, openness);
event_sample.offset = (event.offset + pos) * resample_ratio;
if(settings.normalized_samples.load() && sample->getNormalized())
{
- event_sample.scale *= event.velocity;
+ event_sample.scale *= event.velocity_or_state;
}
}
}
diff --git a/src/inputprocessor.h b/src/inputprocessor.h
index a8dc45b..a68a517 100644
--- a/src/inputprocessor.h
+++ b/src/inputprocessor.h
@@ -3,7 +3,7 @@
* inputprocessor.h
*
* Sat Apr 23 20:39:30 CEST 2016
- * Copyright 2016 André Nusser
+ * Copyright 2016 Andr� Nusser
* andre.nusser@googlemail.com
****************************************************************************/
@@ -28,6 +28,7 @@
#include <vector>
#include <list>
+#include <unordered_map>
#include <memory>
@@ -37,6 +38,7 @@
#include "id.h"
#include "inputfilter.h"
#include "engineevent.h"
+#include "instrumentstate.h"
struct Settings;
class Random;
@@ -62,7 +64,10 @@ private:
bool processOnset(event_t& event, std::size_t pos, double resample_ratio);
bool processChoke(event_t& event, std::size_t pos, double resample_ratio);
+ bool processStateChange(event_t& event, std::size_t pos);
+ bool processResetStates();
bool processStop(event_t& event);
+ bool processOpennessChange(event_t& event, Instrument &inst, float openness, size_t pos);
//! Applies choke with rampdown time in ms to event starting at offset.
void applyChoke(Settings& settings, SampleEvent& event,
@@ -86,4 +91,5 @@ private:
Settings& settings;
float original_velocity{0.0f};
+ std::unordered_map<std::size_t, InstrumentState> instrument_states;
};
diff --git a/src/instrument.cc b/src/instrument.cc
index b7bcdd9..6556fb1 100644
--- a/src/instrument.cc
+++ b/src/instrument.cc
@@ -30,10 +30,11 @@
#include "sample.h"
-Instrument::Instrument(Settings& settings, Random& rand)
+Instrument::Instrument(Settings& settings, Random& rand, float openness_choke_threshold)
: settings(settings)
, rand(rand)
, sample_selection(settings, rand, powerlist)
+ , openness_choke_threshold(openness_choke_threshold)
{
DEBUG(instrument, "new %p\n", this);
mod = 1.0;
@@ -54,12 +55,12 @@ bool Instrument::isValid() const
return this == magic;
}
-const Sample* Instrument::sample(level_t level, size_t pos)
+const Sample* Instrument::sample(level_t level, float openness, std::size_t pos)
{
if(version >= VersionStr("2.0"))
{
// Version 2.0
- return sample_selection.get(level * mod, pos);
+ return sample_selection.get(level * mod, openness, pos);
}
else
{
@@ -153,6 +154,11 @@ float Instrument::getMinPower() const
}
}
+float Instrument::getOpennessChokeThreshold() const
+{
+ return openness_choke_threshold;
+}
+
const std::vector<Choke>& Instrument::getChokes()
{
return chokes;
diff --git a/src/instrument.h b/src/instrument.h
index c06ccdc..e5de348 100644
--- a/src/instrument.h
+++ b/src/instrument.h
@@ -46,10 +46,10 @@ struct Choke;
class Instrument
{
public:
- Instrument(Settings& settings, Random& rand);
+ Instrument(Settings& settings, Random& rand, float openness_choke_threshold);
~Instrument();
- const Sample* sample(level_t level, size_t pos);
+ const Sample* sample(level_t level, float openness, std::size_t pos);
std::size_t getID() const;
const std::string& getName() const;
@@ -70,6 +70,8 @@ public:
float getMaxPower() const;
float getMinPower() const;
+ float getOpennessChokeThreshold() const;
+
const std::vector<Choke>& getChokes();
private:
@@ -98,6 +100,7 @@ private:
size_t lastpos;
float mod;
+ float openness_choke_threshold;
Settings& settings;
Random& rand;
PowerList powerlist;
diff --git a/src/instrumentstate.h b/src/instrumentstate.h
new file mode 100644
index 0000000..6c3c0b4
--- /dev/null
+++ b/src/instrumentstate.h
@@ -0,0 +1,40 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * instrumentstate.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
+
+enum class InstrumentStateKind {
+ Openness,
+ NoneOrAny
+};
+
+//! Tracks the persistent state of an instrument during play.
+struct InstrumentState {
+
+ //! Openness (typically for a hi-hat).
+ //! 0.0-1.0, where 0.0 is closed and 1.0 is fully open.
+ float openness = 0.0;
+}; \ No newline at end of file
diff --git a/src/midimapparser.cc b/src/midimapparser.cc
index 363e1d5..20ccc77 100644
--- a/src/midimapparser.cc
+++ b/src/midimapparser.cc
@@ -29,6 +29,8 @@
#include <pugixml.hpp>
#include <hugin.hpp>
+#include <cpp11fix.h>
+
bool MidiMapParser::parseFile(const std::string& filename)
{
pugi::xml_document doc;
@@ -42,16 +44,42 @@ 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 cc = map_node.attribute("cc").as_int(default_int);
+ auto instr = std::string(map_node.attribute("instr").as_string());
+ auto control_str = std::string(map_node.attribute("control").as_string());
+ auto control = (control_str == "openness" ? InstrumentStateKind::Openness :
+ InstrumentStateKind::NoneOrAny);
+
+ bool is_conflict = (note != default_int && cc != default_int);
+ bool is_note_play_instrument =
+ (!is_conflict && instr != "" && note != default_int);
+ bool is_cc_control =
+ (!is_conflict && instr != "" && control != InstrumentStateKind::NoneOrAny && cc != default_int);
+
+ if (is_note_play_instrument)
{
- continue;
+ MidimapEntry entry(
+ MapFrom::Note,
+ note,
+ MapTo::PlayInstrument,
+ instr,
+ InstrumentStateKind::NoneOrAny
+ );
+ midimap.push_back(entry);
+ }
+ else if (is_cc_control)
+ {
+ MidimapEntry entry(
+ MapFrom::CC,
+ cc,
+ MapTo::InstrumentState,
+ instr,
+ control
+ );
+ midimap.push_back(entry);
}
-
- MidimapEntry entry{note, instr};
- midimap.push_back(entry);
}
return true;
diff --git a/src/midimapper.cc b/src/midimapper.cc
index 345ce2f..b4de580 100644
--- a/src/midimapper.cc
+++ b/src/midimapper.cc
@@ -26,25 +26,72 @@
*/
#include "midimapper.h"
-std::vector<int> MidiMapper::lookup(int note_id)
+#include <cpp11fix.h>
+
+MidimapEntry::MidimapEntry(MapFrom from_kind,
+ int from_id,
+ MapTo to_kind,
+ std::string instrument_name,
+ InstrumentStateKind maybe_instrument_state_kind
+ ) :
+ from_kind(from_kind)
+ , from_id(from_id)
+ , to_kind(to_kind)
+ , instrument_name(instrument_name)
+ , maybe_instrument_state_kind(maybe_instrument_state_kind)
+{}
+
+MidimapEntry::MidimapEntry(const MidimapEntry& other)
+{
+ *this = other;
+}
+
+MidimapEntry &MidimapEntry::operator=(const MidimapEntry& other)
{
- std::vector<int> instruments;
+ from_kind = other.from_kind;
+ from_id = other.from_id;
+ to_kind = other.to_kind;
+ instrument_name = other.instrument_name;
+ maybe_instrument_state_kind = other.maybe_instrument_state_kind;
+
+ 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 from_id,
+ MapFrom from_kind,
+ MapTo to_kind,
+ InstrumentStateKind state_kind)
+{
+ 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)
+ bool match = true;
+ match = match && (from_id == -1 || from_id == map_entry.from_id);
+ match = match && (from_kind == MapFrom::NoneOrAny || from_kind == map_entry.from_kind);
+ match = match && (to_kind == MapTo::NoneOrAny || to_kind == map_entry.to_kind);
+ match = match && (state_kind == InstrumentStateKind::NoneOrAny || state_kind == map_entry.maybe_instrument_state_kind);
+
+ if(match)
{
- 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..dcca6dd 100644
--- a/src/midimapper.h
+++ b/src/midimapper.h
@@ -30,11 +30,40 @@
#include <string>
#include <mutex>
#include <vector>
+#include <memory>
+
+#include "instrumentstate.h"
+
+enum class MapFrom {
+ Note,
+ CC,
+ NoneOrAny
+};
+
+enum class MapTo {
+ PlayInstrument,
+ InstrumentState,
+ NoneOrAny
+};
struct MidimapEntry
{
- int note_id;
+ MapFrom from_kind; // What kind of input is mapped from
+ int from_id; // note or CC number
+ MapTo to_kind; // What kind of action is mapped to
+
std::string instrument_name;
+
+ // For mapping to control state changes
+ InstrumentStateKind maybe_instrument_state_kind;
+
+ MidimapEntry &operator=(const MidimapEntry& other);
+ MidimapEntry(const MidimapEntry& other);
+ MidimapEntry(MapFrom from_kind,
+ int from_id,
+ MapTo to_kind,
+ std::string instrument_name,
+ InstrumentStateKind maybe_instrument_state_kind);
};
using midimap_t = std::vector<MidimapEntry>;
@@ -43,8 +72,15 @@ 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 query.
+ std::vector<MidimapEntry> lookup(
+ int from_id = -1, // note or cc #. -1 matches all notes/cc's
+ MapFrom from_kind = MapFrom::NoneOrAny, // NoneOrAny will return both CC and note maps
+ MapTo to_kind = MapTo::NoneOrAny, // NoneOrAny will return both instrument hits and controls
+ InstrumentStateKind state_kind = InstrumentStateKind::NoneOrAny // NoneOrAny maps all state control kinds
+ );
+
+ int lookup_instrument(std::string name);
//! Set new map sets.
void swap(instrmap_t& instrmap, midimap_t& midimap);
diff --git a/src/powermapfilter.cc b/src/powermapfilter.cc
index 45df51e..0bf02b4 100644
--- a/src/powermapfilter.cc
+++ b/src/powermapfilter.cc
@@ -38,7 +38,7 @@ bool PowermapFilter::filter(event_t& event, size_t pos)
// the position is irrelevant for this filter
(void) pos;
- settings.powermap_input.store(event.velocity);
+ settings.powermap_input.store(event.velocity_or_state);
if (settings.enable_powermap.load())
{
powermap.setFixed0({settings.powermap_fixed0_x.load(), settings.powermap_fixed0_y.load()});
@@ -46,9 +46,9 @@ bool PowermapFilter::filter(event_t& event, size_t pos)
powermap.setFixed2({settings.powermap_fixed2_x.load(), settings.powermap_fixed2_y.load()});
powermap.setShelf(settings.powermap_shelf.load());
- event.velocity = powermap.map(event.velocity);
+ event.velocity_or_state = powermap.map(event.velocity_or_state);
}
- settings.powermap_output.store(event.velocity);
+ settings.powermap_output.store(event.velocity_or_state);
return true;
}
diff --git a/src/sample.cc b/src/sample.cc
index 9af2c08..63a16d9 100644
--- a/src/sample.cc
+++ b/src/sample.cc
@@ -28,9 +28,11 @@
#include <sndfile.h>
-Sample::Sample(const std::string& name, double power, bool normalized)
+Sample::Sample(const std::string& name, double power,
+ double openness, bool normalized)
: name{name}
, power{power}
+ , openness{openness}
, normalized(normalized)
, audiofiles{}
{
@@ -69,6 +71,11 @@ double Sample::getPower() const
return power;
}
+double Sample::getOpenness() const
+{
+ return openness;
+}
+
bool Sample::getNormalized() const
{
return normalized;
diff --git a/src/sample.h b/src/sample.h
index 6c31b6b..31a3741 100644
--- a/src/sample.h
+++ b/src/sample.h
@@ -37,12 +37,14 @@ using AudioFiles = std::map<const InstrumentChannel*, AudioFile*>;
class Sample
{
public:
- Sample(const std::string& name, double power, bool normalized = false);
+ Sample(const std::string& name, double power,
+ double openness, bool normalized = false);
~Sample();
AudioFile* getAudioFile(const Channel& channel) const;
double getPower() const;
+ double getOpenness() const;
bool getNormalized() const;
private:
@@ -55,6 +57,7 @@ private:
std::string name;
double power;
+ double openness;
bool normalized;
AudioFiles audiofiles;
};
diff --git a/src/sample_selection.cc b/src/sample_selection.cc
index 31313bb..8603687 100644
--- a/src/sample_selection.cc
+++ b/src/sample_selection.cc
@@ -54,7 +54,7 @@ void SampleSelection::finalise()
last.assign(powerlist.getPowerListItems().size(), 0);
}
-const Sample* SampleSelection::get(level_t level, std::size_t pos)
+const Sample* SampleSelection::get(level_t level, float openness, std::size_t pos)
{
const auto& samples = powerlist.getPowerListItems();
if(!samples.size())
@@ -64,6 +64,7 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos)
std::size_t index_opt = 0;
float power_opt{0.f};
+ float openness_opt{0.f};
float value_opt{std::numeric_limits<float>::max()};
// the following three values are mostly for debugging
float random_opt = 0.;
@@ -72,6 +73,7 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos)
// Note the magic values in front of the settings factors.
const float f_close = 4.*settings.sample_selection_f_close.load();
+ const float f_openness = 10.*settings.sample_selection_f_openness.load();
const float f_diverse = (1./2.)*settings.sample_selection_f_diverse.load();
const float f_random = (1./3.)*settings.sample_selection_f_random.load();
@@ -136,12 +138,19 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos)
auto random = rand.floatInRange(0.,1.);
auto close = (samples[current_index].power - level)/power_range;
auto diverse = 1./(1. + (float)(pos - last[current_index])/settings.samplerate);
- auto value = f_close*pow2(close) + f_diverse*diverse + f_random*random;
+ auto closeopenness = samples[current_index].sample->getOpenness() - openness;
+ // note that the value below for close and closepos is actually the weighted squared l2 distance in 2d
+ auto value =
+ f_close*pow2(close)
+ + f_openness*pow2(closeopenness)
+ + f_diverse*diverse
+ + f_random*random;
if (value < value_opt)
{
index_opt = current_index;
power_opt = samples[current_index].power;
+ openness_opt = samples[current_index].sample->getOpenness();
value_opt = value;
random_opt = random;
close_opt = close;
@@ -151,7 +160,7 @@ const Sample* SampleSelection::get(level_t level, std::size_t pos)
}
while (up_value_lb <= value_opt || down_value_lb <= value_opt);
- DEBUG(rand, "Chose sample with index: %d, value: %f, power %f, random: %f, close: %f, diverse: %f, count: %d", (int)index_opt, value_opt, power_opt, random_opt, close_opt, diverse_opt, (int)count);
+ DEBUG(rand, "Chose sample with index: %d, value: %f, power: %f, openness: %f, random: %f, close: %f, diverse: %f, count: %d", (int)index_opt, value_opt, power_opt, openness_opt, random_opt, close_opt, diverse_opt, (int)count);
last[index_opt] = pos;
return samples[index_opt].sample;
diff --git a/src/sample_selection.h b/src/sample_selection.h
index 8da4e0d..e5f2050 100644
--- a/src/sample_selection.h
+++ b/src/sample_selection.h
@@ -40,7 +40,7 @@ public:
SampleSelection(Settings& settings, Random& rand, const PowerList& powerlist);
void finalise();
- const Sample* get(level_t level, std::size_t pos);
+ const Sample* get(level_t level, float openness, std::size_t pos);
private:
Settings& settings;
diff --git a/src/settings.h b/src/settings.h
index fb93d79..d24d77a 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -3,7 +3,7 @@
* settings.h
*
* Tue Mar 22 10:59:46 CET 2016
- * Copyright 2016 Christian Glöckner
+ * Copyright 2016 Christian Gl�ckner
* cgloeckner@freenet.de
****************************************************************************/
@@ -76,13 +76,17 @@ struct Settings
static float constexpr velocity_modifier_falloff_default = 0.5f;
static float constexpr velocity_modifier_weight_default = 0.25f;
static float constexpr velocity_stddev_default = .45f;
+ static float constexpr openness_stddev_default = 0.f; // FIXME: set to something sensible
static float constexpr sample_selection_f_close_default = .85f;
+ static float constexpr sample_selection_f_openness_default = 1.f;
static float constexpr sample_selection_f_diverse_default = .16f;
static float constexpr sample_selection_f_random_default = .07f;
Atomic<float> velocity_modifier_falloff{velocity_modifier_falloff_default};
Atomic<float> velocity_modifier_weight{velocity_modifier_weight_default};
Atomic<float> velocity_stddev{velocity_stddev_default};
+ Atomic<float> openness_stddev{openness_stddev_default};
Atomic<float> sample_selection_f_close{sample_selection_f_close_default};
+ Atomic<float> sample_selection_f_openness{sample_selection_f_openness_default};
Atomic<float> sample_selection_f_diverse{sample_selection_f_diverse_default};
Atomic<float> sample_selection_f_random{sample_selection_f_random_default};
@@ -146,7 +150,7 @@ struct Settings
// Current latency offset in ms - for UI
Atomic<float> latency_current{0};
- // Powermap parameters
+ // Power map parameters
Atomic<bool> enable_powermap;
Atomic<float> powermap_fixed0_x{0.};
Atomic<float> powermap_fixed0_y{0.};
@@ -156,7 +160,7 @@ struct Settings
Atomic<float> powermap_fixed2_y{1.};
Atomic<bool> powermap_shelf{true};
- // Powermap visualizer; -1 is "none"
+ // Power map visualizer; -1 is "none"
Atomic<float> powermap_input{-1.};
Atomic<float> powermap_output{-1.};
@@ -200,7 +204,9 @@ struct SettingsGetter
SettingRef<float> velocity_modifier_falloff;
SettingRef<float> velocity_modifier_weight;
SettingRef<float> velocity_stddev;
+ SettingRef<float> openness_stddev;
SettingRef<float> sample_selection_f_close;
+ SettingRef<float> sample_selection_f_openness;
SettingRef<float> sample_selection_f_diverse;
SettingRef<float> sample_selection_f_random;
@@ -275,7 +281,9 @@ struct SettingsGetter
, velocity_modifier_falloff{settings.velocity_modifier_falloff}
, velocity_modifier_weight{settings.velocity_modifier_weight}
, velocity_stddev{settings.velocity_stddev}
+ , openness_stddev{settings.openness_stddev}
, sample_selection_f_close{settings.sample_selection_f_close}
+ , sample_selection_f_openness{settings.sample_selection_f_openness}
, sample_selection_f_diverse{settings.sample_selection_f_diverse}
, sample_selection_f_random{settings.sample_selection_f_random}
, sample_selection_retry_count(settings.sample_selection_retry_count)
@@ -345,7 +353,9 @@ public:
Notifier<float> velocity_modifier_falloff;
Notifier<float> velocity_modifier_weight;
Notifier<float> velocity_stddev;
+ Notifier<float> openness_stddev;
Notifier<float> sample_selection_f_close;
+ Notifier<float> sample_selection_f_openness;
Notifier<float> sample_selection_f_diverse;
Notifier<float> sample_selection_f_random;
Notifier<std::size_t> sample_selection_retry_count;
@@ -424,7 +434,9 @@ public:
EVAL(velocity_modifier_falloff);
EVAL(velocity_modifier_weight);
EVAL(velocity_stddev);
+ EVAL(openness_stddev)
EVAL(sample_selection_f_close);
+ EVAL(sample_selection_f_openness);
EVAL(sample_selection_f_diverse);
EVAL(sample_selection_f_random);
EVAL(sample_selection_retry_count);
diff --git a/src/staminafilter.cc b/src/staminafilter.cc
index 5f7599a..fc14920 100644
--- a/src/staminafilter.cc
+++ b/src/staminafilter.cc
@@ -63,7 +63,7 @@ bool StaminaFilter::filter(event_t& event, size_t pos)
lastpos = 0;
}
- event.velocity *= mod;
+ event.velocity_or_state *= mod;
if(enable_velocity_modifier)
{
diff --git a/src/velocityfilter.cc b/src/velocityfilter.cc
index 81587d5..04bb9d1 100644
--- a/src/velocityfilter.cc
+++ b/src/velocityfilter.cc
@@ -38,10 +38,10 @@ bool VelocityFilter::filter(event_t& event, size_t pos)
{
if (settings.enable_velocity_modifier.load())
{
- float mean = event.velocity;
+ float mean = event.velocity_or_state;
float stddev = settings.velocity_stddev.load();
// the 30.0f were determined empirically
- event.velocity = random.normalDistribution(mean, stddev / 30.0f);
+ event.velocity_or_state = random.normalDistribution(mean, stddev / 30.0f);
}
return true;