rework panning -- Squashed commit of the following:
authorRobin Gareus <robin@gareus.org>
Wed, 8 Jan 2014 23:18:29 +0000 (00:18 +0100)
committerRobin Gareus <robin@gareus.org>
Wed, 8 Jan 2014 23:18:45 +0000 (00:18 +0100)
commit 6f4f4f161b00cb36252727f67ecc4913eb944fd7
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 22:13:09 2014 +0100

    fix panner plugin discovery (prev commit)

commit 26e514f4a80af9192cae3cbd62fde0ae95474dfc
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 18:56:59 2014 +0100

    update panner plugin discovery

    * recurse dirs in 'PANNER_PATH' and 'panner_dir_name' up to 1 level.
    * don't look in ardour_dll_directory() -- no panners are supposed to be in there
    * use .dylib on OSX exclusively.

commit a514c3f1c425dccf3d42eee9d2b183b44fd26a03
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 16:48:34 2014 +0100

    remove debug/devel printf()s

commit d863742ddc69af493ee6a8817bc778968d9b0800
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 16:17:13 2014 +0100

    panner-type: session backward/forward compatibility

commit 25d5e4c663ada34129451b0f9045ab047d6cc2f0
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 16:09:07 2014 +0100

    update URIs -> URLs

commit 00a606a43d9456cfbaf43cae4fb598549326ba71
Merge: 0f1cec1 382eb0f
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 03:29:45 2014 +0100

    Merge branch 'master' into panning

commit 0f1cec19babae538c9697eed4be5d6ddc851b013
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 02:41:15 2014 +0100

    switch panner ID to URI

commit 575282b412c3ae1cd8219cf75f00a1a4239e2813
Author: Robin Gareus <robin@gareus.org>
Date:   Wed Jan 8 00:50:15 2014 +0100

    prepare API for panner URI

commit ea62cd049308859782a7bb16e4f18169d8638b46
Author: Robin Gareus <robin@gareus.org>
Date:   Tue Jan 7 19:57:06 2014 +0100

    update development doc relating to panner selection

commit 586d7de2392e26b9d7f597b1a00b98dfaa42ecdc
Author: Robin Gareus <robin@gareus.org>
Date:   Tue Jan 7 19:56:24 2014 +0100

    clean up PanShell::set_user_selected_panner_type() API

commit 99077886a5a1cacece908d87c29c3be12903027e
Author: Robin Gareus <robin@gareus.org>
Date:   Tue Jan 7 04:46:22 2014 +0100

    panner bypass: visualize & [in]sensitivity

commit 46d688d216f0e67d672376a607157af02b359fb2
Merge: 4e67573 c4cdf61
Author: Robin Gareus <robin@gareus.org>
Date:   Tue Jan 7 02:18:54 2014 +0100

    Merge branch 'master' into panning

commit 4e67573517b3d60ddf65729783687b16cfb2adb7
Author: Robin Gareus <robin@gareus.org>
Date:   Tue Jan 7 01:05:17 2014 +0100

    don't call configure_io() for merely swapping panners

commit d32a4c51f6967f48f7680554866f1f7b311ccde1
Merge: a3226d4 cec3116
Author: Robin Gareus <robin@gareus.org>
Date:   Mon Jan 6 23:49:55 2014 +0100

    Merge branch 'master' into panning

commit a3226d46b598afae54a65ac69320eca84669f347
Author: Robin Gareus <robin@gareus.org>
Date:   Mon Jan 6 17:52:38 2014 +0100

    add notes about panner re-design

commit d1ae2366024605f22b05572a81ee249e6fdbcd2f
Author: Robin Gareus <robin@gareus.org>
Date:   Mon Jan 6 15:06:40 2014 +0100

    add simple stereo-balance panner for testing

commit e0ddd256ff2288b8d8cfad3ad485a916964ce5b5
Author: Robin Gareus <robin@gareus.org>
Date:   Mon Jan 6 17:02:52 2014 +0100

    add frontend/GUI for panner selection

commit 2cb8f846755eb5aea8a2620d31ea981c446c4041
Author: Robin Gareus <robin@gareus.org>
Date:   Mon Jan 6 17:02:20 2014 +0100

    prepare backend for panner selection

24 files changed:
gtk2_ardour/ardev_common.sh.in
gtk2_ardour/mixer_strip.cc
gtk2_ardour/mixer_strip.h
gtk2_ardour/mono_panner.cc
gtk2_ardour/mono_panner.h
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
gtk2_ardour/stereo_panner.cc
gtk2_ardour/stereo_panner.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/panner_manager.h
libs/ardour/ardour/panner_shell.h
libs/ardour/ardour/route.h
libs/ardour/panner_manager.cc
libs/ardour/panner_search_path.cc
libs/ardour/panner_shell.cc
libs/ardour/route.cc
libs/panners/1in2out/panner_1in2out.cc
libs/panners/2in2out/panner_2in2out.cc
libs/panners/stereobalance/panner_balance.cc [new file with mode: 0644]
libs/panners/stereobalance/panner_balance.h [new file with mode: 0644]
libs/panners/stereobalance/wscript [new file with mode: 0644]
libs/panners/vbap/vbap.cc
libs/panners/wscript

index 850a83c954e7218b22ec68c3e631391bd4ec60b7..8724ff2c7d5cf7bee4b0c8c6c8dbbbc0919ce7e7 100644 (file)
@@ -12,7 +12,7 @@ libs=$TOP/@LIBS@
 
 export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/gtk2_ardour:$TOP/gtk2_ardour:.
 export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/wiimote
-export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap
+export ARDOUR_PANNER_PATH=$libs/panners
 export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
 export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
 export ARDOUR_MCP_PATH=$TOP/mcp:.
index aa49e20d0c9174ec00df7f3dbac5cfcde1307463..e7c3f138b8815f52fc0a67894f3163a591c56cc4 100644 (file)
@@ -41,6 +41,7 @@
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
+#include "ardour/panner_manager.h"
 #include "ardour/port.h"
 #include "ardour/profile.h"
 #include "ardour/route.h"
@@ -515,7 +516,10 @@ MixerStrip::set_route (boost::shared_ptr<Route> rt)
        _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
        _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
 
+       _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
+
        if (_route->panner_shell()) {
+               update_panner_choices();
                _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
        }
 
@@ -1019,9 +1023,48 @@ MixerStrip::connect_to_pan ()
        p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
        p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
 
-       panners.panshell_changed ();
+       /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
+        * However, that only works a panner was previously set.
+        *
+        * PannerUI must remain subscribed to _panshell->Changed() in case
+        * we switch the panner eg. AUX-Send and back
+        * _route->panner_shell()->Changed() vs _panshell->Changed
+        */
+       if (panners._panner == 0) {
+               panners.panshell_changed ();
+       }
 }
 
