Merge branch 'master' into export-dialog
authorColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 24 Feb 2014 18:11:48 +0000 (18:11 +0000)
committerColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 24 Feb 2014 18:11:48 +0000 (18:11 +0000)
49 files changed:
gtk2_ardour/ardour_ui.cc
gtk2_ardour/audio_region_view.cc
gtk2_ardour/audio_region_view.h
gtk2_ardour/automation_region_view.cc
gtk2_ardour/automation_region_view.h
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/automation_time_axis.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/gain_meter.cc
gtk2_ardour/level_meter.cc
gtk2_ardour/mixer_strip.cc
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
gtk2_ardour/plugin_ui.cc
gtk2_ardour/processor_box.cc
gtk2_ardour/route_ui.cc
gtk2_ardour/sfdb_ui.cc
gtk2_ardour/stereo_panner.cc
libs/ardour/amp.cc
libs/ardour/ardour/audio_buffer.h
libs/ardour/ardour/buffer.h
libs/ardour/ardour/file_source.h
libs/ardour/ardour/midi_buffer.h
libs/ardour/ardour/vestige/aeffectx.h
libs/ardour/audio_buffer.cc
libs/ardour/automation_control.cc
libs/ardour/automation_watch.cc
libs/ardour/delivery.cc
libs/ardour/export_channel.cc
libs/ardour/file_source.cc
libs/ardour/internal_send.cc
libs/ardour/lv2_evbuf.c
libs/ardour/plugin_insert.cc
libs/ardour/plugin_manager.cc
libs/ardour/route.cc
libs/ardour/session_state.cc
libs/evoral/evoral/ControlList.hpp
libs/evoral/src/Control.cpp
libs/evoral/src/ControlList.cpp
libs/panners/vbap/vbap.cc
libs/panners/vbap/vbap_speakers.cc
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/mackie/strip.cc
libs/surfaces/mackie/strip.h
libs/surfaces/mackie/surface.cc
wscript

index eb31ee4d80947dd544f64fe68881aae91b3119c6..262e8f990c5b706d4e2bfea49f88ccea796feacc 100644 (file)
@@ -496,10 +496,13 @@ ARDOUR_UI::post_engine ()
 
                vector<string>::iterator n;
                vector<string>::iterator k;
-               for (n = names.begin(), k = keys.begin(); n != names.end(); ++n, ++k) {
-                       cout << "Action: " << (*n) << " bound to " << (*k) << endl;
+               vector<string>::iterator p;
+               for (n = names.begin(), k = keys.begin(), p = paths.begin(); n != names.end(); ++n, ++k, ++p) {
+                       cout << "Action: '" << (*n) << "' bound to '" << (*k) << "' Path: '" << (*p) << "'" << endl;
                }
 
+               halt_connection.disconnect ();
+               AudioEngine::instance()->stop ();
                exit (0);
        }
 
@@ -3264,7 +3267,7 @@ ARDOUR_UI::setup_order_hint ()
        } else {
                for (TrackSelection::iterator s = editor->get_selection().tracks.begin(); s != editor->get_selection().tracks.end(); ++s) {
                        RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (*s);
-                       if (tav->route()->order_key() > order_hint) {
+                       if (tav && tav->route() && tav->route()->order_key() > order_hint) {
                                order_hint = tav->route()->order_key();
                        }
                }
index c6e42c0a4bd280a1da4a7ad14349363f6669fe8e..a42d5a0105f4e7adebc0f22704742b48442566d9 100644 (file)
@@ -963,7 +963,7 @@ AudioRegionView::peaks_ready_handler (uint32_t which)
 }
 
 void
-AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
+AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, bool with_guard_points)
 {
        if (!gain_line) {
                return;
@@ -1008,7 +1008,7 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
                trackview.session()->add_command (new MementoCommand<AudioRegion>(*(audio_region().get()), &region_before, &region_after));
        }
 
-       audio_region()->envelope()->add (fx, y);
+       audio_region()->envelope()->add (fx, y, with_guard_points);
 
        XMLNode &after = audio_region()->envelope()->get_state();
        trackview.session()->add_command (new MementoCommand<AutomationList>(*audio_region()->envelope().get(), &before, &after));
index 10159d0cfbf6970716051d7952ebf3ae803d033f..b7b3492e34467134f28b76cd9de5a330619ef935 100644 (file)
@@ -81,7 +81,7 @@ class AudioRegionView : public RegionView
 
        void update_envelope_visibility ();
 
-       void add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *event);
+        void add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *event, bool with_guard_points);
        void remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent *event);
 
        boost::shared_ptr<AudioRegionGainLine> get_gain_line() const { return gain_line; }
index 5c9fc767dfa04354713d853602e495295ce01e7b..409890e7b219efd9799e46d3dd640d8cdd532b82 100644 (file)
@@ -27,6 +27,8 @@
 #include "ardour/midi_region.h"
 #include "ardour/session.h"
 
+#include "gtkmm2ext/keyboard.h"
+
 #include "automation_region_view.h"
 #include "editing.h"
 #include "editor.h"
@@ -120,7 +122,11 @@ AutomationRegionView::canvas_event (GdkEvent* ev)
                y = std::max (y, 0.0);
                y = std::min (y, _height - NAME_HIGHLIGHT_SIZE);
 
-               add_automation_event (ev, trackview.editor().pixel_to_frame (x) - _region->position() + _region->start(), y);
+               /* guard points only if primary modifier is used */
+
+               bool with_guard_points = Gtkmm2ext::Keyboard::modifier_state_equals (ev->button.state, Gtkmm2ext::Keyboard::PrimaryModifier);
+
+               add_automation_event (ev, trackview.editor().pixel_to_frame (x) - _region->position() + _region->start(), y, with_guard_points);
        }
 
        return false;
@@ -130,7 +136,7 @@ AutomationRegionView::canvas_event (GdkEvent* ev)
  *  @param y y position, relative to our TimeAxisView.
  */
 void
