diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2008-03-21 20:22:00 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2008-03-21 20:22:00 +0000 |
commit | fec2a96cec0f557cf30dde2f9bf21b76be36551d (patch) | |
tree | a1b446d153191d641f36cb30e178e3fc400be495 | |
parent | aa06f1f9f8be010d4abfc5b5c2fd61ab8e39fa58 (diff) |
fix dragging that involves locked regions; auto-rebinding patch for people to experiment with (probably needs a little work)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3164 d708f5d6-7413-0410-9779-e7cbd77b26cf
26 files changed, 397 insertions, 91 deletions
diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index ca3628f879..3f24ffb96b 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -364,6 +364,8 @@ <menuitem action='RemoteUserDefined'/> <menuitem action='RemoteMixerDefined'/> <menuitem action='RemoteEditorDefined'/> + <separator/> + <menuitem action='AutoRebinding'/> </menu> <menu action='Monitoring'> <menuitem action='UseHardwareMonitoring'/> diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index 1ef2e00b56..8eec76956c 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -37,6 +37,7 @@ #include "engine_dialog.h" #include "editor.h" #include "actions.h" +#include "mixer_ui.h" #ifdef GTKOSX #include <gtkmm2ext/sync-menu.h> @@ -573,6 +574,10 @@ ARDOUR_UI::install_actions () act = ActionManager::register_radio_action (option_actions, remote_group, X_("RemoteEditorDefined"), _("Remote ID follows order of Editor"), hide_return (bind (mem_fun (*this, &ARDOUR_UI::set_remote_model), EditorOrdered))); ActionManager::session_sensitive_actions.push_back (act); + act = ActionManager::register_toggle_action (option_actions, X_("AutoRebinding"), _("Auto Rebind Controls"), mem_fun (*(this->mixer), &Mixer_UI::toggle_auto_rebinding)); + ActionManager::session_sensitive_actions.push_back (act); + + ActionManager::add_action_group (shuttle_actions); ActionManager::add_action_group (option_actions); ActionManager::add_action_group (jack_actions); diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index a5b767c8cd..16c8e54bed 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -116,6 +116,28 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other) init (c, true); } +AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_ptr<AudioRegion> other_region) + : RegionView (other, boost::shared_ptr<Region> (other_region)) + , zero_line(0) + , fade_in_shape(0) + , fade_out_shape(0) + , fade_in_handle(0) + , fade_out_handle(0) + , gain_line(0) + , _amplitude_above_axis(1.0) + , _flags(0) + , fade_color(0) + +{ + Gdk::Color c; + int r,g,b,a; + + UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a); + c.set_rgb_p (r/255.0, g/255.0, b/255.0); + + init (c, true); +} + void AudioRegionView::init (Gdk::Color& basic_color, bool wfd) { diff --git a/gtk2_ardour/audio_region_view.h b/gtk2_ardour/audio_region_view.h index 63e7647591..99029d7afa 100644 --- a/gtk2_ardour/audio_region_view.h +++ b/gtk2_ardour/audio_region_view.h @@ -55,6 +55,7 @@ class AudioRegionView : public RegionView Gdk::Color& basic_color); AudioRegionView (const AudioRegionView& other); + AudioRegionView (const AudioRegionView& other, boost::shared_ptr<ARDOUR::AudioRegion>); ~AudioRegionView (); diff --git a/gtk2_ardour/draginfo.h b/gtk2_ardour/draginfo.h index d8e3eb74b4..bd6313d970 100644 --- a/gtk2_ardour/draginfo.h +++ b/gtk2_ardour/draginfo.h @@ -53,7 +53,8 @@ struct DragInfo { double last_pointer_y; void (Editor::*motion_callback)(ArdourCanvas::Item*, GdkEvent*); void (Editor::*finished_callback)(ArdourCanvas::Item*, GdkEvent*); - TimeAxisView* last_trackview; + TimeAxisView* source_trackview; + TimeAxisView* dest_trackview; bool x_constrained; bool y_constrained; bool copy; diff --git a/gtk2_ardour/editor_imageframe.cc b/gtk2_ardour/editor_imageframe.cc index 7634617f01..397af2d305 100644 --- a/gtk2_ardour/editor_imageframe.cc +++ b/gtk2_ardour/editor_imageframe.cc @@ -430,7 +430,8 @@ Editor::start_imageframe_grab(ArdourCanvas::Item* item, GdkEvent* event) drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback; drag_info.last_frame_position = ifv->get_position() ; - drag_info.last_trackview = &ifv->get_time_axis_view() ; + drag_info.source_trackview = &ifv->get_time_axis_view() ; + drag_info.dest_trackview = drag_info.source_trackview; /* this is subtle. raising the regionview itself won't help, because raise_to_top() just puts the item on the top of @@ -442,7 +443,7 @@ Editor::start_imageframe_grab(ArdourCanvas::Item* item, GdkEvent* event) */ drag_info.item->raise_to_top(); - drag_info.last_trackview->canvas_display->raise_to_top(); + drag_info.source_trackview->canvas_display->raise_to_top(); //time_line_group->raise_to_top(); cursor_group->raise_to_top (); @@ -463,7 +464,8 @@ Editor::start_markerview_grab(ArdourCanvas::Item* item, GdkEvent* event) drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback; drag_info.last_frame_position = mv->get_position() ; - drag_info.last_trackview = &mv->get_time_axis_view() ; + drag_info.source_trackview = &mv->get_time_axis_view() ; + drag_info.dest_trackview = drag_info.source_trackview; /* this is subtle. raising the regionview itself won't help, because raise_to_top() just puts the item on the top of @@ -475,7 +477,7 @@ Editor::start_markerview_grab(ArdourCanvas::Item* item, GdkEvent* event) */ drag_info.item->raise_to_top(); - drag_info.last_trackview->canvas_display->raise_to_top(); + drag_info.source_trackview->canvas_display->raise_to_top(); //time_line_group->raise_to_top(); cursor_group->raise_to_top (); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 035dd9d7af..5fe65d16dc 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -1846,7 +1846,8 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.copy = false; drag_info.motion_callback = 0; drag_info.finished_callback = 0; - drag_info.last_trackview = 0; + drag_info.dest_trackview = 0; + drag_info.source_trackview = 0; drag_info.last_frame_position = 0; drag_info.grab_frame = 0; drag_info.last_pointer_frame = 0; @@ -2944,7 +2945,8 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; @@ -2974,7 +2976,8 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event) speed = atv->get_diskstream()->speed(); } - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; // we want a move threshold @@ -3009,7 +3012,8 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event) drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; - drag_info.last_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.source_trackview = &clicked_regionview->get_time_axis_view(); + drag_info.dest_trackview = drag_info.source_trackview; // we want a move threshold drag_info.want_move_threshold = true; drag_info.brushing = true; @@ -3024,7 +3028,7 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event) drag_info.want_move_threshold = false; // don't copy again - /* duplicate the region(s) */ + /* duplicate the regionview(s) and region(s) */ vector<RegionView*> new_regionviews; @@ -3032,7 +3036,7 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event) RegionView* rv; RegionView* nrv; AudioRegionView* arv; - + rv = (*i); @@ -3040,8 +3044,12 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event) /* XXX handle MIDI here */ continue; } + + const boost::shared_ptr<const Region> original = arv->region(); + boost::shared_ptr<Region> region_copy = RegionFactory::create (original); + boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy); - nrv = new AudioRegionView (*arv); + nrv = new AudioRegionView (*arv, ar); nrv->get_canvas_group()->show (); new_regionviews.push_back (nrv); @@ -3178,7 +3186,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) return; } - original_pointer_order = drag_info.last_trackview->order; + original_pointer_order = drag_info.dest_trackview->order; /************************************************************ Y-Delta Computation @@ -3190,7 +3198,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) goto y_axis_done; } - if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) { + if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) { int32_t children = 0, numtracks = 0; // XXX hard coding track limit, oh my, so very very bad @@ -3234,16 +3242,16 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) /* find the actual span according to the canvas */ canvas_pointer_y_span = pointer_y_span; - if (drag_info.last_trackview->order >= tv->order) { + if (drag_info.dest_trackview->order >= tv->order) { int32_t y; - for (y = tv->order; y < drag_info.last_trackview->order; y++) { + for (y = tv->order; y < drag_info.dest_trackview->order; y++) { if (height_list[y] == 0 ) { canvas_pointer_y_span--; } } } else { int32_t y; - for (y = drag_info.last_trackview->order;y <= tv->order; y++) { + for (y = drag_info.dest_trackview->order;y <= tv->order; y++) { if ( height_list[y] == 0 ) { canvas_pointer_y_span++; } @@ -3255,6 +3263,10 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) double ix1, ix2, iy1, iy2; int32_t n = 0; + if (rv2->region()->locked()) { + continue; + } + rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); rv2->get_canvas_group()->i2w (ix1, iy1); TimeAxisView* tvp2 = trackview_by_y_position (iy1); @@ -3326,13 +3338,13 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) } } - } else if (drag_info.last_trackview == tv) { + } else if (drag_info.dest_trackview == tv) { clamp_y_axis = true; } y_axis_done: if (!clamp_y_axis) { - drag_info.last_trackview = tv; + drag_info.dest_trackview = tv; } /************************************************************ @@ -3465,6 +3477,10 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) double ix1, ix2, iy1, iy2; int32_t temp_pointer_y_span = pointer_y_span; + if (rv->region()->locked()) { + continue; + } + /* get item BBox, which will be relative to parent. so we have to query on a child, then convert to world coordinates using the parent. @@ -3502,6 +3518,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) y_delta -= (*j); temp_pointer_y_span--; } + while (temp_pointer_y_span < 0) { y_delta += (*j); if (x != original_pointer_order) { @@ -3532,7 +3549,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event) x++; } } - + + /* prevent the regionview from being moved to before the zero position on the canvas. */ @@ -3597,6 +3615,10 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event bool regionview_y_movement; bool regionview_x_movement; vector<RegionView*> copies; + RouteTimeAxisView* tvp1; + boost::shared_ptr<Diskstream> ds; + boost::shared_ptr<Playlist> from_playlist; + bool axis_motion; /* first_move is set to false if the regionview has been moved in the motion handler. @@ -3632,19 +3654,40 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event goto out; } + /* get the playlist where this drag started. we can't use rv->region()->playlist() + because we may have copied the region and it has not been attached to a playlist. + */ + + assert ((tvp1 = dynamic_cast<RouteTimeAxisView*> (drag_info.source_trackview))); + assert ((ds = tvp1->get_diskstream())); + assert ((from_playlist = ds->playlist())); + /* adjust for track speed */ speed = 1.0; - atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview); + atv = dynamic_cast<AudioTimeAxisView*> (drag_info.dest_trackview); + if (atv && atv->get_diskstream()) { speed = atv->get_diskstream()->speed(); } + + /* check all regions for motion because some might have been locked */ - regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed)); - regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view()); + regionview_x_movement = false; + regionview_y_movement = false; + + for (list<RegionView*>::const_iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + if (drag_info.last_frame_position != (nframes_t) ((*i)->region()->position()/speed)) { + regionview_x_movement = true; + } + + if (drag_info.dest_trackview != &(*i)->get_time_axis_view()) { + regionview_y_movement = true; + } + } //printf ("last_frame: %s position is %lu %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); - //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); + //printf ("last_rackview: %s \n", drag_info.dest_trackview->name().c_str()); char* op_string; @@ -3673,19 +3716,29 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) { RegionView* rv = (*i); + + if (rv->region()->locked()) { + ++i; + continue; + } double ix1, ix2, iy1, iy2; - rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2); rv->get_canvas_group()->i2w (ix1, iy1); TimeAxisView* tvp2 = trackview_by_y_position (iy1); AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2); - boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist(); boost::shared_ptr<Playlist> to_playlist = atv2->playlist(); where = (nframes_t) (unit_to_frame (ix1) * speed); - boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region())); + boost::shared_ptr<Region> new_region; + + if (drag_info.copy) { + /* we already made a copy */ + new_region = rv->region(); + } else { + new_region = RegionFactory::create (rv->region()); + } /* undo the previous hide_dependent_views so that xfades don't disappear on copying regions @@ -3797,31 +3850,21 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event where = rv->region()->position(); } - boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist(); - - assert (to_playlist); - /* add the undo */ - session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0)); - + session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0)); + if (drag_info.copy) { - boost::shared_ptr<Region> newregion; - boost::shared_ptr<Region> ar; + /* we already made a copy of the region */ - if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) { - newregion = RegionFactory::create (ar); - } else { - /* XXX MIDI HERE drobilla */ - continue; - } + boost::shared_ptr<Region> newregion = rv->region(); /* add it */ latest_regionviews.clear (); sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view)); - to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed())); + from_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed())); c.disconnect (); if (!latest_regionviews.empty()) { @@ -3844,7 +3887,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event /* add the redo */ - session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state())); + session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state())); if (drag_info.copy) { copies.push_back (rv); @@ -4109,7 +4152,8 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event) start_grab (event); - drag_info.last_trackview = clicked_trackview; + drag_info.source_trackview = clicked_trackview; + drag_info.dest_trackview = drag_info.source_trackview; drag_info.last_frame_position = latest_regionviews.front()->region()->position(); drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position; diff --git a/gtk2_ardour/gain_meter.h b/gtk2_ardour/gain_meter.h index ba6984088d..1673e8ca07 100644 --- a/gtk2_ardour/gain_meter.h +++ b/gtk2_ardour/gain_meter.h @@ -73,6 +73,7 @@ class GainMeter : public Gtk::VBox void set_meter_strip_name (const char * name); void set_fader_name (const char * name); + PBD::Controllable& get_controllable() { return _io->gain_control(); } void clear_meters (); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 3484fddd38..17871de7d5 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -223,6 +223,8 @@ Mixer_UI::Mixer_UI () signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler)); _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection)); + + auto_rebinding = FALSE; } Mixer_UI::~Mixer_UI () @@ -712,8 +714,97 @@ Mixer_UI::redisplay_track_list () Route::SyncOrderKeys (); // EMIT SIGNAL ignore_sync = false; } + + // Rebind all of the midi controls automatically + + if (auto_rebinding) + auto_rebind_midi_controls (); + +} + +void +Mixer_UI::set_auto_rebinding( bool val ) +{ + if( val == TRUE ) + { + auto_rebinding = TRUE; + Session::AutoBindingOff(); + } + else + { + auto_rebinding = FALSE; + Session::AutoBindingOn(); + } } +void +Mixer_UI::toggle_auto_rebinding() +{ + if (auto_rebinding) + { + set_auto_rebinding( FALSE ); + } + + else + { + set_auto_rebinding( TRUE ); + } + + auto_rebind_midi_controls(); +} + +void +Mixer_UI::auto_rebind_midi_controls () +{ + TreeModel::Children rows = track_model->children(); + TreeModel::Children::iterator i; + int pos; + + // Create bindings for all visible strips and remove those that are not visible + pos = 1; // 0 is reserved for the master strip + for (i = rows.begin(); i != rows.end(); ++i) { + MixerStrip* strip = (*i)[track_columns.strip]; + + if ( (*i)[track_columns.visible] == true ) { // add bindings for + // make the actual binding + //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl; + + int controlValue = pos; + if( strip->route()->master() ) { + controlValue = 0; + } + else { + pos++; + } + + PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable(), controlValue, 0); + PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable(), controlValue, 1); + + if( strip->is_audio_track() ) { + PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable(), controlValue, 2); + } + + PBD::Controllable::CreateBinding ( &(strip->gpm.get_controllable()), controlValue, 3); + PBD::Controllable::CreateBinding ( strip->panners.get_controllable(), controlValue, 4); + + } + else { // Remove any existing binding + PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable() ); + PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable() ); + + if( strip->is_audio_track() ) { + PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable() ); + } + + PBD::Controllable::DeleteBinding ( &(strip->gpm.get_controllable()) ); + PBD::Controllable::DeleteBinding ( strip->panners.get_controllable() ); // This only takes the first panner if there are multiples... + } + + } // for + +} + + struct SignalOrderRouteSorter { bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) { /* use of ">" forces the correct sort order */ @@ -1069,6 +1160,7 @@ Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::Tree if (name != group->name()) { group->set_name (name); } + } void diff --git a/gtk2_ardour/mixer_ui.h b/gtk2_ardour/mixer_ui.h index 14884bfeed..06a16519cc 100644 --- a/gtk2_ardour/mixer_ui.h +++ b/gtk2_ardour/mixer_ui.h @@ -75,6 +75,8 @@ class Mixer_UI : public Gtk::Window void hide_strip (MixerStrip *); void ensure_float (Gtk::Window&); + void toggle_auto_rebinding (); + void set_auto_rebinding(bool); RouteRedirectSelection& selection() { return _selection; } @@ -129,6 +131,9 @@ class Mixer_UI : public Gtk::Window void unselect_all_audiobus_strips (); void select_all_audiobus_strips (); + void auto_rebind_midi_controls (); + bool auto_rebinding; + void strip_select_op (bool audiotrack, bool select); void select_strip_op (MixerStrip*, bool select); diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index 4ad59fb1ea..fcbaf8d8bb 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -152,6 +152,12 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s) pan_automation_state_changed (); } +PBD::Controllable* +PannerUI::get_controllable() +{ + return pan_bars[0]->get_controllable(); +} + bool PannerUI::panning_link_button_press (GdkEventButton* ev) { diff --git a/gtk2_ardour/panner_ui.h b/gtk2_ardour/panner_ui.h index d587359ecd..093800387d 100644 --- a/gtk2_ardour/panner_ui.h +++ b/gtk2_ardour/panner_ui.h @@ -68,9 +68,11 @@ class PannerUI : public Gtk::HBox void effective_pan_display (); void set_meter_strip_name (string name); + PBD::Controllable* get_controllable(); private: friend class MixerStrip; + boost::shared_ptr<ARDOUR::IO> _io; ARDOUR::Session& _session; diff --git a/gtk2_ardour/region_view.cc b/gtk2_ardour/region_view.cc index 89e78b44b9..bb16637d6a 100644 --- a/gtk2_ardour/region_view.cc +++ b/gtk2_ardour/region_view.cc @@ -93,6 +93,23 @@ RegionView::RegionView (const RegionView& other) _height = other._height; } +RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other_region) + : TimeAxisViewItem (other) +{ + /* this is a pseudo-copy constructor used when dragging regions + around on the canvas. + */ + + /* derived concrete type will call init () */ + + _region = other_region; + editor = other.editor; + current_visible_sync_position = other.current_visible_sync_position; + valid = false; + _pixel_width = other._pixel_width; + _height = other._height; +} + RegionView::RegionView (ArdourCanvas::Group* parent, TimeAxisView& tv, boost::shared_ptr<ARDOUR::Region> r, diff --git a/gtk2_ardour/region_view.h b/gtk2_ardour/region_view.h index 4f160c5a58..bc5771e848 100644 --- a/gtk2_ardour/region_view.h +++ b/gtk2_ardour/region_view.h @@ -47,6 +47,7 @@ class RegionView : public TimeAxisViewItem Gdk::Color& basic_color); RegionView (const RegionView& other); + RegionView (const RegionView& other, boost::shared_ptr<ARDOUR::Region> other_region); ~RegionView (); diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index b944202c36..ada5afcd9c 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -42,6 +42,12 @@ class RegionFactory { static sigc::signal<void,boost::shared_ptr<Region> > CheckNewRegion; + static boost::shared_ptr<Region> create (boost::shared_ptr<const Region>); + + /* note: both of the first two should use const shared_ptr as well, but + gcc 4.1 doesn't seem to be able to disambiguate them if they do. + */ + static boost::shared_ptr<Region> create (boost::shared_ptr<Region>, nframes_t start, nframes_t length, std::string name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true); @@ -50,7 +56,6 @@ class RegionFactory { layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true); static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true); static boost::shared_ptr<Region> create (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true); - static boost::shared_ptr<Region> create (boost::shared_ptr<Region>); static boost::shared_ptr<Region> create (Session&, XMLNode&, bool); static boost::shared_ptr<Region> create (SourceList &, const XMLNode&); }; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 76590368f8..fa50212f3a 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -249,6 +249,9 @@ class Session : public PBD::StatefulDestructible bool deletion_in_progress() const { return _state_of_the_state & Deletion; } sigc::signal<void> DirtyChanged; + static sigc::signal<void> AutoBindingOn; + static sigc::signal<void> AutoBindingOff; + std::string sound_dir (bool with_path = true) const; std::string peak_dir () const; std::string dead_sound_dir () const; diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index 5cab802801..163cb07c6c 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -35,8 +35,8 @@ sigc::signal<void,boost::shared_ptr<Region> > RegionFactory::CheckNewRegion; boost::shared_ptr<Region> RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start, - nframes_t length, std::string name, - layer_t layer, Region::Flag flags, bool announce) + nframes_t length, std::string name, + layer_t layer, Region::Flag flags, bool announce) { boost::shared_ptr<const AudioRegion> other; @@ -57,11 +57,11 @@ RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start, } boost::shared_ptr<Region> -RegionFactory::create (boost::shared_ptr<Region> region) +RegionFactory::create (boost::shared_ptr<const Region> region) { - boost::shared_ptr<AudioRegion> other; + boost::shared_ptr<const AudioRegion> other; - if ((other = boost::dynamic_pointer_cast<AudioRegion>(region)) != 0) { + if ((other = boost::dynamic_pointer_cast<const AudioRegion>(region)) != 0) { boost::shared_ptr<Region> ret (new AudioRegion (other)); /* pure copy constructor - no CheckNewRegion emitted */ return ret; @@ -75,8 +75,8 @@ RegionFactory::create (boost::shared_ptr<Region> region) boost::shared_ptr<Region> RegionFactory::create (boost::shared_ptr<AudioRegion> region, nframes_t start, - nframes_t length, std::string name, - layer_t layer, Region::Flag flags, bool announce) + nframes_t length, std::string name, + layer_t layer, Region::Flag flags, bool announce) { return create (boost::static_pointer_cast<Region> (region), start, length, name, layer, flags, announce); } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index d53291b178..d7c3c95d9d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -113,6 +113,10 @@ sigc::signal<void> Session::SMPTEOffsetChanged; sigc::signal<void> Session::StartTimeChanged; sigc::signal<void> Session::EndTimeChanged; +sigc::signal<void> Session::AutoBindingOn; +sigc::signal<void> Session::AutoBindingOff; + + int Session::find_session (string str, string& path, string& snapshot, bool& isnew) { diff --git a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h index c91f4c8a06..d149ded527 100644 --- a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h +++ b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h @@ -59,6 +59,7 @@ class BarController : public Gtk::Frame /* export this to allow direct connection to button events */ Gtk::Widget& event_widget() { return darea; } + PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); } protected: Gtk::Adjustment& adjustment; diff --git a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h index 2ddd3628fc..1cde32c5ba 100644 --- a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h +++ b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h @@ -47,7 +47,8 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton return true; } } - + + PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); } private: BindingProxy binding_proxy; }; @@ -71,6 +72,8 @@ class BindableButton : public Gtkmm2ext::StatefulButton } } + PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); } + private: BindingProxy binding_proxy; }; diff --git a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h index d8f37c7649..dd9b94319d 100644 --- a/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h +++ b/libs/gtkmm2ext/gtkmm2ext/binding_proxy.h @@ -40,6 +40,7 @@ class BindingProxy : public sigc::trackable bool button_press_handler (GdkEventButton *); + PBD::Controllable* get_controllable() { return &controllable; } protected: Gtkmm2ext::PopUp* prompter; diff --git a/libs/pbd/controllable.cc b/libs/pbd/controllable.cc index bf0f7f5c6d..6a0028668b 100644 --- a/libs/pbd/controllable.cc +++ b/libs/pbd/controllable.cc @@ -28,6 +28,8 @@ using namespace PBD; sigc::signal<void,Controllable*> Controllable::Destroyed; sigc::signal<bool,Controllable*> Controllable::StartLearning; sigc::signal<void,Controllable*> Controllable::StopLearning; +sigc::signal<void,Controllable*,int,int> Controllable::CreateBinding; +sigc::signal<void,Controllable*> Controllable::DeleteBinding; Glib::Mutex* Controllable::registry_lock = 0; Controllable::Controllables Controllable::registry; diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index 85f09726ca..17ee8f1a6f 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -44,6 +44,8 @@ class Controllable : public PBD::StatefulDestructible { virtual bool can_send_feedback() const { return true; } sigc::signal<void> LearningFinished; + static sigc::signal<void,PBD::Controllable*,int,int> CreateBinding; + static sigc::signal<void,PBD::Controllable*> DeleteBinding; static sigc::signal<bool,PBD::Controllable*> StartLearning; static sigc::signal<void,PBD::Controllable*> StopLearning; diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 720711d94b..90b6b4bda9 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -56,9 +56,17 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) _feedback_interval = 10000; // microseconds last_feedback_time = 0; + auto_binding = FALSE; + Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning)); Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning)); Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback)); + + Controllable::CreateBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::create_binding)); + Controllable::DeleteBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::delete_binding)); + + Session::AutoBindingOn.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_on)); + Session::AutoBindingOff.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_off)); } GenericMidiControlProtocol::~GenericMidiControlProtocol () @@ -225,6 +233,71 @@ GenericMidiControlProtocol::stop_learning (Controllable* c) } } +void +GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control ) +{ + if( control != 0 ) { + Glib::Mutex::Lock lm2 (controllables_lock); + + for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) { + MIDIControllable* existingBinding = (*iter); + + if( control == &(existingBinding->get_controllable()) ) { + delete existingBinding; + controllables.erase (iter); + } + + } // end for midi controllables + } // end null check +} +void +GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number) +{ + if( control != NULL ) { + Glib::Mutex::Lock lm2 (controllables_lock); + + MIDI::channel_t channel = (pos & 0xf); + MIDI::byte value = control_number; + + // Create a MIDIControllable:: + MIDIControllable* mc = new MIDIControllable (*_port, *control); + + // Remove any old binding for this midi channel/type/value pair + // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information + for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) { + MIDIControllable* existingBinding = (*iter); + + if( (existingBinding->get_control_channel() & 0xf ) == channel && + existingBinding->get_control_additional() == value && + (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) { + + delete existingBinding; + controllables.erase (iter); + } + + } // end for midi controllables + + + // Update the MIDI Controllable based on the the pos param + // Here is where a table lookup for user mappings could go; for now we'll just wing it... + mc->bind_midi( channel, MIDI::controller, value ); + + controllables.insert (mc); + } // end null test +} + +void +GenericMidiControlProtocol::auto_binding_on() +{ + auto_binding = TRUE; +} + +void +GenericMidiControlProtocol::auto_binding_off() +{ + auto_binding = FALSE; +} + XMLNode& GenericMidiControlProtocol::get_state () { @@ -269,46 +342,47 @@ GenericMidiControlProtocol::set_state (const XMLNode& node) _feedback_interval = 10000; } - Controllable* c; - - { - Glib::Mutex::Lock lm (pending_lock); - pending_controllables.clear (); - } - - Glib::Mutex::Lock lm2 (controllables_lock); - - controllables.clear (); - - nlist = node.children(); // "controls" - - if (nlist.empty()) { - return 0; - } - - nlist = nlist.front()->children (); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - if ((prop = (*niter)->property ("id")) != 0) { - - ID id = prop->value (); - - c = Controllable::by_id (id); + // Are we using the autobinding feature? If so skip this part + if ( !auto_binding ) { + + Controllable* c; + + { + Glib::Mutex::Lock lm (pending_lock); + pending_controllables.clear (); + } + + Glib::Mutex::Lock lm2 (controllables_lock); + controllables.clear (); + nlist = node.children(); // "controls" + + if (nlist.empty()) { + return 0; + } + + nlist = nlist.front()->children (); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if (c) { - MIDIControllable* mc = new MIDIControllable (*_port, *c); - if (mc->set_state (**niter) == 0) { - controllables.insert (mc); - } + if ((prop = (*niter)->property ("id")) != 0) { + + ID id = prop->value (); + c = session->controllable_by_id (id); - } else { - warning << string_compose (_("Generic MIDI control: controllable %1 not found (ignored)"), id) - << endmsg; + if (c) { + MIDIControllable* mc = new MIDIControllable (*_port, *c); + if (mc->set_state (**niter) == 0) { + controllables.insert (mc); + } + + } else { + warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"), + id) + << endmsg; + } } } - } - + } // end autobinding check return 0; } diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.h b/libs/surfaces/generic_midi/generic_midi_control_protocol.h index 95aeb77cdb..dfff8810d1 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.h +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.h @@ -44,6 +44,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol { ARDOUR::microseconds_t last_feedback_time; bool do_feedback; + bool auto_binding; void _send_feedback (); void send_feedback (); @@ -59,6 +60,13 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol { void stop_learning (PBD::Controllable*); void learning_stopped (MIDIControllable*); + + void create_binding (PBD::Controllable*, int, int); + void delete_binding (PBD::Controllable*); + + void auto_binding_on(); + void auto_binding_off(); + }; #endif /* ardour_generic_midi_control_protocol_h */ diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h index 4bac325feb..df6eea396c 100644 --- a/libs/surfaces/generic_midi/midicontrollable.h +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -63,6 +63,10 @@ class MIDIControllable : public Stateful XMLNode& get_state (void); int set_state (const XMLNode&); + void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte); + MIDI::channel_t get_control_channel () { return control_channel; } + MIDI::eventType get_control_type () { return control_type; } + MIDI::byte get_control_additional () { return control_additional; } private: PBD::Controllable& controllable; MIDI::Port& _port; @@ -86,8 +90,6 @@ class MIDIControllable : public Stateful void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *); void midi_sense_program_change (MIDI::Parser &, MIDI::byte); void midi_sense_pitchbend (MIDI::Parser &, MIDI::pitchbend_t); - - void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte); }; #endif // __gm_midicontrollable_h__ |