+void
+MixerStrip::update_panner_choices ()
+{
+       ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
+       if (!_route->panner_shell()) { return; }
+
+       int in = _route->output()->n_ports().n_audio();
+       int out = in;
+
+       if (_route->panner()) {
+               in = _route->panner()->in().n_audio();
+       }
+
+       if (out < 2 || in == 0) {
+               panners.set_available_panners(_route, std::map<std::string,std::string>());
+               return;
+       }
+
+       std::map<std::string,std::string> panner_list;
+       std::list<PannerInfo*> panner_info = PannerManager::instance().panner_info;
+       /* get available panners for current configuration. */
+       for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
+                PanPluginDescriptor* d = &(*p)->descriptor;
+                if (d->in != -1 && d->in != in) continue;
+                if (d->out != -1 && d->out != out) continue;
+                if (d->in == -1 && d->out == -1 && out <= 2) continue;
+                panner_list.insert(std::pair<std::string,std::string>(d->panner_uri,d->name));
+       }
+       panners.set_available_panners(_route, panner_list);
+}
 
 /*
  * Output port labelling
@@ -1272,6 +1315,12 @@ MixerStrip::diskstream_changed ()
        Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
 }
 
+void
+MixerStrip::io_changed_proxy ()
+{
+       Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
+}
+
 void
 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
 {
@@ -1843,6 +1892,8 @@ MixerStrip::show_send (boost::shared_ptr<Send> send)
        gain_meter().setup_meters ();
 
        panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
+       panner_ui().set_available_panners(boost::shared_ptr<ARDOUR::Route>(), std::map<std::string,std::string>());
+
        panner_ui().setup_pan ();
 
        /* make sure the send has audio output */
@@ -1884,6 +1935,7 @@ MixerStrip::revert_to_default_display ()
        gain_meter().setup_meters ();
 
        panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
+       update_panner_choices();
        panner_ui().setup_pan ();
 
        if (has_audio_outputs ()) {
index fe10070fbb6397d2be554415b41fd0c02b457ba5..883bfffdba14a131fa8347373c20e74e7165f689 100644 (file)
@@ -226,6 +226,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
        void edit_output_configuration ();
 
        void diskstream_changed ();
+       void io_changed_proxy ();
 
        Gtk::Menu *send_action_menu;
        Gtk::MenuItem* rename_menu_item;
@@ -237,6 +238,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
        PBD::ScopedConnection panstate_connection;
        PBD::ScopedConnection panstyle_connection;
        void connect_to_pan ();
+       void update_panner_choices ();
 
        void update_diskstream_display ();
        void update_input_display ();
index daec1eede1c8f8bcc5c04681885637dabd03b414..c1039092c5226ac8ca5823f601efa7e19eed887a 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 
 #include "ardour_ui.h"
 #include "global_signals.h"
@@ -57,23 +58,25 @@ static const int top_step = 2;
 MonoPanner::ColorScheme MonoPanner::colors;
 bool MonoPanner::have_colors = false;
 
-MonoPanner::MonoPanner (boost::shared_ptr<ARDOUR::Panner> panner)
-       : PannerInterface (panner)
+MonoPanner::MonoPanner (boost::shared_ptr<ARDOUR::PannerShell> p)
+       : PannerInterface (p->panner())
+       , _panner_shell (p)
        , position_control (_panner->pannable()->pan_azimuth_control)
-        , drag_start_x (0)
-        , last_drag_x (0)
-        , accumulated_delta (0)
-        , detented (false)
-        , position_binder (position_control)
+       , drag_start_x (0)
+       , last_drag_x (0)
+       , accumulated_delta (0)
+       , detented (false)
+       , position_binder (position_control)
        , _dragging (false)
 {
-        if (!have_colors) {
-                set_colors ();
-                have_colors = true;
-        }
+       if (!have_colors) {
+               set_colors ();
+               have_colors = true;
+       }
 
-        position_control->Changed.connect (connections, invalidator(*this), boost::bind (&MonoPanner::value_change, this), gui_context());
+       position_control->Changed.connect (connections, invalidator(*this), boost::bind (&MonoPanner::value_change, this), gui_context());
 
+       _panner_shell->Changed.connect (connections, invalidator (*this), boost::bind (&MonoPanner::bypass_handler, this), gui_context());
        ColorsChanged.connect (sigc::mem_fun (*this, &MonoPanner::color_handler));
 
        set_tooltip ();
@@ -87,6 +90,10 @@ MonoPanner::~MonoPanner ()
 void
 MonoPanner::set_tooltip ()
 {
+       if (_panner_shell->bypassed()) {
+               _tooltip.set_tip (_("bypassed"));
+               return;
+       }
         double pos = position_control->get_value(); // 0..1
 
         /* We show the position of the center of the image relative to the left & right.
@@ -128,13 +135,17 @@ MonoPanner::on_expose_event (GdkEventExpose*)
 
         /* background */
 
-        context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
+        if (!_panner_shell->bypassed()) {
+                context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
+        } else {
+                context->set_source_rgba (0.1, 0.1, 0.1, 0.2);
+        }
         context->rectangle (0, 0, width, height);
         context->fill ();
 
-        double usable_width = width - pos_box_size;
+       double usable_width = width - pos_box_size;
 
-        /* compute the centers of the L/R boxes based on the current stereo width */
+       /* compute the centers of the L/R boxes based on the current stereo width */
 
         if (fmod (usable_width,2.0) == 0) {
                 /* even width, but we need odd, so that there is an exact center.
@@ -158,6 +169,10 @@ MonoPanner::on_expose_event (GdkEventExpose*)
         context->line_to ((pos_box_size/2.0) + (usable_width/2.0), height);
         context->stroke ();
 
+       if (_panner_shell->bypassed()) {
+               return true;
+       }
+
         /* left box */
 
         rounded_rectangle (context,
@@ -250,6 +265,9 @@ MonoPanner::on_button_press_event (GdkEventButton* ev)
        if (PannerInterface::on_button_press_event (ev)) {
                return true;
        }
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
        
         drag_start_x = ev->x;
         last_drag_x = ev->x;
@@ -319,6 +337,10 @@ MonoPanner::on_button_release_event (GdkEventButton* ev)
                 return false;
         }
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
         _dragging = false;
        _tooltip.target_stop_drag ();
         accumulated_delta = 0;
@@ -340,6 +362,10 @@ MonoPanner::on_scroll_event (GdkEventScroll* ev)
         double pv = position_control->get_value(); // 0..1.0 ; 0 = left
         double step;
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
         if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
                 step = one_degree;
         } else {
@@ -365,6 +391,9 @@ MonoPanner::on_scroll_event (GdkEventScroll* ev)
 bool
 MonoPanner::on_motion_notify_event (GdkEventMotion* ev)
 {
+       if (_panner_shell->bypassed()) {
+               _dragging = false;
+       }
         if (!_dragging) {
                 return false;
         }
@@ -406,6 +435,10 @@ MonoPanner::on_key_press_event (GdkEventKey* ev)
         double pv = position_control->get_value(); // 0..1.0 ; 0 = left
         double step;
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
         if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
                 step = one_degree;
         } else {
@@ -450,6 +483,12 @@ MonoPanner::color_handler ()
        queue_draw ();
 }
 
+void
+MonoPanner::bypass_handler ()
+{
+       queue_draw ();
+}
+
 PannerEditor*
 MonoPanner::editor ()
 {
index 1c387ad32cab50ed7c364e49fc672eab998a7042..523f9731dee890140d1bad7207fb322c2380fe02 100644 (file)
 
 #include "panner_interface.h"
 
+namespace ARDOUR {
+       class PannerShell;
+}
+
 namespace PBD {
         class Controllable;
 }
@@ -35,7 +39,7 @@ namespace PBD {
 class MonoPanner : public PannerInterface
 {
   public:
-       MonoPanner (boost::shared_ptr<ARDOUR::Panner>);
+       MonoPanner (boost::shared_ptr<ARDOUR::PannerShell>);
        ~MonoPanner ();
 
         boost::shared_ptr<PBD::Controllable> get_controllable() const { return position_control; }
@@ -53,6 +57,7 @@ class MonoPanner : public PannerInterface
 
   private:
        PannerEditor* editor ();
+       boost::shared_ptr<ARDOUR::PannerShell> _panner_shell;
        
         boost::shared_ptr<PBD::Controllable> position_control;
         PBD::ScopedConnectionList connections;
@@ -80,6 +85,7 @@ class MonoPanner : public PannerInterface
         static void set_colors ();
         static bool have_colors;
        void color_handler ();
+       void bypass_handler ();
 };
 
 #endif /* __gtk_ardour_mono_panner_h__ */
index c82a44f3992989a415759de024e6f301f5ad996d..3ee794e5781ccdc628f0439b7ec0ea186bae4ede 100644 (file)
@@ -51,6 +51,7 @@ PannerUI::PannerUI (Session* s)
        , _current_nins (-1)
        , pan_automation_style_button ("")
        , pan_automation_state_button ("")
+       , _panner_list()
 {
        set_session (s);
 
@@ -203,7 +204,7 @@ PannerUI::~PannerUI ()
 void
 PannerUI::panshell_changed ()
 {
-        set_panner (_panshell, _panshell->panner());
+       set_panner (_panshell, _panshell->panner());
        setup_pan ();
 }
 
@@ -233,73 +234,51 @@ PannerUI::setup_pan ()
                return;
        }
 
-       if (nouts == 0 || nouts == 1) {
+       if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_2in2out#ui")
+       {
+               boost::shared_ptr<Pannable> pannable = _panner->pannable();
 
-               /* stick something into the panning viewport so that it redraws */
-
-               EventBox* eb = manage (new EventBox());
-               pan_vbox.pack_start (*eb, false, false);
-
-               delete big_window;
-               big_window = 0;
-
-       } else if (nouts == 2) {
-
-                if (nins == 2) {
-
-                        /* add integrated 2in/2out panner GUI */
-
-                        boost::shared_ptr<Pannable> pannable = _panner->pannable();
-
-                        _stereo_panner = new StereoPanner (_panner);
-                        _stereo_panner->set_size_request (-1, pan_bar_height);
-                        pan_vbox.pack_start (*_stereo_panner, false, false);
-
-                        boost::shared_ptr<AutomationControl> ac;
+               _stereo_panner = new StereoPanner (_panshell);
+               _stereo_panner->set_size_request (-1, pan_bar_height);
+               pan_vbox.pack_start (*_stereo_panner, false, false);
 
-                        ac = pannable->pan_azimuth_control;
-                        _stereo_panner->StartPositionGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
-                                                                                  boost::weak_ptr<AutomationControl> (ac)));
-                        _stereo_panner->StopPositionGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
-                                                                                 boost::weak_ptr<AutomationControl>(ac)));
+               boost::shared_ptr<AutomationControl> ac;
 
-                        ac = pannable->pan_width_control;
-                        _stereo_panner->StartWidthGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
-                                                                               boost::weak_ptr<AutomationControl> (ac)));
-                        _stereo_panner->StopWidthGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
-                                                                              boost::weak_ptr<AutomationControl>(ac)));
-                        _stereo_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
+               ac = pannable->pan_azimuth_control;
+               _stereo_panner->StartPositionGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
+                                       boost::weak_ptr<AutomationControl> (ac)));
+               _stereo_panner->StopPositionGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
+                                       boost::weak_ptr<AutomationControl>(ac)));
 
