From 0e59f90e2396190ec3e3a7195bac9c79e12fd6cc Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Tue, 23 Jul 2024 13:23:38 +0200 Subject: Add CC-controller openness parameter for hi-hats. --- src/DGDOM.h | 3 +- src/audioinputenginemidi.cc | 101 ++++++++++++++++++++++++++++++++------------ src/audioinputenginemidi.h | 3 ++ src/dgxmlparser.cc | 22 ++++++---- src/domloader.cc | 1 + src/drumgizmo.cc | 2 - src/engineevent.h | 9 ++-- src/inputprocessor.cc | 5 +-- src/instrument.cc | 4 +- src/instrument.h | 2 +- src/instrumentstate.h | 9 ++++ src/midimapparser.cc | 45 ++++++++++++++++---- src/midimapper.cc | 48 +++++++++++++++++---- src/midimapper.h | 37 ++++++++++++++-- src/sample.cc | 9 +++- src/sample.h | 5 ++- src/sample_selection.cc | 15 +++++-- src/sample_selection.h | 2 +- src/settings.h | 14 +++++- 19 files changed, 259 insertions(+), 77 deletions(-) create mode 100644 src/instrumentstate.h (limited to 'src') diff --git a/src/DGDOM.h b/src/DGDOM.h index 474b29c..374bf2c 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 audiofiles; }; diff --git a/src/audioinputenginemidi.cc b/src/audioinputenginemidi.cc index 2e794f4..aa70372 100644 --- a/src/audioinputenginemidi.cc +++ b/src/audioinputenginemidi.cc @@ -26,6 +26,7 @@ */ #include "audioinputenginemidi.h" +#include "instrument.h" #include "midimapparser.h" #include @@ -70,6 +71,7 @@ bool AudioInputEngineMidi::loadMidiMap(const std::string& midimap_file, for(size_t i = 0; i < instruments.size(); i++) { instrmap[instruments[i]->getName()] = static_cast(i); + instrument_states[i] = InstrumentState{}; } mmap.swap(instrmap, midimap_parser.midimap); @@ -94,6 +96,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}; @@ -108,42 +111,84 @@ 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, MapFrom::Note, MapTo::PlayInstrument); + auto instruments = mmap.lookup_instruments(map_entries); + for(const auto& instrument_idx : instruments) { - 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(velocity) - lower_offset) / midi_velocity_max; - events.push_back({EventType::OnSet, (std::size_t)instrument_idx, - offset, centered_velocity}); + if(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(velocity) - lower_offset) / midi_velocity_max; + float position = 0.0f; + float openness = 0.0f; // TODO + auto instr_it = instrument_states.find(instrument_idx); + if(instr_it != instrument_states.end()) + { + openness = instr_it->second.openness; + } + events.push_back({EventType::OnSet, (std::size_t)instrument_idx, + offset, centered_velocity, openness}); + } } - 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); + auto instruments = mmap.lookup_instruments(map_entries); + for(const auto& instrument_idx : instruments) { - events.push_back({EventType::Choke, (std::size_t)instrument_idx, - offset, .0f}); + if(velocity > 0) + { + events.push_back({EventType::Choke, (std::size_t)instrument_idx, + offset, .0f, .0f}); + } } - break; + } + break; - default: - break; + case ControlChange: + { + auto controller_number = midi_buffer[1]; // NOLINT - span + auto value = midi_buffer[2]; // NOLINT - span + + 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) { + auto state_it = instrument_states.find(instrument_idx); + if (state_it != instrument_states.end()) { + InstrumentState &state = state_it->second; + auto const max = (float) entry.state_max; + auto const min = (float) entry.state_min; + auto const in_clamped = std::min(std::max((float)value, std::min(min, max)), std::max(min, max)); + float fvalue = (in_clamped - min) / (max - min); + if (entry.maybe_instrument_state_kind == InstrumentStateKind::Openness) { + state_it->second.openness = fvalue; + } + } + } + } } } } diff --git a/src/audioinputenginemidi.h b/src/audioinputenginemidi.h index 386a055..e045785 100644 --- a/src/audioinputenginemidi.h +++ b/src/audioinputenginemidi.h @@ -27,11 +27,13 @@ #pragma once #include +#include #include "audioinputengine.h" #include "midimapper.h" #include "instrument.h" #include "configfile.h" +#include "instrumentstate.h" class AudioInputEngineMidi : public AudioInputEngine @@ -65,6 +67,7 @@ public: protected: MidiMapper mmap; + std::map instrument_states; private: std::string midimap; 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..31e7b04 100644 --- a/src/domloader.cc +++ b/src/domloader.cc @@ -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..0c298d4 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -26,11 +26,9 @@ */ #include "drumgizmo.h" -#include #include #include #include -#include #include "audiotypes.h" #include diff --git a/src/engineevent.h b/src/engineevent.h index 9c60a4a..2288e2c 100644 --- a/src/engineevent.h +++ b/src/engineevent.h @@ -39,8 +39,9 @@ enum class EventType //! 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 + float velocity; //!< The velocity if the type is a note on [0; 1] + float openness; //!< The openness of the instrument. 0 := closed, 1 := open }; diff --git a/src/inputprocessor.cc b/src/inputprocessor.cc index c0c0e92..19ecc05 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 @@ -251,7 +250,7 @@ bool InputProcessor::processOnset(event_t& event, std::size_t pos, 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); + const auto sample = instr->sample(instrument_level, event.openness, event.offset + pos); if(sample == nullptr) { diff --git a/src/instrument.cc b/src/instrument.cc index b7bcdd9..5c5b891 100644 --- a/src/instrument.cc +++ b/src/instrument.cc @@ -54,12 +54,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 { diff --git a/src/instrument.h b/src/instrument.h index c06ccdc..f0b12e2 100644 --- a/src/instrument.h +++ b/src/instrument.h @@ -49,7 +49,7 @@ public: Instrument(Settings& settings, Random& rand); ~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; diff --git a/src/instrumentstate.h b/src/instrumentstate.h new file mode 100644 index 0000000..485f2be --- /dev/null +++ b/src/instrumentstate.h @@ -0,0 +1,9 @@ +#pragma once + +//! Tracks the MIDI 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..1f307ef 100644 --- a/src/midimapparser.cc +++ b/src/midimapparser.cc @@ -42,16 +42,45 @@ 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_min = map_node.attribute("min").as_int(default_int); + auto control_max = map_node.attribute("max").as_int(default_int); + 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; + midimap.push_back(MidimapEntry { + MapFrom::Note, + MapTo::PlayInstrument, + note, + instr, + InstrumentStateKind::NoneOrAny, + 0, 0 + }); + } + else if (is_cc_control) + { + midimap.push_back(MidimapEntry { + MapFrom::CC, + MapTo::InstrumentState, + cc, + instr, + control, + (control_min != default_int) ? (uint8_t)control_min : (uint8_t)0, + (control_max != default_int) ? (uint8_t)control_max : (uint8_t)127 + }); } - - MidimapEntry entry{note, instr}; - midimap.push_back(entry); } return true; diff --git a/src/midimapper.cc b/src/midimapper.cc index 345ce2f..9de5429 100644 --- a/src/midimapper.cc +++ b/src/midimapper.cc @@ -25,26 +25,56 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "midimapper.h" +#include -std::vector MidiMapper::lookup(int note_id) +int MidiMapper::lookup_instrument(std::string name) { + const std::lock_guard guard(mutex); + auto instrmap_it = instrmap.find(name); + if(instrmap_it != instrmap.end()) + { + return instrmap_it->second; + } + return -1; +} + +std::vector MidiMapper::lookup_instruments(std::vector const& entries) { + const std::lock_guard guard(mutex); + std::set rval; + for(const auto& entry : entries) + { + auto it = instrmap.find(entry.instrument_name); + if (it != instrmap.end()) { + rval.insert(it->second); + } + } + return std::vector(rval.begin(), rval.end()); +} + +std::vector MidiMapper::lookup( + int from_id, + MapFrom from_kind, + MapTo to_kind, + InstrumentStateKind state_kind) { - std::vector instruments; + std::vector rval; const std::lock_guard 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..064f88a 100644 --- a/src/midimapper.h +++ b/src/midimapper.h @@ -31,10 +31,33 @@ #include #include +enum class MapFrom { + Note, + CC, + NoneOrAny +}; + +enum class MapTo { + PlayInstrument, + InstrumentState, + NoneOrAny +}; + +enum class InstrumentStateKind { + Position, + Openness, + NoneOrAny +}; + struct MidimapEntry { - int note_id; + MapFrom from_kind; + MapTo to_kind; + int from_id; // note or CC number std::string instrument_name; + InstrumentStateKind maybe_instrument_state_kind; + uint8_t state_min; // cc value mapping to state 0.0 + uint8_t state_max; // cc value mapping to state 1.0 }; using midimap_t = std::vector; @@ -43,8 +66,16 @@ using instrmap_t = std::map; class MidiMapper { public: - //! Lookup note in map and returns the corresponding instrument index list. - std::vector lookup(int note_id); + //! Lookup midi map entries matching the given query. + std::vector 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); + std::vector lookup_instruments(std::vector const& entries); //! Set new map sets. void swap(instrmap_t& instrmap, midimap_t& midimap); 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 -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; 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::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..3a72424 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 velocity_modifier_falloff{velocity_modifier_falloff_default}; Atomic velocity_modifier_weight{velocity_modifier_weight_default}; Atomic velocity_stddev{velocity_stddev_default}; + Atomic openness_stddev{openness_stddev_default}; Atomic sample_selection_f_close{sample_selection_f_close_default}; + Atomic sample_selection_f_openness{sample_selection_f_openness_default}; Atomic sample_selection_f_diverse{sample_selection_f_diverse_default}; Atomic sample_selection_f_random{sample_selection_f_random_default}; @@ -200,7 +204,9 @@ struct SettingsGetter SettingRef velocity_modifier_falloff; SettingRef velocity_modifier_weight; SettingRef velocity_stddev; + SettingRef openness_stddev; SettingRef sample_selection_f_close; + SettingRef sample_selection_f_openness; SettingRef sample_selection_f_diverse; SettingRef 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 velocity_modifier_falloff; Notifier velocity_modifier_weight; Notifier velocity_stddev; + Notifier openness_stddev; Notifier sample_selection_f_close; + Notifier sample_selection_f_openness; Notifier sample_selection_f_diverse; Notifier sample_selection_f_random; Notifier 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); -- cgit v1.2.3