diff --git a/gtk2_ardour/automation_controller.cc b/gtk2_ardour/automation_controller.cc index 4fa7f67ccd..20ef5ab21c 100644 --- a/gtk2_ardour/automation_controller.cc +++ b/gtk2_ardour/automation_controller.cc @@ -21,11 +21,13 @@ #include #include +#include "pbd/compose.h" #include "pbd/error.h" #include "ardour/automatable.h" #include "ardour/automation_control.h" #include "ardour/session.h" +#include "ardour/tempo.h" #include "ardour_ui.h" #include "automation_controller.h" @@ -158,19 +160,56 @@ AutomationController::run_note_select_dialog() delete dialog; } +void +AutomationController::set_freq_beats(double beats) +{ + const ARDOUR::Session& session = _controllable->session(); + const ARDOUR::Tempo& tempo = session.tempo_map().tempo_at(0); + const double bpm = tempo.beats_per_minute(); + const double bps = bpm / 60.0; + _controllable->set_value(bps / beats); +} + +void +AutomationController::set_ratio(double ratio) +{ + _controllable->set_value(_controllable->get_value() * ratio); +} + bool AutomationController::on_button_release(GdkEventButton* ev) { using namespace Gtk::Menu_Helpers; - if (ev->button == 3 && _controllable->desc().unit == ARDOUR::ParameterDescriptor::MIDI_NOTE) { + if (ev->button != 3) { + return false; + } + + if (_controllable->desc().unit == ARDOUR::ParameterDescriptor::MIDI_NOTE) { Gtk::Menu* menu = manage(new Menu()); MenuList& items = menu->items(); items.push_back(MenuElem(_("Select Note..."), sigc::mem_fun(*this, &AutomationController::run_note_select_dialog))); menu->popup(1, ev->time); return true; + } else if (_controllable->desc().unit == ARDOUR::ParameterDescriptor::HZ) { + Gtk::Menu* menu = manage(new Menu()); + MenuList& items = menu->items(); + items.push_back(MenuElem(_("Halve"), + sigc::bind(sigc::mem_fun(*this, &AutomationController::set_ratio), + 0.5))); + items.push_back(MenuElem(_("Double"), + sigc::bind(sigc::mem_fun(*this, &AutomationController::set_ratio), + 2.0))); + for (double beats = 1.0; beats <= 16; ++beats) { + items.push_back(MenuElem(string_compose(_("Set to %1 beat(s)"), (int)beats), + sigc::bind(sigc::mem_fun(*this, &AutomationController::set_freq_beats), + beats))); + } + menu->popup(1, ev->time); + return true; } + return false; } diff --git a/gtk2_ardour/automation_controller.h b/gtk2_ardour/automation_controller.h index fcc3904e49..6350c5b67e 100644 --- a/gtk2_ardour/automation_controller.h +++ b/gtk2_ardour/automation_controller.h @@ -72,6 +72,8 @@ private: void end_touch(); void run_note_select_dialog(); + void set_ratio(double ratio); + void set_freq_beats(double beats); bool on_button_release(GdkEventButton* ev); void value_changed(); diff --git a/libs/ardour/ardour/parameter_descriptor.h b/libs/ardour/ardour/parameter_descriptor.h index 379d97ef3e..d647183b2d 100644 --- a/libs/ardour/ardour/parameter_descriptor.h +++ b/libs/ardour/ardour/parameter_descriptor.h @@ -37,6 +37,7 @@ struct ParameterDescriptor NONE, ///< No unit DB, ///< Decibels MIDI_NOTE, ///< MIDI note number + HZ, ///< Frequency in Hertz }; ParameterDescriptor(const Evoral::Parameter& parameter) diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 61f57a6cc6..86d8c66593 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -547,7 +547,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void set_silent (bool yn); bool silent () { return _silent; } - TempoMap& tempo_map() { return *_tempo_map; } + TempoMap& tempo_map() { return *_tempo_map; } + const TempoMap& tempo_map() const { return *_tempo_map; } /* region info */ diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index f78d018082..4affd89eae 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -148,6 +148,7 @@ public: LilvNode* ui_GtkUI; LilvNode* ui_external; LilvNode* ui_externalkx; + LilvNode* units_hz; LilvNode* units_db; LilvNode* units_unit; LilvNode* units_render; @@ -1337,6 +1338,8 @@ load_parameter_descriptor_units(LilvWorld* lworld, ParameterDescriptor& desc, co desc.unit = ParameterDescriptor::MIDI_NOTE; } else if (lilv_nodes_contains(units, _world.units_db)) { desc.unit = ParameterDescriptor::DB; + } else if (lilv_nodes_contains(units, _world.units_hz)) { + desc.unit = ParameterDescriptor::HZ; } else if (lilv_nodes_size(units) > 0) { const LilvNode* unit = lilv_nodes_get_first(units); LilvNode* render = get_value(lworld, unit, _world.units_render); @@ -2314,6 +2317,7 @@ LV2World::LV2World() ui_externalkx = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"); units_unit = lilv_new_uri(world, LV2_UNITS__unit); units_render = lilv_new_uri(world, LV2_UNITS__render); + units_hz = lilv_new_uri(world, LV2_UNITS__hz); units_midiNote = lilv_new_uri(world, LV2_UNITS__midiNote); units_db = lilv_new_uri(world, LV2_UNITS__db); patch_writable = lilv_new_uri(world, LV2_PATCH__writable); @@ -2324,6 +2328,7 @@ LV2World::~LV2World() { lilv_node_free(patch_Message); lilv_node_free(patch_writable); + lilv_node_free(units_hz); lilv_node_free(units_midiNote); lilv_node_free(units_db); lilv_node_free(units_unit);