-AutomationRegionView::add_automation_event (GdkEvent *, framepos_t when, double y)
+AutomationRegionView::add_automation_event (GdkEvent *, framepos_t when, double y, bool with_guard_points)
 {
        if (!_line) {
                boost::shared_ptr<Evoral::Control> c = _region->control(_parameter, true);
@@ -160,7 +166,7 @@ AutomationRegionView::add_automation_event (GdkEvent *, framepos_t when, double
        view->session()->begin_reversible_command (_("add automation event"));
        XMLNode& before = _line->the_list()->get_state();
 
-       _line->the_list()->add (when_d, y);
+       _line->the_list()->add (when_d, y, with_guard_points);
 
        XMLNode& after = _line->the_list()->get_state();
 
index 3e2a9b6bbff531965fcadc5fb471d14f148d88a3..8933b30b19e7d660a61e7c2f2c9a6661c5052b7a 100644 (file)
@@ -66,7 +66,7 @@ protected:
        bool set_position(framepos_t pos, void* src, double* ignored);
        void region_resized (const PBD::PropertyChange&);
        bool canvas_event(GdkEvent* ev);
-       void add_automation_event (GdkEvent* event, framepos_t when, double y);
+        void add_automation_event (GdkEvent* event, framepos_t when, double y, bool with_guard_points);
        void entered (bool);
        void exited();
 
index 848298c8b805a47f7ab4f9aa0773b71910dd763d..08690cd61c01b984f10082136e9eb53f958c1378 100644 (file)
@@ -550,7 +550,7 @@ AutomationTimeAxisView::build_display_menu ()
 }
 
 void
-AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, double y)
+AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, double y, bool with_guard_points)
 {
        if (!_line) {
                return;
@@ -583,7 +583,7 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when,
        _session->begin_reversible_command (_("add automation event"));
        XMLNode& before = list->get_state();
 
-       list->add (when, y);
+       list->add (when, y, with_guard_points);
 
        XMLNode& after = list->get_state();
        _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
index f1a9a8bd57327a35a7da6c81d9b4cb02f53a6196..3e298318726a912be190a1212f5a168776df7925 100644 (file)
@@ -72,7 +72,7 @@ class AutomationTimeAxisView : public TimeAxisView {
        void set_samples_per_unit (double);
        std::string name() const { return _name; }
 
-       void add_automation_event (GdkEvent *, framepos_t, double);
+        void add_automation_event (GdkEvent *, framepos_t, double, bool with_guard_points);
 
        void clear_lines ();
 
index a5e2e8a369b19fa98f19d50cbdcb448afb850e08..62d67e8ec83d9b709afe73c1d862ff7d73f36387 100644 (file)
@@ -1143,94 +1143,94 @@ Editor::prev_snap_choice ()
 {
        switch (_snap_type) {
        case Editing::SnapToCDFrame:
-               set_snap_to (Editing::SnapToTimecodeFrame);
+               set_snap_to (Editing::SnapToRegionBoundary);
                break;
        case Editing::SnapToTimecodeFrame:
-               set_snap_to (Editing::SnapToTimecodeSeconds);
+               set_snap_to (Editing::SnapToCDFrame);
                break;
        case Editing::SnapToTimecodeSeconds:
-               set_snap_to (Editing::SnapToTimecodeMinutes);
+               set_snap_to (Editing::SnapToTimecodeFrame);
                break;
        case Editing::SnapToTimecodeMinutes:
-               set_snap_to (Editing::SnapToSeconds);
+               set_snap_to (Editing::SnapToTimecodeSeconds);
                break;
        case Editing::SnapToSeconds:
-               set_snap_to (Editing::SnapToMinutes);
+               set_snap_to (Editing::SnapToTimecodeMinutes);
                break;
        case Editing::SnapToMinutes:
-               set_snap_to (Editing::SnapToBeatDiv128);
+               set_snap_to (Editing::SnapToSeconds);
                break;
        case Editing::SnapToBeatDiv128:
-               set_snap_to (Editing::SnapToBeatDiv64);
+               set_snap_to (Editing::SnapToMinutes);
                break;
        case Editing::SnapToBeatDiv64:
-               set_snap_to (Editing::SnapToBeatDiv32);
+               set_snap_to (Editing::SnapToBeatDiv128);
                break;
        case Editing::SnapToBeatDiv32:
-               set_snap_to (Editing::SnapToBeatDiv28);
+               set_snap_to (Editing::SnapToBeatDiv64);
                break;
        case Editing::SnapToBeatDiv28:
-               set_snap_to (Editing::SnapToBeatDiv24);
+               set_snap_to (Editing::SnapToBeatDiv32);
                break;
        case Editing::SnapToBeatDiv24:
-               set_snap_to (Editing::SnapToBeatDiv20);
+               set_snap_to (Editing::SnapToBeatDiv28);
                break;
        case Editing::SnapToBeatDiv20:
-               set_snap_to (Editing::SnapToBeatDiv16);
+               set_snap_to (Editing::SnapToBeatDiv24);
                break;
        case Editing::SnapToBeatDiv16:
-               set_snap_to (Editing::SnapToBeatDiv14);
+               set_snap_to (Editing::SnapToBeatDiv20);
                break;
        case Editing::SnapToBeatDiv14:
-               set_snap_to (Editing::SnapToBeatDiv12);
+               set_snap_to (Editing::SnapToBeatDiv16);
                break;
        case Editing::SnapToBeatDiv12:
-               set_snap_to (Editing::SnapToBeatDiv10);
+               set_snap_to (Editing::SnapToBeatDiv14);
                break;
        case Editing::SnapToBeatDiv10:
-               set_snap_to (Editing::SnapToBeatDiv8);
+               set_snap_to (Editing::SnapToBeatDiv12);
                break;
        case Editing::SnapToBeatDiv8:
-               set_snap_to (Editing::SnapToBeatDiv7);
+               set_snap_to (Editing::SnapToBeatDiv10);
                break;
        case Editing::SnapToBeatDiv7:
-               set_snap_to (Editing::SnapToBeatDiv6);
+               set_snap_to (Editing::SnapToBeatDiv8);
                break;
        case Editing::SnapToBeatDiv6:
-               set_snap_to (Editing::SnapToBeatDiv5);
+               set_snap_to (Editing::SnapToBeatDiv7);
                break;
        case Editing::SnapToBeatDiv5:
-               set_snap_to (Editing::SnapToBeatDiv4);
+               set_snap_to (Editing::SnapToBeatDiv6);
                break;
        case Editing::SnapToBeatDiv4:
-               set_snap_to (Editing::SnapToBeatDiv3);
+               set_snap_to (Editing::SnapToBeatDiv5);
                break;
        case Editing::SnapToBeatDiv3:
-               set_snap_to (Editing::SnapToBeatDiv2);
+               set_snap_to (Editing::SnapToBeatDiv4);
                break;
        case Editing::SnapToBeatDiv2:
-               set_snap_to (Editing::SnapToBeat);
+               set_snap_to (Editing::SnapToBeatDiv3);
                break;
        case Editing::SnapToBeat:
-               set_snap_to (Editing::SnapToBar);
+               set_snap_to (Editing::SnapToBeatDiv2);
                break;
        case Editing::SnapToBar:
-               set_snap_to (Editing::SnapToMark);
+               set_snap_to (Editing::SnapToBeat);
                break;
        case Editing::SnapToMark:
-               set_snap_to (Editing::SnapToRegionStart);
+               set_snap_to (Editing::SnapToBar);
                break;
        case Editing::SnapToRegionStart:
-               set_snap_to (Editing::SnapToRegionEnd);
+               set_snap_to (Editing::SnapToMark);
                break;
        case Editing::SnapToRegionEnd:
-               set_snap_to (Editing::SnapToRegionSync);
+               set_snap_to (Editing::SnapToRegionStart);
                break;
        case Editing::SnapToRegionSync:
-               set_snap_to (Editing::SnapToRegionBoundary);
+               set_snap_to (Editing::SnapToRegionEnd);
                break;
        case Editing::SnapToRegionBoundary:
-               set_snap_to (Editing::SnapToCDFrame);
+               set_snap_to (Editing::SnapToRegionSync);
                break;
        }
 }
index a7060dd433836c90adc13f710f2cbcedf92912e8..8118aa3dbc120f11e13d04b4622584d94c48f416 100644 (file)
@@ -3275,10 +3275,24 @@ LineDrag::motion (GdkEvent* event, bool)
 }
 
 void
-LineDrag::finished (GdkEvent* event, bool)
+LineDrag::finished (GdkEvent* event, bool movement_occured)
 {
-       motion (event, false);
-       _line->end_drag (false, 0);
+       if (movement_occured) {
+               motion (event, false);
+               _line->end_drag (false, 0);
+       } else {
+               /* add a new control point on the line */
+
+               AutomationTimeAxisView* atv;
+
+               _line->end_drag (false, 0);
+
+               if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
+                       framepos_t where = _editor->event_frame (event, 0, 0);
+                       atv->add_automation_event (event, where, event->button.y, false);
+               }
+       }
+
        _editor->session()->commit_reversible_command ();
 }
 
index 7f90fe2d0fffc0065fd0b3f3a6d215dd85e42593..e20bd863849e4fb0c6ab75f8ee91b67fe958db44 100644 (file)
@@ -1682,7 +1682,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case AutomationTrackItem:
                                atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
                                if (atv) {
-                                       atv->add_automation_event (event, where, event->button.y);
+                                       bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
+                                       atv->add_automation_event (event, where, event->button.y, with_guard_points);
                                }
                                return true;
                                break;
@@ -1701,17 +1702,20 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                */
                                AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
                                if (!were_dragging && arv) {
-                                       arv->add_gain_point_event (item, event);
+                                       bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
+                                       arv->add_gain_point_event (item, event, with_guard_points);
                                }
                                return true;
                                break;
                        }
 
-                       case AutomationTrackItem:
+                       case AutomationTrackItem: {
+                               bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
                                dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
-                                       add_automation_event (event, where, event->button.y);
+                                       add_automation_event (event, where, event->button.y, with_guard_points);
                                return true;
                                break;
+                       }
                        default:
                                break;
                        }
