summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSander Vocke <sandervocke@gmail.com>2024-07-23 13:23:38 +0200
committerSander Vocke <sandervocke@gmail.com>2024-07-23 13:23:38 +0200
commit0e59f90e2396190ec3e3a7195bac9c79e12fd6cc (patch)
treef7fd25a952c6e64db9108eab2a774e46fd9f0a83
parent019d478818950f7880d2c0f80d8fc8f963e9736b (diff)
Add CC-controller openness parameter for hi-hats.
-rw-r--r--drumgizmo/drumgizmoc.cc12
-rw-r--r--drumgizmo/input/jackmidi.cc9
-rw-r--r--plugin/drumgizmo_plugin.cc15
-rw-r--r--src/DGDOM.h3
-rw-r--r--src/audioinputenginemidi.cc101
-rw-r--r--src/audioinputenginemidi.h3
-rw-r--r--src/dgxmlparser.cc22
-rw-r--r--src/domloader.cc1
-rw-r--r--src/drumgizmo.cc2
-rw-r--r--src/engineevent.h9
-rw-r--r--src/inputprocessor.cc5
-rw-r--r--src/instrument.cc4
-rw-r--r--src/instrument.h2
-rw-r--r--src/instrumentstate.h9
-rw-r--r--src/midimapparser.cc45
-rw-r--r--src/midimapper.cc48
-rw-r--r--src/midimapper.h37
-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.h14
-rw-r--r--test/dgreftest/midiinputengine.cc4
-rw-r--r--test/midimapparsertest.cc12
-rw-r--r--test/midimappertest.cc21
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());
}
}