From 8e1db53556463c95abf934b6c3d654e1fcfd12d7 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sat, 2 Jun 2012 16:21:08 +0000 Subject: [PATCH] merge -r12436:12545 from 3.0 git-svn-id: svn://localhost/ardour2/branches/3.0-SG@12546 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour_ui.cc | 12 +- gtk2_ardour/ardour_ui.h | 5 +- gtk2_ardour/ardour_ui2.cc | 1 + gtk2_ardour/ardour_ui_dialogs.cc | 1 + gtk2_ardour/ardour_ui_ed.cc | 1 + gtk2_ardour/ardour_ui_options.cc | 1 + gtk2_ardour/au_pluginui.mm | 113 ++++---- gtk2_ardour/audio_clock.cc | 4 +- gtk2_ardour/audio_clock.h | 7 +- gtk2_ardour/audio_region_view.cc | 48 ++-- gtk2_ardour/audio_region_view.h | 4 +- gtk2_ardour/automation_line.cc | 181 ++++++------- gtk2_ardour/automation_line.h | 30 ++- gtk2_ardour/automation_region_view.cc | 3 +- gtk2_ardour/automation_time_axis.cc | 2 +- gtk2_ardour/automation_time_axis.h | 2 +- gtk2_ardour/canvas_patch_change.cc | 3 + gtk2_ardour/editor.cc | 54 ++-- gtk2_ardour/editor.h | 11 +- gtk2_ardour/editor_actions.cc | 2 +- gtk2_ardour/editor_drag.cc | 13 +- gtk2_ardour/editor_mouse.cc | 80 +++--- gtk2_ardour/editor_ops.cc | 31 ++- gtk2_ardour/editor_regions.cc | 1 + gtk2_ardour/editor_selection.cc | 34 ++- gtk2_ardour/gain_meter.cc | 10 +- gtk2_ardour/gain_meter.h | 3 +- gtk2_ardour/icons/fader_belt_desensitised.png | Bin 0 -> 2949 bytes .../icons/fader_belt_h_desensitised.png | Bin 0 -> 2372 bytes .../icons/fader_belt_h_thin_desensitised.png | Bin 0 -> 929 bytes gtk2_ardour/main.cc | 153 +++-------- gtk2_ardour/main_clock.cc | 68 +++++ gtk2_ardour/main_clock.h | 33 +++ gtk2_ardour/midi_region_view.cc | 36 ++- gtk2_ardour/midi_region_view.h | 16 +- gtk2_ardour/midi_streamview.cc | 11 + gtk2_ardour/midi_streamview.h | 2 + gtk2_ardour/midi_time_axis.cc | 27 +- gtk2_ardour/midi_time_axis.h | 3 + gtk2_ardour/mouse_cursors.cc | 4 +- gtk2_ardour/option_editor.cc | 6 + gtk2_ardour/option_editor.h | 1 + gtk2_ardour/patch_change_dialog.cc | 240 ++++++++++++++++- gtk2_ardour/patch_change_dialog.h | 29 +++ gtk2_ardour/playlist_selector.cc | 2 +- gtk2_ardour/processor_box.cc | 15 +- gtk2_ardour/processor_box.h | 3 +- gtk2_ardour/public_editor.h | 1 + gtk2_ardour/rc_option_editor.cc | 16 -- gtk2_ardour/route_time_axis.cc | 25 +- gtk2_ardour/route_time_axis.h | 3 +- gtk2_ardour/selection.cc | 6 + gtk2_ardour/selection.h | 1 + gtk2_ardour/sfdb_ui.cc | 1 + gtk2_ardour/splash.cc | 26 ++ gtk2_ardour/splash.h | 2 +- gtk2_ardour/stereo_panner.cc | 74 ++++-- gtk2_ardour/time_axis_view.cc | 4 +- gtk2_ardour/time_axis_view.h | 2 +- gtk2_ardour/verbose_cursor.cc | 1 + gtk2_ardour/visual_time_axis.cc | 2 +- gtk2_ardour/visual_time_axis.h | 2 +- gtk2_ardour/wscript | 1 + libs/ardour/ardour/audio_buffer.h | 1 + libs/ardour/ardour/audioplaylist.h | 3 +- libs/ardour/ardour/audioregion.h | 14 +- libs/ardour/ardour/lv2_plugin.h | 6 +- libs/ardour/ardour/mtdm.h | 10 +- libs/ardour/ardour/panner.h | 5 + libs/ardour/ardour/playlist.h | 7 +- libs/ardour/ardour/region_factory.h | 8 +- libs/ardour/ardour/uri_map.h | 28 +- libs/ardour/ardour/utils.h | 1 - libs/ardour/audio_diskstream.cc | 2 + libs/ardour/audio_playlist.cc | 244 +++++++++++++----- libs/ardour/audioregion.cc | 143 ++++++---- libs/ardour/buffer_set.cc | 2 +- libs/ardour/file_source.cc | 9 +- libs/ardour/lv2_plugin.cc | 12 +- libs/ardour/mtdm.cc | 2 +- libs/ardour/panner.cc | 15 ++ libs/ardour/playlist.cc | 23 +- libs/ardour/port_insert.cc | 8 +- libs/ardour/region_factory.cc | 15 +- libs/ardour/session.cc | 12 +- libs/ardour/session_state.cc | 2 +- libs/ardour/test/mtdm_test.cc | 29 +++ libs/ardour/test/mtdm_test.h | 17 ++ libs/ardour/track.cc | 10 +- libs/ardour/uri_map.cc | 126 ++++----- libs/ardour/utils.cc | 52 ---- libs/ardour/wscript | 20 +- libs/evoral/evoral/ControlList.hpp | 4 + libs/evoral/evoral/Range.hpp | 69 +++-- libs/evoral/test/RangeTest.cpp | 63 +++++ libs/evoral/test/RangeTest.hpp | 5 + libs/gtkmm2ext/gtkmm2ext/keyboard.h | 2 + libs/gtkmm2ext/gtkmm2ext/pixfader.h | 20 +- libs/gtkmm2ext/gtkmm2ext/slider_controller.h | 4 +- libs/gtkmm2ext/keyboard.cc | 7 +- libs/gtkmm2ext/motionfeedback.cc | 21 +- libs/gtkmm2ext/pixfader.cc | 63 +++-- libs/gtkmm2ext/slider_controller.cc | 19 +- libs/midi++2/midi++/midnam_patch.h | 5 +- libs/midi++2/midi++/port.h | 4 +- libs/panners/2in2out/panner_2in2out.cc | 13 + libs/panners/2in2out/panner_2in2out.h | 1 + libs/pbd/filesystem.cc | 33 +++ libs/pbd/pbd/filesystem.h | 4 + libs/pbd/test/filesystem_test.cc | 35 +++ libs/pbd/test/filesystem_test.h | 14 + libs/pbd/wscript | 1 + libs/surfaces/mackie/device_info.cc | 15 ++ libs/surfaces/mackie/device_info.h | 2 + .../mackie/mackie_control_protocol.cc | 74 +++--- .../surfaces/mackie/mackie_control_protocol.h | 5 +- libs/surfaces/mackie/mcp_buttons.cc | 6 +- libs/surfaces/mackie/meter.cc | 28 +- libs/surfaces/mackie/meter.h | 5 +- libs/surfaces/mackie/strip.cc | 34 ++- libs/surfaces/mackie/strip.h | 4 + libs/surfaces/mackie/surface.cc | 89 ++++--- libs/surfaces/mackie/surface.h | 9 +- mcp/bcf2000.device | 14 + mcp/bcf2000_basic_edit_mix.profile | 47 ++++ mcp/cmc.device | 1 - mcp/mcpro.device | 1 - mcp/mcproxt.device | 1 - mcp/nucleus.device | 1 - tools/linux_packaging/build | 14 +- tools/resample_session.pl | 16 +- 131 files changed, 2056 insertions(+), 996 deletions(-) create mode 100644 gtk2_ardour/icons/fader_belt_desensitised.png create mode 100644 gtk2_ardour/icons/fader_belt_h_desensitised.png create mode 100644 gtk2_ardour/icons/fader_belt_h_thin_desensitised.png create mode 100644 gtk2_ardour/main_clock.cc create mode 100644 gtk2_ardour/main_clock.h create mode 100644 libs/ardour/test/mtdm_test.cc create mode 100644 libs/ardour/test/mtdm_test.h create mode 100644 libs/pbd/test/filesystem_test.cc create mode 100644 libs/pbd/test/filesystem_test.h create mode 100644 mcp/bcf2000.device create mode 100644 mcp/bcf2000_basic_edit_mix.profile diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 6236bcd5dc..c210926b55 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -89,6 +89,7 @@ typedef uint64_t microseconds_t; #include "gui_thread.h" #include "keyboard.h" #include "location_ui.h" +#include "main_clock.h" #include "missing_file_dialog.h" #include "missing_plugin_dialog.h" #include "mixer_ui.h" @@ -123,15 +124,13 @@ sigc::signal ARDOUR_UI::RapidScreenUpdate; sigc::signal ARDOUR_UI::SuperRapidScreenUpdate; sigc::signal ARDOUR_UI::Clock; -bool could_be_a_valid_path (const std::string& path); - ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp) , gui_object_state (new GUIObjectState) - , primary_clock (new AudioClock (X_("primary"), false, X_("transport"), true, true, false, true)) - , secondary_clock (new AudioClock (X_("secondary"), false, X_("secondary"), true, true, false, true)) + , primary_clock (new MainClock (X_("primary"), false, X_("transport"), true, true, true, false, true)) + , secondary_clock (new MainClock (X_("secondary"), false, X_("secondary"), true, true, false, false, true)) /* big clock */ @@ -2845,9 +2844,8 @@ ARDOUR_UI::show_splash () void ARDOUR_UI::hide_splash () { - if (splash) { - splash->hide(); - } + delete splash; + splash = 0; } void diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 79bb130a38..a1c6260ccf 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -83,6 +83,7 @@ class ButtonJoiner; class ConnectionEditor; class KeyEditor; class LocationUIWindow; +class MainClock; class Mixer_UI; class PublicEditor; class RCOptionEditor; @@ -194,8 +195,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr GUIObjectState* gui_object_state; - AudioClock* primary_clock; - AudioClock* secondary_clock; + MainClock* primary_clock; + MainClock* secondary_clock; void focus_on_clock (); TimeInfoBox* time_info_box; diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 847aaf1499..32880a2f0b 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -47,6 +47,7 @@ #include "audio_clock.h" #include "actions.h" #include "button_joiner.h" +#include "main_clock.h" #include "utils.h" #include "theme_manager.h" #include "midi_tracer.h" diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index 2dd48b71d1..b5bd622ae5 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -35,6 +35,7 @@ #include "gui_thread.h" #include "keyeditor.h" #include "location_ui.h" +#include "main_clock.h" #include "midi_tracer.h" #include "mixer_ui.h" #include "public_editor.h" diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index bd99be4ff2..394b1c5470 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -55,6 +55,7 @@ #include "window_proxy.h" #include "global_port_matrix.h" #include "location_ui.h" +#include "main_clock.h" #include diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index 19e7106378..f2f7d397df 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -38,6 +38,7 @@ #include "actions.h" #include "gui_thread.h" #include "public_editor.h" +#include "main_clock.h" #include "i18n.h" diff --git a/gtk2_ardour/au_pluginui.mm b/gtk2_ardour/au_pluginui.mm index 43378aed3e..359b32478a 100644 --- a/gtk2_ardour/au_pluginui.mm +++ b/gtk2_ardour/au_pluginui.mm @@ -57,20 +57,23 @@ static const gchar* _automation_mode_strings[] = { self = [ super init ]; if (self) { - plugin_ui = apluginui; - cocoa_parent = cp; + plugin_ui = apluginui; top_level_parent = tlp; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(cocoaParentActivationHandler:) - name:NSWindowDidBecomeMainNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(cocoaParentBecameKeyHandler:) - name:NSWindowDidBecomeKeyNotification - object:nil]; - } + + if (cp) { + cocoa_parent = cp; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(cocoaParentActivationHandler:) + name:NSWindowDidBecomeMainNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(cocoaParentBecameKeyHandler:) + name:NSWindowDidBecomeKeyNotification + object:nil]; + } + } return self; } @@ -103,7 +106,7 @@ static const gchar* _automation_mode_strings[] = { - (void)auViewResized:(NSNotification *)notification; { - (void) notification; + (void) notification; // stop complaints about unusued argument plugin_ui->cocoa_view_resized(); } @@ -192,11 +195,13 @@ AUPluginUI::AUPluginUI (boost::shared_ptr insert) AUPluginUI::~AUPluginUI () { + if (_notify) { + [[NSNotificationCenter defaultCenter] removeObserver:_notify]; + } + if (cocoa_parent) { NSWindow* win = get_nswindow(); - [[NSNotificationCenter defaultCenter] removeObserver:_notify]; [win removeChildWindow:cocoa_parent]; - } #ifdef WITH_CARBON @@ -302,6 +307,7 @@ AUPluginUI::create_cocoa_view () string_compose ( "based on %1, there are %2 cocoa UI classes\n", dataSize, numberOfClasses)); cocoaViewInfo = (AudioUnitCocoaViewInfo *)malloc(dataSize); + if(AudioUnitGetProperty(*au->get_au(), kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, @@ -315,8 +321,8 @@ AUPluginUI::create_cocoa_view () factoryClassName = (NSString *)cocoaViewInfo->mCocoaAUViewClass[0]; DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("the factory name is %1 bundle is %2\n", - factoryClassName, CocoaViewBundlePath)); - + [factoryClassName UTF8String], CocoaViewBundlePath)); + } else { DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("No cocoaUI property cocoaViewInfo = %1\n", cocoaViewInfo)); @@ -328,8 +334,6 @@ AUPluginUI::create_cocoa_view () } } - NSRect crect = { { 0, 0 }, { 1, 1} }; - // [A] Show custom UI if view has it if (CocoaViewBundlePath && factoryClassName) { @@ -354,8 +358,8 @@ AUPluginUI::create_cocoa_view () return -1; } // make a factory - id factoryInstance = [[[factoryClass alloc] init] autorelease]; - if (factoryInstance == nil) { + id factory = [[[factoryClass alloc] init] autorelease]; + if (factory == nil) { error << _("AUPluginUI: Could not create an instance of the AU view factory") << endmsg; return -1; } @@ -363,7 +367,7 @@ AUPluginUI::create_cocoa_view () DEBUG_TRACE (DEBUG::AudioUnits, "got a factory instance\n"); // make a view - au_view = [factoryInstance uiViewForAudioUnit:*au->get_au() withSize:crect.size]; + au_view = [factory uiViewForAudioUnit:*au->get_au() withSize:NSZeroSize]; DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("view created @ %1\n", au_view)); @@ -389,24 +393,10 @@ AUPluginUI::create_cocoa_view () [(AUGenericView *)au_view setShowsExpertParameters:YES]; } - // watch for size changes of the view - - [[NSNotificationCenter defaultCenter] addObserver:_notify - selector:@selector(auViewResized:) name:NSViewBoundsDidChangeNotification - object:au_view]; - - - [[NSNotificationCenter defaultCenter] addObserver:_notify - selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification - object:au_view]; - - // Get the size of the new AU View's frame + // Get the initial size of the new AU View's frame - NSRect packFrame; - packFrame = [au_view frame]; - prefwidth = packFrame.size.width; - prefheight = packFrame.size.height; - low_box.set_size_request (prefwidth, prefheight); + NSRect rect = [au_view frame]; + low_box.set_size_request (rect.size.width, rect.size.height); return 0; } @@ -414,10 +404,35 @@ AUPluginUI::create_cocoa_view () void AUPluginUI::cocoa_view_resized () { - NSRect packFrame = [au_view frame]; - prefwidth = packFrame.size.width; - prefheight = packFrame.size.height; - low_box.set_size_request (prefwidth, prefheight); + GtkRequisition topsize = top_box.size_request (); + NSWindow* window = get_nswindow (); + NSSize oldContentSize= [window contentRectForFrameRect:[window frame]].size; + NSSize newContentSize= [au_view frame].size; + NSRect windowFrame= [window frame]; + + oldContentSize.height -= topsize.height; + + float dy = oldContentSize.height - newContentSize.height; + float dx = oldContentSize.width - newContentSize.width; + + windowFrame.origin.y += dy; + windowFrame.origin.x += dx; + windowFrame.size.height -= dy; + windowFrame.size.width -= dx; + + [[NSNotificationCenter defaultCenter] removeObserver:_notify + name:NSViewFrameDidChangeNotification + object:au_view]; + + NSUInteger old_auto_resize = [au_view autoresizingMask]; + + [au_view setAutoresizingMask:NSViewNotSizable]; + [window setFrame:windowFrame display:YES]; + [au_view setAutoresizingMask:old_auto_resize]; + + [[NSNotificationCenter defaultCenter] addObserver:_notify + selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification + object:au_view]; } int @@ -597,7 +612,15 @@ AUPluginUI::parent_cocoa_window () NSPoint origin = { 0, a.height }; [au_view setFrameOrigin:origin]; - [view addSubview:au_view]; + [view addSubview:au_view positioned:NSWindowBelow relativeTo:nil]; + + // watch for size changes of the view + + _notify = [ [NotificationObject alloc] initWithPluginUI:this andCocoaParent:nil andTopLevelParent:win ]; + + [[NSNotificationCenter defaultCenter] addObserver:_notify + selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification + object:au_view]; return 0; } diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index e20dfffcd8..dbe7f03a14 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -61,7 +61,8 @@ const double AudioClock::x_leading_padding = 6.0; AudioClock::AudioClock (const string& clock_name, bool transient, const string& widget_name, bool allow_edit, bool follows_playhead, bool duration, bool with_info) - : _name (clock_name) + : ops_menu (0) + , _name (clock_name) , is_transient (transient) , is_duration (duration) , editable (allow_edit) @@ -71,7 +72,6 @@ AudioClock::AudioClock (const string& clock_name, bool transient, const string& , layout_x_offset (0) , em_width (0) , _edit_by_click_field (false) - , ops_menu (0) , editing_attr (0) , foreground_attr (0) , first_height (0) diff --git a/gtk2_ardour/audio_clock.h b/gtk2_ardour/audio_clock.h index 33b486a8f1..ed96ac43d4 100644 --- a/gtk2_ardour/audio_clock.h +++ b/gtk2_ardour/audio_clock.h @@ -87,6 +87,9 @@ class AudioClock : public CairoWidget, public ARDOUR::SessionHandlePtr protected: void render (cairo_t*); + virtual void build_ops_menu (); + Gtk::Menu *ops_menu; + private: Mode _mode; std::string _name; @@ -101,8 +104,6 @@ class AudioClock : public CairoWidget, public ARDOUR::SessionHandlePtr int em_width; bool _edit_by_click_field; - Gtk::Menu *ops_menu; - Glib::RefPtr _layout; Glib::RefPtr _left_layout; Glib::RefPtr _right_layout; @@ -197,8 +198,6 @@ class AudioClock : public CairoWidget, public ARDOUR::SessionHandlePtr framepos_t frames_from_minsec_string (const std::string&) const; framepos_t frames_from_audioframes_string (const std::string&) const; - void build_ops_menu (); - void session_configuration_changed (std::string); Field index_to_field () const; diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 48f1f6b8e5..6e837b1626 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -199,13 +199,8 @@ AudioRegionView::init (Gdk::Color const & basic_color, bool wfd) if (!Profile->get_sae()) { gain_line.reset (new AudioRegionGainLine (line_name, *this, *group, audio_region()->envelope())); } - - if (Config->get_show_region_gain()) { - gain_line->show (); - } else { - gain_line->hide (); - } - + + update_envelope_visibility (); gain_line->reset (); set_height (trackview.current_height()); @@ -499,9 +494,7 @@ AudioRegionView::set_height (gdouble height) if ((height/wcnt) < NAME_HIGHLIGHT_THRESH) { gain_line->hide (); } else { - if (Config->get_show_region_gain ()) { - gain_line->show (); - } + update_envelope_visibility (); } gain_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE) - 2); @@ -663,7 +656,8 @@ AudioRegionView::reset_fade_out_shape () void AudioRegionView::reset_fade_out_shape_width (framecnt_t width) { - if (dragging()) { + if (dragging() && audio_region()->fade_out_is_xfade()) { + /* we hide xfades while dragging regions */ return; } @@ -857,20 +851,20 @@ AudioRegionView::temporarily_hide_envelope () void AudioRegionView::unhide_envelope () { - if (gain_line) { - gain_line->show (); - } + update_envelope_visibility (); } void -AudioRegionView::set_envelope_visible (bool yn) +AudioRegionView::update_envelope_visibility () { - if (gain_line) { - if (yn) { - gain_line->show (); - } else { - gain_line->hide (); - } + if (!gain_line) { + return; + } + + if (Config->get_show_region_gain() || trackview.editor().current_mouse_mode() == Editing::MouseGain) { + gain_line->add_visibility (AutomationLine::Line); + } else { + gain_line->hide (); } } @@ -1035,7 +1029,7 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev) /* don't create points that can't be seen */ - set_envelope_visible (true); + update_envelope_visibility (); x = ev->button.x; y = ev->button.y; @@ -1222,9 +1216,9 @@ AudioRegionView::entered (bool internal_editing) { trackview.editor().set_current_trimmable (_region); trackview.editor().set_current_movable (_region); - - if (gain_line && Config->get_show_region_gain ()) { - gain_line->show_all_control_points (); + + if (gain_line && trackview.editor().current_mouse_mode() == Editing::MouseGain) { + gain_line->add_visibility (AutomationLine::ControlPoints); } if (fade_in_handle && !internal_editing) { @@ -1239,8 +1233,8 @@ AudioRegionView::exited () trackview.editor().set_current_trimmable (boost::shared_ptr()); trackview.editor().set_current_movable (boost::shared_ptr()); - if (gain_line) { - gain_line->hide_all_but_selected_control_points (); + if (gain_line && trackview.editor().current_mouse_mode() == Editing::MouseGain) { + gain_line->remove_visibility (AutomationLine::ControlPoints); } if (fade_in_handle) { diff --git a/gtk2_ardour/audio_region_view.h b/gtk2_ardour/audio_region_view.h index 5e41c382bb..773e136952 100644 --- a/gtk2_ardour/audio_region_view.h +++ b/gtk2_ardour/audio_region_view.h @@ -79,7 +79,7 @@ class AudioRegionView : public RegionView void temporarily_hide_envelope (); ///< Dangerous! void unhide_envelope (); ///< Dangerous! - void set_envelope_visible (bool); + void update_envelope_visibility (); void set_waveform_visible (bool yn); void set_waveform_shape (ARDOUR::WaveformShape); void set_waveform_scale (ARDOUR::WaveformScale); @@ -198,6 +198,8 @@ class AudioRegionView : public RegionView void transients_changed(); + AutomationLine::VisibleAspects automation_line_visibility () const; + private: void setup_fade_handle_positions (); diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 975281440a..744ec806d4 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -42,7 +42,6 @@ #include "time_axis_view.h" #include "point_selection.h" #include "automation_time_axis.h" -#include "public_editor.h" #include "ardour/event_type_map.h" #include "ardour/session.h" @@ -77,11 +76,11 @@ AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanv _our_time_converter = true; } - points_visible = false; + _visible = Line; + update_pending = false; _uses_gain_mapping = false; no_draw = false; - _visible = true; _is_boolean = false; terminal_points_can_slide = true; _height = 0; @@ -136,27 +135,36 @@ AutomationLine::queue_reset () void AutomationLine::show () { - if (alist->interpolation() != AutomationList::Discrete) { - line->show(); + if (_visible & Line) { + if (alist->interpolation() != AutomationList::Discrete) { + line->show(); + } else { + line->hide (); + } + } else { + line->hide(); } - if (points_visible) { + if (_visible & ControlPoints) { for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->set_visible (true); (*i)->show (); } + } else if (_visible & SelectedControlPoints) { + for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->set_visible ((*i)->get_selected()); + } + } else { + for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->set_visible (false); + } } - - _visible = true; } void AutomationLine::hide () { - line->hide(); - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - (*i)->hide(); - } - _visible = false; + set_visibility (VisibleAspects (0)); } double @@ -238,7 +246,7 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y) y = min (1.0, y); y = _height - (y * _height); - double const x = trackview.editor().frame_to_unit (_time_converter->to((*cp.model())->when) - _offset); + double const x = trackview.editor().frame_to_unit_unrounded (_time_converter->to((*cp.model())->when) - _offset); trackview.editor().session()->begin_reversible_command (_("automation event move")); trackview.editor().session()->add_command ( @@ -457,7 +465,7 @@ AutomationLine::start_drag_common (double x, float fraction) * @return x position and y fraction that were actually used (once clamped). */ pair -AutomationLine::drag_motion (double x, float fraction, bool ignore_x, bool with_push) +AutomationLine::drag_motion (double const x, float fraction, bool ignore_x, bool with_push) { /* setup the points that are to be moved this time round */ list points = _drag_points; @@ -469,49 +477,51 @@ AutomationLine::drag_motion (double x, float fraction, bool ignore_x, bool with_ double dx = ignore_x ? 0 : (x - _drag_x); double dy = fraction - _last_drag_fraction; - /* find x limits */ - ControlPoint* before = 0; - ControlPoint* after = 0; - - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - if ((*i)->get_x() < points.front()->get_x()) { - before = *i; - } - if ((*i)->get_x() > points.back()->get_x() && after == 0) { - after = *i; - } - } - - double const before_x = before ? before->get_x() : 0; - double const after_x = after ? after->get_x() : DBL_MAX; - - /* clamp x */ for (list::iterator i = points.begin(); i != points.end(); ++i) { + /* Find the points that aren't being moved before and after + this one on the control_points list + */ - if ((*i)->can_slide() && !ignore_x) { + ControlPoint* before = 0; + ControlPoint* after = 0; - /* clamp min x */ - double const a = (*i)->get_x() + dx; - double const b = before_x + 1; - if (a < b) { - dx += b - a; + ControlPoint* last = 0; + for (vector::iterator j = control_points.begin(); j != control_points.end(); ++j) { + + if (*j == *i) { + + before = last; + + vector::iterator k = j; + + /* Next point */ + ++k; + + /* Now move past any points that are being moved this time */ + while (find (points.begin(), points.end(), *k) != points.end() && k != control_points.end ()) { + ++k; + } + + if (k != control_points.end()) { + after = *k; + } + break; } - /* clamp max x */ - if (after) { - - if (after_x - before_x < 2) { - /* after and before are very close, so just leave this alone */ - dx = 0; - } else { - double const a = (*i)->get_x() + dx; - double const b = after_x - 1; - if (a > b) { - dx -= a - b; - } - } + if (find (points.begin(), points.end(), *j) == points.end ()) { + /* This point isn't being moved, so it's the `last' point we've seen */ + last = *j; } } + + /* Clamp dx for this point */ + double const before_x = before ? before->get_x() : 0; + double const after_x = after ? after->get_x() : DBL_MAX; + + double tx = (*i)->get_x() + dx; + tx = max (tx, before_x); + tx = min (tx, after_x); + dx = tx - (*i)->get_x (); } /* clamp y */ @@ -527,7 +537,7 @@ AutomationLine::drag_motion (double x, float fraction, bool ignore_x, bool with_ pair const clamped (_drag_x + dx, _last_drag_fraction + dy); _drag_distance += dx; - _drag_x = x; + _drag_x += dx; _last_drag_fraction = fraction; for (list::iterator i = _drag_points.begin(); i != _drag_points.end(); ++i) { @@ -548,7 +558,9 @@ AutomationLine::drag_motion (double x, float fraction, bool ignore_x, bool with_ } _drag_had_movement = true; - did_push = with_push; + if (with_push) { + did_push = with_push; + } return clamped; } @@ -597,7 +609,7 @@ AutomationLine::sync_model_with_view_point (ControlPoint& cp, framecnt_t distanc /* if xval has not changed, set it directly from the model to avoid rounding errors */ - if (view_x == trackview.editor().frame_to_unit (_time_converter->to ((*cp.model())->when)) - _offset) { + if (view_x == trackview.editor().frame_to_unit_unrounded (_time_converter->to ((*cp.model())->when)) - _offset) { view_x = (*cp.model())->when - _offset; } else { view_x = trackview.editor().unit_to_frame (view_x); @@ -609,11 +621,6 @@ AutomationLine::sync_model_with_view_point (ControlPoint& cp, framecnt_t distanc view_to_model_coord_y (view_y); alist->modify (cp.model(), view_x, view_y); - - if (did_push) { - /* move all points after cp by the same distance */ - alist->slide (cp.model()++, _time_converter->from (distance)); - } } bool @@ -623,7 +630,7 @@ AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_ ControlPoint *acp = 0; double unit_xval; - unit_xval = trackview.editor().frame_to_unit (xval); + unit_xval = trackview.editor().frame_to_unit_unrounded (xval); for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { @@ -807,7 +814,7 @@ AutomationLine::reset_callback (const Evoral::ControlList& events) * zoom and scroll into account). */ - tx = trackview.editor().frame_to_unit (tx); + tx = trackview.editor().frame_to_unit_unrounded (tx); /* convert from canonical view height (0..1.0) to actual * height coordinates (using X11's top-left rooted system) @@ -896,44 +903,31 @@ AutomationLine::set_list (boost::shared_ptr list) } void -AutomationLine::show_all_control_points () +AutomationLine::add_visibility (VisibleAspects va) { - if (_is_boolean) { - // show the line but don't allow any control points - return; - } - - points_visible = true; - - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - if (!(*i)->visible()) { - (*i)->show (); - (*i)->set_visible (true); - } - } + _visible = VisibleAspects (_visible | va); + show (); } void -AutomationLine::hide_all_but_selected_control_points () +AutomationLine::set_visibility (VisibleAspects va) { - if (alist->interpolation() == AutomationList::Discrete) { - return; - } + _visible = va; + show (); +} - points_visible = false; - - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - if (!(*i)->get_selected()) { - (*i)->set_visible (false); - } - } +void +AutomationLine::remove_visibility (VisibleAspects va) +{ + _visible = VisibleAspects (_visible & ~va); + show (); } void AutomationLine::track_entered() { if (alist->interpolation() != AutomationList::Discrete) { - show_all_control_points(); + add_visibility (ControlPoints); } } @@ -941,7 +935,7 @@ void AutomationLine::track_exited() { if (alist->interpolation() != AutomationList::Discrete) { - hide_all_but_selected_control_points(); + remove_visibility (ControlPoints); } } @@ -1012,11 +1006,10 @@ void AutomationLine::interpolation_changed (AutomationList::InterpolationStyle style) { if (style == AutomationList::Discrete) { - show_all_control_points(); + set_visibility (ControlPoints); line->hide(); } else { - hide_all_but_selected_control_points(); - line->show(); + set_visibility (Line); } } @@ -1060,13 +1053,11 @@ AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, dou /* finally, control visibility */ - if (_visible && points_visible) { + if (_visible & ControlPoints) { control_points[view_index]->show (); control_points[view_index]->set_visible (true); } else { - if (!points_visible) { - control_points[view_index]->set_visible (false); - } + control_points[view_index]->set_visible (false); } } diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index bee78c4ef5..b758dfb557 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -57,6 +57,12 @@ namespace Gnome { class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible { public: + enum VisibleAspects { + Line = 0x1, + ControlPoints = 0x2, + SelectedControlPoints = 0x4 + }; + AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Group&, boost::shared_ptr, Evoral::TimeConverter* converter = 0); @@ -85,17 +91,20 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible uint32_t npoints() const { return control_points.size(); } std::string name() const { return _name; } - bool visible() const { return _visible; } + bool visible() const { return _visible != VisibleAspects(0); } guint32 height() const { return _height; } void set_line_color (uint32_t); uint32_t get_line_color() const { return _line_color; } - void show (); - void hide (); - void set_height (guint32); - void set_uses_gain_mapping (bool yn); - bool get_uses_gain_mapping () const { return _uses_gain_mapping; } + void set_visibility (VisibleAspects); + void add_visibility (VisibleAspects); + void remove_visibility (VisibleAspects); + + void hide (); + void set_height (guint32); + void set_uses_gain_mapping (bool yn); + bool get_uses_gain_mapping () const { return _uses_gain_mapping; } TimeAxisView& trackview; @@ -113,9 +122,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible void set_list(boost::shared_ptr list); boost::shared_ptr the_list() const { return alist; } - void show_all_control_points (); - void hide_all_but_selected_control_points (); - void track_entered(); void track_exited(); @@ -157,13 +163,14 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible /** true if _time_converter belongs to us (ie we should delete it on destruction) */ bool _our_time_converter; - bool _visible : 1; + VisibleAspects _visible; + bool _uses_gain_mapping : 1; bool terminal_points_can_slide : 1; bool update_pending : 1; bool no_draw : 1; bool _is_boolean : 1; - bool points_visible : 1; + /** true if we did a push at any point during the current drag */ bool did_push; ArdourCanvas::Group& _parent_group; @@ -195,6 +202,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible */ ARDOUR::framecnt_t _offset; + void show (); void reset_line_coords (ControlPoint&); void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t); double control_point_box_size (); diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index 0fb22b05bc..5c9fc767df 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -89,8 +89,7 @@ AutomationRegionView::create_line (boost::shared_ptr lis &_source_relative_time_converter)); _line->set_colors(); _line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE)); - _line->show(); - _line->show_all_control_points(); + _line->set_visibility (AutomationLine::VisibleAspects (AutomationLine::Line|AutomationLine::ControlPoints)); _line->set_maximum_time (_region->length()); _line->set_offset (_region->start ()); } diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index e0d53e1707..7c9f38aa0f 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -730,7 +730,7 @@ AutomationTimeAxisView::add_line (boost::shared_ptr line) /* pick up the current state */ automation_state_changed (); - line->show(); + line->add_visibility (AutomationLine::Line); } void diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index d883b189c0..f1a9a8bd57 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -86,7 +86,7 @@ class AutomationTimeAxisView : public TimeAxisView { void get_selectables (ARDOUR::framepos_t start, ARDOUR::framepos_t end, double top, double bot, std::list&); void get_inverted_selectables (Selection&, std::list& results); - void show_timestretch (framepos_t /*start*/, framepos_t /*end*/) {} + void show_timestretch (framepos_t /*start*/, framepos_t /*end*/, int /*layers*/, int /*layer*/) {} void hide_timestretch () {} /* editing operations */ diff --git a/gtk2_ardour/canvas_patch_change.cc b/gtk2_ardour/canvas_patch_change.cc index 7f7413e39e..b4da0d5b46 100644 --- a/gtk2_ardour/canvas_patch_change.cc +++ b/gtk2_ardour/canvas_patch_change.cc @@ -213,6 +213,9 @@ CanvasPatchChange::on_event (GdkEvent* ev) _region.patch_left (this); break; + case GDK_BUTTON_RELEASE: + return true; + default: break; } diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index d19359a0f3..e17c8046c4 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -281,6 +281,7 @@ Editor::Editor () , _region_selection_change_updates_region_list (true) , _following_mixer_selection (false) + , _control_point_toggled_on_press (false) { constructed = false; @@ -4526,16 +4527,15 @@ Editor::get_regions_from_selection () * the edit point is `mouse' and the mouse is over an unselected * region. In this case, start with just that region. * - * Then, make an initial track list of the tracks that these - * regions are on, and if the edit point is not `mouse', add the - * selected tracks. + * Then, add equivalent regions in active edit groups to the region list. * - * Look at this track list and add any other tracks that are on the - * same active edit-enabled route group as one of the initial tracks. + * Then, search the list of selected tracks to find any selected tracks which + * do not contain regions already in the region list. If there are no selected + * tracks and 'No Selection = All Tracks' is active, search all tracks rather + * than just the selected. * - * Finally take the initial region list and add any regions that are - * under the edit point on one of the tracks on the track list to get - * the returned region list. + * Add any regions that are under the edit point on these tracks to get the + * returned region list. * * The rationale here is that the mouse edit point is special in that * its position describes both a time and a track; the other edit @@ -4563,22 +4563,40 @@ Editor::get_regions_from_selection_and_edit_point () tracks = selection->tracks; } - /* Add any other tracks that have regions that are in the same + /* Add any other regions that are in the same edit-activated route group as one of our regions. */ - for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) { + regions = get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id); + framepos_t const where = get_preferred_edit_position (); - RouteGroup* g = (*i)->get_time_axis_view().route_group (); - - if (g && g->is_active() && g->is_edit()) { - tracks.add (axis_views_from_routes (g->route_list())); - } + if (_route_groups->all_group_active_button().get_active() && tracks.empty()) { + /* tracks is empty (no track selected), and 'No Selection = All Tracks' + * is enabled, so consider all tracks + */ + tracks = track_views; } if (!tracks.empty()) { - /* now find regions that are at the edit position on those tracks */ - framepos_t const where = get_preferred_edit_position (); - get_regions_at (regions, where, tracks); + /* now search the selected tracks for tracks which don't + already contain regions to be acted upon, and get regions at + the edit point on those tracks too. + */ + TrackViewList tracks_without_relevant_regions; + + for (TrackViewList::iterator t = tracks.begin (); t != tracks.end (); ++t) { + if (!regions.involves (**t)) { + /* there are no equivalent regions on this track */ + tracks_without_relevant_regions.push_back (*t); + } + } + + if (!tracks_without_relevant_regions.empty()) { + /* there are some selected tracks with neither selected + * regions or their equivalents: act upon all regions in + * those tracks + */ + get_regions_at (regions, where, tracks_without_relevant_regions); + } } return regions; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 2e203e9c75..0dd162ca20 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -239,6 +239,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD return rint ((double) frame / (double) frames_per_unit); } + double frame_to_unit_unrounded (framepos_t frame) const { + return frame / frames_per_unit; + } + double frame_to_unit (double frame) const { return rint (frame / frames_per_unit); } @@ -1951,8 +1955,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD gint left_automation_track (); void reset_canvas_action_sensitivity (bool); - void set_gain_envelope_visibility (bool); - void set_region_gain_visibility (RegionView*, bool); + void set_gain_envelope_visibility (); + void set_region_gain_visibility (RegionView*); void toggle_gain_envelope_active (); void reset_region_gain_envelopes (); @@ -2092,6 +2096,9 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void toggle_sound_midi_notes (); + /** Flag for a bit of a hack wrt control point selection; see set_selected_control_point_from_click */ + bool _control_point_toggled_on_press; + friend class Drag; friend class RegionDrag; friend class RegionMoveDrag; diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 074d51428e..00ea296e61 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -1483,7 +1483,7 @@ Editor::parameter_changed (std::string p) } } } else if (p == "show-region-gain") { - set_gain_envelope_visibility (Config->get_show_region_gain ()); + set_gain_envelope_visibility (); } } diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 97590be1a9..c84b5520b7 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -2818,6 +2818,10 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/) event->button.x + 10, event->button.y + 10); _editor->verbose_cursor()->show (); + + if (!_point->can_slide ()) { + _x_constrained = true; + } } void @@ -2871,7 +2875,7 @@ ControlPointDrag::motion (GdkEvent* event, bool) bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); - _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push); + _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push); _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction)); } @@ -3245,11 +3249,16 @@ void TimeFXDrag::motion (GdkEvent* event, bool) { RegionView* rv = _primary; + StreamView* cv = rv->get_time_axis_view().view (); + + pair const tv = _editor->trackview_by_y_position (grab_y()); + int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; + int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers(); framepos_t const pf = adjusted_current_frame (event); if (pf > rv->region()->position()) { - rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf); + rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer); } show_verbose_cursor_time (pf); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index b5c7635265..1ce02457fc 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -247,61 +247,38 @@ Editor::set_current_movable (boost::shared_ptr m) void Editor::set_canvas_cursor () { - if (_internal_editing) { + switch (mouse_mode) { + case MouseRange: + current_canvas_cursor = _cursors->selector; + break; - switch (mouse_mode) { - case MouseDraw: - current_canvas_cursor = _cursors->midi_pencil; - break; + case MouseObject: + current_canvas_cursor = which_grabber_cursor(); + break; - case MouseObject: - current_canvas_cursor = which_grabber_cursor(); - break; + case MouseDraw: + current_canvas_cursor = _cursors->midi_pencil; + break; - case MouseTimeFX: - current_canvas_cursor = _cursors->midi_resize; - break; + case MouseGain: + current_canvas_cursor = _cursors->cross_hair; + break; - default: - return; + case MouseZoom: + if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { + current_canvas_cursor = _cursors->zoom_out; + } else { + current_canvas_cursor = _cursors->zoom_in; } + break; - } else { + case MouseTimeFX: + current_canvas_cursor = _cursors->time_fx; // just use playhead + break; - switch (mouse_mode) { - case MouseRange: - current_canvas_cursor = _cursors->selector; - break; - - case MouseObject: - current_canvas_cursor = which_grabber_cursor(); - break; - - case MouseDraw: - /* shouldn't be possible, but just cover it anyway ... */ - current_canvas_cursor = _cursors->midi_pencil; - break; - - case MouseGain: - current_canvas_cursor = _cursors->cross_hair; - break; - - case MouseZoom: - if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { - current_canvas_cursor = _cursors->zoom_out; - } else { - current_canvas_cursor = _cursors->zoom_in; - } - break; - - case MouseTimeFX: - current_canvas_cursor = _cursors->time_fx; // just use playhead - break; - - case MouseAudition: - current_canvas_cursor = _cursors->speaker; - break; - } + case MouseAudition: + current_canvas_cursor = _cursors->speaker; + break; } switch (_join_object_range_state) { @@ -473,6 +450,7 @@ Editor::mouse_mode_toggled (MouseMode m) } set_canvas_cursor (); + set_gain_envelope_visibility (); MouseModeChanged (); /* EMIT SIGNAL */ } @@ -583,7 +561,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp (mouse_mode != MouseRange) && (mouse_mode != MouseDraw)) || ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) || - internal_editing()) { + (internal_editing() && mouse_mode != MouseTimeFX)) { return; } @@ -1157,8 +1135,8 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT /* drag notes if we're in internal edit mode */ _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor); return true; - } else if ((!internal_editing() || dynamic_cast (clicked_regionview)) && clicked_regionview) { - /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */ + } else if (clicked_regionview) { + /* do time-FX */ _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event); return true; } diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 7f4c22616e..ec6a6463b7 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -1369,6 +1369,13 @@ Editor::temporal_zoom (gdouble fpu) } nfpu = fpu; + + // Imposing an arbitrary limit to zoom out as too much zoom out produces + // segfaults for lack of memory. If somebody decides this is not high enough I + // believe it can be raisen to higher values but some limit must be in place. + if (nfpu > 8e+08) { + nfpu = 8e+08; + } new_page_size = (framepos_t) floor (_canvas_width * nfpu); half_page_size = new_page_size / 2; @@ -4720,8 +4727,14 @@ Editor::insert_patch_change (bool from_context) const framepos_t p = get_preferred_edit_position (false, from_context); + /* XXX: bit of a hack; use the MIDNAM from the first selected region; + there may be more than one, but the PatchChangeDialog can only offer + one set of patch menus. + */ + MidiRegionView* first = dynamic_cast (rs.front ()); + Evoral::PatchChange empty (0, 0, 0, 0); - PatchChangeDialog d (0, _session, empty, Gtk::Stock::ADD); + PatchChangeDialog d (0, _session, empty, first->model_name(), first->custom_device_mode(), Gtk::Stock::ADD); if (d.run() == RESPONSE_CANCEL) { return; @@ -4851,16 +4864,16 @@ Editor::reset_region_gain_envelopes () } void -Editor::set_region_gain_visibility (RegionView* rv, bool yn) +Editor::set_region_gain_visibility (RegionView* rv) { AudioRegionView* arv = dynamic_cast (rv); if (arv) { - arv->set_envelope_visible (yn); + arv->update_envelope_visibility(); } } void -Editor::set_gain_envelope_visibility (bool yn) +Editor::set_gain_envelope_visibility () { if (!_session) { return; @@ -4869,7 +4882,7 @@ Editor::set_gain_envelope_visibility (bool yn) for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { AudioTimeAxisView* v = dynamic_cast(*i); if (v) { - v->audio_view()->foreach_regionview (sigc::bind (sigc::mem_fun (this, &Editor::set_region_gain_visibility), yn)); + v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility)); } } } @@ -5916,6 +5929,10 @@ Editor::split_region_at_points (boost::shared_ptr r, AnalysisFeatureList plist.add (ARDOUR::Properties::layer, 0); boost::shared_ptr nr = RegionFactory::create (r->sources(), plist, false); + /* because we set annouce to false, manually add the new region to the + RegionFactory map + */ + RegionFactory::map_add (nr); pl->add_region (nr, r->position() + pos); @@ -5940,6 +5957,10 @@ Editor::split_region_at_points (boost::shared_ptr r, AnalysisFeatureList plist.add (ARDOUR::Properties::layer, 0); boost::shared_ptr nr = RegionFactory::create (r->sources(), plist, false); + /* because we set annouce to false, manually add the new region to the + RegionFactory map + */ + RegionFactory::map_add (nr); pl->add_region (nr, r->position() + pos); if (select_new) { diff --git a/gtk2_ardour/editor_regions.cc b/gtk2_ardour/editor_regions.cc index 01b26705f5..c57d07965f 100644 --- a/gtk2_ardour/editor_regions.cc +++ b/gtk2_ardour/editor_regions.cc @@ -47,6 +47,7 @@ #include "utils.h" #include "editor_regions.h" #include "editor_drag.h" +#include "main_clock.h" #include "i18n.h" diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 6e12a03856..eb67fb3631 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -318,19 +318,39 @@ Editor::set_selected_control_point_from_click (bool press, Selection::Operation return false; } - if (!press) { - return true; - } - switch (op) { case Selection::Set: - selection->set (clicked_control_point); + if (press) { + selection->set (clicked_control_point); + } break; case Selection::Add: - selection->add (clicked_control_point); + if (press) { + selection->add (clicked_control_point); + } break; case Selection::Toggle: - selection->toggle (clicked_control_point); + /* This is a bit of a hack; if we Primary-Click-Drag a control + point (for push drag) we want the point we clicked on to be + selected, otherwise we end up confusingly dragging an + unselected point. So here we ensure that the point is selected + after the press, and if we subsequently get a release (meaning no + drag occurred) we set things up so that the toggle has happened. + */ + if (press && !selection->selected (clicked_control_point)) { + /* This is the button press, and the control point is not selected; make it so, + in case this press leads to a drag. Also note that having done this, we don't + need to toggle again on release. + */ + selection->toggle (clicked_control_point); + _control_point_toggled_on_press = true; + } else if (!press && !_control_point_toggled_on_press) { + /* This is the release, and the point wasn't toggled on the press, so do it now */ + selection->toggle (clicked_control_point); + } else { + /* Reset our flag */ + _control_point_toggled_on_press = false; + } break; case Selection::Extend: /* XXX */ diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index 9f4e286f3a..166d29ef2c 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -62,6 +62,7 @@ sigc::signal GainMeterBase::ResetGroupPeakDisplays; GainMeter::MetricPatterns GainMeter::metric_patterns; Glib::RefPtr GainMeter::slider; +Glib::RefPtr GainMeter::slider_desensitised; void @@ -70,10 +71,15 @@ GainMeter::setup_slider_pix () if ((slider = ::get_icon ("fader_belt")) == 0) { throw failed_constructor(); } + + if ((slider_desensitised = ::get_icon ("fader_belt_desensitised")) == 0) { + throw failed_constructor(); + } } GainMeterBase::GainMeterBase (Session* s, const Glib::RefPtr& pix, + const Glib::RefPtr& pix_desensitised, bool horizontal, int fader_length) : gain_adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0.0, 1.0, 0.01, 0.1) @@ -95,11 +101,13 @@ GainMeterBase::GainMeterBase (Session* s, if (horizontal) { gain_slider = manage (new HSliderController (pix, + pix_desensitised, &gain_adjustment, fader_length, false)); } else { gain_slider = manage (new VSliderController (pix, + pix_desensitised, &gain_adjustment, fader_length, false)); @@ -839,7 +847,7 @@ GainMeterBase::on_theme_changed() } GainMeter::GainMeter (Session* s, int fader_length) - : GainMeterBase (s, slider, false, fader_length) + : GainMeterBase (s, slider, slider_desensitised, false, fader_length) , gain_display_box(true, 0) , hbox(true, 2) { diff --git a/gtk2_ardour/gain_meter.h b/gtk2_ardour/gain_meter.h index e706606c76..4536bb691b 100644 --- a/gtk2_ardour/gain_meter.h +++ b/gtk2_ardour/gain_meter.h @@ -65,7 +65,7 @@ namespace Gtk { class GainMeterBase : virtual public sigc::trackable, ARDOUR::SessionHandlePtr { public: - GainMeterBase (ARDOUR::Session*, const Glib::RefPtr& pix, + GainMeterBase (ARDOUR::Session*, const Glib::RefPtr&, const Glib::RefPtr &, bool horizontal, int); virtual ~GainMeterBase (); @@ -231,6 +231,7 @@ class GainMeter : public GainMeterBase, public Gtk::VBox std::vector _types; static Glib::RefPtr slider; + static Glib::RefPtr slider_desensitised; }; #endif /* __ardour_gtk_gain_meter_h__ */ diff --git a/gtk2_ardour/icons/fader_belt_desensitised.png b/gtk2_ardour/icons/fader_belt_desensitised.png new file mode 100644 index 0000000000000000000000000000000000000000..9c7094e03908b32feead4cea20fc258c50dfd8d7 GIT binary patch literal 2949 zcmbVO`#;l-7yr2B61j#)%I21oM?x;iWy&Q+E@g~~@-VjWY`gYmlsib7%nK&}CPAZuIb)umf%p#UKA$M%wiYxL-% z$Hfe`;gR4scXdRSfZ9c!$4aWTi#))@F#zEGol3U=-Y$tt0E1a?sN#~! zyY*Pr%y7{=JK}((MH7BA7wg;|xgy$vku37O>DZydg;kvnv+;&8xMv&r-csY`nHHgC zN*&@rkZ~th(%f7z)f(43zizh_VRB;Qaq*O-a?C~4b`EzhH55CxKN11wzgmMMxR=<$ z%|9kX?&GcaGz&IWfGMja-s^H{+m_Wz-1DnFr(m`*0%w*8_~HFH`JX(oON3v~SvA{e ztY96LZAj~2=TIeYjk7SuA|T&B#nkO-i*uo^Q!C=OMMxIhWTM7Q-a_Ke-zeqhYLWZB zs%1c_(SzK8XVu9z@35bS41Qm3m9`e%(prV0`QB6r)Sg;}rlAujw%A!G@*S*yT>mIm zJoF^_J1R$zh#Kbz3ZFu2j%@;m>$o2rK^`d)(47#Rxn`5jVv^D1(R3-2bGmaE z_`Ji$1$8~)TjN)PGoXlIBiIX-&)o06@vk}YicGMPL)C#_980jsP%Zv=g5P+jNY~Un z?1biO9`ns2@gFPDAc?$c{7b!j_3`(#ZZKW;7 zbfGInW6*OTE$gDrxR&OV>{4vI`(KA9`=N35S?656hnTCZH@ZIs-&LvAuy!8|>NL00yKp#%)@f#WTo^h9pH1%G;U7askYJO^*?5oqVeV&S}Lm3SXQz1FNJF(cJfQx&7s#5sbSxRmy@QIMH8)M$GI+i=^=$iW?XRTR#fCd-FqDl?xgRemZ&Q^I_R z*a&6yc!;mum1YQ<&#)tP^7YNU1`vs9`Dp05DkwVixheYvl}Y>9!gjnbZ?)G*>I)OoK@hajOK@#yJ;F#e~HrN)v_Sf`q`?}TRJFOtViq_=Cz zd!X*0rpvc4dtGgU4gu)vc0!<+su3*spum8kmOi3f4)TI|d$@jh#sG3A^qJrvLc`0s z*LPc36o7Q}bOQM4hb~x;OOE=85Yb#!Cgw{VlWOZor;{x+2e4t;hdv6IaWY;@Wj&PA zo-vT@oz=ZuNOa*}X_~{YhHQ}j{Nz6G9}U1zE3(oq68TGg$9~%*Jmsp$!&U@Mr8kZ8 zuK&t(p7t^ybUD+*@KLx16DNQ_w3QcUONXvam$P$c?FIy@=L?H@h$tAXy)>FWAG$u# zrDA0G>G|l0u2C{*@asG3(Tpn~fqP|Z@RB=a;l5Q}7Qa6y_Uu8M4x0RJNp{lcHn3@Z zai(Mrxek70%AYrtb7RBxQGPe>+Qp+AVC7eP(3FtdO2J-=L7ya`RXwzmyhp!?-OZMe zlx3FHS4Eq4%~|1#Aolqfu16bk1)PI=F@!e`5|%Dyg%caXY8uSV@#MN%PYV3vaAYN$ zyfU%d-a0LX)E66#_8Zl!h(bql3*U!SeG~moZ|gATC?CJ2^2)n!4~hxt;yhTPc@O!> z5F0>3XPe~u>K^w@mj{vh<;2K^#SR5K&)&DP3@WT%=Zj=xz^;u)ZbhcLX$=n32k_~F1% zf4>gnFhnDC`@BGHkLQAbm**pw8>anaYf@N563X)?PGKrjf*$yC;oe=kiW{xbFlrIe zpAcH>hQ07e=1gri(*u>`Wj!Ep(K2VYdX99mS5czAulr-})H_4z8mul(nfo)k!@zq`X>{;oh?8D(oMJjO6B~UyyO;7)NNQoNr&?pMTp2 zhToh03cB`ER;F&Ste!@3EJpJC)q9zl5sH~s{BTBrKIokiZv_P z7)@}QDqssFUoF3%R^%5ls~#o;*RHE^^5@!YzuUm_tJdw*sznb3R>lo9FJ73ekr}6^ zBm~8^k`dYZzV>uZ9$5lPH`wf&{uA|?QI^V6CaTIdQCQHx>0e8|RgK7@?mGWy&eTjL z&t`uG1^c|N&shO8&Sh0?@OI8I=MW1A>!{ZNN`tN11#nqWY)jZeY*MC1;!RG&#=J$Q$ca->!>%9h-oPKifA_KOU9WGHJH}L-ft4ZAG literal 0 HcmV?d00001 diff --git a/gtk2_ardour/icons/fader_belt_h_desensitised.png b/gtk2_ardour/icons/fader_belt_h_desensitised.png new file mode 100644 index 0000000000000000000000000000000000000000..d2952d051eec646cef94e5022df75c80a84f9889 GIT binary patch literal 2372 zcmV-K3A^@*P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyh$ z00IHq<+ddN000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000P|Nkl@aitG#efO_o|GoVEPHV%Ei4!?a)`lgF=!0TZ40Gg)8 zX0y(T;=_BZ#$nsPz*ixODoxU+snPfA@LSt}2imCb%LMLJ-(puD=o*Lq>q~*{J<&8Z zzV_<^8&N;X(`Y8TrpC5^fgRPp0-DC5?_W}TUI$atIBd2r@KufvD)no#4soe8MitO@ zHNGlNo&<@maoB8L(jbk#H4}fK@ow50n`md1dvMj(==+Qll7y)1j~K>XPDV|t+I_?@ z7&!>4d>`cmgh8RMKVlfZ<;2tHuKtK|zspHZ0uuDc;TtF!hpF9f!9vD?!GJUDiotS= z{rBpxeE$5OVoY~k4FDL1Bi={u0PqI+C49j!9PoYZe~bM4+4=nZ1pwjP0|4M~crEmQ zUiqKJc3?S<+@-(r`Sb5QzrU0P1&C{fiEG%G+LJ2al zS0@~%T^}dJFXx>zudQ3zalo1U+a^>Z|%3?%=Tl}U(y#o zbgjxK`d!qwabv_^>wd;3DwN}I;zlO?a-%}H`XNv_v)`f4%)_uV?b65x7~Yj;@?jVP zb(_rmL!@M8Z)I_!?M1uHeMk_stBwBK?7v5U~wBtEYN?A*YE-I5oe8?mK}4|I?Nv7>s+;JCw!6-O0EXdE%ro7}K^WvTPgbi2B!j->xuxqI z1W`Zx)pogEV(OQX3m^pAl>?aViA}dF2S6|m`w&Bkdp)O=@{nZF9sn4|1BB%Ok8&8! z(yoI2yWOFfSXVYalH=C44iI461^b_$zd!`2Yo$iV*JAQ++yfxMe*aob{H}h0s9cc! za&--WnUYx?rCxRoaP9$&luTnu#fJa~#gBd8sQ&Tzn)>io`|r_Td3^kh=PBtDig)fz z=oWI>0lzALI#SsFD9z1`s2Qsm=@s} zgBIBnXF>qo8*zqlpC>Yg3E{*}82JJm66m@tM@%qT5PxkqQ`xfJc=M-e0yQcq+z*!v z_WR>cWY5p*uRK2fN@`Dw5M)WYB(Ad*P&Y|p_L7lU3zIhNH;P@d1pt+KgrzVq=rn!0VM_Ns~wBOQN&&lqCzbO`UEzrNcrz zKRqp|{K|scEthORcm0rOba6v9m({rOul9(wDt-6_RNOT0dAx*FR}pKm~zD(H68 z>ypocB5t;QE_iuM)n4)hpwuE&S;>S?rMMeoPdp%QyC&Hav6-r-eSsaRV8GA1WT32@ z?MrceBiXZ^R8!+ch)6Z{^f^>_W86^0D8-Zc@IS8L+ij@Idgci+R$r$VOuAMtHbi>G z7=J_Qb1;-L?b5A@SiNpkPII$~AYAjHl#ltQ1tW#qr9<@?&q(dk4UUBWkmR|~ytX@- zdp5z3jPq9e@6%uT^yydHRV7e9VmFjk!EV(+sB*JXbSnpH^>Vsb6(Hfng>MzBzSHVr z7pkwj@i3uwx!P$zCsj(8)!lJC6!X!lb)cv|^JYKcXuENdj#b>)Zmg)rI1J++Z&IokBIN1lXNL#H((KUv(X6B$rJ=#{or5cu?Ofz1jKuWYRX=o1ZnKkV?G zlHbWQuOTq3%>K@U`nw=8YDbP4ial!hQMQrn(l&y*pu@^ZEOK zK7?7{o4u1SR-kkyXRb@9#4s~Wxh3*t85p6B%og(v-SDo@tEYH_<-ZfQF9sFq3*Ykj zzc+iz90wXkP!#0p^XoF*X_PSsr*SsV3v6HB2~j_0Z$h8_?`cbJ$z0k$U3!-b_IlTj zgKn|^KK=br{D%lr+?eg>Iar=)YI3=rJ|F4WYAJm9?ls{(O`(REJ^y(RY|i*Y6cPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyh$ z02&9dor_5T000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0008~NklGfl5EkJ1tRL_S5TZFLI;JMl-*l6`zc~{LuX$_vL1LCR{4Om;C zst( z>!%JUc)K;Is(|B2LKw3_^Y$9Z=t*UY4VosBgP@ncw{ilCPQjQBn&u@C |gSYqB zKyngLPXDcW0ad_Zx9^MdkZobm;WV#Fv8?g`TH{r_-H)WE%hCVnpzf1aO@kM{tGqWA^?I39l|`j;#Jle7c*IO;0nRlD7HPWP88V*s%k>xuU!U~#;e?$%V)!Ko3* z=|-Fos6n9p-b$@#=9xJFVdkLV)S_HVmUCR}xk7*JI%rS;lVhv8 z?%#C*dI2kE?Noi$HZ}kOrPSHtUCwc+6%f!Q{d+LS`KiXi29W+`oc(5(OuOR`CY_|i zyqJHhOS$NCGcmXr6u}o~j+gP0(hhW#(ig%g4FdQJ9Ux|TJ^&V;00000NkvXXu0mjf DFMF$` literal 0 HcmV?d00001 diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 463fb70482..3895a96fd6 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2001-2007 Paul Davis + Copyright (C) 2001-2012 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -141,35 +141,14 @@ fixup_bundle_environment (int, char* []) _NSGetExecutablePath (execpath, &pathsz); - std::string dir_path = Glib::path_get_dirname (execpath); std::string path; - const char *cstr = getenv ("PATH"); + std::string exec_dir = Glib::path_get_dirname (execpath); + std::string bundle_dir; + std::string userconfigdir = user_config_directory().to_string(); - /* ensure that we find any bundled executables (e.g. JACK), - and find them before any instances of the same name - elsewhere in PATH - */ + bundle_dir = Glib::path_get_dirname (exec_dir); - path = dir_path; - - /* JACK is often in /usr/local/bin and since Info.plist refuses to - set PATH, we have to force this in order to discover a running - instance of JACK ... - */ - - path += ':'; - path += "/usr/local/bin"; - - if (cstr) { - path += ':'; - path += cstr; - } - setenv ("PATH", path.c_str(), 1); - - export_search_path (dir_path, "ARDOUR_DLL_PATH", "/../lib"); - - path += dir_path; - path += "/../Resources"; + export_search_path (bundle_dir, "ARDOUR_DLL_PATH", "/lib"); /* inside an OS X .app bundle, there is no difference between DATA and CONFIG locations, since OS X doesn't @@ -177,17 +156,13 @@ fixup_bundle_environment (int, char* []) machine-independent shared data. */ - export_search_path (dir_path, "ARDOUR_DATA_PATH", "/../Resources"); - export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/../Resources"); - export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/../Resources"); - - export_search_path (dir_path, "LADSPA_PATH", "/../Plugins"); - export_search_path (dir_path, "VAMP_PATH", "/../lib"); - export_search_path (dir_path, "SUIL_MODULE_DIR", "/../lib"); - - path = dir_path; - path += "/../lib/clearlooks"; - setenv ("GTK_PATH", path.c_str(), 1); + export_search_path (bundle_dir, "ARDOUR_DATA_PATH", "/Resources"); + export_search_path (bundle_dir, "ARDOUR_CONFIG_PATH", "/Resources"); + export_search_path (bundle_dir, "ARDOUR_INSTANT_XML_PATH", "/Resources"); + export_search_path (bundle_dir, "LADSPA_PATH", "/Plugins"); + export_search_path (bundle_dir, "VAMP_PATH", "/lib"); + export_search_path (bundle_dir, "SUIL_MODULE_DIR", "/lib"); + export_search_path (bundle_dir, "GTK_PATH", "/lib/clearlooks"); /* unset GTK_RC_FILES so that we only load the RC files that we define */ @@ -195,12 +170,7 @@ fixup_bundle_environment (int, char* []) unsetenv ("GTK_RC_FILES"); if (!ARDOUR::translations_are_disabled ()) { - - path = dir_path; - path += "/../Resources/locale"; - - localedir = strdup (path.c_str()); - setenv ("GTK_LOCALEDIR", localedir, 1); + export_search_path (bundle_dir, "GTK_LOCALEDIR", "/Resources/locale"); } /* write a pango.rc file and tell pango to use it. we'd love @@ -211,62 +181,35 @@ fixup_bundle_environment (int, char* []) actually exists ... */ - try { - sys::create_directories (user_config_directory ()); - } - catch (const sys::filesystem_error& ex) { - error << _("Could not create user configuration directory") << endmsg; - } - - sys::path pangopath = user_config_directory(); - pangopath /= "pango.rc"; - path = pangopath.to_string(); + if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) { + error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno)) + << endmsg; + return; + } + path = Glib::build_filename (userconfigdir, "pango.rc"); std::ofstream pangorc (path.c_str()); if (!pangorc) { error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; - return; } else { - pangorc << "[Pango]\nModuleFiles="; - - pangopath = dir_path; - pangopath /= ".."; - pangopath /= "Resources"; - pangopath /= "pango.modules"; - - pangorc << pangopath.to_string() << endl; - + pangorc << "[Pango]\nModuleFiles=" + << Glib::build_filename (bundle_dir, "Resources/pango.modules") + << endl; pangorc.close (); setenv ("PANGO_RC_FILE", path.c_str(), 1); } - // gettext charset aliases + // gettext charset aliases XXX do we really need this, since the path + // is totally wrong? setenv ("CHARSETALIASDIR", path.c_str(), 1); - // font config - - path = dir_path; - path += "/../Resources/fonts.conf"; - - setenv ("FONTCONFIG_FILE", path.c_str(), 1); + setenv ("FONTCONFIG_FILE", Glib::build_filename (bundle_dir, "Resources/fonts.conf").c_str(), 1); // GDK Pixbuf loader module file - path = dir_path; - path += "/../Resources/gdk-pixbuf.loaders"; - - setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1); - - if (getenv ("ARDOUR_WITH_JACK")) { - // JACK driver dir - - path = dir_path; - path += "/../lib"; - - setenv ("JACK_DRIVER_DIR", path.c_str(), 1); - } + setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (bundle_dir, "Resources/gdk-pixbuf.loaders").c_str(), 1); } #else @@ -284,9 +227,9 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV")); - Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0])); - Glib::ustring path; - Glib::ustring userconfigdir = user_config_directory().to_string(); + std::string path; + std::string dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0])); + std::string userconfigdir = user_config_directory().to_string(); /* note that this function is POSIX/Linux specific, so using / as a dir separator in this context is just fine. @@ -296,14 +239,11 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc"); export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share"); export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share"); - - export_search_path (dir_path, "LADSPA_PATH", "/../plugins"); + export_search_path (dir_path, "LADSPA_PATH", "/plugins"); export_search_path (dir_path, "VAMP_PATH", "/lib"); export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib"); - path = dir_path; - path += "/lib/clearlooks"; - setenv ("GTK_PATH", path.c_str(), 1); + export_search_path (dir_path, "GTK_PATH", "/lib/clearlooks"); /* unset GTK_RC_FILES so that we only load the RC files that we define */ @@ -311,11 +251,7 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) unsetenv ("GTK_RC_FILES"); if (!ARDOUR::translations_are_disabled ()) { - path = dir_path; - path += "/share/locale"; - - localedir = strdup (path.c_str()); - setenv ("GTK_LOCALEDIR", localedir, 1); + export_search_path (dir_path, "GTK_LOCALEDIR", "/share/locale"); } /* Tell fontconfig where to find fonts.conf. Use the system version @@ -330,8 +266,7 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf"); setenv ("FONTCONFIG_FILE", path.c_str(), 1); - path = Glib::build_filename (dir_path, "etc/fonts"); - setenv ("FONTCONFIG_PATH", "/etc/fonts", 1); + export_search_path (dir_path, "FONTCONFIG_PATH", "/etc/fonts"); } /* write a pango.rc file and tell pango to use it. we'd love @@ -348,18 +283,14 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) return; } - Glib::ustring mpath; - path = Glib::build_filename (userconfigdir, "pango.rc"); - std::ofstream pangorc (path.c_str()); if (!pangorc) { error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; } else { - mpath = Glib::build_filename (userconfigdir, "pango.modules"); - - pangorc << "[Pango]\nModuleFiles="; - pangorc << mpath << endl; + pangorc << "[Pango]\nModuleFiles=" + << Glib::build_filename (userconfigdir, "pango.modules") + << endl; pangorc.close (); } @@ -369,8 +300,7 @@ fixup_bundle_environment (int /*argc*/, char* argv[]) to specify where it lives. */ - mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders"); - setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1); + setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1); } #endif @@ -423,10 +353,6 @@ sigpipe_handler (int /*signal*/) } } -#ifdef HAVE_LV2 -void close_external_ui_windows(); -#endif - #ifdef WINDOWS_VST_SUPPORT extern int windows_vst_gui_init (int* argc, char** argv[]); @@ -534,9 +460,6 @@ int main (int argc, char *argv[]) ARDOUR::cleanup (); pthread_cancel_all (); -#ifdef HAVE_LV2 - close_external_ui_windows(); -#endif return 0; } #ifdef WINDOWS_VST_SUPPORT diff --git a/gtk2_ardour/main_clock.cc b/gtk2_ardour/main_clock.cc new file mode 100644 index 0000000000..cf00e25839 --- /dev/null +++ b/gtk2_ardour/main_clock.cc @@ -0,0 +1,68 @@ +/* + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "ardour/rc_configuration.h" +#include "main_clock.h" +#include "i18n.h" + +using namespace Gtk; + +MainClock::MainClock ( + const std::string& clock_name, + bool is_transient, + const std::string& widget_name, + bool editable, + bool follows_playhead, + bool primary, + bool duration, + bool with_info + ) + : AudioClock (clock_name, is_transient, widget_name, editable, follows_playhead, duration, with_info) + , _primary (primary) +{ + +} + +void +MainClock::build_ops_menu () +{ + using namespace Menu_Helpers; + + AudioClock::build_ops_menu (); + + MenuList& ops_items = ops_menu->items(); + ops_items.push_back (SeparatorElem ()); + ops_items.push_back (CheckMenuElem (_("Display delta to edit cursor"), sigc::mem_fun (*this, &MainClock::display_delta_to_edit_cursor))); + CheckMenuItem* c = dynamic_cast (&ops_items.back()); + if (_primary) { + c->set_active (ARDOUR::Config->get_primary_clock_delta_edit_cursor ()); + } else { + c->set_active (ARDOUR::Config->get_secondary_clock_delta_edit_cursor ()); + } +} + +void +MainClock::display_delta_to_edit_cursor () +{ + if (_primary) { + ARDOUR::Config->set_primary_clock_delta_edit_cursor (!ARDOUR::Config->get_primary_clock_delta_edit_cursor ()); + } else { + ARDOUR::Config->set_secondary_clock_delta_edit_cursor (!ARDOUR::Config->get_secondary_clock_delta_edit_cursor ()); + } +} diff --git a/gtk2_ardour/main_clock.h b/gtk2_ardour/main_clock.h new file mode 100644 index 0000000000..959a3f9440 --- /dev/null +++ b/gtk2_ardour/main_clock.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2012 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "audio_clock.h" + +/** A simple subclass of AudioClock that adds the `display delta to edit cursor' option to its context menu */ +class MainClock : public AudioClock +{ +public: + MainClock (const std::string &, bool, const std::string &, bool, bool, bool primary, bool duration = false, bool with_info = false); + +private: + + void build_ops_menu (); + void display_delta_to_edit_cursor (); + bool _primary; +}; diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 299ca8ee50..26b03b1a2f 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -113,6 +113,10 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); + /* Look up MIDNAM details from our MidiTimeAxisView */ + MidiTimeAxisView& mtv = dynamic_cast (tv); + midi_patch_settings_changed (mtv.midi_patch_model (), mtv.midi_patch_custom_device_node ()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); @@ -149,6 +153,10 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView & _note_group->raise_to_top(); PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); + /* Look up MIDNAM details from our MidiTimeAxisView */ + MidiTimeAxisView& mtv = dynamic_cast (tv); + midi_patch_settings_changed (mtv.midi_patch_model (), mtv.midi_patch_custom_device_node ()); + connect_to_diskstream (); SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); @@ -327,7 +335,10 @@ MidiRegionView::canvas_event(GdkEvent* ev) return trackview.editor().toggle_internal_editing_from_double_click (ev); } - if (!trackview.editor().internal_editing()) { + if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) || + (trackview.editor().current_mouse_mode() == MouseTimeFX) || + (trackview.editor().current_mouse_mode() == MouseZoom)) { + // handle non-draw modes elsewhere return false; } @@ -388,6 +399,13 @@ MidiRegionView::enter_notify (GdkEventCrossing* ev) group->grab_focus(); } + // if current operation is non-operational in a midi region, change the cursor to so indicate + if (trackview.editor().current_mouse_mode() == MouseGain) { + Editor* editor = dynamic_cast (&trackview.editor()); + pre_enter_cursor = editor->get_canvas_cursor(); + editor->set_canvas_cursor(editor->cursors()->timebar); + } + return false; } @@ -399,6 +417,11 @@ MidiRegionView::leave_notify (GdkEventCrossing*) trackview.editor().verbose_cursor()->hide (); remove_ghost_note (); + if (pre_enter_cursor) { + Editor* editor = dynamic_cast (&trackview.editor()); + editor->set_canvas_cursor(pre_enter_cursor); + } + return false; } @@ -596,8 +619,8 @@ MidiRegionView::motion (GdkEventMotion* ev) editor.verbose_cursor()->hide (); return true; } else if (m == MouseObject) { - editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); + clear_selection (); _mouse_state = SelectRectDragging; return true; } else if (m == MouseRange) { @@ -1939,9 +1962,7 @@ MidiRegionView::delete_note (boost::shared_ptr n) void MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal) { - bool changed = false; - - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { if ((*i) != ev) { Selection::iterator tmp = i; ++tmp; @@ -1949,7 +1970,6 @@ MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool (*i)->set_selected (false); (*i)->hide_velocity (); _selection.erase (i); - changed = true; i = tmp; } else { @@ -1961,7 +1981,7 @@ MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool selection. */ - if (changed && signal) { + if (signal) { SelectionCleared (this); /* EMIT SIGNAL */ } } @@ -3628,7 +3648,7 @@ MidiRegionView::trim_front_ending () void MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc) { - PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY); + PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), _model_name, _custom_device_mode, Gtk::Stock::APPLY); if (d.run () != Gtk::RESPONSE_ACCEPT) { return; } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index c3084aee38..ae663b8cfd 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -292,6 +292,16 @@ public: void trim_front_ending (); void create_note_at (framepos_t, double, double, bool); + + void clear_selection (bool signal = true) { clear_selection_except (0, signal); } + + std::string model_name () const { + return _model_name; + } + + std::string custom_device_mode () const { + return _custom_device_mode; + } protected: /** Allows derived types to specify their visibility requirements @@ -318,7 +328,10 @@ private: friend class MidiRubberbandSelectDrag; friend class MidiVerticalSelectDrag; - /** Emitted when the selection has been cleared in one MidiRegionView */ + /** Emitted when the selection has been cleared in one MidiRegionView, + * with the expectation that others will clear their selections in + * sympathy. + */ static PBD::Signal1 SelectionCleared; PBD::ScopedConnection _selection_cleared_connection; void selection_cleared (MidiRegionView *); @@ -353,7 +366,6 @@ private: ARDOUR::MidiModel::TimeType end_delta); void clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal = true); - void clear_selection (bool signal = true) { clear_selection_except (0, signal); } void update_drag_selection (double last_x, double x, double last_y, double y, bool extend); void update_vertical_drag_selection (double last_y, double y, bool extend); diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 676aa52028..311327f0ec 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -647,3 +647,14 @@ MidiStreamView::resume_updates () draw_note_lines (); apply_note_range_to_regions (); } + +void +MidiStreamView::leave_internal_edit_mode () +{ + StreamView::leave_internal_edit_mode (); + for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) { + MidiRegionView* mrv = dynamic_cast (*i); + assert (mrv); + mrv->clear_selection (); + } +} diff --git a/gtk2_ardour/midi_streamview.h b/gtk2_ardour/midi_streamview.h index d216752eda..1e6289d63c 100644 --- a/gtk2_ardour/midi_streamview.h +++ b/gtk2_ardour/midi_streamview.h @@ -76,6 +76,8 @@ class MidiStreamView : public StreamView void redisplay_track (); + void leave_internal_edit_mode (); + inline double contents_height() const { return (child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 2); } diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 4df22c7c96..7cebb806af 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -199,14 +199,14 @@ MidiTimeAxisView::set_route (boost::shared_ptr rt) _model_selector.append_text(m->c_str()); } + _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed)); _custom_device_mode_selector.signal_changed().connect( sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed)); - // TODO: persist the choice - // this initializes the comboboxes and sends out the signal - _model_selector.set_active(0); + _model_selector.set_active_text (gui_property (X_("midnam-model-name"))); + _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode"))); midi_controls_hbox->pack_start(_channel_selector, true, false); if (!patch_manager.all_models().empty()) { @@ -317,12 +317,15 @@ MidiTimeAxisView::model_changed() } _custom_device_mode_selector.set_active(0); + + set_gui_property (X_("midnam-model-name"), midi_patch_model ()); } -void MidiTimeAxisView::custom_device_mode_changed() +void +MidiTimeAxisView::custom_device_mode_changed() { - _midi_patch_settings_changed.emit(_model_selector.get_active_text(), - _custom_device_mode_selector.get_active_text()); + _midi_patch_settings_changed.emit (midi_patch_model (), midi_patch_custom_device_node ()); + set_gui_property (X_("midnam-custom-device-mode"), midi_patch_custom_device_node ()); } MidiStreamView* @@ -1203,3 +1206,15 @@ MidiTimeAxisView::note_range_changed () set_gui_property ("note-range-min", (int) midi_view()->lowest_note ()); set_gui_property ("note-range-max", (int) midi_view()->highest_note ()); } + +string +MidiTimeAxisView::midi_patch_model () const +{ + return _model_selector.get_active_text (); +} + +string +MidiTimeAxisView::midi_patch_custom_device_node () const +{ + return _custom_device_mode_selector.get_active_text (); +} diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 8207792f83..bc1dccfb78 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -102,6 +102,9 @@ class MidiTimeAxisView : public RouteTimeAxisView uint8_t get_channel_for_add () const; + std::string midi_patch_model () const; + std::string midi_patch_custom_device_node () const; + protected: void start_step_editing (); void stop_step_editing (); diff --git a/gtk2_ardour/mouse_cursors.cc b/gtk2_ardour/mouse_cursors.cc index fa3ce331d6..98e33d77d6 100644 --- a/gtk2_ardour/mouse_cursors.cc +++ b/gtk2_ardour/mouse_cursors.cc @@ -29,7 +29,7 @@ MouseCursors::MouseCursors () { RefPtr p (::get_icon ("zoom_in_cursor")); - zoom_in = new Cursor (Display::get_default(), p, 5, 5); + zoom_in = new Cursor (Display::get_default(), p, 10, 5); } { @@ -49,7 +49,7 @@ MouseCursors::MouseCursors () { RefPtr source = Bitmap::create ((char const *) speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height); RefPtr mask = Bitmap::create ((char const *) speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height); - speaker = new Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot); + speaker = new Cursor (source, mask, ffg, fbg, speaker_cursor_width >> 1, speaker_cursor_height >> 1); } { diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 3e1fa73ca3..ca8fa0a2cf 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -176,7 +176,13 @@ FaderOption::FaderOption (string const & i, string const & n, sigc::slot throw failed_constructor (); } + _pix_desensitised = ::get_icon (X_("fader_belt_h_desensitised")); + if (_pix_desensitised == 0) { + throw failed_constructor (); + } + _db_slider = manage (new HSliderController (_pix, + _pix_desensitised, &_db_adjustment, 115, false)); diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index c70e6c6bab..5d6a48a024 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -362,6 +362,7 @@ private: Gtk::Adjustment _db_adjustment; Gtkmm2ext::HSliderController* _db_slider; Glib::RefPtr _pix; + Glib::RefPtr _pix_desensitised; Gtk::Entry _db_display; Gtk::Label _label; Gtk::HBox _box; diff --git a/gtk2_ardour/patch_change_dialog.cc b/gtk2_ardour/patch_change_dialog.cc index 4bedf63df6..c7b80a5480 100644 --- a/gtk2_ardour/patch_change_dialog.cc +++ b/gtk2_ardour/patch_change_dialog.cc @@ -20,10 +20,13 @@ #include #include +#include +#include "ardour/midi_patch_manager.h" #include "ardour/beats_frames_converter.h" #include "patch_change_dialog.h" #include "i18n.h" +using namespace std; using namespace Gtk; /** @param tc If non-0, a time converter for this patch change. If 0, time control will be desensitized */ @@ -31,14 +34,19 @@ PatchChangeDialog::PatchChangeDialog ( const ARDOUR::BeatsFramesConverter* tc, ARDOUR::Session* session, Evoral::PatchChange const & patch, + string const & model_name, + string const & custom_device_node, const Gtk::BuiltinStockID& ok ) : ArdourDialog (_("Patch Change"), true) , _time_converter (tc) + , _model_name (model_name) + , _custom_device_mode (custom_device_node) , _time (X_("patchchangetime"), true, "", true, false) , _channel (*manage (new Adjustment (1, 1, 16, 1, 4))) , _program (*manage (new Adjustment (1, 1, 128, 1, 16))) , _bank (*manage (new Adjustment (1, 1, 16384, 1, 64))) + , _ignore_signals (false) { Table* t = manage (new Table (4, 2)); Label* l; @@ -58,13 +66,30 @@ PatchChangeDialog::PatchChangeDialog ( _time.set (_time_converter->to (patch.time ()), true); } + l = manage (new Label (_("Patch Bank"))); + l->set_alignment (0, 0.5); + t->attach (*l, 0, 1, r, r + 1); + t->attach (_bank_combo, 1, 2, r, r + 1); + ++r; + + _bank_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_combo_changed)); + + l = manage (new Label (_("Patch"))); + l->set_alignment (0, 0.5); + t->attach (*l, 0, 1, r, r + 1); + t->attach (_patch_combo, 1, 2, r, r + 1); + ++r; + + _patch_combo.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::patch_combo_changed)); + l = manage (new Label (_("Channel"))); l->set_alignment (0, 0.5); t->attach (*l, 0, 1, r, r + 1); t->attach (_channel, 1, 2, r, r + 1); ++r; - + _channel.set_value (patch.channel() + 1); + _channel.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::channel_changed)); l = manage (new Label (_("Program"))); l->set_alignment (0, 0.5); @@ -73,6 +98,7 @@ PatchChangeDialog::PatchChangeDialog ( ++r; _program.set_value (patch.program () + 1); + _program.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::program_changed)); l = manage (new Label (_("Bank"))); l->set_alignment (0, 0.5); @@ -81,6 +107,7 @@ PatchChangeDialog::PatchChangeDialog ( ++r; _bank.set_value (patch.bank() + 1); + _bank.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeDialog::bank_changed)); get_vbox()->add (*t); @@ -88,6 +115,10 @@ PatchChangeDialog::PatchChangeDialog ( add_button (ok, RESPONSE_ACCEPT); set_default_response (RESPONSE_ACCEPT); + fill_bank_combo (); + set_active_bank_combo (); + bank_combo_changed (); + show_all (); } @@ -107,3 +138,210 @@ PatchChangeDialog::patch () const _bank.get_value_as_int() - 1 ); } + +/** Fill the bank_combo according to the current _channel */ +void +PatchChangeDialog::fill_bank_combo () +{ + MIDI::Name::ChannelNameSet::PatchBanks const * banks = get_banks (); + if (banks == 0) { + return; + } + + for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = banks->begin(); i != banks->end(); ++i) { + string n = (*i)->name (); + boost::replace_all (n, "_", " "); + _bank_combo.append_text (n); + } +} + +/** Set the active value of the bank_combo, and _current_patch_bank, from the contents of _bank */ +void +PatchChangeDialog::set_active_bank_combo () +{ + _current_patch_bank.reset (); + + MIDI::Name::ChannelNameSet::PatchBanks const * banks = get_banks (); + if (banks == 0) { + return; + } + + for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = banks->begin(); i != banks->end(); ++i) { + string n = (*i)->name (); + boost::replace_all (n, "_", " "); + + MIDI::Name::PatchPrimaryKey const * key = (*i)->patch_primary_key (); + if (key && ((key->msb << 7) | key->lsb) == _bank.get_value () - 1) { + _current_patch_bank = *i; + _ignore_signals = true; + _bank_combo.set_active_text (n); + _ignore_signals = false; + return; + } + } + + _ignore_signals = true; + _bank_combo.set_active (-1); + _ignore_signals = false; +} + +/** Update _current_patch_bank and reflect the current value of + * bank_combo in the rest of the dialog. + */ +void +PatchChangeDialog::bank_combo_changed () +{ + if (_ignore_signals) { + return; + } + + _current_patch_bank.reset (); + + MIDI::Name::ChannelNameSet::PatchBanks const * banks = get_banks (); + if (banks == 0) { + return; + } + + for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = banks->begin(); i != banks->end(); ++i) { + string n = (*i)->name (); + boost::replace_all (n, "_", " "); + if (n == _bank_combo.get_active_text()) { + _current_patch_bank = *i; + } + } + + if (_current_patch_bank == 0) { + return; + } + + /* Reflect */ + + fill_patch_combo (); + set_active_patch_combo (); + + MIDI::Name::PatchPrimaryKey const * key = _current_patch_bank->patch_primary_key (); + if (key) { + _ignore_signals = true; + _bank.set_value (((key->msb << 7) | key->lsb) + 1); + _ignore_signals = false; + } +} + +/** Fill the contents of the patch combo */ +void +PatchChangeDialog::fill_patch_combo () +{ + _patch_combo.clear (); + + if (_current_patch_bank == 0) { + return; + } + + const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list (); + for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) { + string n = (*j)->name (); + boost::replace_all (n, "_", " "); + _patch_combo.append_text (n); + } +} + +/** Set the active value of the patch combo from the value of the _program entry */ +void +PatchChangeDialog::set_active_patch_combo () +{ + if (_ignore_signals) { + return; + } + + if (_current_patch_bank == 0) { + _ignore_signals = true; + _patch_combo.set_active (-1); + _ignore_signals = false; + return; + } + + const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list (); + for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) { + string n = (*j)->name (); + boost::replace_all (n, "_", " "); + + MIDI::Name::PatchPrimaryKey const & key = (*j)->patch_primary_key (); + if (key.program_number == _program.get_value() - 1) { + _ignore_signals = true; + _patch_combo.set_active_text (n); + _ignore_signals = false; + return; + } + } + + _ignore_signals = true; + _patch_combo.set_active (-1); + _ignore_signals = false; +} + +/** Set _program from the current state of _patch_combo */ +void +PatchChangeDialog::patch_combo_changed () +{ + if (_ignore_signals || _current_patch_bank == 0) { + return; + } + + const MIDI::Name::PatchBank::PatchNameList& patches = _current_patch_bank->patch_name_list (); + for (MIDI::Name::PatchBank::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) { + string n = (*j)->name (); + boost::replace_all (n, "_", " "); + if (n == _patch_combo.get_active_text ()) { + MIDI::Name::PatchPrimaryKey const & key = (*j)->patch_primary_key (); + _ignore_signals = true; + _program.set_value (key.program_number + 1); + _ignore_signals = false; + } + } +} + +void +PatchChangeDialog::channel_changed () +{ + fill_bank_combo (); + set_active_bank_combo (); + fill_patch_combo (); + set_active_patch_combo (); +} + +void +PatchChangeDialog::program_changed () +{ + if (_ignore_signals) { + return; + } + + set_active_patch_combo (); +} + +void +PatchChangeDialog::bank_changed () +{ + if (_ignore_signals) { + return; + } + + set_active_bank_combo (); + fill_patch_combo (); + set_active_patch_combo (); +} + +MIDI::Name::ChannelNameSet::PatchBanks const * +PatchChangeDialog::get_banks () +{ + MIDI::Name::MidiPatchManager& mpm = MIDI::Name::MidiPatchManager::instance (); + boost::shared_ptr channel_name_set = mpm.find_channel_name_set ( + _model_name, _custom_device_mode, _channel.get_value_as_int() - 1 + ); + + if (!channel_name_set) { + return 0; + } + + return &channel_name_set->patch_banks (); +} diff --git a/gtk2_ardour/patch_change_dialog.h b/gtk2_ardour/patch_change_dialog.h index 02eb311d48..0691260126 100644 --- a/gtk2_ardour/patch_change_dialog.h +++ b/gtk2_ardour/patch_change_dialog.h @@ -19,6 +19,8 @@ */ #include +#include +#include "midi++/midnam_patch.h" #include "evoral/PatchChange.hpp" #include "ardour_dialog.h" #include "audio_clock.h" @@ -28,6 +30,12 @@ namespace ARDOUR { class Session; } +namespace MIDI { + namespace Name { + class PatchBank; + } +} + class PatchChangeDialog : public ArdourDialog { public: @@ -35,15 +43,36 @@ public: const ARDOUR::BeatsFramesConverter *, ARDOUR::Session *, Evoral::PatchChange const &, + std::string const &, + std::string const &, const Gtk::BuiltinStockID & ); Evoral::PatchChange patch () const; private: + void fill_bank_combo (); + void set_active_bank_combo (); + void fill_patch_combo (); + void set_active_patch_combo (); + void bank_combo_changed (); + void patch_combo_changed (); + void channel_changed (); + void bank_changed (); + void program_changed (); + + MIDI::Name::ChannelNameSet::PatchBanks const * get_banks (); + const ARDOUR::BeatsFramesConverter* _time_converter; + std::string _model_name; + std::string _custom_device_mode; AudioClock _time; Gtk::SpinButton _channel; Gtk::SpinButton _program; Gtk::SpinButton _bank; + Gtk::ComboBoxText _bank_combo; + Gtk::ComboBoxText _patch_combo; + + boost::shared_ptr _current_patch_bank; + bool _ignore_signals; }; diff --git a/gtk2_ardour/playlist_selector.cc b/gtk2_ardour/playlist_selector.cc index cda844ee89..b0bc515a61 100644 --- a/gtk2_ardour/playlist_selector.cc +++ b/gtk2_ardour/playlist_selector.cc @@ -62,7 +62,7 @@ PlaylistSelector::PlaylistSelector () get_vbox()->pack_start (scroller); - Button* b = add_button (_("close"), RESPONSE_CANCEL); + Button* b = add_button (_("Close"), RESPONSE_CANCEL); b->signal_clicked().connect (sigc::mem_fun(*this, &PlaylistSelector::close_button_click)); } diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index d9c21dcc9b..c698aeed06 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -91,6 +91,7 @@ RefPtr ProcessorBox::rename_action; RefPtr ProcessorBox::edit_action; RefPtr ProcessorBox::edit_generic_action; Glib::RefPtr ProcessorEntry::_slider_pixbuf; +Glib::RefPtr ProcessorEntry::_slider_pixbuf_desensitised; ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr p, Width w) : _button (ArdourButton::led_default_elements) @@ -121,7 +122,13 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr p = _processor->what_can_be_automated (); for (set::iterator i = p.begin(); i != p.end(); ++i) { - Control* c = new Control (_slider_pixbuf, _processor->automation_control (*i), _processor->describe_parameter (*i)); + Control* c = new Control ( + _slider_pixbuf, + _slider_pixbuf_desensitised, + _processor->automation_control (*i), + _processor->describe_parameter (*i) + ); + _controls.push_back (c); if (boost::dynamic_pointer_cast (_processor) == 0) { @@ -301,6 +308,8 @@ ProcessorEntry::setup_slider_pix () { _slider_pixbuf = ::get_icon ("fader_belt_h_thin"); assert (_slider_pixbuf); + _slider_pixbuf_desensitised = ::get_icon ("fader_belt_h_thin_desensitised"); + assert (_slider_pixbuf_desensitised); } void @@ -398,10 +407,10 @@ ProcessorEntry::toggle_control_visibility (Control* c) _parent->update_gui_object_state (this); } -ProcessorEntry::Control::Control (Glib::RefPtr s, boost::shared_ptr c, string const & n) +ProcessorEntry::Control::Control (Glib::RefPtr s, Glib::RefPtr sd, boost::shared_ptr c, string const & n) : _control (c) , _adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0, 1, 0.01, 0.1) - , _slider (s, &_adjustment, 0, false) + , _slider (s, sd, &_adjustment, 0, false) , _button (ArdourButton::Element (ArdourButton::Text | ArdourButton::Indicator)) , _ignore_ui_adjustment (false) , _visible (false) diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 0e6582bb2f..a8a84e636c 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -140,6 +140,7 @@ protected: virtual void setup_visuals (); static Glib::RefPtr _slider_pixbuf; + static Glib::RefPtr _slider_pixbuf_desensitised; private: void led_clicked(); @@ -157,7 +158,7 @@ private: class Control { public: - Control (Glib::RefPtr, boost::shared_ptr, std::string const &); + Control (Glib::RefPtr, Glib::RefPtr, boost::shared_ptr, std::string const &); void set_pixel_width (int); void set_visible (bool); diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 9f903b547c..80b3e2aa02 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -202,6 +202,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible { virtual framepos_t unit_to_frame (double unit) const = 0; virtual double frame_to_unit (framepos_t frame) const = 0; virtual double frame_to_unit (double frame) const = 0; + virtual double frame_to_unit_unrounded (framepos_t frame) const = 0; virtual framepos_t pixel_to_frame (double pixel) const = 0; virtual gulong frame_to_pixel (framepos_t frame) const = 0; virtual Selection& get_selection () const = 0; diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index ac9c11a5bb..f5a8cc56f0 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -1005,22 +1005,6 @@ RCOptionEditor::RCOptionEditor () sigc::mem_fun (*_rc_config, &RCConfiguration::set_seamless_loop) )); - add_option (_("Transport"), - new BoolOption ( - "primary-clock-delta-edit-cursor", - _("Primary clock delta to edit cursor"), - sigc::mem_fun (*_rc_config, &RCConfiguration::get_primary_clock_delta_edit_cursor), - sigc::mem_fun (*_rc_config, &RCConfiguration::set_primary_clock_delta_edit_cursor) - )); - - add_option (_("Transport"), - new BoolOption ( - "secondary-clock-delta-edit-cursor", - _("Secondary clock delta to edit cursor"), - sigc::mem_fun (*_rc_config, &RCConfiguration::get_secondary_clock_delta_edit_cursor), - sigc::mem_fun (*_rc_config, &RCConfiguration::set_secondary_clock_delta_edit_cursor) - )); - add_option (_("Transport"), new BoolOption ( "disable-disarm-during-roll", diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 6b52e64572..fc898b8f75 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -86,6 +86,7 @@ using namespace std; using std::list; Glib::RefPtr RouteTimeAxisView::slider; +Glib::RefPtr RouteTimeAxisView::slider_desensitised; void RouteTimeAxisView::setup_slider_pix () @@ -93,6 +94,10 @@ RouteTimeAxisView::setup_slider_pix () if ((slider = ::get_icon ("fader_belt_h")) == 0) { throw failed_constructor (); } + + if ((slider_desensitised = ::get_icon ("fader_belt_h_desensitised")) == 0) { + throw failed_constructor (); + } } RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas) @@ -112,7 +117,7 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& c , playlist_action_menu (0) , mode_menu (0) , color_mode_menu (0) - , gm (sess, slider, true, 115) + , gm (sess, slider, slider_desensitised, true, 115) { } @@ -124,6 +129,7 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) gm.set_controls (_route, _route->shared_peak_meter(), _route->amp()); gm.get_level_meter().set_no_show_all(); gm.get_level_meter().setup_meters(50); + gm.update_gain_sensitive (); string str = gui_property ("height"); if (!str.empty()) { @@ -757,13 +763,9 @@ RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection) } void -RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end) +RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer) { - double x1; - double x2; - double y2; - - TimeAxisView::show_timestretch (start, end); + TimeAxisView::show_timestretch (start, end, layers, layer); hide_timestretch (); @@ -802,14 +804,13 @@ RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end) timestretch_rect->show (); timestretch_rect->raise_to_top (); - x1 = start / _editor.get_current_zoom(); - x2 = (end - 1) / _editor.get_current_zoom(); - y2 = current_height() - 2; + double const x1 = start / _editor.get_current_zoom(); + double const x2 = (end - 1) / _editor.get_current_zoom(); timestretch_rect->property_x1() = x1; - timestretch_rect->property_y1() = 1.0; + timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers; timestretch_rect->property_x2() = x2; - timestretch_rect->property_y2() = y2; + timestretch_rect->property_y2() = current_height() * (layers - layer) / layers; } void diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index eb1a212d0e..f18d3361e0 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -81,7 +81,7 @@ public: void set_samples_per_unit (double); void set_height (uint32_t h); - void show_timestretch (framepos_t start, framepos_t end); + void show_timestretch (framepos_t start, framepos_t end, int layers, int layer); void hide_timestretch (); void selection_click (GdkEventButton*); void set_selected_points (PointSelection&); @@ -296,6 +296,7 @@ protected: GainMeterBase gm; static Glib::RefPtr slider; + static Glib::RefPtr slider_desensitised; XMLNode* underlay_xml_node; bool set_underlay_state(); diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 724618e540..009b40d26d 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -861,6 +861,12 @@ Selection::selected (RegionView* rv) return find (regions.begin(), regions.end(), rv) != regions.end(); } +bool +Selection::selected (ControlPoint* cp) +{ + return find (points.begin(), points.end(), cp) != points.end(); +} + bool Selection::empty (bool internal_selection) { diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index df7212593f..e30ca612ea 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -114,6 +114,7 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList bool selected (TimeAxisView*); bool selected (RegionView*); bool selected (Marker*); + bool selected (ControlPoint*); void set (std::list const &); void add (std::list const &); diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 0e233e83d2..4dc919d4c5 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -61,6 +61,7 @@ #include "editing.h" #include "utils.h" #include "gain_meter.h" +#include "main_clock.h" #ifdef FREESOUND #include "sfdb_freesound_mootcher.h" diff --git a/gtk2_ardour/splash.cc b/gtk2_ardour/splash.cc index f91ae89e69..6cc4cd5ded 100644 --- a/gtk2_ardour/splash.cc +++ b/gtk2_ardour/splash.cc @@ -1,3 +1,22 @@ +/* + Copyright (C) 2008 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + #include #include "pbd/failed_constructor.h" @@ -20,6 +39,8 @@ Splash* Splash::the_splash = 0; Splash::Splash () { + assert (the_splash == 0); + sys::path splash_file; if (!find_file_in_search_path (ardour_data_search_path(), "splash.png", splash_file)) { @@ -59,6 +80,11 @@ Splash::Splash () ARDOUR::BootMessage.connect (msg_connection, invalidator (*this), boost::bind (&Splash::boot_message, this, _1), gui_context()); } +Splash::~Splash () +{ + the_splash = 0; +} + void Splash::pop_back_for (Gtk::Window& win) { diff --git a/gtk2_ardour/splash.h b/gtk2_ardour/splash.h index d6b103609a..bb3e7b6f6a 100644 --- a/gtk2_ardour/splash.h +++ b/gtk2_ardour/splash.h @@ -34,7 +34,7 @@ class Splash : public Gtk::Window { public: Splash (); - ~Splash () {} + ~Splash (); static Splash* instance() { return the_splash; } diff --git a/gtk2_ardour/stereo_panner.cc b/gtk2_ardour/stereo_panner.cc index 730ef58910..96e1821ee6 100644 --- a/gtk2_ardour/stereo_panner.cc +++ b/gtk2_ardour/stereo_panner.cc @@ -476,35 +476,67 @@ StereoPanner::on_motion_notify_event (GdkEventMotion* ev) if (dragging_left) { delta = -delta; } - + if (dragging_left || dragging_right) { - /* maintain position as invariant as we change the width */ + if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) { + /* change width and position in a way that keeps the + * other side in the same place + */ - /* create a detent close to the center */ + _panner->freeze (); + + double pv = position_control->get_value(); - if (!detented && fabs (current_width) < 0.02) { - detented = true; - /* snap to zero */ - width_control->set_value (0); - } - - if (detented) { - - accumulated_delta += delta; - - /* have we pulled far enough to escape ? */ - - if (fabs (accumulated_delta) >= 0.025) { - width_control->set_value (current_width + accumulated_delta); - detented = false; - accumulated_delta = false; + if (dragging_left) { + position_control->set_value (pv - delta); + } else { + position_control->set_value (pv + delta); } + if (delta > 0.0) { + /* delta is positive, so we're about to + increase the width. But we need to increase it + by twice the required value so that the + other side remains in place when we set + the position as well. + */ + width_control->set_value (current_width + (delta * 2.0)); + } else { + width_control->set_value (current_width + delta); + } + + _panner->thaw (); + } else { - /* width needs to change by 2 * delta because both L & R move */ - width_control->set_value (current_width + delta * 2); + + /* maintain position as invariant as we change the width */ + + /* create a detent close to the center */ + + if (!detented && fabs (current_width) < 0.02) { + detented = true; + /* snap to zero */ + width_control->set_value (0); + } + + if (detented) { + + accumulated_delta += delta; + + /* have we pulled far enough to escape ? */ + + if (fabs (accumulated_delta) >= 0.025) { + width_control->set_value (current_width + accumulated_delta); + detented = false; + accumulated_delta = false; + } + + } else { + /* width needs to change by 2 * delta because both L & R move */ + width_control->set_value (current_width + (delta * 2.0)); + } } } else if (dragging_position) { diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc index 229a7065a2..ddf03e968e 100644 --- a/gtk2_ardour/time_axis_view.cc +++ b/gtk2_ardour/time_axis_view.cc @@ -764,10 +764,10 @@ TimeAxisView::set_samples_per_unit (double spu) } void -TimeAxisView::show_timestretch (framepos_t start, framepos_t end) +TimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer) { for (Children::iterator i = children.begin(); i != children.end(); ++i) { - (*i)->show_timestretch (start, end); + (*i)->show_timestretch (start, end, layers, layer); } } diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h index ba69b3b154..5966d78ed8 100644 --- a/gtk2_ardour/time_axis_view.h +++ b/gtk2_ardour/time_axis_view.h @@ -157,7 +157,7 @@ class TimeAxisView : public virtual AxisView virtual void show_selection (TimeSelection&); virtual void hide_selection (); virtual void reshow_selection (TimeSelection&); - virtual void show_timestretch (framepos_t start, framepos_t end); + virtual void show_timestretch (framepos_t start, framepos_t end, int layers, int layer); virtual void hide_timestretch (); /* editing operations */ diff --git a/gtk2_ardour/verbose_cursor.cc b/gtk2_ardour/verbose_cursor.cc index f80b74c4f8..eb73cb91c8 100644 --- a/gtk2_ardour/verbose_cursor.cc +++ b/gtk2_ardour/verbose_cursor.cc @@ -26,6 +26,7 @@ #include "audio_clock.h" #include "editor.h" #include "editor_drag.h" +#include "main_clock.h" #include "utils.h" #include "verbose_cursor.h" diff --git a/gtk2_ardour/visual_time_axis.cc b/gtk2_ardour/visual_time_axis.cc index 10c8513ec8..bad9ce30e9 100644 --- a/gtk2_ardour/visual_time_axis.cc +++ b/gtk2_ardour/visual_time_axis.cc @@ -409,7 +409,7 @@ VisualTimeAxis::name_entry_key_release_handler(GdkEventKey* ev) // Super class methods not handled by VisualTimeAxis void -VisualTimeAxis::show_timestretch (framepos_t start, framepos_t end) +VisualTimeAxis::show_timestretch (framepos_t start, framepos_t end, int layers, int layer) { // Not handled by purely visual TimeAxis } diff --git a/gtk2_ardour/visual_time_axis.h b/gtk2_ardour/visual_time_axis.h index df0a061ba6..25ec1d8ef3 100644 --- a/gtk2_ardour/visual_time_axis.h +++ b/gtk2_ardour/visual_time_axis.h @@ -250,7 +250,7 @@ class VisualTimeAxis : public TimeAxisView * * @todo should VisualTimeAxis handle this? */ - void show_timestretch (nframes_t start, nframes_t end); + void show_timestretch (nframes_t start, nframes_t end, int layers, int layer); /** * Not handle by purely visual TimeAxis diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 0613e7af2c..7a3aff9236 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -133,6 +133,7 @@ gtk2_ardour_sources = [ 'lineset.cc', 'location_ui.cc', 'main.cc', + 'main_clock.cc', 'marker.cc', 'midi_automation_line.cc', 'midi_channel_dialog.cc', diff --git a/libs/ardour/ardour/audio_buffer.h b/libs/ardour/ardour/audio_buffer.h index faa3de7685..6b771814db 100644 --- a/libs/ardour/ardour/audio_buffer.h +++ b/libs/ardour/ardour/audio_buffer.h @@ -173,6 +173,7 @@ public: void prepare () { _written = false; _silent = false; } bool written() const { return _written; } + void set_written(bool w) { _written = w; } private: bool _owns_data; diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h index 39efd2505d..a5f6b4ef39 100644 --- a/libs/ardour/ardour/audioplaylist.h +++ b/libs/ardour/ardour/audioplaylist.h @@ -59,7 +59,8 @@ private: int set_state (const XMLNode&, int version); void dump () const; bool region_changed (const PBD::PropertyChange&, boost::shared_ptr); - void source_offset_changed (boost::shared_ptr region); + void source_offset_changed (boost::shared_ptr); + void load_legacy_crossfades (const XMLNode&, int version); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index c03f32cb63..0d83b33539 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -94,6 +94,11 @@ class AudioRegion : public Region bool fade_out_is_xfade() const { return _fade_out_is_xfade; } void set_fade_out_is_xfade (bool yn); + bool fade_in_is_short() const { return _fade_in_is_short; } + void set_fade_in_is_short (bool yn); + bool fade_out_is_short() const { return _fade_out_is_short; } + void set_fade_out_is_short (bool yn); + boost::shared_ptr fade_in() { return _fade_in; } boost::shared_ptr inverse_fade_in() { return _inverse_fade_in; } boost::shared_ptr fade_out() { return _fade_out; } @@ -199,6 +204,10 @@ class AudioRegion : public Region PBD::Property _fade_out_active; /** linear gain to apply to the whole region */ PBD::Property _scale_amplitude; + PBD::Property _fade_in_is_xfade; + PBD::Property _fade_out_is_xfade; + PBD::Property _fade_in_is_short; + PBD::Property _fade_out_is_short; void register_properties (); void post_set (const PBD::PropertyChange&); @@ -231,11 +240,6 @@ class AudioRegion : public Region boost::shared_ptr _envelope; uint32_t _fade_in_suspended; uint32_t _fade_out_suspended; - /* This is not a Property because its not subject to user control, - or undo/redo. XXX this may prove to be a mistake. - */ - bool _fade_in_is_xfade; - bool _fade_out_is_xfade; boost::shared_ptr get_single_other_xfade_region (bool start) const; diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index a4de7c4ae1..27e002ca66 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -104,10 +104,7 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee boost::shared_ptr get_scale_points(uint32_t port_index) const; - /// Return the URID of midi:MidiEvent - static uint32_t midi_event_type (bool event_api) { - return event_api ? _midi_event_type_ev : _midi_event_type; - } + static uint32_t midi_event_type() { return _midi_event_type; } void set_insert_info(const PluginInsert* insert); @@ -140,7 +137,6 @@ class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee static URIMap _uri_map; - static uint32_t _midi_event_type_ev; static uint32_t _midi_event_type; static uint32_t _chunk_type; static uint32_t _sequence_type; diff --git a/libs/ardour/ardour/mtdm.h b/libs/ardour/ardour/mtdm.h index 9d67e1cd64..59b504b821 100644 --- a/libs/ardour/ardour/mtdm.h +++ b/libs/ardour/ardour/mtdm.h @@ -27,11 +27,11 @@ public: MTDM (); int process (size_t len, float *inp, float *out); - int resolve (void); - void invert (void) { _inv ^= 1; } - int inv (void) { return _inv; } - double del (void) { return _del; } - double err (void) { return _err; } + int resolve (); + void invert () { _inv ^= 1; } + int inv () { return _inv; } + double del () { return _del; } + double err () { return _err; } private: class Freq { diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 51d75ebf03..4c585f8b31 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -151,6 +151,9 @@ public: return fabs (a.azi - b.azi) < 1.0; } + virtual void freeze (); + virtual void thaw (); + protected: boost::shared_ptr _pannable; @@ -158,6 +161,8 @@ protected: virtual void distribute_one_automated (AudioBuffer&, BufferSet& obufs, framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which) = 0; + + int32_t _frozen; }; } // namespace diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index b31abaca51..ade781a4a7 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -31,9 +31,6 @@ #include #include -#ifdef HAVE_GLIB_THREADS_RECMUTEX -#include -#endif #include "pbd/undo.h" #include "pbd/stateful.h" @@ -121,6 +118,7 @@ public: bool hidden() const { return _hidden; } bool empty() const; uint32_t n_regions() const; + bool all_regions_empty() const; std::pair get_extent () const; layer_t top_layer() const; @@ -228,6 +226,8 @@ public: uint64_t highest_layering_index () const; void set_layer (boost::shared_ptr, double); + + void set_capture_insertion_in_progress (bool yn); protected: friend class Session; @@ -294,6 +294,7 @@ public: bool in_flush; bool in_partition; bool _frozen; + bool _capture_insertion_underway; uint32_t subcnt; PBD::ID _orig_track_id; bool auto_partition; diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index 80584e6859..4ba261f80c 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -111,6 +111,13 @@ public: static void add_compound_association (boost::shared_ptr, boost::shared_ptr); + /* exposed because there may be cases where regions are created with + * announce=false but they still need to be in the map soon after + * creation. + */ + + static void map_add (boost::shared_ptr); + private: static void region_changed (PBD::PropertyChange const &, boost::weak_ptr); @@ -118,7 +125,6 @@ public: static Glib::StaticMutex region_map_lock; static RegionMap region_map; - static void map_add (boost::shared_ptr); static Glib::StaticMutex region_name_map_lock; diff --git a/libs/ardour/ardour/uri_map.h b/libs/ardour/ardour/uri_map.h index a6c9dc7a8b..18008f0df2 100644 --- a/libs/ardour/ardour/uri_map.h +++ b/libs/ardour/ardour/uri_map.h @@ -30,8 +30,10 @@ namespace ARDOUR { - /** Implementation of the LV2 uri-map and urid extensions. + * + * This just uses a pair of std::map and is not so great in the space overhead + * department, but it's fast enough and not really performance critical anyway. */ class URIMap : public boost::noncopyable { public: @@ -44,26 +46,15 @@ public: LV2_URID_Map* urid_map() { return &_urid_map_feature_data; } LV2_URID_Unmap* urid_unmap() { return &_urid_unmap_feature_data; } - uint32_t uri_to_id(const char* map, const char* uri); - - const char* id_to_uri(const char* map, uint32_t id); + uint32_t uri_to_id(const char* uri); + const char* id_to_uri(uint32_t id) const; private: - static uint32_t uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri); + typedef std::map Map; + typedef std::map Unmap; - static LV2_URID urid_map(LV2_URID_Map_Handle handle, - const char* uri); - - static const char* urid_unmap(LV2_URID_Unmap_Handle handle, - LV2_URID urid); - - typedef std::map EventToGlobal; - typedef std::map GlobalToEvent; - - EventToGlobal _event_to_global; - GlobalToEvent _global_to_event; + Map _map; + Unmap _unmap; LV2_Feature _uri_map_feature; LV2_URI_Map_Feature _uri_map_feature_data; @@ -73,7 +64,6 @@ private: LV2_URID_Unmap _urid_unmap_feature_data; }; - } // namespace ARDOUR #endif // __ardour_uri_map_h__ diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index 7eba3fa18c..5052f03bab 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -61,7 +61,6 @@ std::string path_expand (std::string); /* single file path */ std::string search_path_expand (std::string); /* colon-separated search path */ std::string region_name_from_path (std::string path, bool strip_channels, bool add_channel_suffix = false, uint32_t total = 0, uint32_t this_one = 0); bool path_is_paired (std::string path, std::string& pair_base); -bool inodes_same (const std::string &, const std::string &); void compute_equal_power_fades (ARDOUR::framecnt_t nframes, float* in, float* out); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 8c5b606388..8e6b5c27f1 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -1476,6 +1476,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n"; _playlist->clear_changes (); + _playlist->set_capture_insertion_in_progress (true); _playlist->freeze (); for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { @@ -1514,6 +1515,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo } _playlist->thaw (); + _playlist->set_capture_insertion_in_progress (false); _session.add_command (new StatefulDiffCommand (_playlist)); } diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 25fad6122f..dad62daf41 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -49,6 +49,8 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden in_set_state--; relayer (); + + load_legacy_crossfades (node, Stateful::loading_state_version); } AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) @@ -200,7 +202,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr /* This will be a list of the bits of regions that we need to read */ list to_do; - + /* Now go through the `all' list filling in `to_do' and `done' */ for (RegionList::iterator i = all->begin(); i != all->end(); ++i) { boost::shared_ptr ar = boost::dynamic_pointer_cast (*i); @@ -213,6 +215,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr region_range.to = min (region_range.to, start + cnt - 1); /* ... and then remove the bits that are already done */ + Evoral::RangeList region_to_do = Evoral::subtract (region_range, done); /* Read those bits, adding their bodies (the parts between end-of-fade-in @@ -239,6 +242,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr /* Now go backwards through the to_do list doing the actual reads */ for (list::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) { + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n", + name(), i->region->name(), i->range.from, + i->range.to - i->range.from + 1, (int) chan_n, + buf, i->range.from - start)); i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n); } @@ -313,35 +320,51 @@ AudioPlaylist::check_crossfades (Evoral::Range range) /* Top's fade-in will cause an implicit fade-out of bottom */ - framecnt_t len = 0; - switch (_session.config.get_xfade_model()) { - case FullCrossfade: - len = bottom->last_frame () - top->first_frame (); - break; - case ShortCrossfade: - len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); - break; - } - - top->set_fade_in_active (true); - top->set_fade_in_is_xfade (true); + if (top->fade_in_is_xfade() && top->fade_in_is_short()) { - /* XXX may 2012: -3dB and -6dB curves - * are the same right now - */ + /* its already an xfade. if its + * really short, leave it + * alone. + */ - switch (_session.config.get_xfade_choice ()) { - case ConstantPowerMinus3dB: - top->set_fade_in (FadeConstantPower, len); - break; - case ConstantPowerMinus6dB: - top->set_fade_in (FadeConstantPower, len); - break; - case RegionFades: - top->set_fade_in_length (len); - break; + } else { + framecnt_t len = 0; + + if (_capture_insertion_underway) { + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + } else { + switch (_session.config.get_xfade_model()) { + case FullCrossfade: + len = bottom->last_frame () - top->first_frame (); + top->set_fade_in_is_short (false); + break; + case ShortCrossfade: + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + top->set_fade_in_is_short (true); + break; + } + } + + top->set_fade_in_active (true); + top->set_fade_in_is_xfade (true); + + /* XXX may 2012: -3dB and -6dB curves + * are the same right now + */ + + switch (_session.config.get_xfade_choice ()) { + case ConstantPowerMinus3dB: + top->set_fade_in (FadeConstantPower, len); + break; + case ConstantPowerMinus6dB: + top->set_fade_in (FadeConstantPower, len); + break; + case RegionFades: + top->set_fade_in_length (len); + break; + } } - + done_start.insert (top); } @@ -356,29 +379,44 @@ AudioPlaylist::check_crossfades (Evoral::Range range) if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) { /* Top's fade-out will cause an implicit fade-in of bottom */ - framecnt_t len = 0; - switch (_session.config.get_xfade_model()) { - case FullCrossfade: - len = top->last_frame () - bottom->first_frame (); - break; - case ShortCrossfade: - len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); - break; - } - top->set_fade_out_active (true); - top->set_fade_out_is_xfade (true); + if (top->fade_out_is_xfade() && top->fade_out_is_short()) { - switch (_session.config.get_xfade_choice ()) { - case ConstantPowerMinus3dB: - top->set_fade_out (FadeConstantPower, len); - break; - case ConstantPowerMinus6dB: - top->set_fade_out (FadeConstantPower, len); - break; - case RegionFades: - top->set_fade_out_length (len); - break; + /* its already an xfade. if its + * really short, leave it + * alone. + */ + + } else { + framecnt_t len = 0; + + if (_capture_insertion_underway) { + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + } else { + switch (_session.config.get_xfade_model()) { + case FullCrossfade: + len = top->last_frame () - bottom->first_frame (); + break; + case ShortCrossfade: + len = _session.config.get_short_xfade_seconds() * _session.frame_rate(); + break; + } + } + + top->set_fade_out_active (true); + top->set_fade_out_is_xfade (true); + + switch (_session.config.get_xfade_choice ()) { + case ConstantPowerMinus3dB: + top->set_fade_out (FadeConstantPower, len); + break; + case ConstantPowerMinus6dB: + top->set_fade_out (FadeConstantPower, len); + break; + case RegionFades: + top->set_fade_out_length (len); + break; + } } done_end.insert (top); @@ -390,14 +428,18 @@ AudioPlaylist::check_crossfades (Evoral::Range range) for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) { if (done_start.find (*i) == done_start.end()) { boost::shared_ptr r = boost::dynamic_pointer_cast (*i); - r->set_default_fade_in (); + if (r->fade_in_is_xfade()) { + r->set_default_fade_in (); + } } } for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) { if (done_end.find (*i) == done_end.end()) { boost::shared_ptr r = boost::dynamic_pointer_cast (*i); - r->set_default_fade_out (); + if (r->fade_out_is_xfade()) { + r->set_default_fade_out (); + } } } } @@ -621,11 +663,12 @@ AudioPlaylist::pre_uncombine (vector >& originals, boo int AudioPlaylist::set_state (const XMLNode& node, int version) { - int const r = Playlist::set_state (node, version); - if (r) { - return r; - } + return Playlist::set_state (node, version); +} +void +AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version) +{ /* Read legacy Crossfade nodes and set up region fades accordingly */ XMLNodeList children = node.children (); @@ -634,36 +677,97 @@ AudioPlaylist::set_state (const XMLNode& node, int version) XMLProperty* p = (*i)->property (X_("active")); assert (p); + if (!string_is_affirmative (p->value())) { continue; } + + if ((p = (*i)->property (X_("in"))) == 0) { + continue; + } - p = (*i)->property (X_("in")); - assert (p); boost::shared_ptr in = region_by_id (PBD::ID (p->value ())); - assert (in); + + if (!in) { + warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"), + name()) + << endmsg; + continue; + } + boost::shared_ptr in_a = boost::dynamic_pointer_cast (in); assert (in_a); - p = (*i)->property (X_("out")); - assert (p); + if ((p = (*i)->property (X_("out"))) == 0) { + continue; + } + boost::shared_ptr out = region_by_id (PBD::ID (p->value ())); - assert (out); + + if (!out) { + warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"), + name()) + << endmsg; + continue; + } + boost::shared_ptr out_a = boost::dynamic_pointer_cast (out); assert (out_a); - XMLNodeList c = (*i)->children (); - for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) { - if ((*j)->name() == X_("FadeIn")) { - in_a->fade_in()->set_state (**j, version); - in_a->set_fade_in_active (true); - } else if ((*j)->name() == X_("FadeOut")) { - out_a->fade_out()->set_state (**j, version); - out_a->set_fade_out_active (true); + /* now decide whether to add a fade in or fade out + * xfade and to which region + */ + + if (in->layer() <= out->layer()) { + + /* incoming region is below the outgoing one, + * so apply a fade out to the outgoing one + */ + + const XMLNodeList c = (*i)->children (); + + for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) { + if ((*j)->name() == X_("FadeOut")) { + out_a->fade_out()->set_state (**j, version); + } else if ((*j)->name() == X_("FadeIn")) { + out_a->inverse_fade_out()->set_state (**j, version); + } } + + if ((p = (*i)->property ("follow-overlap")) != 0) { + out_a->set_fade_out_is_short (!string_is_affirmative (p->value())); + } else { + out_a->set_fade_out_is_short (false); + } + + out_a->set_fade_out_is_xfade (true); + out_a->set_fade_out_active (true); + + } else { + + /* apply a fade in to the incoming region, + * since its above the outgoing one + */ + + const XMLNodeList c = (*i)->children (); + + for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) { + if ((*j)->name() == X_("FadeIn")) { + in_a->fade_in()->set_state (**j, version); + } else if ((*j)->name() == X_("FadeOut")) { + in_a->inverse_fade_in()->set_state (**j, version); + } + } + + if ((p = (*i)->property ("follow-overlap")) != 0) { + in_a->set_fade_in_is_short (!string_is_affirmative (p->value())); + } else { + in_a->set_fade_in_is_short (false); + } + + in_a->set_fade_in_is_xfade (true); + in_a->set_fade_in_active (true); } } } - - return 0; } diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index a57aecb388..42a29c12af 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -61,6 +61,10 @@ namespace ARDOUR { PBD::PropertyDescriptor fade_in_active; PBD::PropertyDescriptor fade_out_active; PBD::PropertyDescriptor scale_amplitude; + PBD::PropertyDescriptor fade_out_is_xfade; + PBD::PropertyDescriptor fade_out_is_short; + PBD::PropertyDescriptor fade_in_is_xfade; + PBD::PropertyDescriptor fade_in_is_short; } } @@ -74,14 +78,14 @@ reverse_curve (boost::shared_ptr dst, boost::shared_ptrback()->when; for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); it++) { - dst->add ( len - (*it)->when, (*it)->value ); + dst->add (len - (*it)->when, (*it)->value); } } static void generate_inverse_power_curve (boost::shared_ptr dst, boost::shared_ptr src) { - //calc inverse curve using sum of squares + // calc inverse curve using sum of squares for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); ++it ) { float value = (*it)->value; value = 1 - powf(value,2); @@ -90,18 +94,6 @@ generate_inverse_power_curve (boost::shared_ptr dst, boost: } } -/* -static void -generate_inverse_coefficient_curve (boost::shared_ptr dst, boost::shared_ptr src) -{ - //calc inverse gain coefficient curve - for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); ++it ) { - float value = 1.0 - (*it)->value; - dst->fast_simple_add ( (*it)->when, value ); - } -} -*/ - static void generate_db_fade (boost::shared_ptr dst, double len, int num_steps, float dB_drop) { @@ -164,6 +156,14 @@ AudioRegion::make_property_quarks () DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n", Properties::fade_out_active.property_id)); Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n", Properties::scale_amplitude.property_id)); + Properties::fade_out_is_xfade.property_id = g_quark_from_static_string (X_("fade-out-is-xfade")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-is-xfade = %1\n", Properties::fade_out_is_xfade.property_id)); + Properties::fade_out_is_short.property_id = g_quark_from_static_string (X_("fade-out-is-short")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-is-short = %1\n", Properties::fade_out_is_short.property_id)); + Properties::fade_in_is_xfade.property_id = g_quark_from_static_string (X_("fade-in-is-xfade")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-is-xfade = %1\n", Properties::fade_in_is_xfade.property_id)); + Properties::fade_in_is_short.property_id = g_quark_from_static_string (X_("fade-in-is-short")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-is-short = %1\n", Properties::fade_in_is_short.property_id)); } void @@ -177,6 +177,10 @@ AudioRegion::register_properties () add_property (_fade_in_active); add_property (_fade_out_active); add_property (_scale_amplitude); + add_property (_fade_out_is_xfade); + add_property (_fade_out_is_short); + add_property (_fade_in_is_xfade); + add_property (_fade_in_is_short); } #define AUDIOREGION_STATE_DEFAULT \ @@ -185,7 +189,11 @@ AudioRegion::register_properties () , _default_fade_out (Properties::default_fade_out, true) \ , _fade_in_active (Properties::fade_in_active, true) \ , _fade_out_active (Properties::fade_out_active, true) \ - , _scale_amplitude (Properties::scale_amplitude, 1.0) + , _scale_amplitude (Properties::scale_amplitude, 1.0) \ + , _fade_in_is_xfade (Properties::fade_in_is_xfade, false) \ + , _fade_out_is_xfade (Properties::fade_out_is_xfade, false) \ + , _fade_in_is_short (Properties::fade_in_is_short, false) \ + , _fade_out_is_short (Properties::fade_out_is_short, false) #define AUDIOREGION_COPY_STATE(other) \ _envelope_active (Properties::envelope_active, other->_envelope_active) \ @@ -193,7 +201,11 @@ AudioRegion::register_properties () , _default_fade_out (Properties::default_fade_out, other->_default_fade_out) \ , _fade_in_active (Properties::fade_in_active, other->_fade_in_active) \ , _fade_out_active (Properties::fade_out_active, other->_fade_out_active) \ - , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) + , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \ + , _fade_in_is_xfade (Properties::fade_in_is_xfade, other->_fade_in_is_xfade) \ + , _fade_out_is_xfade (Properties::fade_out_is_xfade, other->_fade_out_is_xfade) \ + , _fade_in_is_short (Properties::fade_in_is_short, other->_fade_in_is_short) \ + , _fade_out_is_short (Properties::fade_out_is_short, other->_fade_out_is_short) /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */ void @@ -223,8 +235,6 @@ AudioRegion::AudioRegion (Session& s, framepos_t start, framecnt_t len, std::str , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { init (); assert (_sources.size() == _master_sources.size()); @@ -242,8 +252,6 @@ AudioRegion::AudioRegion (const SourceList& srcs) , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { init (); assert (_sources.size() == _master_sources.size()); @@ -263,8 +271,6 @@ AudioRegion::AudioRegion (boost::shared_ptr other) , _envelope (new AutomationList (*other->_envelope, 0, other->_length)) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { /* don't use init here, because we got fade in/out from the other region */ @@ -291,8 +297,6 @@ AudioRegion::AudioRegion (boost::shared_ptr other, framecnt_t , _envelope (new AutomationList (*other->_envelope, offset, other->_length)) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { /* don't use init here, because we got fade in/out from the other region */ @@ -316,8 +320,6 @@ AudioRegion::AudioRegion (boost::shared_ptr other, const Sour , _envelope (new AutomationList (*other->_envelope)) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */ @@ -341,8 +343,6 @@ AudioRegion::AudioRegion (SourceList& srcs) , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation))) , _fade_in_suspended (0) , _fade_out_suspended (0) - , _fade_in_is_xfade (false) - , _fade_out_is_xfade (false) { init (); @@ -524,7 +524,7 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */ - /* Amount of fade in that we are dealing with in this read */ + /* Amount (length) of fade in that we are dealing with in this read */ framecnt_t fade_in_limit = 0; /* Offset from buf / mixdown_buffer of the start @@ -532,7 +532,7 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, */ frameoffset_t fade_out_offset = 0; - /* Amount of fade in that we are dealing with in this read */ + /* Amount (length) of fade out that we are dealing with in this read */ framecnt_t fade_out_limit = 0; framecnt_t fade_interval_start = 0; @@ -542,7 +542,7 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, if (_fade_in_active && _session.config.get_use_region_fades()) { framecnt_t fade_in_length = (framecnt_t) _fade_in->back()->when; - + /* see if this read is within the fade in */ if (internal_offset < fade_in_length) { @@ -611,7 +611,6 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); } - /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO * buf. The key things to realize here: (1) the fade being applied is * (as of April 26th 2012) just the inverse of the fade in curve (2) @@ -623,6 +622,7 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, if (opaque()) { if (_inverse_fade_in) { + /* explicit inverse fade in curve (e.g. for constant * power), so we have to fetch it. @@ -671,7 +671,7 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, _inverse_fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); - /* Fade the data from lower levels out */ + /* Fade the data from lower levels in */ for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { buf[m] *= gain_buffer[n]; } @@ -682,7 +682,8 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, } else { - /* no explicit inverse fade out, so just use (1 - fade + /* no explicit inverse fade out (which is + * actually a fade in), so just use (1 - fade * out) for the fade in of lower layers */ @@ -696,12 +697,13 @@ AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit); } - /* Mix our newly-read data out, with the fade */ + /* Mix our newly-read data with whatever was already there, + with the fade out applied to our data. + */ for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { buf[m] += mixdown_buffer[m] * gain_buffer[n]; } } - /* MIX OR COPY THE REGION BODY FROM mixdown_buffer INTO buf */ @@ -806,7 +808,6 @@ AudioRegion::state () } child = node.add_child (X_("FadeIn")); - child->add_property ("is-xfade", (_fade_in_is_xfade ? "yes" : "no")); if (_default_fade_in) { child->add_property ("default", "yes"); @@ -820,7 +821,6 @@ AudioRegion::state () } child = node.add_child (X_("FadeOut")); - child->add_property ("is-xfade", (_fade_out_is_xfade ? "yes" : "no")); if (_default_fade_out) { child->add_property ("default", "yes"); @@ -899,12 +899,6 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } - if ((prop = child->property ("is-xfade")) != 0) { - _fade_in_is_xfade = string_is_affirmative (prop->value()); - } else { - _fade_in_is_xfade = false; - } - if ((prop = child->property ("active")) != 0) { if (string_is_affirmative (prop->value())) { set_fade_in_active (true); @@ -913,6 +907,12 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } + /* legacy a3 */ + + if ((prop = child->property ("is-xfade")) != 0) { + _fade_in_is_xfade = string_is_affirmative (prop->value()); + } + } else if (child->name() == "FadeOut") { _fade_out->clear (); @@ -925,14 +925,8 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ _fade_out->set_state (*grandchild, version); } } - - if ((prop = child->property ("is-xfade")) != 0) { - _fade_out_is_xfade = string_is_affirmative (prop->value()); - } else { - _fade_out_is_xfade = false; - } - - if ((prop = child->property ("active")) != 0) { + + if ((prop = child->property ("active")) != 0) { if (string_is_affirmative (prop->value())) { set_fade_out_active (true); } else { @@ -940,6 +934,12 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_ } } + /* legacy a3 */ + + if ((prop = child->property ("is-xfade")) != 0) { + _fade_out_is_xfade = string_is_affirmative (prop->value()); + } + } else if (child->name() == "InvFadeIn") { XMLNode* grandchild = child->child ("AutomationList"); if (grandchild) { @@ -1163,7 +1163,7 @@ AudioRegion::set_fade_in_length (framecnt_t len) if (_session.config.get_xfade_model() == FullCrossfade && _session.config.get_auto_xfade() && - _fade_in_is_xfade) { + _fade_in_is_xfade && !_fade_in_is_short) { /* trim a single other region below us to the new start of the fade. @@ -1202,7 +1202,7 @@ AudioRegion::set_fade_out_length (framecnt_t len) if (_session.config.get_xfade_model() == FullCrossfade && _session.config.get_auto_xfade() && - _fade_out_is_xfade) { + _fade_out_is_xfade && !_fade_out_is_short) { /* trim a single other region below us to the new start of the fade. @@ -1256,6 +1256,7 @@ AudioRegion::set_default_fade_in () { _fade_in_suspended = 0; _fade_in_is_xfade = false; + _fade_in_is_short = true; set_fade_in (FadeLinear, 64); } @@ -1264,6 +1265,7 @@ AudioRegion::set_default_fade_out () { _fade_out_suspended = 0; _fade_out_is_xfade = false; + _fade_out_is_short = true; set_fade_out (FadeLinear, 64); } @@ -1835,13 +1837,46 @@ AudioRegion::body_range () const void AudioRegion::set_fade_in_is_xfade (bool yn) { + if (yn == _fade_in_is_xfade) { + return; + } + _fade_in_is_xfade = yn; + send_change (PropertyChange (Properties::fade_in_is_xfade)); } void AudioRegion::set_fade_out_is_xfade (bool yn) { + if (yn == _fade_out_is_xfade) { + return; + } + _fade_out_is_xfade = yn; + send_change (PropertyChange (Properties::fade_out_is_xfade)); +} + +void +AudioRegion::set_fade_in_is_short (bool yn) +{ + if (yn == _fade_in_is_short) { + return; + } + + _fade_in_is_short = yn; + send_change (PropertyChange (Properties::fade_in_is_short)); + +} + +void +AudioRegion::set_fade_out_is_short (bool yn) +{ + if (yn == _fade_out_is_short) { + return; + } + + _fade_out_is_short = yn; + send_change (PropertyChange (Properties::fade_out_is_short)); } boost::shared_ptr diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index 046ea71de5..f9e683cdda 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -269,7 +269,7 @@ BufferSet::get_lv2_midi(bool input, size_t i, bool old_api) mbuf.size(), (void*) mbuf.data())); LV2_Evbuf_Iterator i = lv2_evbuf_begin(evbuf); - const uint32_t type = LV2Plugin::midi_event_type(old_api); + const uint32_t type = LV2Plugin::midi_event_type(); for (MidiBuffer::iterator e = mbuf.begin(); e != mbuf.end(); ++e) { const Evoral::MIDIEvent ev(*e, false); #ifndef NDEBUG diff --git a/libs/ardour/file_source.cc b/libs/ardour/file_source.cc index a7e3585e64..f4f3c40d9d 100644 --- a/libs/ardour/file_source.cc +++ b/libs/ardour/file_source.cc @@ -33,6 +33,7 @@ #include "pbd/strsplit.h" #include "pbd/shortpath.h" #include "pbd/enumwriter.h" +#include "pbd/filesystem.h" #include #include @@ -122,11 +123,7 @@ FileSource::init (const string& pathstr, bool must_exist) } set_within_session_from_path (_path); - - if (!within_session()) { - _session.ensure_search_path_includes (Glib::path_get_dirname (_path), _type); - } - + _name = Glib::path_get_basename (_path); if (_file_is_new && must_exist) { @@ -281,7 +278,7 @@ FileSource::find (Session& s, DataType type, const string& path, bool must_exist ++j; while (j != hits.end()) { - if (inodes_same (*i, *j)) { + if (PBD::sys::inodes_same (*i, *j)) { /* *i and *j are the same file; break out of the loop early */ break; } diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 50552d2eb8..e9ec835ef1 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -68,20 +68,16 @@ using namespace ARDOUR; using namespace PBD; URIMap LV2Plugin::_uri_map; -uint32_t LV2Plugin::_midi_event_type_ev = _uri_map.uri_to_id( - "http://lv2plug.in/ns/ext/event", - "http://lv2plug.in/ns/ext/midi#MidiEvent"); uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id( - NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent"); uint32_t LV2Plugin::_chunk_type = _uri_map.uri_to_id( - NULL, LV2_ATOM__Chunk); + LV2_ATOM__Chunk); uint32_t LV2Plugin::_sequence_type = _uri_map.uri_to_id( - NULL, LV2_ATOM__Sequence); + LV2_ATOM__Sequence); uint32_t LV2Plugin::_event_transfer_type = _uri_map.uri_to_id( - NULL, LV2_ATOM__eventTransfer); + LV2_ATOM__eventTransfer); uint32_t LV2Plugin::_path_type = _uri_map.uri_to_id( - NULL, LV2_ATOM__Path); + LV2_ATOM__Path); class LV2World : boost::noncopyable { public: diff --git a/libs/ardour/mtdm.cc b/libs/ardour/mtdm.cc index ba73025053..4c27b3b600 100644 --- a/libs/ardour/mtdm.cc +++ b/libs/ardour/mtdm.cc @@ -20,7 +20,7 @@ #include "ardour/mtdm.h" -MTDM::MTDM (void) +MTDM::MTDM () : _cnt (0) , _inv (0) { diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 6f3aec4646..c88fbe44f4 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -28,6 +28,7 @@ using namespace std; using namespace ARDOUR; Panner::Panner (boost::shared_ptr p) + : _frozen (0) { // boost_debug_shared_ptr_mark_interesting (this, "panner"); _pannable = p; @@ -118,3 +119,17 @@ Panner::set_state (XMLNode const &, int) { return 0; } + +void +Panner::freeze () +{ + _frozen++; +} + +void +Panner::thaw () +{ + if (_frozen > 0.0) { + _frozen--; + } +} diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 4a470bc03d..bc6aef41ec 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -47,9 +47,9 @@ using namespace ARDOUR; using namespace PBD; namespace ARDOUR { -namespace Properties { -PBD::PropertyDescriptor regions; -} + namespace Properties { + PBD::PropertyDescriptor regions; + } } struct ShowMeTheList { @@ -311,6 +311,7 @@ Playlist::init (bool hide) in_partition = false; subcnt = 0; _frozen = false; + _capture_insertion_underway = false; _combine_ops = 0; _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this)); @@ -2179,6 +2180,16 @@ Playlist::n_regions() const return regions.size(); } +/** @return true if the all_regions list is empty, ie this playlist + * has never had a region added to it. + */ +bool +Playlist::all_regions_empty() const +{ + RegionReadLock rl (const_cast (this)); + return all_regions.empty(); +} + pair Playlist::get_extent () const { @@ -3124,3 +3135,9 @@ restart: check_crossfades (*i); } } + +void +Playlist::set_capture_insertion_in_progress (bool yn) +{ + _capture_insertion_underway = yn; +} diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc index 46c4d7e9d5..aa2d1392f5 100644 --- a/libs/ardour/port_insert.cc +++ b/libs/ardour/port_insert.cc @@ -62,10 +62,7 @@ PortInsert::~PortInsert () void PortInsert::start_latency_detection () { - if (_mtdm != 0) { - delete _mtdm; - } - + delete _mtdm; _mtdm = new MTDM; _latency_flush_frames = false; _latency_detect = true; @@ -118,8 +115,9 @@ PortInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, Sample* out = outbuf.data(); _mtdm->process (nframes, in, out); - + outbuf.set_is_silent (false); + outbuf.set_written (true); } return; diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index 01860048f4..fa948844ab 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -75,11 +75,10 @@ RegionFactory::create (boost::shared_ptr region, bool announce) if (ret->session().config.get_glue_new_regions_to_bars_and_beats ()) { ret->set_position_lock_style (MusicTime); } - - map_add (ret); /* pure copy constructor - no property list */ if (announce) { + map_add (ret); CheckNewRegion (ret); } } @@ -119,9 +118,8 @@ RegionFactory::create (boost::shared_ptr region, const PropertyList& pli ret->set_position_lock_style (MusicTime); } - map_add (ret); - if (announce) { + map_add (ret); CheckNewRegion (ret); } } @@ -161,9 +159,8 @@ RegionFactory::create (boost::shared_ptr region, frameoffset_t offset, c ret->set_position_lock_style (MusicTime); } - map_add (ret); - if (announce) { + map_add (ret); CheckNewRegion (ret); } } @@ -203,9 +200,8 @@ RegionFactory::create (boost::shared_ptr region, const SourceList& srcs, ret->set_position_lock_style (MusicTime); } - map_add (ret); - if (announce) { + map_add (ret); CheckNewRegion (ret); } } @@ -248,9 +244,8 @@ RegionFactory::create (const SourceList& srcs, const PropertyList& plist, bool a ret->set_position_lock_style (MusicTime); } - map_add (ret); - if (announce) { + map_add (ret); CheckNewRegion (ret); } } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index b09d67fb35..fdddb3b2c2 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2952,6 +2952,14 @@ Session::add_source (boost::shared_ptr source) /* yay, new source */ + boost::shared_ptr fs = boost::dynamic_pointer_cast (source); + + if (fs) { + if (!fs->within_session()) { + ensure_search_path_includes (Glib::path_get_dirname (fs->path()), fs->type()); + } + } + set_dirty(); boost::shared_ptr afs; @@ -4470,7 +4478,7 @@ Session::ensure_search_path_includes (const string& path, DataType type) search_path = config.get_midi_search_path (); break; } - + split (search_path, dirs, ':'); for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { @@ -4480,7 +4488,7 @@ Session::ensure_search_path_includes (const string& path, DataType type) On Windows, I think we could just do if (*i == path) here. */ - if (inodes_same (*i, path)) { + if (PBD::sys::inodes_same (*i, path)) { return; } } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 4b40a80d80..e729a7b00d 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -428,7 +428,7 @@ bool Session::path_is_within_session (const std::string& path) { for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { - if (path.find ((*i).path) == 0) { + if (PBD::sys::path_is_within (i->path, path)) { return true; } } diff --git a/libs/ardour/test/mtdm_test.cc b/libs/ardour/test/mtdm_test.cc new file mode 100644 index 0000000000..e409bd664a --- /dev/null +++ b/libs/ardour/test/mtdm_test.cc @@ -0,0 +1,29 @@ +#include +#include +#include "ardour/mtdm.h" +#include "mtdm_test.h" + +CPPUNIT_TEST_SUITE_REGISTRATION (MTDMTest); + +using namespace std; + +void +MTDMTest::basicTest () +{ + float in[256]; + float out[256]; + + memset (in, 0, 256 * sizeof (float)); + MTDM* mtdm = new MTDM; + mtdm->process (256, in, out); + memcpy (in, out, 256 * sizeof (float)); + + for (int i = 0; i < 64; ++i) { + mtdm->process (256, in, out); + memcpy (in, out, 256 * sizeof (float)); + + CPPUNIT_ASSERT_EQUAL (0, mtdm->resolve ()); + CPPUNIT_ASSERT (mtdm->err() < 1); + CPPUNIT_ASSERT_EQUAL (256.0, rint (mtdm->del())); + } +} diff --git a/libs/ardour/test/mtdm_test.h b/libs/ardour/test/mtdm_test.h new file mode 100644 index 0000000000..c02a4f2d12 --- /dev/null +++ b/libs/ardour/test/mtdm_test.h @@ -0,0 +1,17 @@ +#include +#include +#include + +class MTDMTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (MTDMTest); + CPPUNIT_TEST (basicTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp () {} + void tearDown () {} + + void basicTest (); +}; + diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 9699558ff1..0ad59d1b31 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -329,7 +329,15 @@ Track::set_name (const string& str) return false; } - _diskstream->set_name (str); + if (_diskstream->playlist()->all_regions_empty ()) { + /* 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). + */ + _diskstream->set_name (str); + } /* save state so that the statefile fully reflects any filename changes */ diff --git a/libs/ardour/uri_map.cc b/libs/ardour/uri_map.cc index 6a1d0a58d1..163a460624 100644 --- a/libs/ardour/uri_map.cc +++ b/libs/ardour/uri_map.cc @@ -18,112 +18,94 @@ */ #include -#include +#include +#include #include #include -#include - #include "pbd/error.h" #include "ardour/uri_map.h" -using namespace std; - namespace ARDOUR { +static uint32_t +c_uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri) +{ + URIMap* const me = (URIMap*)callback_data; + const uint32_t id = me->uri_to_id(uri); + + /* The event context with the uri-map extension guarantees a value in the + range of uint16_t. Ardour used to map to a separate range to achieve + this, but unfortunately some plugins are broken and use the incorrect + context. To compensate, we simply use the same context for everything + and hope that anything in the event context gets mapped before + UINT16_MAX is reached (which will be fine unless something seriously + weird is going on). If this fails there is nothing we can do, die. + */ + assert(!map || strcmp(map, "http://lv2plug.in/ns/ext/event") + || id < UINT16_MAX); + + return id; +} + +static LV2_URID +c_urid_map(LV2_URID_Map_Handle handle, + const char* uri) +{ + URIMap* const me = (URIMap*)handle; + return me->uri_to_id(uri); +} + +static const char* +c_urid_unmap(LV2_URID_Unmap_Handle handle, + LV2_URID urid) +{ + URIMap* const me = (URIMap*)handle; + return me->id_to_uri(urid); +} URIMap::URIMap() { - _uri_map_feature_data.uri_to_id = &URIMap::uri_map_uri_to_id; + _uri_map_feature_data.uri_to_id = c_uri_map_uri_to_id; _uri_map_feature_data.callback_data = this; _uri_map_feature.URI = LV2_URI_MAP_URI; _uri_map_feature.data = &_uri_map_feature_data; - _urid_map_feature_data.map = &URIMap::urid_map; + _urid_map_feature_data.map = c_urid_map; _urid_map_feature_data.handle = this; _urid_map_feature.URI = LV2_URID_MAP_URI; _urid_map_feature.data = &_urid_map_feature_data; - _urid_unmap_feature_data.unmap = &URIMap::urid_unmap; + _urid_unmap_feature_data.unmap = c_urid_unmap; _urid_unmap_feature_data.handle = this; _urid_unmap_feature.URI = LV2_URID_UNMAP_URI; _urid_unmap_feature.data = &_urid_unmap_feature_data; } - uint32_t -URIMap::uri_to_id(const char* map, - const char* uri) +URIMap::uri_to_id(const char* uri) { - const uint32_t id = static_cast(g_quark_from_string(uri)); - if (map && !strcmp(map, "http://lv2plug.in/ns/ext/event")) { - GlobalToEvent::iterator i = _global_to_event.find(id); - if (i != _global_to_event.end()) { - return i->second; - } else { - if (_global_to_event.size() + 1 > UINT16_MAX) { - PBD::error << "Event URI " << uri << " ID out of range." << endl; - return 0; - } - const uint16_t ev_id = _global_to_event.size() + 1; - assert(_event_to_global.find(ev_id) == _event_to_global.end()); - _global_to_event.insert(make_pair(id, ev_id)); - _event_to_global.insert(make_pair(ev_id, id)); - return ev_id; - } - } else { - return id; + const std::string urimm(uri); + const Map::const_iterator i = _map.find(urimm); + if (i != _map.end()) { + return i->second; } + const uint32_t id = _map.size() + 1; + _map.insert(std::make_pair(urimm, id)); + _unmap.insert(std::make_pair(id, urimm)); + return id; } - const char* -URIMap::id_to_uri(const char* map, - const uint32_t id) +URIMap::id_to_uri(const uint32_t id) const { - if (map && !strcmp(map, "http://lv2plug.in/ns/ext/event")) { - EventToGlobal::iterator i = _event_to_global.find(id); - if (i == _event_to_global.end()) { - PBD::error << "Failed to unmap event URI " << id << endl; - return NULL; - } - return g_quark_to_string(i->second); - } else { - return g_quark_to_string(id); - } - + const Unmap::const_iterator i = _unmap.find(id); + return (i != _unmap.end()) ? i->second.c_str() : NULL; } - -uint32_t -URIMap::uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri) -{ - URIMap* const me = (URIMap*)callback_data; - return me->uri_to_id(map, uri); -} - - -LV2_URID -URIMap::urid_map(LV2_URID_Map_Handle handle, - const char* uri) -{ - URIMap* const me = (URIMap*)handle; - return me->uri_to_id(NULL, uri); -} - - -const char* -URIMap::urid_unmap(LV2_URID_Unmap_Handle handle, - LV2_URID urid) -{ - URIMap* const me = (URIMap*)handle; - return me->id_to_uri(NULL, urid); -} - - } // namespace ARDOUR diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index beb003e713..7751ea20ca 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -120,46 +120,6 @@ bump_name_once (const std::string& name, char delimiter) } -bool -could_be_a_valid_path (const string& path) -{ - vector posix_dirs; - vector dos_dirs; - string testpath; - - split (path, posix_dirs, '/'); - split (path, dos_dirs, '\\'); - - /* remove the last component of each */ - - posix_dirs.erase (--posix_dirs.end()); - dos_dirs.erase (--dos_dirs.end()); - - if (G_DIR_SEPARATOR == '/') { - for (vector::iterator x = posix_dirs.begin(); x != posix_dirs.end(); ++x) { - testpath = Glib::build_filename (testpath, *x); - cerr << "Testing " << testpath << endl; - if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - return false; - } - } - } - - if (G_DIR_SEPARATOR == '\\') { - testpath = ""; - for (vector::iterator x = dos_dirs.begin(); x != dos_dirs.end(); ++x) { - testpath = Glib::build_filename (testpath, *x); - cerr << "Testing " << testpath << endl; - if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - return false; - } - } - } - - return true; -} - - XMLNode * find_named_node (const XMLNode& node, string name) { @@ -745,18 +705,6 @@ double slider_position_to_gain_with_max (double g, double max_gain) return slider_position_to_gain (g * max_gain/2.0); } -/** @return true if files a and b have the same inode */ -bool -inodes_same (const string& a, const string& b) -{ - struct stat bA; - int const rA = stat (a.c_str(), &bA); - struct stat bB; - int const rB = stat (b.c_str(), &bB); - - return (rA == 0 && rB == 0 && bA.st_ino == bB.st_ino); -} - extern "C" { void c_stacktrace() { stacktrace (cerr); } } diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 7ceaac9831..38e1fe81ef 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -259,11 +259,19 @@ def configure(conf): autowaf.check_pkg(conf, 'sigc++-2.0', uselib_store='SIGCPP', atleast_version='2.0') if Options.options.lv2: + autowaf.check_pkg(conf, 'lv2', uselib_store='LV2', + atleast_version='1.0.0', mandatory=True) + autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', + atleast_version='0.14.0', mandatory=True) + autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD', + atleast_version='0.8.0', mandatory=True) + autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', + atleast_version='0.2.0', mandatory=True) autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV', - atleast_version='0.14.0', mandatory=False) - if conf.is_defined('HAVE_LILV'): - autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL', - atleast_version='0.6.0', mandatory=False) + atleast_version='0.14.0', mandatory=True) + autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL', + atleast_version='0.6.0', mandatory=False) + conf.define ('LV2_SUPPORT', 1) # autowaf.check_pkg(conf, 'soundtouch-1.0', uselib_store='SOUNDTOUCH', # mandatory=False) @@ -335,9 +343,6 @@ int main(int argc, char **argv) { if ogg_supported(): conf.define ('HAVE_OGG', 1) - if Options.options.lv2 and conf.is_defined('HAVE_LILV'): - conf.define ('LV2_SUPPORT', 1) - conf.write_config_header('libardour-config.h', remove=False) # Boost headers @@ -437,6 +442,7 @@ def build(bld): test/playlist_layering_test.cc test/playlist_read_test.cc test/control_surfaces_test.cc + test/mtdm_test.cc test/testrunner.cc '''.split() diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index f7eba87ba6..40e2e54625 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -106,6 +106,10 @@ public: void set_parameter(const Parameter& p) { _parameter = p; } EventList::size_type size() const { return _events.size(); } + double length() const { + Glib::Mutex::Lock lm (_lock); + return _events.empty() ? 0.0 : _events.back()->when; + } bool empty() const { return _events.empty(); } void reset_default (double val) { diff --git a/libs/evoral/evoral/Range.hpp b/libs/evoral/evoral/Range.hpp index bc353a47d7..02d92100b9 100644 --- a/libs/evoral/evoral/Range.hpp +++ b/libs/evoral/evoral/Range.hpp @@ -139,7 +139,6 @@ public: return _list.empty (); } -private: void coalesce () { if (!_dirty) { return; @@ -164,6 +163,8 @@ private: _dirty = false; } + +private: List _list; bool _dirty; @@ -178,37 +179,71 @@ struct RangeMove { T to; ///< new start of the range }; +/** Subtract the ranges in `sub' from that in `range', + * returning the result. + */ template RangeList subtract (Range range, RangeList sub) { + /* Start with the input range */ RangeList result; + result.add (range); if (sub.empty ()) { - result.add (range); return result; } - - T x = range.from; typename RangeList::List s = sub.get (); + + /* The basic idea here is to keep a list of the result ranges, and subtract + the bits of `sub' from them one by one. + */ for (typename RangeList::List::const_iterator i = s.begin(); i != s.end(); ++i) { - if (coverage (range.from, range.to, i->from, i->to) == OverlapNone) { - continue; + /* Here's where we'll put the new current result after subtracting *i from it */ + RangeList new_result; + + typename RangeList::List r = result.get (); + + /* Work on all parts of the current result using this range *i */ + for (typename RangeList::List::const_iterator j = r.begin(); j != r.end(); ++j) { + + switch (coverage (j->from, j->to, i->from, i->to)) { + case OverlapNone: + /* The thing we're subtracting does not overlap this bit of the result, + so pass it through. + */ + new_result.add (*j); + break; + case OverlapInternal: + /* Internal overlap of the thing we're subtracting from this bit of the result, + so we might end up with two bits left over. + */ + if (j->from < (i->from - 1)) { + new_result.add (Range (j->from, i->from - 1)); + } + if (j->to != i->to) { + new_result.add (Range (i->to, j->to)); + } + break; + case OverlapStart: + /* The bit we're subtracting overlaps the start of the bit of the result */ + new_result.add (Range (i->to, j->to - 1)); + break; + case OverlapEnd: + /* The bit we're subtracting overlaps the end of the bit of the result */ + new_result.add (Range (j->from, i->from - 1)); + break; + case OverlapExternal: + /* total overlap of the bit we're subtracting with the result bit, so the + result bit is completely removed; do nothing */ + break; + } } - Range clamped (std::max (range.from, i->from), std::min (range.to, i->to)); - - if (clamped.from != x) { - result.add (Range (x, clamped.from - 1)); - } - - x = clamped.to; - } - - if (s.back().to < range.to) { - result.add (Range (x, range.to)); + new_result.coalesce (); + result = new_result; } return result; diff --git a/libs/evoral/test/RangeTest.cpp b/libs/evoral/test/RangeTest.cpp index ff9856a9b6..07decbf2c9 100644 --- a/libs/evoral/test/RangeTest.cpp +++ b/libs/evoral/test/RangeTest.cpp @@ -1,5 +1,6 @@ #include "RangeTest.hpp" #include "evoral/Range.hpp" +#include CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest); @@ -24,6 +25,7 @@ RangeTest::coalesceTest () CPPUNIT_ASSERT_EQUAL (8, i->to); } +/* Basic subtraction of a few smaller ranges from a larger one */ void RangeTest::subtractTest1 () { @@ -51,6 +53,7 @@ RangeTest::subtractTest1 () CPPUNIT_ASSERT_EQUAL (10, i->to); } +/* Test subtraction of a range B from a range A, where A and B do not overlap */ void RangeTest::subtractTest2 () { @@ -69,6 +72,7 @@ RangeTest::subtractTest2 () CPPUNIT_ASSERT_EQUAL (10, i->to); } +/* Test subtraction of B from A, where B entirely overlaps A */ void RangeTest::subtractTest3 () { @@ -82,3 +86,62 @@ RangeTest::subtractTest3 () RangeList::List s = sheila.get (); CPPUNIT_ASSERT_EQUAL (size_t (0), s.size ()); } + +/* A bit like subtractTest1, except some of the ranges + we are subtracting overlap. +*/ +void +RangeTest::subtractTest4 () +{ + Range fred (0, 10); + + RangeList jim; + jim.add (Range (2, 4)); + jim.add (Range (7, 8)); + jim.add (Range (8, 9)); + + RangeList sheila = subtract (fred, jim); + + RangeList::List s = sheila.get (); + CPPUNIT_ASSERT_EQUAL (size_t (3), s.size ()); + + RangeList::List::iterator i = s.begin (); + CPPUNIT_ASSERT_EQUAL (0, i->from); + CPPUNIT_ASSERT_EQUAL (1, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (4, i->from); + CPPUNIT_ASSERT_EQUAL (6, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (9, i->from); + CPPUNIT_ASSERT_EQUAL (10, i->to); +} + +/* A bit like subtractTest1, except some of the ranges + we are subtracting overlap the start / end of the + initial range. +*/ +void +RangeTest::subtractTest5 () +{ + Range fred (1, 12); + + RangeList jim; + jim.add (Range (0, 4)); + jim.add (Range (6, 7)); + jim.add (Range (9, 42)); + + RangeList sheila = subtract (fred, jim); + + RangeList::List s = sheila.get (); + CPPUNIT_ASSERT_EQUAL (size_t (2), s.size ()); + + RangeList::List::iterator i = s.begin (); + CPPUNIT_ASSERT_EQUAL (4, i->from); + CPPUNIT_ASSERT_EQUAL (5, i->to); + + ++i; + CPPUNIT_ASSERT_EQUAL (7, i->from); + CPPUNIT_ASSERT_EQUAL (8, i->to); +} diff --git a/libs/evoral/test/RangeTest.hpp b/libs/evoral/test/RangeTest.hpp index 6b65ad02d2..892347b3b0 100644 --- a/libs/evoral/test/RangeTest.hpp +++ b/libs/evoral/test/RangeTest.hpp @@ -6,7 +6,10 @@ class RangeTest : public CppUnit::TestFixture CPPUNIT_TEST_SUITE (RangeTest); CPPUNIT_TEST (coalesceTest); CPPUNIT_TEST (subtractTest1); + CPPUNIT_TEST (subtractTest2); CPPUNIT_TEST (subtractTest3); + CPPUNIT_TEST (subtractTest4); + CPPUNIT_TEST (subtractTest5); CPPUNIT_TEST_SUITE_END (); public: @@ -14,6 +17,8 @@ public: void subtractTest1 (); void subtractTest2 (); void subtractTest3 (); + void subtractTest4 (); + void subtractTest5 (); }; diff --git a/libs/gtkmm2ext/gtkmm2ext/keyboard.h b/libs/gtkmm2ext/gtkmm2ext/keyboard.h index 9b083317e3..3287cdb37e 100644 --- a/libs/gtkmm2ext/gtkmm2ext/keyboard.h +++ b/libs/gtkmm2ext/gtkmm2ext/keyboard.h @@ -56,6 +56,8 @@ class Keyboard : public sigc::trackable, PBD::Stateful static uint32_t Level4Modifier; static uint32_t CopyModifier; static uint32_t RangeSelectModifier; + static uint32_t GainFineScaleModifier; + static uint32_t GainExtraFineScaleModifier; static const char* primary_modifier_name (); static const char* secondary_modifier_name (); diff --git a/libs/gtkmm2ext/gtkmm2ext/pixfader.h b/libs/gtkmm2ext/gtkmm2ext/pixfader.h index 43df8e555e..9f1e4b75ca 100644 --- a/libs/gtkmm2ext/gtkmm2ext/pixfader.h +++ b/libs/gtkmm2ext/gtkmm2ext/pixfader.h @@ -32,7 +32,7 @@ namespace Gtkmm2ext { class PixFader : public Gtk::DrawingArea { public: - PixFader (Glib::RefPtr belt_image, Gtk::Adjustment& adjustment, int orientation, int); + PixFader (Glib::RefPtr, Glib::RefPtr, Gtk::Adjustment& adjustment, int orientation, int); virtual ~PixFader (); void set_fader_length (int); @@ -58,10 +58,17 @@ class PixFader : public Gtk::DrawingArea HORIZ=2, }; - private: - Cairo::RefPtr belt_context; - Cairo::RefPtr belt_surface; - Glib::RefPtr pixbuf; + private: + + enum State { + NORMAL, + DESENSITISED, + STATES + }; + + Cairo::RefPtr belt_context[STATES]; + Cairo::RefPtr belt_surface[STATES]; + Glib::RefPtr pixbuf[STATES]; int span, girth; int _orien; float left_r; @@ -85,9 +92,6 @@ class PixFader : public Gtk::DrawingArea int display_span (); void set_adjustment_from_event (GdkEventButton *); void update_unity_position (); - - static int fine_scale_modifier; - static int extra_fine_scale_modifier; }; diff --git a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h index d6afefbdfc..64dd977e9b 100644 --- a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h +++ b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h @@ -39,7 +39,7 @@ namespace Gtkmm2ext { class SliderController : public Gtkmm2ext::PixFader { public: - SliderController (Glib::RefPtr image, Gtk::Adjustment* adj, int orientation, int); + SliderController (Glib::RefPtr, Glib::RefPtr, Gtk::Adjustment* adj, int orientation, int); virtual ~SliderController () {} @@ -66,6 +66,7 @@ class VSliderController : public SliderController { public: VSliderController (Glib::RefPtr image, + Glib::RefPtr image_desensitised, Gtk::Adjustment *adj, int, bool with_numeric = true); }; @@ -74,6 +75,7 @@ class HSliderController : public SliderController { public: HSliderController (Glib::RefPtr image, + Glib::RefPtr image_desensitised, Gtk::Adjustment *adj, int, bool with_numeric = true); }; diff --git a/libs/gtkmm2ext/keyboard.cc b/libs/gtkmm2ext/keyboard.cc index e8f59af8c9..6328eb977c 100644 --- a/libs/gtkmm2ext/keyboard.cc +++ b/libs/gtkmm2ext/keyboard.cc @@ -58,9 +58,9 @@ guint Keyboard::snap_mod = GDK_MOD3_MASK; #ifdef GTKOSX uint Keyboard::PrimaryModifier = GDK_MOD2_MASK; // Command -guint Keyboard::SecondaryModifier = GDK_CONTROL_MASK; // Alt/Option +guint Keyboard::SecondaryModifier = GDK_CONTROL_MASK; // Control guint Keyboard::TertiaryModifier = GDK_SHIFT_MASK; // Shift -guint Keyboard::Level4Modifier = GDK_MOD1_MASK; // Control +guint Keyboard::Level4Modifier = GDK_MOD1_MASK; // Alt/Option guint Keyboard::CopyModifier = GDK_CONTROL_MASK; // Control guint Keyboard::RangeSelectModifier = GDK_SHIFT_MASK; guint Keyboard::button2_modifiers = Keyboard::SecondaryModifier|Keyboard::Level4Modifier; @@ -91,6 +91,9 @@ const char* Keyboard::rangeselect_modifier_name() { return S_("Key|Shift"); } #endif +guint Keyboard::GainFineScaleModifier = Keyboard::PrimaryModifier; +guint Keyboard::GainExtraFineScaleModifier = Keyboard::SecondaryModifier; + Keyboard* Keyboard::_the_keyboard = 0; Gtk::Window* Keyboard::current_window = 0; bool Keyboard::_some_magic_widget_has_focus = false; diff --git a/libs/gtkmm2ext/motionfeedback.cc b/libs/gtkmm2ext/motionfeedback.cc index 7a6d02f531..cb1d2cba88 100644 --- a/libs/gtkmm2ext/motionfeedback.cc +++ b/libs/gtkmm2ext/motionfeedback.cc @@ -184,7 +184,10 @@ MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) /* shift click back to the default */ _controllable->set_value (default_value); return true; - } + } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { + /* ctrl click back to the minimum value */ + _controllable->set_value (_controllable->lower ()); + } break; case 3: @@ -381,23 +384,25 @@ MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev) return false; } - if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) { - scale = 0.01; - } else if (ev->state & Keyboard::PrimaryModifier) { - scale = 0.1; + if (ev->state & Keyboard::GainFineScaleModifier) { + if (ev->state & Keyboard::GainExtraFineScaleModifier) { + scale = 0.01; + } else { + scale = 0.05; + } } else { - scale = 1.0; + scale = 0.25; } switch (ev->direction) { case GDK_SCROLL_UP: case GDK_SCROLL_RIGHT: - _controllable->set_value (adjust (scale * step_inc)); + _controllable->set_value (adjust (scale * page_inc)); break; case GDK_SCROLL_DOWN: case GDK_SCROLL_LEFT: - _controllable->set_value (adjust (-scale * step_inc)); + _controllable->set_value (adjust (-scale * page_inc)); break; } diff --git a/libs/gtkmm2ext/pixfader.cc b/libs/gtkmm2ext/pixfader.cc index 9851b6397d..02597f331e 100644 --- a/libs/gtkmm2ext/pixfader.cc +++ b/libs/gtkmm2ext/pixfader.cc @@ -28,17 +28,19 @@ using namespace Gtkmm2ext; using namespace Gtk; using namespace std; -int PixFader::fine_scale_modifier = Keyboard::PrimaryModifier; -int PixFader::extra_fine_scale_modifier = Keyboard::SecondaryModifier; - -PixFader::PixFader (Glib::RefPtr belt, Gtk::Adjustment& adj, int orientation, int fader_length) - +PixFader::PixFader ( + Glib::RefPtr belt, + Glib::RefPtr belt_desensitised, + Gtk::Adjustment& adj, + int orientation, + int fader_length + ) : adjustment (adj), - pixbuf (belt), _orien(orientation) { - Cairo::Format format; - + pixbuf[NORMAL] = belt; + pixbuf[DESENSITISED] = belt_desensitised; + dragging = false; default_value = adjustment.get_value(); last_drawn = -1; @@ -47,9 +49,9 @@ PixFader::PixFader (Glib::RefPtr belt, Gtk::Adjustment& adj, int or view.y = 0; if (orientation == VERT) { - view.width = girth = pixbuf->get_width(); + view.width = girth = pixbuf[0]->get_width(); } else { - view.height = girth = pixbuf->get_height(); + view.height = girth = pixbuf[0]->get_height(); } set_fader_length (fader_length); @@ -59,16 +61,21 @@ PixFader::PixFader (Glib::RefPtr belt, Gtk::Adjustment& adj, int or adjustment.signal_value_changed().connect (mem_fun (*this, &PixFader::adjustment_changed)); adjustment.signal_changed().connect (mem_fun (*this, &PixFader::adjustment_changed)); - if (pixbuf->get_has_alpha()) { - format = Cairo::FORMAT_ARGB32; - } else { - format = Cairo::FORMAT_RGB24; - } - belt_surface = Cairo::ImageSurface::create (format, pixbuf->get_width(), pixbuf->get_height()); - belt_context = Cairo::Context::create (belt_surface); - Gdk::Cairo::set_source_pixbuf (belt_context, pixbuf, 0.0, 0.0); - belt_context->paint(); + for (int i = 0; i < STATES; ++i) { + Cairo::Format format; + + if (pixbuf[i]->get_has_alpha()) { + format = Cairo::FORMAT_ARGB32; + } else { + format = Cairo::FORMAT_RGB24; + } + belt_surface[i] = Cairo::ImageSurface::create (format, pixbuf[i]->get_width(), pixbuf[i]->get_height()); + belt_context[i] = Cairo::Context::create (belt_surface[i]); + Gdk::Cairo::set_source_pixbuf (belt_context[i], pixbuf[i], 0.0, 0.0); + belt_context[i]->paint(); + } + left_r = 0; left_g = 0; left_b = 0; @@ -99,6 +106,8 @@ PixFader::set_border_colors (uint32_t left, uint32_t right) bool PixFader::on_expose_event (GdkEventExpose* ev) { + int const pi = get_sensitive() ? NORMAL : DESENSITISED; + Cairo::RefPtr context = get_window()->create_cairo_context(); int srcx, srcy; int const ds = display_span (); @@ -106,9 +115,9 @@ PixFader::on_expose_event (GdkEventExpose* ev) /* account for fader lengths that are shorter than the fader pixbuf */ if (_orien == VERT) { - offset_into_pixbuf += pixbuf->get_height() / 2 - view.height; + offset_into_pixbuf += pixbuf[pi]->get_height() / 2 - view.height; } else { - offset_into_pixbuf += pixbuf->get_width() / 2 - view.width; + offset_into_pixbuf += pixbuf[pi]->get_width() / 2 - view.width; } context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height); @@ -125,7 +134,7 @@ PixFader::on_expose_event (GdkEventExpose* ev) /* fader */ context->save(); - context->set_source (belt_surface, -srcx, -srcy); + context->set_source (belt_surface[pi], -srcx, -srcy); context->rectangle (0, 0, get_width(), get_height()); context->clip (); context->paint(); @@ -221,7 +230,7 @@ PixFader::on_button_release_event (GdkEventButton* ev) if (ev->state & Keyboard::TertiaryModifier) { adjustment.set_value (default_value); - } else if (ev->state & fine_scale_modifier) { + } else if (ev->state & Keyboard::GainFineScaleModifier) { adjustment.set_value (adjustment.get_lower()); } else if ((_orien == VERT && ev_pos < span - display_span()) || (_orien == HORIZ && ev_pos > span - display_span())) { /* above the current display height, remember X Window coords */ @@ -255,8 +264,8 @@ PixFader::on_scroll_event (GdkEventScroll* ev) double scale; bool ret = false; - if (ev->state & fine_scale_modifier) { - if (ev->state & extra_fine_scale_modifier) { + if (ev->state & Keyboard::GainFineScaleModifier) { + if (ev->state & Keyboard::GainExtraFineScaleModifier) { scale = 0.01; } else { scale = 0.05; @@ -320,8 +329,8 @@ PixFader::on_motion_notify_event (GdkEventMotion* ev) return true; } - if (ev->state & fine_scale_modifier) { - if (ev->state & extra_fine_scale_modifier) { + if (ev->state & Keyboard::GainFineScaleModifier) { + if (ev->state & Keyboard::GainExtraFineScaleModifier) { scale = 0.05; } else { scale = 0.1; diff --git a/libs/gtkmm2ext/slider_controller.cc b/libs/gtkmm2ext/slider_controller.cc index f86fd19820..c55697a2ee 100644 --- a/libs/gtkmm2ext/slider_controller.cc +++ b/libs/gtkmm2ext/slider_controller.cc @@ -28,9 +28,14 @@ using namespace Gtkmm2ext; using namespace PBD; -SliderController::SliderController (Glib::RefPtr image, Gtk::Adjustment *adj, int orientation, int fader_length) - - : PixFader (image, *adj, orientation, fader_length), +SliderController::SliderController ( + Glib::RefPtr image, + Glib::RefPtr image_desensitised, + Gtk::Adjustment *adj, + int orientation, + int fader_length + ) + : PixFader (image, image_desensitised, *adj, orientation, fader_length), spin (*adj, 0, 2) { spin.set_name ("SliderControllerValue"); @@ -55,11 +60,11 @@ SliderController::on_button_press_event (GdkEventButton *ev) return PixFader::on_button_press_event (ev); } -VSliderController::VSliderController (Glib::RefPtr image, +VSliderController::VSliderController (Glib::RefPtr image, Glib::RefPtr image_desensitised, Gtk::Adjustment *adj, int fader_length, bool with_numeric) - : SliderController (image, adj, VERT, fader_length) + : SliderController (image, image_desensitised, adj, VERT, fader_length) { if (with_numeric) { spin_frame.add (spin); @@ -70,11 +75,11 @@ VSliderController::VSliderController (Glib::RefPtr image, } } -HSliderController::HSliderController (Glib::RefPtr image, +HSliderController::HSliderController (Glib::RefPtr image, Glib::RefPtr image_desensitised, Gtk::Adjustment *adj, int fader_length, bool with_numeric) - : SliderController (image, adj, HORIZ, fader_length) + : SliderController (image, image_desensitised, adj, HORIZ, fader_length) { if (with_numeric) { spin_frame.add (spin); diff --git a/libs/midi++2/midi++/midnam_patch.h b/libs/midi++2/midi++/midnam_patch.h index 505245ae84..11bdb79f8b 100644 --- a/libs/midi++2/midi++/midnam_patch.h +++ b/libs/midi++2/midi++/midnam_patch.h @@ -21,6 +21,7 @@ #ifndef MIDNAM_PATCH_H_ #define MIDNAM_PATCH_H_ +#include #include #include #include @@ -73,7 +74,7 @@ public: return true; } else if (msb == id.msb && lsb < id.lsb) { return true; - } else if (lsb == id.lsb && program_number < id.program_number) { + } else if (msb == id.msb && lsb == id.lsb && program_number < id.program_number) { return true; } @@ -139,8 +140,6 @@ private: std::string _patch_list_name; }; -#include - class ChannelNameSet { public: diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index 89b0e283dc..599fabaa87 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -48,8 +48,8 @@ class Port { Port (const XMLNode&); virtual ~Port (); - XMLNode& get_state () const; - void set_state (const XMLNode&); + virtual XMLNode& get_state () const; + virtual void set_state (const XMLNode&); // FIXME: make Manager a friend of port so these can be hidden? diff --git a/libs/panners/2in2out/panner_2in2out.cc b/libs/panners/2in2out/panner_2in2out.cc index 8e798315d0..57b8836787 100644 --- a/libs/panners/2in2out/panner_2in2out.cc +++ b/libs/panners/2in2out/panner_2in2out.cc @@ -123,9 +123,22 @@ Panner2in2out::set_width (double p) } } +void +Panner2in2out::thaw () +{ + Panner::thaw (); + if (_frozen == 0) { + update (); + } +} + void Panner2in2out::update () { + if (_frozen) { + return; + } + /* it would be very nice to split this out into a virtual function that can be accessed from BaseStereoPanner and used in do_distribute_automated(). diff --git a/libs/panners/2in2out/panner_2in2out.h b/libs/panners/2in2out/panner_2in2out.h index 232d63ec62..001d3064e8 100644 --- a/libs/panners/2in2out/panner_2in2out.h +++ b/libs/panners/2in2out/panner_2in2out.h @@ -67,6 +67,7 @@ class Panner2in2out : public Panner void update (); void reset (); + void thaw (); protected: float left[2]; diff --git a/libs/pbd/filesystem.cc b/libs/pbd/filesystem.cc index a4bf781802..819a044a7b 100644 --- a/libs/pbd/filesystem.cc +++ b/libs/pbd/filesystem.cc @@ -254,6 +254,39 @@ get_absolute_path (const path & p) return f->get_path (); } +/** @return true if a and b have the same inode */ +bool +inodes_same (const path& a, const path& b) +{ + struct stat bA; + int const rA = stat (a.to_string().c_str(), &bA); + struct stat bB; + int const rB = stat (b.to_string().c_str(), &bB); + + return (rA == 0 && rB == 0 && bA.st_ino == bB.st_ino); +} + +/** Find out if `needle' is a file or directory within the + * directory `haystack'. + * @return true if it is. + */ +bool +path_is_within (path const & haystack, path needle) +{ + while (1) { + if (inodes_same (haystack, needle)) { + return true; + } + + needle = needle.branch_path (); + if (needle.to_string().empty() || needle.to_string() == "/") { + break; + } + } + + return false; +} + } // namespace sys } // namespace PBD diff --git a/libs/pbd/pbd/filesystem.h b/libs/pbd/pbd/filesystem.h index e8073adf0e..1fb6fbc155 100644 --- a/libs/pbd/pbd/filesystem.h +++ b/libs/pbd/pbd/filesystem.h @@ -205,6 +205,10 @@ std::string extension (const path& p); path get_absolute_path (const path &); +bool path_is_within (const path &, path); + +bool inodes_same (const path &, const path &); + } // namespace sys } // namespace PBD diff --git a/libs/pbd/test/filesystem_test.cc b/libs/pbd/test/filesystem_test.cc new file mode 100644 index 0000000000..11371c8cb6 --- /dev/null +++ b/libs/pbd/test/filesystem_test.cc @@ -0,0 +1,35 @@ +#include +#include +#include "filesystem_test.h" +#include "pbd/filesystem.h" + +using namespace std; + +CPPUNIT_TEST_SUITE_REGISTRATION (FilesystemTest); + +void +FilesystemTest::testPathIsWithin () +{ + system ("rm -r foo"); + PBD::sys::create_directories ("foo/bar/baz"); + + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar/baz", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar")); + + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar/baz", "frobozz") == false); + + int const r = symlink ("bar", "foo/jim"); + CPPUNIT_ASSERT (r == 0); + + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar/baz", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar/baz")); + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/bar", "foo/bar")); + + CPPUNIT_ASSERT (PBD::sys::path_is_within ("foo/jim/baz", "frobozz") == false); +} + diff --git a/libs/pbd/test/filesystem_test.h b/libs/pbd/test/filesystem_test.h new file mode 100644 index 0000000000..57f26631d0 --- /dev/null +++ b/libs/pbd/test/filesystem_test.h @@ -0,0 +1,14 @@ +#include +#include + +class FilesystemTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (FilesystemTest); + CPPUNIT_TEST (testPathIsWithin); + CPPUNIT_TEST_SUITE_END (); + +public: + void testPathIsWithin (); + +}; + diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 48a7a731b2..f5be3ce77e 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -139,6 +139,7 @@ def build(bld): test/scalar_properties.cc test/signals_test.cc test/convert_test.cc + test/filesystem_test.cc '''.split() testobj.target = 'run-tests' testobj.includes = obj.includes + ['test', '../pbd'] diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index 8d2186cf0c..7c95220e76 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -51,6 +51,7 @@ DeviceInfo::DeviceInfo() , _uses_logic_control_buttons (false) , _uses_ipmidi (false) , _no_handshake (false) + , _has_meters (true) , _name (X_("Mackie Control Universal Pro")) { mackie_control_buttons (); @@ -290,6 +291,14 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */) _no_handshake = false; } + if ((child = node.child ("HasMeters")) != 0) { + if ((prop = child->property ("value")) != 0) { + _has_meters = string_is_affirmative (prop->value()); + } + } else { + _has_meters = true; + } + if ((child = node.child ("LogicControlButtons")) != 0) { if ((prop = child->property ("value")) != 0) { _uses_logic_control_buttons = string_is_affirmative (prop->value()); @@ -374,6 +383,12 @@ DeviceInfo::has_master_fader() const return _has_master_fader; } +bool +DeviceInfo::has_meters() const +{ + return _has_meters; +} + bool DeviceInfo::has_two_character_display() const { diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index 294bc091ed..1b97ee75bf 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -68,6 +68,7 @@ class DeviceInfo bool has_touch_sense_faders() const; bool uses_ipmidi() const; bool no_handshake() const; + bool has_meters() const; const std::string& name() const; static std::map device_info; @@ -88,6 +89,7 @@ class DeviceInfo bool _uses_logic_control_buttons; bool _uses_ipmidi; bool _no_handshake; + bool _has_meters; std::string _name; std::map _global_buttons; diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index f8aada29bc..b13314fdc4 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -107,6 +107,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session) , _modifier_state (0) , _ipmidi_base (MIDI::IPMIDIPort::lowest_ipmidi_port_default) , needs_ipmidi_restart (false) + , _metering_active (true) { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n"); @@ -123,7 +124,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session) MackieControlProtocol::~MackieControlProtocol() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n"); - + drop_connections (); tear_down_gui (); @@ -472,7 +473,7 @@ MackieControlProtocol::update_global_led (int id, LedState ls) { boost::shared_ptr surface = surfaces.front(); - if (!surface->type() == mcu) { + if (surface->type() != mcu) { return; } @@ -713,40 +714,29 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) return retval; } - -///////////////////////////////////////////////// -// handlers for Route signals -// TODO should these be part of RouteSignal? -// They started off as signal/slot handlers for signals -// from Route, but they're also used in polling for automation -///////////////////////////////////////////////// - -// TODO handle plugin automation polling string MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) { Timecode::BBT_Time bbt_time; + session->bbt_time (now_frame, bbt_time); - // According to the Logic docs - // digits: 888/88/88/888 - // BBT mode: Bars/Beats/Subdivisions/Ticks + // The Mackie protocol spec is built around a BBT time display of + // + // digits: 888/88/88/888 + // semantics: BBB/bb/ss/ttt + // + // The third field is "subdivisions" which is a concept found in Logic + // but not present in Ardour. Instead Ardour displays a 4 digit tick + // count, which we need to spread across the 5 digits of ss/ttt. + ostringstream os; + os << setw(3) << setfill('0') << bbt_time.bars; os << setw(2) << setfill('0') << bbt_time.beats; - - // figure out subdivisions per beat - const ARDOUR::Meter & meter = session->tempo_map().meter_at (now_frame); - int subdiv = 2; - if (meter.note_divisor() == 8 && (meter.divisions_per_bar() == 12.0 || meter.divisions_per_bar() == 9.0 || meter.divisions_per_bar() == 6.0)) { - subdiv = 3; - } - - uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv); - uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv); - - os << setw(2) << setfill('0') << subdivisions + 1; - os << setw(3) << setfill('0') << ticks; + os << ' '; + os << setw(1) << setfill('0') << bbt_time.ticks / 1000; + os << setw(3) << setfill('0') << bbt_time.ticks % 1000; return os.str(); } @@ -761,10 +751,12 @@ MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) // digits: 888/88/88/888 // Timecode mode: Hours/Minutes/Seconds/Frames ostringstream os; - os << setw(3) << setfill('0') << timecode.hours; + os << ' '; + os << setw(2) << setfill('0') << timecode.hours; os << setw(2) << setfill('0') << timecode.minutes; os << setw(2) << setfill('0') << timecode.seconds; - os << setw(3) << setfill('0') << timecode.frames; + os << ' '; + os << setw(2) << setfill('0') << timecode.frames; return os.str(); } @@ -778,7 +770,7 @@ MackieControlProtocol::update_timecode_display() boost::shared_ptr surface = surfaces.front(); - if (surface->type() != mcu || !_device_info.has_timecode_display()) { + if (surface->type() != mcu || !_device_info.has_timecode_display() || !surface->active ()) { return; } @@ -893,9 +885,19 @@ MackieControlProtocol::notify_transport_state_changed() update_global_button (Button::Rewind, session->transport_speed() < 0.0); update_global_button (Button::Ffwd, session->transport_speed() > 1.0); + notify_metering_state_changed (); + _transport_previously_rolling = session->transport_rolling(); } +void +MackieControlProtocol::notify_metering_state_changed() +{ + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->notify_metering_state_changed (); + } +} + void MackieControlProtocol::notify_record_state_changed () { @@ -1133,6 +1135,18 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) if (ioc & IO_IN) { + /* Devices using regular JACK MIDI ports will need to have + the x-thread FIFO drained to avoid burning endless CPU. + + Devices using ipMIDI have port->selectable() as the same + file descriptor that data arrives on, so doing this + for them will simply throw all incoming data away. + */ + + if (!_device_info.uses_ipmidi()) { + CrossThreadChannel::drain (port->selectable()); + } + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name())); framepos_t now = session->engine().frame_time(); port->parse (now); diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index 0fd5d8c8a2..eb3cc22b8c 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -128,6 +128,7 @@ class MackieControlProtocol bool flip_mode () const { return _flip_mode; } ViewMode view_mode () const { return _view_mode; } bool zoom_mode () const { return _zoom_mode; } + bool metering_active () const { return _metering_active; } void set_view_mode (ViewMode); void set_flip_mode (bool); @@ -164,6 +165,7 @@ class MackieControlProtocol void notify_record_state_changed(); void notify_transport_state_changed(); void notify_loop_state_changed(); + void notify_metering_state_changed(); // mainly to pick up punch-in and punch-out void notify_parameter_changed(std::string const &); void notify_solo_active_changed(bool); @@ -171,7 +173,7 @@ class MackieControlProtocol /// Turn timecode on and beats off, or vice versa, depending /// on state of _timecode_type void update_timecode_beats_led(); - + /// this is called to generate the midi to send in response to a button press. void update_led(Mackie::Surface&, Mackie::Button & button, Mackie::LedState); @@ -286,6 +288,7 @@ class MackieControlProtocol ButtonMap button_map; int16_t _ipmidi_base; bool needs_ipmidi_restart; + bool _metering_active; ARDOUR::RouteNotificationList _last_selected_routes; diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc index c864b5b871..be51f01651 100644 --- a/libs/surfaces/mackie/mcp_buttons.cc +++ b/libs/surfaces/mackie/mcp_buttons.cc @@ -1009,12 +1009,14 @@ MackieControlProtocol::snapshot_release (Mackie::Button&) Mackie::LedState MackieControlProtocol::read_press (Mackie::Button&) { - return none; + _metering_active = !_metering_active; + notify_metering_state_changed (); + return _metering_active; } Mackie::LedState MackieControlProtocol::read_release (Mackie::Button&) { - return none; + return _metering_active; } Mackie::LedState MackieControlProtocol::write_press (Mackie::Button&) diff --git a/libs/surfaces/mackie/meter.cc b/libs/surfaces/mackie/meter.cc index 82aef2bef8..fe11357d3a 100644 --- a/libs/surfaces/mackie/meter.cc +++ b/libs/surfaces/mackie/meter.cc @@ -40,6 +40,29 @@ Meter::factory (Surface& surface, int id, const char* name, Group& group) return m; } +void +Meter::notify_metering_state_changed(Surface& surface, bool transport_is_rolling, bool metering_active) +{ + MidiByteArray msg; + + // sysex header + msg << surface.sysex_hdr(); + + // code for Channel Meter Enable Message + msg << 0x20; + + // Channel identification + msg << id(); + + // Enable (0x07) / Disable (0x00) level meter on LCD, peak hold display on horizontal meter and signal LED + msg << ((transport_is_rolling && metering_active) ? 0x07 : 0x00); + + // sysex trailer + msg << MIDI::eox; + + surface.write (msg); +} + void Meter::send_update (Surface& surface, float dB) { @@ -89,10 +112,7 @@ Meter::send_update (Surface& surface, float dB) int segment = lrintf ((def/115.0) * 13.0); - if (last_segment_value_sent != segment) { - last_segment_value_sent = segment; - surface.write (MidiByteArray (2, 0xD0, (id()<<4) | segment)); - } + surface.write (MidiByteArray (2, 0xd0, (id()<<4) | segment)); } MidiByteArray diff --git a/libs/surfaces/mackie/meter.h b/libs/surfaces/mackie/meter.h index b94c7ac98a..db3e8cffb3 100644 --- a/libs/surfaces/mackie/meter.h +++ b/libs/surfaces/mackie/meter.h @@ -32,7 +32,6 @@ class Meter : public Control public: Meter (int id, std::string name, Group & group) : Control (id, name, group) - , last_segment_value_sent (-1) , overload_on (false) {} void send_update (Surface&, float dB); @@ -40,8 +39,8 @@ public: MidiByteArray zero(); static Control* factory (Surface&, int id, const char*, Group&); - - int last_segment_value_sent; + + void notify_metering_state_changed(Surface& surface, bool transport_is_rolling, bool metering_active); private: bool overload_on; diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc index d886f5a658..84c32138a3 100644 --- a/libs/surfaces/mackie/strip.cc +++ b/libs/surfaces/mackie/strip.cc @@ -73,6 +73,8 @@ Strip::Strip (Surface& s, const std::string& name, int index, const map (Fader::factory (*_surface, index, "fader", *this)); _vpot = dynamic_cast (Pot::factory (*_surface, Pot::ID + index, "vpot", *this)); - _meter = dynamic_cast (Meter::factory (*_surface, index, "meter", *this)); + + if (s.mcp().device_info().has_meters()) { + _meter = dynamic_cast (Meter::factory (*_surface, index, "meter", *this)); + } for (map::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) { Button* bb = dynamic_cast (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this)); @@ -687,7 +692,7 @@ Strip::update_automation () void Strip::update_meter () { - if (_meter) { + if (_meter && _transport_is_rolling && _metering_active) { float dB = const_cast (_route->peak_meter()).peak_power (0); _meter->send_update (*_surface, dB); } @@ -1063,3 +1068,28 @@ Strip::reset_saved_values () _last_gain_position_written = -1.0; } + +void +Strip::notify_metering_state_changed() +{ + if (!_route || !_meter) { + return; + } + + bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f); + bool metering_active = _surface->mcp().metering_active (); + + if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) { + return; + } + + _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active); + + if (!transport_is_rolling || !metering_active) { + notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name)); + notify_panner_azi_changed (true); + } + + _transport_is_rolling = transport_is_rolling; + _metering_active = metering_active; +} diff --git a/libs/surfaces/mackie/strip.h b/libs/surfaces/mackie/strip.h index 99e51ae6e1..d5fdc6c17c 100644 --- a/libs/surfaces/mackie/strip.h +++ b/libs/surfaces/mackie/strip.h @@ -83,6 +83,8 @@ public: bool locked() const { return _controls_locked; } void gui_selection_changed (const ARDOUR::StrongRouteNotificationList&); + + void notify_metering_state_changed(); private: Button* _solo; @@ -97,6 +99,8 @@ private: int _index; Surface* _surface; bool _controls_locked; + bool _transport_is_rolling; + bool _metering_active; uint64_t _reset_display_at; boost::shared_ptr _route; PBD::ScopedConnectionList route_connections; diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 7617156589..a63f4757e7 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -409,9 +409,9 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count */ if (_stype == mcu) { - mackie_sysex_hdr[3] = bytes[4]; + mackie_sysex_hdr[4] = bytes[4]; } else { - mackie_sysex_hdr_xt[3] = bytes[4]; + mackie_sysex_hdr_xt[4] = bytes[4]; } switch (bytes[5]) { @@ -515,6 +515,15 @@ Surface::turn_it_on () (*s)->notify_all (); } update_view_mode_display (); + if (_mcp.device_info ().has_master_fader ()) { + master_gain_changed (); + } + + if (_mcp.device_info ().has_global_controls ()) { + _mcp.update_global_button (Button::Read, _mcp.metering_active ()); + _mcp.update_global_button (Button::Stop, _mcp.get_session ().transport_stopped ()); + _mcp.update_global_button (Button::Play, (_mcp.get_session ().transport_speed () == 1.0f)); + } } } @@ -573,8 +582,18 @@ Surface::nth_strip (uint32_t n) const void Surface::zero_all () { - // TODO turn off Timecode displays + if (_mcp.device_info().has_timecode_display ()) { + display_timecode (string (10, '0'), string (10, ' ')); + } + + if (_mcp.device_info().has_two_character_display()) { + show_two_char_display (string (2, '0'), string (2, ' ')); + } + if (_mcp.device_info().has_master_fader ()) { + _port->write (_master_fader->zero ()); + } + // zero all strips for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) { (*it)->zero(); @@ -591,7 +610,7 @@ Surface::zero_controls () } // turn off global buttons and leds - // global buttons are only ever on mcu_port, so we don't have + // global buttons are only ever on mcu_port, so we don't have // to figure out which port. for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) { @@ -601,12 +620,6 @@ Surface::zero_controls () } } - if (_number == 0 && _mcp.device_info().has_two_character_display()) { - // any hardware-specific stuff - // clear 2-char display - show_two_char_display (" "); - } - // and the led ring for the master strip blank_jog_ring (); } @@ -692,19 +705,14 @@ Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/ show_two_char_display (os.str()); } -void -Surface::display_timecode (const std::string & timecode, const std::string & timecode_last) +void +Surface::display_timecode (const std::string & timecode, const std::string & last_timecode) { - if (_active && _mcp.device_info().has_timecode_display()) { - _port->write (timecode_display (timecode, timecode_last)); + if (!_active || !_mcp.device_info().has_timecode_display()) { + return; } -} - -MidiByteArray -Surface::timecode_display (const std::string & timecode, const std::string & last_timecode) -{ // if there's no change, send nothing, not even sysex header - if (timecode == last_timecode) return MidiByteArray(); + if (timecode == last_timecode) return; // length sanity checking string local_timecode = timecode; @@ -718,29 +726,20 @@ Surface::timecode_display (const std::string & timecode, const std::string & las while (local_timecode.length() < 10) { local_timecode += " "; } - - // find the suffix of local_timecode that differs from last_timecode - std::pair pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin()); - MidiByteArray retval; - - // sysex header - retval << sysex_hdr(); - - // code for timecode display - retval << 0x10; - - // translate characters. These are sent in reverse order of display - // hence the reverse iterators - string::reverse_iterator rend = reverse_iterator (pp.second); - for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) { - retval << translate_seven_segment (*it); + // translate characters. + // Only the characters that actually changed are sent. + int position = 0x3f; + int i; + for (i = local_timecode.length () - 1; i >= 0; i--) { + position++; + if (local_timecode[i] == last_timecode[i]) { + continue; + } + MidiByteArray retval (2, 0xb0, position); + retval << translate_seven_segment (local_timecode[i]); + _port->write (retval); } - - // sysex trailer - retval << MIDI::eox; - - return retval; } void @@ -859,3 +858,11 @@ Surface::route_is_locked_to_strip (boost::shared_ptr r) const } return false; } + +void +Surface::notify_metering_state_changed() +{ + for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) { + (*s)->notify_metering_state_changed (); + } +} diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 2034dd4096..1e33d117a5 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -136,13 +136,6 @@ public: void show_two_char_display (const std::string & msg, const std::string & dots = " "); void show_two_char_display (unsigned int value, const std::string & dots = " "); - /** - Timecode display. Only the difference between timecode and last_timecode will - be encoded, to save midi bandwidth. If they're the same, an empty array will - be returned - */ - MidiByteArray timecode_display (const std::string & timecode, const std::string & last_timecode = ""); - void update_view_mode_display (); void update_flip_mode_display (); @@ -152,6 +145,8 @@ public: void next_jog_mode (); void set_jog_mode (Mackie::JogWheel::Mode); + + void notify_metering_state_changed(); protected: diff --git a/mcp/bcf2000.device b/mcp/bcf2000.device new file mode 100644 index 0000000000..f3207d88b0 --- /dev/null +++ b/mcp/bcf2000.device @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/mcp/bcf2000_basic_edit_mix.profile b/mcp/bcf2000_basic_edit_mix.profile new file mode 100644 index 0000000000..e09917b686 --- /dev/null +++ b/mcp/bcf2000_basic_edit_mix.profile @@ -0,0 +1,47 @@ + + + + + +