-                } else if (nins == 1) {
-                        /* 1-in/2out */
-
-                        boost::shared_ptr<Pannable> pannable = _panner->pannable();
-                        boost::shared_ptr<AutomationControl> ac = pannable->pan_azimuth_control;
-
-                        _mono_panner = new MonoPanner (_panner);
-                       
-                        _mono_panner->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
-                                                                      boost::weak_ptr<AutomationControl> (ac)));
-                        _mono_panner->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
-                                                             boost::weak_ptr<AutomationControl>(ac)));
-
-                        _mono_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
-
-                        _mono_panner->set_size_request (-1, pan_bar_height);
+               ac = pannable->pan_width_control;
+               _stereo_panner->StartWidthGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
+                                       boost::weak_ptr<AutomationControl> (ac)));
+               _stereo_panner->StopWidthGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
+                                       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")
+       {
+               boost::shared_ptr<Pannable> pannable = _panner->pannable();
+               boost::shared_ptr<AutomationControl> ac = pannable->pan_azimuth_control;
 
-                        update_pan_sensitive ();
-                        pan_vbox.pack_start (*_mono_panner, false, false);
+               _mono_panner = new MonoPanner (_panshell);
 
-                } else {
-                        warning << string_compose (_("No panner user interface is currently available for %1-in/2out tracks/busses"),
-                                                   nins) << endmsg;
-                }
+               _mono_panner->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch),
+                                       boost::weak_ptr<AutomationControl> (ac)));
+               _mono_panner->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch),
+                                       boost::weak_ptr<AutomationControl>(ac)));
 
-               delete big_window;
-               big_window = 0;
+               _mono_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
 
-       } else {
+               _mono_panner->set_size_request (-1, pan_bar_height);
 
+               update_pan_sensitive ();
+               pan_vbox.pack_start (*_mono_panner, false, false);
+       }
+       else if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_vbap#ui")
+       {
                if (!twod_panner) {
                        twod_panner = new Panner2d (_panshell, 61);
                        twod_panner->set_name ("MixerPanZone");
@@ -309,17 +288,26 @@ PannerUI::setup_pan ()
 
                update_pan_sensitive ();
                twod_panner->reset (nins);
-               if (big_window) {
-                       big_window->reset (nins);
-               }
+               if (big_window) {
+                       big_window->reset (nins);
+               }
                twod_panner->set_size_request (-1, 61);
 
                /* and finally, add it to the panner frame */
 
-                pan_vbox.pack_start (*twod_panner, false, false);
+               pan_vbox.pack_start (*twod_panner, false, false);
+       }
+       else
+       {
+               /* stick something into the panning viewport so that it redraws */
+               EventBox* eb = manage (new EventBox());
+               pan_vbox.pack_start (*eb, false, false);
+
+               delete big_window;
+               big_window = 0;
        }
 
-        pan_vbox.show_all ();
+       pan_vbox.show_all ();
 }
 
 void