index 53dd0062245bfae345a98da944f1afdafa41fc2c..c2a507103044d5a68f33e6e08c612adbd1442e47 100644 (file)
@@ -274,18 +274,24 @@ void
 GainMeterBase::setup_meters (int len)
 {
        int meter_width = 5;
+       uint32_t meter_channels = 0;
+       if (_meter) {
+               meter_channels = _meter->input_streams().n_total();
+       } else if (_route) {
+               meter_channels = _route->shared_peak_meter()->input_streams().n_total();
+       }
 
        switch (_width) {
                case Wide:
                        //meter_ticks1_area.show();
                        //meter_ticks2_area.show();
                        meter_metric_area.show();
-                       if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
+                       if (meter_channels == 1) {
                                meter_width = 10;
                        }
                        break;
                case Narrow:
-                       if (_route && _route->shared_peak_meter()->input_streams().n_total() > 1) {
+                       if (meter_channels > 1) {
                                meter_width = 4;
                        }
                        //meter_ticks1_area.hide();
index 493a08baea6b9257fb2a53adeb986bb079d514ca..d76b450e384a541e3613a22a741f3e4f33b7a6b3 100644 (file)
@@ -78,6 +78,7 @@ LevelMeterBase::set_meter (PeakMeter* meter)
        _meter_type_connection.disconnect();
 
        _meter = meter;
+       color_changed = true;
 
        if (_meter) {
                _meter->ConfigurationChanged.connect (_configuration_connection, parent_invalidator, boost::bind (&LevelMeterBase::configuration_changed, this, _1, _2), gui_context());
index 830d2bf161bcf4934d70978a6d5ee4b90c0d9f8c..284f46ce4babe84614627746af943aeadd624f91 100644 (file)
@@ -37,6 +37,7 @@
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/internal_send.h"
+#include "ardour/meter.h"
 #include "ardour/midi_track.h"
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
@@ -1033,6 +1034,7 @@ MixerStrip::connect_to_pan ()
        if (panners._panner == 0) {
                panners.panshell_changed ();
        }
+       update_panner_choices();
 }
 
 void
@@ -1869,6 +1871,7 @@ MixerStrip::show_send (boost::shared_ptr<Send> send)
 
        set_current_delivery (send);
 
+       send->meter()->set_type(_route->shared_peak_meter()->get_type());
        send->set_metering (true);
        _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
 
@@ -2159,8 +2162,8 @@ MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
 
        _suspend_menu_callbacks = true;
        add_level_meter_item_point (items, group, _("Input"), MeterInput);
-       add_level_meter_item_point (items, group, _("Pre-fader"), MeterPreFader);
-       add_level_meter_item_point (items, group, _("Post-fader"), MeterPostFader);
+       add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
+       add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
        add_level_meter_item_point (items, group, _("Output"), MeterOutput);
        add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
 
index 2a6568992b39a54db3038d60c9e66ff8ccdc332a..c0c305fae1f72e1023c343fdf815ac67adf7e2d0 100644 (file)
@@ -49,6 +49,7 @@ const int PannerUI::pan_bar_height = 35;
 PannerUI::PannerUI (Session* s)
        : _current_nouts (-1)
        , _current_nins (-1)
+       , _current_uri ("")
        , pan_automation_style_button ("")
        , pan_automation_state_button ("")
        , _panner_list()
@@ -214,12 +215,17 @@ PannerUI::setup_pan ()
        int const nouts = _panner ? _panner->out().n_audio() : -1;
        int const nins = _panner ? _panner->in().n_audio() : -1;
 
-       if (nouts == _current_nouts && nins == _current_nins) {
+       if (nouts == _current_nouts
+                       && nins == _current_nins
+                       && _current_uri == _panshell->panner_gui_uri()
+                       )
+       {
                return;
        }
 
         _current_nins = nins;
         _current_nouts = nouts;
+        _current_uri = _panshell->panner_gui_uri();
 
         container_clear (pan_vbox);
 
@@ -236,7 +242,7 @@ PannerUI::setup_pan ()
                return;
        }
 
-       if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_2in2out#ui")
+       if (_current_uri == "http://ardour.org/plugin/panner_2in2out#ui")
        {
                delete big_window;
                big_window = 0;
@@ -262,8 +268,8 @@ PannerUI::setup_pan ()
                                        boost::weak_ptr<AutomationControl>(ac)));
                _stereo_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
        }
-       else if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_1in2out#ui"
-                       || _panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_balance#ui")
+       else if (_current_uri == "http://ardour.org/plugin/panner_1in2out#ui"
+                       || _current_uri == "http://ardour.org/plugin/panner_balance#ui")
        {
                delete big_window;
                big_window = 0;
@@ -284,7 +290,7 @@ PannerUI::setup_pan ()
                update_pan_sensitive ();
                pan_vbox.pack_start (*_mono_panner, false, false);
        }
-       else if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_vbap#ui")
+       else if (_current_uri == "http://ardour.org/plugin/panner_vbap#ui")
        {
                if (!twod_panner) {
                        twod_panner = new Panner2d (_panshell, 61);
@@ -392,9 +398,6 @@ PannerUI::build_pan_menu ()
                RadioMenuItem::Group group;
                items.push_back (SeparatorElem());
 
-               assert(_panshell->user_selected_panner_uri() == ""
-                               ||  _panshell->user_selected_panner_uri() == _panshell->current_panner_uri());
-
                _suspend_menu_callbacks = true;
                for (std::map<std::string,std::string>::const_iterator p = _panner_list.begin(); p != _panner_list.end(); ++p) {
                        items.push_back (RadioMenuElem (group, p->second,
index 9b349d664f0f292b68e83a650f1e0b1905db0231..8bf448c7ea509c47815041718f6706cf142dff70 100644 (file)
@@ -96,6 +96,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        bool in_pan_update;
        int _current_nouts;
        int _current_nins;
+       std::string _current_uri;
 
        static const int pan_bar_height;
 
index 3f523a82d202872a06857a1c999ad9d42152d0dd..bb3cafb4cf10e079e1a7cc9cf12d4d4c5eba75d6 100644 (file)
@@ -161,7 +161,9 @@ PluginUIWindow::PluginUIWindow (
 
 PluginUIWindow::~PluginUIWindow ()
 {
+#ifndef NDEBUG
        cerr << "PluginWindow deleted for " << this << endl;
+#endif
        delete _pluginui;
 }
 
index c2421dcabf560cd822750a8165dc438d8204e974..cb72162773fb55fb770a6d21c970fed49cef0245 100644 (file)
@@ -844,6 +844,13 @@ ProcessorEntry::RoutingIcon::on_expose_event (GdkEventExpose* ev)
                cairo_move_to (cr, si_x, height);
                cairo_line_to (cr, si_x, 0);
                cairo_stroke (cr);
+       } else if (midi_sources == 1 && midi_sinks == 1) {
+               /* unusual cases -- removed synth, midi-track w/audio plugins */
+               const float si_x  = rintf(width * (sinks   > 1 ? .2f : .5f)) + .5f;
+               const float si_x0 = rintf(width * (sources > 1 ? .2f : .5f)) + .5f;
+               cairo_move_to (cr, si_x, height);
+               cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0);
+               cairo_stroke (cr);
        }
 
        /* AUDIO */
@@ -855,8 +862,10 @@ ProcessorEntry::RoutingIcon::on_expose_event (GdkEventExpose* ev)
                        UINT_RGBA_B_FLT(audio_port_color));
 
        if (_splitting) {
+               assert(audio_sources < 2);
                assert(audio_sinks > 1);
-               const float si_x0 = rintf(width * .5f) + .5f;
+               /* assume there is only ever one MIDI port */
+               const float si_x0 = rintf(width * (midi_sources > 0 ? .8f : .5f)) + .5f;
                for (uint32_t i = midi_sinks; i < sinks; ++i) {
                        const float si_x = rintf(width * (.2f + .6f * i / (sinks - 1.f))) + .5f;
                        cairo_move_to (cr, si_x, height);
index ad5e3bfd94be8ae67f8ad60c7f725201f8a17ff8..d4c1975e00c6e6f14d490f8a3348bf4e8e577f92 100644 (file)
@@ -1237,13 +1237,13 @@ RouteUI::build_mute_menu(void)
 
        MenuList& items = mute_menu->items();
 
-       pre_fader_mute_check = manage (new CheckMenuItem(_("Pre Fader")));
+       pre_fader_mute_check = manage (new CheckMenuItem(_("Pre Fader Sends")));
        init_mute_menu(MuteMaster::PreFader, pre_fader_mute_check);
        pre_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PreFader, pre_fader_mute_check));
        items.push_back (CheckMenuElem(*pre_fader_mute_check));
        pre_fader_mute_check->show_all();
 
-       post_fader_mute_check = manage (new CheckMenuItem(_("Post Fader")));
+       post_fader_mute_check = manage (new CheckMenuItem(_("Post Fader Sends")));
        init_mute_menu(MuteMaster::PostFader, post_fader_mute_check);
        post_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PostFader, post_fader_mute_check));
        items.push_back (CheckMenuElem(*post_fader_mute_check));
index 39b7a335250cb4623719f37eda52b0bf966a2470..4b89cd51fe78cf6d30f1d260e7d4346b7afeaeee 100644 (file)
@@ -116,7 +116,7 @@ importmode2string (ImportMode mode)
        return _("as new tracks");
 }
 
-SoundFileBox::SoundFileBox (bool persistent)
+SoundFileBox::SoundFileBox (bool /*persistent*/)
        : table (6, 2),
          length_clock ("sfboxLengthClock", true, "", false, false, true, false),
          timecode_clock ("sfboxTimecodeClock", true, "", false, false, false, false),
index d480c60529a4004bca683f43413a00ebc97e0a69..2a5f521f1da3529394340533ab11daa3af043975 100644 (file)
@@ -99,6 +99,7 @@ StereoPanner::StereoPanner (boost::shared_ptr<PannerShell> p)
        width_control->Changed.connect (panvalue_connections, invalidator(*this), boost::bind (&StereoPanner::value_change, this), gui_context());
 
        _panner_shell->Changed.connect (panshell_connections, invalidator (*this), boost::bind (&StereoPanner::bypass_handler, this), gui_context());
+       _panner_shell->PannableChanged.connect (panshell_connections, invalidator (*this), boost::bind (&StereoPanner::pannable_handler, this), gui_context());
 
        ColorsChanged.connect (sigc::mem_fun (*this, &StereoPanner::color_handler));
 
index c97d6244405eaecdd2a410e947d6b46cc0d48219..132be2e1c6db4c9cf8efa2066730b77dd885a34d 100644 (file)
@@ -372,19 +372,9 @@ Amp::inc_gain (gain_t factor, void *src)
 }
 
 void
