From fc611232d218758b2bef5f7ccf71fdf7e9139851 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 18 Nov 2022 14:04:32 -0700 Subject: [PATCH] redesign and reimplement save/restore of MIDI learn for triggers --- libs/ardour/ardour/triggerbox.h | 10 +-- libs/ardour/session_state.cc | 6 ++ libs/ardour/triggerbox.cc | 118 +++++++++++++++++++++++++------- 3 files changed, 106 insertions(+), 28 deletions(-) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 39f6247687..c6d49c0371 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -815,11 +815,13 @@ class LIBARDOUR_API TriggerBox : public Processor /* This is null for TriggerBoxen constructed with DataType::AUDIO */ MidiStateTracker* tracker; - static bool lookup_custom_midi_binding (int id, int& x, int& y); - static void add_custom_midi_binding (int id, int x, int y); + static bool lookup_custom_midi_binding (std::vector const &, int& x, int& y); + static void add_custom_midi_binding (std::vector const &, int x, int y); static void remove_custom_midi_binding (int x, int y); static void clear_custom_midi_bindings (); - static int dump_custom_midi_bindings (std::string const & path); + static int save_custom_midi_bindings (std::string const & path); + static int load_custom_midi_bindings (XMLNode const &); + static XMLNode* get_custom_midi_binding_state (); void begin_midi_learn (int index); void midi_unlearn (int index); @@ -936,7 +938,7 @@ class LIBARDOUR_API TriggerBox : public Processor PBD::ScopedConnection stop_all_connection; - typedef std::map > CustomMidiMap; + typedef std::map,std::pair > CustomMidiMap; static CustomMidiMap _custom_midi_map; static void midi_learn_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t); diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 89b33e4074..05d7dbe7ab 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1344,6 +1344,8 @@ Session::state (bool save_template, snapshot_t snapshot_type, bool for_archive, } } + node->add_child_nocopy (*TriggerBox::get_custom_midi_binding_state()); + child = node->add_child ("Regions"); if (!save_template) { @@ -1782,6 +1784,10 @@ Session::set_state (const XMLNode& node, int version) error << _("Session: XML state has no options section") << endmsg; } + if ((child = find_named_node (node, X_("TriggerBindings"))) != 0) { + TriggerBox::load_custom_midi_bindings (*child); + } + if (version >= 3000) { if ((child = find_named_node (node, "Metadata")) == 0) { warning << _("Session: XML state has no metadata section") << endmsg; diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 0f4fb1db5d..3431a7dcd8 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -3867,9 +3867,11 @@ TriggerBox::note_to_trigger (int midi_note, int channel) case Custom: if (!_learning) { - if (lookup_custom_midi_binding (channel * 16 + midi_note, x, y)) { + + std::vector msg { uint8_t (MIDI::on | channel), (uint8_t) midi_note }; + + if (lookup_custom_midi_binding (msg, x, y)) { if (x == _order) { - std::cerr << "yes, slot " << y << std::endl; return y; } } @@ -3885,9 +3887,9 @@ TriggerBox::note_to_trigger (int midi_note, int channel) } bool -TriggerBox::lookup_custom_midi_binding (int id, int& x, int& y) +TriggerBox::lookup_custom_midi_binding (std::vector const & msg, int& x, int& y) { - CustomMidiMap::iterator i = _custom_midi_map.find (id); + CustomMidiMap::iterator i = _custom_midi_map.find (msg); if (i == _custom_midi_map.end()) { return false; @@ -3907,9 +3909,9 @@ TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz, } if ((ev[0] & 0xf0) == MIDI::on) { - int channel = ev[0] & 0xf; - int note = ev[1] & 0x7f; - add_custom_midi_binding (channel * 16 + note, learning_for.first, learning_for.second); + /* throw away velocity */ + std::vector msg { ev[0], ev[1] }; + add_custom_midi_binding (msg, learning_for.first, learning_for.second); TriggerMIDILearned (); /* emit signal */ return; } @@ -3970,32 +3972,103 @@ TriggerBox::clear_custom_midi_bindings () } int -TriggerBox::dump_custom_midi_bindings (std::string const & path) +TriggerBox::save_custom_midi_bindings (std::string const & path) { - std::ofstream f (path.c_str()); + XMLTree tree; - if (!f) { + tree.set_root (get_custom_midi_binding_state()); + + if (!tree.write (path)) { return -1; } - f << "\n\n"; + return 0; +} - for (CustomMidiMap::iterator i = _custom_midi_map.begin(); i != _custom_midi_map.end(); ++i) { - string str = string_compose (X_("\t\n"), - i->first % 16, /* note number */ - i->first / 16, /* channel */ - i->second.first, - i->second.second); - f << str; +XMLNode* +TriggerBox::get_custom_midi_binding_state () +{ + XMLTree tree; + XMLNode* root = new XMLNode (X_("TriggerBindings")); + + for (auto const & b : _custom_midi_map) { + + XMLNode* n = new XMLNode (X_("Binding")); + n->set_property (X_("col"), b.second.first); + n->set_property (X_("row"), b.second.second); + + std::stringstream str; + + for (auto const & v : b.first) { + str << std::hex << "0x" << (int) v << ' '; + } + + n->set_property (X_("msg"), str.str()); + + root->add_child_nocopy (*n); } - f << "\n"; + return root; +} + +int +TriggerBox::load_custom_midi_bindings (XMLNode const & root) +{ + if (root.name() != X_("TriggerBindings")) { + return -1; + } + + _custom_midi_map.clear (); + + for (auto const & n : root.children()) { + int x; + int y; + + if (n->name() != X_("Binding")) { + continue; + } + + if (!n->get_property (X_("col"), x)) { + continue; + } + + if (!n->get_property (X_("row"), y)) { + continue; + } + + std::string str; + + if (!n->get_property (X_("msg"), str)) { + continue; + } + + std::istringstream istr (str); + std::vector msg; + + do { + int x; + istr >> std::setbase (16) >> x; + if (!istr) { + break; + } + msg.push_back (uint8_t (x)); + + } while (true); + + add_custom_midi_binding (msg, x, y); + } + + return 0; } void -TriggerBox::add_custom_midi_binding (int id, int x, int y) +TriggerBox::add_custom_midi_binding (std::vector const & msg, int x, int y) { - _custom_midi_map.insert (std::make_pair (id, std::make_pair (x, y))); + std::pair res = _custom_midi_map.insert (std::make_pair (msg, std::make_pair (x, y))); + + if (!res.second) { + _custom_midi_map[msg] = std::make_pair (x, y); + } } void @@ -4057,12 +4130,9 @@ TriggerBox::process_midi_trigger_requests (BufferSet& bufs) */ t->set_velocity_gain (1.0 - (t->velocity_effect() * (*ev).velocity() / 127.f)); } - std:: cerr << "bang\n"; t->bang (); } else if ((*ev).is_note_off()) { - - std:: cerr << "unbang\n"; t->unbang (); } }