@@ -388,8 +376,27 @@ PannerUI::build_pan_menu ()
        bypass_menu_item->set_active (_panshell->bypassed());
        bypass_menu_item->signal_toggled().connect (sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle));
 
-       items.push_back (MenuElem (_("Reset"), sigc::mem_fun (*this, &PannerUI::pan_reset)));
-       items.push_back (MenuElem (_("Edit..."), sigc::mem_fun (*this, &PannerUI::pan_edit)));
+       if (!_panshell->bypassed()) {
+               items.push_back (MenuElem (_("Reset"), sigc::mem_fun (*this, &PannerUI::pan_reset)));
+               items.push_back (MenuElem (_("Edit..."), sigc::mem_fun (*this, &PannerUI::pan_edit)));
+       }
+
+       if (_route && _panner_list.size() > 1 && !_panshell->bypassed()) {
+               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,
+                                               sigc::bind(sigc::mem_fun (*this, &PannerUI::pan_set_custom_type), p->first)));
+                       RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
+                       i->set_active (_panshell->current_panner_uri() == p->first);
+               }
+               _suspend_menu_callbacks = false;
+       }
 }
 
 void
@@ -403,6 +410,9 @@ PannerUI::pan_bypass_toggle ()
 void
 PannerUI::pan_edit ()
 {
+       if (_panshell->bypassed()) {
+               return;
+       }
        if (_mono_panner) {
                _mono_panner->edit ();
        } else if (_stereo_panner) {
@@ -413,9 +423,20 @@ PannerUI::pan_edit ()
 void
 PannerUI::pan_reset ()
 {
+       if (_panshell->bypassed()) {
+               return;
+       }
        _panner->reset ();
 }
 
+void
+PannerUI::pan_set_custom_type (std::string uri) {
+       if (_suspend_menu_callbacks) return;
+       if (_route) {
+               _route->set_custom_panner_uri(uri);
+       }
+}
+
 void
 PannerUI::effective_pan_display ()
 {
@@ -609,3 +630,10 @@ void
 PannerUI::position_adjusted ()
 {
 }
+
+void
+PannerUI::set_available_panners(boost::shared_ptr<ARDOUR::Route> r, std::map<std::string,std::string> p)
+{
+       _route = r;
+       _panner_list = p;
+}
index de93d49956f0aee7785e591f6ddab43c51e5f453..dca24451d198aec34cd34668883eb5de3a652e2f 100644 (file)
@@ -73,6 +73,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
 
        void set_width (Width);
        void setup_pan ();
+       void set_available_panners(boost::shared_ptr<ARDOUR::Route>, std::map<std::string,std::string>);
 
        void effective_pan_display ();
 
@@ -141,6 +142,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        void pan_reset ();
        void pan_bypass_toggle ();
        void pan_edit ();
+       void pan_set_custom_type (std::string type);
 
        void pan_automation_state_changed();
        void pan_automation_style_changed();
@@ -158,6 +160,10 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
 
         void start_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
         void stop_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
+
+       boost::shared_ptr<ARDOUR::Route> _route;
+       std::map<std::string,std::string> _panner_list;
+       bool _suspend_menu_callbacks;
 };
 
 #endif /* __ardour_gtk_panner_ui_h__ */
index 721567bf10de0a8c4c074d7d5ce530a4d533fd74..c2b38867abf473414c0a8cbb5fa79f0580da1da3 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 
 #include "ardour_ui.h"
 #include "global_signals.h"
@@ -58,8 +59,9 @@ bool StereoPanner::have_colors = false;
 
 using namespace ARDOUR;
 
-StereoPanner::StereoPanner (boost::shared_ptr<Panner> panner)
-       : PannerInterface (panner)
+StereoPanner::StereoPanner (boost::shared_ptr<PannerShell> p)
+       : PannerInterface (p->panner())
+       , _panner_shell (p)
        , position_control (_panner->pannable()->pan_azimuth_control)
        , width_control (_panner->pannable()->pan_width_control)
        , dragging_position (false)
@@ -80,6 +82,7 @@ StereoPanner::StereoPanner (boost::shared_ptr<Panner> panner)
 
        position_control->Changed.connect (connections, invalidator(*this), boost::bind (&StereoPanner::value_change, this), gui_context());
        width_control->Changed.connect (connections, invalidator(*this), boost::bind (&StereoPanner::value_change, this), gui_context());
+       _panner_shell->Changed.connect (connections, invalidator (*this), boost::bind (&StereoPanner::bypass_handler, this), gui_context());
 
        ColorsChanged.connect (sigc::mem_fun (*this, &StereoPanner::color_handler));
 
@@ -94,6 +97,10 @@ StereoPanner::~StereoPanner ()
 void
 StereoPanner::set_tooltip ()
 {
+       if (_panner_shell->bypassed()) {
+               _tooltip.set_tip (_("bypassed"));
+               return;
+       }
        double pos = position_control->get_value(); // 0..1
 
        /* We show the position of the center of the image relative to the left & right.
@@ -145,7 +152,11 @@ StereoPanner::on_expose_event (GdkEventExpose*)
 
        /* background */
 
-       context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
+       if (!_panner_shell->bypassed()) {
+               context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
+       } else {
+               context->set_source_rgba (0.1, 0.1, 0.1, 0.2);
+       }
        cairo_rectangle (context->cobj(), 0, 0, width, height);
        context->fill ();
 
@@ -183,6 +194,10 @@ StereoPanner::on_expose_event (GdkEventExpose*)
        context->set_source_rgba (UINT_RGBA_R_FLT(r), UINT_RGBA_G_FLT(r), UINT_RGBA_B_FLT(r), UINT_RGBA_A_FLT(r));
        context->stroke ();
 
+       if (_panner_shell->bypassed()) {
+               return true;
+       }
+
        /* compute & draw the line through the box */
 
        context->set_line_width (2);
@@ -267,6 +282,10 @@ StereoPanner::on_button_press_event (GdkEventButton* ev)
        if (PannerInterface::on_button_press_event (ev)) {
                return true;
        }
+
+       if (_panner_shell->bypassed()) {
+               return true;
+       }
        
        drag_start_x = ev->x;
        last_drag_x = ev->x;
@@ -412,6 +431,10 @@ StereoPanner::on_button_release_event (GdkEventButton* ev)
                return false;
        }
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
        bool const dp = dragging_position;
 
        _dragging = false;
@@ -443,6 +466,10 @@ StereoPanner::on_scroll_event (GdkEventScroll* ev)
        double wv = width_control->get_value(); // 0..1.0 ; 0 = left
        double step;
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
        if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
                step = one_degree;
        } else {
@@ -474,6 +501,9 @@ StereoPanner::on_scroll_event (GdkEventScroll* ev)
 bool
 StereoPanner::on_motion_notify_event (GdkEventMotion* ev)
 {
+       if (_panner_shell->bypassed()) {
+               _dragging = false;
+       }
        if (!_dragging) {
                return false;
        }
@@ -566,6 +596,10 @@ StereoPanner::on_key_press_event (GdkEventKey* ev)
        double wv = width_control->get_value(); // 0..1.0 ; 0 = left
        double step;
 
+       if (_panner_shell->bypassed()) {
+               return false;
+       }
+
        if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
                step = one_degree;
        } else {
@@ -641,6 +675,12 @@ StereoPanner::color_handler ()
        queue_draw ();
 }
 
+void
+StereoPanner::bypass_handler ()
+{
+       queue_draw ();
+}
+
 PannerEditor*
 StereoPanner::editor ()
 {
index 8b62b7d82a412bb41971914215817e52ecdb331b..bb4687d209e8d21fc532951d999470aee6718577 100644 (file)
 #include "gtkmm2ext/binding_proxy.h"
 #include "panner_interface.h"
 
+namespace ARDOUR {
+       class PannerShell;
+}
+
 namespace PBD {
         class Controllable;
 }
@@ -35,7 +39,7 @@ namespace ARDOUR {
 class StereoPanner : public PannerInterface
 {
   public:
-       StereoPanner (boost::shared_ptr<ARDOUR::Panner>);
+       StereoPanner (boost::shared_ptr<ARDOUR::PannerShell>);
        ~StereoPanner ();
 
         boost::shared_ptr<PBD::Controllable> get_position_controllable() const { return position_control; }
@@ -56,6 +60,7 @@ class StereoPanner : public PannerInterface
 
   private:
        PannerEditor* editor ();
+       boost::shared_ptr<ARDOUR::PannerShell> _panner_shell;
                   
         boost::shared_ptr<PBD::Controllable> position_control;
         boost::shared_ptr<PBD::Controllable> width_control;
@@ -94,6 +99,7 @@ class StereoPanner : public PannerInterface
         static void set_colors ();
         static bool have_colors;
        void color_handler ();
+       void bypass_handler ();
 };
 
 #endif /* __gtk_ardour_stereo_panner_h__ */
index b30b1859a3ec2e7dca916d386657c4f9951895bc..fa72b236f748766fb881a59aa82d47d20cea9c3f 100644 (file)
@@ -174,6 +174,8 @@ protected:
 extern "C" {
 struct PanPluginDescriptor {
        std::string name;
+       std::string panner_uri;
+       std::string gui_uri;
        int32_t in;
        int32_t out;
        ARDOUR::Panner* (*factory)(boost::shared_ptr<ARDOUR::Pannable>, boost::shared_ptr<ARDOUR::Speakers>);
index 016ba56ce3c4efbcc3d76c4d6e88f687beab4442..7b52c65c0ab3965119061b4ecbe06c20d3f24f0f 100644 (file)
@@ -49,7 +49,8 @@ public:
        void discover_panners ();
        std::list<PannerInfo*> panner_info;
 
-       PannerInfo* select_panner (ChanCount in, ChanCount out);
+       PannerInfo* select_panner (ChanCount in, ChanCount out, std::string const uri = "");
+       PannerInfo* get_by_uri (std::string uri);
 
 private:
        PannerManager();
index dba5826370dbd65fe3a6a3c9bea6ff4c4e492dae..f798e0da97b922d759db25cee9b835eab06ee984 100644 (file)
@@ -36,6 +36,7 @@
 namespace ARDOUR {
 
 class Session;
+class Route;
 class Panner;
 class BufferSet;
 class AudioBuffer;
@@ -70,11 +71,23 @@ public:
        bool bypassed () const;
        void set_bypassed (bool);
 
+       std::string current_panner_uri() const { return _current_panner_uri; }
+       std::string user_selected_panner_uri() const { return _user_selected_panner_uri; }
+       std::string panner_gui_uri() const { return _panner_gui_uri; }
+
   private:
+       friend class Route;
        void distribute_no_automation (BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff);
+       bool set_user_selected_panner_uri (std::string const uri);
+
        boost::shared_ptr<Panner> _panner;
        boost::shared_ptr<Pannable> _pannable;
        bool _bypassed;
+
+       std::string _current_panner_uri;
+       std::string _user_selected_panner_uri;
+       std::string _panner_gui_uri;
+       bool _force_reselect;
 };
 
 } // namespace ARDOUR
index 83605d74136c5492d26e287d5a1ff8df1d68fd3b..0d200ae44cc3d20f86fc453ad094fd8a594af2a6 100644 (file)
@@ -255,6 +255,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
        int remove_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0, bool need_process_lock = true);
        int remove_processors (const ProcessorList&, ProcessorStreams* err = 0);
        int reorder_processors (const ProcessorList& new_order, ProcessorStreams* err = 0);
+       void set_custom_panner_uri (std::string const panner_uri);
        void disable_processors (Placement);
        void disable_processors ();
        void disable_plugins (Placement);
index 94e8d4ff2f0eb38b1ee051083dd90c8cc3ffe12c..b62f36b3554cb2c5d5697c4dedf173b6fa97fd0b 100644 (file)
@@ -24,7 +24,8 @@
 
 #include "pbd/error.h"
 #include "pbd/compose.h"
-#include "pbd/file_utils.h"
+#include "pbd/pathscanner.h"
+#include "pbd/stl_delete.h"
 
 #include "ardour/debug.h"
 #include "ardour/panner_manager.h"
@@ -59,25 +60,30 @@ PannerManager::instance ()
        return *_instance;
 }
 
+static bool panner_filter (const string& str, void */*arg*/)
+{
+#ifdef __APPLE__
+       return str[0] != '.' && (str.length() > 6 && str.find (".dylib") == (str.length() - 6));
+#else
+       return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3));
+#endif
+}
+
 void
 PannerManager::discover_panners ()
 {
-       vector<std::string> panner_modules;
-
-       Glib::PatternSpec so_extension_pattern("*.so");
-       Glib::PatternSpec dylib_extension_pattern("*.dylib");
+       PathScanner scanner;
+       std::vector<std::string *> *panner_modules;
+       std::string search_path = panner_search_path().to_string();
 
-       find_matching_files_in_search_path (panner_search_path (),
-                                           so_extension_pattern, panner_modules);
+       DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), search_path));
 
