diff options
author | Sander Vocke <sandervocke@gmail.com> | 2024-07-23 13:23:38 +0200 |
---|---|---|
committer | Sander Vocke <sandervocke@gmail.com> | 2024-07-23 13:23:38 +0200 |
commit | 0e59f90e2396190ec3e3a7195bac9c79e12fd6cc (patch) | |
tree | f7fd25a952c6e64db9108eab2a774e46fd9f0a83 | |
parent | 019d478818950f7880d2c0f80d8fc8f963e9736b (diff) |
Add CC-controller openness parameter for hi-hats.
-rw-r--r-- | drumgizmo/drumgizmoc.cc | 12 | ||||
-rw-r--r-- | drumgizmo/input/jackmidi.cc | 9 | ||||
-rw-r--r-- | plugin/drumgizmo_plugin.cc | 15 | ||||
-rw-r--r-- | src/DGDOM.h | 3 | ||||
-rw-r--r-- | src/audioinputenginemidi.cc | 101 | ||||
-rw-r--r-- | src/audioinputenginemidi.h | 3 | ||||
-rw-r--r-- | src/dgxmlparser.cc | 22 | ||||
-rw-r--r-- | src/domloader.cc | 1 | ||||
-rw-r--r-- | src/drumgizmo.cc | 2 | ||||
-rw-r--r-- | src/engineevent.h | 9 | ||||
-rw-r--r-- | src/inputprocessor.cc | 5 | ||||
-rw-r--r-- | src/instrument.cc | 4 | ||||
-rw-r--r-- | src/instrument.h | 2 | ||||
-rw-r--r-- | src/instrumentstate.h | 9 | ||||
-rw-r--r-- | src/midimapparser.cc | 45 | ||||
-rw-r--r-- | src/midimapper.cc | 48 | ||||
-rw-r--r-- | src/midimapper.h | 37 | ||||
-rw-r--r-- | src/sample.cc | 9 | ||||
-rw-r--r-- | src/sample.h | 5 | ||||
-rw-r--r-- | src/sample_selection.cc | 15 | ||||
-rw-r--r-- | src/sample_selection.h | 2 | ||||
-rw-r--r-- | src/settings.h | 14 | ||||
-rw-r--r-- | test/dgreftest/midiinputengine.cc | 4 | ||||
-rw-r--r-- | test/midimapparsertest.cc | 12 | ||||
-rw-r--r-- | test/midimappertest.cc | 21 |
25 files changed, 311 insertions, 98 deletions
diff --git a/drumgizmo/drumgizmoc.cc b/drumgizmo/drumgizmoc.cc index bca55d2..5b44e6a 100644 --- a/drumgizmo/drumgizmoc.cc +++ b/drumgizmo/drumgizmoc.cc @@ -137,6 +137,8 @@ static std::string arguments() "Sample selection parameters:\n" " close: The importance given to choosing a sample close to\n" " the actual velocity value (after humanization). [0,1]\n" + " openness: The importance given to choosing a sample close to\n" + " the actual openness value (after humanization). [0,1]\n" " diverse: The importance given to choosing samples\n" " which haven't been played recently. [0,1]\n" " random: The amount of randomness added. [0,1]\n" @@ -615,6 +617,16 @@ int main(int argc, char* argv[]) } settings.sample_selection_f_close.store(val); } + else if(token.key == "openness") + { + auto val = atof_nol(token.value.data()); + if(val < 0 || val > 1) + { + std::cerr << "openness range is [0, 1].\n"; + return 1; + } + settings.sample_selection_f_openness.store(val); + } else if(token.key == "diverse") { auto val = atof_nol(token.value.data()); diff --git a/drumgizmo/input/jackmidi.cc b/drumgizmo/input/jackmidi.cc index 7081bf1..2637cc5 100644 --- a/drumgizmo/input/jackmidi.cc +++ b/drumgizmo/input/jackmidi.cc @@ -3,7 +3,7 @@ * jackmidi.cc * * Mo 25. Jan 11:26:06 CET 2016 - * Copyright 2016 Christian Glöckner + * Copyright 2016 Christian Gl�ckner * cgloeckner@freenet.de ****************************************************************************/ @@ -50,7 +50,7 @@ bool JackMidiInputEngine::init(const Instruments& instruments) { if(!loadMidiMap(midimap_file, instruments)) { - std::cerr << "[MidifileInputEngine] Failed to parse midimap '" + std::cerr << "[JackMidiInputEngine] Failed to parse midimap '" << midimap_file << "'\n"; return false; } @@ -114,7 +114,10 @@ void JackMidiInputEngine::process(jack_nframes_t num_frames) // It might not be though in case the system is under heavy load. // Make room for both the new and old events to make sure we don't throw // anything away. - events.reserve(events.size() + num_events); + + // FIXME: NarcoticV: Had to comment out the line below on my system + // (pipewire-jack), otherwise I got race conditions and crashes. + // events.reserve(events.size() + num_events); for(jack_nframes_t i = 0; i < num_events; ++i) { diff --git a/plugin/drumgizmo_plugin.cc b/plugin/drumgizmo_plugin.cc index 7960763..e582f93 100644 --- a/plugin/drumgizmo_plugin.cc +++ b/plugin/drumgizmo_plugin.cc @@ -410,16 +410,23 @@ bool DrumGizmoPlugin::Input::loadMidiMap(const std::string& file, bool result = AudioInputEngineMidi::loadMidiMap(file, i); std::vector<std::pair<int, std::string>> midnam; + // Create MIDNAM mappings. + // FIXME: Can CC entries somehow be added to the MIDNAM file? + // For now, limiting this to note mappings. const auto& midimap = mmap.getMap(); std::map<int, std::string> map; for(const auto& entry : midimap) { - // in case of multiple instruments mapped to one note, use '/' as separator - if(!map[entry.note_id].empty()) + if(entry.from_kind == MapFrom::Note && + entry.to_kind == MapTo::PlayInstrument) { - map[entry.note_id] += "/"; + // in case of multiple instruments mapped to one note, use '/' as separator + if(!map[entry.from_id].empty()) + { + map[entry.from_id] += "/"; + } + map[entry.from_id] += entry.instrument_name; } - map[entry.note_id] += entry.instrument_name; } midnam.reserve(map.size()); 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<AudioFileDOM> 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 <cassert> @@ -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<int>(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<float>(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<float>(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 <string> +#include <map> #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<int, InstrumentState> 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 <cmath> #include <cstdio> #include <cassert> #include <cstring> -#include <mutex> #include "audiotypes.h" #include <config.h> 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 <set> -std::vector<int> MidiMapper::lookup(int note_id) +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<int> MidiMapper::lookup_instruments(std::vector<MidimapEntry> const& entries) { + const std::lock_guard<std::mutex> guard(mutex); + std::set<int> rval; + for(const auto& entry : entries) + { + auto it = instrmap.find(entry.instrument_name); + if (it != instrmap.end()) { + rval.insert(it->second); + } + } + return std::vector<int>(rval.begin(), rval.end()); +} + +std::vector<MidimapEntry> MidiMapper::lookup( + int from_id, + MapFrom from_kind, + MapTo to_kind, + InstrumentStateKind state_kind) { - 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) + 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 <mutex> #include <vector> +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<MidimapEntry>; @@ -43,8 +66,16 @@ 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); + std::vector<int> lookup_instruments(std::vector<MidimapEntry> 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 <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..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<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}; @@ -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/test/dgreftest/midiinputengine.cc b/test/dgreftest/midiinputengine.cc index dbffec9..8d1df8e 100644 --- a/test/dgreftest/midiinputengine.cc +++ b/test/dgreftest/midiinputengine.cc @@ -143,6 +143,7 @@ void MidifileInputEngine::run(size_t pos, size_t len, std::vector<event_t>& even current_event = smf_get_next_event(smf); } + // FIXME: not sure if this section should support CC control? while(current_event && current_event->time_seconds < current_max_time) { if(!smf_event_is_metadata(current_event)) @@ -155,7 +156,8 @@ 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); + auto entries = mmap.lookup(key, MapFrom::Note, MapTo::PlayInstrument); + auto instruments = mmap.lookup_instruments(entries); for(const auto& instrument_idx : instruments) { events.emplace_back(); diff --git a/test/midimapparsertest.cc b/test/midimapparsertest.cc index 3e77c44..7ec95fb 100644 --- a/test/midimapparsertest.cc +++ b/test/midimapparsertest.cc @@ -59,24 +59,24 @@ public: const auto& midimap = parser.midimap; uASSERT_EQUAL(6u, midimap.size()); - uASSERT_EQUAL(54, midimap[0].note_id); + uASSERT_EQUAL(54, midimap[0].from_id); uASSERT_EQUAL(std::string("Crash_left_tip"), midimap[0].instrument_name); - uASSERT_EQUAL(60, midimap[1].note_id); + uASSERT_EQUAL(60, midimap[1].from_id); uASSERT_EQUAL(std::string("Crash_left_whisker"), midimap[1].instrument_name); - uASSERT_EQUAL(55, midimap[2].note_id); + uASSERT_EQUAL(55, midimap[2].from_id); uASSERT_EQUAL(std::string("Crash_right_tip"), midimap[2].instrument_name); // These next two note numbers are intentionally the same and trigger two // different instruments: - uASSERT_EQUAL(62, midimap[3].note_id); + uASSERT_EQUAL(62, midimap[3].from_id); uASSERT_EQUAL(std::string("Crash_right_whisker"), midimap[3].instrument_name); - uASSERT_EQUAL(62, midimap[4].note_id); + uASSERT_EQUAL(62, midimap[4].from_id); uASSERT_EQUAL(std::string("Hihat_closed"), midimap[4].instrument_name); - uASSERT_EQUAL(56, midimap[5].note_id); + uASSERT_EQUAL(56, midimap[5].from_id); uASSERT_EQUAL(std::string("Hihat_closed"), midimap[5].instrument_name); } diff --git a/test/midimappertest.cc b/test/midimappertest.cc index 703c646..e8f02db 100644 --- a/test/midimappertest.cc +++ b/test/midimappertest.cc @@ -66,25 +66,29 @@ public: mapper.swap(instrmap, midimap); { - auto is = mapper.lookup(54); + auto is = mapper.lookup_instruments( + mapper.lookup(54, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(1u, is.size()); uASSERT_EQUAL(0, is[0]); } { - auto is = mapper.lookup(60); + auto is = mapper.lookup_instruments( + mapper.lookup(60, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(1u, is.size()); uASSERT_EQUAL(1, is[0]); } { - auto is = mapper.lookup(55); + auto is = mapper.lookup_instruments( + mapper.lookup(55, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(1u, is.size()); uASSERT_EQUAL(2, is[0]); } { - auto is = mapper.lookup(62); + auto is = mapper.lookup_instruments( + mapper.lookup(62, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(2u, is.size()); // We don't care about the order, so just count the instances uASSERT_EQUAL(1u, std::count(is.begin(), is.end(), 3)); @@ -92,7 +96,8 @@ public: } { - auto is = mapper.lookup(56); + auto is = mapper.lookup_instruments( + mapper.lookup(56, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(1u, is.size()); uASSERT_EQUAL(4, is[0]); } @@ -124,13 +129,15 @@ public: // no such note id { - auto is = mapper.lookup(42); + auto is = mapper.lookup_instruments( + mapper.lookup(42, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(0u, is.size()); } // no such instrument { - auto is = mapper.lookup(60); + auto is = mapper.lookup_instruments( + mapper.lookup(60, MapFrom::Note, MapTo::PlayInstrument)); uASSERT_EQUAL(0u, is.size()); } } |