-Amp::set_gain (gain_t val, void *src)
+Amp::set_gain (gain_t val, void *)
 {
-       val = min (val, max_gain_coefficient);
-
-       if (src != _gain_control.get()) {
-               _gain_control->set_value (val);
-               // bit twisty, this will come back and call us again
-               // (this keeps control in sync with reality)
-               return;
-       }
-
-       _gain_control->set_double (val);
-       _session.set_dirty();
+       _gain_control->set_value (val);
 }
 
 XMLNode&
@@ -414,13 +404,8 @@ Amp::set_state (const XMLNode& node, int version)
 void
 Amp::GainControl::set_value (double val)
 {
-       if (val > max_gain_coefficient) {
-               val = max_gain_coefficient;
-       }
-
-       _amp->set_gain (val, this);
-
-       AutomationControl::set_value(val);
+       AutomationControl::set_value (min (val, (double) max_gain_coefficient));
+       _amp->session().set_dirty ();
 }
 
 double
index c356ed82b9fd18490037efadf2fa0fdeb3a5210d..aaad961abb7239791fad13b79fc79041a14e8aae 100644 (file)
@@ -62,7 +62,7 @@ public:
                assert(&src != this);
                assert(_capacity > 0);
                assert(src.type() == DataType::AUDIO);
-               assert(len <= _capacity);
+               assert(dst_offset + len <= _capacity);
                assert( src_offset <= ((framecnt_t) src.capacity()-len));
                memcpy(_data + dst_offset, ((const AudioBuffer&)src).data() + src_offset, sizeof(Sample) * len);
                if (dst_offset == 0 && src_offset == 0 && len == _capacity) {
@@ -173,7 +173,6 @@ public:
        void set_data (Sample* data, size_t size) {
                assert(!_owns_data); // prevent leaks
                _capacity = size;
-               _size = size;
                _data = data;
                _silent = false;
                _written = false;
@@ -185,8 +184,6 @@ public:
         */
        void resize (size_t nframes);
 
-       bool empty() const { return _size == 0; }
-
        const Sample* data (framecnt_t offset = 0) const {
                assert(offset <= _capacity);
                return _data + offset;
@@ -198,7 +195,7 @@ public:
                return _data + offset;
        }
 
-       bool check_silence (pframes_t, bool, pframes_t&) const;
+       bool check_silence (pframes_t, pframes_t&) const;
 
        void prepare () { _written = false; _silent = false; }
        bool written() const { return _written; }
index 0d0f5d37584280544f39dc7cf0b32478668e5d31..87f7a90fc30a72ba6e3e55be93cf2bb14dabc70b 100644 (file)
@@ -46,16 +46,9 @@ public:
        /** Factory function */
        static Buffer* create(DataType type, size_t capacity);
 
-       /** Maximum capacity of buffer.
-        * Note in some cases the entire buffer may not contain valid data, use size. */
+       /** Maximum capacity of buffer. */
        size_t capacity() const { return _capacity; }
 
-       /** Amount of valid data in buffer.  Use this over capacity almost always. */
-       size_t size() const { return _size; }
-
-       /** Return true if the buffer contains no data, false otherwise */
-       virtual bool empty() const { return _size == 0; }
-
        /** Type of this buffer.
         * Based on this you can static cast a Buffer* to the desired type. */
        DataType type() const { return _type; }
@@ -80,12 +73,11 @@ public:
 
   protected:
        Buffer(DataType type)
-               : _type(type), _capacity(0), _size(0), _silent (true)
+               : _type(type), _capacity(0), _silent (true)
        {}
 
        DataType  _type;
        pframes_t _capacity;
-       pframes_t _size;
        bool      _silent;
 };
 
index 5898d04f0abaff68ab1d4dbf23c1032b8e267913..3d9c8c623f0e53ef2afe200f6ecc3807126f6287 100644 (file)
@@ -74,6 +74,7 @@ public:
 
        void inc_use_count ();
        bool removable () const;
+        bool is_stub () const;
 
        const std::string& origin() const { return _origin; }
 
index 781396a598778fc6977e97c005e1840e4f2c17e5..c67eef178af850d2270e38652fcb832c4f9c7cc3 100644 (file)
@@ -48,6 +48,8 @@ public:
        uint8_t* reserve(TimeType time, size_t size);
 
        void resize(size_t);
+       size_t size() const { return _size; }
+       bool empty() const { return _size == 0; }
 
        bool merge_in_place(const MidiBuffer &other);
 
@@ -159,6 +161,7 @@ private:
        friend class iterator_base< const MidiBuffer, const Evoral::MIDIEvent<TimeType> >;
 
        uint8_t* _data; ///< timestamp, event, timestamp, event, ...
+       pframes_t _size;
 };
 
 
index bd2e3a147929c1da7fb3f379be4742c42d15a311..4007ecf5efb999ebe2a91c1952cad7d36f042fe3 100644 (file)
@@ -270,8 +270,6 @@ typedef struct _VstTimeInfo
 
 } VstTimeInfo;
 
-typedef struct _VstTimeInfo VstTimeInfo;
-
 typedef intptr_t (* audioMasterCallback) (AEffect *, int32_t, int32_t, intptr_t, void *, float);
 
 #endif
index aa4f64755aeb9399d31e402b8703eae2dd1a4d1d..de2c1ddf00b916701e889e9bae3f96ded2e9d807 100644 (file)
@@ -57,12 +57,6 @@ AudioBuffer::resize (size_t size)
 
        if (_data && size < _capacity) {
                /* buffer is already large enough */
-               
-               if (size < _size) {
-                       /* truncate */
-                       _size = size;
-               }
-
                return;
        }
 
