diff options
author | David Robillard <d@drobilla.net> | 2014-01-06 17:02:55 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-01-10 20:51:54 -0500 |
commit | 72d8ca89e2f4b2e194b2bbd99261deb5b2108a40 (patch) | |
tree | 4e187229cf6cad791d4b8f0896a265d0f3574662 | |
parent | 0fe968a140cab6270b9480059cb173e7f7b33322 (diff) |
Support midnam controller value labels.
-rw-r--r-- | gtk2_ardour/automation_line.h | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_automation_line.cc | 43 | ||||
-rw-r--r-- | gtk2_ardour/midi_automation_line.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_patch_manager.h | 2 | ||||
-rw-r--r-- | libs/ardour/instrument_info.cc | 2 | ||||
-rw-r--r-- | libs/midi++2/midi++/midnam_patch.h | 66 | ||||
-rw-r--r-- | libs/midi++2/midnam_patch.cc | 152 | ||||
-rw-r--r-- | libs/midi++2/test/MidnamTest.cpp | 11 |
8 files changed, 266 insertions, 14 deletions
diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index 405fc63e11..054e84e789 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -112,7 +112,7 @@ public: ArdourCanvas::Item& parent_group() const { return _parent_group; } ArdourCanvas::Item& grab_item() const { return *line; } - std::string get_verbose_cursor_string (double) const; + virtual std::string get_verbose_cursor_string (double) const; std::string get_verbose_cursor_relative_string (double, double) const; std::string fraction_to_string (double) const; std::string fraction_to_relative_string (double, double) const; diff --git a/gtk2_ardour/midi_automation_line.cc b/gtk2_ardour/midi_automation_line.cc index a61c17601a..971944266f 100644 --- a/gtk2_ardour/midi_automation_line.cc +++ b/gtk2_ardour/midi_automation_line.cc @@ -18,7 +18,11 @@ */ #include "ardour/midi_automation_list_binder.h" +#include "midi++/midnam_patch.h" #include "midi_automation_line.h" +#include "midi_time_axis.h" + +#include "i18n.h" using namespace std; @@ -42,3 +46,42 @@ MidiAutomationLine::memento_command_binder () { return new ARDOUR::MidiAutomationListBinder (_region->midi_source(), _parameter); } + +string +MidiAutomationLine::get_verbose_cursor_string (double fraction) const +{ + using namespace MIDI::Name; + + if (_parameter.type() != ARDOUR::MidiCCAutomation) { + return AutomationLine::get_verbose_cursor_string(fraction); + } + + MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(trackview.get_parent()); + if (!mtv) { + return AutomationLine::get_verbose_cursor_string(fraction); + } + + boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names()); + if (!device_names) { + return AutomationLine::get_verbose_cursor_string(fraction); + } + + const std::string& device_mode = mtv->gui_property(X_("midnam-custom-device-mode")); + const uint8_t channel = mtv->get_channel_for_add(); + + boost::shared_ptr<const ValueNameList> value_names = device_names->value_name_list_by_control( + device_mode, channel, _parameter.id()); + if (!value_names) { + return AutomationLine::get_verbose_cursor_string(fraction); + } + + const uint16_t cc_value = floor(std::max(std::min(fraction * 127.0, 127.0), 0.0)); + + boost::shared_ptr<const Value> value = value_names->max_value_below(cc_value); + if (!value) { + return AutomationLine::get_verbose_cursor_string(fraction); + } + + return value->name(); +} + diff --git a/gtk2_ardour/midi_automation_line.h b/gtk2_ardour/midi_automation_line.h index 2e1195a16b..df4db06c2c 100644 --- a/gtk2_ardour/midi_automation_line.h +++ b/gtk2_ardour/midi_automation_line.h @@ -34,6 +34,8 @@ public: MementoCommandBinder<ARDOUR::AutomationList>* memento_command_binder (); + virtual std::string get_verbose_cursor_string (double) const; + private: boost::shared_ptr<ARDOUR::MidiRegion> _region; Evoral::Parameter _parameter; diff --git a/libs/ardour/ardour/midi_patch_manager.h b/libs/ardour/ardour/midi_patch_manager.h index 32b3ebc61d..77e63a2791 100644 --- a/libs/ardour/ardour/midi_patch_manager.h +++ b/libs/ardour/ardour/midi_patch_manager.h @@ -72,7 +72,7 @@ public: boost::shared_ptr<MIDI::Name::MasterDeviceNames> master_device = master_device_by_model(model); if (master_device != 0 && custom_device_mode != "") { - return master_device->channel_name_set_by_device_mode_and_channel(custom_device_mode, channel); + return master_device->channel_name_set_by_channel(custom_device_mode, channel); } else { return boost::shared_ptr<ChannelNameSet>(); } diff --git a/libs/ardour/instrument_info.cc b/libs/ardour/instrument_info.cc index 725dc3b5e9..d6c18ebd4d 100644 --- a/libs/ardour/instrument_info.cc +++ b/libs/ardour/instrument_info.cc @@ -119,7 +119,7 @@ InstrumentInfo::get_controller_name (Evoral::Parameter param) const } boost::shared_ptr<ChannelNameSet> chan_names( - dev_names->channel_name_set_by_device_mode_and_channel( + dev_names->channel_name_set_by_channel( external_instrument_mode, param.channel())); if (!chan_names) { return ""; diff --git a/libs/midi++2/midi++/midnam_patch.h b/libs/midi++2/midi++/midnam_patch.h index ddd62c2916..f3d766d7e3 100644 --- a/libs/midi++2/midi++/midnam_patch.h +++ b/libs/midi++2/midi++/midnam_patch.h @@ -262,6 +262,55 @@ private: Notes _notes; }; +class Value +{ +public: + Value() {} + Value(const uint16_t number, + const std::string& name) + : _number(number) + , _name(name) + {} + + uint16_t number() const { return _number; } + const std::string& name() const { return _name; } + + void set_number(uint16_t number) { _number = number; } + void set_name(const std::string& name) { _name = name; } + + XMLNode& get_state(void); + int set_state(const XMLTree&, const XMLNode&); + +private: + uint16_t _number; + std::string _name; +}; + +class ValueNameList +{ +public: + typedef std::map<uint16_t, boost::shared_ptr<Value> > Values; + + ValueNameList() {} + ValueNameList(const std::string& name) : _name(name) {} + + const std::string& name() const { return _name; } + + void set_name(const std::string& name) { _name = name; } + + boost::shared_ptr<const Value> value(uint16_t num) const; + boost::shared_ptr<const Value> max_value_below(uint16_t num) const; + + const Values& values() const { return _values; } + + XMLNode& get_state(void); + int set_state(const XMLTree&, const XMLNode&); + +private: + std::string _name; + Values _values; +}; + class Control { public: @@ -278,6 +327,9 @@ public: uint16_t number() const { return _number; } const std::string& name() const { return _name; } + const std::string& value_name_list_name() const { return _value_name_list_name; } + boost::shared_ptr<const ValueNameList> value_name_list() const { return _value_name_list; } + void set_type(const std::string& type) { _type = type; } void set_number(uint16_t number) { _number = number; } void set_name(const std::string& name) { _name = name; } @@ -289,6 +341,9 @@ private: std::string _type; uint16_t _number; std::string _name; + + std::string _value_name_list_name; ///< Global, UsesValueNameList + boost::shared_ptr<ValueNameList> _value_name_list; ///< Local, ValueNameList }; class ControlNameList @@ -352,6 +407,7 @@ public: typedef std::map<std::string, boost::shared_ptr<ChannelNameSet> > ChannelNameSets; typedef std::map<std::string, boost::shared_ptr<NoteNameList> > NoteNameLists; typedef std::map<std::string, boost::shared_ptr<ControlNameList> > ControlNameLists; + typedef std::map<std::string, boost::shared_ptr<ValueNameList> > ValueNameLists; typedef std::map<std::string, PatchNameList> PatchNameLists; MasterDeviceNames() {}; @@ -364,14 +420,21 @@ public: void set_models(const Models some_models) { _models = some_models; } const ControlNameLists& controls() const { return _control_name_lists; } + const ValueNameLists& values() const { return _value_name_lists; } + + boost::shared_ptr<const ValueNameList> value_name_list_by_control( + const std::string& mode, + uint8_t channel, + uint8_t number); const CustomDeviceModeNames& custom_device_mode_names() const { return _custom_device_mode_names; } boost::shared_ptr<CustomDeviceMode> custom_device_mode_by_name(const std::string& mode_name); - boost::shared_ptr<ChannelNameSet> channel_name_set_by_device_mode_and_channel(const std::string& mode, uint8_t channel); + boost::shared_ptr<ChannelNameSet> channel_name_set_by_channel(const std::string& mode, uint8_t channel); boost::shared_ptr<Patch> find_patch(const std::string& mode, uint8_t channel, const PatchPrimaryKey& key); boost::shared_ptr<ControlNameList> control_name_list(const std::string& name); + boost::shared_ptr<ValueNameList> value_name_list(const std::string& name); boost::shared_ptr<NoteNameList> note_name_list(const std::string& name); boost::shared_ptr<ChannelNameSet> channel_name_set(const std::string& name); @@ -393,6 +456,7 @@ private: NoteNameLists _note_name_lists; PatchNameLists _patch_name_lists; ControlNameLists _control_name_lists; + ValueNameLists _value_name_lists; }; class MIDINameDocument diff --git a/libs/midi++2/midnam_patch.cc b/libs/midi++2/midnam_patch.cc index bd8bbfc42d..0b8f1ffcb2 100644 --- a/libs/midi++2/midnam_patch.cc +++ b/libs/midi++2/midnam_patch.cc @@ -251,6 +251,22 @@ Control::set_state (const XMLTree& tree, const XMLNode& node) _number = string_to_int(tree, node.property("Number")->value()); _name = node.property("Name")->value(); + for (XMLNodeList::const_iterator i = node.children().begin(); + i != node.children().end(); ++i) { + if ((*i)->name() == "Values") { + // <Values> has Min and Max properties, but we don't care + for (XMLNodeList::const_iterator j = (*i)->children().begin(); + j != (*i)->children().end(); ++j) { + if ((*j)->name() == "ValueNameList") { + _value_name_list = boost::shared_ptr<ValueNameList>(new ValueNameList()); + _value_name_list->set_state(tree, **j); + } else if ((*j)->name() == "UsesValueNameList") { + _value_name_list_name = (*j)->property("Name")->value(); + } + } + } + } + return 0; } @@ -299,6 +315,91 @@ ControlNameList::control(uint16_t num) const } XMLNode& +Value::get_state (void) +{ + XMLNode* node = new XMLNode("Value"); + node->add_property("Number", _number); + node->add_property("Name", _name); + + return *node; +} + +int +Value::set_state (const XMLTree& tree, const XMLNode& node) +{ + assert(node.name() == "Value"); + _number = string_to_int(tree, node.property("Number")->value()); + _name = node.property("Name")->value(); + + return 0; +} + +XMLNode& +ValueNameList::get_state (void) +{ + XMLNode* node = new XMLNode("ValueNameList"); + node->add_property("Name", _name); + + return *node; +} + +int +ValueNameList::set_state (const XMLTree& tree, const XMLNode& node) +{ + assert(node.name() == "ValueNameList"); + const XMLProperty* name_prop = node.property("Name"); + if (name_prop) { + // May be anonymous if written inline within a single <Control> tag + _name = name_prop->value(); + } + + _values.clear(); + for (XMLNodeList::const_iterator i = node.children().begin(); + i != node.children().end(); ++i) { + if ((*i)->name() == "Value") { + boost::shared_ptr<Value> value(new Value()); + value->set_state (tree, *(*i)); + if (_values.find(value->number()) == _values.end()) { + _values.insert(make_pair(value->number(), value)); + } else { + PBD::warning << string_compose("%1: Duplicate value %2 ignored", + tree.filename(), value->number()) + << endmsg; + } + } + } + + return 0; +} + +boost::shared_ptr<const Value> +ValueNameList::value(uint16_t num) const +{ + Values::const_iterator i = _values.find(num); + if (i != _values.end()) { + return i->second; + } + return boost::shared_ptr<const Value>(); +} + +boost::shared_ptr<const Value> +ValueNameList::max_value_below(uint16_t num) const +{ + Values::const_iterator i = _values.lower_bound(num); + if (i->first == num) { + // Exact match + return i->second; + } else if (i == _values.begin()) { + // No value is < num + return boost::shared_ptr<const Value>(); + } else { + // Found the smallest element >= num, so the previous one is our result + --i; + return i->second; + } +} + +XMLNode& PatchBank::get_state (void) { XMLNode* node = new XMLNode("PatchBank"); @@ -525,6 +626,31 @@ CustomDeviceMode::get_state(void) return *custom_device_mode; } +boost::shared_ptr<const ValueNameList> +MasterDeviceNames::value_name_list_by_control(const std::string& mode, uint8_t channel, uint8_t number) +{ + boost::shared_ptr<ChannelNameSet> chan_names = channel_name_set_by_channel(mode, channel); + if (!chan_names) { + return boost::shared_ptr<const ValueNameList>(); + } + + boost::shared_ptr<ControlNameList> control_names = control_name_list(chan_names->control_list_name()); + if (!control_names) { + return boost::shared_ptr<const ValueNameList>(); + } + + boost::shared_ptr<const Control> control = control_names->control(number); + if (!control) { + return boost::shared_ptr<const ValueNameList>(); + } + + if (!control->value_name_list_name().empty()) { + return value_name_list(control->value_name_list_name()); + } else { + return control->value_name_list(); + } +} + boost::shared_ptr<CustomDeviceMode> MasterDeviceNames::custom_device_mode_by_name(const std::string& mode_name) { @@ -532,7 +658,7 @@ MasterDeviceNames::custom_device_mode_by_name(const std::string& mode_name) } boost::shared_ptr<ChannelNameSet> -MasterDeviceNames::channel_name_set_by_device_mode_and_channel(const std::string& mode, uint8_t channel) +MasterDeviceNames::channel_name_set_by_channel(const std::string& mode, uint8_t channel) { boost::shared_ptr<CustomDeviceMode> cdm = custom_device_mode_by_name(mode); boost::shared_ptr<ChannelNameSet> cns = _channel_name_sets[cdm->channel_name_set_name_by_channel(channel)]; @@ -542,7 +668,7 @@ MasterDeviceNames::channel_name_set_by_device_mode_and_channel(const std::string boost::shared_ptr<Patch> MasterDeviceNames::find_patch(const std::string& mode, uint8_t channel, const PatchPrimaryKey& key) { - boost::shared_ptr<ChannelNameSet> cns = channel_name_set_by_device_mode_and_channel(mode, channel); + boost::shared_ptr<ChannelNameSet> cns = channel_name_set_by_channel(mode, channel); if (!cns) return boost::shared_ptr<Patch>(); return cns->find_patch(key); } @@ -567,6 +693,16 @@ MasterDeviceNames::control_name_list(const std::string& name) return boost::shared_ptr<ControlNameList>(); } +boost::shared_ptr<ValueNameList> +MasterDeviceNames::value_name_list(const std::string& name) +{ + ValueNameLists::const_iterator i = _value_name_lists.find(name); + if (i != _value_name_lists.end()) { + return i->second; + } + return boost::shared_ptr<ValueNameList>(); +} + boost::shared_ptr<NoteNameList> MasterDeviceNames::note_name_list(const std::string& name) { @@ -598,7 +734,7 @@ MasterDeviceNames::note_name(const std::string& mode_name, note_name_list(patch->note_list_name())); if (!note_names) { /* No note names specific to this patch, check the ChannelNameSet */ - boost::shared_ptr<ChannelNameSet> chan_names = channel_name_set_by_device_mode_and_channel( + boost::shared_ptr<ChannelNameSet> chan_names = channel_name_set_by_channel( mode_name, channel); if (chan_names) { note_names = note_name_list(chan_names->note_list_name()); @@ -675,6 +811,16 @@ MasterDeviceNames::set_state(const XMLTree& tree, const XMLNode&) _control_name_lists[control_name_list->name()] = control_name_list; } + // ValueNameLists + boost::shared_ptr<XMLSharedNodeList> value_name_lists = tree.find("/child::MIDINameDocument/child::MasterDeviceNames/child::ValueNameList"); + for (XMLSharedNodeList::iterator i = value_name_lists->begin(); + i != value_name_lists->end(); + ++i) { + boost::shared_ptr<ValueNameList> value_name_list(new ValueNameList()); + value_name_list->set_state (tree, *(*i)); + _value_name_lists[value_name_list->name()] = value_name_list; + } + // global/post-facto PatchNameLists boost::shared_ptr<XMLSharedNodeList> patch_name_lists = tree.find("/child::MIDINameDocument/child::MasterDeviceNames/child::PatchNameList"); for (XMLSharedNodeList::iterator i = patch_name_lists->begin(); diff --git a/libs/midi++2/test/MidnamTest.cpp b/libs/midi++2/test/MidnamTest.cpp index ba96d318c4..d8c89eb011 100644 --- a/libs/midi++2/test/MidnamTest.cpp +++ b/libs/midi++2/test/MidnamTest.cpp @@ -56,11 +56,9 @@ MidnamTest::protools_patchfile_test() } boost::shared_ptr<ChannelNameSet> nameSet1 = - masterDeviceNames->channel_name_set_by_device_mode_and_channel( - modename, 0); + masterDeviceNames->channel_name_set_by_channel(modename, 0); boost::shared_ptr<ChannelNameSet> nameSet2 = - masterDeviceNames->channel_name_set_by_device_mode_and_channel( - modename, 9); + masterDeviceNames->channel_name_set_by_channel(modename, 9); CPPUNIT_ASSERT_EQUAL(ns1, nameSet1->name()); CPPUNIT_ASSERT_EQUAL(ns2, nameSet2->name()); @@ -127,8 +125,7 @@ MidnamTest::yamaha_PSRS900_patchfile_test() CPPUNIT_ASSERT_EQUAL(ns, mode->channel_name_set_name_by_channel(i)); boost::shared_ptr<ChannelNameSet> nameSet = - masterDeviceNames->channel_name_set_by_device_mode_and_channel( - ns, 1); + masterDeviceNames->channel_name_set_by_channel(ns, 1); CPPUNIT_ASSERT_EQUAL(ns, nameSet->name()); @@ -196,7 +193,7 @@ MidnamTest::load_all_midnams_test () boost::shared_ptr<CustomDeviceMode> mode = device->second->custom_device_mode_by_name(modename); CPPUNIT_ASSERT_EQUAL(deviceModeName, mode->name()); - boost::shared_ptr<ChannelNameSet> nameSet = device->second->channel_name_set_by_device_mode_and_channel(modename, 0); + boost::shared_ptr<ChannelNameSet> nameSet = device->second->channel_name_set_by_channel(modename, 0); } } |