-       find_matching_files_in_search_path (panner_search_path (),
-                                           dylib_extension_pattern, panner_modules);
+       panner_modules = scanner (search_path, panner_filter, 0, false, true, 1, true);
 
-       DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1"), panner_search_path().to_string()));
-
-       for (vector<std::string>::iterator i = panner_modules.begin(); i != panner_modules.end(); ++i) {
-               panner_discover (*i);
+       for (vector<std::string *>::iterator i = panner_modules->begin(); i != panner_modules->end(); ++i) {
+               panner_discover (**i);
        }
+       vector_delete (panner_modules);
 }
 
 int
@@ -97,7 +103,7 @@ PannerManager::panner_discover (string path)
 
                if (i == panner_info.end()) {
                        panner_info.push_back (pinfo);
-                       DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2"), pinfo->descriptor.name, path));
+                       DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2\n"), pinfo->descriptor.name, path));
                }
        }
 
@@ -138,12 +144,21 @@ PannerManager::get_descriptor (string path)
 }
 
 PannerInfo*
-PannerManager::select_panner (ChanCount in, ChanCount out)
+PannerManager::select_panner (ChanCount in, ChanCount out, std::string const uri)
 {
        PanPluginDescriptor* d;
        int32_t nin = in.n_audio();
        int32_t nout = out.n_audio();
 
+       /* look for user-preference -- check if channels match */
+       for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
+               d = &(*p)->descriptor;
+               if (d->panner_uri != uri) continue;
+               if (d->in != nin && d->in != -1) continue;
+               if (d->out != nout && d->out != -1) continue;
+               return *p;
+       }
+
        /* look for exact match first */
 
        for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
@@ -188,3 +203,15 @@ PannerManager::select_panner (ChanCount in, ChanCount out)
 
        return 0;
 }
+
+PannerInfo*
+PannerManager::get_by_uri (std::string uri)
+{
+       PannerInfo* pi = NULL;
+       for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
+               if ((*p)->descriptor.panner_uri != uri) continue;
+               pi = (*p);
+               break;
+       }
+       return pi;
+}
index 63802ffc4e7e113a3e717236ca9a94b7d87eb375..49ca786182ef3c0483f8cb0b82bb206a35383197 100644 (file)
@@ -35,9 +35,7 @@ SearchPath
 panner_search_path ()
 {
        SearchPath spath(user_config_directory ());
-       spath += ardour_dll_directory ();
        spath.add_subdirectory_to_paths(panner_dir_name);
-
        spath += SearchPath(Glib::getenv(panner_env_variable_name));
        return spath;
 }
index 1aeb94eb7ee3cb050cefeee7410d09b991495312..a25cb49ab5051ff68050aa2509613b92f60348cf 100644 (file)
@@ -63,6 +63,10 @@ PannerShell::PannerShell (string name, Session& s, boost::shared_ptr<Pannable> p
        : SessionObject (s, name)
        , _pannable (p)
        , _bypassed (false)
+       , _current_panner_uri("")
+       , _user_selected_panner_uri("")
+       , _panner_gui_uri("")
+       , _force_reselect (false)
 {
        set_name (name);
 }
@@ -82,7 +86,7 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
           the config hasn't changed, we're done.
        */
 
-       if (_panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
+       if (!_force_reselect && _panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
                return;
        }
 
@@ -90,17 +94,21 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
                /* no need for panning with less than 2 outputs or no inputs */
                if (_panner) {
                        _panner.reset ();
+                       _current_panner_uri = "";
+                       _panner_gui_uri = "";
                        Changed (); /* EMIT SIGNAL */
                }
                return;
        }
 
-       PannerInfo* pi = PannerManager::instance().select_panner (in, out);
+       PannerInfo* pi = PannerManager::instance().select_panner (in, out, _user_selected_panner_uri);
        if (!pi) {
                cerr << "No panner found: check that panners are being discovered correctly during startup.\n";
                assert (pi);
        }
 
+       DEBUG_TRACE (DEBUG::Panning, string_compose (_("select panner: %1\n"), pi->descriptor.name.c_str()));
+
        boost::shared_ptr<Speakers> speakers = _session.get_speakers ();
 
        if (nouts != speakers->size()) {
@@ -116,6 +124,8 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
        // boost_debug_shared_ptr_mark_interesting (p, "Panner");
        _panner.reset (p);
        _panner->configure_io (in, out);
+       _current_panner_uri = pi->descriptor.panner_uri;
+       _panner_gui_uri = pi->descriptor.gui_uri;
 
        Changed (); /* EMIT SIGNAL */
 }
@@ -126,6 +136,7 @@ PannerShell::get_state ()
        XMLNode* node = new XMLNode ("PannerShell");
 
        node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no"));
+       node->add_property (X_("user-panner"), _user_selected_panner_uri);
 
        if (_panner) {
                node->add_child_nocopy (_panner->get_state ());
@@ -146,12 +157,29 @@ PannerShell::set_state (const XMLNode& node, int version)
                set_bypassed (string_is_affirmative (prop->value ()));
        }
 
+       if ((prop = node.property (X_("user-panner"))) != 0) {
+               _user_selected_panner_uri = prop->value ();
+       }
+
        _panner.reset ();
        
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
                if ((*niter)->name() == X_("Panner")) {
 
+                       if ((prop = (*niter)->property (X_("uri")))) {
+                               PannerInfo* p = PannerManager::instance().get_by_uri(prop->value());
+                               if (p) {
+                                       _panner.reset (p->descriptor.factory (_pannable, _session.get_speakers ()));
+                                       _current_panner_uri = p->descriptor.panner_uri;
+                                       _panner_gui_uri = p->descriptor.gui_uri;
+                                       if (_panner->set_state (**niter, version) == 0) {
+                                               return -1;
+                                       }
+                               }
+                       }
+
+                       else /* backwards compatibility */
                        if ((prop = (*niter)->property (X_("type")))) {
 
                                list<PannerInfo*>::iterator p;
@@ -166,6 +194,8 @@ PannerShell::set_state (const XMLNode& node, int version)
                                                */
 
                                                _panner.reset ((*p)->descriptor.factory (_pannable, _session.get_speakers ()));
+                                               _current_panner_uri = (*p)->descriptor.panner_uri;
+                                               _panner_gui_uri = (*p)->descriptor.gui_uri;
 
                                                if (_panner->set_state (**niter, version) == 0) {
                                                        return -1;
@@ -347,3 +377,19 @@ PannerShell::bypassed () const
 {
        return _bypassed;
 }
+
+/* set custom-panner config
+ *
+ * This function is intended to be only called from
+ * Route::set_custom_panner()
+ * which will trigger IO-reconfigutaion if this fn return true
+ */
+bool
+PannerShell::set_user_selected_panner_uri (std::string const uri)
+{
+       if (uri == _user_selected_panner_uri) return false;
+       _user_selected_panner_uri = uri;
+       if (uri == _current_panner_uri) return false;
+       _force_reselect = true;
+       return true;
+}
index c032b77efff7612d5e278866ce243dd834d9c773..951a4ff30c6cbf3184374d105e0988a960779dba 100644 (file)
@@ -51,6 +51,7 @@
 #include "ardour/midi_port.h"
 #include "ardour/monitor_processor.h"
 #include "ardour/pannable.h"
+#include "ardour/panner.h"
 #include "ardour/panner_shell.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
@@ -1573,6 +1574,51 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
        return 0;
 }
 
+void
+Route::set_custom_panner_uri (std::string const panner_uri)
+{
+       if (!_main_outs->panner_shell()->set_user_selected_panner_uri(panner_uri)) {
+               DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- no change needed\n"), name(), panner_uri));
+               /* no change needed */
+               return;
+       }
+
+       DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- reconfigure I/O\n"), name(), panner_uri));
+
+       if (_in_configure_processors) {
+               DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1' -- called while in_configure_processors\n"), name()));
+               return;
+       }
+
+       /* reconfigure I/O */
+       {
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+               if (panner())
+               {
+                       /* there is already a panner it can just be re-configured in-place */
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       ChanCount in = panner()->in();
+                       ChanCount out = panner()->out();
+                       _main_outs->panner_shell()->configure_io(in, out);
+                       _main_outs->panner_shell()->pannable()->set_panner(panner());
+               }
+               else
+               {
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (0)) {
+                               pstate.restore ();
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
+               }
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+}
+
 void
 Route::reset_instrument_info ()
 {
index 4524ed560bce98c556189af77418680bf374df50..b7ca5ce9142604216878e3895ba395bfc0b6965c 100644 (file)
@@ -63,6 +63,8 @@ using namespace PBD;
 
 static PanPluginDescriptor _descriptor = {
         "Mono to Stereo Panner",
+        "http://ardour.org/plugin/panner_1in2out",
+        "http://ardour.org/plugin/panner_1in2out#ui",
         1, 2, 
         Panner1in2out::factory
 };
@@ -332,6 +334,8 @@ XMLNode&
 Panner1in2out::get_state ()
 {
        XMLNode& root (Panner::get_state ());
+       root.add_property (X_("uri"), _descriptor.panner_uri);
+       /* this is needed to allow new sessions to load with old Ardour: */
        root.add_property (X_("type"), _descriptor.name);
        return root;
 }
index a316b764c77276c543939d3a148057036c4bd624..43cf4840a1a1aa0b1eb2a397f5238ec9c7507257 100644 (file)
@@ -63,6 +63,8 @@ using namespace PBD;
 
 static PanPluginDescriptor _descriptor = {
         "Equal Power Stereo",
+        "http://ardour.org/plugin/panner_2in2out",
+        "http://ardour.org/plugin/panner_2in2out#ui",
         2, 2,
         Panner2in2out::factory
 };
@@ -464,6 +466,8 @@ XMLNode&
 Panner2in2out::get_state ()
 {
        XMLNode& root (Panner::get_state ());
+       root.add_property (X_("uri"), _descriptor.panner_uri);
+       /* this is needed to allow new sessions to load with old Ardour: */
        root.add_property (X_("type"), _descriptor.name);
        return root;
 }
diff --git a/libs/panners/stereobalance/panner_balance.cc b/libs/panners/stereobalance/panner_balance.cc
new file mode 100644 (file)
index 0000000..18bda54
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+    Copyright (C) 2004-2011 Paul Davis
+    adopted from 2in2out panner by Robin Gareus <robin@gareus.org>
+
+    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 <inttypes.h>
+
+#include <cmath>
+#include <cerrno>
+#include <fstream>
+#include <cstdlib>
+#include <string>
+#include <cstdio>
+#include <locale.h>
+#include <unistd.h>
+#include <float.h>
+#include <iomanip>
+
+#include <glibmm.h>
+
+#include "pbd/cartesian.h"
+#include "pbd/convert.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/xml++.h"
+#include "pbd/enumwriter.h"
+
+#include "evoral/Curve.hpp"
+
+#include "ardour/audio_buffer.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/buffer_set.h"
+#include "ardour/pan_controllable.h"
+#include "ardour/pannable.h"
+#include "ardour/runtime_functions.h"
+#include "ardour/session.h"
+#include "ardour/utils.h"
+#include "ardour/mix.h"
+
+#include "panner_balance.h"
+
+#include "i18n.h"
+
+#include "pbd/mathfix.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+static PanPluginDescriptor _descriptor = {
+       "Stereo Balance",
+       "http://ardour.org/plugin/panner_balance",
+       "http://ardour.org/plugin/panner_balance#ui",
+       2, 2,
+       Pannerbalance::factory
+};
+
+extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
+
+Pannerbalance::Pannerbalance (boost::shared_ptr<Pannable> p)
+       : Panner (p)
+{
+       if (!_pannable->has_state()) {
+               _pannable->pan_azimuth_control->set_value (0.5);
+       }
+
+       update ();
+
+       /* LEFT SIGNAL */
+       pos_interp[0] = pos[0] = desired_pos[0];
+       /* RIGHT SIGNAL */
+       pos_interp[1] = pos[1] = desired_pos[1];
+
+       _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Pannerbalance::update, this));
+}
+
+Pannerbalance::~Pannerbalance ()
+{
+}
+
+double
+Pannerbalance::position () const
+{
+       return _pannable->pan_azimuth_control->get_value();
+}
+
+       void
+Pannerbalance::set_position (double p)
+{
+       if (clamp_position (p)) {
+               _pannable->pan_azimuth_control->set_value (p);
+       }
+}
+
+       void
+Pannerbalance::thaw ()
+{
+       Panner::thaw ();
+       if (_frozen == 0) {
+               update ();
+       }
+}
+
+void
+Pannerbalance::update ()
+{
+       if (_frozen) {
+               return;
+       }
+
+       float const pos = _pannable->pan_azimuth_control->get_value();
+
+       if (pos == .5) {
+               desired_pos[0] = 1.0;
+               desired_pos[1] = 1.0;
+       } else if (pos > .5) {
+               desired_pos[0] = 2 - 2. * pos;
+               desired_pos[1] = 1.0;
+       } else {
+               desired_pos[0] = 1.0;
+               desired_pos[1] = 2. * pos;
+       }
+}
+
+bool
+Pannerbalance::clamp_position (double& p)
+{
+       p = max (min (p, 1.0), 0.0);
+       return true;
+}
+
+pair<double, double>
+Pannerbalance::position_range () const
+{
+       return make_pair (0, 1);
+}
+
+void
+Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
+{
+       assert (obufs.count().n_audio() == 2);
+
+       pan_t delta;
+       Sample* dst;
+       pan_t pan;
+
+       Sample* const src = srcbuf.data();
+
+       dst = obufs.get_audio(which).data();
+
+       if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc
+
+               /* we've moving the pan by an appreciable amount, so we must
+                        interpolate over 64 frames or nframes, whichever is smaller */
+
+               pframes_t const limit = min ((pframes_t) 64, nframes);
+               pframes_t n;
+
+               delta = -(delta / (float) (limit));
+
+               for (n = 0; n < limit; n++) {
+                       pos_interp[which] = pos_interp[which] + delta;
+                       pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]);
+                       dst[n] += src[n] * pos[which] * gain_coeff;
+               }
+
+               /* then pan the rest of the buffer; no need for interpolation for this bit */
+
+               pan = pos[which] * gain_coeff;
+
+               mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
+
+       } else {
+
+               pos[which] = desired_pos[which];
+               pos_interp[which] = pos[which];
+
+               if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
+
+                       if (pan != 0.0f) {
+
+                               /* pan is 1 but also not 0, so we must do it "properly" */
+
+                               //obufs.get_audio(1).read_from (srcbuf, nframes);
+                               mix_buffers_with_gain(dst,src,nframes,pan);
+
+                               /* mark that we wrote into the buffer */
+
+                               // obufs[0] = 0;
+
+                       }
+
+               } else {
+                       /* pan is 1 so we can just copy the input samples straight in */
+                       mix_buffers_no_gain(dst,src,nframes);
+               }
+       }
+}
+
+void
+Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+                                         framepos_t start, framepos_t end, pframes_t nframes,
+                                         pan_t** buffers, uint32_t which)
+{
+       assert (obufs.count().n_audio() == 2);
+
+       Sample* dst;
+       pan_t* pbuf;
+       Sample* const src = srcbuf.data();
+       pan_t* const position = buffers[0];
+
+       /* fetch positional data */
+
+       if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
+               /* fallback */
+               distribute_one (srcbuf, obufs, 1.0, nframes, which);
+               return;
+       }
+
+       for (pframes_t n = 0; n < nframes; ++n) {
+
+               float const pos = position[n];
+
+               if (which == 0) { // Left
+                       if (pos > .5) {
+                               buffers[which][n] = 2 - 2. * pos;
+                       } else {
+                               buffers[which][n] = 1.0;
+                       }
+               } else { // Right
+                       if (pos < .5) {
+                               buffers[which][n] = 2. * pos;
+                       } else {
+                               buffers[which][n] = 1.0;
+                       }
+               }
+       }
+
+       dst = obufs.get_audio(which).data();
+       pbuf = buffers[which];
+
+       for (pframes_t n = 0; n < nframes; ++n) {
+               dst[n] += src[n] * pbuf[n];
+       }
+
+       /* XXX it would be nice to mark the buffer as written to */
+}
+
+Panner*
+Pannerbalance::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
+{
+       return new Pannerbalance (p);
+}
+
+       XMLNode&
+Pannerbalance::get_state ()
+{
+       XMLNode& root (Panner::get_state ());
+       root.add_property (X_("uri"), _descriptor.panner_uri);
+       /* this is needed to allow new sessions to load with old Ardour: */
+       root.add_property (X_("type"), _descriptor.name);
+       return root;
+}
+
+std::set<Evoral::Parameter>
+Pannerbalance::what_can_be_automated() const
+{
+       set<Evoral::Parameter> s;
+       s.insert (Evoral::Parameter (PanAzimuthAutomation));
+       return s;
+}
+
+string
+Pannerbalance::describe_parameter (Evoral::Parameter p)
+{
+       switch (p.type()) {
+               case PanAzimuthAutomation:
+                       return _("L/R");
+               default:
+                       return _pannable->describe_parameter (p);
+       }
+}
+
+string
+Pannerbalance::value_as_string (boost::shared_ptr<AutomationControl> ac) const
+{
+       /* DO NOT USE LocaleGuard HERE */
+       double val = ac->get_value();
+
+       switch (ac->parameter().type()) {
+               case PanAzimuthAutomation:
+                       /* We show the position of the center of the image relative to the left & right.
+                                This is expressed as a pair of percentage values that ranges from (100,0)
+                                (hard left) through (50,50) (hard center) to (0,100) (hard right).
+
+                                This is pretty wierd, but its the way audio engineers expect it. Just remember that
+                                the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
+
+                                This is designed to be as narrow as possible. Dedicated
+                                panner GUIs can do their own version of this if they need
+                                something less compact.
+                                */
+
+                       return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
+                                       (int) rint (100.0 * val));
+
+               default:
+                       return _pannable->value_as_string (ac);
+       }
+}
+
+void
+Pannerbalance::reset ()
+{
+       set_position (0.5);
+       update ();
+}
diff --git a/libs/panners/stereobalance/panner_balance.h b/libs/panners/stereobalance/panner_balance.h
new file mode 100644 (file)
index 0000000..f381340
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    Copyright (C) 2004-2014 Paul Davis
+    adopted from 2in2out panner by Robin Gareus <robin@gareus.org>
+
+    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.
+
+*/
+
+#ifndef __ardour_panner_balance_h__
+#define __ardour_panner_balance_h__
+
+#include <cmath>
+#include <cassert>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include "pbd/stateful.h"
+#include "pbd/controllable.h"
+#include "pbd/cartesian.h"
+
+#include "ardour/automation_control.h"
+#include "ardour/automatable.h"
+#include "ardour/panner.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class Pannerbalance : public Panner
+{
+       public:
+       Pannerbalance (boost::shared_ptr<Pannable>);
+       ~Pannerbalance ();
+
+       ChanCount in() const { return ChanCount (DataType::AUDIO, 2); }
+       ChanCount out() const { return ChanCount (DataType::AUDIO, 2); }
+
+       void set_position (double);
+       bool clamp_position (double&);
+       std::pair<double, double> position_range () const;
+       double position () const;
+
+       std::set<Evoral::Parameter> what_can_be_automated() const;
+
+       static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
+
+       std::string describe_parameter (Evoral::Parameter);
+       std::string value_as_string (boost::shared_ptr<AutomationControl>) const;
+
+       XMLNode& get_state ();
+
+       void reset ();
+       void thaw ();
+
+       protected:
+       float pos[2];
+       float desired_pos[2];
+       float pos_interp[2];
+
+       void update ();
+
+       private:
+       void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
+       void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+                       framepos_t start, framepos_t end, pframes_t nframes,
+                       pan_t** buffers, uint32_t which);
+};
+
+} // namespace
+
+#endif /* __ardour_panner_balance_h__ */
diff --git a/libs/panners/stereobalance/wscript b/libs/panners/stereobalance/wscript
new file mode 100644 (file)
index 0000000..75eccca
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LIBARDOUR_PAN2IN2OUT_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    autowaf.set_options(opt)
+
+def configure(conf):
+    autowaf.configure(conf)
+
+def build(bld):
+    obj = bld(features = 'cxx cxxshlib')
+    obj.source = [ 'panner_balance.cc' ]
+    obj.export_includes = ['.']
+    obj.cxxflags     = '-DPACKAGE="libardour_panbalance"'
+    obj.includes     = ['.']
+    obj.name         = 'libardour_panbalance'
+    obj.target       = 'panbalance'
+    obj.use          = 'libardour libardour_cp libpbd'
+    obj.vnum         = LIBARDOUR_PAN2IN2OUT_LIB_VERSION
+    obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+    autowaf.shutdown()
index 1cef98fc7d0897a17efced6646faf3b3bedce35e..83095cf7b8b0f97e64289ab66893088cb3f4d39a 100644 (file)
@@ -46,6 +46,8 @@ using namespace std;
 
 static PanPluginDescriptor _descriptor = {
         "VBAP 2D panner",
+        "http://ardour.org/plugin/panner_vbap",
+        "http://ardour.org/plugin/panner_vbap#ui",
         -1, -1,
         VBAPanner::factory
 };
@@ -392,7 +394,9 @@ VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
 XMLNode&
 VBAPanner::get_state ()
 {
-        XMLNode& node (Panner::get_state());
+       XMLNode& node (Panner::get_state());
+       node.add_property (X_("uri"), _descriptor.panner_uri);
+       /* this is needed to allow new sessions to load with old Ardour: */
        node.add_property (X_("type"), _descriptor.name);
        return node;
 }
index aec57eb40f35ad3fbfdfcf3f3256d46a180607b5..f3ce6e6f1997e4165ac78116d46f3053ce508f63 100644 (file)
@@ -6,7 +6,7 @@ import os
 top = '.'
 out = 'build'
 
-panners = [ '2in2out', '1in2out', 'vbap' ]
+panners = [ '2in2out', '1in2out', 'vbap', 'stereobalance' ]
 
 def options(opt):
     autowaf.set_options(opt)