@@ -71,14 +65,13 @@ AudioBuffer::resize (size_t size)
        cache_aligned_malloc ((void**) &_data, sizeof (Sample) * size);
 
        _capacity = size;
-       _size = 0;
        _silent = false;
 }
 
 bool
-AudioBuffer::check_silence (pframes_t nframes, bool wholebuffer, pframes_t& n) const
+AudioBuffer::check_silence (pframes_t nframes, pframes_t& n) const
 {
-       for (n = 0; (wholebuffer || n < _size) &&  n < nframes; ++n) {
+       for (n = 0; n < nframes; ++n) {
                if (_data[n] != Sample (0)) {
                        return false;
                }
index 2586a14b5841810d0891a1cbfd8a49167a7cf954..355b0176ced0fe09ad8f6933dad613c4901a8a94 100644 (file)
@@ -117,15 +117,23 @@ AutomationControl::set_automation_style (AutoStyle as)
 void
 AutomationControl::start_touch(double when)
 {
-       set_touching (true);
-       alist()->start_touch(when);
-       AutomationWatch::instance().add_automation_watch (shared_from_this());
+       if (!touching()) {
+               if (alist()->automation_state() == Touch) {
+                       alist()->start_touch (when);
+                       AutomationWatch::instance().add_automation_watch (shared_from_this());
+               }
+               set_touching (true);
+       }
 }
 
 void
 AutomationControl::stop_touch(bool mark, double when)
 {
-       set_touching (false);
-       alist()->stop_touch (mark, when);
-       AutomationWatch::instance().remove_automation_watch (shared_from_this());
+       if (touching()) {
+               set_touching (false);
+               if (alist()->automation_state() == Touch) {
+                       alist()->stop_touch (mark, when);
+                       AutomationWatch::instance().remove_automation_watch (shared_from_this());
+               }
+       }
 }
index 16e10c95f941f938340488419fae9f3c88545dc3..87ac08abc21f85b8508c9f421a7bba711e62cb12 100644 (file)
@@ -122,7 +122,7 @@ AutomationWatch::timer ()
 
                for (AutomationWatches::iterator aw = automation_watches.begin(); aw != automation_watches.end(); ++aw) {
                        if ((*aw)->alist()->automation_write()) {
-                               (*aw)->list()->add (time, (*aw)->user_double());
+                               (*aw)->list()->add (time, (*aw)->user_double(), true);
                        }
                }
        }
index 4a392a8145ba85947bdadac633a8b2604d356987..8c12d44e5107518ec495ffe30d9b00b1ffdb4102 100644 (file)
@@ -397,7 +397,7 @@ Delivery::reset_panner ()
        if (panners_legal) {
                if (!_no_panner_reset) {
 
-                       if (_panshell && _role != Insert) {
+                       if (_panshell && _role != Insert && _role != Listen) {
                                _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, pan_outs()));
                        }
                }
index c67f33bb918e5dc3b4a0f69b1328a7d2a123c6d3..0e029a01f7731e06759c5535717bf5541bb28a10 100644 (file)
@@ -239,7 +239,7 @@ RouteExportChannel::read (Sample const *& data, framecnt_t frames) const
 {
        assert(processor);
        AudioBuffer const & buffer = processor->get_capture_buffers().get_audio (channel);
-       assert (frames <= (framecnt_t) buffer.size());
+       assert (frames <= (framecnt_t) buffer.capacity());
        data = buffer.data();
 }
 
index 09214981861f142536d714b894b44adeb8e2d2a1..c6f3a9cd5d9f120739dc5de41127f4ae1ae3cbd3 100644 (file)
@@ -101,7 +101,7 @@ FileSource::removable () const
 {
         bool r = ((_flags & Removable)
                   && ((_flags & RemoveAtDestroy) ||
-                      ((_flags & RemovableIfEmpty) && empty() == 0)));
+                      ((_flags & RemovableIfEmpty) && empty())));
 
         return r;
 }
@@ -589,3 +589,21 @@ FileSource::inc_use_count ()
         Source::inc_use_count ();
 }
 
+bool
+FileSource::is_stub () const
+{
+       if (!empty()) {
+               return false;
+       }
+       
+       if (!removable()) {
+               return false;
+       }
+
+       if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)) {
+               return false;
+       }
+
+       return true;
+}
+               
index 1d4e18d06efbefd676114175ff56fc1c5628b7ab..17a3ca1f421b47dc0acbdd2d866f5286e5739048 100644 (file)
@@ -129,7 +129,7 @@ InternalSend::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame
        // we have to copy the input, because we may alter the buffers with the amp
        // in-place, which a send must never do.
 
