From 3bb7d368a820cc75cbec900b117f85fa411609c9 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 2 Aug 2012 00:04:51 +0000 Subject: [PATCH] merge 12791:13034 from 3.0 git-svn-id: svn://localhost/ardour2/branches/3.0-SG@13105 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour_ui.cc | 38 +- gtk2_ardour/ardour_ui.h | 1 + gtk2_ardour/audio_clock.cc | 23 +- gtk2_ardour/audio_region_view.cc | 75 ++- gtk2_ardour/audio_region_view.h | 20 +- gtk2_ardour/audio_streamview.cc | 17 +- gtk2_ardour/audio_streamview.h | 2 +- gtk2_ardour/automation_line.cc | 74 ++- gtk2_ardour/automation_line.h | 6 +- gtk2_ardour/canvas-waveview.c | 561 +++++++++++++++++- gtk2_ardour/canvas-waveview.h | 2 + gtk2_ardour/editor.cc | 13 +- gtk2_ardour/editor.h | 2 +- gtk2_ardour/editor_actions.cc | 8 +- gtk2_ardour/editor_drag.cc | 37 +- gtk2_ardour/editor_drag.h | 6 +- gtk2_ardour/editor_mouse.cc | 1 - gtk2_ardour/editor_routes.cc | 70 +-- gtk2_ardour/editor_routes.h | 1 - gtk2_ardour/engine_dialog.cc | 1 + gtk2_ardour/mixer_strip.cc | 4 + gtk2_ardour/mixer_ui.cc | 36 +- gtk2_ardour/shuttle_control.cc | 31 +- gtk2_ardour/splash.cc | 53 +- gtk2_ardour/splash.h | 4 + gtk2_ardour/startup.cc | 5 +- gtk2_ardour/theme_manager.cc | 17 + gtk2_ardour/theme_manager.h | 2 + gtk2_ardour/ui_config_vars.h | 1 + libs/appleutility/CAAUParameter.cpp | 2 +- libs/ardour/ardour/amp.h | 1 + libs/ardour/ardour/plugin.h | 3 + libs/ardour/ardour/plugin_insert.h | 7 +- libs/ardour/ardour/route.h | 3 +- libs/ardour/ardour/session.h | 1 + libs/ardour/audio_track.cc | 2 +- libs/ardour/audio_unit.cc | 62 +- libs/ardour/buffer_manager.cc | 1 + libs/ardour/globals.cc | 6 +- libs/ardour/midi_ring_buffer.cc | 2 +- libs/ardour/midi_track.cc | 7 + libs/ardour/plugin_insert.cc | 63 +- libs/ardour/route.cc | 152 +++-- libs/ardour/session.cc | 63 +- libs/ardour/session_state.cc | 27 +- libs/ardour/session_transport.cc | 8 + libs/ardour/track.cc | 17 +- .../audiographer/process_context.h | 12 +- libs/audiographer/private/sndfile.hh | 5 - libs/evoral/evoral/ControlList.hpp | 4 +- libs/evoral/src/ControlList.cpp | 91 ++- .../generic_midi_control_protocol.cc | 12 +- .../surfaces/generic_midi/midicontrollable.cc | 25 +- libs/surfaces/generic_midi/midicontrollable.h | 3 +- midi_maps/Korg_nanoKONTROL.map | 12 +- midi_maps/Korg_nanoKONTROL2.map | 83 +++ midi_maps/Korg_nanoKONTROL2_With_Master.map | 90 +++ 57 files changed, 1595 insertions(+), 280 deletions(-) create mode 100644 midi_maps/Korg_nanoKONTROL2.map create mode 100644 midi_maps/Korg_nanoKONTROL2_With_Master.map diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 96e309ed01..c5d2bf5c11 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -794,6 +794,8 @@ If you still wish to quit, please use the\n\n\ */ save_ardour_state (); + loading_message (_("Please wait while Ardour cleans up...")); + if (_session) { // _session->set_deletion_in_progress (); _session->set_clean (); @@ -2394,20 +2396,6 @@ ARDOUR_UI::idle_load (const std::string& path) } } -void -ARDOUR_UI::loading_message (const std::string& msg) -{ - if (ARDOUR_COMMAND_LINE::no_splash) { - return; - } - - show_splash (); - if (splash) { - splash->message (msg); - flush_pending (); - } -} - /** @param quit_on_cancel true if exit() should be called if the user clicks `cancel' in the new session dialog */ int ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, string load_template) @@ -2812,7 +2800,7 @@ ARDOUR_UI::launch_manual () void ARDOUR_UI::launch_reference () { - PBD::open_uri("http://ardour.org/refmanual"); + PBD::open_uri ("http://ardour.org/refmanual"); } void @@ -2830,6 +2818,20 @@ ARDOUR_UI::about_signal_response (int /*response*/) hide_about(); } +void +ARDOUR_UI::loading_message (const std::string& msg) +{ + if (ARDOUR_COMMAND_LINE::no_splash) { + return; + } + + if (!splash) { + show_splash (); + } + + splash->message (msg); +} + void ARDOUR_UI::show_splash () { @@ -2841,11 +2843,7 @@ ARDOUR_UI::show_splash () } } - splash->present (); - splash->pop_front (); - splash->queue_draw (); - splash->get_window()->process_updates (true); - flush_pending (); + splash->display (); } void diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index c48d373ca3..92e7295514 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -611,6 +611,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr About* about; Splash* splash; + void pop_back_splash (Gtk::Window&); /* cleanup */ diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index dbe7f03a14..fd8aca8df1 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -1455,15 +1455,6 @@ AudioClock::on_button_press_event (GdkEventButton *ev) switch (ev->button) { case 1: if (editable && !_off) { - dragging = true; - /* make absolutely sure that the pointer is grabbed */ - gdk_pointer_grab(ev->window,false , - GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), - NULL,NULL,ev->time); - drag_accum = 0; - drag_start_y = ev->y; - drag_y = ev->y; - int index; int trailing; int y; @@ -1476,10 +1467,16 @@ AudioClock::on_button_press_event (GdkEventButton *ev) y = ev->y - ((upper_height - layout_height)/2); x = ev->x - layout_x_offset; - if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { drag_field = index_to_field (index); - } else { - drag_field = Field (0); + dragging = true; + /* make absolutely sure that the pointer is grabbed */ + gdk_pointer_grab(ev->window,false , + GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), + NULL,NULL,ev->time); + drag_accum = 0; + drag_start_y = ev->y; + drag_y = ev->y; } } break; @@ -1660,7 +1657,7 @@ AudioClock::on_motion_notify_event (GdkEventMotion *ev) int dir; dir = (drag_accum < 0 ? 1:-1); pos = current_time(); - frames = get_frame_step (drag_field,pos,dir); + frames = get_frame_step (drag_field, pos, dir); if (frames != 0 && frames * drag_accum < current_time()) { set ((framepos_t) floor (pos - drag_accum * frames), false); // minus because up is negative in GTK diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index c3fc279991..83015c9f63 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -66,6 +66,7 @@ using namespace Editing; using namespace ArdourCanvas; static const int32_t sync_mark_width = 9; +static double const handle_size = 6; /* height of fade handles */ AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr r, double spu, Gdk::Color const & basic_color) @@ -79,9 +80,11 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView , start_xfade_in (0) , start_xfade_out (0) , start_xfade_rect (0) + , _start_xfade_visible (false) , end_xfade_in (0) , end_xfade_out (0) , end_xfade_rect (0) + , _end_xfade_visible (false) , _amplitude_above_axis(1.0) , fade_color(0) { @@ -100,9 +103,11 @@ AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView , start_xfade_in (0) , start_xfade_out (0) , start_xfade_rect (0) + , _start_xfade_visible (false) , end_xfade_in (0) , end_xfade_out (0) , end_xfade_rect (0) + , _end_xfade_visible (false) , _amplitude_above_axis(1.0) , fade_color(0) { @@ -119,9 +124,11 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_pt , start_xfade_in (0) , start_xfade_out (0) , start_xfade_rect (0) + , _start_xfade_visible (false) , end_xfade_in (0) , end_xfade_out (0) , end_xfade_rect (0) + , _end_xfade_visible (false) , _amplitude_above_axis (other._amplitude_above_axis) , fade_color(0) { @@ -161,13 +168,13 @@ AudioRegionView::init (Gdk::Color const & basic_color, bool wfd) if (!_recregion) { fade_in_handle = new ArdourCanvas::SimpleRect (*group); fade_in_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fill_color, 0); - fade_in_handle->property_outline_pixels() = 0; + fade_in_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 0); fade_in_handle->set_data ("regionview", this); fade_out_handle = new ArdourCanvas::SimpleRect (*group); fade_out_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fill_color, 0); - fade_out_handle->property_outline_pixels() = 0; + fade_out_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 0); fade_out_handle->set_data ("regionview", this); @@ -448,17 +455,15 @@ AudioRegionView::setup_fade_handle_positions() { /* position of fade handle offset from the top of the region view */ double const handle_pos = 2; - /* height of fade handles */ - double const handle_height = 5; if (fade_in_handle) { fade_in_handle->property_y1() = handle_pos; - fade_in_handle->property_y2() = handle_pos + handle_height; + fade_in_handle->property_y2() = handle_pos + handle_size; } if (fade_out_handle) { fade_out_handle->property_y1() = handle_pos; - fade_out_handle->property_y2() = handle_pos + handle_height; + fade_out_handle->property_y2() = handle_pos + handle_size; } } @@ -559,6 +564,7 @@ AudioRegionView::reset_fade_in_shape_width (framecnt_t width) start_xfade_in->hide (); start_xfade_out->hide (); start_xfade_rect->hide (); + _start_xfade_visible = false; } } @@ -589,7 +595,7 @@ AudioRegionView::reset_fade_in_shape_width (framecnt_t width) /* Put the fade in handle so that its left side is at the end-of-fade line */ fade_in_handle->property_x1() = handle_center; - fade_in_handle->property_x2() = handle_center + 6; + fade_in_handle->property_x2() = handle_center + handle_size; if (pwidth < 5) { fade_in_shape->hide(); @@ -668,6 +674,7 @@ AudioRegionView::reset_fade_out_shape_width (framecnt_t width) end_xfade_in->hide (); end_xfade_out->hide (); end_xfade_rect->hide (); + _end_xfade_visible = false; } } @@ -1140,7 +1147,9 @@ AudioRegionView::entered (bool internal_editing) } if (fade_in_handle && !internal_editing) { + fade_in_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 255); fade_in_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 255); + fade_out_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 255); fade_out_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 255); } } @@ -1156,7 +1165,9 @@ AudioRegionView::exited () } if (fade_in_handle) { + fade_in_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 0); fade_in_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 0); + fade_out_handle->property_outline_color_rgba() = RGBA_TO_UINT (0, 0, 0, 0); fade_out_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 0); } } @@ -1455,6 +1466,7 @@ AudioRegionView::redraw_start_xfade () start_xfade_in->hide (); start_xfade_out->hide (); start_xfade_rect->hide (); + _start_xfade_visible = false; } return; } @@ -1544,6 +1556,8 @@ AudioRegionView::redraw_start_xfade_to (boost::shared_ptr ar, frame start_xfade_out->show (); start_xfade_out->raise_to_top (); + _start_xfade_visible = true; + delete points; } @@ -1561,6 +1575,7 @@ AudioRegionView::redraw_end_xfade () end_xfade_in->hide (); end_xfade_out->hide (); end_xfade_rect->hide (); + _end_xfade_visible = false; } return; } @@ -1652,12 +1667,20 @@ AudioRegionView::redraw_end_xfade_to (boost::shared_ptr ar, framecn end_xfade_out->show (); end_xfade_out->raise_to_top (); + _end_xfade_visible = true; delete points; } void AudioRegionView::hide_xfades () +{ + hide_start_xfade (); + hide_end_xfade (); +} + +void +AudioRegionView::hide_start_xfade () { if (start_xfade_in) { start_xfade_in->hide(); @@ -1668,6 +1691,13 @@ AudioRegionView::hide_xfades () if (start_xfade_rect) { start_xfade_rect->hide (); } + + _start_xfade_visible = false; +} + +void +AudioRegionView::hide_end_xfade () +{ if (end_xfade_in) { end_xfade_in->hide(); } @@ -1677,10 +1707,12 @@ AudioRegionView::hide_xfades () if (end_xfade_rect) { end_xfade_rect->hide (); } + + _end_xfade_visible = false; } void -AudioRegionView::show_xfades () +AudioRegionView::show_start_xfade () { if (start_xfade_in) { start_xfade_in->show(); @@ -1691,6 +1723,13 @@ AudioRegionView::show_xfades () if (start_xfade_rect) { start_xfade_rect->show (); } + + _start_xfade_visible = true; +} + +void +AudioRegionView::show_end_xfade () +{ if (end_xfade_in) { end_xfade_in->show(); } @@ -1700,6 +1739,15 @@ AudioRegionView::show_xfades () if (end_xfade_rect) { end_xfade_rect->show (); } + + _end_xfade_visible = true; +} + +void +AudioRegionView::show_xfades () +{ + show_start_xfade (); + show_end_xfade (); } void @@ -1722,11 +1770,16 @@ AudioRegionView::drag_end () { TimeAxisViewItem::drag_end (); - for (list::iterator i = _hidden_xfades.begin(); i != _hidden_xfades.end(); ++i) { - (*i)->show_xfades (); + for (list::iterator i = _hidden_xfades.first.begin(); i != _hidden_xfades.first.end(); ++i) { + (*i)->show_start_xfade (); } - _hidden_xfades.clear (); + for (list::iterator i = _hidden_xfades.second.begin(); i != _hidden_xfades.second.end(); ++i) { + (*i)->show_end_xfade (); + } + + _hidden_xfades.first.clear (); + _hidden_xfades.second.clear (); } void diff --git a/gtk2_ardour/audio_region_view.h b/gtk2_ardour/audio_region_view.h index ee03fdbc80..5e3240670d 100644 --- a/gtk2_ardour/audio_region_view.h +++ b/gtk2_ardour/audio_region_view.h @@ -119,7 +119,19 @@ class AudioRegionView : public RegionView void redraw_end_xfade (); void hide_xfades (); + void hide_start_xfade (); + void hide_end_xfade (); void show_xfades (); + void show_start_xfade (); + void show_end_xfade (); + + bool start_xfade_visible () const { + return _start_xfade_visible; + } + + bool end_xfade_visible () const { + return _end_xfade_visible; + } protected: @@ -149,10 +161,12 @@ class AudioRegionView : public RegionView ArdourCanvas::Line *start_xfade_in; ArdourCanvas::Line *start_xfade_out; ArdourCanvas::SimpleRect* start_xfade_rect; + bool _start_xfade_visible; ArdourCanvas::Line *end_xfade_in; ArdourCanvas::Line *end_xfade_out; ArdourCanvas::SimpleRect* end_xfade_rect; + bool _end_xfade_visible; boost::shared_ptr gain_line; @@ -203,8 +217,10 @@ private: */ std::vector _data_ready_connections; - /** RegionViews that we hid the xfades for at the start of the current drag */ - std::list _hidden_xfades; + /** RegionViews that we hid the xfades for at the start of the current drag; + * first list is for start xfades, second list is for end xfades. + */ + std::pair, std::list > _hidden_xfades; }; #endif /* __gtk_ardour_audio_region_view_h__ */ diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index 80ed198ab1..6d24dff2ca 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -493,12 +493,14 @@ AudioStreamView::hide_all_fades () } /** Hide xfades for regions that overlap ar. - * @return AudioRegionViews that xfades were hidden for. + * @return Pair of lists; first is the AudioRegionViews that start xfades were hidden for, + * second is the AudioRegionViews that end xfades were hidden for. */ -list +pair, list > AudioStreamView::hide_xfades_with (boost::shared_ptr ar) { - list hidden; + list start_hidden; + list end_hidden; for (list::iterator i = region_views.begin(); i != region_views.end(); ++i) { AudioRegionView* const arv = dynamic_cast(*i); @@ -507,14 +509,19 @@ AudioStreamView::hide_xfades_with (boost::shared_ptr ar) case Evoral::OverlapNone: break; default: + if (arv->start_xfade_visible ()) { + start_hidden.push_back (arv); + } + if (arv->end_xfade_visible ()) { + end_hidden.push_back (arv); + } arv->hide_xfades (); - hidden.push_back (arv); break; } } } - return hidden; + return make_pair (start_hidden, end_hidden); } void diff --git a/gtk2_ardour/audio_streamview.h b/gtk2_ardour/audio_streamview.h index a36c57b552..a3f7c63559 100644 --- a/gtk2_ardour/audio_streamview.h +++ b/gtk2_ardour/audio_streamview.h @@ -59,7 +59,7 @@ class AudioStreamView : public StreamView void show_all_fades (); void hide_all_fades (); - std::list hide_xfades_with (boost::shared_ptr ar); + std::pair, std::list > hide_xfades_with (boost::shared_ptr ar); RegionView* create_region_view (boost::shared_ptr, bool, bool); diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index f5d2669d0a..4a21e7905a 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -266,7 +266,7 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y) } alist->freeze (); - sync_model_with_view_point (cp, 0); + sync_model_with_view_point (cp); alist->thaw (); update_pending = false; @@ -289,12 +289,12 @@ AutomationLine::reset_line_coords (ControlPoint& cp) } void -AutomationLine::sync_model_with_view_points (list cp, int64_t distance) +AutomationLine::sync_model_with_view_points (list cp) { update_pending = true; for (list::iterator i = cp.begin(); i != cp.end(); ++i) { - sync_model_with_view_point (**i, distance); + sync_model_with_view_point (**i); } } @@ -309,6 +309,31 @@ AutomationLine::get_verbose_cursor_string (double fraction) const return s; } +string +AutomationLine::get_verbose_cursor_relative_string (double original, double fraction) const +{ + std::string s = fraction_to_string (fraction); + if (_uses_gain_mapping) { + s += " dB"; + } + + std::string d = fraction_to_relative_string (original, fraction); + + if (!d.empty()) { + + s += " (\u0394"; + s += d; + + if (_uses_gain_mapping) { + s += " dB"; + } + + s += ')'; + } + + return s; +} + /** * @param fraction y fraction * @return string representation of this value, using dB if appropriate. @@ -336,6 +361,45 @@ AutomationLine::fraction_to_string (double fraction) const return buf; } +/** + * @param original an old y-axis fraction + * @param fraction the new y fraction + * @return string representation of the difference between original and fraction, using dB if appropriate. + */ +string +AutomationLine::fraction_to_relative_string (double original, double fraction) const +{ + char buf[32]; + + if (original == fraction) { + return "0"; + } + + if (_uses_gain_mapping) { + if (original == 0.0) { + /* there is no sensible representation of a relative + change from -inf dB, so return an empty string. + */ + return ""; + } else if (fraction == 0.0) { + snprintf (buf, sizeof (buf), "-inf"); + } else { + double old_db = accurate_coefficient_to_dB (slider_position_to_gain_with_max (original, Config->get_max_gain())); + double new_db = accurate_coefficient_to_dB (slider_position_to_gain_with_max (fraction, Config->get_max_gain())); + snprintf (buf, sizeof (buf), "%.1f", new_db - old_db); + } + } else { + view_to_model_coord_y (original); + view_to_model_coord_y (fraction); + if (EventTypeMap::instance().is_integer (alist->parameter())) { + snprintf (buf, sizeof (buf), "%d", (int)fraction - (int)original); + } else { + snprintf (buf, sizeof (buf), "%.2f", fraction - original); + } + } + + return buf; +} /** * @param s Value string in the form as returned by fraction_to_string. @@ -586,7 +650,7 @@ AutomationLine::end_drag () points.sort (ControlPointSorter ()); } - sync_model_with_view_points (points, trackview.editor().unit_to_frame (_drag_distance)); + sync_model_with_view_points (points); alist->thaw (); @@ -601,7 +665,7 @@ AutomationLine::end_drag () } void -AutomationLine::sync_model_with_view_point (ControlPoint& cp, framecnt_t distance) +AutomationLine::sync_model_with_view_point (ControlPoint& cp) { /* find out where the visual control point is. initial results are in canvas units. ask the diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index b758dfb557..6be7ccd6b8 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -113,7 +113,9 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible ArdourCanvas::Item& grab_item() const { return *line; } 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; double string_to_fraction (std::string const &) const; void view_to_model_coord (double& x, double& y) const; void view_to_model_coord_y (double &) const; @@ -179,8 +181,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible ArdourCanvas::Points line_points; /* coordinates for canvas line */ std::vector control_points; /* visible control points */ - void sync_model_with_view_point (ControlPoint&, ARDOUR::framecnt_t); - void sync_model_with_view_points (std::list, ARDOUR::framecnt_t); + void sync_model_with_view_point (ControlPoint&); + void sync_model_with_view_points (std::list); void start_drag_common (double, float); virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); diff --git a/gtk2_ardour/canvas-waveview.c b/gtk2_ardour/canvas-waveview.c index 4d0c63ba3a..108952454a 100644 --- a/gtk2_ardour/canvas-waveview.c +++ b/gtk2_ardour/canvas-waveview.c @@ -121,6 +121,9 @@ static guint32 gnome_canvas_waveview_ensure_cache (GnomeCanvasWaveView *waveview gulong start_sample, gulong end_sample); + +static int _gradient_rendering = 0; + static GnomeCanvasItemClass *parent_class; GType @@ -328,6 +331,12 @@ gnome_canvas_waveview_class_init (GnomeCanvasWaveViewClass *class) item_class->draw = gnome_canvas_waveview_draw; } +void +gnome_canvas_waveview_set_gradient_waveforms (int yn) +{ + _gradient_rendering = yn; +} + GnomeCanvasWaveViewCache* gnome_canvas_waveview_cache_new () { @@ -1094,8 +1103,8 @@ gnome_canvas_waveview_update (GnomeCanvasItem *item, double *affine, ArtSVP *cli } static void -gnome_canvas_waveview_render (GnomeCanvasItem *item, - GnomeCanvasBuf *buf) +gnome_canvas_waveview_gradient_render (GnomeCanvasItem *item, + GnomeCanvasBuf *buf) { GnomeCanvasWaveView *waveview; gulong s1, s2; @@ -1349,9 +1358,10 @@ gnome_canvas_waveview_render (GnomeCanvasItem *item, if(pymax == fill_max) { PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax); ++fill_max; + } else { + PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top); } - else { - PAINT_VERTA_GR(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max, wave_middle, wave_top); } + } if((prev_pymin > pymin && next_pymin > pymin) || @@ -1640,6 +1650,549 @@ gnome_canvas_waveview_render (GnomeCanvasItem *item, } +static void +gnome_canvas_waveview_flat_render (GnomeCanvasItem *item, + GnomeCanvasBuf *buf) +{ + GnomeCanvasWaveView *waveview; + gulong s1, s2; + int clip_length = 0; + int pymin, pymax; + guint cache_index; + double half_height; + int x; + char rectify; + + waveview = GNOME_CANVAS_WAVEVIEW (item); + +// check_cache (waveview, "start of render"); + + if (parent_class->render) { + (*parent_class->render) (item, buf); + } + + if (buf->is_bg) { + gnome_canvas_buf_ensure_buf (buf); + buf->is_bg = FALSE; + } + + /* a "unit" means a pixel */ + + /* begin: render start x (units) */ + int const begin = MAX (waveview->bbox_ulx, buf->rect.x0); + + /* zbegin: start x for zero line (units) */ + int const zbegin = (begin == waveview->bbox_ulx) ? (begin + 1) : begin; + + /* end: render end x (units) */ + int const end = (waveview->bbox_lrx >= 0) ? MIN (waveview->bbox_lrx,buf->rect.x1) : buf->rect.x1; + + /* zend: end x for zero-line (units) */ + int const zend = (end == waveview->bbox_lrx) ? (end - 1) : end; + + if (begin == end) { + return; + } + + /* s1: start sample + s2: end sample + */ + + s1 = floor ((begin - waveview->bbox_ulx) * waveview->samples_per_unit); + + // fprintf (stderr, "0x%x begins at sample %f\n", waveview, waveview->bbox_ulx * waveview->samples_per_unit); + + if (end == waveview->bbox_lrx) { + /* This avoids minor rounding errors when we have the + entire region visible. + */ + s2 = waveview->samples; + } else { + s2 = s1 + floor ((end - begin) * waveview->samples_per_unit); + } + +#if 0 + printf ("0x%x r (%d..%d)(%d..%d) bbox (%d..%d)(%d..%d)" + " b/e %d..%d s= %lu..%lu @ %f\n", + waveview, + buf->rect.x0, + buf->rect.x1, + buf->rect.y0, + buf->rect.y1, + waveview->bbox_ulx, + waveview->bbox_lrx, + waveview->bbox_uly, + waveview->bbox_lry, + begin, end, s1, s2, + waveview->samples_per_unit); +#endif + + /* now ensure that the cache is full and properly + positioned. + */ + +// check_cache (waveview, "pre-ensure"); + + if (waveview->cache_updater && waveview->reload_cache_in_render) { + waveview->cache->start = 0; + waveview->cache->end = 0; + waveview->reload_cache_in_render = FALSE; + } + +// check_cache (waveview, "post-ensure"); + + /* don't rectify at single-sample zoom */ + if (waveview->rectified && waveview->samples_per_unit > 1) { + rectify = TRUE; + } + else { + rectify = FALSE; + } + + clip_length = MIN(5,(waveview->height/4)); + + /* + Now draw each line, clipping it appropriately. The clipping + is done by the macros PAINT_FOO(). + */ + + half_height = waveview->half_height; + +/* this makes it slightly easier to comprehend whats going on */ +#define origin half_height + + if (waveview->filled && !rectify) { + int prev_pymin = 1; + int prev_pymax = 0; + int last_pymin = 1; + int last_pymax = 0; + int next_pymin, next_pymax; + double max, min; + int next_clip_max = 0; + int next_clip_min = 0; + + if (s1 < waveview->samples_per_unit) { + /* we haven't got a prev vars to compare with, so outline the whole line here */ + prev_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit); + prev_pymin = prev_pymax; + } + else { + s1 -= waveview->samples_per_unit; + } + + if(end == waveview->bbox_lrx) { + /* we don't have the NEXT vars for the last sample */ + last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit); + last_pymin = last_pymax; + } + else { + s2 += waveview->samples_per_unit; + } + + cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2); + + /* + * Compute the variables outside the rendering rect + */ + if(prev_pymax != prev_pymin) { + + prev_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[cache_index].max, 1.0) * half_height) * item->canvas->pixels_per_unit); + prev_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[cache_index].min, -1.0) * half_height) * item->canvas->pixels_per_unit); + ++cache_index; + } + if(last_pymax != last_pymin) { + /* take the index of one sample right of what we render */ + guint index = cache_index + (end - begin); + + if (index >= waveview->cache->data_size) { + + /* the data we want is off the end of the cache, which must mean its beyond + the end of the region's source; hence the peak values are 0 */ + last_pymax = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit); + last_pymin = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit); + + } else { + + last_pymax = (int) rint ((item->y1 + origin - MIN(waveview->cache->data[index].max, 1.0) * half_height) * item->canvas->pixels_per_unit); + last_pymin = (int) rint ((item->y1 + origin - MAX(waveview->cache->data[index].min, -1.0) * half_height) * item->canvas->pixels_per_unit); + + } + + } + + /* + * initialize NEXT* variables for the first run, duplicated in the loop for speed + */ + max = waveview->cache->data[cache_index].max; + min = waveview->cache->data[cache_index].min; + + if (max >= 1.0) { + max = 1.0; + next_clip_max = 1; + } + + if (min <= -1.0) { + min = -1.0; + next_clip_min = 1; + } + + max *= half_height; + min *= half_height; + + next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit); + next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit); + + /* + * And now the loop + */ + for(x = begin; x < end; ++x) { + int clip_max = next_clip_max; + int clip_min = next_clip_min; + int fill_max, fill_min; + + pymax = next_pymax; + pymin = next_pymin; + + /* compute next */ + if(x == end - 1) { + /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/ + next_pymax = last_pymax; + next_pymin = last_pymin; + } + else { + ++cache_index; + + if (cache_index < waveview->cache->data_size) { + max = waveview->cache->data[cache_index].max; + min = waveview->cache->data[cache_index].min; + } else { + max = min = 0; + } + + next_clip_max = 0; + next_clip_min = 0; + + if (max >= 1.0) { + max = 1.0; + next_clip_max = 1; + } + + if (min <= -1.0) { + min = -1.0; + next_clip_min = 1; + } + + max *= half_height; + min *= half_height; + + next_pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit); + next_pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit); + } + + /* render */ + if (pymax == pymin) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin); + } else { + if((prev_pymax < pymax && next_pymax < pymax) || + (prev_pymax == pymax && next_pymax == pymax)) { + fill_max = pymax + 1; + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax); + } + else { + fill_max = MAX(prev_pymax, next_pymax); + if(pymax == fill_max) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax); + ++fill_max; + } + else { + PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max); + } + } + + if((prev_pymin > pymin && next_pymin > pymin) || + (prev_pymin == pymin && next_pymin == pymin)) { + fill_min = pymin - 1; + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin-1); + } + else { + fill_min = MIN(prev_pymin, next_pymin); + if(pymin == fill_min) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin); + } + else { + PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, fill_min, pymin); + } + } + + if(fill_max < fill_min) { + PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, fill_min); + } + else if(fill_max == fill_min) { + PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max); + } + } + + if (clip_max) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length); + } + + if (clip_min) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin); + } + + prev_pymax = pymax; + prev_pymin = pymin; + } + + } else if (waveview->filled && rectify) { + + int prev_pymax = -1; + int last_pymax = -1; + int next_pymax; + double max, min; + int next_clip_max = 0; + int next_clip_min = 0; + + // for rectified, this stays constant throughout the loop + pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit); + + if(s1 < waveview->samples_per_unit) { + /* we haven't got a prev vars to compare with, so outline the whole line here */ + prev_pymax = pymin; + } + else { + s1 -= waveview->samples_per_unit; + } + + if(end == waveview->bbox_lrx) { + /* we don't have the NEXT vars for the last sample */ + last_pymax = pymin; + } + else { + s2 += waveview->samples_per_unit; + } + + cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2); + + /* + * Compute the variables outside the rendering rect + */ + if(prev_pymax < 0) { + max = MIN(waveview->cache->data[cache_index].max, 1.0); + min = MAX(waveview->cache->data[cache_index].min, -1.0); + + if (fabs (min) > fabs (max)) { + max = fabs (min); + } + + prev_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit); + ++cache_index; + } + if(last_pymax < 0) { + /* take the index of one sample right of what we render */ + int index = cache_index + (end - begin); + + max = MIN(waveview->cache->data[index].max, 1.0); + min = MAX(waveview->cache->data[index].min, -1.0); + + if (fabs (min) > fabs (max)) { + max = fabs (min); + } + + last_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit); + } + + /* + * initialize NEXT* variables for the first run, duplicated in the loop for speed + */ + max = waveview->cache->data[cache_index].max; + min = waveview->cache->data[cache_index].min; + + if (max >= 1.0) { + max = 1.0; + next_clip_max = 1; + } + + if (min <= -1.0) { + min = -1.0; + next_clip_min = 1; + } + + if (fabs (min) > fabs (max)) { + max = fabs (min); + } + + next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit); + + /* + * And now the loop + */ + for(x = begin; x < end; ++x) { + int clip_max = next_clip_max; + int clip_min = next_clip_min; + int fill_max; + + pymax = next_pymax; + + /* compute next */ + if(x == end - 1) { + /*next is now the last column, which is outside the rendering rect, and possibly outside the region*/ + next_pymax = last_pymax; + } + else { + ++cache_index; + + max = waveview->cache->data[cache_index].max; + min = waveview->cache->data[cache_index].min; + + if (max >= 1.0) { + max = 1.0; + next_clip_max = 1; + } + + if (min <= -1.0) { + min = -1.0; + next_clip_min = 1; + } + + if (fabs (min) > fabs (max)) { + max = fabs (min); + } + + next_pymax = (int) rint ((item->y1 + waveview->height - max * waveview->height) * item->canvas->pixels_per_unit); + } + + /* render */ + if (pymax == pymin) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin); + } else { + if((prev_pymax < pymax && next_pymax < pymax) || + (prev_pymax == pymax && next_pymax == pymax)) { + fill_max = pymax + 1; + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax); + } + else { + fill_max = MAX(prev_pymax, next_pymax); + if(pymax == fill_max) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax); + ++fill_max; + } + else { + PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, fill_max); + } + } + + if(fill_max < pymin) { + PAINT_VERTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, fill_max, pymin); + } + else if(fill_max == pymin) { + PAINT_DOTA(buf, waveview->fill_r, waveview->fill_g, waveview->fill_b, waveview->fill_a, x, pymin); + } + } + + if (clip_max) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length); + } + + if (clip_min) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin); + } + + prev_pymax = pymax; + } + } + else { + cache_index = gnome_canvas_waveview_ensure_cache (waveview, s1, s2); + + for (x = begin; x < end; x++) { + + double max, min; + int clip_max, clip_min; + + clip_max = 0; + clip_min = 0; + + max = waveview->cache->data[cache_index].max; + min = waveview->cache->data[cache_index].min; + + if (max >= 1.0) { + max = 1.0; + clip_max = 1; + } + + if (min <= -1.0) { + min = -1.0; + clip_min = 1; + } + + if (rectify) { + + if (fabs (min) > fabs (max)) { + max = fabs (min); + } + + max = max * waveview->height; + + pymax = (int) rint ((item->y1 + waveview->height - max) * item->canvas->pixels_per_unit); + pymin = (int) rint ((item->y1 + waveview->height) * item->canvas->pixels_per_unit); + + } else { + + max = max * half_height; + min = min * half_height; + + pymax = (int) rint ((item->y1 + origin - max) * item->canvas->pixels_per_unit); + pymin = (int) rint ((item->y1 + origin - min) * item->canvas->pixels_per_unit); + } + + /* OK, now fill the RGB buffer at x=i with a line between pymin and pymax, + or, if samples_per_unit == 1, then a dot at each location. + */ + + if (pymax == pymin) { + PAINT_DOTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymin); + } else { + PAINT_VERTA(buf, waveview->wave_r, waveview->wave_g, waveview->wave_b, waveview->wave_a, x, pymax, pymin); + } + + /* show clipped waveforms with small red lines */ + + if (clip_max) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymax, pymax+clip_length); + } + + if (clip_min) { + PAINT_VERTA(buf, waveview->clip_r, waveview->clip_g, waveview->clip_b, waveview->clip_a, x, pymin-clip_length, pymin); + } + + /* presto, we're done */ + + cache_index++; + } + } + + if (!waveview->rectified && waveview->zero_line && waveview->height >= 100) { + // Paint zeroline. + + unsigned char zero_r, zero_g, zero_b, zero_a; + UINT_TO_RGBA( waveview->zero_color, &zero_r, &zero_g, &zero_b, &zero_a); + int zeroline_y = (int) rint ((item->y1 + origin) * item->canvas->pixels_per_unit); + PAINT_HORIZA(buf, zero_r, zero_g, zero_b, zero_a, zbegin, zend, zeroline_y); + } +#undef origin +} + +static void +gnome_canvas_waveview_render (GnomeCanvasItem *item, + GnomeCanvasBuf *buf) +{ + if (_gradient_rendering) { + gnome_canvas_waveview_gradient_render (item, buf); + } else { + gnome_canvas_waveview_flat_render (item, buf); + } +} + static void gnome_canvas_waveview_draw (GnomeCanvasItem *item, GdkDrawable *drawable, diff --git a/gtk2_ardour/canvas-waveview.h b/gtk2_ardour/canvas-waveview.h index f04a4b38c2..9ece3f5425 100644 --- a/gtk2_ardour/canvas-waveview.h +++ b/gtk2_ardour/canvas-waveview.h @@ -64,6 +64,8 @@ struct _GnomeCanvasWaveViewCache GnomeCanvasWaveViewCache* gnome_canvas_waveview_cache_new (); void gnome_canvas_waveview_cache_destroy (GnomeCanvasWaveViewCache*); +void gnome_canvas_waveview_set_gradient_waveforms (int); + typedef gulong (*waveview_length_function_t)(void*); typedef gulong (*waveview_sourcefile_length_function_t)(void*, double); typedef void (*waveview_gain_curve_function_t)(void *arg, double start, double end, float* vector, gint64 veclen); diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index f33dda2dac..e6b8f748eb 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -1404,7 +1404,7 @@ Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start) /** Pop up a context menu for when the user clicks on a start crossfade */ void -Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) +Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/) { using namespace Menu_Helpers; @@ -1419,7 +1419,7 @@ Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Ite /** Pop up a context menu for when the user clicks on an end crossfade */ void -Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) +Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/) { using namespace Menu_Helpers; @@ -1909,7 +1909,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection))); edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection))); - edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))); + edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false))); edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false))); @@ -2743,7 +2743,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) _session->locations()->marks_either_side (start, before, after); - if (before == max_framepos) { + if (before == max_framepos && after == max_framepos) { + /* No marks to snap to, so just don't snap */ + return; + } else if (before == max_framepos) { start = after; } else if (after == max_framepos) { start = before; @@ -3251,7 +3254,7 @@ Editor::history_changed () } void -Editor::duplicate_dialog (bool with_dialog) +Editor::duplicate_range (bool with_dialog) { float times = 1.0f; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 9408b27f8f..d53972afc3 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1838,7 +1838,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD /* duplication */ - void duplicate_dialog (bool with_dialog); + void duplicate_range (bool with_dialog); framepos_t event_frame (GdkEvent const *, double* px = 0, double* py = 0) const; diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index d2c53dc01c..1f697a1610 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -285,7 +285,7 @@ Editor::register_actions () reg_sens (editor_actions, "set-playhead", _("Playhead to Mouse"), sigc::mem_fun(*this, &Editor::set_playhead_cursor)); reg_sens (editor_actions, "set-edit-point", _("Active Marker to Mouse"), sigc::mem_fun(*this, &Editor::set_edit_point)); - reg_sens (editor_actions, "duplicate-range", _("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)); + reg_sens (editor_actions, "duplicate-range", _("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false)); undo_action = reg_sens (editor_actions, "undo", S_("Command|Undo"), sigc::bind (sigc::mem_fun(*this, &Editor::undo), 1U)); redo_action = reg_sens (editor_actions, "redo", _("Redo"), sigc::bind (sigc::mem_fun(*this, &Editor::redo), 1U)); @@ -1597,15 +1597,15 @@ Editor::register_region_actions () _region_actions, "toggle-region-fades", _("Fades"), sigc::bind (sigc::mem_fun(*this, &Editor::toggle_region_fades), 0) ); - /* Open the dialogue to duplicate selected regions */ - reg_sens (_region_actions, "duplicate-region", _("Duplicate"), sigc::bind (sigc::mem_fun (*this, &Editor::duplicate_dialog), false)); + /* Duplicate selected regions */ + reg_sens (_region_actions, "duplicate-region", _("Duplicate"), sigc::bind (sigc::mem_fun (*this, &Editor::duplicate_range), false)); /* Open the dialogue to duplicate selected regions multiple times */ reg_sens ( _region_actions, "multi-duplicate-region", _("Multi-Duplicate..."), - sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true) + sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), true) ); /* Fill tracks with selected regions */ diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 8ae618b37f..42af2bb9fc 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -29,6 +29,7 @@ #include "gtkmm2ext/utils.h" +#include "ardour/audioengine.h" #include "ardour/audioregion.h" #include "ardour/dB.h" #include "ardour/midi_region.h" @@ -2170,9 +2171,20 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c) s->cancel_audition (); } - s->request_suspend_timecode_transmission (); - while (!s->timecode_transmission_suspended ()) { - /* twiddle our thumbs */ + + if (AudioEngine::instance()->connected()) { + + /* do this only if we're the engine is connected + * because otherwise this request will never be + * serviced and we'll busy wait forever. likewise, + * notice if we are disconnected while waiting for the + * request to be serviced. + */ + + s->request_suspend_timecode_transmission (); + while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) { + /* twiddle our thumbs */ + } } } @@ -4094,7 +4106,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView , _nothing_to_drag (false) { DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n"); - + y_origin = atv->y_position(); setup (atv->lines ()); } @@ -4108,6 +4120,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, l list > lines; lines.push_back (rv->get_gain_line ()); + y_origin = rv->get_time_axis_view().y_position(); setup (lines); } @@ -4149,6 +4162,12 @@ AutomationRangeDrag::setup (list > const & lin /* Now ::lines contains the AutomationLines that somehow overlap our drag */ } +double +AutomationRangeDrag::y_fraction (boost::shared_ptr line, double global_y) const +{ + return 1.0 - ((global_y - y_origin) / line->height()); +} + void AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { @@ -4157,6 +4176,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) /* Get line states before we start changing things */ for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { i->state = &i->line->get_state (); + i->original_fraction = y_fraction (i->line, _drags->current_pointer_y()); } if (_ranges.empty()) { @@ -4262,7 +4282,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state); + i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state); } } @@ -4273,11 +4293,12 @@ AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/) return; } - for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - float const f = 1 - (_drags->current_pointer_y() / i->line->height()); + for (list::iterator l = _lines.begin(); l != _lines.end(); ++l) { + float const f = y_fraction (l->line, _drags->current_pointer_y()); /* we are ignoring x position for this drag, so we can just pass in anything */ - i->line->drag_motion (0, f, true, false); + l->line->drag_motion (0, f, true, false); + show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f)); } } diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 1579a178a8..2177555856 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -957,7 +957,8 @@ public: private: void setup (std::list > const &); - + double y_fraction (boost::shared_ptr, double global_y_position) const; + std::list _ranges; /** A line that is part of the drag */ @@ -966,10 +967,11 @@ private: std::list points; ///< points to drag on the line std::pair range; ///< the range of all points on the line, in session frames XMLNode* state; ///< the XML state node before the drag + double original_fraction; ///< initial y-fraction before the drag }; std::list _lines; - + double y_origin; bool _nothing_to_drag; }; diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index a6446b67c3..143f18aaae 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -2081,7 +2081,6 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type) ArdourCanvas::SimpleRect *rect = dynamic_cast (item); if (rect) { rect->property_fill_color_rgba() = rv->get_fill_color(); - rect->property_outline_pixels() = 0; } } set_canvas_cursor (current_canvas_cursor); diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 94e3803577..9b2d613fad 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -817,25 +817,14 @@ EditorRoutes::sync_order_keys_from_model () bool changed = false; uint32_t order = 0; - for (ri = rows.begin(); ri != rows.end(); ++ri) { + for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) { boost::shared_ptr route = (*ri)[_columns.route]; - bool visible = (*ri)[_columns.visible]; - uint32_t old_key = route->order_key (EditorSort); - uint32_t new_key; - if (!visible) { - new_key = UINT_MAX; - } else { - new_key = order; - } - - if (old_key != new_key) { - route->set_order_key (EditorSort, new_key); + if (order != old_key) { + route->set_order_key (EditorSort, order); changed = true; } - - order++; } if (changed) { @@ -847,6 +836,10 @@ EditorRoutes::sync_order_keys_from_model () void EditorRoutes::sync_model_from_order_keys (RouteSortOrderKey src) { + /* Some route order key(s) for `src' has been changed, make sure that + we update out tree/list model and GUI to reflect the change. + */ + if (!_session || _session->deletion_in_progress()) { return; } @@ -872,6 +865,11 @@ EditorRoutes::sync_model_from_order_keys (RouteSortOrderKey src) (*i)->sync_order_keys (src); } } + + /* we could get here after either a change in the Mixer or Editor sort + * order, but either way, the mixer order keys reflect the intended + * order for the GUI, so reorder the treeview model to match it. + */ /* we could get here after either a change in the Mixer or Editor sort * order, but either way, the mixer order keys reflect the intended @@ -880,7 +878,8 @@ EditorRoutes::sync_model_from_order_keys (RouteSortOrderKey src) vector neworder; TreeModel::Children rows = _model->children(); - uint32_t n = 0; + uint32_t old_order = 0; + bool changed = false; if (rows.empty()) { return; @@ -888,14 +887,22 @@ EditorRoutes::sync_model_from_order_keys (RouteSortOrderKey src) neworder.assign (rows.size(), 0); - for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++n) { + for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) { boost::shared_ptr route = (*ri)[_columns.route]; - neworder[route->order_key (EditorSort)] = n; - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor change order for %1 to %2\n", - route->name(), route->order_key (MixerSort))); + uint32_t new_order = route->order_key (EditorSort); + + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor change order for %1 from %2 to %3\n", + route->name(), old_order, new_order)); + + neworder[new_order] = old_order; + + if (old_order != new_order) { + changed = true; + } + } - { + if (changed) { Unwinder uw (_ignore_reorder, true); _model->reorder (neworder); } @@ -1595,26 +1602,3 @@ EditorRoutes::show_tracks_with_regions_at_playhead () resume_redisplay (); } -uint32_t -EditorRoutes::count_displayed_non_special_routes () const -{ - if (!_model) { - return 0; - } - uint32_t cnt = 0; - TreeModel::Children rows = _model->children (); - for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) { - bool visible = (*i)[_columns.visible]; - if (visible) { - boost::shared_ptr route = (*i)[_columns.route]; - if (route) { - if (route->is_master() || route->is_monitor()) { - continue; - } - cnt++; - } - } - } - - return cnt; -} diff --git a/gtk2_ardour/editor_routes.h b/gtk2_ardour/editor_routes.h index cbe9825cb5..008304303f 100644 --- a/gtk2_ardour/editor_routes.h +++ b/gtk2_ardour/editor_routes.h @@ -59,7 +59,6 @@ public: std::list views () const; void hide_all_tracks (bool); void clear (); - uint32_t count_displayed_non_special_routes () const; void sync_order_keys_from_model (); private: diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc index 1452eccf94..5c40330565 100644 --- a/gtk2_ardour/engine_dialog.cc +++ b/gtk2_ardour/engine_dialog.cc @@ -1114,6 +1114,7 @@ EngineControl::get_device_name (const string& driver, const string& human_readab another computer system in it */ MessageDialog msg (_("You need to choose an audio device first.")); + msg.set_position (WIN_POS_MOUSE); msg.run (); return string(); } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index adce954a91..848439ccd1 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -772,6 +772,8 @@ MixerStrip::edit_output_configuration () } else { output_selector->present (); } + + output_selector->set_keep_above (true); } void @@ -786,6 +788,8 @@ MixerStrip::edit_input_configuration () } else { input_selector->present (); } + + input_selector->set_keep_above (true); } gint diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 17d37fa5fb..a8278a7654 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -413,21 +413,12 @@ Mixer_UI::sync_order_keys_from_model () bool changed = false; uint32_t order = 0; - for (ri = rows.begin(); ri != rows.end(); ++ri) { + for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) { boost::shared_ptr route = (*ri)[track_columns.route]; - bool visible = (*ri)[track_columns.visible]; - uint32_t old_key = route->order_key (MixerSort); - uint32_t new_key; - if (!visible) { - new_key = UINT_MAX; - } else { - new_key = order; - } - - if (old_key != new_key) { - route->set_order_key (MixerSort, new_key); + if (old_key != order) { + route->set_order_key (MixerSort, order); changed = true; } @@ -477,6 +468,7 @@ Mixer_UI::sync_model_from_order_keys (RouteSortOrderKey src) vector neworder; TreeModel::Children rows = track_model->children(); uint32_t n = 0; + bool changed = false; if (rows.empty()) { return; @@ -486,12 +478,19 @@ Mixer_UI::sync_model_from_order_keys (RouteSortOrderKey src) for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++n) { boost::shared_ptr route = (*ri)[track_columns.route]; - neworder[route->order_key (MixerSort)] = n; + uint32_t o = route->order_key (MixerSort); + + neworder[o] = n; + + if (o != n) { + changed = true; + } + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("mixer change order for %1 to %2\n", route->name(), route->order_key (MixerSort))); } - { + if (changed) { Unwinder uw (ignore_reorder, true); track_model->reorder (neworder); } @@ -693,6 +692,7 @@ Mixer_UI::show_strip (MixerStrip* ms) MixerStrip* strip = (*i)[track_columns.strip]; if (strip == ms) { (*i)[track_columns.visible] = true; + redisplay_track_list (); break; } } @@ -709,6 +709,7 @@ Mixer_UI::hide_strip (MixerStrip* ms) MixerStrip* strip = (*i)[track_columns.strip]; if (strip == ms) { (*i)[track_columns.visible] = false; + redisplay_track_list (); break; } } @@ -985,6 +986,8 @@ Mixer_UI::initial_track_display () track_model->clear (); add_strips (copy); } + + _session->sync_order_keys (MixerSort); redisplay_track_list (); } @@ -1023,7 +1026,6 @@ Mixer_UI::track_display_button_press (GdkEventButton* ev) return false; case 1: /* visibility */ - if ((iter = track_model->get_iter (path))) { MixerStrip* strip = (*iter)[track_columns.strip]; if (strip) { @@ -1031,10 +1033,8 @@ Mixer_UI::track_display_button_press (GdkEventButton* ev) if (!strip->route()->is_master() && !strip->route()->is_monitor()) { bool visible = (*iter)[track_columns.visible]; (*iter)[track_columns.visible] = !visible; + redisplay_track_list (); } -#ifdef GTKOSX - track_display.queue_draw(); -#endif } } return true; diff --git a/gtk2_ardour/shuttle_control.cc b/gtk2_ardour/shuttle_control.cc index 88159fe020..3f18c26feb 100644 --- a/gtk2_ardour/shuttle_control.cc +++ b/gtk2_ardour/shuttle_control.cc @@ -241,10 +241,16 @@ ShuttleControl::on_button_press_event (GdkEventButton* ev) switch (ev->button) { case 1: - add_modal_grab (); - shuttle_grabbed = true; - shuttle_speed_on_grab = _session->transport_speed (); - mouse_shuttle (ev->x, true); + if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + if (_session->transport_rolling()) { + _session->request_transport_speed (1.0); + } + } else { + add_modal_grab (); + shuttle_grabbed = true; + shuttle_speed_on_grab = _session->transport_speed (); + mouse_shuttle (ev->x, true); + } break; case 2: @@ -265,15 +271,16 @@ ShuttleControl::on_button_release_event (GdkEventButton* ev) switch (ev->button) { case 1: - shuttle_grabbed = false; - remove_modal_grab (); - - if (Config->get_shuttle_behaviour() == Sprung) { - _session->request_transport_speed (shuttle_speed_on_grab); - } else { - mouse_shuttle (ev->x, true); + if (shuttle_grabbed) { + shuttle_grabbed = false; + remove_modal_grab (); + + if (Config->get_shuttle_behaviour() == Sprung) { + _session->request_transport_speed (shuttle_speed_on_grab); + } else { + mouse_shuttle (ev->x, true); + } } - return true; case 2: diff --git a/gtk2_ardour/splash.cc b/gtk2_ardour/splash.cc index 5643adbd6a..78b5547ded 100644 --- a/gtk2_ardour/splash.cc +++ b/gtk2_ardour/splash.cc @@ -21,6 +21,7 @@ #include "pbd/failed_constructor.h" #include "pbd/file_utils.h" + #include "ardour/ardour.h" #include "ardour/filesystem_paths.h" @@ -44,6 +45,7 @@ Splash::Splash () std::string splash_file; if (!find_file_in_search_path (ardour_data_search_path(), "splash.png", splash_file)) { + cerr << "Cannot find splash screen image file\n"; throw failed_constructor(); } @@ -52,6 +54,7 @@ Splash::Splash () } catch (...) { + cerr << "Cannot construct splash screen image\n"; throw failed_constructor(); } @@ -77,6 +80,8 @@ Splash::Splash () set_default_size (pixbuf->get_width(), pixbuf->get_height()); the_splash = this; + expose_done = false; + ARDOUR::BootMessage.connect (msg_connection, invalidator (*this), boost::bind (&Splash::boot_message, this, _1), gui_context()); } @@ -107,10 +112,15 @@ Splash::on_realize () layout->set_font_description (get_style()->get_font()); } - bool -Splash::on_button_release_event (GdkEventButton*) +Splash::on_button_release_event (GdkEventButton* ev) { + RefPtr window = get_window(); + + if (!window || ev->window != window->gobj()) { + return false; + } + hide (); return true; } @@ -135,6 +145,8 @@ Splash::expose (GdkEventExpose* ev) Glib::RefPtr white = style->get_white_gc(); window->draw_layout (white, 10, pixbuf->get_height() - 30, layout); + + Glib::signal_idle().connect (sigc::mem_fun (this, &Splash::idle_after_expose)); return true; } @@ -145,6 +157,32 @@ Splash::boot_message (std::string msg) message (msg); } +bool +Splash::idle_after_expose () +{ + expose_done = true; + return false; +} + +void +Splash::display () +{ + bool was_mapped = is_mapped (); + + if (!was_mapped) { + expose_done = false; + } + + pop_front (); + present (); + + if (!was_mapped) { + while (!expose_done) { + gtk_main_iteration (); + } + } +} + void Splash::message (const string& msg) { @@ -156,9 +194,12 @@ Splash::message (const string& msg) Glib::RefPtr win = darea.get_window(); if (win) { - win->invalidate_rect (Gdk::Rectangle (0, darea.get_height() - 30, - darea.get_width(), 30), true); - win->process_updates (true); - gdk_flush (); + expose_done = false; + + win->invalidate_rect (Gdk::Rectangle (0, darea.get_height() - 30, darea.get_width(), 30), true); + + while (!expose_done) { + gtk_main_iteration (); + } } } diff --git a/gtk2_ardour/splash.h b/gtk2_ardour/splash.h index bb3e7b6f6a..536d7f30f1 100644 --- a/gtk2_ardour/splash.h +++ b/gtk2_ardour/splash.h @@ -38,6 +38,7 @@ class Splash : public Gtk::Window static Splash* instance() { return the_splash; } + void display (); void pop_back_for (Gtk::Window&); void pop_front (); @@ -56,6 +57,9 @@ class Splash : public Gtk::Window void boot_message (std::string); PBD::ScopedConnection msg_connection; + + bool expose_done; + bool idle_after_expose (); }; #endif /* __ardour_gtk_splash_h__ */ diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc index a811fd7cc1..dbdaa2a5f0 100644 --- a/gtk2_ardour/startup.cc +++ b/gtk2_ardour/startup.cc @@ -623,7 +623,10 @@ void ArdourStartup::on_apply () { if (engine_dialog) { - engine_dialog->setup_engine (); + if (engine_dialog->setup_engine ()) { + set_current_page (audio_page_index); + return; + } } if (config_modified) { diff --git a/gtk2_ardour/theme_manager.cc b/gtk2_ardour/theme_manager.cc index 5df00faf98..442ba77e41 100644 --- a/gtk2_ardour/theme_manager.cc +++ b/gtk2_ardour/theme_manager.cc @@ -35,6 +35,7 @@ #include "ardour/filesystem_paths.h" #include "ardour_button.h" +#include "canvas-waveview.h" #include "theme_manager.h" #include "rgb_macros.h" #include "ardour_ui.h" @@ -56,6 +57,7 @@ ThemeManager::ThemeManager() , light_button (_("Light Theme")) , reset_button (_("Restore Defaults")) , flat_buttons (_("Draw \"flat\" buttons")) + , gradient_waveforms (_("Draw waveforms with color gradient")) { set_title (_("Theme Manager")); @@ -91,6 +93,7 @@ ThemeManager::ThemeManager() vbox->pack_start (theme_selection_hbox, PACK_SHRINK); vbox->pack_start (reset_button, PACK_SHRINK); vbox->pack_start (flat_buttons, PACK_SHRINK); + vbox->pack_start (gradient_waveforms, PACK_SHRINK); vbox->pack_start (scroller); add (*vbox); @@ -105,6 +108,7 @@ ThemeManager::ThemeManager() light_button.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_light_theme_button_toggled)); reset_button.signal_clicked().connect (sigc::mem_fun (*this, &ThemeManager::reset_canvas_colors)); flat_buttons.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_flat_buttons_toggled)); + gradient_waveforms.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_gradient_waveforms_toggled)); set_size_request (-1, 400); setup_theme (); @@ -238,6 +242,18 @@ ThemeManager::on_flat_buttons_toggled () gtk_rc_reset_styles (gtk_settings_get_default()); } +void +ThemeManager::on_gradient_waveforms_toggled () +{ + ARDOUR_UI::config()->gradient_waveforms.set (gradient_waveforms.get_active()); + ARDOUR_UI::config()->set_dirty (); + + gnome_canvas_waveview_set_gradient_waveforms (gradient_waveforms.get_active()); + + /* force a redraw */ + gtk_rc_reset_styles (gtk_settings_get_default()); +} + void ThemeManager::on_dark_theme_button_toggled() { @@ -347,6 +363,7 @@ ThemeManager::setup_theme () } flat_buttons.set_active (ARDOUR_UI::config()->flat_buttons.get()); + gradient_waveforms.set_active (ARDOUR_UI::config()->gradient_waveforms.get()); load_rc_file(rcfile, false); } diff --git a/gtk2_ardour/theme_manager.h b/gtk2_ardour/theme_manager.h index 2fe4482905..d08713dc3e 100644 --- a/gtk2_ardour/theme_manager.h +++ b/gtk2_ardour/theme_manager.h @@ -43,6 +43,7 @@ class ThemeManager : public ArdourWindow void on_dark_theme_button_toggled (); void on_light_theme_button_toggled (); void on_flat_buttons_toggled (); + void on_gradient_waveforms_toggled (); private: struct ColorDisplayModelColumns : public Gtk::TreeModel::ColumnRecord { @@ -69,6 +70,7 @@ class ThemeManager : public ArdourWindow Gtk::RadioButton light_button; Gtk::Button reset_button; Gtk::CheckButton flat_buttons; + Gtk::CheckButton gradient_waveforms; bool button_press_event (GdkEventButton*); }; diff --git a/gtk2_ardour/ui_config_vars.h b/gtk2_ardour/ui_config_vars.h index f42d98476b..3b33f7ab70 100644 --- a/gtk2_ardour/ui_config_vars.h +++ b/gtk2_ardour/ui_config_vars.h @@ -1,3 +1,4 @@ UI_CONFIG_VARIABLE(std::string, ui_rc_file, "ui-rc-file", "ardour3_ui_dark.rc") UI_CONFIG_VARIABLE(bool, flat_buttons, "flat-buttons", false) +UI_CONFIG_VARIABLE(bool, gradient_waveforms, "gradient-waveforms", false) diff --git a/libs/appleutility/CAAUParameter.cpp b/libs/appleutility/CAAUParameter.cpp index b99b6ab749..247dc1cf7c 100644 --- a/libs/appleutility/CAAUParameter.cpp +++ b/libs/appleutility/CAAUParameter.cpp @@ -106,7 +106,7 @@ void CAAUParameter::Init (AudioUnit au, AudioUnitParameterID param, AudioUnitSc } else mParamName = CFStringCreateWithCString(NULL, mParamInfo.name, kCFStringEncodingUTF8); - char* str = 0; + const char* str = 0; switch (mParamInfo.unit) { case kAudioUnitParameterUnit_Boolean: diff --git a/libs/ardour/ardour/amp.h b/libs/ardour/ardour/amp.h index 82f93d312b..46dbdba227 100644 --- a/libs/ardour/ardour/amp.h +++ b/libs/ardour/ardour/amp.h @@ -81,6 +81,7 @@ public: : AutomationControl (session, param, al, name) , _amp (a) { set_flags (Controllable::Flag (flags() | Controllable::GainLike)); + alist()->reset_default (1.0); } void set_value (double val); diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index bf91de903b..9e4f5c40fd 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -255,6 +255,9 @@ class Plugin : public PBD::StatefulDestructible, public Latent void set_cycles (uint32_t c) { _cycles = c; } cycles_t cycles() const { return _cycles; } + PBD::Signal1 StartTouch; + PBD::Signal1 EndTouch; + protected: friend class PluginInsert; diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 5b4a5294f2..d80c759cff 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -137,12 +137,12 @@ class PluginInsert : public Processor Split, ///< we copy one of our insert's inputs to multiple plugin inputs Hide, ///< we `hide' some of the plugin's inputs by feeding them silence }; - + private: /* disallow copy construction */ PluginInsert (const PluginInsert&); - void parameter_changed (Evoral::Parameter, float); + void parameter_changed (uint32_t, float); void set_parameter (Evoral::Parameter param, float val); float get_parameter (Evoral::Parameter param); @@ -185,6 +185,9 @@ class PluginInsert : public Processor boost::shared_ptr plugin_factory (boost::shared_ptr); void add_plugin (boost::shared_ptr); + + void start_touch (uint32_t param_id); + void end_touch (uint32_t param_id); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 695e151dbb..f6c737d766 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -423,6 +423,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, void set_remote_control_id (uint32_t id, bool notify_class_listeners = true); uint32_t remote_control_id () const; + void set_remote_control_id_from_order_key (RouteSortOrderKey); /* for things concerned about *this* route's RID */ @@ -534,7 +535,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember, typedef std::map OrderKeys; OrderKeys order_keys; - uint32_t* _remote_control_id; + uint32_t _remote_control_id; void input_change_handler (IOChange, void *src); void output_change_handler (IOChange, void *src); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index a132a4a5e8..bc14805074 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -235,6 +235,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi }; void sync_order_keys (RouteSortOrderKey); + void sync_remote_id_from_order_keys (RouteSortOrderKey); template void foreach_route (T *obj, void (T::*func)(Route&)); template void foreach_route (T *obj, void (T::*func)(boost::shared_ptr)); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 293ca0d2c1..6161147f44 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -672,7 +672,7 @@ AudioTrack::freeze_me (InterThreadInfo& itt) for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { - if (!(*r)->does_routing()) { + if (!(*r)->does_routing() && !boost::dynamic_pointer_cast(*r)) { FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo ((*r)->get_state(), (*r)); diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index e02a00c28e..431fb99a48 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -2753,6 +2753,26 @@ AUPlugin::listen_to_parameter (uint32_t param_id) return -1; } + event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; + event.mArgument.mParameter.mAudioUnit = unit->AU(); + event.mArgument.mParameter.mParameterID = descriptors[param_id].id; + event.mArgument.mParameter.mScope = descriptors[param_id].scope; + event.mArgument.mParameter.mElement = descriptors[param_id].element; + + if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) { + return -1; + } + + event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; + event.mArgument.mParameter.mAudioUnit = unit->AU(); + event.mArgument.mParameter.mParameterID = descriptors[param_id].id; + event.mArgument.mParameter.mScope = descriptors[param_id].scope; + event.mArgument.mParameter.mElement = descriptors[param_id].element; + + if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) { + return -1; + } + return 0; } @@ -2775,6 +2795,26 @@ AUPlugin::end_listen_to_parameter (uint32_t param_id) return -1; } + event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; + event.mArgument.mParameter.mAudioUnit = unit->AU(); + event.mArgument.mParameter.mParameterID = descriptors[param_id].id; + event.mArgument.mParameter.mScope = descriptors[param_id].scope; + event.mArgument.mParameter.mElement = descriptors[param_id].element; + + if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) { + return -1; + } + + event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; + event.mArgument.mParameter.mAudioUnit = unit->AU(); + event.mArgument.mParameter.mParameterID = descriptors[param_id].id; + event.mArgument.mParameter.mScope = descriptors[param_id].scope; + event.mArgument.mParameter.mElement = descriptors[param_id].element; + + if (AUEventListenerRemoveEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) { + return -1; + } + return 0; } @@ -2787,9 +2827,23 @@ AUPlugin::_parameter_change_listener (void* arg, void* src, const AudioUnitEvent void AUPlugin::parameter_change_listener (void* /*arg*/, void* /*src*/, const AudioUnitEvent* event, UInt64 /*host_time*/, Float32 new_value) { - ParameterMap::iterator i = parameter_map.find (event->mArgument.mParameter.mParameterID); + ParameterMap::iterator i; - if (i != parameter_map.end()) { - ParameterChanged (i->second, new_value); - } + if ((i = parameter_map.find (event->mArgument.mParameter.mParameterID)) == parameter_map.end()) { + return; + } + + switch (event->mEventType) { + case kAudioUnitEvent_BeginParameterChangeGesture: + StartTouch (i->second); + break; + case kAudioUnitEvent_EndParameterChangeGesture: + EndTouch (i->second); + break; + case kAudioUnitEvent_ParameterValueChange: + ParameterChanged (i->second, new_value); + break; + default: + break; + } } diff --git a/libs/ardour/buffer_manager.cc b/libs/ardour/buffer_manager.cc index b30fd6ebb6..e66c4a8dee 100644 --- a/libs/ardour/buffer_manager.cc +++ b/libs/ardour/buffer_manager.cc @@ -45,6 +45,7 @@ BufferManager::init (uint32_t size) thread_buffers->write (&ts, 1); thread_buffers_list->push_back (ts); } + } ThreadBuffers* diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index b2d59071b1..43db5cef53 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -303,7 +303,11 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization) (void) PluginManager::instance(); ProcessThread::init (); - BufferManager::init (hardware_concurrency() + 1); + /* the + 4 is a bit of a handwave. i don't actually know + how many more per-thread buffer sets we need above + the h/w concurrency, but its definitely > 1 more. + */ + BufferManager::init (hardware_concurrency() + 4); PannerManager::instance().discover_panners(); diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index 786ed3c080..3eb76d06e5 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -197,7 +197,7 @@ MidiRingBuffer::read(MidiBuffer& dst, framepos_t start, framepos_t end, frame template void -MidiRingBuffer::flush (framepos_t start, framepos_t end) +MidiRingBuffer::flush (framepos_t /*start*/, framepos_t end) { const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index a8a0e218a6..1d352d622b 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -722,6 +722,13 @@ MidiTrack::act_on_mute () XXX we should should also stop all relevant note trackers. */ + /* If we haven't got a diskstream yet, there's nothing to worry about, + and we can't call get_channel_mask() anyway. + */ + if (!midi_diskstream()) { + return; + } + if (muted()) { /* only send messages for channels we are using */ diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index ba61e43e9c..6e7cfe373f 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -262,21 +262,24 @@ PluginInsert::create_automatable_parameters () } void -PluginInsert::parameter_changed (Evoral::Parameter which, float val) +PluginInsert::parameter_changed (uint32_t which, float val) { - if (which.type() != PluginAutomation) - return; + boost::shared_ptr ac = automation_control (Evoral::Parameter (PluginAutomation, 0, which)); - Plugins::iterator i = _plugins.begin(); - - /* don't set the first plugin, just all the slaves */ - - if (i != _plugins.end()) { - ++i; - for (; i != _plugins.end(); ++i) { - (*i)->set_parameter (which, val); - } - } + if (ac) { + ac->set_double (val); + + Plugins::iterator i = _plugins.begin(); + + /* don't set the first plugin, just all the slaves */ + + if (i != _plugins.end()) { + ++i; + for (; i != _plugins.end(); ++i) { + (*i)->set_parameter (which, val); + } + } + } } int @@ -1155,7 +1158,11 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p, const Evoral::Param , _plugin (p) { Plugin::ParameterDescriptor desc; - p->plugin(0)->get_parameter_descriptor (param.id(), desc); + boost::shared_ptr plugin = p->plugin (0); + + alist()->reset_default (plugin->default_value (param.id())); + + plugin->get_parameter_descriptor (param.id(), desc); _logarithmic = desc.logarithmic; _sr_dependent = desc.sr_dependent; _toggled = desc.toggled; @@ -1254,6 +1261,16 @@ void PluginInsert::add_plugin (boost::shared_ptr plugin) { plugin->set_insert_info (this); + + if (_plugins.empty()) { + /* first (and probably only) plugin instance - connect to relevant signals + */ + + plugin->ParameterChanged.connect_same_thread (*this, boost::bind (&PluginInsert::parameter_changed, this, _1, _2)); + plugin->StartTouch.connect_same_thread (*this, boost::bind (&PluginInsert::start_touch, this, _1)); + plugin->EndTouch.connect_same_thread (*this, boost::bind (&PluginInsert::end_touch, this, _1)); + } + _plugins.push_back (plugin); } @@ -1280,3 +1297,21 @@ PluginInsert::monitoring_changed () (*i)->monitoring_changed (); } } + +void +PluginInsert::start_touch (uint32_t param_id) +{ + boost::shared_ptr ac = automation_control (Evoral::Parameter (PluginAutomation, 0, param_id)); + if (ac) { + ac->start_touch (session().audible_frame()); + } +} + +void +PluginInsert::end_touch (uint32_t param_id) +{ + boost::shared_ptr ac = automation_control (Evoral::Parameter (PluginAutomation, 0, param_id)); + if (ac) { + ac->stop_touch (true, session().audible_frame()); + } +} diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 2112336d8b..f7fdb2f11b 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -195,8 +195,6 @@ Route::~Route () } _processors.clear (); - - delete _remote_control_id; } void @@ -232,11 +230,9 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) } if (id != remote_control_id()) { - if (!_remote_control_id) { - _remote_control_id = new uint32_t; - } - *_remote_control_id = id; + _remote_control_id = id; RemoteControlIDChanged (); + if (notify_class_listeners) { RemoteControlIDChange (); } @@ -246,16 +242,6 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) uint32_t Route::remote_control_id() const { - switch (Config->get_remote_model()) { - case UserOrdered: - if (_remote_control_id) { - return *_remote_control_id; - } - break; - default: - break; - } - if (is_master()) { return MasterBusRemoteControlID; } @@ -264,16 +250,7 @@ Route::remote_control_id() const return MonitorBusRemoteControlID; } - /* order keys are zero-based, remote control ID's are one-based - */ - - switch (Config->get_remote_model()) { - case EditorOrdered: - return order_key (EditorSort) + 1; - case MixerOrdered: - default: - return order_key (MixerSort) + 1; - } + return _remote_control_id; } bool @@ -291,12 +268,25 @@ Route::order_key (RouteSortOrderKey key) const return 0; } - return i->second; + /* order keys are zero-based, remote control ID's are one-based + */ + + switch (Config->get_remote_model()) { + case EditorOrdered: + return order_key (EditorSort) + 1; + case MixerOrdered: + default: + return order_key (MixerSort) + 1; + } } void Route::sync_order_keys (RouteSortOrderKey base) { + /* this is called after changes to 1 or more route order keys have been + * made, and we want to sync up. + */ + OrderKeys::iterator i = order_keys.find (base); if (i == order_keys.end()) { @@ -305,27 +295,113 @@ Route::sync_order_keys (RouteSortOrderKey base) for (OrderKeys::iterator k = order_keys.begin(); k != order_keys.end(); ++k) { - if (k->first == MixerSort && (is_master() || is_monitor())) { - /* don't sync the mixer sort keys for master/monitor, + if (is_master() || is_monitor()) { + /* don't sync the sort keys for master/monitor, * since they are not part of the normal ordering. */ - continue; } if (k->first != base) { + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 set key for %2 to %3 from %4\n", + name(), + enum_2_string (k->first), + i->second, + base)); + k->second = i->second; } } } +void +Route::set_remote_control_id_from_order_key (RouteSortOrderKey key) +{ + if (is_master() || is_monitor() || is_hidden()) { + /* hard-coded remote IDs, or no remote ID */ + return; + } + + uint32_t n = order_keys[key]; + + /* we have a nasty glitch in an otherwise fairly clean system here. + + in theory, a route's remote control ID is determined by the order + key matching the current remote model (for UserOrdered, the user + controls everything). its one greater, because order keys are zero + based and remote control IDs start at one. + + but ... an order key for the master bus may place it before or even + within normal routes, yet its remote control ID (like the monitor + bus) is hardcoded to MasterBusRemoteControlID. this means that all + routes ordered after it (in whatever controls the EditorSort or + MixerSort ordering) will end up with a remote control ID that is one + too large. + + we therefore check on the master bus ordering, and adjust + later-sorted routes remote control ID to avoid this "off by one" + error, which keeps remote control ID's contiguous and "right". + + ideally, this would be done in a UI layer, where this logic + is really understood and has meaning, rather than in libardour where + its fundamentally meaningless. + */ + + switch (Config->get_remote_model()) { + case UserOrdered: + break; + case EditorOrdered: + if (key == EditorSort) { + boost::shared_ptr master = _session.master_out(); + if (master && n > 0 && n > master->order_key (EditorSort)) { + --n; + } + _remote_control_id = n + 1; + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: from order key %2, set edit-based RID to %3\n", + name(), n, _remote_control_id)); + RemoteControlIDChanged (); /* EMIT SIGNAL * (per-route) */ + } + break; + + case MixerOrdered: + if (key == MixerSort) { + boost::shared_ptr master = _session.master_out(); + if (master && n > 0 && n > master->order_key (MixerSort)) { + --n; + } + _remote_control_id = n + 1; + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: from order key %2, set mix-based RID to %3\n", + name(), n, _remote_control_id)); + RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */ + } + break; + } + + /* don't emit the class-level RID signal RemoteControlIDChange here, + leave that to the entity that changed the order key, so that we + don't get lots of emissions for no good reasons (e.g. when changing + all route order keys). + + See Session::sync_remote_id_from_order_keys() for the (primary|only) + spot where that is emitted. + */ +} + void Route::set_order_key (RouteSortOrderKey key, uint32_t n) { - if (order_keys.find (key) == order_keys.end() || order_keys[key] != n) { - order_keys[key] = n; - _session.set_dirty (); + OrderKeys::iterator i = order_keys.find (key); + + if (i != order_keys.end() && i->second == n) { + return; } + + order_keys[key] = n; + + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key %2 set to %3 (chk=%4)\n", + name(), enum_2_string (key), n, order_key (key))); + + _session.set_dirty (); } string @@ -1894,12 +1970,10 @@ Route::state(bool full_state) node->add_child_nocopy (_mute_control->get_state ()); node->add_child_nocopy (_mute_master->get_state ()); - if (_remote_control_id) { - XMLNode* remote_control_node = new XMLNode (X_("RemoteControl")); - snprintf (buf, sizeof (buf), "%d", *_remote_control_id); - remote_control_node->add_property (X_("id"), buf); - node->add_child_nocopy (*remote_control_node); - } + XMLNode* remote_control_node = new XMLNode (X_("RemoteControl")); + snprintf (buf, sizeof (buf), "%d", _remote_control_id); + remote_control_node->add_property (X_("id"), buf); + node->add_child_nocopy (*remote_control_node); if (_comment.length()) { XMLNode *cmt = node->add_child ("Comment"); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 062d07ec26..47f39d96b7 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -574,6 +574,7 @@ Session::when_engine_running () BootMessage (_("Connect to engine")); _engine.set_session (this); + _engine.reset_timebase (); } void @@ -2205,7 +2206,22 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output } RouteAdded (new_routes); /* EMIT SIGNAL */ - Route::RemoteControlIDChange (); /* EMIT SIGNAL */ + + /* we added at least one new route, and everyone who needs to has now + * handled this event. This means that route order keys are correctly + * set and we can now ensure that remote control IDs are set. + */ + + switch (Config->get_remote_model()) { + case UserOrdered: + break; + case MixerOrdered: + sync_remote_id_from_order_keys (MixerSort); + break; + case EditorOrdered: + sync_remote_id_from_order_keys (EditorSort); + break; + } } void @@ -3514,6 +3530,8 @@ Session::graph_reordered () boost::optional Session::available_capture_duration () { + Glib::Mutex::Lock lm (space_lock); + if (_total_free_4k_blocks_uncertain) { return boost::optional (); } @@ -4712,11 +4730,54 @@ Session::sync_order_keys (RouteSortOrderKey sort_key_changed) /* tell everyone that something has happened to the sort keys and let them sync up with the change(s) + this will give objects that manage the sort order keys the + opportunity to keep them in sync if they wish to. */ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("Sync Order Keys, based on %1\n", enum_2_string (sort_key_changed))); Route::SyncOrderKeys (sort_key_changed); /* EMIT SIGNAL */ + + /* ensure that remote control IDs are in sync with the relevant + order keys. + */ + + sync_remote_id_from_order_keys (sort_key_changed); +} + +void +Session::sync_remote_id_from_order_keys (RouteSortOrderKey sort_key_changed) +{ + /* update remote control IDs if that makes sense */ + + bool do_update = false; + + DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("sync RID to order key %1\n", enum_2_string (sort_key_changed))); + + switch (Config->get_remote_model()) { + case UserOrdered: + break; + case EditorOrdered: + if (sort_key_changed == EditorSort) { + do_update = true; + } + break; + case MixerOrdered: + if (sort_key_changed == MixerSort) { + do_update = true; + } + break; + } + + if (do_update) { + DEBUG_TRACE (DEBUG::OrderKeys, "\tactually update + signal\n"); + boost::shared_ptr r = routes.reader(); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_remote_control_id_from_order_key (sort_key_changed); + } + + Route::RemoteControlIDChange (); /* EMIT SIGNAL - static */ + } } bool diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index da6f618c11..a3460dddfb 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -3506,8 +3506,33 @@ Session::config_changed (std::string p, bool ours) setup_fpu (); } else if (p == "history-depth") { set_history_depth (Config->get_history_depth()); + } else if (p == "remote-model") { + switch (Config->get_remote_model()) { + case UserOrdered: + break; + case MixerOrdered: + sync_remote_id_from_order_keys (MixerSort); + break; + case EditorOrdered: + sync_remote_id_from_order_keys (EditorSort); + break; + } } else if (p == "sync-all-route-ordering") { - /* XXX sync_order_keys (UndefinedSort); */ + + /* sync to editor order unless mixer is used for remote IDs + */ + + switch (Config->get_remote_model()) { + case UserOrdered: + sync_order_keys (EditorSort); + break; + case EditorOrdered: + sync_order_keys (EditorSort); + break; + case MixerOrdered: + sync_order_keys (MixerSort); + } + } else if (p == "initial-program-change") { if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 205c682b6f..a878b9fabc 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -798,6 +798,14 @@ Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, if (target_frame != pos) { + if (config.get_jack_time_master()) { + /* actually locate now, since otherwise jack_timebase_callback + will use the incorrect _transport_frame and report an old + and incorrect time to Jack transport + */ + locate (target_frame, with_roll, with_flush, with_loop, force); + } + /* tell JACK to change transport position, and we will follow along later in ::follow_slave() */ diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 7f307f3edc..af6206b5e5 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -28,6 +28,7 @@ #include "ardour/processor.h" #include "ardour/route_group_specialized.h" #include "ardour/session.h" +#include "ardour/session_playlists.h" #include "ardour/track.h" #include "ardour/utils.h" @@ -333,12 +334,18 @@ Track::set_name (const string& str) return false; } - if (_diskstream->playlist()->all_regions_empty ()) { + boost::shared_ptr me = boost::dynamic_pointer_cast (shared_from_this ()); + if (_diskstream->playlist()->all_regions_empty () && _session.playlists->playlists_for_track (me).size() == 1) { /* Only rename the diskstream (and therefore the playlist) if - the playlist has never had a region added to it. Otherwise - people can get confused if, say, they have notes about a - playlist with a given name and then it changes (see mantis - #4759). + a) the playlist has never had a region added to it and + b) there is only one playlist for this track. + + If (a) is not followed, people can get confused if, say, + they have notes about a playlist with a given name and then + it changes (see mantis #4759). + + If (b) is not followed, we rename the current playlist and not + the other ones, which is a bit confusing (see mantis #4977). */ _diskstream->set_name (str); } diff --git a/libs/audiographer/audiographer/process_context.h b/libs/audiographer/audiographer/process_context.h index df00812c01..15128ab4ea 100644 --- a/libs/audiographer/audiographer/process_context.h +++ b/libs/audiographer/audiographer/process_context.h @@ -15,6 +15,7 @@ namespace AudioGrapher { + /** * Processing context. Constness only applies to data, not flags */ @@ -23,6 +24,9 @@ template class ProcessContext : public Throwing<> { + // Support older compilers that don't support template base class initialization without template parameters + // This will need to be modified if if it's modified above + static const ThrowLevel throwLevel = DEFAULT_THROW_LEVEL; BOOST_STATIC_ASSERT (boost::has_trivial_destructor::value); @@ -43,25 +47,25 @@ public: /// Normal copy constructor ProcessContext (ProcessContext const & other) - : _data (other._data), _frames (other._frames), _channels (other._channels), _flags (other._flags) + : Throwing(), _data (other._data), _frames (other._frames), _channels (other._channels), _flags (other._flags) { /* No need to validate data */ } /// "Copy constructor" with unique data, frame and channel count, but copies flags template ProcessContext (ProcessContext const & other, T * data, framecnt_t frames, ChannelCount channels) - : _data (data), _frames (frames), _channels (channels), _flags (other.flags()) + : Throwing(), _data (data), _frames (frames), _channels (channels), _flags (other.flags()) { validate_data(); } /// "Copy constructor" with unique data and frame count, but copies channel count and flags template ProcessContext (ProcessContext const & other, T * data, framecnt_t frames) - : _data (data), _frames (frames), _channels (other.channels()), _flags (other.flags()) + : Throwing(), _data (data), _frames (frames), _channels (other.channels()), _flags (other.flags()) { validate_data(); } /// "Copy constructor" with unique data, but copies frame and channel count + flags template ProcessContext (ProcessContext const & other, T * data) - : _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) + : Throwing(), _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) { /* No need to validate data */ } /// Make new Context out of the beginning of this context diff --git a/libs/audiographer/private/sndfile.hh b/libs/audiographer/private/sndfile.hh index 53d0131a81..1c4ef5ab58 100644 --- a/libs/audiographer/private/sndfile.hh +++ b/libs/audiographer/private/sndfile.hh @@ -52,8 +52,6 @@ #ifndef SNDFILE_HH #define SNDFILE_HH -#include - #include #include @@ -171,7 +169,6 @@ SndfileHandle::SndfileHandle (const char *path, int mode, int fmt, int chans, in p->sfinfo.seekable = 0 ; p->sf = sf_open (path, mode, &p->sfinfo) ; - std::cerr << "3 attempted to open " << path << " got " << p->sf << std::endl; } ; return ; @@ -194,7 +191,6 @@ SndfileHandle::SndfileHandle (std::string const & path, int mode, int fmt, int c p->sfinfo.seekable = 0 ; p->sf = sf_open (path.c_str (), mode, &p->sfinfo) ; - std::cerr << "attempted to open " << path << " got " << p->sf << std::endl; } ; return ; @@ -220,7 +216,6 @@ SndfileHandle::SndfileHandle (int fd, bool close_desc, int mode, int fmt, int ch p->sfinfo.seekable = 0 ; p->sf = sf_open_fd (fd, mode, &p->sfinfo, close_desc) ; - std::cerr << "2 attempted to open via fd " << fd << " got " << p->sf << std::endl; } ; return ; diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index b1ec7897cf..1e546e04fa 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -291,10 +291,12 @@ protected: EventList events; double start_time; double end_time; - + double same_value_cnt; + NascentInfo (double start = -1.0) : start_time (start) , end_time (-1.0) + , same_value_cnt (0) {} }; diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 53200398a5..9b672612ed 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -27,7 +27,6 @@ using namespace std; namespace Evoral { - inline bool event_time_less_than (ControlEvent* a, ControlEvent* b) { return a->when < b->when; @@ -254,12 +253,15 @@ ControlList::merge_nascent (double when) return; } + bool was_empty = _events.empty(); + for (list::iterator n = nascent.begin(); n != nascent.end(); ++n) { NascentInfo* ninfo = *n; EventList& nascent_events (ninfo->events); bool need_adjacent_start_clamp; bool need_adjacent_end_clamp; + EventList::iterator at; if (nascent_events.empty()) { delete ninfo; @@ -276,22 +278,61 @@ ControlList::merge_nascent (double when) ninfo->end_time = when; } - bool preexisting = !_events.empty(); + if (_events.empty()) { - if (!preexisting) { + /* add an initial point just before + the nascent data, unless nascent_events + contains a point at zero or one + */ - _events = nascent_events; + if (ninfo->start_time > 0) { + nascent_events.insert (nascent_events.begin(), new ControlEvent (ninfo->start_time - 1, _default_value)); + } - } else if (ninfo->end_time < _events.front()->when) { + /* add closing "clamp" point before we insert */ - /* all points in nascent are before the first existing point */ + nascent_events.insert (nascent_events.end(), new ControlEvent (ninfo->end_time + 1, _default_value)); + + /* insert - front or back doesn't matter since + * _events is empty + */ _events.insert (_events.begin(), nascent_events.begin(), nascent_events.end()); + } else if (ninfo->end_time < _events.front()->when) { + + /* all points in nascent are before the first existing point */ + + if (ninfo->start_time > (_events.front()->when + 1)) { + nascent_events.insert (nascent_events.begin(), new ControlEvent (ninfo->start_time - 1, _default_value)); + } + + /* add closing "clamp" point before we insert */ + + nascent_events.insert (nascent_events.end(), new ControlEvent (ninfo->end_time + 1, _default_value)); + + /* insert at front */ + + _events.insert (_events.begin(), nascent_events.begin(), nascent_events.end()); + + /* now add another default control point right + after the inserted nascent data + */ + } else if (ninfo->start_time > _events.back()->when) { /* all points in nascent are after the last existing point */ + if (ninfo->start_time > (_events.back()->when + 1)) { + nascent_events.insert (nascent_events.begin(), new ControlEvent (ninfo->start_time - 1, _default_value)); + } + + /* add closing "clamp" point before we insert */ + + nascent_events.insert (nascent_events.end(), new ControlEvent (ninfo->end_time + 1, _default_value)); + + /* insert */ + _events.insert (_events.end(), nascent_events.begin(), nascent_events.end()); } else { @@ -350,7 +391,7 @@ ControlList::merge_nascent (double when) range_begin is the first event on our list after the first nascent event range_end is the first event on our list after the last nascent event - range_begin may be equal to _events.end() iff the last event on our list + range_begin may be equal to _events.end() if the last event on our list was at the same time as the first nascent event. */ @@ -376,6 +417,12 @@ ControlList::merge_nascent (double when) delete ninfo; } + if (was_empty && !_events.empty()) { + if (_events.front()->when != 0) { + _events.insert (_events.begin(), new ControlEvent (0, _default_value)); + } + } + nascent.clear (); if (writing()) { @@ -394,8 +441,8 @@ ControlList::rt_add (double when, double value) if (touch_enabled() && !touching()) { return; } - - //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl; + + // cerr << "RT: alist " << this << " add " << value << " @ " << when << endl; Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK); @@ -405,18 +452,28 @@ ControlList::rt_add (double when, double value) sort them in merge_nascent. */ - EventList& el (nascent.back()->events); + NascentInfo* ni (nascent.back()); + EventList& el (ni->events); + + if (!el.empty() && (when >= el.back()->when) && (value == el.back()->value)) { - if (el.size() > 1 && (when >= el.back()->when) && (value == el.back()->value)) { /* same value, later timestamp, effective slope is * zero, so just move the last point in nascent to our * new time position. this avoids storing an unlimited * number of points to represent a flat line. */ - el.back()->when = when; + + ni->same_value_cnt++; + + if (ni->same_value_cnt > 1) { + el.back()->when = when; + return; + } } else { - nascent.back()->events.push_back (new ControlEvent (when, value)); + ni->same_value_cnt = 0; } + + el.push_back (new ControlEvent (when, value)); } } @@ -489,6 +546,12 @@ ControlList::add (double when, double value) bool insert = true; iterator insertion_point; + if (_events.empty()) { + if (when > 1) { + _events.insert (_events.end(), new ControlEvent (0, _default_value)); + } + } + for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) { /* only one point allowed per time point */ @@ -505,9 +568,7 @@ ControlList::add (double when, double value) } if (insert) { - _events.insert (insertion_point, new ControlEvent (when, value)); - } mark_dirty (); diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 05883b99be..d6a75f66f9 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -24,6 +24,7 @@ #include #include +#include #include "pbd/controllable_descriptor.h" #include "pbd/error.h" @@ -284,6 +285,12 @@ GenericMidiControlProtocol::_send_feedback () in a single jack_midi_event_write then some bridges will only pass the first on to ALSA. */ + + Glib::Mutex::Lock lm (controllables_lock, Glib::TRY_LOCK); + if (!lm.locked ()) { + return; + } + for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) { MIDI::byte* end = (*r)->write_feedback (buf, bsize); if (end != buf) { @@ -778,10 +785,7 @@ GenericMidiControlProtocol::reset_controllables () * binding" (or "lazy binding") if/when any data arrives. */ - boost::shared_ptr c = session->controllable_by_descriptor (desc); - if (c) { - existingBinding->set_controllable (c.get()); - } + existingBinding->lookup_controllable (); } iter = next; diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index f721e7fbbc..4be5e25b92 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -61,11 +61,12 @@ MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, Controllable& c, bool m) : _surface (s) - , controllable (&c) , _descriptor (0) , _port (p) , _momentary (m) { + set_controllable (&c); + _learned = true; /* from controllable */ setting = false; last_value = 0; // got a better idea ? @@ -113,7 +114,19 @@ MIDIControllable::drop_external_control () void MIDIControllable::set_controllable (Controllable* c) { + if (c == controllable) { + return; + } + + controllable_death_connection.disconnect (); + controllable = c; + + if (controllable) { + controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR, + boost::bind (&MIDIControllable::drop_controllable, this), + MidiControlUI::instance()); + } } void @@ -199,10 +212,7 @@ MIDIControllable::lookup_controllable() return -1; } - controllable = c.get(); - controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR, - boost::bind (&MIDIControllable::drop_controllable, this), - MidiControlUI::instance()); + set_controllable (c.get ()); return 0; } @@ -210,9 +220,8 @@ MIDIControllable::lookup_controllable() void MIDIControllable::drop_controllable () { - cerr << "removed controllable\n"; - controllable_death_connection.disconnect (); - controllable = 0; + cerr << "removed controllable " << controllable << "\n"; + set_controllable (0); } void diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h index ff4411a9ca..dd9dc988b4 100644 --- a/libs/surfaces/generic_midi/midicontrollable.h +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -88,6 +88,8 @@ class MIDIControllable : public PBD::Stateful 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; } + + int lookup_controllable(); private: @@ -118,7 +120,6 @@ class MIDIControllable : public PBD::Stateful std::string _what; bool _bank_relative; - int lookup_controllable(); void drop_controllable(); void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t); diff --git a/midi_maps/Korg_nanoKONTROL.map b/midi_maps/Korg_nanoKONTROL.map index bac8c43bb6..05308a1b0f 100644 --- a/midi_maps/Korg_nanoKONTROL.map +++ b/midi_maps/Korg_nanoKONTROL.map @@ -5,12 +5,12 @@ - - - - - - + + + + + + diff --git a/midi_maps/Korg_nanoKONTROL2.map b/midi_maps/Korg_nanoKONTROL2.map new file mode 100644 index 0000000000..7f85945e90 --- /dev/null +++ b/midi_maps/Korg_nanoKONTROL2.map @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/midi_maps/Korg_nanoKONTROL2_With_Master.map b/midi_maps/Korg_nanoKONTROL2_With_Master.map new file mode 100644 index 0000000000..adf8ee72cd --- /dev/null +++ b/midi_maps/Korg_nanoKONTROL2_With_Master.map @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +