13
0

redesign and reimplement save/restore of MIDI learn for triggers

This commit is contained in:
Paul Davis 2022-11-18 14:04:32 -07:00
parent 58c2b0a848
commit fc611232d2
3 changed files with 106 additions and 28 deletions

View File

@ -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<uint8_t> const &, int& x, int& y);
static void add_custom_midi_binding (std::vector<uint8_t> 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<int,std::pair<int,int> > CustomMidiMap;
typedef std::map<std::vector<uint8_t>,std::pair<int,int> > CustomMidiMap;
static CustomMidiMap _custom_midi_map;
static void midi_learn_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t);

View File

@ -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;

View File

@ -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<uint8_t> 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<uint8_t> 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<uint8_t> 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ArdourMIDIBindings version=\"1.0.0\" name=\"The name of this set of bindings\">\n";
return 0;
}
for (CustomMidiMap::iterator i = _custom_midi_map.begin(); i != _custom_midi_map.end(); ++i) {
string str = string_compose (X_("\t<Binding msg note=\"%1\" channel=\"%2\" action=\"Cues/trigger-slot-%1-%2\"/>\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 << "</ArdourMIDIBindings>\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<uint8_t> 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<uint8_t> const & msg, int x, int y)
{
_custom_midi_map.insert (std::make_pair (id, std::make_pair (x, y)));
std::pair<CustomMidiMap::iterator,bool> 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 ();
}
}