X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fautomation_time_axis.cc;h=369989334b2e71a5134a1d0ef52f2f82f375ce3c;hb=dfef8b7f6257364053e13ab4d5e317b703abd0af;hp=b2902633ae8ceaf03ee01963683c96cd222d712b;hpb=973d58e8f9f6bbf79ee7eee5647d4dcc4a3ae602;p=ardour.git diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index b2902633ae..369989334b 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -18,26 +18,32 @@ */ #include -#include -#include + #include +#include + #include "pbd/error.h" #include "pbd/memento_command.h" #include "pbd/stacktrace.h" #include "pbd/string_convert.h" #include "pbd/types_convert.h" +#include "pbd/unwind.h" #include "ardour/automation_control.h" -#include "ardour/beats_frames_converter.h" +#include "ardour/beats_samples_converter.h" #include "ardour/event_type_map.h" #include "ardour/parameter_types.h" #include "ardour/profile.h" #include "ardour/route.h" #include "ardour/session.h" +#include "gtkmm2ext/utils.h" + #include "canvas/debug.h" +#include "widgets/tooltips.h" + #include "automation_time_axis.h" #include "automation_streamview.h" #include "gui_thread.h" @@ -46,7 +52,6 @@ #include "paste_context.h" #include "public_editor.h" #include "selection.h" -#include "tooltips.h" #include "rgb_macros.h" #include "point_selection.h" #include "control_point.h" @@ -58,6 +63,7 @@ using namespace std; using namespace ARDOUR; +using namespace ArdourWidgets; using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; @@ -132,12 +138,16 @@ AutomationTimeAxisView::AutomationTimeAxisView ( auto_off_item = 0; auto_touch_item = 0; + auto_latch_item = 0; auto_write_item = 0; auto_play_item = 0; mode_discrete_item = 0; mode_line_item = 0; + mode_log_item = 0; + mode_exp_item = 0; ignore_state_request = false; + ignore_mode_request = false; first_call_to_set_height = true; CANVAS_DEBUG_NAME (_base_rect, string_compose ("base rect for %1", _name)); @@ -152,14 +162,17 @@ AutomationTimeAxisView::AutomationTimeAxisView ( using namespace Menu_Helpers; - auto_dropdown.AddMenuElem (MenuElem (S_("Automation|Manual"), sigc::bind (sigc::mem_fun(*this, + auto_dropdown.AddMenuElem (MenuElem (automation_state_off_string(), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) ARDOUR::Off))); auto_dropdown.AddMenuElem (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Play))); - auto_dropdown.AddMenuElem (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this, - &AutomationTimeAxisView::set_automation_state), (AutoState) Write))); - auto_dropdown.AddMenuElem (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this, - &AutomationTimeAxisView::set_automation_state), (AutoState) Touch))); + + if (!(_parameter.type() >= MidiCCAutomation && + _parameter.type() <= MidiChannelPressureAutomation)) { + auto_dropdown.AddMenuElem (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Write))); + auto_dropdown.AddMenuElem (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch))); + auto_dropdown.AddMenuElem (MenuElem (_("Latch"), sigc::bind (sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Latch))); + } /* XXX translators: use a string here that will be at least as long as the longest automation label (see ::automation_state_changed() @@ -304,13 +317,16 @@ AutomationTimeAxisView::AutomationTimeAxisView ( AutomationTimeAxisView::~AutomationTimeAxisView () { - cleanup_gui_properties (); + if (_stripable) { + cleanup_gui_properties (); + } delete _view; } void AutomationTimeAxisView::route_going_away () { + cleanup_gui_properties (); _stripable.reset (); } @@ -326,6 +342,7 @@ AutomationTimeAxisView::set_automation_state (AutoState state) } else if (_control) { _control->set_automation_state (state); + _session->set_dirty (); } if (_view) { @@ -354,50 +371,76 @@ AutomationTimeAxisView::automation_state_changed () state = ARDOUR::Off; } - switch (state & (ARDOUR::Off|Play|Touch|Write)) { + switch (state & (ARDOUR::Off|Play|Touch|Write|Latch)) { case ARDOUR::Off: - auto_dropdown.set_text (S_("Automation|Manual")); + auto_dropdown.set_text (automation_state_off_string()); + ignore_state_request = true; if (auto_off_item) { - ignore_state_request = true; auto_off_item->set_active (true); auto_play_item->set_active (false); + } + if (auto_touch_item) { auto_touch_item->set_active (false); + auto_latch_item->set_active (false); auto_write_item->set_active (false); - ignore_state_request = false; } + ignore_state_request = false; break; case Play: auto_dropdown.set_text (_("Play")); - if (auto_play_item) { - ignore_state_request = true; + ignore_state_request = true; + if (auto_off_item) { auto_play_item->set_active (true); auto_off_item->set_active (false); + } + if (auto_touch_item) { auto_touch_item->set_active (false); + auto_latch_item->set_active (false); auto_write_item->set_active (false); - ignore_state_request = false; } + ignore_state_request = false; break; case Write: auto_dropdown.set_text (_("Write")); - if (auto_write_item) { - ignore_state_request = true; - auto_write_item->set_active (true); + ignore_state_request = true; + if (auto_off_item) { auto_off_item->set_active (false); auto_play_item->set_active (false); + } + if (auto_touch_item) { + auto_write_item->set_active (true); auto_touch_item->set_active (false); - ignore_state_request = false; + auto_latch_item->set_active (false); } + ignore_state_request = false; break; case Touch: auto_dropdown.set_text (_("Touch")); + ignore_state_request = true; + if (auto_off_item) { + auto_off_item->set_active (false); + auto_play_item->set_active (false); + } if (auto_touch_item) { - ignore_state_request = true; auto_touch_item->set_active (true); + auto_write_item->set_active (false); + auto_latch_item->set_active (false); + } + ignore_state_request = false; + break; + case Latch: + auto_dropdown.set_text (_("Latch")); + ignore_state_request = true; + if (auto_off_item) { auto_off_item->set_active (false); auto_play_item->set_active (false); + } + if (auto_touch_item) { + auto_latch_item->set_active (true); + auto_touch_item->set_active (false); auto_write_item->set_active (false); - ignore_state_request = false; } + ignore_state_request = false; break; default: auto_dropdown.set_text (_("???")); @@ -409,14 +452,33 @@ AutomationTimeAxisView::automation_state_changed () void AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s) { - if (mode_line_item && mode_discrete_item) { - if (s == AutomationList::Discrete) { - mode_discrete_item->set_active(true); - mode_line_item->set_active(false); - } else { - mode_line_item->set_active(true); - mode_discrete_item->set_active(false); - } + if (ignore_mode_request) { + return; + } + PBD::Unwinder uw (ignore_mode_request, true); + switch (s) { + case AutomationList::Discrete: + if (mode_discrete_item) { + mode_discrete_item->set_active(true); + } + break; + case AutomationList::Linear: + if (mode_line_item) { + mode_line_item->set_active(true); + } + break; + case AutomationList::Logarithmic: + if (mode_log_item) { + mode_log_item->set_active(true); + } + break; + case AutomationList::Exponential: + if (mode_exp_item) { + mode_exp_item->set_active(true); + } + break; + default: + break; } } @@ -448,7 +510,9 @@ AutomationTimeAxisView::clear_clicked () } else if (_view) { _view->clear (); } - set_automation_state ((AutoState) ARDOUR::Off); + if (!EventTypeMap::instance().type_is_midi (_control->parameter().type())) { + set_automation_state ((AutoState) ARDOUR::Off); + } _editor.commit_reversible_command (); _session->set_dirty (); } @@ -478,10 +542,7 @@ AutomationTimeAxisView::set_height (uint32_t h, TrackHeightMode m) first_call_to_set_height = false; if (h >= preset_height (HeightNormal)) { - if (!(_parameter.type() >= MidiCCAutomation && - _parameter.type() <= MidiChannelPressureAutomation)) { - auto_dropdown.show(); - } + auto_dropdown.show(); name_label.show(); hide_button.show(); @@ -519,13 +580,23 @@ AutomationTimeAxisView::hide_clicked () { hide_button.set_sensitive(false); set_marked_for_display (false); - RouteTimeAxisView* rtv = dynamic_cast(parent); - if (rtv) { - rtv->request_redraw (); + StripableTimeAxisView* stv = dynamic_cast(parent); + if (stv) { + stv->request_redraw (); } hide_button.set_sensitive(true); } +string +AutomationTimeAxisView::automation_state_off_string () const +{ + if (_parameter.type() >= MidiCCAutomation && _parameter.type() <= MidiChannelPressureAutomation) { + return S_("Automation|Off"); + } + + return S_("Automation|Manual"); +} + void AutomationTimeAxisView::build_display_menu () { @@ -550,7 +621,7 @@ AutomationTimeAxisView::build_display_menu () auto_state_menu->set_name ("ArdourContextMenu"); MenuList& as_items = auto_state_menu->items(); - as_items.push_back (CheckMenuElem (S_("Automation|Manual"), sigc::bind ( + as_items.push_back (CheckMenuElem (automation_state_off_string(), sigc::bind ( sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) ARDOUR::Off))); auto_off_item = dynamic_cast(&as_items.back()); @@ -560,21 +631,26 @@ AutomationTimeAxisView::build_display_menu () (AutoState) Play))); auto_play_item = dynamic_cast(&as_items.back()); - as_items.push_back (CheckMenuElem (_("Write"), sigc::bind ( - sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), - (AutoState) Write))); - auto_write_item = dynamic_cast(&as_items.back()); + if (!(_parameter.type() >= MidiCCAutomation && + _parameter.type() <= MidiChannelPressureAutomation)) { + as_items.push_back (CheckMenuElem (_("Write"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), + (AutoState) Write))); + auto_write_item = dynamic_cast(&as_items.back()); - as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind ( - sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), + as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), (AutoState) Touch))); - auto_touch_item = dynamic_cast(&as_items.back()); + auto_touch_item = dynamic_cast(&as_items.back()); - if (!(_parameter.type() >= MidiCCAutomation && - _parameter.type() <= MidiChannelPressureAutomation)) { - items.push_back (MenuElem (_("State"), *auto_state_menu)); + as_items.push_back (CheckMenuElem (_("Latch"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state), + (AutoState) Latch))); + auto_latch_item = dynamic_cast(&as_items.back()); } + items.push_back (MenuElem (_("State"), *auto_state_menu)); + /* mode menu */ /* current interpolation state */ @@ -592,15 +668,55 @@ AutomationTimeAxisView::build_display_menu () sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), AutomationList::Discrete))); mode_discrete_item = dynamic_cast(&am_items.back()); - mode_discrete_item->set_active (s == AutomationList::Discrete); am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind ( sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), AutomationList::Linear))); mode_line_item = dynamic_cast(&am_items.back()); - mode_line_item->set_active (s == AutomationList::Linear); items.push_back (MenuElem (_("Mode"), *auto_mode_menu)); + + } else { + + Menu* auto_mode_menu = manage (new Menu); + auto_mode_menu->set_name ("ArdourContextMenu"); + MenuList& am_items = auto_mode_menu->items(); + bool have_options = false; + + RadioMenuItem::Group group; + + am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), + AutomationList::Linear))); + mode_line_item = dynamic_cast(&am_items.back()); + + if (_control->desc().logarithmic) { + am_items.push_back (RadioMenuElem (group, _("Logarithmic"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), + AutomationList::Logarithmic))); + mode_log_item = dynamic_cast(&am_items.back()); + have_options = true; + } else { + mode_log_item = 0; + } + + if (_line->get_uses_gain_mapping () && !_control->desc().logarithmic) { + am_items.push_back (RadioMenuElem (group, _("Exponential"), sigc::bind ( + sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation), + AutomationList::Exponential))); + mode_exp_item = dynamic_cast(&am_items.back()); + have_options = true; + } else { + mode_exp_item = 0; + } + + if (have_options) { + items.push_back (MenuElem (_("Interpolation"), *auto_mode_menu)); + } else { + mode_line_item = 0; + delete auto_mode_menu; + auto_mode_menu = 0; + } } /* make sure the automation menu state is correct */ @@ -610,7 +726,7 @@ AutomationTimeAxisView::build_display_menu () } void -AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame, double y, bool with_guard_points) +AutomationTimeAxisView::add_automation_event (GdkEvent* event, samplepos_t sample, double y, bool with_guard_points) { if (!_line) { return; @@ -625,14 +741,14 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame, return; } - MusicFrame when (frame, 0); + MusicSample when (sample, 0); _editor.snap_to_with_modifier (when, event); if (UIConfiguration::instance().get_new_automation_points_on_lane()) { if (_control->list()->size () == 0) { y = _control->get_value (); } else { - y = _control->list()->eval (when.frame); + y = _control->list()->eval (when.sample); } } else { double x = 0; @@ -646,12 +762,12 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame, XMLNode& before = list->get_state(); std::list results; - if (list->editor_add (when.frame, y, with_guard_points)) { + if (list->editor_add (when.sample, y, with_guard_points)) { XMLNode& after = list->get_state(); _editor.begin_reversible_command (_("add automation event")); _session->add_command (new MementoCommand (*list.get (), &before, &after)); - _line->get_selectables (when.frame, when.frame, 0.0, 1.0, results); + _line->get_selectables (when.sample, when.sample, 0.0, 1.0, results); _editor.get_selection ().set (results); _editor.commit_reversible_command (); @@ -660,7 +776,7 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t frame, } bool -AutomationTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t divisions) +AutomationTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t divisions) { if (_line) { return paste_one (pos, ctx.count, ctx.times, selection, ctx.counts, ctx.greedy); @@ -681,7 +797,7 @@ AutomationTimeAxisView::paste (framepos_t pos, const Selection& selection, Paste } bool -AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts, bool greedy) +AutomationTimeAxisView::paste_one (samplepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts, bool greedy) { boost::shared_ptr alist(_line->the_list()); @@ -708,7 +824,7 @@ AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float t if (parameter_is_midi (src_type)) { // convert length to samples (incl tempo-ramps) - len = DoubleBeatsFramesConverter (_session->tempo_map(), pos).to (len * paste_count); + len = DoubleBeatsSamplesConverter (_session->tempo_map(), pos).to (len * paste_count); pos += _editor.get_paste_offset (pos, paste_count > 0 ? 1 : 0, len); } else { pos += _editor.get_paste_offset (pos, paste_count, len); @@ -718,14 +834,14 @@ AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float t double const model_pos = _line->time_converter().from (pos - _line->time_converter().origin_b ()); XMLNode &before = alist->get_state(); - alist->paste (**p, model_pos, DoubleBeatsFramesConverter (_session->tempo_map(), pos)); + alist->paste (**p, model_pos, DoubleBeatsSamplesConverter (_session->tempo_map(), pos)); _session->add_command (new MementoCommand(*alist.get(), &before, &alist->get_state())); return true; } void -AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list& results, bool /*within*/) +AutomationTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list& results, bool /*within*/) { if (!_line && !_view) { return; @@ -819,6 +935,19 @@ AutomationTimeAxisView::add_line (boost::shared_ptr line) line->add_visibility (AutomationLine::Line); } +bool +AutomationTimeAxisView::propagate_time_selection () const +{ + /* MIDI automation is part of the MIDI region. It is always + * implicily selected with the parent, regardless of TAV selection + */ + if (_parameter.type() >= MidiCCAutomation && + _parameter.type() <= MidiChannelPressureAutomation) { + return true; + } + return false; +} + void AutomationTimeAxisView::entered() { @@ -1009,7 +1138,7 @@ AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& sel XMLNode &before = alist->get_state(); /* convert time selection to automation list model coordinates */ - const Evoral::TimeConverter& tc = line.time_converter (); + const Evoral::TimeConverter& tc = line.time_converter (); double const start = tc.from (selection.time.front().start - tc.origin_b ()); double const end = tc.from (selection.time.front().end - tc.origin_b ()); @@ -1068,4 +1197,3 @@ AutomationTimeAxisView::color () const { return gdk_color_from_rgb (_stripable->presentation_info().color()); } -