-       if (_panshell && !_panshell->bypassed()) {
+       if (_panshell && !_panshell->bypassed() && role() != Listen) {
                _panshell->run (bufs, mixbufs, start_frame, end_frame, nframes);
        } else {
                if (role() == Listen) {
index 8942d19a9b2b038cb2ad9599939508b051250375..f0e62d9b6592b835941cbb7dd7b869bb16c52f86 100644 (file)
@@ -203,7 +203,7 @@ lv2_evbuf_get(LV2_Evbuf_Iterator iter,
        switch (iter.evbuf->type) {
        case LV2_EVBUF_EVENT:
                ebuf = &iter.evbuf->buf.event;
-               ev = (LV2_Event*)ebuf->data + iter.offset;
+               ev = (LV2_Event*)((char*)ebuf->data + iter.offset);
                *frames    = ev->frames;
                *subframes = ev->subframes;
                *type      = ev->type;
index f2689bf998abb8e79b8053c9214625a64181d450..2f900174983bcd71f3d6bf9968475ef8fbe16212 100644 (file)
@@ -464,39 +464,31 @@ PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end
                }
 
        } else {
-               if (has_no_audio_inputs()) {
+               uint32_t in = input_streams ().n_audio ();
+               uint32_t out = output_streams().n_audio ();
+
+               if (has_no_audio_inputs() || in == 0) {
 
                        /* silence all (audio) outputs. Should really declick
                         * at the transitions of "active"
                         */
 
-                       uint32_t out = output_streams().n_audio ();
-
                        for (uint32_t n = 0; n < out; ++n) {
                                bufs.get_audio (n).silence (nframes);
                        }
 
-                       bufs.count().set_audio (out);
-
-               } else {
-
-                       /* does this need to be done with MIDI? it appears not */
+               } else if (out > in) {
 
-                       uint32_t in = input_streams ().n_audio ();
-                       uint32_t out = output_streams().n_audio ();
+                       /* not active, but something has make up for any channel count increase */
 
-                       if (out > in) {
-
-                               /* not active, but something has make up for any channel count increase */
-                               
-                               // TODO: option round-robin (n % in) or silence additional buffers ??
-                               for (uint32_t n = in; n < out; ++n) {
-                                       bufs.get_audio(n).read_from(bufs.get_audio(in - 1), nframes);
-                               }
+                       // TODO: option round-robin (n % in) or silence additional buffers ??
+                       // for now , simply replicate last buffer
+                       for (uint32_t n = in; n < out; ++n) {
+                               bufs.get_audio(n).read_from(bufs.get_audio(in - 1), nframes);
                        }
-
-                       bufs.count().set_audio (out);
                }
+
+               bufs.count().set_audio (out);
        }
 
        _active = _pending_active;
index 90522a7e06b2ca76efea55d2bff2b49e7f5b355f..df1b7fc441249a9ddadf29795d30f518fb6bd88a 100644 (file)
@@ -646,7 +646,9 @@ PluginManager::lxvst_refresh ()
        }
 
        if (lxvst_path.length() == 0) {
-               lxvst_path = "/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst";
+               lxvst_path = "/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst:"
+                       "/usr/local/lib64/linux_vst:/usr/local/lib/linux_vst:/usr/lib64/linux_vst:/usr/lib/linux_vst:"
+                       "/usr/lib/vst:/usr/local/lib/vst";
        }
 
        lxvst_discover_from_path (lxvst_path);
index 9e649362eec1003aa6930371d53e3f6c1ed6bbc2..71af69fdee4c9fe2ee3b97123e0083458d66775f 100644 (file)
@@ -419,6 +419,9 @@ Route::process_output_buffers (BufferSet& bufs,
                               framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
                               int declick, bool gain_automation_ok)
 {
+       /* Caller must hold process lock */
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
        assert(lm.locked());
 
@@ -1382,7 +1385,16 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 {
        // TODO once the export point can be configured properly, do something smarter here
        if (processor == _capturing_processor) {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
+               if (need_process_lock) {
+                       lx.acquire();
+               }
+
                _capturing_processor.reset();
+
+               if (need_process_lock) {
+                       lx.release();
+               }
        }
 
        /* these can never be removed */
@@ -1402,7 +1414,12 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (need_process_lock) {
                        lx.acquire();
                }
-               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+               /* Caller must hold process lock */
+               assert (!AudioEngine::instance()->process_lock().trylock());
+
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock); // XXX deadlock after export
+
                ProcessorState pstate (this);
 
                ProcessorList::iterator i;
@@ -3062,6 +3079,7 @@ Route::set_meter_point (MeterPoint p, bool force)
        bool meter_was_visible_to_user = _meter->display_to_user ();
 
        {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
 
                maybe_note_meter_position ();
@@ -3147,12 +3165,16 @@ Route::listen_position_changed ()
 boost::shared_ptr<CapturingProcessor>
 Route::add_export_point()
 {
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        if (!_capturing_processor) {
+               lm.release();
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lw (_processor_lock);
 
                _capturing_processor.reset (new CapturingProcessor (_session));
                _capturing_processor->activate ();
 
-               configure_processors (0);
+               configure_processors_unlocked (0);
 
        }
 
@@ -4132,7 +4154,7 @@ Route::non_realtime_locate (framepos_t pos)
 
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
                
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
index f3ad9d66dd094f5a58785a06e83e71351c06b4d9..ffbe55afbf7184d250e8dd1b7c26a53d74aa9e1b 100644 (file)
@@ -136,13 +136,21 @@ Session::pre_engine_init (string fullpath)
        /* discover canonical fullpath */
 
        char buf[PATH_MAX+1];
-       if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
-               error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
-               destroy ();
-               throw failed_constructor();
-       }
 
-       _path = string(buf);
+       if (!realpath (fullpath.c_str(), buf)) {
+               if (errno == ENOENT) {
+                       /* fullpath does not exist yet, so realpath() returned
+                        * ENOENT. Just use it as-is
+                        */
+                       _path = fullpath;
+               } else {
+                       error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
+                       destroy ();
+                       throw failed_constructor();
+               }
+       } else {
+               _path = string(buf);
+       }
        
        /* we require _path to end with a dir separator */
 
@@ -2692,19 +2700,23 @@ Session::cleanup_sources (CleanupReport& rep)
                 ++tmp;
 
                if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
-                        if (playlists->source_use_count (fs) != 0) {
-                                all_sources.insert (fs->path());
-                        } else {
 
-                                /* we might not remove this source from disk, because it may be used
-                                   by other snapshots, but its not being used in this version
-                                   so lets get rid of it now, along with any representative regions
-                                   in the region list.
-                                */
+                       if (!fs->is_stub()) {
 
-                                RegionFactory::remove_regions_using_source (i->second);
-                                sources.erase (i);
-                        }
+                               if (playlists->source_use_count (fs) != 0) {
+                                       all_sources.insert (fs->path());
+                               } else {
+                                       
+                                       /* we might not remove this source from disk, because it may be used
+                                          by other snapshots, but its not being used in this version
+                                          so lets get rid of it now, along with any representative regions
+                                          in the region list.
+                                       */
+                                       
+                                       RegionFactory::remove_regions_using_source (i->second);
+                                       sources.erase (i);
+                               }
+                       }
                }
 
                 i = tmp;
index 967e08d619909deefee28dda5eb6d3ceecaa89aa..5f1a8d3a75d13f567fa51adbfac35c5ea09e3fb4 100644 (file)
@@ -115,7 +115,7 @@ public:
 
        virtual bool clamp_value (double& /*when*/, double& /*value*/) const { return true; }
 
-        virtual void add (double when, double value);
+        virtual void add (double when, double value, bool with_guards=true);
        void fast_simple_add (double when, double value);
 
        void erase_range (double start, double end);
index bb272ea8b32a782042cc7bf853e2810586051db3..480d027ccc85812ae6a750d9bc861e6c2a2f5322 100644 (file)
@@ -17,6 +17,9 @@
  */
 
 #include <iostream>
+
+#include "pbd/stacktrace.h"
+
 #include "evoral/Control.hpp"
 #include "evoral/ControlList.hpp"
 
@@ -49,9 +52,13 @@ void
 Control::set_double (double value, double frame, bool to_list)
 {
        _user_value = value;
+       
+       /* if we're in a write pass, the automation watcher will determine the
+          values and add them to the list, so we we don't need to bother.
+       */
 
-       if (to_list) {
-               _list->add (frame, value);
+       if (to_list && !_list->in_write_pass()) {
+               _list->add (frame, value, false);
        }
 }
 
index a095daa13527e9f25e6dbbaf3076495d39adfa63..f34bfa3fafea8e13dde792b6d396a83b1203dba1 100644 (file)
@@ -342,21 +342,25 @@ ControlList::start_write_pass (double when)
 {
        Glib::Threads::Mutex::Lock lm (_lock);
 
+       DEBUG_TRACE (DEBUG::ControlList, string_compose ("%1: setup write pass @ %2\n", this, when));
+
        new_write_pass = true;
        did_write_during_pass = false;
        insert_position = when;
-
+       
        /* leave the insert iterator invalid, so that we will do the lookup
           of where it should be in a "lazy" way - deferring it until
           we actually add the first point (which may never happen).
        */
-
+       
        unlocked_invalidate_insert_iterator ();
 }
 
 void
 ControlList::write_pass_finished (double /*when*/)
 {
+       DEBUG_TRACE (DEBUG::ControlList, "write pass finished\n");
+
        if (did_write_during_pass) {
                thin ();
                did_write_during_pass = false;
@@ -367,7 +371,9 @@ ControlList::write_pass_finished (double /*when*/)
 
 void
 ControlList::set_in_write_pass (bool yn, bool add_point, double when)
-{
+{      
+       DEBUG_TRACE (DEBUG::ControlList, string_compose ("now in write pass @ %1, add point ? %2\n", when, add_point));
+       
        _in_write_pass = yn;
 
        if (yn && add_point) {
@@ -381,8 +387,6 @@ ControlList::add_guard_point (double when)
        ControlEvent cp (when, 0.0);
        most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
-       DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 ADD GUARD POINT @ %2looked up insert iterator for new write pass\n", this, when));
-       
        double eval_value = unlocked_eval (insert_position);
        
        if (most_recent_insert_iterator == _events.end()) {
@@ -437,7 +441,7 @@ ControlList::in_write_pass () const
 }
 
 void
-ControlList::add (double when, double value)
+ControlList::add (double when, double value, bool with_guards)
 {
        /* this is for making changes from some kind of user interface or
           control surface (GUI, MIDI, OSC etc)
@@ -447,8 +451,8 @@ ControlList::add (double when, double value)
                return;
        }
 
-       DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add %2 at %3 w/erase = %4 at end ? %5\n", 
-                                                        this, value, when, _in_write_pass, (most_recent_insert_iterator == _events.end())));
+       DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add %2 at %3 w/erase = %4 (new ? %6) at end ? %5\n", 
+                                                        this, value, when, _in_write_pass, (most_recent_insert_iterator == _events.end()), new_write_pass));
        {
                Glib::Threads::Mutex::Lock lm (_lock);
                ControlEvent cp (when, 0.0f);
@@ -460,7 +464,7 @@ ControlList::add (double when, double value)
                         * add an "anchor" point there.
                         */
 
-                       if (when > 1) {
+                       if (when >= 1) {
                                _events.insert (_events.end(), new ControlEvent (0, _default_value));
                                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _default_value));
                        }
@@ -468,8 +472,10 @@ ControlList::add (double when, double value)
 
                if (_in_write_pass && new_write_pass) {
 
-                       add_guard_point (insert_position);
-                       did_write_during_pass = true;
+                       if (with_guards) {
+                               add_guard_point (insert_position);
+                               did_write_during_pass = true;
+                       }
 
                } else if (most_recent_insert_iterator == _events.end() || when > (*most_recent_insert_iterator)->when) {
                        
@@ -492,7 +498,7 @@ ControlList::add (double when, double value)
                                        ++most_recent_insert_iterator;
                                }
 
-                               if (most_recent_insert_iterator != _events.end()) {
+                               if (with_guards && most_recent_insert_iterator != _events.end()) {
                                        if ((*most_recent_insert_iterator)->when - when > 64) {
                                                /* next control point is some
                                                 * distance from where our new
@@ -631,7 +637,7 @@ ControlList::add (double when, double value)
                                }
                        }
 
-                       if (most_recent_insert_iterator != _events.end()) {
+                       if (with_guards && most_recent_insert_iterator != _events.end()) {
                                if ((*most_recent_insert_iterator)->when - when > 64) {
                                        /* next control point is some
                                         * distance from where our new
@@ -1729,7 +1735,7 @@ ControlList::dump (ostream& o)
        /* NOT LOCKED ... for debugging only */
 
        for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
-               o << (*x)->value << " @ " << (*x)->when << endl;
+               o << (*x)->value << " @ " << (uint64_t) (*x)->when << endl;
        }
 }
 
index 8c1390832fab441ab01ddb1f577f9e49475a4752..ea2d26fb313e043e1768cd43b41b58119f168599 100644 (file)
@@ -403,7 +403,7 @@ VBAPanner::describe_parameter (Evoral::Parameter p)
 {
         switch (p.type()) {
         case PanAzimuthAutomation:
-                return _("Direction");
+                return _("Azimuth");
         case PanWidthAutomation:
                 return _("Width");
         case PanElevationAutomation:
index 313fe7a5cd8d3e8ebeb1ab62e18ece7697f8a4e1..4c662a8c6dd66a877a87aeb14260317604db53cd 100644 (file)
@@ -115,27 +115,36 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
 
        int i,j,k,l,table_size;
        int n_speakers = _speakers.size ();
-       int connections[n_speakers][n_speakers];
-       float distance_table[((n_speakers * (n_speakers - 1)) / 2)];
-       int distance_table_i[((n_speakers * (n_speakers - 1)) / 2)];
-       int distance_table_j[((n_speakers * (n_speakers - 1)) / 2)];
-       float distance;
-       struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
 
        if (n_speakers == 0) {
                return;
        }
 
+       /* variable length arrays arrived in C99, became optional in C11, and
+          are only planned for C++14. Use alloca which is functionally
+          identical (but uglier to read).
+       */
+       int* connections = (int*) alloca (sizeof (int) * n_speakers * n_speakers);
+       float* distance_table = (float *) alloca (sizeof (float) * ((n_speakers * (n_speakers - 1)) / 2));
+       int* distance_table_i = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
+       int* distance_table_j = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
+       float distance;
+       struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
+
+       for (i = 0; i < n_speakers * n_speakers; i++) {
+               connections[i] = 0;
+       }
+
        for (i = 0; i < n_speakers; i++) {
                for (j = i+1; j < n_speakers; j++) {
                        for(k = j+1; k < n_speakers; k++) {
                                if (vol_p_side_lgth(i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) {
-                                       connections[i][j]=1;
-                                       connections[j][i]=1;
-                                       connections[i][k]=1;
-                                       connections[k][i]=1;
-                                       connections[j][k]=1;
-                                       connections[k][j]=1;
+                                       connections[(i*n_speakers)+j]=1;
+                                       connections[(j*n_speakers)+i]=1;
+                                       connections[(i*n_speakers)+k]=1;
+                                       connections[(k*n_speakers)+i]=1;
+                                       connections[(j*n_speakers)+k]=1;
+                                       connections[(k*n_speakers)+j]=1;
                                        add_ldsp_triplet(i,j,k,ls_triplets);
                                }
                        }
@@ -150,7 +159,7 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
 
        for (i = 0;i < n_speakers; i++) {
                for (j = i+1; j < n_speakers; j++) {
-                       if (connections[i][j] == 1) {
+                       if (connections[(i*n_speakers)+j] == 1) {
                                distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
                                k=0;
                                while(distance_table[k] < distance) {
@@ -175,13 +184,13 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
        for (i = 0; i < table_size; i++) {
                int fst_ls = distance_table_i[i];
                int sec_ls = distance_table_j[i];
-               if (connections[fst_ls][sec_ls] == 1) {
+               if (connections[(fst_ls*n_speakers)+sec_ls] == 1) {
                        for (j = 0; j < n_speakers; j++) {
                                for (k = j+1; k < n_speakers; k++) {
                                        if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) {
                                                if (lines_intersect(fst_ls, sec_ls, j, k) == 1){
-                                                       connections[j][k] = 0;
-                                                       connections[k][j] = 0;
+                                                       connections[(j*n_speakers)+k] = 0;
+                                                       connections[(k*n_speakers)+j] = 0;
                                                }
                                        }
                                }
@@ -197,9 +206,9 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
                i = trip_ptr->ls_nos[0];
                j = trip_ptr->ls_nos[1];
                k = trip_ptr->ls_nos[2];
-               if (connections[i][j] == 0 ||
-                   connections[i][k] == 0 ||
-                   connections[j][k] == 0 ||
+               if (connections[(i*n_speakers)+j] == 0 ||
+                   connections[(i*n_speakers)+k] == 0 ||
+                   connections[(j*n_speakers)+k] == 0 ||
                    any_ls_inside_triplet(i,j,k) == 1 ){
                        if (prev != 0) {
                                prev->next = trip_ptr->next;
@@ -526,19 +535,23 @@ VBAPSpeakers::choose_speaker_pairs (){
           matrices and stores the data to a global array
        */
        const int n_speakers = _speakers.size();
-       const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
-       int sorted_speakers[n_speakers];
-       bool exists[n_speakers];
-       double inverse_matrix[n_speakers][4];
-       int expected_pairs = 0;
-       int pair;
-       int speaker;
-
 
        if (n_speakers == 0) {
                return;
        }
 
+       const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
+       /* variable length arrays arrived in C99, became optional in C11, and
+          are only planned for C++14. Use alloca which is functionally
+          identical (but uglier to read).
+       */
+       int* sorted_speakers = (int*) alloca (sizeof (int) * n_speakers);
+       bool* exists = (bool*) alloca (sizeof(bool) * n_speakers);
+       double* inverse_matrix = (double*) alloca (sizeof (double) * n_speakers * 4);
+       int expected_pairs = 0;
+       int pair;
+       int speaker;
+
        for (speaker = 0; speaker < n_speakers; ++speaker) {
                exists[speaker] = false;
        }
@@ -553,7 +566,7 @@ VBAPSpeakers::choose_speaker_pairs (){
                     _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
                        if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
                                                 _speakers[sorted_speakers[speaker+1]].angles().azi,
-                                                inverse_matrix[speaker]) != 0){
+                                                &inverse_matrix[4 * speaker]) != 0){
                                exists[speaker] = true;
                                expected_pairs++;
                        }
@@ -564,7 +577,7 @@ VBAPSpeakers::choose_speaker_pairs (){
             +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
                if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
                                        _speakers[sorted_speakers[0]].angles().azi,
-                                       inverse_matrix[n_speakers-1]) != 0) {
+                                       &inverse_matrix[4*(n_speakers-1)]) != 0) {
                        exists[n_speakers-1] = true;
                        expected_pairs++;
                }
@@ -582,10 +595,10 @@ VBAPSpeakers::choose_speaker_pairs (){
 
        for (speaker = 0; speaker < n_speakers - 1; speaker++) {
                if (exists[speaker]) {
-                       _matrices[pair][0] = inverse_matrix[speaker][0];
-                       _matrices[pair][1] = inverse_matrix[speaker][1];
-                       _matrices[pair][2] = inverse_matrix[speaker][2];
-                       _matrices[pair][3] = inverse_matrix[speaker][3];
+                       _matrices[pair][0] = inverse_matrix[(speaker*4)+0];
+                       _matrices[pair][1] = inverse_matrix[(speaker*4)+1];
+                       _matrices[pair][2] = inverse_matrix[(speaker*4)+2];
+                       _matrices[pair][3] = inverse_matrix[(speaker*4)+3];
 
                        _speaker_tuples[pair][0] = sorted_speakers[speaker];
                        _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
@@ -595,10 +608,10 @@ VBAPSpeakers::choose_speaker_pairs (){
        }
 
        if (exists[n_speakers-1]) {
-               _matrices[pair][0] = inverse_matrix[speaker][0];
-               _matrices[pair][1] = inverse_matrix[speaker][1];
-               _matrices[pair][2] = inverse_matrix[speaker][2];
-               _matrices[pair][3] = inverse_matrix[speaker][3];
+               _matrices[pair][0] = inverse_matrix[(speaker*4)+0];
+               _matrices[pair][1] = inverse_matrix[(speaker*4)+1];
+               _matrices[pair][2] = inverse_matrix[(speaker*4)+2];
+               _matrices[pair][3] = inverse_matrix[(speaker*4)+3];
 
                _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
                _speaker_tuples[pair][1] = sorted_speakers[0];
index e9bef5a9b0bf4b326e24050aed2c8db0df6dfeb8..fc1491d9e86a30b57042c1a55cd62175ec1608c1 100644 (file)
@@ -345,7 +345,9 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force)
 
        if (_current_initial_bank <= sorted.size()) {
 
-               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3\n", _current_initial_bank, strip_cnt, sorted.size()));
+               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3 on %4 surfaces\n", 
+                                                                  _current_initial_bank, strip_cnt, sorted.size(),
+                                                                  surfaces.size()));
 
                // link routes to strips
 
@@ -521,11 +523,23 @@ MackieControlProtocol::update_global_led (int id, LedState ls)
        }
 }
 
+void
+MackieControlProtocol::device_ready ()
+{
+       /* this is not required to be called, but for devices which do
+        * handshaking, it can be called once the device has verified the
+        * connection.
+        */
+        
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("device ready init (active=%1)\n", active()));
+       update_surfaces ();
+}
+
 // send messages to surface to set controls to correct values
 void 
 MackieControlProtocol::update_surfaces()
 {
-       DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::update_surfaces() init\n");
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::update_surfaces() init (active=%1)\n", active()));
        if (!active()) {
                return;
        }
index e1a71a2460fc04f01089dd61242666749a72e577..fc965d868b2d6f83f2591c6e5655693d0303d290 100644 (file)
@@ -119,6 +119,8 @@ class MackieControlProtocol
        const Mackie::DeviceInfo& device_info() const { return _device_info; }
        Mackie::DeviceProfile& device_profile() { return _device_profile; }
 
+        void device_ready ();
+
        int set_active (bool yn);
        int  set_device (const std::string&);
         void set_profile (const std::string&);
index ef4447d900906d6a92f34957de7e1523a35b2014..1893e31b8dbf6610aa59341e4910bdf0a81449b1 100644 (file)
@@ -626,13 +626,22 @@ Strip::do_parameter_display (AutomationType type, float val)
        }
 }
 
+void
+Strip::handle_fader_touch (Fader& fader, bool touch_on)
+{
+       if (touch_on) {
+               fader.start_touch (_surface->mcp().transport_frame());
+       } else {
+               fader.stop_touch (_surface->mcp().transport_frame(), false);
+       }
+}
+
 void
 Strip::handle_fader (Fader& fader, float position)
 {
        DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
 
        fader.set_value (position);
-       fader.start_touch (_surface->mcp().transport_frame());
        queue_display_reset (2000);
 
        // must echo bytes back to slider now, because
index 225783d0fe36b28e6eb7f73f96959116d20adfce..c330e0e54db3a99622c609f129dc62952b5d43bf 100644 (file)
@@ -61,6 +61,7 @@ public:
 
        void handle_button (Button&, ButtonState bs);
        void handle_fader (Fader&, float position);
+       void handle_fader_touch (Fader&, bool touch_on);
        void handle_pot (Pot&, float delta);
 
        void periodic (uint64_t now_usecs);
index 694c73b1bcb638f49bb3772bceeb6a55159f33cb..09c5fff051dad322c559fb081a5d0478cdf3f7c3 100644 (file)
@@ -369,7 +369,7 @@ Surface::connect_to_signals ()
                p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
                /* Button messages are NoteOn */
                p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
-               /* Button messages are NoteOn. libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
+               /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
                p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
                /* Fader messages are Pitchbend */
                uint32_t i;
@@ -386,7 +386,7 @@ Surface::connect_to_signals ()
 void
 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
 {
-       /* Pitchbend messages are fader messages. Nothing in the data we get
+       /* Pitchbend messages are fader position messages. Nothing in the data we get
         * from the MIDI::Parser conveys the fader ID, which was given by the
         * channel ID in the status byte.
         *
@@ -394,7 +394,6 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin
         * when we connected to the per-channel pitchbend events.
         */
 
-
        DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2\n",
                                                           fader_id, pb, _number));
        
@@ -429,6 +428,28 @@ Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
                turn_it_on ();
        }
 
+       /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
+        * master. 
+        */
+
+       if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
+               Fader* fader = faders[ev->note_number];
+
+               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
+
+               if (fader) {
+
+                       Strip* strip = dynamic_cast<Strip*> (&fader->group());
+
+                       if (ev->velocity > 64) {
+                               strip->handle_fader_touch (*fader, true);
+                       } else {
+                               strip->handle_fader_touch (*fader, false);
+                       }
+               }
+               return;
+       }
+
        Button* button = buttons[ev->note_number];
 
        if (button) {
@@ -516,8 +537,10 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count
                   LCP: Connection Challenge 
                */
                if (bytes[4] == 0x10 || bytes[4] == 0x11) {
+                       DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
                        write_sysex (host_connection_query (bytes));
                } else {
+                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
                        if (!_active) {
                                turn_it_on ();
                        }
@@ -525,6 +548,7 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count
                break;
 
        case 0x03: /* LCP Connection Confirmation */
+               DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
                if (bytes[4] == 0x10 || bytes[4] == 0x11) {
                        write_sysex (host_connection_confirmation (bytes));
                        _active = true;
@@ -532,6 +556,7 @@ Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count
                break;
 
        case 0x04: /* LCP: Confirmation Denied */
+               DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
                _active = false;
                break;
        default:
@@ -610,6 +635,8 @@ Surface::turn_it_on ()
 
        _active = true;
 
+       _mcp.device_ready ();
+
        for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
                (*s)->notify_all ();
        }
@@ -739,6 +766,8 @@ Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
        vector<boost::shared_ptr<Route> >::const_iterator r;
        Strips::iterator s = strips.begin();
 
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes", routes.size()));
+
        for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
 
                /* don't try to assign routes to a locked strip. it won't
diff --git a/wscript b/wscript
index ae4b85e61dba0a1b661bf76db22ee2e41c3522dd..57d3496465df5ae15fcfa369136d2af5332dbaac 100644 (file)
--- a/wscript
+++ b/wscript
@@ -177,10 +177,6 @@ def set_compiler_flags (conf,opt):
     if opt.gprofile:
         debug_flags = [ '-pg' ]
 
-    if opt.backtrace:
-        if platform != 'darwin' and not is_clang:
-            debug_flags = [ '-rdynamic' ]
-
     # Autodetect
     if opt.dist_target == 'auto':
         if platform == 'darwin':
@@ -415,6 +411,10 @@ def set_compiler_flags (conf,opt):
         conf.env.append_value('CFLAGS', optimization_flags)
         conf.env.append_value('CXXFLAGS', optimization_flags)
 
+    if opt.backtrace:
+        if platform != 'darwin' and not is_clang:
+            linker_flags += [ '-rdynamic' ]
+
     conf.env.append_value('CFLAGS', compiler_flags)
     conf.env.append_value('CFLAGS', c_flags)
     conf.env.append_value('CXXFLAGS', compiler_flags)