merge 3.0-panexp (pan experiments) branch, revisions 8534-8585 into 3.0, thus ending...
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 27 Jan 2011 01:31:03 +0000 (01:31 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Thu, 27 Jan 2011 01:31:03 +0000 (01:31 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@8586 d708f5d6-7413-0410-9779-e7cbd77b26cf

78 files changed:
gtk2_ardour/about.cc
gtk2_ardour/ardev_common.sh.in
gtk2_ardour/audio_time_axis.cc
gtk2_ardour/automation_controller.cc
gtk2_ardour/automation_line.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_selection.cc
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/mixer_strip.cc
gtk2_ardour/mono_panner.cc
gtk2_ardour/panner2d.cc
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
gtk2_ardour/processor_box.cc
gtk2_ardour/stereo_panner.cc
gtk2_ardour/stereo_panner.h
gtk2_ardour/wscript
libs/ardour/ardour/automatable.h
libs/ardour/ardour/debug.h
libs/ardour/ardour/delivery.h
libs/ardour/ardour/directory_names.h
libs/ardour/ardour/internal_send.h
libs/ardour/ardour/pannable.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/port_insert.h
libs/ardour/ardour/route.h
libs/ardour/ardour/send.h
libs/ardour/ardour/speakers.h
libs/ardour/ardour/types.h
libs/ardour/ardour/vbap.h [deleted file]
libs/ardour/ardour/vbap_speakers.h [deleted file]
libs/ardour/audioengine.cc
libs/ardour/auditioner.cc
libs/ardour/automatable.cc
libs/ardour/automation_list.cc
libs/ardour/debug.cc
libs/ardour/delivery.cc
libs/ardour/directory_names.cc
libs/ardour/diskstream.cc
libs/ardour/enums.cc
libs/ardour/event_type_map.cc
libs/ardour/globals.cc
libs/ardour/internal_send.cc
libs/ardour/pannable.cc
libs/ardour/panner.cc
libs/ardour/panner_shell.cc
libs/ardour/port_insert.cc
libs/ardour/route.cc
libs/ardour/send.cc
libs/ardour/session.cc
libs/ardour/session_state.cc
libs/ardour/speakers.cc
libs/ardour/thread_buffers.cc
libs/ardour/vbap.cc [deleted file]
libs/ardour/vbap_speakers.cc [deleted file]
libs/ardour/wscript
libs/evoral/evoral/ControlList.hpp
libs/evoral/src/ControlList.cpp
libs/gtkmm2ext/gtkmm2ext/utils.h
libs/gtkmm2ext/utils.cc
libs/panners/1in2out/panner_1in2out.cc
libs/panners/1in2out/panner_1in2out.h
libs/panners/1in2out/wscript [new file with mode: 0644]
libs/panners/2in2out/panner_2in2out.cc
libs/panners/2in2out/panner_2in2out.h
libs/panners/2in2out/wscript
libs/panners/vbap/vbap.cc
libs/panners/vbap/vbap.h
libs/panners/vbap/vbap_speakers.cc
libs/panners/vbap/wscript [new file with mode: 0644]
libs/panners/wscript
libs/pbd/pbd/controllable_descriptor.h
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/route_signal.cc
libs/surfaces/osc/osc.cc
wscript

index 7eb33f02dfa3b3f649f9f31c29c5bbaa7904a828..8f4d6e052630e86ee8acfca1fe019c8e04a99c20 100644 (file)
@@ -165,6 +165,7 @@ static const char* authors[] = {
        N_("Petter Sundlöf"),
        N_("Mike Täht"),
        N_("Thorsten Wilms"),
+        N_("Robin Gareus"),
        0
 };
 
index 44dc3445c76820f36a7a0ce67ca370a305dbaaba..116a6d88fc857b25b88d1c63c682e9d219962e70 100644 (file)
@@ -6,6 +6,7 @@ libs=$TOP/@LIBS@
 
 export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/default/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
+export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap
 export ARDOUR_DATA_PATH=$TOP/gtk2_ardour:build/default/gtk2_ardour:.
 
 if test -d $HOME/gtk/inst ; then
index d791290562d48700c5fbbb9ee3f80837b29fcef4..fcdc4bbdfa59e9f9146b71b88c6aecd8796cfabe 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/audioplaylist.h"
 #include "ardour/event_type_map.h"
 #include "ardour/location.h"
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/playlist.h"
 #include "ardour/processor.h"
@@ -191,7 +192,9 @@ AudioTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool
 
                create_gain_automation_child (param, show);
 
-       } else if (param.type() == PanAutomation) {
+       } else if (param.type() == PanWidthAutomation ||
+                   param.type() == PanElevationAutomation ||
+                   param.type() == PanAzimuthAutomation) {
 
                ensure_xml_node ();
                ensure_pan_views (show);
@@ -217,13 +220,11 @@ AudioTimeAxisView::ensure_pan_views (bool show)
                return;
        }
 
-       const set<Evoral::Parameter>& params = _route->panner()->what_can_be_automated();
+       set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
        set<Evoral::Parameter>::iterator p;
 
        for (p = params.begin(); p != params.end(); ++p) {
-               boost::shared_ptr<ARDOUR::AutomationControl> pan_control
-                       = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
-                               _route->panner()->control(*p));
+               boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
 
                if (pan_control->parameter().type() == NullAutomation) {
                        error << "Pan control has NULL automation type!" << endmsg;
@@ -238,7 +239,9 @@ AudioTimeAxisView::ensure_pan_views (bool show)
 
                        boost::shared_ptr<AutomationTimeAxisView> t (
                                new AutomationTimeAxisView (_session,
-                                                           _route, _route->panner(), pan_control,
+                                                           _route, 
+                                                            _route->pannable(), 
+                                                            pan_control,
                                                            _editor,
                                                            *this,
                                                            false,
@@ -442,7 +445,7 @@ AudioTimeAxisView::build_automation_action_menu ()
        pan_automation_item = dynamic_cast<CheckMenuItem*> (&automation_items.back ());
        pan_automation_item->set_active (pan_tracks.front()->marked_for_display ());
 
-       set<Evoral::Parameter> const & params = _route->panner()->what_can_be_automated ();
+       set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
        for (set<Evoral::Parameter>::iterator p = params.begin(); p != params.end(); ++p) {
                _main_automation_menu_map[*p] = pan_automation_item;
        }
index 3af5f3444511d277810f62c9d74c3d84d4b8b512..f09fae905bb24dc986011818889e53be602a6efa 100644 (file)
@@ -27,6 +27,7 @@
 #include "ardour/event_type_map.h"
 #include "ardour/automatable.h"
 #include "ardour/panner.h"
+#include "ardour/pan_controllable.h"
 #include "ardour/session.h"
 
 #include "ardour_ui.h"
@@ -90,8 +91,6 @@ AutomationController::get_label (int&)
        // Hack to display CC rounded to int
        if (_controllable->parameter().type() == MidiCCAutomation) {
                s << (int)_controllable->get_value();
-       } else if (_controllable->parameter().type() == PanAutomation) {
-               s << Panner::value_as_string (_controllable->get_value ());
        } else {
                s << std::fixed << std::setprecision(3) << _controllable->get_value();
        }
index a8125c0818be4e0b649003ad56fc5ff1a18c114b..45b9573f71b388a74c320c0e81f13e38060fb6f1 100644 (file)
@@ -1204,8 +1204,9 @@ AutomationLine::view_to_model_coord_y (double& y) const
                y = slider_position_to_gain (y);
                y = max (0.0, y);
                y = min (2.0, y);
-       } else if (alist->parameter().type() == PanAutomation) {
-               // vertical coordinate axis reversal
+       } else if (alist->parameter().type() == PanAzimuthAutomation ||
+                   alist->parameter().type() == PanElevationAutomation ||
+                   alist->parameter().type() == PanWidthAutomation) {
                y = 1.0 - y;
        } else if (alist->parameter().type() == PluginAutomation) {
                y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
@@ -1221,7 +1222,9 @@ AutomationLine::model_to_view_coord (double& x, double& y) const
        if (alist->parameter().type() == GainAutomation ||
            alist->parameter().type() == EnvelopeAutomation) {
                y = gain_to_slider_position (y);
-       } else if (alist->parameter().type() == PanAutomation) {
+       } else if (alist->parameter().type() == PanAzimuthAutomation ||
+                   alist->parameter().type() == PanElevationAutomation ||
+                   alist->parameter().type() == PanWidthAutomation) {
                // vertical coordinate axis reversal
                y = 1.0 - y;
        } else if (alist->parameter().type() == PluginAutomation) {
index b14c5f20e700b7ec5be5870eee8a6b15ddedaba1..aca401508381c694c393ef203e0ba1fe4c442dac 100644 (file)
@@ -639,6 +639,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
 
        void set_selected_track (TimeAxisView&, Selection::Operation op = Selection::Set, bool no_remove=false);
        void select_all_tracks ();
+        void select_all_internal_edit (Selection::Operation);
 
        bool set_selected_control_point_from_click (Selection::Operation op = Selection::Set, bool no_remove=false);
        void set_selected_track_from_click (bool press, Selection::Operation op = Selection::Set, bool no_remove=false);
index 0c8da2bf1b8acc45458d93159a13474d766be375..d1d9afc985c74f48ab92a1b2b65f6b8b330b4a86 100644 (file)
@@ -2628,11 +2628,6 @@ Editor::set_internal_edit (bool yn)
                ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
                mouse_mode_toggled (mouse_mode);
 
-               /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
-                  region because cut means "cut note" rather than "cut region".
-               */
-               selection->clear ();
-
        } else {
 
                mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
index 482a778ae721fcc61bb326853282bdace81efaf0..3212c93bb76ecbf6ba553cb08fc5811222e9b777 100644 (file)
@@ -37,6 +37,7 @@
 #include "control_point.h"
 #include "editor_regions.h"
 #include "editor_cursors.h"
+#include "midi_region_view.h"
 
 #include "i18n.h"
 
@@ -1212,11 +1213,27 @@ Editor::select_all_in_track (Selection::Operation op)
        }
 }
 
+void
+Editor::select_all_internal_edit (Selection::Operation op)
+{
+        /* currently limited to MIDI only */
+
+       for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
+               MidiRegionView* mrv = *i;
+               mrv->select_all_notes ();
+       }
+}
+
 void
 Editor::select_all (Selection::Operation op)
 {
        list<Selectable *> touched;
 
+        if (_internal_editing) {
+                select_all_internal_edit (op);
+                return;
+        }
+
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
index fd2d629a09919d74ffab249aa5b126e3a0cb607e..60e07c17e08fc2784fd68ae741a858b635c53ba7 100644 (file)
@@ -1873,6 +1873,16 @@ MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
        }
 }
 
+void
+MidiRegionView::select_all_notes ()
+{
+        clear_selection ();
+
+       for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+                add_to_selection (*i);
+        }
+}
+
 void
 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
 {
index 846b1651a14b7b21ac5c805a4eccdb0b8b02ad11..edf418afb418b7ccd757914f9e3785b60c3600ec 100644 (file)
@@ -191,6 +191,7 @@ class MidiRegionView : public RegionView
        void   delete_selection();
        void   delete_note (boost::shared_ptr<NoteType>);
        size_t selection_size() { return _selection.size(); }
+        void   select_all_notes ();
 
        void move_selection(double dx, double dy, double cumulative_dy);
        void note_dropped (ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::frameoffset_t, int8_t d_note);
index 8c6d612012b4726a07d6b9173d8df6b1f704bdd6..245b5c0072b1e1bff3cbc9239a7027701fc6692a 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/audio_track.h"
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/send.h"
 #include "ardour/processor.h"
@@ -967,14 +968,10 @@ MixerStrip::connect_to_pan ()
                return;
        }
 
-       boost::shared_ptr<ARDOUR::AutomationControl> pan_control
-               = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
-                               _route->panner()->control(Evoral::Parameter(PanAutomation)));
+        boost::shared_ptr<Pannable> p = _route->pannable ();
 
-       if (pan_control) {
-               pan_control->alist()->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
-               pan_control->alist()->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
-       }
+        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.panner_changed (this);
 }
@@ -1598,10 +1595,10 @@ MixerStrip::reset_strip_style ()
                if (is_midi_track()) {
                        if (_route->active()) {
                                set_name ("MidiTrackStripBase");
-                               gpm.set_meter_strip_name ("MidiTrackStripBase");
+                               gpm.set_meter_strip_name ("MidiTrackMetrics");
                        } else {
                                set_name ("MidiTrackStripBaseInactive");
-                               gpm.set_meter_strip_name ("MidiTrackStripBaseInactive");
+                               gpm.set_meter_strip_name ("MidiTrackMetricsInactive");
                        }
                        gpm.set_fader_name ("MidiTrackFader");
                } else if (is_audio_track()) {
index 93f635d9709b8c71cd8ff3c37e07ba35c22a2795..52c417a9fd34e0b77900622e4492676b45c0b726 100644 (file)
@@ -31,6 +31,7 @@
 #include "gtkmm2ext/gtk_ui.h"
 #include "gtkmm2ext/keyboard.h"
 
+#include "ardour/panner.h"
 #include "ardour/panner.h"
 
 #include "ardour_ui.h"
index 070068505689da4876c9b2cd7452c93bdba13299..1d98ca00933b793ac7917100680d0cd19408c2bc 100644 (file)
@@ -78,7 +78,7 @@ Panner2d::~Panner2d()
 void
 Panner2d::reset (uint32_t n_inputs)
 {
-       Targets::size_type existing_pucks = pucks.size();
+        uint32_t nouts = panner->out().n_audio();
 
        /* pucks */
 
@@ -120,36 +120,40 @@ Panner2d::reset (uint32_t n_inputs)
                break;
        }
 
+#ifdef PANNER_HACKS
        for (uint32_t i = existing_pucks; i < n_inputs; ++i) {
                pucks[i]->position = panner->streampanner (i).get_position ();
                pucks[i]->visible = true;
        }
+#endif
 
        /* add all outputs */
 
-       while (targets.size() < panner->nouts()) {
+       while (targets.size() < nouts) {
                add_target (AngularVector());
        }
 
-       if (targets.size() > panner->nouts()) {
-               for (uint32_t i = panner->nouts(); i < targets.size(); ++i) {
+       if (targets.size() > nouts) {
+               for (uint32_t i = nouts; i < targets.size(); ++i) {
                        delete targets[i];
                }
 
-               targets.resize (panner->nouts ());
+               targets.resize (nouts);
        }
 
        for (Targets::iterator x = targets.begin(); x != targets.end(); ++x) {
                (*x)->visible = false;
        }
 
-       for (uint32_t n = 0; n < panner->nouts(); ++n) {
+       for (uint32_t n = 0; n < nouts; ++n) {
                char buf[16];
 
                snprintf (buf, sizeof (buf), "%d", n+1);
                targets[n]->set_text (buf);
+#ifdef PANNER_HACKS
                targets[n]->position = panner->output(n).position;
                targets[n]->visible = true;
+#endif
        }
 
        queue_draw ();
@@ -201,6 +205,7 @@ Panner2d::handle_state_change ()
 void
 Panner2d::handle_position_change ()
 {
+#ifdef PANNER_HACKS 
        uint32_t n;
        ENSURE_GUI_THREAD (*this, &Panner2d::handle_position_change)
 
@@ -213,6 +218,7 @@ Panner2d::handle_position_change ()
        }
 
        queue_draw ();
+#endif
 }
 
 void
@@ -528,7 +534,9 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state)
 
                        cp.angular (drag_target->position); /* sets drag target position */
 
+#ifdef PANNER_HACKS
                        panner->streampanner (drag_index).set_position (drag_target->position);
+#endif 
                         
                        queue_draw ();
                }
index 52ae16a64884bc40fd93d08ffe3fdc4ddd42064a..79a7259b32ff966692bd83cb4df5231bcdadabc1 100644 (file)
@@ -36,6 +36,7 @@
 #include "ardour/delivery.h"
 #include "ardour/session.h"
 #include "ardour/panner.h"
+#include "ardour/pannable.h"
 #include "ardour/route.h"
 
 #include "i18n.h"
@@ -50,13 +51,7 @@ const int PannerUI::pan_bar_height = 40;
 
 PannerUI::PannerUI (Session* s)
        : _current_nouts (-1)
-       , _current_npans (-1)
-       , hAdjustment(0.0, 0.0, 0.0)
-       , vAdjustment(0.0, 0.0, 0.0)
-       , panning_viewport(hAdjustment, vAdjustment)
-       , panning_up_arrow (Gtk::ARROW_UP, Gtk::SHADOW_OUT)
-       , panning_down_arrow (Gtk::ARROW_DOWN, Gtk::SHADOW_OUT)
-       , panning_link_button (_("link"))
+       , _current_nins (-1)
        , pan_automation_style_button ("")
        , pan_automation_state_button ("")
 {
@@ -80,52 +75,13 @@ PannerUI::PannerUI (Session* s)
        //set_size_request_to_display_given_text (pan_automation_state_button, X_("O"), 2, 2);
        //set_size_request_to_display_given_text (pan_automation_style_button, X_("0"), 2, 2);
 
-       panning_viewport.set_name (X_("BaseFrame"));
-
-       ARDOUR_UI::instance()->set_tip (panning_link_button,
-                                                  _("panning link control"));
-       ARDOUR_UI::instance()->set_tip (panning_link_direction_button,
-                                                  _("panning link direction"));
-
        pan_automation_style_button.unset_flags (Gtk::CAN_FOCUS);
        pan_automation_state_button.unset_flags (Gtk::CAN_FOCUS);
 
        pan_automation_style_button.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_style_button_event), false);
        pan_automation_state_button.signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_automation_state_button_event), false);
 
-       panning_link_button.set_name (X_("PanningLinkButton"));
-       panning_link_direction_button.set_name (X_("PanningLinkDirectionButton"));
-
-       panning_link_box.pack_start (panning_link_button, true, true);
-       panning_link_box.pack_start (panning_link_direction_button, true, true);
-       panning_link_box.pack_start (pan_automation_state_button, true, true);
-
-       /* the pixmap will be reset at some point, but the key thing is that
-          we need a pixmap in the button just to get started.
-       */
-       panning_link_direction_button.add (*(manage (new Image (get_xpm("forwardblarrow.xpm")))));
-
-       panning_link_direction_button.signal_clicked().connect
-               (sigc::mem_fun(*this, &PannerUI::panning_link_direction_clicked));
-
-       panning_link_button.signal_button_press_event().connect
-               (sigc::mem_fun(*this, &PannerUI::panning_link_button_press), false);
-       panning_link_button.signal_button_release_event().connect
-               (sigc::mem_fun(*this, &PannerUI::panning_link_button_release), false);
-
-       panning_up.set_border_width (3);
-       panning_down.set_border_width (3);
-       panning_up.add (panning_up_arrow);
-       panning_down.add (panning_down_arrow);
-       panning_up.set_name (X_("PanScrollerBase"));
-       panning_down.set_name (X_("PanScrollerBase"));
-       panning_up_arrow.set_name (X_("PanScrollerArrow"));
-       panning_down_arrow.set_name (X_("PanScrollerArrow"));
-
        pan_vbox.set_spacing (2);
-       pan_vbox.pack_start (panning_viewport, Gtk::PACK_SHRINK);
-       pan_vbox.pack_start (panning_link_box, Gtk::PACK_SHRINK);
-        
        pack_start (pan_vbox, true, true);
 
        twod_panner = 0;
@@ -158,18 +114,16 @@ PannerUI::set_panner (boost::shared_ptr<Panner> p)
        }
 
        _panner->Changed.connect (connections, invalidator (*this), boost::bind (&PannerUI::panner_changed, this, this), gui_context());
-       _panner->LinkStateChanged.connect (connections, invalidator (*this), boost::bind (&PannerUI::update_pan_linkage, this), gui_context());
        _panner->StateChanged.connect (connections, invalidator (*this), boost::bind (&PannerUI::update_pan_state, this), gui_context());
 
         /* new panner object, force complete reset of panner GUI
          */
 
         _current_nouts = 0;
-        _current_npans = 0;
+        _current_nins = 0;
 
        panner_changed (0);
        update_pan_sensitive ();
-       update_pan_linkage ();
        pan_automation_state_changed ();
 
 }
@@ -224,61 +178,6 @@ PannerUI::get_controllable()
        return pan_bars[0]->get_controllable();
 }
 
-bool
-PannerUI::panning_link_button_press (GdkEventButton*)
-{
-       return true;
-}
-
-bool
-PannerUI::panning_link_button_release (GdkEventButton*)
-{
-       if (!ignore_toggle) {
-               _panner->set_linked (!_panner->linked());
-       }
-       return true;
-}
-
-void
-PannerUI::panning_link_direction_clicked()
-{
-       switch (_panner->link_direction()) {
-       case Panner::SameDirection:
-               _panner->set_link_direction (Panner::OppositeDirection);
-               break;
-       default:
-               _panner->set_link_direction (Panner::SameDirection);
-               break;
-       }
-}
-
-void
-PannerUI::update_pan_linkage ()
-{
-       ENSURE_GUI_THREAD (*this, &PannerUI::update_pan_linkage)
-
-       bool const x = _panner->linked();
-       bool const bx = panning_link_button.get_active();
-
-       if (x != bx) {
-
-               ignore_toggle = true;
-               panning_link_button.set_active (x);
-               ignore_toggle = false;
-       }
-
-       panning_link_direction_button.set_sensitive (x);
-
-       switch (_panner->link_direction()) {
-       case Panner::SameDirection:
-               panning_link_direction_button.set_image (*(manage (new Image (get_xpm ("forwardblarrow.xpm")))));
-               break;
-       default:
-               panning_link_direction_button.set_image (*(manage (new Image (get_xpm("revdblarrow.xpm")))));
-               break;
-       }
-}
-
 void
 PannerUI::on_size_allocate (Allocation& a)
 {
@@ -288,19 +187,9 @@ PannerUI::on_size_allocate (Allocation& a)
 void
 PannerUI::set_width (Width w)
 {
-       switch (w) {
-       case Wide:
-               panning_link_button.set_label (_("link"));
-               break;
-       case Narrow:
-               panning_link_button.set_label (_("L"));
-               break;
-       }
-
        _width = w;
 }
 
-
 PannerUI::~PannerUI ()
 {
        for (vector<MonoPanner*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
@@ -319,50 +208,13 @@ PannerUI::~PannerUI ()
 void
 PannerUI::panner_changed (void* src)
 {
-       ENSURE_GUI_THREAD (*this, &PannerUI::panner_changed)
-
        setup_pan ();
-
-       if (src == this) {
-               return;
-       }
-
-       switch (_panner->npanners()) {
-       case 0:
-               panning_link_direction_button.set_sensitive (false);
-               panning_link_button.set_sensitive (false);
-               return;
-       case 1:
-               panning_link_direction_button.set_sensitive (false);
-               panning_link_button.set_sensitive (false);
-               break;
-       default:
-               panning_link_direction_button.set_sensitive (_panner->linked ());
-               panning_link_button.set_sensitive (true);
-       }
-
-       uint32_t const nouts = _panner->nouts();
-
-       switch (nouts) {
-       case 0:
-       case 1:
-               /* relax */
-               break;
-
-       case 2:
-               break;
-
-       default:
-               // panner->move_puck (pan_value (pans[0], pans[1]), 0.5);
-               break;
-       }
 }
 
 void
 PannerUI::update_pan_state ()
 {
        /* currently nothing to do */
-       // ENSURE_GUI_THREAD (*this, &PannerUI::update_panner_state)
 }
 
 void
@@ -370,101 +222,81 @@ PannerUI::setup_pan ()
 {
        if (!_panner) {
                return;
-       }
-
-       uint32_t const nouts = _panner->nouts();
-       uint32_t const npans = _panner->npanners();
+       } 
+       uint32_t const nouts = _panner->out().n_audio();
+       uint32_t const nins = _panner->in().n_audio();
 
-       if (int32_t (nouts) == _current_nouts && int32_t (npans) == _current_npans) {
+       if (int32_t (nouts) == _current_nouts && int32_t (nins) == _current_nins) {
                return;
        }
 
-       _current_nouts = nouts;
-       _current_npans = npans;
-
-        panning_viewport.remove ();
+        container_clear (pan_vbox);
 
         delete twod_panner;
         twod_panner = 0;
         delete _stereo_panner;
         _stereo_panner = 0;
-
+        
        if (nouts == 0 || nouts == 1) {
 
-               while (!pan_bars.empty()) {
-                       delete pan_bars.back();
-                       pan_bars.pop_back ();
-               }
+                delete _stereo_panner;
+                delete twod_panner;
 
                /* stick something into the panning viewport so that it redraws */
 
                EventBox* eb = manage (new EventBox());
-               panning_viewport.add (*eb);
+               pan_vbox.pack_start (*eb, false, false);
 
        } else if (nouts == 2) {
 
-               vector<Adjustment*>::size_type p;
-
-               while (!pan_bars.empty()) {
-                       delete pan_bars.back();
-                       pan_bars.pop_back ();
-               }
-
-                if (npans == 2) {
+                if (nins == 2) {
 
                         /* add integrated 2in/2out panner GUI */
 
-                        _stereo_panner = new StereoPanner (_panner->direction_control(), 
-                                                           _panner->width_control());
+                        boost::shared_ptr<Pannable> pannable = _panner->pannable();
+
+                        _stereo_panner = new StereoPanner (_panner);
                         _stereo_panner->set_size_request (-1, pan_bar_height);
-                        panning_viewport.add (*_stereo_panner);
+                        pan_vbox.pack_start (*_stereo_panner, false, false);
 
                         boost::shared_ptr<AutomationControl> ac;
 
-                        ac = _panner->direction_control();
+                        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)));
 
-                        ac = _panner->width_control();
+                        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)));
 
-                } else {
+                } else if (nins == 1) {
+                        /* 1-in/2out */
                         
-                        /* N-in/2out - just use a set of single-channel panners */
-
-                        while ((p = pan_bars.size()) < npans) {
-                                
-                                MonoPanner* mp;
-                                boost::shared_ptr<AutomationControl> ac = _panner->pan_control (p);
-
-                                mp = new MonoPanner (_panner->pan_control (p));
-                                
-                                mp->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch), 
+                        MonoPanner* mp;
+                        boost::shared_ptr<Pannable> pannable = _panner->pannable();
+                        boost::shared_ptr<AutomationControl> ac = pannable->pan_azimuth_control;
+                        
+                        mp = new MonoPanner (ac);
+                        
+                        mp->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch), 
                                                                       boost::weak_ptr<AutomationControl> (ac)));
-                                mp->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch), 
-                                                                     boost::weak_ptr<AutomationControl>(ac)));
-
-                                mp->signal_button_release_event().connect
-                                        (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) p));
-                                
-                                mp->set_size_request (-1, pan_bar_height);
-                                
-                                pan_bars.push_back (mp);
-                                pan_bar_packer.pack_start (*mp, false, false);
-                        }
+                        mp->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch), 
+                                                             boost::weak_ptr<AutomationControl>(ac)));
                         
-                        /* now that we actually have the pan bars,
-                           set their sensitivity based on current
-                           automation state.
-                        */
+                        mp->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
+                        
+                        mp->set_size_request (-1, pan_bar_height);
                         
                         update_pan_sensitive ();
-                        panning_viewport.add (pan_bar_packer);
+                        pan_vbox.pack_start (*mp, false, false);
+
+                } else {
+                        warning << string_compose (_("No panner user interface is currently available for %1-in/2out tracks/busses"),
+                                                   nins) << endmsg;
                 }
 
 
@@ -474,24 +306,22 @@ PannerUI::setup_pan ()
                        twod_panner = new Panner2d (_panner, 61);
                        twod_panner->set_name ("MixerPanZone");
                        twod_panner->show ();
-
-                       twod_panner->signal_button_press_event().connect
-                               (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_button_event), (uint32_t) 0), false);
+                       twod_panner->signal_button_press_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event), false);
                }
 
                update_pan_sensitive ();
-               twod_panner->reset (npans);
+               twod_panner->reset (nins);
                if (big_window) {
-                       big_window->reset (npans);
+                       big_window->reset (nins);
                }
                twod_panner->set_size_request (-1, 61);
 
                /* and finally, add it to the panner frame */
 
-               panning_viewport.add (*twod_panner);
+                pan_vbox.pack_start (*twod_panner, false, false);
        }
 
-        panning_viewport.show_all ();
+        pan_vbox.show_all ();
 }
 
 void
@@ -515,13 +345,13 @@ PannerUI::stop_touch (boost::weak_ptr<AutomationControl> wac)
 }
 
 bool
-PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
+PannerUI::pan_button_event (GdkEventButton* ev)
 {
        switch (ev->button) {
        case 1:
                if (twod_panner && ev->type == GDK_2BUTTON_PRESS) {
                        if (!big_window) {
-                               big_window = new Panner2dWindow (_panner, 400, _panner->npanners());
+                               big_window = new Panner2dWindow (_panner, 400, _panner->in().n_audio());
                        }
                        big_window->show ();
                        return true;
@@ -533,7 +363,7 @@ PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
                        pan_menu = manage (new Menu);
                        pan_menu->set_name ("ArdourContextMenu");
                }
-               build_pan_menu (which);
+               build_pan_menu ();
                pan_menu->popup (1, ev->time);
                return true;
                break;
@@ -545,21 +375,13 @@ PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
 }
 
 void
-PannerUI::build_pan_menu (uint32_t which)
+PannerUI::build_pan_menu ()
 {
        using namespace Menu_Helpers;
        MenuList& items (pan_menu->items());
 
        items.clear ();
 
-       items.push_back (CheckMenuElem (_("Mute")));
-
-       /* set state first, connect second */
-
-       (dynamic_cast<CheckMenuItem*> (&items.back()))->set_active (_panner->streampanner(which).muted());
-       (dynamic_cast<CheckMenuItem*> (&items.back()))->signal_toggled().connect
-               (sigc::bind (sigc::mem_fun(*this, &PannerUI::pan_mute), which));
-
        items.push_back (CheckMenuElem (_("Bypass"), sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle)));
        bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
 
@@ -568,16 +390,7 @@ PannerUI::build_pan_menu (uint32_t which)
        bypass_menu_item->set_active (_panner->bypassed());
        bypass_menu_item->signal_toggled().connect (sigc::mem_fun(*this, &PannerUI::pan_bypass_toggle));
 
-       items.push_back (MenuElem (_("Reset"), sigc::bind (sigc::mem_fun (*this, &PannerUI::pan_reset), which)));
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Reset all"), sigc::mem_fun (*this, &PannerUI::pan_reset_all)));
-}
-
-void
-PannerUI::pan_mute (uint32_t which)
-{
-       StreamPanner& sp = _panner->streampanner(which);
-       sp.set_muted (!sp.muted());
+       items.push_back (MenuElem (_("Reset"), sigc::mem_fun (*this, &PannerUI::pan_reset)));
 }
 
 void
@@ -589,15 +402,9 @@ PannerUI::pan_bypass_toggle ()
 }
 
 void
-PannerUI::pan_reset (uint32_t which)
-{
-       _panner->reset_streampanner (which);
-}
-
-void
-PannerUI::pan_reset_all ()
+PannerUI::pan_reset ()
 {
-       _panner->reset_to_default ();
+       _panner->reset ();
 }
 
 void
@@ -617,26 +424,14 @@ PannerUI::effective_pan_display ()
 void
 PannerUI::update_pan_sensitive ()
 {
-       bool const sensitive = !(_panner->mono()) && !(_panner->automation_state() & Play);
+       bool const sensitive = !(_panner->is_mono()) && !(_panner->pannable()->automation_state() & Play);
 
-       switch (_panner->nouts()) {
-       case 0:
-       case 1:
-               break;
-       case 2:
-               for (vector<MonoPanner*>::iterator i = pan_bars.begin(); i != pan_bars.end(); ++i) {
-                       (*i)->set_sensitive (sensitive);
-               }
-               break;
-       default:
-               if (twod_panner) {
-                       twod_panner->set_sensitive (sensitive);
-               }
-               if (big_window) {
-                       big_window->set_sensitive (sensitive);
-               }
-               break;
-       }
+#ifdef PANNER_HACKS
+        pan_vbox.set_sensitive (sensitive);
+#endif
+        if (big_window) {
+                big_window->set_sensitive (sensitive);
+        }
 }
 
 gint
@@ -700,42 +495,31 @@ PannerUI::pan_automation_style_changed ()
 void
 PannerUI::pan_automation_state_changed ()
 {
-       ENSURE_GUI_THREAD (*this, &PannerUI::pan_automation_state_changed)
-
-       bool x;
+        boost::shared_ptr<Pannable> pannable (_panner->pannable());
 
        switch (_width) {
        case Wide:
-         pan_automation_state_button.set_label (astate_string(_panner->automation_state()));
+                pan_automation_state_button.set_label (astate_string(pannable->automation_state()));
                break;
        case Narrow:
-         pan_automation_state_button.set_label (short_astate_string(_panner->automation_state()));
+                pan_automation_state_button.set_label (short_astate_string(pannable->automation_state()));
                break;
        }
 
-       /* when creating a new session, we get to create busses (and
-          sometimes tracks) with no outputs by the time they get
-          here.
-       */
-
-       if (_panner->empty()) {
-               return;
-       }
-
-       x = (_panner->streampanner(0).pan_control()->alist()->automation_state() != Off);
-
+       bool x = (pannable->automation_state() != Off);
+        
        if (pan_automation_state_button.get_active() != x) {
-       ignore_toggle = true;
+                ignore_toggle = true;
                pan_automation_state_button.set_active (x);
                ignore_toggle = false;
        }
 
        update_pan_sensitive ();
-
+        
        /* start watching automation so that things move */
-
+        
        pan_watching.disconnect();
-
+        
        if (x) {
                pan_watching = ARDOUR_UI::RapidScreenUpdate.connect (sigc::mem_fun (*this, &PannerUI::effective_pan_display));
        }
index 50fc6b228d19c4dac3a790f6935237b893bd0114..e64419b4575b66157fe11d38fcaee42ac39e00ee 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <gtkmm/box.h>
 #include <gtkmm/adjustment.h>
-#include <gtkmm/viewport.h>
 #include <gtkmm/eventbox.h>
 #include <gtkmm/arrow.h>
 #include <gtkmm/togglebutton.h>
@@ -95,7 +94,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        bool ignore_toggle;
        bool in_pan_update;
        int _current_nouts;
-       int _current_npans;
+       int _current_nins;
 
        static const int pan_bar_height;
 
@@ -103,13 +102,6 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        Panner2dWindow* big_window;
 
        Gtk::VBox           pan_bar_packer;
-       Gtk::Adjustment     hAdjustment;
-       Gtk::Adjustment     vAdjustment;
-       Gtk::Viewport       panning_viewport;
-       Gtk::EventBox       panning_up;
-       Gtk::Arrow          panning_up_arrow;
-       Gtk::EventBox       panning_down;
-       Gtk::Arrow          panning_down_arrow;
        Gtk::VBox           pan_vbox;
         Gtk::VBox           poswidth_box;
        Width              _width;
@@ -122,25 +114,15 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
         void position_adjusted ();
         void show_position ();
 
-       Gtk::ToggleButton   panning_link_button;
-       Gtk::Button         panning_link_direction_button;
-       Gtk::HBox           panning_link_box;
-
-       bool panning_link_button_press (GdkEventButton*);
-       bool panning_link_button_release (GdkEventButton*);
-
        Gtk::Menu* pan_astate_menu;
        Gtk::Menu* pan_astyle_menu;
 
        Gtk::Button pan_automation_style_button;
        Gtk::ToggleButton pan_automation_state_button;
 
-       void panning_link_direction_clicked ();
-
        std::vector<MonoPanner*> pan_bars;
 
        void pan_value_changed (uint32_t which);
-       void update_pan_linkage ();
        void update_pan_state ();
        void build_astate_menu ();
        void build_astyle_menu ();
@@ -153,14 +135,12 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        gint start_pan_touch (GdkEventButton*);
        gint end_pan_touch (GdkEventButton*);
 
-       bool pan_button_event (GdkEventButton*, uint32_t which);
+       bool pan_button_event (GdkEventButton*);
 
        Gtk::Menu* pan_menu;
        Gtk::CheckMenuItem* bypass_menu_item;
-       void build_pan_menu (uint32_t which);
-       void pan_mute (uint32_t which);
-       void pan_reset (uint32_t);
-       void pan_reset_all ();
+       void build_pan_menu ();
+       void pan_reset ();
        void pan_bypass_toggle ();
 
        void pan_automation_state_changed();
index 05f49a4e4e72413b8ede768006203bd2b9d87e16..2eaeeb83f77a78c6c5d49405618e00910b508acc 100644 (file)
@@ -898,7 +898,7 @@ ProcessorBox::weird_plugin_dialog (Plugin& p, Route::ProcessorStreams streams)
 void
 ProcessorBox::choose_insert ()
 {
-       boost::shared_ptr<Processor> processor (new PortInsert (*_session, _route->mute_master()));
+       boost::shared_ptr<Processor> processor (new PortInsert (*_session, _route->pannable(), _route->mute_master()));
        _route->add_processor (processor, _placement);
 }
 
@@ -906,7 +906,7 @@ ProcessorBox::choose_insert ()
 void
 ProcessorBox::choose_send ()
 {
-       boost::shared_ptr<Send> send (new Send (*_session, _route->mute_master()));
+       boost::shared_ptr<Send> send (new Send (*_session, _route->pannable(), _route->mute_master()));
 
        /* make an educated guess at the initial number of outputs for the send */
        ChanCount outs = (_session->master_out())
@@ -1510,7 +1510,7 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr
 
                                XMLNode n (**niter);
                                Send::make_unique (n, *_session);
-                                Send* s = new Send (*_session, _route->mute_master());
+                                Send* s = new Send (*_session, _route->pannable(), _route->mute_master());
                                 if (s->set_state (n, Stateful::loading_state_version)) {
                                         delete s;
                                         return;
index 4e715c49eee9e9eb287739e54045f18000732630..8442b45e6704e086e82b91c29e0d7d85b46d5d00 100644 (file)
@@ -31,6 +31,7 @@
 #include "gtkmm2ext/gtk_ui.h"
 #include "gtkmm2ext/keyboard.h"
 
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
 
 #include "ardour_ui.h"
@@ -53,9 +54,12 @@ static const int top_step = 2;
 StereoPanner::ColorScheme StereoPanner::colors[3];
 bool StereoPanner::have_colors = false;
 
-StereoPanner::StereoPanner (boost::shared_ptr<PBD::Controllable> position, boost::shared_ptr<PBD::Controllable> width)
-        : position_control (position)
-        , width_control (width)
+using namespace ARDOUR;
+
+StereoPanner::StereoPanner (boost::shared_ptr<Panner> panner)
+        : _panner (panner)
+        , position_control (_panner->pannable()->pan_azimuth_control)
+        , width_control (_panner->pannable()->pan_width_control)
         , dragging (false)
         , dragging_position (false)
         , dragging_left (false)
@@ -66,8 +70,8 @@ StereoPanner::StereoPanner (boost::shared_ptr<PBD::Controllable> position, boost
         , detented (false)
         , drag_data_window (0)
         , drag_data_label (0)
-        , position_binder (position)
-        , width_binder (width)
+        , position_binder (position_control)
+        , width_binder (width_control)
 {
         if (!have_colors) {
                 set_colors ();
@@ -326,8 +330,9 @@ StereoPanner::on_button_press_event (GdkEventButton* ev)
                                         /* 2ndary-double click on right, collapse to hard right */
                                         width_control->set_value (0);
                                         position_control->set_value (1.0);
+                                } else {
+                                        position_control->set_value (max_pos);
                                 }
-                                position_control->set_value (max_pos);
                         } else {
                                 position_control->set_value (0.5);
                         }
index 86a53eccf70c54aa08d0b7e4a46324d4be834f91..294f86babb55cdc35dc8795dd6ba0947977475e1 100644 (file)
@@ -31,10 +31,14 @@ namespace PBD {
         class Controllable;
 }
 
+namespace ARDOUR { 
+        class Panner;
+}
+
 class StereoPanner : public Gtk::DrawingArea
 {
   public:
-       StereoPanner (boost::shared_ptr<PBD::Controllable> pos, boost::shared_ptr<PBD::Controllable> width);
+       StereoPanner (boost::shared_ptr<ARDOUR::Panner>);
        ~StereoPanner ();
 
        sigc::signal<void> StartPositionGesture;
@@ -54,6 +58,7 @@ class StereoPanner : public Gtk::DrawingArea
         bool on_leave_notify_event (GdkEventCrossing* ev);
 
   private:
+        boost::shared_ptr<ARDOUR::Panner> _panner;
         boost::shared_ptr<PBD::Controllable> position_control;
         boost::shared_ptr<PBD::Controllable> width_control;
         PBD::ScopedConnectionList connections;
index 43b45e10398e8f6d1c7d0600c858fd926148ee51..38700fcbc3110f51c7b3a03a8f915c5000c7f0b8 100644 (file)
@@ -156,7 +156,6 @@ gtk2_ardour_sources = [
         'note_player.cc',
        'option_editor.cc',
        'opts.cc',
-       'panner.cc',
        'panner2d.cc',
        'panner_ui.cc',
        'piano_roll_header.cc',
index 541e9d562d4abd6357998bfd06b1d35ad089a27e..c9e900e6a9d908f07a1a81c6381fe87d22e88c37 100644 (file)
@@ -44,7 +44,6 @@ class Automatable : virtual public Evoral::ControlSet
 public:
        Automatable(Session&);
         Automatable (const Automatable& other);
-       Automatable();
 
        virtual ~Automatable() {}
 
@@ -93,6 +92,8 @@ public:
 
        typedef Evoral::ControlSet::Controls Controls;
 
+        static const std::string xml_node_name;
+
        int set_automation_xml_state (const XMLNode&, Evoral::Parameter default_param);
        XMLNode& get_automation_xml_state();
        
index 43fb468b7af66cd1c96801e6eada9f94db5c0e2f..69421662f303772b8e9f7d1c5acf7334f643c780 100644 (file)
@@ -48,6 +48,7 @@ namespace PBD {
                 extern uint64_t Monitor;
                 extern uint64_t Solo;
                 extern uint64_t AudioPlayback;
+                extern uint64_t Panning;
        }
 }
 
index e1aa389df7dac724a2135c5a18495f502d47715a..99893976ba7e3b3a50d7f3bf01ce85cada7881fa 100644 (file)
@@ -30,7 +30,9 @@ namespace ARDOUR {
 class BufferSet;
 class IO;
 class MuteMaster;
+class PannerShell;
 class Panner;
+class Pannable;
 
 class Delivery : public IOProcessor
 {
@@ -52,11 +54,11 @@ public:
 
        /* Delivery to an existing output */
 
-       Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
+       Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
 
        /* Delivery to a new output owned by this object */
 
-       Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
+       Delivery (Session& s, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
        ~Delivery ();
 
        bool set_name (const std::string& name);
@@ -90,15 +92,14 @@ public:
        static int  disable_panners (void);
        static int  reset_panners (void);
 
-       boost::shared_ptr<Panner> panner() const { return _panner; }
+       boost::shared_ptr<PannerShell> panner_shell() const { return _panshell; }
+       boost::shared_ptr<Panner> panner() const;
 
        void reset_panner ();
        void defer_pan_reset ();
        void allow_pan_reset ();
 
        uint32_t pans_required() const { return _configured_input.n_audio(); }
-       void start_pan_touch (uint32_t which, double when);
-       void end_pan_touch (uint32_t which, bool mark, double when);
 
   protected:
        Role        _role;
@@ -108,7 +109,7 @@ public:
        bool        _no_outs_cuz_we_no_monitor;
        boost::shared_ptr<MuteMaster> _mute_master;
        bool         no_panner_reset;
-       boost::shared_ptr<Panner> _panner;
+       boost::shared_ptr<PannerShell> _panshell;
 
        static bool panners_legal;
        static PBD::Signal0<int>            PannersLegal;
index 9cce077ad09de27b45bc66da2315669ec9921ada..a5fd6dc7545f6a13f7b5d988158900c54481ea8c 100644 (file)
@@ -21,6 +21,7 @@ extern const char* const route_templates_dir_name;
 extern const char* const surfaces_dir_name;
 extern const char* const user_config_dir_name;
 extern const char* const stub_dir_name;
+extern const char* const panner_dir_name;
 
 };
 
index 903e4673df9a318529bddac890926e6777e220a0..53c6686887595cb95b7914bd4983c5caa1389392 100644 (file)
@@ -28,7 +28,7 @@ namespace ARDOUR {
 class InternalSend : public Send
 {
   public:
-       InternalSend (Session&, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role);
+       InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role);
        virtual ~InternalSend ();
 
        std::string display_name() const;
index e6ed2e41c16a20fe79a0ecb9ce25cd3ce126488e..dff64cec4bc875ff77f58bf23479dce97602be70 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <boost/shared_ptr.hpp>
 
+#include "pbd/stateful.h"
 #include "evoral/Parameter.hpp"
 
 #include "ardour/automatable.h"
@@ -33,8 +34,9 @@ namespace ARDOUR {
 
 class Session;
 class AutomationControl;
+class Panner;
 
-struct Pannable : public Automatable, public SessionHandleRef {
+struct Pannable : public PBD::Stateful, public Automatable, public SessionHandleRef {
         Pannable (Session& s);
 
         boost::shared_ptr<AutomationControl> pan_azimuth_control;
@@ -42,6 +44,9 @@ struct Pannable : public Automatable, public SessionHandleRef {
         boost::shared_ptr<AutomationControl> pan_width_control;
         boost::shared_ptr<AutomationControl> pan_frontback_control;
         boost::shared_ptr<AutomationControl> pan_lfe_control;
+        
+        boost::shared_ptr<Panner> panner() const { return _panner; }
+        void set_panner(boost::shared_ptr<Panner>);
 
         Session& session() { return _session; }
 
@@ -66,10 +71,21 @@ struct Pannable : public Automatable, public SessionHandleRef {
        bool writing() const { return _auto_state == Write; }
         bool touch_enabled() const { return _auto_state == Touch; }
 
+        XMLNode& get_state ();
+        XMLNode& state (bool full_state);
+        int set_state (const XMLNode&, int version);
+
+        bool has_state() const { return _has_state; }
+
   protected:
+        boost::shared_ptr<Panner> _panner;
         AutoState _auto_state;
         AutoStyle _auto_style;
         gint      _touching;
+        bool      _has_state;
+        uint32_t  _responding_to_control_auto_state_change;
+
+        void control_auto_state_changed (AutoState);
 };
 
 } // namespace 
index b07167862e038a4ca721f5b7b9d7d62566c693eb..f43be2dbbc4722e93021adba64eb9138137e524e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2004 Paul Davis
+    Copyright (C) 2004-2011 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <string>
 #include <iostream>
 
-#include "pbd/stateful.h"
-#include "pbd/controllable.h"
 #include "pbd/cartesian.h"
+#include "pbd/signals.h"
+#include "pbd/stateful.h"
 
 #include "ardour/types.h"
 #include "ardour/automation_control.h"
-#include "ardour/processor.h"
+#include "ardour/automatable.h"
 
 namespace ARDOUR {
 
 class Session;
-class Panner;
+class Pannable;
 class BufferSet;
 class AudioBuffer;
 class Speakers;
 
-class StreamPanner : public PBD::Stateful
+class Panner : public PBD::Stateful, public PBD::ScopedConnectionList
 {
   public:
-       StreamPanner (Panner& p, Evoral::Parameter param);
-       ~StreamPanner ();
-
-       void set_muted (bool yn);
-       bool muted() const { return _muted; }
-
-       const PBD::AngularVector& get_position() const { return _angles; }
-       const PBD::AngularVector& get_effective_position() const { return _effective_angles; }
-       void set_position (const PBD::AngularVector&, bool link_call = false);
-       void set_diffusion (double);
-
-       void distribute (AudioBuffer &, BufferSet &, gain_t, pframes_t);
-       void distribute_automated (AudioBuffer &, BufferSet &, framepos_t, framepos_t, pframes_t, pan_t **);
-
-       /* the basic StreamPanner API */
-
-       /**
-        *  Pan some input samples to a number of output buffers.
-        *
-        *  @param src Input buffer.
-        *  @param obufs Output buffers (one per panner output).
-        *  @param gain_coeff Gain coefficient to apply to output samples.
-        *  @param nframes Number of frames in the input.
-        */
-       virtual void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes) = 0;
-       virtual void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                                             framepos_t start, framepos_t end, pframes_t nframes,
-                                             pan_t** buffers) = 0;
-
-       boost::shared_ptr<AutomationControl> pan_control()  { return _control; }
-
-       PBD::Signal0<void> Changed;      /* for position or diffusion */
-       PBD::Signal0<void> StateChanged; /* for mute, mono */
-
-       int set_state (const XMLNode&, int version);
-       virtual XMLNode& state (bool full_state) = 0;
-
-       Panner & get_parent() { return parent; }
-
-       /* old school automation loading */
-       virtual int load (std::istream&, std::string path, uint32_t&) = 0;
-
-       struct PanControllable : public AutomationControl {
-               PanControllable (Session& s, std::string name, StreamPanner* p, Evoral::Parameter param)
-                       : AutomationControl (s, param,
-                                             boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
-                       , streampanner (p)
-               { assert (param.type() == PanAutomation); }
-                
-               AutomationList* alist() { return (AutomationList*)_list.get(); }
-               StreamPanner* streampanner;
-
-               void set_value (double);
-               double get_value (void) const;
-                double lower () const;
-       };
-
-  protected:
-       friend class Panner;
-       Panner& parent;
+       Panner (boost::shared_ptr<Pannable>);
+       ~Panner ();
 
-       void set_mono (bool);
-       
-       PBD::AngularVector _angles;
-       PBD::AngularVector _effective_angles;
-       double        _diffusion; 
+        virtual ChanCount in() const = 0;
+        virtual ChanCount out() const = 0;
 
-       bool _muted;
-       bool _mono;
+        virtual void configure_io (ARDOUR::ChanCount in, ARDOUR::ChanCount out) {}
+        
+        /* derived implementations of these methods must indicate
+           whether it is legal for a Controllable to use the
+           value of the argument (post-call) in a call to 
+           Controllable::set_value().
+           
+           they have a choice of:
+
+           * return true, leave argument unchanged
+           * return true, modify argument
+           * return false
+
+        */
+
+        virtual bool clamp_position (double&) { return true; }
+        virtual bool clamp_width (double&) { return true; }
+        virtual bool clamp_elevation (double&) { return true; }
+
+        virtual void set_position (double) { }
+        virtual void set_width (double) { }
+        virtual void set_elevation (double) { }
         
-       boost::shared_ptr<AutomationControl> _control;
+        virtual double position () const { return 0.0; }
+        virtual double width () const { return 0.0; }
+        virtual double elevation () const { return 0.0; }
 
-       XMLNode& get_state ();
+        virtual void reset() {}
 
-       /* Update internal parameters based on this.angles */
-       virtual void update () = 0;
-};
+       virtual bool bypassed() const { return _bypassed; }
+       virtual void set_bypassed (bool yn);
 
-class BaseStereoPanner : public StreamPanner
-{
-  public:
-       BaseStereoPanner (Panner&, Evoral::Parameter param);
-       ~BaseStereoPanner ();
+       virtual bool is_mono () const { return _mono; }
+       virtual void set_mono (bool);
+
+       void      set_automation_state (AutoState);
+       AutoState automation_state() const;
+       void      set_automation_style (AutoStyle);
+       AutoStyle automation_style() const;
 
-       /* this class just leaves the pan law itself to be defined
-          by the update(), do_distribute_automated()
-          methods. derived classes also need a factory method
-          and a type name. See EqualPowerStereoPanner as an example.
-       */
+       virtual std::set<Evoral::Parameter> what_can_be_automated() const;
+        virtual std::string describe_parameter (Evoral::Parameter);
 
-       void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+       bool touching() const;
 
        static double azimuth_to_lr_fract (double azi) { 
                /* 180.0 degrees=> left => 0.0 */
@@ -159,176 +118,70 @@ class BaseStereoPanner : public StreamPanner
                return rint (180.0 - (fract * 180.0));
        }
        
-       /* old school automation loading */
-
-       int load (std::istream&, std::string path, uint32_t&);
-
-  protected:
-       float left;
-       float right;
-       float desired_left;
-       float desired_right;
-       float left_interp;
-       float right_interp;
-};
-
-class EqualPowerStereoPanner : public BaseStereoPanner
-{
-  public:
-       EqualPowerStereoPanner (Panner&, Evoral::Parameter param);
-       ~EqualPowerStereoPanner ();
-
-       void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                                     framepos_t start, framepos_t end, pframes_t nframes,
-                                     pan_t** buffers);
-
-       void get_current_coefficients (pan_t*) const;
-       void get_desired_coefficients (pan_t*) const;
-
-       static StreamPanner* factory (Panner&, Evoral::Parameter param, Speakers&);
-       static std::string name;
-
-       XMLNode& state (bool full_state); 
-       XMLNode& get_state (void); 
-       int      set_state (const XMLNode&, int version);
-
-  private:
-       void update ();
-};
-
-/** Class to pan from some number of inputs to some number of outputs.
- *  This class has a number of StreamPanners, one for each input.
- */
-class Panner : public SessionObject, public Automatable
-{
-public:
-       struct Output {
-            PBD::AngularVector position;
-            pan_t current_pan;
-            pan_t desired_pan;
-            
-            Output (const PBD::AngularVector& a) 
-            : position (a), current_pan (0), desired_pan (0) {}
-
-       };
-
-       Panner (std::string name, Session&);
-       virtual ~Panner ();
-
-       void clear_panners ();
-       bool empty() const { return _streampanners.empty(); }
-
-       void set_automation_state (AutoState);
-       AutoState automation_state() const;
-       void set_automation_style (AutoStyle);
-       AutoStyle automation_style() const;
-       bool touching() const;
-
-       std::string describe_parameter (Evoral::Parameter param);
-
-       bool can_support_io_configuration (const ChanCount& /*in*/, ChanCount& /*out*/) const { return true; };
+       /**
+        *  Pan some input buffers to a number of output buffers.
+        *
+        *  @param ibufs Input buffers (one per panner input)
+        *  @param obufs Output buffers (one per panner output).
+        *  @param gain_coeff fixed, additional gain coefficient to apply to output samples.
+        *  @param nframes Number of frames in the input.
+         *
+         *  Derived panners can choose to implement these if they need to gain more control over the panning algorithm.
+         *  the default is to (1) check if _mono is true, and if so, just deliver .. (2) otherwise, call
+         *  distribute_one() or distribute_one_automated() on each input buffer to deliver it to each output 
+         *  buffer.
+         * 
+         *  If a panner does not need to override this default behaviour, it can just implement
+         *  distribute_one() and distribute_one_automated() (below).
+        */
+       virtual void distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+       virtual void distribute_automated (BufferSet& ibufs, BufferSet& obufs,
+                                           framepos_t start, framepos_t end, pframes_t nframes,
+                                           pan_t** buffers);
 
-       /// The fundamental Panner function
-       void run (BufferSet& src, BufferSet& dest, framepos_t start_frame, framepos_t end_frames, pframes_t nframes);
+       PBD::Signal0<void> Changed;      /* for positional info */
+       PBD::Signal0<void> StateChanged; /* for mute, mono */
 
-       bool bypassed() const { return _bypassed; }
-       void set_bypassed (bool yn);
-       bool mono () const { return _mono; }
-       void set_mono (bool);
+       int set_state (const XMLNode&, int version);
+       virtual XMLNode& state (bool full_state) = 0;
 
-       StreamPanner* add ();
-       void remove (uint32_t which);
-       void reset (uint32_t noutputs, uint32_t npans);
-       void reset_streampanner (uint32_t which_panner);
-       void reset_to_default ();
+        boost::shared_ptr<Pannable> pannable() const { return _pannable; }
 
-       XMLNode& get_state (void);
-       XMLNode& state (bool full);
-       int      set_state (const XMLNode&, int version);
+        //virtual std::string describe_parameter (Evoral::Parameter);
+        //virtual std::string value_as_string (Evoral::Parameter, double val);
 
        static bool equivalent (pan_t a, pan_t b) {
                return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner
        }
+
        static bool equivalent (const PBD::AngularVector& a, const PBD::AngularVector& b) {
                 /* XXX azimuth only, at present */
                return fabs (a.azi - b.azi) < 1.0;
        }
 
-       void move_output (uint32_t, float x, float y);
-       uint32_t nouts() const { return outputs.size(); }
-       Output& output (uint32_t n) { return outputs[n]; }
-
-       enum LinkDirection {
-               SameDirection,
-               OppositeDirection
-       };
-
-       LinkDirection link_direction() const { return _link_direction; }
-       void set_link_direction (LinkDirection);
-
-       bool linked() const { return _linked; }
-       void set_linked (bool yn);
-
-       StreamPanner &streampanner( uint32_t n ) const { assert( n < _streampanners.size() ); return *_streampanners[n]; }
-       uint32_t npanners() const { return _streampanners.size(); }
-
-       PBD::Signal0<void> Changed; /* panner and/or outputs count changed */
-       PBD::Signal0<void> LinkStateChanged;
-       PBD::Signal0<void> StateChanged; /* for bypass */
-
-       /* only StreamPanner should call these */
-
-       void set_position (const PBD::AngularVector&, StreamPanner& orig);
-
-       /* old school automation */
-
-       int load ();
-
-       boost::shared_ptr<AutomationControl> pan_control (int id, uint32_t chan=0) {
-               return automation_control (Evoral::Parameter (PanAutomation, chan, id));
-       }
-
-       boost::shared_ptr<const AutomationControl> pan_control (int id, uint32_t chan=0) const {
-               return automation_control (Evoral::Parameter (PanAutomation, chan, id));
-       }
-
-       boost::shared_ptr<AutomationControl> direction_control () {
-               return automation_control (Evoral::Parameter (PanAutomation, 0, 100));
-       }
-
-       boost::shared_ptr<AutomationControl> width_control () {
-               return automation_control (Evoral::Parameter (PanAutomation, 0, 200));
-       }
-
-        void set_stereo_position (double);
-        void set_stereo_width (double);
-        bool set_stereo_pan (double pos, double width);
-        
-       static std::string value_as_string (double);
-        
-  private:
-       /* disallow copy construction */
-       Panner (Panner const &);
-
-       void distribute_no_automation(BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff);
-       std::vector<StreamPanner*> _streampanners; ///< one StreamPanner per input
-       std::vector<Output> outputs;
-       uint32_t     current_outs;
-       bool             _linked;
-       bool             _bypassed;
-       bool             _mono;
-       LinkDirection    _link_direction;
-
-       static float current_automation_version_number;
+  protected:
+        boost::shared_ptr<Pannable> _pannable;
+        bool        _mono;
+        bool        _bypassed;
 
-       void setup_speakers (uint32_t nouts);
-        void setup_meta_controls ();
+       XMLNode& get_state ();
 
-       /* old school automation handling */
 
-       std::string automation_path;
+       virtual void distribute_one (AudioBuffer&, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which) = 0;
+       virtual void distribute_one_automated (AudioBuffer&, BufferSet& obufs,
+                                               framepos_t start, framepos_t end, pframes_t nframes,
+                                               pan_t** buffers, uint32_t which) = 0;
 };
 
-} // namespace ARDOUR
+} // namespace
+
+extern "C" {
+        struct PanPluginDescriptor {
+            std::string name;
+            int32_t in;
+            int32_t out;
+            ARDOUR::Panner* (*factory)(boost::shared_ptr<ARDOUR::Pannable>, ARDOUR::Speakers&);
+        };
+}
 
-#endif /*__ardour_panner_h__ */
+#endif /* __ardour_panner_h__ */
index d460aa55461d0f2b0024afb47609265a3759a679..959e46b4a58e52ec875b4c964c29d938234809d4 100644 (file)
@@ -37,13 +37,14 @@ class Session;
 class IO;
 class Delivery;
 class MuteMaster;
+class Pannable;
 
 /** Port inserts: send output to a Jack port, pick up input at a Jack port
  */
 class PortInsert : public IOProcessor
 {
   public:
-       PortInsert (Session&, boost::shared_ptr<MuteMaster> mm);
+       PortInsert (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm);
        ~PortInsert ();
 
        XMLNode& state(bool full);
index 90f5664eba429a56653cdf12879821fbf23667d5..f90ec77b7ee593a2cbfe6d733494ff57b2be35b1 100644 (file)
@@ -54,11 +54,13 @@ class Amp;
 class Delivery;
 class IOProcessor;
 class Panner;
+class PannerShell;
 class Processor;
 class RouteGroup;
 class Send;
 class InternalReturn;
 class MonitorProcessor;
+class Pannable;
 class CapturingProcessor;
 
 class Route : public SessionObject, public Automatable, public RouteGroupMember, public GraphNode
@@ -365,8 +367,10 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
           here.
        */
 
-       boost::shared_ptr<Panner> panner() const;
+       boost::shared_ptr<Panner> panner() const;  /* may return null */
+       boost::shared_ptr<PannerShell> panner_shell() const;
        boost::shared_ptr<AutomationControl> gain_control() const;
+        boost::shared_ptr<Pannable> pannable() const;
 
        void automation_snapshot (framepos_t now, bool force=false);
        void protect_automation ();
@@ -423,6 +427,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
        boost::shared_ptr<Delivery> _monitor_send;
        boost::shared_ptr<InternalReturn> _intreturn;
        boost::shared_ptr<MonitorProcessor> _monitor_control;
+        boost::shared_ptr<Pannable> _pannable;
 
        Flag           _flags;
        int            _pending_declick;
index bf5c5d73707cb7b229a0b69e04698204af38be48..40045d6b855d4d30eed5dc3010f6f55fbbf4ba49 100644 (file)
@@ -36,7 +36,7 @@ class Amp;
 class Send : public Delivery
 {
   public:
-       Send (Session&, boost::shared_ptr<MuteMaster>, Delivery::Role r = Delivery::Send);
+       Send (Session&, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster>, Delivery::Role r = Delivery::Send);
        virtual ~Send ();
 
        uint32_t bit_slot() const { return _bitslot; }
index bae8cb96db918a7181c5a23a1696e040ff84db43..f6e6e22848f6f83afaa99997be2304a6d2299fc2 100644 (file)
 #include <iostream>
 
 #include <pbd/signals.h>
+#include <pbd/stateful.h>
 
 #include "ardour/speaker.h"
 
+class XMLNode;
+
 namespace ARDOUR  {
 
-class Speakers {
+class Speakers : public PBD::Stateful {
 public:
        Speakers ();
        virtual ~Speakers ();
@@ -38,12 +41,17 @@ public:
        virtual void move_speaker (int id, const PBD::AngularVector& new_position);
        virtual void clear_speakers ();
 
+        void setup_default_speakers (uint32_t nspeakers);
+
        std::vector<Speaker>& speakers() { return _speakers; }
 
        void dump_speakers (std::ostream&);
 
-       PBD::Signal0<void> Changed;
+        XMLNode& get_state ();
+        int set_state (const XMLNode&, int version);
 
+       PBD::Signal0<void> Changed;
+        
 protected:
        std::vector<Speaker>  _speakers;
 
index 78e21d5a9db08ef8d8ae5564168204e20a2c1cf5..744c06f5522a6c96ad5d42c145c20e67f7e38dfe 100644 (file)
@@ -122,24 +122,27 @@ namespace ARDOUR {
                 InsertMergeExtend   // extend new (or old) to the range of old+new
         };
 
-       /** See parameter.h
-        * XXX: I don't think/hope these hex values matter anymore.
+       /** See evoral/Parameter.hpp
         */
        enum AutomationType {
-               NullAutomation = 0x0,
-               GainAutomation = 0x1,
-               PanAutomation = 0x2,
-               PluginAutomation = 0x4,
-               SoloAutomation = 0x8,
-               MuteAutomation = 0x10,
-               MidiCCAutomation = 0x20,
-               MidiPgmChangeAutomation = 0x21,
-               MidiPitchBenderAutomation = 0x22,
-               MidiChannelPressureAutomation = 0x23,
-               MidiSystemExclusiveAutomation = 0x24,
-               FadeInAutomation = 0x40,
-               FadeOutAutomation = 0x80,
-               EnvelopeAutomation = 0x100
+               NullAutomation,
+               GainAutomation,
+               PanAzimuthAutomation,
+               PanElevationAutomation,
+               PanWidthAutomation,
+               PanFrontBackAutomation,
+               PanLFEAutomation,
+               PluginAutomation,
+               SoloAutomation,
+               MuteAutomation,
+               MidiCCAutomation,
+               MidiPgmChangeAutomation,
+               MidiPitchBenderAutomation,
+               MidiChannelPressureAutomation,
+               MidiSystemExclusiveAutomation,
+               FadeInAutomation,
+               FadeOutAutomation,
+               EnvelopeAutomation
        };
 
        enum AutoState {
diff --git a/libs/ardour/ardour/vbap.h b/libs/ardour/ardour/vbap.h
deleted file mode 100644 (file)
index 419930c..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-    Copyright (C) 2010 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __libardour_vbap_h__
-#define __libardour_vbap_h__
-
-#include <string>
-#include <map>
-
-#include "ardour/panner.h"
-#include "ardour/vbap_speakers.h"
-
-namespace ARDOUR {
-
-class Speakers;
-
-class VBAPanner : public StreamPanner { 
-public:
-       VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s);
-       ~VBAPanner ();
-
-       static StreamPanner* factory (Panner& parent, Evoral::Parameter param, Speakers& s);
-       static std::string name;
-
-       void do_distribute (AudioBuffer&, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
-       void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                                     framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers);
-
-       void set_azimuth_elevation (double azimuth, double elevation);
-
-       XMLNode& state (bool full_state);
-       XMLNode& get_state ();
-       int set_state (const XMLNode&, int version);
-
-       /* there never was any old-school automation */
-
-       int load (std::istream&, std::string path, uint32_t&) { return 0; }
-
-private:
-       bool   _dirty;
-       double gains[3];
-       double desired_gains[3];
-       int    outputs[3];
-       int    desired_outputs[3];
-
-       VBAPSpeakers& _speakers;
-        
-       void compute_gains (double g[3], int ls[3], int azi, int ele);
-
-       void update ();
-};
-
-} /* namespace */
-
-#endif /* __libardour_vbap_h__ */
diff --git a/libs/ardour/ardour/vbap_speakers.h b/libs/ardour/ardour/vbap_speakers.h
deleted file mode 100644 (file)
index 8098964..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-    Copyright (C) 2010 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __libardour_vbap_speakers_h__
-#define __libardour_vbap_speakers_h__
-
-#include <string>
-#include <vector>
-
-#include <boost/utility.hpp>
-
-#include <pbd/signals.h>
-
-#include "ardour/panner.h"
-#include "ardour/speakers.h"
-
-namespace ARDOUR {
-
-class Speakers;
-
-class VBAPSpeakers : public boost::noncopyable {
-public:
-       typedef std::vector<double> dvector;
-
-       const dvector matrix (int tuple) const  { return _matrices[tuple]; }
-       int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; }
-
-       int           n_tuples () const  { return _matrices.size(); }
-       int           dimension() const { return _dimension; }
-
-       static VBAPSpeakers& instance (Speakers&);
-
-       ~VBAPSpeakers ();
-
-private:
-       static VBAPSpeakers* _instance;
-       static const double MIN_VOL_P_SIDE_LGTH = 0.01;
-       int   _dimension;  
-       std::vector<Speaker>& _speakers;
-       PBD::ScopedConnection speaker_connection;
-
-       VBAPSpeakers (Speakers&);
-
-       struct azimuth_sorter {
-               bool operator() (const Speaker& s1, const Speaker& s2) {
-                       return s1.angles().azi < s2.angles().azi;
-               }
-       };
-
-       struct twoDmatrix : public dvector {
-       twoDmatrix() : dvector (4, 0.0) {}
-       };
-
-       struct threeDmatrix : public dvector {
-       threeDmatrix() : dvector (9, 0.0) {}
-       };
-        
-       struct tmatrix : public dvector {
-       tmatrix() : dvector (3, 0.0) {}
-       };
-
-       std::vector<dvector>  _matrices;       /* holds matrices for a given speaker combinations */
-       std::vector<tmatrix>  _speaker_tuples; /* holds speakers IDs for a given combination */
-
-       /* A struct for all loudspeakers */
-       struct ls_triplet_chain {
-               int ls_nos[3];
-               float inv_mx[9];
-               struct ls_triplet_chain *next;
-       };
-
-       static float vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2);
-       static float vec_length(PBD::CartesianVector v1);
-       static float vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2);
-       static float vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&);
-       static void  cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res);
-
-       void update ();
-       int  any_ls_inside_triplet (int a, int b, int c);
-       void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain **ls_triplets);
-       int  lines_intersect (int i,int j,int k,int l);
-       void calculate_3x3_matrixes (struct ls_triplet_chain *ls_triplets);
-       void choose_speaker_triplets (struct ls_triplet_chain **ls_triplets);
-       void choose_speaker_pairs ();
-       void sort_2D_lss (int* sorted_lss);
-       int  calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat);
-        
-};
-
-} /* namespace */
-
-#endif /* __libardour_vbap_speakers_h__ */
index ef94d0d7407a63e3e2396220a81eac4135da54be..f6557fbc4f96a487523d7d2374eaf0ef598b19da 100644 (file)
@@ -99,7 +99,9 @@ AudioEngine::AudioEngine (string client_name, string session_uuid)
        Evoral::Parameter p(NullAutomation);
        p = EventTypeMap::instance().new_parameter(NullAutomation);
        p = EventTypeMap::instance().new_parameter(GainAutomation);
-       p = EventTypeMap::instance().new_parameter(PanAutomation);
+       p = EventTypeMap::instance().new_parameter(PanAzimuthAutomation);
+       p = EventTypeMap::instance().new_parameter(PanElevationAutomation);
+       p = EventTypeMap::instance().new_parameter(PanWidthAutomation);
        p = EventTypeMap::instance().new_parameter(PluginAutomation);
        p = EventTypeMap::instance().new_parameter(SoloAutomation);
        p = EventTypeMap::instance().new_parameter(MuteAutomation);
index d7605f26e1b3bc4d28da7dae431dc8f54c9207ef..209d26dc3075fef45bbd6b0907b7100623a0f584 100644 (file)
@@ -30,6 +30,7 @@
 #include "ardour/auditioner.h"
 #include "ardour/audioplaylist.h"
 #include "ardour/audio_port.h"
+#include "ardour/panner_shell.h"
 #include "ardour/panner.h"
 #include "ardour/data_type.h"
 #include "ardour/region_factory.h"
@@ -139,7 +140,8 @@ Auditioner::audition_current_playlist ()
 
        /* force a panner reset now that we have all channels */
 
-       _main_outs->panner()->reset (n_outputs().n_audio(), _diskstream->n_channels().n_audio());
+       _main_outs->panner_shell()->configure_io (ChanCount (DataType::AUDIO, _diskstream->n_channels().n_audio()), 
+                                                  ChanCount (DataType::AUDIO, n_outputs().n_audio()));
 
        g_atomic_int_set (&_auditioning, 1);
 }
index 03e5ad97c31e7d511222931fb86e24a3085652d5..0d2413571057d82e8c1e51f39c1645e01b2e545c 100644 (file)
@@ -35,7 +35,9 @@
 #include "ardour/amp.h"
 #include "ardour/event_type_map.h"
 #include "ardour/midi_track.h"
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
+#include "ardour/pan_controllable.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/session.h"
 
@@ -46,6 +48,7 @@ using namespace ARDOUR;
 using namespace PBD;
 
 framecnt_t Automatable::_automation_interval = 0;
+const string Automatable::xml_node_name = X_("Automation");
 
 Automatable::Automatable(Session& session)
        : _a_session(session)
@@ -182,9 +185,6 @@ Automatable::describe_parameter (Evoral::Parameter param)
 
        if (param == Evoral::Parameter(GainAutomation)) {
                return _("Fader");
-       } else if (param.type() == PanAutomation) {
-               /* ID's are zero-based, present them as 1-based */
-               return (string_compose(_("Pan %1"), param.id() + 1));
        } else if (param.type() == MidiCCAutomation) {
                return string_compose("%1: %2 [%3]",
                                param.id() + 1, midi_name(param.id()), int(param.channel()) + 1);
@@ -255,19 +255,21 @@ Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter le
                                continue;
                         }
 
-                       boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
 
+                       
                        if (!id_prop) {
                                warning << "AutomationList node without automation-id property, "
                                        << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
                        }
 
-                       boost::shared_ptr<Evoral::Control> existing = control(param);
+                       boost::shared_ptr<AutomationControl> existing = automation_control (param);
+
                        if (existing) {
-                               existing->set_list(al);
+                                existing->alist()->set_state (**niter, 3000);
                        } else {
-                           boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
-                               add_control(newcontrol);
+                                boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
+                               add_control (newcontrol);
+                                boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
                                newcontrol->set_list(al);
                        }
 
@@ -285,7 +287,7 @@ XMLNode&
 Automatable::get_automation_xml_state ()
 {
        Glib::Mutex::Lock lm (control_lock());
-       XMLNode* node = new XMLNode (X_("Automation"));
+       XMLNode* node = new XMLNode (Automatable::xml_node_name);
 
        if (controls().empty()) {
                return *node;
@@ -455,13 +457,12 @@ Automatable::control_factory(const Evoral::Parameter& param)
                } else {
                        warning << "GainAutomation for non-Amp" << endl;
                }
-       } else if (param.type() == PanAutomation) {
-               Panner* panner = dynamic_cast<Panner*>(this);
-               if (panner) {
-                        StreamPanner& sp (panner->streampanner (param.channel()));
-                       control = new StreamPanner::PanControllable (_a_session, X_("direction"), &sp, param);
+       } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
+               Pannable* pannable = dynamic_cast<Pannable*>(this);
+               if (pannable) {
+                       control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
                } else {
-                       warning << "PanAutomation for non-Panner" << endl;
+                       warning << "PanAutomation for non-Pannable" << endl;
                }
        }
 
index f4f4f95238efe3defda0df552156544a263ddd38..5f18bbcebba1fc242866209fbe8bf50854459700 100644 (file)
@@ -124,7 +124,9 @@ AutomationList::create_curve_if_necessary()
 {
        switch (_parameter.type()) {
        case GainAutomation:
-       case PanAutomation:
+       case PanAzimuthAutomation:
+       case PanElevationAutomation:
+       case PanWidthAutomation:
        case FadeInAutomation:
        case FadeOutAutomation:
        case EnvelopeAutomation:
@@ -184,7 +186,6 @@ AutomationList::set_automation_state (AutoState s)
                         Glib::Mutex::Lock lm (ControlList::_lock);
                         nascent.push_back (new NascentInfo (false));
                 }
-
                automation_state_changed (s); /* EMIT SIGNAL */
        }
 }
index 7ddb6ddcc8d673b86cdcd74f68803c10995e4dcd..2c413f407e3d546ead8d5431a7d2e4ea88322ba8 100644 (file)
@@ -45,4 +45,5 @@ uint64_t PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock");
 uint64_t PBD::DEBUG::Monitor = PBD::new_debug_bit ("monitor");
 uint64_t PBD::DEBUG::Solo = PBD::new_debug_bit ("solo");
 uint64_t PBD::DEBUG::AudioPlayback = PBD::new_debug_bit ("audioplayback");
+uint64_t PBD::DEBUG::Panning = PBD::new_debug_bit ("panning");
 
index 80135baf28c0da2d77a49b5469369d7e53904eb8..5c535b89182860bea73c89769c5753bda324606d 100644 (file)
@@ -33,6 +33,8 @@
 #include "ardour/meter.h"
 #include "ardour/mute_master.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
+#include "ardour/pannable.h"
 #include "ardour/port.h"
 #include "ardour/session.h"
 #include "ardour/audioengine.h"
@@ -49,7 +51,8 @@ bool                          Delivery::panners_legal = false;
 
 /* deliver to an existing IO object */
 
-Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
+Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pannable> pannable, 
+                    boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
        : IOProcessor(s, boost::shared_ptr<IO>(), (role_requires_output_ports (r) ? io : boost::shared_ptr<IO>()), name)
        , _role (r)
        , _output_buffers (new BufferSet())
@@ -59,7 +62,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Mute
        , _mute_master (mm)
        , no_panner_reset (false)
 {
-       _panner = boost::shared_ptr<Panner>(new Panner (_name, _session));
+       _panshell = boost::shared_ptr<PannerShell>(new PannerShell (_name, _session, pannable));
        _display_to_user = false;
 
        if (_output) {
@@ -71,7 +74,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Mute
 
 /* deliver to a new IO object */
 
-Delivery::Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
+Delivery::Delivery (Session& s, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
        : IOProcessor(s, false, (role_requires_output_ports (r) ? true : false), name)
        , _role (r)
        , _output_buffers (new BufferSet())
@@ -81,7 +84,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const string&
        , _mute_master (mm)
        , no_panner_reset (false)
 {
-       _panner = boost::shared_ptr<Panner>(new Panner (_name, _session));
+       _panshell = boost::shared_ptr<PannerShell>(new PannerShell (_name, _session, pannable));
        _display_to_user = false;
 
        if (_output) {
@@ -228,6 +231,8 @@ Delivery::configure_io (ChanCount in, ChanCount out)
 void
 Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool result_required)
 {
+        boost::shared_ptr<Panner> panner;
+
        assert (_output);
 
        PortSet& ports (_output->ports());
@@ -279,11 +284,13 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf
                Amp::apply_simple_gain (bufs, nframes, tgain);
        }
 
-       if (_panner && _panner->npanners() && !_panner->bypassed()) {
+        panner = _panshell->panner();
+
+       if (panner && !panner->bypassed()) {
 
                // Use the panner to distribute audio to output port buffers
 
-               _panner->run (bufs, output_buffers(), start_frame, end_frame, nframes);
+               _panshell->run (bufs, output_buffers(), start_frame, end_frame, nframes);
 
                if (result_required) {
                        bufs.read_from (output_buffers (), nframes);
@@ -322,7 +329,7 @@ Delivery::state (bool full_state)
        }
 
        node.add_property("role", enum_2_string(_role));
-       node.add_child_nocopy (_panner->state (full_state));
+       node.add_child_nocopy (_panshell->state (full_state));
 
        return node;
 }
@@ -346,7 +353,7 @@ Delivery::set_state (const XMLNode& node, int version)
        XMLNode* pan_node = node.child (X_("Panner"));
 
        if (pan_node) {
-               _panner->set_state (*pan_node, version);
+               _panshell->set_state (*pan_node, version);
        }
 
        reset_panner ();
@@ -368,7 +375,7 @@ Delivery::reset_panner ()
                                ntargets = _configured_output.n_audio();
                        }
 
-                       _panner->reset (ntargets, pans_required());
+                       _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, ntargets));
                }
        } else {
                panner_legal_c.disconnect ();
@@ -387,8 +394,10 @@ Delivery::panners_became_legal ()
                ntargets = _configured_output.n_audio();
        }
 
-       _panner->reset (ntargets, pans_required());
+       _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, ntargets));
+#ifdef PANNER_HACKS
        _panner->load (); // automation
+#endif
        panner_legal_c.disconnect ();
        return 0;
 }
@@ -421,25 +430,6 @@ Delivery::reset_panners ()
        return *PannersLegal ();
 }
 
-
-void
-Delivery::start_pan_touch (uint32_t which, double when)
-{
-       if (which < _panner->npanners()) {
-               _panner->pan_control(which)->start_touch(when);
-       }
-}
-
-void
-Delivery::end_pan_touch (uint32_t which, bool mark, double when)
-{
-       if (which < _panner->npanners()) {
-               _panner->pan_control(which)->stop_touch(mark, when);
-       }
-
-}
-
-
 void
 Delivery::flush_buffers (framecnt_t nframes, framepos_t time)
 {
@@ -456,8 +446,7 @@ void
 Delivery::transport_stopped (framepos_t now)
 {
         Processor::transport_stopped (now);
-
-       _panner->transport_stopped (now);
+        _panshell->pannable()->transport_stopped (now);
 
         if (_output) {
                 PortSet& ports (_output->ports());
@@ -533,7 +522,7 @@ Delivery::set_name (const std::string& name)
        bool ret = IOProcessor::set_name (name);
 
        if (ret) {
-               ret = _panner->set_name (name);
+               ret = _panshell->set_name (name);
        }
 
        return ret;
@@ -547,3 +536,9 @@ Delivery::output_changed (IOChange change, void* /*src*/)
                _output_buffers->attach_buffers (_output->ports ());
        }
 }
+
+boost::shared_ptr<Panner>
+Delivery::panner () const
+{
+        return _panshell->panner();
+}
index d6e2f9445536c56e61df6736d6b25e2eb3e532fb..18d43f7ec8d170ed355d2d7820fca816a0a5b827 100644 (file)
@@ -18,5 +18,6 @@ const char* const route_templates_dir_name = X_("route_templates");
 const char* const surfaces_dir_name = X_("surfaces");
 const char* const user_config_dir_name = X_("ardour3");
 const char* const stub_dir_name = X_(".stubs");
+const char* const panner_dir_name = X_("panners");
 
 }
index bf77d7d6adb085d356f3e281f7e5ceb20c5eb49f..df9d1a9b4df5c0dc84f747f1f93ab963c84c1b9f 100644 (file)
@@ -46,6 +46,8 @@
 #include "ardour/configuration.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/send.h"
+#include "ardour/pannable.h"
+#include "ardour/panner_shell.h"
 #include "ardour/playlist.h"
 #include "ardour/cycle_timer.h"
 #include "ardour/region.h"
@@ -458,18 +460,23 @@ Diskstream::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
        }
 
        /* move panner automation */
-       boost::shared_ptr<Panner> p = _track->main_outs()->panner ();
-       if (p) {
-               for (uint32_t i = 0; i < p->npanners (); ++i) {
-                       boost::shared_ptr<AutomationList> pan_alist = p->streampanner(i).pan_control()->alist();
-                       XMLNode & before = pan_alist->get_state ();
-                       bool const things_moved = pan_alist->move_ranges (movements);
-                       if (things_moved) {
-                               _session.add_command (new MementoCommand<AutomationList> (
-                                                             *pan_alist.get(), &before, &pan_alist->get_state ()));
-                       }
-               }
-       }
+       boost::shared_ptr<Pannable> pannable = _track->pannable();
+        Evoral::ControlSet::Controls& c (pannable->controls());
+        
+        for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) {
+                boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl>(ci->second);
+                if (!ac) {
+                        continue;
+                }
+                boost::shared_ptr<AutomationList> alist = ac->alist();
+                
+                XMLNode & before = alist->get_state ();
+                bool const things_moved = alist->move_ranges (movements);
+                if (things_moved) {
+                        _session.add_command (new MementoCommand<AutomationList> (
+                                                      *alist.get(), &before, &alist->get_state ()));
+                }
+        }
 
        /* move processor automation */
        _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
index cde3f418fdd433cb0443cbccc461945f3950a923..5987c874fbc9e4df88ff382305bcc5d3220a1b07 100644 (file)
@@ -83,7 +83,6 @@ setup_enum_writer ()
        TimecodeFormat _Session_TimecodeFormat;
        Session::PullupFormat _Session_PullupFormat;
        FadeShape _FadeShape;
-       Panner::LinkDirection _Panner_LinkDirection;
        IOChange _IOChange;
        AutomationType _AutomationType;
        AutoState _AutoState;
@@ -135,7 +134,9 @@ setup_enum_writer ()
        REGISTER (_OverlapType);
 
        REGISTER_ENUM (GainAutomation);
-       REGISTER_ENUM (PanAutomation);
+       REGISTER_ENUM (PanAzimuthAutomation);
+       REGISTER_ENUM (PanElevationAutomation);
+       REGISTER_ENUM (PanWidthAutomation);
        REGISTER_ENUM (PluginAutomation);
        REGISTER_ENUM (SoloAutomation);
        REGISTER_ENUM (MuteAutomation);
@@ -410,10 +411,6 @@ setup_enum_writer ()
        REGISTER_CLASS_ENUM (Location, IsRangeMarker);
        REGISTER_BITS (_Location_Flags);
 
-       REGISTER_CLASS_ENUM (Panner, SameDirection);
-       REGISTER_CLASS_ENUM (Panner, OppositeDirection);
-       REGISTER (_Panner_LinkDirection);
-
        REGISTER_CLASS_ENUM (Track, NoFreeze);
        REGISTER_CLASS_ENUM (Track, Frozen);
        REGISTER_CLASS_ENUM (Track, UnFrozen);
index d88e5afd265a8c1693140dfbbd838e187207e92e..c8c48c5b8674550d8ab2eb9fb7f2a3f66dfb8405 100644 (file)
@@ -140,15 +140,25 @@ EventTypeMap::new_parameter(uint32_t type, uint8_t channel, uint32_t id) const
        double min    = 0.0f;
        double max    = 1.0f;
        double normal = 0.0f;
+
        switch((AutomationType)type) {
        case NullAutomation:
        case GainAutomation:
                max = 2.0f;
                normal = 1.0f;
                break;
-       case PanAutomation:
-               normal = 0.5f;
-               break;
+       case PanAzimuthAutomation:
+               normal = 0.5f; // there really is no normal but this works for stereo, sort of
+                break;
+       case PanWidthAutomation:
+                min = -1.0;
+                max = 1.0;
+               normal = 0.0f;
+                break;
+        case PanElevationAutomation:
+        case PanFrontBackAutomation:
+        case PanLFEAutomation:
+                break;
        case PluginAutomation:
        case SoloAutomation:
        case MuteAutomation:
@@ -191,11 +201,16 @@ EventTypeMap::new_parameter(const string& str) const
                p_type = FadeOutAutomation;
        } else if (str == "envelope") {
                p_type = EnvelopeAutomation;
-       } else if (str == "pan") {
-               p_type = PanAutomation;
-       } else if (str.length() > 4 && str.substr(0, 4) == "pan-") {
-               p_type = PanAutomation;
-               p_id = atoi(str.c_str()+4);
+       } else if (str == "pan-azimuth") {
+               p_type = PanAzimuthAutomation;
+       } else if (str == "pan-width") {
+               p_type = PanWidthAutomation;
+       } else if (str == "pan-elevation") {
+               p_type = PanElevationAutomation;
+       } else if (str == "pan-frontback") {
+               p_type = PanFrontBackAutomation;
+       } else if (str == "pan-lfe") {
+               p_type = PanLFEAutomation;
        } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
                p_type = PluginAutomation;
                p_id = atoi(str.c_str()+10);
@@ -243,8 +258,16 @@ EventTypeMap::to_symbol(const Evoral::Parameter& param) const
 
        if (t == GainAutomation) {
                return "gain";
-       } else if (t == PanAutomation) {
-               return string_compose("pan-%1", param.id());
+       } else if (t == PanAzimuthAutomation) {
+                return "pan-azimuth";
+       } else if (t == PanElevationAutomation) {
+                return "pan-elevation";
+       } else if (t == PanWidthAutomation) {
+                return "pan-width";
+       } else if (t == PanFrontBackAutomation) {
+                return "pan-frontback";
+       } else if (t == PanLFEAutomation) {
+                return "pan-lfe";
        } else if (t == SoloAutomation) {
                return "solo";
        } else if (t == MuteAutomation) {
index d5579b47da27c6867e6dd8fc2286c9347a70244f..afcaa4dfcdc06c8767f1f1372e55cf6c67a82894 100644 (file)
@@ -70,6 +70,7 @@
 #include "ardour/midi_region.h"
 #include "ardour/mix.h"
 #include "ardour/audioplaylist.h"
+#include "ardour/panner_manager.h"
 #include "ardour/plugin_manager.h"
 #include "ardour/process_thread.h"
 #include "ardour/profile.h"
@@ -323,6 +324,8 @@ ARDOUR::init (bool use_vst, bool try_optimization)
         ProcessThread::init ();
         BufferManager::init (10); // XX should be num_processors_for_dsp
 
+        PannerManager::instance().discover_panners();
+
        return 0;
 }
 
index 43276516ddfa53375ea16699845e70607c86c4e3..4c3b385304e88c54f6c871c677d90f787937850c 100644 (file)
@@ -32,8 +32,8 @@ using namespace PBD;
 using namespace ARDOUR;
 using namespace std;
 
-InternalSend::InternalSend (Session& s, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role)
-       : Send (s, mm, role)
+InternalSend::InternalSend (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role)
+       : Send (s, p, mm, role)
        , target (0)
 {
         if (sendto) {
index e2f8ccc30ee89a122b37f7b82782fde79128f134..4243ab45dfe07e2856a42c43d127810729c7dd91 100644 (file)
@@ -1,31 +1,87 @@
+/*
+    Copyright (C) 2011 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "pbd/error.h"
+#include "pbd/convert.h"
+
 #include "ardour/automation_control.h"
 #include "ardour/automation_list.h"
 #include "ardour/pannable.h"
+#include "ardour/pan_controllable.h"
 #include "ardour/session.h"
 
+using namespace PBD;
 using namespace ARDOUR;
 
 Pannable::Pannable (Session& s)
         : Automatable (s)
         , SessionHandleRef (s)
-        , pan_azimuth_control (new AutomationControl (s, PanAzimuthAutomation, 
-                                                      boost::shared_ptr<AutomationList>(new AutomationList(PanAzimuthAutomation)), ""))
-        , pan_elevation_control (new AutomationControl (s, PanElevationAutomation, 
-                                                        boost::shared_ptr<AutomationList>(new AutomationList(PanElevationAutomation)), ""))
-        , pan_width_control (new AutomationControl (s, PanWidthAutomation, 
-                                                    boost::shared_ptr<AutomationList>(new AutomationList(PanWidthAutomation)), ""))
-        , pan_frontback_control (new AutomationControl (s, PanFrontBackAutomation, 
-                                                        boost::shared_ptr<AutomationList>(new AutomationList(PanFrontBackAutomation)), ""))
-        , pan_lfe_control (new AutomationControl (s, PanLFEAutomation, 
-                                                  boost::shared_ptr<AutomationList>(new AutomationList(PanLFEAutomation)), ""))
+        , pan_azimuth_control (new PanControllable (s, "", this, PanAzimuthAutomation))
+        , pan_elevation_control (new PanControllable (s, "", this, PanElevationAutomation))
+        , pan_width_control (new PanControllable (s, "", this, PanWidthAutomation))
+        , pan_frontback_control (new PanControllable (s, "", this, PanFrontBackAutomation))
+        , pan_lfe_control (new PanControllable (s, "", this, PanLFEAutomation))
         , _auto_state (Off)
         , _auto_style (Absolute)
+        , _has_state (false)
+        , _responding_to_control_auto_state_change (0)
 {
         add_control (pan_azimuth_control);
         add_control (pan_elevation_control);
         add_control (pan_width_control);
         add_control (pan_frontback_control);
         add_control (pan_lfe_control);
+
+        /* all controls change state together */
+
+        pan_azimuth_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+        pan_elevation_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+        pan_width_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+        pan_frontback_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+        pan_lfe_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+}
+
+void
+Pannable::control_auto_state_changed (AutoState new_state)
+{
+        if (_responding_to_control_auto_state_change) {
+                return;
+        }
+
+        _responding_to_control_auto_state_change++;
+
+        pan_azimuth_control->set_automation_state (new_state);
+        pan_width_control->set_automation_state (new_state);
+        pan_elevation_control->set_automation_state (new_state);
+        pan_frontback_control->set_automation_state (new_state);
+        pan_lfe_control->set_automation_state (new_state);
+
+        _responding_to_control_auto_state_change--;
+
+        _auto_state = new_state;
+        automation_state_changed (new_state);  /* EMIT SIGNAL */
+}
+
+void
+Pannable::set_panner (boost::shared_ptr<Panner> p)
+{
+        _panner = p;
 }
 
 void
@@ -95,3 +151,100 @@ Pannable::stop_touch (bool mark, double when)
         }
         g_atomic_int_set (&_touching, 0);
 }
+
+XMLNode&
+Pannable::get_state ()
+{
+        return state (true);
+}
+
+XMLNode&
+Pannable::state (bool full)
+{
+        XMLNode* node = new XMLNode (X_("Pannable"));
+        XMLNode* control_node;
+        char buf[32];
+
+        control_node = new XMLNode (X_("azimuth"));
+        snprintf (buf, sizeof(buf), "%.12g", pan_azimuth_control->get_value());
+        control_node->add_property (X_("value"), buf);
+        node->add_child_nocopy (*control_node);
+        
+        control_node = new XMLNode (X_("width"));
+        snprintf (buf, sizeof(buf), "%.12g", pan_width_control->get_value());
+        control_node->add_property (X_("value"), buf);
+        node->add_child_nocopy (*control_node);
+
+        control_node = new XMLNode (X_("elevation"));
+        snprintf (buf, sizeof(buf), "%.12g", pan_elevation_control->get_value());
+        control_node->add_property (X_("value"), buf);
+        node->add_child_nocopy (*control_node);
+
+        control_node = new XMLNode (X_("frontback"));
+        snprintf (buf, sizeof(buf), "%.12g", pan_frontback_control->get_value());
+        control_node->add_property (X_("value"), buf);
+        node->add_child_nocopy (*control_node);
+
+        control_node = new XMLNode (X_("lfe"));
+        snprintf (buf, sizeof(buf), "%.12g", pan_lfe_control->get_value());
+        control_node->add_property (X_("value"), buf);
+        node->add_child_nocopy (*control_node);
+        
+        node->add_child_nocopy (get_automation_xml_state ());
+
+     return *node;
+}
+
+int
+Pannable::set_state (const XMLNode& root, int /*version - not used*/)
+{
+        if (root.name() != X_("Pannable")) {
+                warning << string_compose (_("Pannable given XML data for %1 - ignored"), root.name()) << endmsg;
+                return -1;
+        }
+        
+        XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+       const XMLProperty *prop;
+
+        nlist = root.children();
+
+        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+                if ((*niter)->name() == X_("azimuth")) {
+                        prop = (*niter)->property (X_("value"));
+                        if (prop) {
+                                pan_azimuth_control->set_value (atof (prop->value()));
+                        }
+                } else if ((*niter)->name() == X_("width")) {
+                        prop = (*niter)->property (X_("value"));
+                        if (prop) {
+                                pan_width_control->set_value (atof (prop->value()));
+                        }
+                } else if ((*niter)->name() == X_("elevation")) {
+                        prop = (*niter)->property (X_("value"));
+                        if (prop) {
+                                pan_elevation_control->set_value (atof (prop->value()));
+                        }
+                } else if ((*niter)->name() == X_("azimuth")) {
+                        prop = (*niter)->property (X_("value"));
+                        if (prop) {
+                                pan_frontback_control->set_value (atof (prop->value()));
+                        }
+                } else if ((*niter)->name() == X_("lfe")) {
+                        prop = (*niter)->property (X_("value"));
+                        if (prop) {
+                                pan_lfe_control->set_value (atof (prop->value()));
+                        }
+                } else if ((*niter)->name() == Automatable::xml_node_name) {
+                        set_automation_xml_state (**niter, PanAzimuthAutomation);
+                }
+        }
+        
+        _has_state = true;
+
+        return 0;
+}
+
+
+
+        
index e4dc417c8328ad14ce193ad20dc2322a26c90d1c..bfee68a34261d1d09580df3a91ba044b05449a8c 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
-    Copyright (C) 2004 Paul Davis
+    Copyright (C) 2004-2011 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#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/session.h"
-#include "ardour/panner.h"
-#include "ardour/utils.h"
 #include "ardour/audio_buffer.h"
-
-#include "ardour/runtime_functions.h"
 #include "ardour/buffer_set.h"
-#include "ardour/audio_buffer.h"
-#include "ardour/vbap.h"
-
-#include "i18n.h"
-
-#include "pbd/mathfix.h"
+#include "ardour/panner.h"
+#include "ardour/pannable.h"
+#include "ardour/session.h"
+#include "ardour/utils.h"
 
 using namespace std;
 using namespace ARDOUR;
-using namespace PBD;
 
-float Panner::current_automation_version_number = 1.0;
-
-string EqualPowerStereoPanner::name = "Equal Power Stereo";
-
-/* this is a default mapper of control values to a pan position
-   others can be imagined.
-*/
-
-static double direct_control_to_stereo_pan (double fract)
+Panner::Panner (boost::shared_ptr<Pannable> p)
+       : _pannable (p)
+        , _mono (0)
 {
-       return BaseStereoPanner::lr_fract_to_azimuth (fract);
-}
-
-StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
-       : parent (p)
-        , _control (new PanControllable (parent.session(), _("direction"), this, param))
-{
-       assert (param.type() != NullAutomation);
-
-       _muted = false;
-       _mono = false;
-
-        p.add_control (_control);
 }
 
-StreamPanner::~StreamPanner ()
+Panner::~Panner ()
 {
 }
 
 void
-StreamPanner::set_mono (bool yn)
+Panner::set_bypassed (bool yn)
 {
-       if (yn != _mono) {
-               _mono = yn;
+       if (yn != _bypassed) {
+               _bypassed = yn;
                StateChanged ();
        }
 }
 
-double
-StreamPanner::PanControllable::lower () const
-{
-        switch (parameter().id()) {
-        case 200: /* width */
-                return -1.0;
-        default:
-                return 0.0;
-        }
-}
-
-void
-StreamPanner::PanControllable::set_value (double val)
-{
-        Panner& p (streampanner->get_parent());
-        switch (parameter().id()) {
-        case 100:
-                /* position */
-                val = max (min (val, 1.0), 0.0);
-                if (p.set_stereo_pan (val, p.width_control()->get_value())) {
-                        AutomationControl::set_value(val);
-                        p.session().set_dirty ();
-                }
-                break;
-
-        case 200:
-                /* width */
-                val = max (min (val, 1.0), -1.0);
-                if (p.set_stereo_pan (p.direction_control()->get_value(), val)) {
-                        AutomationControl::set_value(val);
-                        p.session().set_dirty ();
-                }
-                break;
-
-        default: /* positional */
-                val = max (min (val, 1.0), 0.0);
-                streampanner->set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
-                AutomationControl::set_value(val);
-                p.session().set_dirty ();
-                break;
-        }
-
-}
-
-double
-StreamPanner::PanControllable::get_value (void) const
-{
-       return AutomationControl::get_value();
-}
-
 void
-StreamPanner::set_muted (bool yn)
+Panner::set_mono (bool yn)
 {
-       if (yn != _muted) {
-               _muted = yn;
+       if (yn != _mono) {
+               _mono = yn;
                StateChanged ();
        }
 }
 
-void
-StreamPanner::set_position (const AngularVector& av, bool link_call)
-{
-       if (!link_call && parent.linked()) {
-               parent.set_position (av, *this);
-       }
-
-       if (_angles != av) {
-               _angles = av;
-               update ();
-               Changed ();
-               _control->Changed ();
-       }
-}
-
 int
-StreamPanner::set_state (const XMLNode& node, int version)
+Panner::set_state (const XMLNode& node, int version)
 {
        const XMLProperty* prop;
        XMLNodeConstIterator iter;
 
-       if ((prop = node.property (X_("muted")))) {
-               set_muted (string_is_affirmative (prop->value()));
-       }
-
        if ((prop = node.property (X_("mono")))) {
                set_mono (string_is_affirmative (prop->value()));
        }
 
-       for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {              
-                if ((*iter)->name() == Controllable::xml_node_name) {
-                       if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
-                               _control->set_state (**iter, version);
-                       } 
-                }
-        }
+       if ((prop = node.property (X_("bypassed"))) != 0) {
+               set_bypassed (string_is_affirmative (prop->value()));
+       }
 
        return 0;
 }
 
 XMLNode&
-StreamPanner::get_state ()
+Panner::get_state ()
 {
-        XMLNode* node = new XMLNode (X_("StreamPanner"));
-       node->add_property (X_("muted"), (muted() ? "yes" : "no"));
+        XMLNode* node = new XMLNode (X_("Panner"));
+
        node->add_property (X_("mono"), (_mono ? "yes" : "no"));
-       node->add_child_nocopy (_control->get_state ());
-        return *node;
-}
+       node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
 
-void
-StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
-{
-       if (_mono) {
-               /* we're in mono mode, so just pan the input to all outputs equally */
-               int const N = parent.nouts ();
-               for (int i = 0; i < N; ++i) {
-                       mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
-               }
-       } else {
-               /* normal mode, call the `real' distribute method */
-               do_distribute (src, obufs, gain_coeff, nframes);
-       }
+        return *node;
 }
 
 void
-StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                                   framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
-{
-       if (_mono) {
-               /* we're in mono mode, so just pan the input to all outputs equally */
-               int const N = parent.nouts ();
-               for (int i = 0; i < N; ++i) {
-                       mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
-               }
-       } else {
-               /* normal mode, call the `real' distribute method */
-               do_distribute_automated (src, obufs, start, end, nframes, buffers);
-       }
-       
-}
-
-
-/*---------------------------------------------------------------------- */
-
-BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
-       : StreamPanner (p, param)
-       , left (0.5)
-       , right (0.5)
-       , left_interp (left)
-       , right_interp (right)
+Panner::distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
 {
-}
-
-BaseStereoPanner::~BaseStereoPanner ()
-{
-}
-
-int
-BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
-{
-       char line[128];
-       LocaleGuard lg (X_("POSIX"));
-
-       _control->list()->clear ();
-
-       while (in.getline (line, sizeof (line), '\n')) {
-               framepos_t when;
-               double value;
-
-               ++linecnt;
-
-               if (strcmp (line, "end") == 0) {
-                       break;
-               }
-
-               if (sscanf (line, "%" PRIu64 " %lf", &when, &value) != 2) {
-                       warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
-                       continue;
-               }
-
-               _control->list()->fast_simple_add (when, value);
-       }
-
-       /* now that we are done loading */
-
-       ((AutomationList*)_control->list().get())->StateChanged ();
+        uint32_t which = 0;
 
-       return 0;
+       for (BufferSet::audio_iterator src = ibufs.audio_begin(); src != ibufs.audio_end(); ++src, ++which) {
+                if (_mono) {
+                        /* we're in mono mode, so just pan the input to all outputs equally (XXX should we scale?) */
+                        for (BufferSet::audio_iterator o = obufs.audio_begin(); o != obufs.audio_end(); ++o) {
+                                mix_buffers_with_gain ((*o).data(), (*src).data(), nframes, gain_coeff);
+                        }
+                } else {
+                        /* normal mode, call the `real' distribute method */
+                        distribute_one (*src, obufs, gain_coeff, nframes, which);
+                }
+        }
 }
 
 void
-BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
-{
-       assert(obufs.count().n_audio() == 2);
-
-       pan_t delta;
-       Sample* dst;
-       pan_t pan;
-
-       if (_muted) {
-               return;
-       }
-
-       Sample* const src = srcbuf.data();
-
-       /* LEFT */
-
-       dst = obufs.get_audio(0).data();
-
-       if (fabsf ((delta = (left - desired_left))) > 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++) {
-                       left_interp = left_interp + delta;
-                       left = left_interp + 0.9 * (left - left_interp);
-                       dst[n] += src[n] * left * gain_coeff;
-               }
-
-               /* then pan the rest of the buffer; no need for interpolation for this bit */
-
-               pan = left * gain_coeff;
-
-               mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
-
-       } else {
-
-               left = desired_left;
-               left_interp = left;
-
-               if ((pan = (left * gain_coeff)) != 1.0f) {
-
-                       if (pan != 0.0f) {
-
-                               /* pan is 1 but also not 0, so we must do it "properly" */
-
-                               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);
-
-                       /* mark that we wrote into the buffer */
-
-                       // obufs[0] = 0;
-               }
-       }
-
-       /* RIGHT */
-
-       dst = obufs.get_audio(1).data();
-
-       if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
-
-               /* we're 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++) {
-                       right_interp = right_interp + delta;
-                       right = right_interp + 0.9 * (right - right_interp);
-                       dst[n] += src[n] * right * gain_coeff;
-               }
-
-               /* then pan the rest of the buffer, no need for interpolation for this bit */
-
-               pan = right * gain_coeff;
-
-               mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
-
-               /* XXX it would be nice to mark the buffer as written to */
-
-       } else {
-
-               right = desired_right;
-               right_interp = right;
-
-               if ((pan = (right * gain_coeff)) != 1.0f) {
-
-                       if (pan != 0.0f) {
-
-                               /* pan is not 1 but also not 0, so we must do it "properly" */
-                               
-                               mix_buffers_with_gain(dst,src,nframes,pan);
-
-                               /* XXX it would be nice to mark the buffer as written to */
-                       }
-
-               } else {
-
-                       /* pan is 1 so we can just copy the input samples straight in */
-                       
-                       mix_buffers_no_gain(dst,src,nframes);
-
-                       /* XXX it would be nice to mark the buffer as written to */
-               }
-       }
-}
-
-/*---------------------------------------------------------------------- */
-
-EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
-       : BaseStereoPanner (p, param)
+Panner::distribute_automated (BufferSet& ibufs, BufferSet& obufs,
+                              framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
 {
-       update ();
-
-       left = desired_left;
-       right = desired_right;
-       left_interp = left;
-       right_interp = right;
-}
+        uint32_t which = 0;
 
-EqualPowerStereoPanner::~EqualPowerStereoPanner ()
-{
+       for (BufferSet::audio_iterator src = ibufs.audio_begin(); src != ibufs.audio_end(); ++src, ++which) {
+                if (_mono) {
+                        /* we're in mono mode, so just pan the input to all outputs equally (XXX should we scale?) */
+                        for (BufferSet::audio_iterator o = obufs.audio_begin(); o != obufs.audio_end(); ++o) {
+                                mix_buffers_with_gain ((*o).data(), (*src).data(), nframes, 1.0);
+                        }
+                } else {
+                        /* normal mode, call the `real' distribute method */
+                        distribute_one_automated (*src, obufs, start, end, nframes, buffers, which);
+                }
+        }
 }
 
 void
-EqualPowerStereoPanner::update ()
+Panner::set_automation_style (AutoStyle style)
 {
-       /* it would be very nice to split this out into a virtual function
-          that can be accessed from BaseStereoPanner and used in do_distribute_automated().
-
-          but the place where its used in do_distribute_automated() is a tight inner loop,
-          and making "nframes" virtual function calls to compute values is an absurd
-          overhead.
-       */
-
-       /* x == 0 => hard left = 180.0 degrees
-          x == 1 => hard right = 0.0 degrees
-       */
-
-       double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
-
-       float const panR = _x;
-       float const panL = 1 - panR;
-
-       float const pan_law_attenuation = -3.0f;
-       float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
-
-       desired_left = panL * (scale * panL + 1.0f - scale);
-       desired_right = panR * (scale * panR + 1.0f - scale);
-
-       _effective_angles = _angles;
-       //_control->set_value(x);
+        _pannable->set_automation_style (style);
 }
 
 void
-EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
-                                                framepos_t start, framepos_t end, pframes_t nframes,
-                                                pan_t** buffers)
-{
-       assert (obufs.count().n_audio() == 2);
-
-       Sample* dst;
-       pan_t* pbuf;
-       Sample* const src = srcbuf.data();
-
-       /* fetch positional data */
-
-       if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
-               /* fallback */
-               if (!_muted) {
-                       do_distribute (srcbuf, obufs, 1.0, nframes);
-               }
-               return;
-       }
-
-       /* store effective pan position. do this even if we are muted */
-
-       if (nframes > 0) {
-               _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
-       }
-
-       if (_muted) {
-               return;
-       }
-
-       /* apply pan law to convert positional data into pan coefficients for
-          each buffer (output)
-       */
-
-       const float pan_law_attenuation = -3.0f;
-       const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
-
-       for (pframes_t n = 0; n < nframes; ++n) {
-
-               float const panR = buffers[0][n];
-               float const panL = 1 - panR;
-
-               buffers[0][n] = panL * (scale * panL + 1.0f - scale);
-               buffers[1][n] = panR * (scale * panR + 1.0f - scale);
-       }
-
-       /* LEFT */
-
-       dst = obufs.get_audio(0).data();
-       pbuf = buffers[0];
-
-       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 */
-
-       /* RIGHT */
-
-       dst = obufs.get_audio(1).data();
-       pbuf = buffers[1];
-
-       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 */
-}
-
-StreamPanner*
-EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
-{
-       return new EqualPowerStereoPanner (parent, param);
-}
-
-XMLNode&
-EqualPowerStereoPanner::get_state (void)
-{
-       return state (true);
-}
-
-XMLNode&
-EqualPowerStereoPanner::state (bool /*full_state*/)
-{
-       XMLNode& root (StreamPanner::get_state ());
-       root.add_property (X_("type"), EqualPowerStereoPanner::name);
-       return root;
-}
-
-int
-EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
-{
-       LocaleGuard lg (X_("POSIX"));
-
-       StreamPanner::set_state (node, version);
-
-       for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
-
-                if ((*iter)->name() == X_("Automation")) {
-                        
-                       _control->alist()->set_state (*((*iter)->children().front()), version);
-                        
-                       if (_control->alist()->automation_state() != Off) {
-                               double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
-                               set_position (AngularVector (degrees, 0.0));
-                       }
-               }
-       }
-
-       return 0;
-}
-
-Panner::Panner (string name, Session& s)
-       : SessionObject (s, name)
-        , Automatable (s)
-{
-       //set_name_old_auto (name);
-       set_name (name);
-
-       _linked = false;
-       _link_direction = SameDirection;
-       _bypassed = false;
-       _mono = false;
-}
-
-Panner::~Panner ()
+Panner::set_automation_state (AutoState state)
 {
+        _pannable->set_automation_state (state);
 }
 
-void
-Panner::set_linked (bool yn)
+AutoState
+Panner::automation_state () const
 {
-       if (yn != _linked) {
-               _linked = yn;
-               _session.set_dirty ();
-               LinkStateChanged (); /* EMIT SIGNAL */
-       }
+        return _pannable->automation_state();
 }
 
-void
-Panner::set_link_direction (LinkDirection ld)
+AutoStyle
+Panner::automation_style () const
 {
-       if (ld != _link_direction) {
-               _link_direction = ld;
-               _session.set_dirty ();
-               LinkStateChanged (); /* EMIT SIGNAL */
-       }
+        return _pannable->automation_style ();
 }
 
-
-void
-Panner::set_bypassed (bool yn)
+bool
+Panner::touching () const
 {
-       if (yn != _bypassed) {
-               _bypassed = yn;
-               StateChanged ();
-       }
+        return _pannable->touching ();
 }
 
-
-void
-Panner::reset_to_default ()
+set<Evoral::Parameter> 
+Panner::what_can_be_automated() const
 {
-       vector<float> positions;
-
-       switch (outputs.size()) {
-       case 0:
-       case 1:
-               return;
-       }
-
-       if (outputs.size() == 2) {
-               AngularVector a;
-               switch (_streampanners.size()) {
-               case 1:
-                       a.azi = 90.0; /* "front" or "top", in degrees */
-                       _streampanners.front()->set_position (a);
-                       _streampanners.front()->pan_control()->list()->reset_default (0.5);
-                       return;
-                       break;
-               case 2:
-                       a.azi = 180.0; /* "left", in degrees */
-                       _streampanners.front()->set_position (a);
-                       _streampanners.front()->pan_control()->list()->reset_default (0.0);
-                       a.azi = 0.0; /* "right", in degrees */
-                       _streampanners.back()->set_position (a);
-                       _streampanners.back()->pan_control()->list()->reset_default (1.0);
-                       return;
-               default:
-                       break;
-               }
-       }
-
-       vector<Output>::iterator o;
-       vector<StreamPanner*>::iterator p;
-
-       for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
-               (*p)->set_position ((*o).position);
-       }
+        return _pannable->what_can_be_automated ();
 }
 
-void
-Panner::reset_streampanner (uint32_t which)
+string
+Panner::describe_parameter (Evoral::Parameter p)
 {
-       AngularVector a;
-
-       if (which >= _streampanners.size() || which >= outputs.size()) {
-               return;
-       }
-
-       switch (outputs.size()) {
-       case 0:
-       case 1:
-               return;
-
-       case 2:
-               switch (_streampanners.size()) {
-               case 1:
-                       /* stereo out, 1 stream, default = middle */
-                       a.azi = 90.0; /* "front" or "top", in degrees */
-                       _streampanners.front()->set_position (a);
-                       _streampanners.front()->pan_control()->list()->reset_default (0.5);
-                       break;
-               case 2:
-                       /* stereo out, 2 streams, default = hard left/right */
-                       if (which == 0) {
-                               a.azi = 180.0; /* "left", in degrees */
-                               _streampanners.front()->set_position (a);
-                               _streampanners.front()->pan_control()->list()->reset_default (0.0);
-                       } else {
-                               a.azi = 0.0; /* "right", in degrees */
-                               _streampanners.back()->set_position (a);
-                               _streampanners.back()->pan_control()->list()->reset_default (1.0);
-                       }
-                       break;
-               }
-               return;
-
-       default:
-               _streampanners[which]->set_position (outputs[which].position);
-       }
-}
-
-/**
- *    Reset the panner with a given number of outs and panners (and hence inputs)
- *
- *    \param nouts Number of outputs.
- *    \param npans Number of panners.
- */
-void
-Panner::reset (uint32_t nouts, uint32_t npans)
-{
-       uint32_t n;
-       bool changed = false;
-       bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
-
-       /* if new and old config don't need panning, or if
-          the config hasn't changed, we're done.
-       */
-
-       if (do_not_and_did_not_need_panning ||
-           ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
-               return;
-       }
-
-       n = _streampanners.size();
-       clear_panners ();
-
-       if (n != npans) {
-               changed = true;
-       }
-
-       n = outputs.size();
-       outputs.clear ();
-
-       if (n != nouts) {
-               changed = true;
-       }
-
-       if (nouts < 2) {
-               /* no need for panning with less than 2 outputs */
-               if (changed) {
-                       Changed (); /* EMIT SIGNAL */
-               }
-               return;
-       }
-
-       switch (nouts) {
-       case 0:
-               /* XXX: this can never happen */
-               break;
-
-       case 1:
-               /* XXX: this can never happen */
-               fatal << _("programming error:")
-                     << X_("Panner::reset() called with a single output")
-                     << endmsg;
-               /*NOTREACHED*/
-               break;
-
-       case 2: // line
-               outputs.push_back (Output (AngularVector (180.0, 0.0)));
-               outputs.push_back (Output (AngularVector (0.0, 0,0)));
-               for (n = 0; n < npans; ++n) {
-                       _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
-               }
-               break;
-
-       default:
-               setup_speakers (nouts);
-               for (n = 0; n < npans; ++n) {
-                       _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
-               }
-               break;
-       }
-
-       for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
-               (*x)->update ();
-       }
-
-        setup_meta_controls ();
-
-       /* must emit Changed here, otherwise the changes to the pan_control below raise further
-          signals which the GUI is not prepared for until it has seen the Changed here.
-       */
-       
-       if (changed) {
-               Changed (); /* EMIT SIGNAL */
-       }
-
-       /* force hard left/right panning in a common case: 2in/2out
-       */
-
-       if (npans == 2 && outputs.size() == 2) {
-
-               /* Do this only if we changed configuration, or our configuration
-                  appears to be the default set up (zero degrees)
-               */
-
-               AngularVector left;
-               AngularVector right;
-
-               left = _streampanners.front()->get_position ();
-               right = _streampanners.back()->get_position ();
-
-               if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
-
-                       _streampanners.front()->set_position (AngularVector (180.0, 0.0));
-                       _streampanners.front()->pan_control()->list()->reset_default (0.0);
-
-                       _streampanners.back()->set_position (AngularVector (0.0, 0.0));
-                       _streampanners.back()->pan_control()->list()->reset_default (1.0);
-               }
-
-       } else if (npans > 1 && outputs.size() > 2) {
-
-               /* 2d panning: spread signals equally around a circle */
-
-               double degree_step = 360.0 / nouts;
-               double deg;
-
-               /* even number of signals? make sure the top two are either side of "top".
-                  otherwise, just start at the "top" (90.0 degrees) and rotate around
-               */
-
-               if (npans % 2) {
-                       deg = 90.0 - degree_step;
-               } else {
-                       deg = 90.0;
-               }
-
-               for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
-                       (*x)->set_position (AngularVector (deg, 0.0));
-                       deg += degree_step;
-               }
-       }
-}
-
-void
-Panner::remove (uint32_t which)
-{
-       vector<StreamPanner*>::iterator i;
-       for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
-
-       if (i != _streampanners.end()) {
-               delete *i;
-               _streampanners.erase (i);
-       }
-}
-
-
-/** Remove all our StreamPanners */
-void
-Panner::clear_panners ()
-{
-       for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               delete *i;
-       }
-
-       _streampanners.clear ();
-}
-
-void
-Panner::set_automation_style (AutoStyle style)
-{
-       for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
-       }
-       _session.set_dirty ();
-}
-
-void
-Panner::set_automation_state (AutoState state)
-{
-       for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
-       }
-       _session.set_dirty ();
-}
-
-AutoState
-Panner::automation_state () const
-{
-       boost::shared_ptr<AutomationList> l;
-       if (!empty()) {
-               boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
-               if (control) {
-                       l = boost::dynamic_pointer_cast<AutomationList>(control->list());
-               }
-       }
-
-       return l ? l->automation_state() : Off;
-}
-
-AutoStyle
-Panner::automation_style () const
-{
-       boost::shared_ptr<AutomationList> l;
-       if (!empty()) {
-               boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
-               if (control) {
-                       l = boost::dynamic_pointer_cast<AutomationList>(control->list());
-               }
-       }
-
-       return l ? l->automation_style() : Absolute;
-}
-
-struct PanPlugins {
-    string name;
-    uint32_t nouts;
-    StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
-};
-
-PanPlugins pan_plugins[] = {
-       { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
-       { VBAPanner::name, 3, VBAPanner::factory },
-       { string (""), 0, 0 }
-};
-
-XMLNode&
-Panner::get_state (void)
-{
-       return state (true);
-}
-
-XMLNode&
-Panner::state (bool full)
-{
-       XMLNode* node = new XMLNode ("Panner");
-
-       char buf[32];
-
-       node->add_property (X_("linked"), (_linked ? "yes" : "no"));
-       node->add_property (X_("link_direction"), enum_2_string (_link_direction));
-       node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
-        snprintf (buf, sizeof (buf), "%zd", outputs.size());
-        node->add_property (X_("outputs"), buf);
-
-       for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               node->add_child_nocopy ((*i)->state (full));
-       }
-
-        node->add_child_nocopy (get_automation_xml_state ());
-
-       return *node;
-}
-
-int
-Panner::set_state (const XMLNode& node, int version)
-{
-       XMLNodeList nlist = node.children ();
-       XMLNodeConstIterator niter;
-       const XMLProperty *prop;
-       uint32_t i;
-       uint32_t num_panners = 0;
-       StreamPanner* sp;
-       LocaleGuard lg (X_("POSIX"));
-
-       clear_panners ();
-
-       ChanCount ins = ChanCount::ZERO;
-       ChanCount outs = ChanCount::ZERO;
-
-       // XXX: this might not be necessary anymore
-       outputs.clear ();
-
-       if ((prop = node.property (X_("linked"))) != 0) {
-               set_linked (string_is_affirmative (prop->value()));
-       }
-
-       if ((prop = node.property (X_("bypassed"))) != 0) {
-               set_bypassed (string_is_affirmative (prop->value()));
-       }
-
-       if ((prop = node.property (X_("link_direction"))) != 0) {
-               LinkDirection ld; /* here to provide type information */
-               set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
-       }
-
-        if ((prop = node.property (X_("outputs"))) != 0) {
-                uint32_t n = atoi (prop->value());
-
-                while (n--) {
-                        AngularVector a; // value is irrelevant
-                        outputs.push_back (Output (a));
-                }
-
-        } else {
-                
-                /* old school */
-
-                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-                        if ((*niter)->name() == X_("Output")) {
-                                
-                                AngularVector a;
-                                
-                                if ((prop = (*niter)->property (X_("azimuth")))) {
-                                        sscanf (prop->value().c_str(), "%lg", &a.azi);
-                                } else if ((prop = (*niter)->property (X_("x")))) {
-                                        /* old school cartesian */
-                                        a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
-                                }
-                                
-                                if ((prop = (*niter)->property (X_("elevation")))) {
-                                        sscanf (prop->value().c_str(), "%lg", &a.ele);
-                                }
-                                
-                                outputs.push_back (Output (a));
-                        }
-                }
-        }
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-               if ((*niter)->name() == X_("StreamPanner")) {
-
-                       if ((prop = (*niter)->property (X_("type")))) {
-
-                               for (i = 0; pan_plugins[i].factory; ++i) {
-                                       if (prop->value() == pan_plugins[i].name) {
-
-
-                                               /* note that we assume that all the stream panners
-                                                  are of the same type. pretty good
-                                                  assumption, but it's still an assumption.
-                                               */
-
-                                               sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
-                                               num_panners++;
-
-                                               if (sp->set_state (**niter, version) == 0) {
-                                                       _streampanners.push_back (sp);
-                                                }
-
-                                               break;
-                                       }
-                               }
-
-                               if (!pan_plugins[i].factory) {
-                                       error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
-                                                         prop->value())
-                                             << endmsg;
-                               }
-
-                       } else {
-                               error << _("panner plugin node has no type information!")
-                                     << endmsg;
-                               return -1;
-                       }
-
-               }
-       }
-
-        setup_meta_controls ();
-
-       reset (outputs.size (), num_panners);
-
-       /* don't try to do old-school automation loading if it wasn't marked as existing */
-
-       if ((prop = node.property (X_("automation")))) {
-
-               /* automation path is relative */
-
-               automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
-       }
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               if ((*niter)->name() == X_("Automation")) {
-                       set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
-               }
-       }
-
-       return 0;
-}
-
-bool
-Panner::touching () const
-{
-       for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-void
-Panner::set_position (const AngularVector& a, StreamPanner& orig)
-{
-       AngularVector delta;
-       AngularVector new_position;
-
-       delta = orig.get_position() - a;
-
-       if (_link_direction == SameDirection) {
-
-               for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-                       if (*i == &orig) {
-                               (*i)->set_position (a, true);
-                       } else {
-                               new_position = (*i)->get_position() + delta;
-                               (*i)->set_position (new_position, true);
-                       }
-               }
-
-       } else {
-
-               for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-                       if (*i == &orig) {
-                               (*i)->set_position (a, true);
-                       } else {
-                               new_position = (*i)->get_position() - delta;
-                               (*i)->set_position (new_position, true);
-                       }
-               }
-       }
-}
-
-void
-Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff)
-{
-       if (outbufs.count().n_audio() == 0) {
-               // Don't want to lose audio...
-               assert(inbufs.count().n_audio() == 0);
-               return;
-       }
-
-       // We shouldn't be called in the first place...
-       assert(!bypassed());
-       assert(!empty());
-
-
-       if (outbufs.count().n_audio() == 1) {
-
-               AudioBuffer& dst = outbufs.get_audio(0);
-
-               if (gain_coeff == 0.0f) {
-
-                       /* only one output, and gain was zero, so make it silent */
-
-                       dst.silence (nframes);
-
-               } else if (gain_coeff == 1.0f){
-
-                       /* mix all buffers into the output */
-
-                       // copy the first
-                       dst.read_from(inbufs.get_audio(0), nframes);
-
-                       // accumulate starting with the second
-                       if (inbufs.count().n_audio() > 0) {
-                               BufferSet::audio_iterator i = inbufs.audio_begin();
-                               for (++i; i != inbufs.audio_end(); ++i) {
-                                       dst.merge_from(*i, nframes);
-                               }
-                       }
-
-               } else {
-
-                       /* mix all buffers into the output, scaling them all by the gain */
-
-                       // copy the first
-                       dst.read_from(inbufs.get_audio(0), nframes);
-
-                       // accumulate (with gain) starting with the second
-                       if (inbufs.count().n_audio() > 0) {
-                               BufferSet::audio_iterator i = inbufs.audio_begin();
-                               for (++i; i != inbufs.audio_end(); ++i) {
-                                       dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
-                               }
-                       }
-
-               }
-
-               return;
-       }
-
-       /* the terrible silence ... */
-       for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
-               i->silence(nframes);
-       }
-
-       BufferSet::audio_iterator i = inbufs.audio_begin();
-
-       for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
-               (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
-       }
-}
-
-void
-Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
-{
-       if (outbufs.count().n_audio() == 0) {
-               // Failing to deliver audio we were asked to deliver is a bug
-               assert(inbufs.count().n_audio() == 0);
-               return;
-       }
-
-       // We shouldn't be called in the first place...
-       assert(!bypassed());
-       assert(!empty());
-
-       // If we shouldn't play automation defer to distribute_no_automation
-       if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
-
-               // Speed quietning
-               gain_t gain_coeff = 1.0;
-
-               if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
-                       gain_coeff = speed_quietning;
-               }
-
-               distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
-               return;
-       }
-
-       // Otherwise.. let the automation flow, baby
-
-       if (outbufs.count().n_audio() == 1) {
-
-               AudioBuffer& dst = outbufs.get_audio(0);
-
-               // FIXME: apply gain automation?
-
-               // copy the first
-               dst.read_from(inbufs.get_audio(0), nframes);
-
-               // accumulate starting with the second
-               BufferSet::audio_iterator i = inbufs.audio_begin();
-               for (++i; i != inbufs.audio_end(); ++i) {
-                       dst.merge_from(*i, nframes);
-               }
-
-               return;
-       }
-
-       // More than 1 output, we should have 1 panner for each input
-       //assert(_streampanners.size() == inbufs.count().n_audio());
-
-       /* the terrible silence ... */
-       for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
-               i->silence(nframes);
-       }
-
-       BufferSet::audio_iterator i = inbufs.audio_begin();
-       for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
-               (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
-       }
-}
-
-/* old school automation handling */
-
-/*
-void
-Panner::set_name (string str)
-{
-       automation_path = Glib::build_filename(_session.automation_dir(),
-               _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
-}
-*/
-
-int
-Panner::load ()
-{
-       char line[128];
-       uint32_t linecnt = 0;
-       float version;
-       vector<StreamPanner*>::iterator sp;
-       LocaleGuard lg (X_("POSIX"));
-
-       if (automation_path.length() == 0) {
-               return 0;
-       }
-
-       if (access (automation_path.c_str(), F_OK)) {
-               return 0;
-       }
-
-       ifstream in (automation_path.c_str());
-
-       if (!in) {
-               error << string_compose (_("cannot open pan automation file %1 (%2)"),
-                                 automation_path, strerror (errno))
-                     << endmsg;
-               return -1;
-       }
-
-       sp = _streampanners.begin();
-
-       while (in.getline (line, sizeof(line), '\n')) {
-
-               if (++linecnt == 1) {
-                       if (memcmp (line, X_("version"), 7) == 0) {
-                               if (sscanf (line, "version %f", &version) != 1) {
-                                       error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
-                                       return -1;
-                               }
-                       } else {
-                               error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
-                                                automation_path, line) << endmsg;
-                               return -1;
-                       }
-
-                       continue;
-               }
-
-               if (strlen (line) == 0 || line[0] == '#') {
-                       continue;
-               }
-
-               if (strcmp (line, "begin") == 0) {
-
-                       if (sp == _streampanners.end()) {
-                               error << string_compose (_("too many panner states found in pan automation file %1"),
-                                                 automation_path)
-                                     << endmsg;
-                               return -1;
-                       }
-
-                       if ((*sp)->load (in, automation_path, linecnt)) {
-                               return -1;
-                       }
-
-                       ++sp;
-               }
-       }
-
-       return 0;
-}
-
-void
-Panner::set_mono (bool yn)
-{
-       if (yn != _mono) {
-               _mono = yn;
-               StateChanged ();
-       }
-
-       for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
-               (*i)->set_mono (yn);
-       }
-}
-
-string
-Panner::value_as_string (double v)
-{
-       if (Panner::equivalent (v, 0.5)) {
-               return _("C");
-       } else if (equivalent (v, 0)) {
-               return _("L");
-       } else if (equivalent (v, 1)) {
-               return _("R");
-       } else if (v < 0.5) {
-               stringstream s;
-               s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
-               return s.str();
-       } else {
-               stringstream s;
-               s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
-               return s.str ();
-       }
-
-       return "";
-}
-
-void
-Panner::setup_speakers (uint32_t nouts)
-{
-       switch (nouts) {
-       case 3:
-               /* top, bottom kind-of-left & bottom kind-of-right */
-               outputs.push_back (AngularVector (90.0, 0.0));
-               outputs.push_back (AngularVector (215.0, 0,0));
-               outputs.push_back (AngularVector (335.0, 0,0));
-               break;
-       case 4:
-               /* clockwise from top left */
-               outputs.push_back (AngularVector (135.0, 0.0));
-               outputs.push_back (AngularVector (45.0, 0.0));
-               outputs.push_back (AngularVector (335.0, 0.0));
-               outputs.push_back (AngularVector (215.0, 0.0));
-               break;
-
-       default: 
-       {
-               double degree_step = 360.0 / nouts;
-               double deg;
-               uint32_t n;
-
-               /* even number of speakers? make sure the top two are either side of "top".
-                  otherwise, just start at the "top" (90.0 degrees) and rotate around
-               */
-
-               if (nouts % 2) {
-                       deg = 90.0 - degree_step;
-               } else {
-                       deg = 90.0;
-               }
-               for (n = 0; n < nouts; ++n, deg += degree_step) {
-                       outputs.push_back (Output (AngularVector (deg, 0.0)));
-               }
-       }
-       }
-
-       Speakers& speakers (_session.get_speakers());
-                        
-       speakers.clear_speakers ();
-
-       for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
-               speakers.add_speaker ((*o).position);
-       }
-}
-
-void
-Panner::set_stereo_width (double val)
-{
-        boost::shared_ptr<AutomationControl> dc = direction_control();
-        if (dc) {
-                dc->set_value (val);
-        }
-}
-
-void
-Panner::set_stereo_position (double val)
-{
-        boost::shared_ptr<AutomationControl> wc = width_control();
-        if (wc) {
-                wc->set_value (val);
-        }
-}
-
-bool
-Panner::set_stereo_pan (double direction_as_lr_fract, double width)
-{
-        AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0);
-        /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing
-           whether its "reversed" (i.e. left signal pans right and right signal pans left).
-           full width = 180 degrees
-        */
-        double spread = 2.0 * fabs(width/2.0) * 180.0; 
-        double l_pos = p.azi + (spread/2.0);  /* more left is "increasing degrees" */
-        double r_pos = p.azi - (spread/2.0);  /* more right is "decreasing degrees" */
-        bool move_left = true;
-        bool move_right = true;
-        int l_index = 0;
-        int r_index = 1;
-
-        assert (_streampanners.size() > 1);
-
-        if (width < 0.0) {
-                swap (l_index, r_index);
-        }
-
-        l_pos = max (min (l_pos, 180.0), 0.0);
-        r_pos = max (min (r_pos, 180.0), 0.0);
-
-        /* if the new left position is less than or equal to 180 (hard left) and the left panner
-           is already there, we're not moving the left signal. 
-        */
-
-        if (l_pos >= 180.0 &&_streampanners[l_index]->get_position().azi == 180.0) {
-                move_left = false;
-        }
-
-        /* if the new right position is less than or equal to zero (hard right) and the right panner
-           is already there, we're not moving the right signal. 
-        */
-        
-        if (r_pos <= 0.0 && _streampanners[r_index]->get_position().azi == 0.0) {
-                move_right = false;
-        }
-
-        if (move_left && move_right) {
-                _streampanners[l_index]->set_position (AngularVector (l_pos, 0.0));
-                _streampanners[r_index]->set_position (AngularVector (r_pos, 0.0));
-        }
-
-        return move_left && move_right;
-}
-
-void
-Panner::setup_meta_controls ()
-{
-        if (_streampanners.size() != 2 || outputs.size() != 2) {
-                return;
-        }
-
-        /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
-           
-           The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
-           but they keep us out of the ordinary range of pan-related parameters.
-        */
-        
-        Evoral::Parameter lr_param (PanAutomation, 0, 100);
-        Evoral::Parameter width_param (PanAutomation, 0, 200);
-        boost::shared_ptr<AutomationControl> dc = automation_control (lr_param);
-        boost::shared_ptr<AutomationControl> wc = automation_control (width_param);
-
-        if (dc) {
-               /* reset parent StreamPanner as the current one may have been deleted */
-               boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (dc);
-               assert (p);
-               p->streampanner = _streampanners.front ();
-       } else {
-                dc.reset (new StreamPanner::PanControllable (_session, _("lr"), _streampanners.front(), lr_param));
-                add_control (dc);
-        }
-        
-        if (wc) {
-               /* reset parent as above */
-               boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (wc);
-               assert (p);
-               p->streampanner = _streampanners.front ();
-       } else {
-                wc.reset (new StreamPanner::PanControllable (_session, _("width"), _streampanners.front(), width_param));
-                add_control (wc);
-        }
-
-        dc->set_value (0.5);
-        wc->set_value (1.0); // full width
-}
-
-string
-Panner::describe_parameter (Evoral::Parameter param)
-{
-        if (param.type() == PanAutomation) {
-                switch (param.id()) {
-                case 100:
-                        return "Pan:position";
-                case 200:
-                        return "Pan:width";
-                default:
-                        if (_streampanners.size() == 2) {
-                                switch (param.id()) {
-                                case 0:
-                                        return "Pan L";
-                                default:
-                                        return "Pan R";
-                                }
-                        } else {
-                                stringstream ss;
-                                ss << "Pan " << param.id() + 1;
-                                return ss.str ();
-                        }
-                }
-        }
-
-        return Automatable::describe_parameter (param);
+        return _pannable->describe_parameter (p);
 }
index c6cbbe36abdcd0b28f6667cc08efdc6e311faaa5..7963682bcffab0d91d71f4c55eae0e67fb631f9b 100644 (file)
@@ -102,6 +102,12 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
         }
 
         _panner.reset (pi->descriptor.factory (_pannable, _session.get_speakers()));
+        _panner->configure_io (in, out);
+
+        /* PANNER_HACKS: only the real owner should be able to claim the pannable
+         */
+
+        _pannable->set_panner (_panner);
 
         Changed (); /* EMIT SIGNAL */
 }
index f0940670d44164a9d423ee22c9a0cf265b4f59ea..df758e223088467cec3c832a3b9c576c81a82576 100644 (file)
@@ -41,9 +41,9 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-PortInsert::PortInsert (Session& s, boost::shared_ptr<MuteMaster> mm)
+PortInsert::PortInsert (Session& s, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster> mm)
        : IOProcessor (s, true, true, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), "")
-       , _out (new Delivery (s, _output, mm, _name, Delivery::Insert))
+       , _out (new Delivery (s, _output, pannable, mm, _name, Delivery::Insert))
 {
         _mtdm = 0;
         _latency_detect = false;
index c1004a18f95d8bde0109022caa5f69865b396b09..b732fd09e8b6c08cff0b59fbfd6b0fff85d62782 100644 (file)
@@ -46,7 +46,9 @@
 #include "ardour/meter.h"
 #include "ardour/mix.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"
 #include "ardour/port_insert.h"
@@ -106,7 +108,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 int
 Route::init ()
 {
-       /* add standard controls */
+        /* add standard controls */
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
@@ -114,6 +116,10 @@ Route::init ()
        add_control (_solo_control);
        add_control (_mute_control);
 
+        /* panning */
+        
+        _pannable.reset (new Pannable (_session));
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
@@ -136,7 +142,7 @@ Route::init ()
 
        add_processor (_meter, PostFader);
 
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+       _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
 
         add_processor (_main_outs, PostFader);
 
@@ -163,7 +169,9 @@ Route::init ()
 
                 /* no panning on the monitor main outs */
 
+#ifdef PANNER_HACKS
                 _main_outs->panner()->set_bypassed (true);
+#endif
        }
 
         if (is_master() || is_monitor() || is_hidden()) {
@@ -975,14 +983,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 
                                } else {
 
-                                       processor.reset (new PortInsert (_session, _mute_master));
+                                       processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                }
 
                        }
 
                } else if (node.name() == "Send") {
 
-                       processor.reset (new Send (_session, _mute_master));
+                       processor.reset (new Send (_session, _pannable, _mute_master));
 
                } else {
 
@@ -1852,6 +1860,8 @@ Route::state(bool full_state)
                cmt->add_content (_comment);
        }
 
+        node->add_child_nocopy (_pannable->state (full_state));
+
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                node->add_child_nocopy((*i)->state (full_state));
        }
@@ -1928,6 +1938,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
                }
+
+
+                if (child->name() == X_("Pannable")) {
+                        _pannable->set_state (*child, version);
+                }
        }
 
        set_processor_state (processor_state);
@@ -2233,7 +2248,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                io_child = *io_niter;
                                
                                if (io_child->name() == X_("Panner")) {
-                                       _main_outs->panner()->set_state(*io_child, version);
+                                       _main_outs->panner_shell()->set_state(*io_child, version);
                                } else if (io_child->name() == X_("Automation")) {
                                        /* IO's automation is for the fader */
                                        _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
@@ -2370,7 +2385,7 @@ Route::set_processor_state (const XMLNode& node)
 
                                 if (prop->value() == "intsend") {
                                         
-                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+                                        processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
                                         
                                 } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                            prop->value() == "lv2" ||
@@ -2381,11 +2396,11 @@ Route::set_processor_state (const XMLNode& node)
                                         
                                 } else if (prop->value() == "port") {
                                         
-                                        processor.reset (new PortInsert (_session, _mute_master));
+                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                         
                                 } else if (prop->value() == "send") {
                                         
-                                        processor.reset (new Send (_session, _mute_master));
+                                        processor.reset (new Send (_session, _pannable, _mute_master));
                                         
                                 } else {
                                         error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -2540,11 +2555,11 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
                                 /* master never sends to control outs */
                                 return 0;
                         } else {
-                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                                listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
                         }
 
                 } else {
-                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                        listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
                 }
 
        } catch (failed_constructor& err) {
@@ -3141,8 +3156,7 @@ Route::set_latency_delay (framecnt_t longest_session_latency)
 void
 Route::automation_snapshot (framepos_t now, bool force)
 {
-       panner()->automation_snapshot (now, force);
-       
+        _pannable->automation_snapshot (now, force);
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
@@ -3271,11 +3285,10 @@ Route::shift (framepos_t pos, framecnt_t frames)
 
        /* pan automation */
         {
-                boost::shared_ptr<AutomationControl> pc;
-                uint32_t npans = _main_outs->panner()->npanners();
-
-                for (uint32_t p = 0; p < npans; ++p) {
-                        pc = _main_outs->panner()->pan_control (0, p);
+                ControlSet::Controls& c (_pannable->controls());
+                
+                for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+                        boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
                         if (pc) {
                                 boost::shared_ptr<AutomationList> al = pc->alist();
                                 XMLNode& before = al->get_state ();
@@ -3461,10 +3474,23 @@ Route::meter ()
        }
 }
 
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+       return _pannable;
+}
+
 boost::shared_ptr<Panner>
 Route::panner() const
 {
-       return _main_outs->panner();
+        /* may be null ! */
+       return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+       return _main_outs->panner_shell();
 }
 
 boost::shared_ptr<AutomationControl>
index 33b4277d93b4129d9df927d99c8ffb5444d2f710..57c85004549ce1408f880d5b7908e7f1a23963f3 100644 (file)
@@ -38,8 +38,8 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
 
-Send::Send (Session& s, boost::shared_ptr<MuteMaster> mm, Role r)
-       : Delivery (s, mm, string_compose (_("send %1"), (_bitslot = s.next_send_id()) + 1), r)
+Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, Role r)
+       : Delivery (s, p, mm, string_compose (_("send %1"), (_bitslot = s.next_send_id()) + 1), r)
        , _metering (false)
 {
        _amp.reset (new Amp (_session));
index 9ac7dcc908c38046c33ee0c6f320ca20adc91ee3..e569188a387333531ecc42cabe8138945412f861 100644 (file)
@@ -99,7 +99,7 @@
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
 #include "ardour/graph.h"
-#include "ardour/vbap_speakers.h"
+#include "ardour/speakers.h"
 #include "ardour/operations.h"
 
 #include "midi++/port.h"
@@ -4207,10 +4207,6 @@ Session::ensure_search_path_includes (const string& path, DataType type)
 Speakers&
 Session::get_speakers() 
 {
-        if (!_speakers) {
-                _speakers = new Speakers;
-        }
-
         return *_speakers;
 }
 
index 4f11291637bd2a8cb2da2ee7bb684fb6ebdef1d3..54c36a3459b21cf89d143208041f5e1abad7cdfb 100644 (file)
@@ -92,7 +92,7 @@
 #include "ardour/midi_source.h"
 #include "ardour/midi_track.h"
 #include "ardour/named_selection.h"
-#include "ardour/panner.h"
+#include "ardour/pannable.h"
 #include "ardour/processor.h"
 #include "ardour/port.h"
 #include "ardour/region_factory.h"
@@ -220,7 +220,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        midi_control_ui = 0;
         _step_editors = 0;
         no_questions_about_missing_files = false;
-        _speakers = 0;
+        _speakers = new Speakers;
 
        AudioDiskstream::allocate_working_buffers();
 
@@ -1174,6 +1174,8 @@ Session::state(bool full_state)
                }
        }
 
+        node->add_child_nocopy (_speakers->get_state());
+
        node->add_child_nocopy (_tempo_map->get_state());
 
        node->add_child_nocopy (get_control_protocol_state());
@@ -1294,6 +1296,13 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
+        if ((child = find_named_node (node, X_("Speakers"))) == 0) {
+               warning << _("Session: XML state has no speakers section - assuming simple stereo") << endmsg;
+                _speakers->setup_default_speakers (2);
+        } else {
+                _speakers->set_state (*child, version);
+        }
+
        Location* location;
 
        if ((location = _locations->auto_loop_location()) != 0) {
@@ -2990,19 +2999,19 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
 
        case ControllableDescriptor::PanDirection:
         {
-                boost::shared_ptr<Panner> p = r->panner();
-                if (p) {
-                        c = p->direction_control();
-                }
+                c = r->pannable()->pan_azimuth_control;
                break;
         }
 
        case ControllableDescriptor::PanWidth:
         {
-                boost::shared_ptr<Panner> p = r->panner();
-                if (p) {
-                        c = p->width_control();
-                }
+                c = r->pannable()->pan_width_control;
+               break;
+        }
+
+       case ControllableDescriptor::PanElevation:
+        {
+                c = r->pannable()->pan_elevation_control;
                break;
         }
 
index 7d0461ff525227c47645cc83fcf56e1e7f22f585..4229e77c658d1e6ca945f6d2dad9c6380ea30a42 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include "pbd/error.h"
+#include "pbd/convert.h"
+#include "pbd/locale_guard.h"
+
 #include "ardour/speaker.h"
 #include "ardour/speakers.h"
 
+#include "i18n.h"
+
 using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
@@ -103,3 +109,119 @@ Speakers::move_speaker (int id, const AngularVector& new_position)
                }
        }
 }
+
+void
+Speakers::setup_default_speakers (uint32_t n)
+{
+        /* default assignment of speaker position for n speakers */
+
+        assert (n>0);
+
+       switch (n) {
+        case 1:
+                add_speaker (AngularVector (0.0, 0.0));
+                break;
+
+        case 2:
+                add_speaker (AngularVector (0.0, 0.0));
+                add_speaker (AngularVector (180.0, 0,0));
+                break;
+
+       case 3:
+               /* top, bottom kind-of-left & bottom kind-of-right */
+                add_speaker (AngularVector (90.0, 0.0));
+                add_speaker (AngularVector (215.0, 0,0));
+                add_speaker (AngularVector (335.0, 0,0));
+               break;
+       case 4:
+               /* clockwise from top left */
+                add_speaker (AngularVector (135.0, 0.0));
+                add_speaker (AngularVector (45.0, 0.0));
+                add_speaker (AngularVector (335.0, 0.0));
+                add_speaker (AngularVector (215.0, 0.0));
+               break;
+
+       default: 
+       {
+               double degree_step = 360.0 / n;
+               double deg;
+               uint32_t i;
+
+               /* even number of speakers? make sure the top two are either side of "top".
+                  otherwise, just start at the "top" (90.0 degrees) and rotate around
+               */
+
+               if (n % 2) {
+                       deg = 90.0 - degree_step;
+               } else {
+                       deg = 90.0;
+               }
+               for (i = 0; i < n; ++i, deg += degree_step) {
+                       add_speaker (AngularVector (deg, 0.0));
+               }
+       }
+        }
+}
+        
+XMLNode&
+Speakers::get_state ()
+{
+        XMLNode* node = new XMLNode (X_("Speakers"));
+        char buf[32];
+        LocaleGuard lg (X_("POSIX"));
+
+        for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
+                XMLNode* speaker = new XMLNode (X_("Speaker"));
+
+                snprintf (buf, sizeof (buf), "%.12g", (*i).angles().azi);
+                speaker->add_property (X_("azimuth"), buf);
+                snprintf (buf, sizeof (buf), "%.12g", (*i).angles().ele);
+                speaker->add_property (X_("elevation"), buf);
+                snprintf (buf, sizeof (buf), "%.12g", (*i).angles().length);
+                speaker->add_property (X_("distance"), buf);
+
+                node->add_child_nocopy (*speaker);
+        }
+        
+        return *node;
+}
+
+int
+Speakers::set_state (const XMLNode& node, int /*version*/)
+{
+        XMLNodeConstIterator i;
+        const XMLProperty* prop;
+        double a, e, d;
+        LocaleGuard lg (X_("POSIX"));
+        int n = 0;
+
+        _speakers.clear ();
+
+        for (i = node.children().begin(); i != node.children().end(); ++i, ++n) {
+                if ((*i)->name() == X_("Speaker")) {
+                        if ((prop = (*i)->property (X_("azimuth"))) == 0) {
+                                warning << _("Speaker information is missing azimuth - speaker ignored") << endmsg;
+                                continue;
+                        }
+                        a = atof (prop->value());
+
+                        if ((prop = (*i)->property (X_("elevation"))) == 0) {
+                                warning << _("Speaker information is missing elevation - speaker ignored") << endmsg;
+                                continue;
+                        }
+                        e = atof (prop->value());
+                                            
+                        if ((prop = (*i)->property (X_("distance"))) == 0) {
+                                warning << _("Speaker information is missing distance - speaker ignored") << endmsg;
+                                continue;
+                        }
+                        d = atof (prop->value());
+
+                        add_speaker (AngularVector (a, e, d));
+                }
+        }
+
+        update ();
+        
+        return 0;
+}
index 2436b6d36549a969c4d52d0a29616764fe982808..f30edee572ad0f63bc6e16ee0483d059bfef1c0a 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include <iostream>
+#include <algorithm>
 
 #include "ardour/audioengine.h"
 #include "ardour/buffer_set.h"
@@ -68,6 +69,10 @@ ThreadBuffers::ensure_buffers (ChanCount howmany)
 void
 ThreadBuffers::allocate_pan_automation_buffers (framecnt_t nframes, uint32_t howmany, bool force)
 {
+        /* we always need at least 2 pan buffers */
+
+        howmany = max (2U, howmany);
+
        if (!force && howmany <= npan_buffers) {
                return;
        }
diff --git a/libs/ardour/vbap.cc b/libs/ardour/vbap.cc
deleted file mode 100644 (file)
index 96fc133..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-
-/* 
-   This software is being provided to you, the licensee, by Ville Pulkki,
-   under the following license. By obtaining, using and/or copying this
-   software, you agree that you have read, understood, and will comply
-   with these terms and conditions: Permission to use, copy, modify and
-   distribute, including the right to grant others rights to distribute
-   at any tier, this software and its documentation for any purpose and
-   without fee or royalty is hereby granted, provided that you agree to
-   comply with the following copyright notice and statements, including
-   the disclaimer, and that the same appear on ALL copies of the software
-   and documentation, including modifications that you make for internal
-   use or for distribution:
-   
-   Copyright 1998 by Ville Pulkki, Helsinki University of Technology.  All
-   rights reserved.  
-   
-   The software may be used, distributed, and included to commercial
-   products without any charges. When included to a commercial product,
-   the method "Vector Base Amplitude Panning" and its developer Ville
-   Pulkki must be referred to in documentation.
-   
-   This software is provided "as is", and Ville Pulkki or Helsinki
-   University of Technology make no representations or warranties,
-   expressed or implied. By way of example, but not limitation, Helsinki
-   University of Technology or Ville Pulkki make no representations or
-   warranties of merchantability or fitness for any particular purpose or
-   that the use of the licensed software or documentation will not
-   infringe any third party patents, copyrights, trademarks or other
-   rights. The name of Ville Pulkki or Helsinki University of Technology
-   may not be used in advertising or publicity pertaining to distribution
-   of the software.
-*/
-
-#include <cmath>
-#include <cstdlib>
-#include <cstdio>
-#include <cstring>
-
-#include <iostream>
-#include <string>
-
-#include "pbd/cartesian.h"
-
-#include "ardour/speakers.h"
-#include "ardour/vbap.h"
-#include "ardour/vbap_speakers.h"
-#include "ardour/audio_buffer.h"
-#include "ardour/buffer_set.h"
-
-using namespace PBD;
-using namespace ARDOUR;
-using namespace std;
-
-string VBAPanner::name = X_("VBAP");
-
-VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s)
-       : StreamPanner (parent, param)
-       , _dirty (true)
-       , _speakers (VBAPSpeakers::instance (s))
-{
-}
-
-VBAPanner::~VBAPanner ()
-{
-}
-
-void
-VBAPanner::update ()
-{
-       /* force 2D for now */
-       _angles.ele = 0.0;
-       _dirty = true;
-
-       Changed ();
-}
-
-void 
-VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) 
-{
-       /* calculates gain factors using loudspeaker setup and given direction */
-       double cartdir[3];
-       double power;
-       int i,j,k;
-       double small_g;
-       double big_sm_g, gtmp[3];
-
-       azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]);  
-       big_sm_g = -100000.0;
-
-       gains[0] = gains[1] = gains[2] = 0;
-       speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
-
-       for (i = 0; i < _speakers.n_tuples(); i++) {
-
-               small_g = 10000000.0;
-
-               for (j = 0; j < _speakers.dimension(); j++) {
-
-                       gtmp[j] = 0.0;
-
-                       for (k = 0; k < _speakers.dimension(); k++) {
-                               gtmp[j] += cartdir[k] * _speakers.matrix(i)[j*_speakers.dimension()+k]; 
-                       }
-
-                       if (gtmp[j] < small_g) {
-                               small_g = gtmp[j];
-                       }
-               }
-
-               if (small_g > big_sm_g) {
-
-                       big_sm_g = small_g;
-
-                       gains[0] = gtmp[0]; 
-                       gains[1] = gtmp[1]; 
-
-                       speaker_ids[0] = _speakers.speaker_for_tuple (i, 0);
-                       speaker_ids[1] = _speakers.speaker_for_tuple (i, 1);
-                        
-                       if (_speakers.dimension() == 3) {
-                               gains[2] = gtmp[2];
-                               speaker_ids[2] = _speakers.speaker_for_tuple (i, 2);
-                       } else {
-                               gains[2] = 0.0;
-                               speaker_ids[2] = -1;
-                       }
-               }
-       }
-        
-       power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
-
-       if (power > 0) {
-               gains[0] /= power; 
-               gains[1] /= power;
-               gains[2] /= power;
-       }
-
-       _dirty = false;
-}
-
-void
-VBAPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
-{
-       if (_muted) {
-               return;
-       }
-
-       Sample* const src = srcbuf.data();
-       Sample* dst;
-       pan_t pan;
-       uint32_t n_audio = obufs.count().n_audio();
-       bool was_dirty;
-
-       if ((was_dirty = _dirty)) {
-               compute_gains (desired_gains, desired_outputs, _angles.azi, _angles.ele);
-               cerr << " @ " << _angles.azi << " /= " << _angles.ele
-                    << " Outputs: "
-                    << desired_outputs[0] + 1 << ' '
-                    << desired_outputs[1] + 1 << ' '
-                    << " Gains "
-                    << desired_gains[0] << ' '
-                    << desired_gains[1] << ' '
-                    << endl;
-       }
-
-       bool todo[n_audio];
-        
-       for (uint32_t o = 0; o < n_audio; ++o) {
-               todo[o] = true;
-       }
-
-        
-       /* VBAP may distribute the signal across up to 3 speakers depending on
-          the configuration of the speakers.
-       */
-
-       for (int o = 0; o < 3; ++o) {
-               if (desired_outputs[o] != -1) {
-
-                       pframes_t n = 0;
-
-                       /* XXX TODO: interpolate across changes in gain and/or outputs
-                        */
-
-                       dst = obufs.get_audio(desired_outputs[o]).data();
-
-                       pan = gain_coefficient * desired_gains[o];
-                       mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
-
-                       todo[o] = false;
-               }
-       }
-        
-       for (uint32_t o = 0; o < n_audio; ++o) {
-               if (todo[o]) {
-                       /* VBAP decided not to deliver any audio to this output, so we write silence */
-                       dst = obufs.get_audio(o).data();
-                       memset (dst, 0, sizeof (Sample) * nframes);
-               }
-       }
-        
-       if (was_dirty) {
-               memcpy (gains, desired_gains, sizeof (gains));
-               memcpy (outputs, desired_outputs, sizeof (outputs));
-       }
-}
-
-void 
-VBAPanner::do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                                    framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
-{
-}
-
-XMLNode&
-VBAPanner::get_state ()
-{
-       return state (true);
-}
-
-XMLNode&
-VBAPanner::state (bool full_state)
-{
-        XMLNode& node (StreamPanner::get_state());
-       node.add_property (X_("type"), VBAPanner::name);
-       return node;
-}
-
-int
-VBAPanner::set_state (const XMLNode& node, int /*version*/)
-{
-       return 0;
-}
-
-StreamPanner*
-VBAPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& s)
-{
-       return new VBAPanner (parent, param, s);
-}
-
diff --git a/libs/ardour/vbap_speakers.cc b/libs/ardour/vbap_speakers.cc
deleted file mode 100644 (file)
index 9090ed6..0000000
+++ /dev/null
@@ -1,658 +0,0 @@
-/* 
-   This software is being provided to you, the licensee, by Ville Pulkki,
-   under the following license. By obtaining, using and/or copying this
-   software, you agree that you have read, understood, and will comply
-   with these terms and conditions: Permission to use, copy, modify and
-   distribute, including the right to grant others rights to distribute
-   at any tier, this software and its documentation for any purpose and
-   without fee or royalty is hereby granted, provided that you agree to
-   comply with the following copyright notice and statements, including
-   the disclaimer, and that the same appear on ALL copies of the software
-   and documentation, including modifications that you make for internal
-   use or for distribution:
-   
-   Copyright 1998 by Ville Pulkki, Helsinki University of Technology.  All
-   rights reserved.  
-   
-   The software may be used, distributed, and included to commercial
-   products without any charges. When included to a commercial product,
-   the method "Vector Base Amplitude Panning" and its developer Ville
-   Pulkki must be referred to in documentation.
-   
-   This software is provided "as is", and Ville Pulkki or Helsinki
-   University of Technology make no representations or warranties,
-   expressed or implied. By way of example, but not limitation, Helsinki
-   University of Technology or Ville Pulkki make no representations or
-   warranties of merchantability or fitness for any particular purpose or
-   that the use of the licensed software or documentation will not
-   infringe any third party patents, copyrights, trademarks or other
-   rights. The name of Ville Pulkki or Helsinki University of Technology
-   may not be used in advertising or publicity pertaining to distribution
-   of the software.
-*/
-
-#include <cmath>
-#include <algorithm>
-#include <stdlib.h>
-
-#include "pbd/cartesian.h"
-#include "ardour/vbap_speakers.h"
-
-using namespace ARDOUR;
-using namespace PBD;
-using namespace std;
-
-VBAPSpeakers* VBAPSpeakers::_instance = 0;
-
-VBAPSpeakers& 
-VBAPSpeakers::instance (Speakers& s)
-{
-       if (_instance == 0) {
-               _instance = new VBAPSpeakers (s);
-       }
-
-       return *_instance;
-}
-
-VBAPSpeakers::VBAPSpeakers (Speakers& s)
-       : _dimension (2)
-       , _speakers (s.speakers())
-{
-       s.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
-}
-
-VBAPSpeakers::~VBAPSpeakers ()
-{
-}
-
-void
-VBAPSpeakers::update ()
-{
-       int dim = 2;
-        
-       for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
-               if ((*i).angles().ele != 0.0) {
-                       cerr << "\n\n\nSPEAKER " << (*i).id << " has ele = " << (*i).angles().ele << "\n\n\n\n";
-                       dim = 3;
-                       break;
-               }
-       }
-
-       _dimension = dim;
-
-       cerr << "update with dimension = " << dim << " speakers = " << _speakers.size() << endl;
-
-       if (_speakers.size() < 2) {
-               /* nothing to be done with less than two speakers */
-               return;
-       }
-
-       if (_dimension == 3)  {
-               ls_triplet_chain *ls_triplets = 0;
-               choose_speaker_triplets (&ls_triplets);
-               if (ls_triplets) {
-                       calculate_3x3_matrixes (ls_triplets);
-                       free (ls_triplets);
-               }
-       } else {
-               choose_speaker_pairs ();
-       }
-}
-
-void 
-VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) 
-{
-       /* Selects the loudspeaker triplets, and
-          calculates the inversion matrices for each selected triplet.
-          A line (connection) is drawn between each loudspeaker. The lines
-          denote the sides of the triangles. The triangles should not be 
-          intersecting. All crossing connections are searched and the 
-          longer connection is erased. This yields non-intesecting triangles,
-          which can be used in panning.
-       */
-
-       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;
-       }
-
-       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;
-                                       add_ldsp_triplet(i,j,k,ls_triplets);
-                               }
-                       }
-               }
-       }
-
-       /*calculate distancies between all speakers and sorting them*/
-       table_size =(((n_speakers - 1) * (n_speakers)) / 2); 
-       for (i = 0; i < table_size; i++) {
-               distance_table[i] = 100000.0;
-       }
-
-       for (i = 0;i < n_speakers; i++) { 
-               for (j = i+1; j < n_speakers; j++) { 
-                       if (connections[i][j] == 1) {
-                               distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
-                               k=0;
-                               while(distance_table[k] < distance) {
-                                       k++;
-                               }
-                               for (l = table_size - 1; l > k ; l--) {
-                                       distance_table[l] = distance_table[l-1];
-                                       distance_table_i[l] = distance_table_i[l-1];
-                                       distance_table_j[l] = distance_table_j[l-1];
-                               }
-                               distance_table[k] = distance;
-                               distance_table_i[k] = i;
-                               distance_table_j[k] = j;
-                       } else
-                               table_size--;
-               }
-       }
-
-       /* disconnecting connections which are crossing shorter ones,
-          starting from shortest one and removing all that cross it,
-          and proceeding to next shortest */
-       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) {
-                       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;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /* remove triangles which had crossing sides
-          with smaller triangles or include loudspeakers*/
-       trip_ptr = *ls_triplets;
-       prev = 0;
-       while (trip_ptr != 0){
-               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 ||
-                   any_ls_inside_triplet(i,j,k) == 1 ){
-                       if (prev != 0) {
-                               prev->next = trip_ptr->next;
-                               tmp_ptr = trip_ptr;
-                               trip_ptr = trip_ptr->next;
-                               free(tmp_ptr);
-                       } else {
-                               *ls_triplets = trip_ptr->next;
-                               tmp_ptr = trip_ptr;
-                               trip_ptr = trip_ptr->next;
-                               free(tmp_ptr);
-                       }
-               } else {
-                       prev = trip_ptr;
-                       trip_ptr = trip_ptr->next;
-
-               }
-       }
-}
-
-int 
-VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
-{
-       /* returns 1 if there is loudspeaker(s) inside given ls triplet */
-       float invdet;
-       const CartesianVector* lp1;
-       const CartesianVector* lp2;
-       const CartesianVector* lp3;
-       float invmx[9];
-       int i,j;
-       float tmp;
-       bool any_ls_inside;
-       bool this_inside;
-       int n_speakers = _speakers.size();
-
-       lp1 =  &(_speakers[a].coords());
-       lp2 =  &(_speakers[b].coords());
-       lp3 =  &(_speakers[c].coords());
-        
-       /* matrix inversion */
-       invdet = 1.0 / (  lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
-                         - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
-                         + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
-        
-       invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
-       invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
-       invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
-       invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
-       invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
-       invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
-       invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
-       invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
-       invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
-        
-       any_ls_inside = false;
-       for (i = 0; i < n_speakers; i++) {
-               if (i != a && i!=b && i != c) {
-                       this_inside = true;
-                       for (j = 0; j < 3; j++) {
-                               tmp = _speakers[i].coords().x * invmx[0 + j*3];
-                               tmp += _speakers[i].coords().y * invmx[1 + j*3];
-                               tmp += _speakers[i].coords().z * invmx[2 + j*3];
-                               if (tmp < -0.001) {
-                                       this_inside = false;
-                               }
-                       }
-                       if (this_inside) {
-                               any_ls_inside = true;
-                       }
-               }
-       }
-
-       return any_ls_inside;
-}
-
-
-void 
-VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets)
-{
-       /* adds i,j,k triplet to triplet chain*/
-
-       struct ls_triplet_chain *trip_ptr, *prev;
-       trip_ptr = *ls_triplets;
-       prev = 0;
-        
-       while (trip_ptr != 0){
-               prev = trip_ptr;
-               trip_ptr = trip_ptr->next;
-       }
-
-       trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain));
-
-       if (prev == 0) {
-               *ls_triplets = trip_ptr;
-       } else {
-               prev->next = trip_ptr;
-       }
-
-       trip_ptr->next = 0;
-       trip_ptr->ls_nos[0] = i;
-       trip_ptr->ls_nos[1] = j;
-       trip_ptr->ls_nos[2] = k;
-}
-
-float 
-VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
-{
-       float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
-                     (vec_length(v1) * vec_length(v2)));
-
-       if (inner > 1.0) {
-               inner= 1.0;
-       }
-
-       if (inner < -1.0) {
-               inner = -1.0;
-       }
-
-       return fabsf((float) acos((double) inner));
-}
-
-float 
-VBAPSpeakers::vec_length(CartesianVector v1)
-{
-       return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
-}
-
-float 
-VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
-{
-       return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
-}
-
-float 
-VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speakers)
-{
-       /* calculate volume of the parallelepiped defined by the loudspeaker
-          direction vectors and divide it with total length of the triangle sides. 
-          This is used when removing too narrow triangles. */
-        
-       float volper, lgth;
-       CartesianVector xprod;
-
-       cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
-       volper = fabsf (vec_prod(xprod, speakers[k].coords()));
-       lgth = (fabsf (vec_angle(speakers[i].coords(), speakers[j].coords())) 
-               + fabsf (vec_angle(speakers[i].coords(), speakers[k].coords())) 
-               + fabsf (vec_angle(speakers[j].coords(), speakers[k].coords())));
-
-       if (lgth > 0.00001) {
-               return volper / lgth;
-       } else {
-               return 0.0;
-       }
-}
-
-void 
-VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res) 
-{
-       float length;
-
-       res->x = (v1.y * v2.z ) - (v1.z * v2.y);
-       res->y = (v1.z * v2.x ) - (v1.x * v2.z);
-       res->z = (v1.x * v2.y ) - (v1.y * v2.x);
-        
-       length = vec_length(*res);
-       res->x /= length;
-       res->y /= length;
-       res->z /= length;
-}
-
-int 
-VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
-{
-       /* checks if two lines intersect on 3D sphere 
-          see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
-          with Multiple Loudspeakers Using VBAP: A Case Study with
-          DIVA Project" in International Conference on 
-          Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
-          if you want to have that paper.
-       */
-
-       CartesianVector v1;
-       CartesianVector v2;
-       CartesianVector v3, neg_v3;
-       float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
-       float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
-        
-       cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
-       cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
-       cross_prod(v1,v2,&v3);
-        
-       neg_v3.x= 0.0 - v3.x;
-       neg_v3.y= 0.0 - v3.y;
-       neg_v3.z= 0.0 - v3.z;
-
-       dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
-       dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
-       dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
-       dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
-       dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
-       dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
-       dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
-       dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
-       dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
-       dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
-
-       /* if one of loudspeakers is close to crossing point, don't do anything*/
-
-
-       if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 || 
-          fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 ||
-          fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 || 
-          fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) {
-               return(0);
-       }
-
-       if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) &&
-            (fabsf(dist_kl - (dist_kv3 + dist_lv3))  <= 0.01)) ||
-           ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01)  &&
-            (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
-               return (1);
-       } else {
-               return (0);
-       }
-}
-
-void  
-VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
-{  
-       /* Calculates the inverse matrices for 3D */
-       float invdet;
-       const CartesianVector* lp1;
-       const CartesianVector* lp2;
-       const CartesianVector* lp3;
-       float *invmx;
-       struct ls_triplet_chain *tr_ptr = ls_triplets;
-       int triplet_count = 0;
-       int triplet;
-
-       assert (tr_ptr);
-        
-       /* counting triplet amount */
-
-       while (tr_ptr != 0) {
-               triplet_count++;
-               tr_ptr = tr_ptr->next;
-       }
-
-       cerr << "@@@ triplets generate " << triplet_count << " of speaker tuples\n";
-
-       triplet = 0;
-
-       _matrices.clear ();
-       _speaker_tuples.clear ();
-
-       for (int n = 0; n < triplet_count; ++n) {
-               _matrices.push_back (threeDmatrix());
-               _speaker_tuples.push_back (tmatrix());
-       }
-
-       while (tr_ptr != 0) {
-               lp1 =  &(_speakers[tr_ptr->ls_nos[0]].coords());
-               lp2 =  &(_speakers[tr_ptr->ls_nos[1]].coords());
-               lp3 =  &(_speakers[tr_ptr->ls_nos[2]].coords());
-                
-               /* matrix inversion */
-               invmx = tr_ptr->inv_mx;
-               invdet = 1.0 / (  lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
-                                 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
-                                 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
-                
-               invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
-               invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
-               invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
-               invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
-               invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
-               invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
-               invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
-               invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
-               invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
-                
-               /* copy the matrix */
-
-               _matrices[triplet][0] = invmx[0];
-               _matrices[triplet][1] = invmx[1];
-               _matrices[triplet][2] = invmx[2];
-               _matrices[triplet][3] = invmx[3];
-               _matrices[triplet][4] = invmx[4];
-               _matrices[triplet][5] = invmx[5];
-               _matrices[triplet][6] = invmx[6];
-               _matrices[triplet][7] = invmx[7];
-               _matrices[triplet][8] = invmx[8];
-
-               _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0];
-               _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1];
-               _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2];
-
-               cerr << "Triplet[" << triplet << "] = " 
-                    << tr_ptr->ls_nos[0] << " + " 
-                    << tr_ptr->ls_nos[1] << " + " 
-                    << tr_ptr->ls_nos[2] << endl;
-
-               triplet++;
-
-               tr_ptr = tr_ptr->next;
-       }
-}
-
-void 
-VBAPSpeakers::choose_speaker_pairs (){
-
-       /* selects the loudspeaker pairs, calculates the inversion
-          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;
-
-       cerr << "CHOOSE PAIRS\n";
-
-       if (n_speakers == 0) {
-               return;
-       }
-
-       for (speaker = 0; speaker < n_speakers; ++speaker) {
-               exists[speaker] = false;
-       }
-
-       /* sort loudspeakers according their aximuth angle */
-       sort_2D_lss (sorted_speakers);
-        
-       /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
-       for (speaker = 0; speaker < n_speakers-1; speaker++) {
-
-               cerr << "Looking at " 
-                    << _speakers[sorted_speakers[speaker]].id << " @ " << _speakers[sorted_speakers[speaker]].angles().azi  
-                    << " and "
-                    << _speakers[sorted_speakers[speaker+1]].id << " @ " << _speakers[sorted_speakers[speaker+1]].angles().azi  
-                    << " delta = " 
-                    << _speakers[sorted_speakers[speaker+1]].angles().azi - _speakers[sorted_speakers[speaker]].angles().azi
-                    << endl;
-
-               if ((_speakers[sorted_speakers[speaker+1]].angles().azi - 
-                    _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){
-                               exists[speaker] = true;
-                               expected_pairs++;
-                       }
-               }
-       }
-        
-       if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi) 
-            +_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) { 
-                       exists[n_speakers-1] = true;
-                       expected_pairs++;
-               } 
-       }
-
-       pair = 0;
-
-       _matrices.clear ();
-       _speaker_tuples.clear ();
-
-       for (int n = 0; n < expected_pairs; ++n) {
-               _matrices.push_back (twoDmatrix());
-               _speaker_tuples.push_back (tmatrix());
-       }
-
-       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];
-
-                       _speaker_tuples[pair][0] = sorted_speakers[speaker];
-                       _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
-
-                       cerr << "PAIR[" << pair << "] = " << sorted_speakers[speaker] << " + " << sorted_speakers[speaker+1] << endl;
-
-                       pair++;
-               }
-       }
-        
-       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];
-
-               _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
-               _speaker_tuples[pair][1] = sorted_speakers[0];
-
-               cerr << "PAIR[" << pair << "] = " << sorted_speakers[n_speakers-1] << " + " << sorted_speakers[0] << endl;
-
-       }
-}
-
-void 
-VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
-{
-       vector<Speaker> tmp = _speakers;
-       vector<Speaker>::iterator s;
-       azimuth_sorter sorter;
-       int n;
-
-       sort (tmp.begin(), tmp.end(), sorter);
-
-       for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) {
-               sorted_speakers[n] = (*s).id;
-               cerr << "Sorted[" << n << "] = " << (*s).id << endl;
-       }
-}
-
-int 
-VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
-{
-       double x1,x2,x3,x4;
-       double det;
-
-       x1 = cos (azi1);
-       x2 = sin (azi1);
-       x3 = cos (azi2);
-       x4 = sin (azi2);
-       det = (x1 * x4) - ( x3 * x2 );
-
-       if (fabs(det) <= 0.001) {
-                
-               inverse_matrix[0] = 0.0;
-               inverse_matrix[1] = 0.0;
-               inverse_matrix[2] = 0.0;
-               inverse_matrix[3] = 0.0;
-
-               return 0;
-
-       } else {
-
-               inverse_matrix[0] = x4 / det;
-               inverse_matrix[1] = -x3 / det;
-               inverse_matrix[2] = -x2 / det;
-               inverse_matrix[3] = x1 / det;
-
-               return 1;
-       }
-}
-
-
index 0a81c306c3d6961d8728d80eaa26e85b64c6f75a..1a3296d4d53b26262da04bddf200488ddb55b576 100644 (file)
@@ -137,7 +137,12 @@ libardour_sources = [
        'named_selection.cc',
        'onset_detector.cc',
        'operations.cc',
-       'panner.cc',
+        'pan_controllable.cc',
+        'pannable.cc',
+        'panner.cc',
+        'panner_manager.cc',
+        'panner_search_path.cc',
+        'panner_shell.cc',
        'pcm_utils.cc',
        'pi_controller.cc',
        'playlist.cc',
@@ -205,8 +210,6 @@ libardour_sources = [
        'unknown_processor.cc',
        'user_bundle.cc',
        'utils.cc',
-        'vbap.cc',
-        'vbap_speakers.cc',
        'version.cc'
 ]
 
index 833fae44402d76eae0db89eb971439303590ef53..cfb7564ab643d6642ef4fcd6e2fb0c754b80e588 100644 (file)
@@ -115,6 +115,8 @@ public:
        void slide (iterator before, double distance);
        void shift (double before, double distance);
 
+        virtual bool clamp_value (double& when, double& value) const { return true; }
+
        void rt_add (double when, double value);
        void add (double when, double value);
        void fast_simple_add (double when, double value);
index cc8ba45001b5150d7a531e8fba47985ee3b7449d..6738e8c1ac897929268a419926af186a6aa9005c 100644 (file)
@@ -394,6 +394,10 @@ ControlList::add (double when, double value)
            control surface (GUI, MIDI, OSC etc) 
         */
 
+        if (!clamp_value (when, value)) {
+                return;
+        }
+
        {
                Glib::Mutex::Lock lm (_lock);
                ControlEvent cp (when, 0.0f);
index aea4ff11f58d07b2569437a5bb3976accef22711..3528435ed1889f71449dbabfb400f384dc968946 100644 (file)
@@ -24,6 +24,7 @@
 #include <string>
 #include <stdint.h>
 
+#include <gtkmm/container.h>
 #include <gtkmm/treeview.h>
 #include <gdkmm/window.h> /* for WMDecoration */
 #include <gdkmm/pixbuf.h>
@@ -86,6 +87,8 @@ namespace Gtkmm2ext {
 
         int physical_screen_height (Glib::RefPtr<Gdk::Window>);
         int physical_screen_width (Glib::RefPtr<Gdk::Window>);
+
+        void container_clear (Gtk::Container&);
 };
 
 #endif /*  __gtkmm2ext_utils_h__ */
index afff784ae8f85d849badeb96a2fbfc45c8865a46..df79e535a75654295b5b8434eaf16e65c566ec98 100644 (file)
@@ -377,3 +377,12 @@ Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
                 return gdk_screen_get_width (scr);
         }
 }
+
+void
+Gtkmm2ext::container_clear (Gtk::Container& c)
+{
+        list<Gtk::Widget*> children = c.get_children();
+        for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
+                c.remove (**child);
+        }
+}
index 2851aec0957ea9fe3fc9814f5143ac958406fa90..7a59aa04cc276bd23bdda87c1c6399ea4bc1ad64 100644 (file)
 
 #include "ardour/session.h"
 #include "ardour/panner.h"
-#include "ardour/panner_1in2out.h"
 #include "ardour/utils.h"
 #include "ardour/audio_buffer.h"
 
+#include "ardour/debug.h"
 #include "ardour/runtime_functions.h"
 #include "ardour/buffer_set.h"
 #include "ardour/audio_buffer.h"
-#include "ardour/vbap.h"
+#include "ardour/pannable.h"
 
 #include "i18n.h"
+#include "panner_1in2out.h"
 
 #include "pbd/mathfix.h"
 
@@ -62,37 +63,72 @@ using namespace PBD;
 
 static PanPluginDescriptor _descriptor = {
         "Mono to Stereo Panner",
-        1, 1, 2, 2,
+        1, 2, 
         Panner1in2out::factory
 };
 
-extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
+extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
 
-Panner1in2out::Panner1in2out (PannerShell& p)
+Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
        : Panner (p)
-        , _position (new PanControllable (parent.session(), _("position"), this, Evoral::Parameter(PanAzimuthAutomation, 0, 0)))
-       , left (0.5)
-       , right (0.5)
-       , left_interp (left)
-       , right_interp (right)
 {
-        desired_left = left;
-        desired_right = right;
+        if (!_pannable->has_state()) {
+                _pannable->pan_azimuth_control->set_value (0.5);
+        }
+        
+        update ();
+
+        left = desired_left;
+        right = desired_right;
+        left_interp = left;
+        right_interp = right;
+
+        _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
 }
 
 Panner1in2out::~Panner1in2out ()
 {
 }
 
+void
+Panner1in2out::update ()
+{
+        float panR, panL;
+        float const pan_law_attenuation = -3.0f;
+        float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+        panR = _pannable->pan_azimuth_control->get_value();
+        panL = 1 - panR;
+
+        desired_left = panL * (scale * panL + 1.0f - scale);
+        desired_right = panR * (scale * panR + 1.0f - scale);
+}
+
 void
 Panner1in2out::set_position (double p)
 {
-        _desired_right = p;
-        _desired_left = 1 - p;
+        if (clamp_position (p)) {
+                _pannable->pan_azimuth_control->set_value (p);
+        }
+}
+
+bool
+Panner1in2out::clamp_position (double& p)
+{
+        /* any position between 0.0 and 1.0 is legal */
+        DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
+        p = max (min (p, 1.0), 0.0);
+        return true;
+}
+
+double 
+Panner1in2out::position () const
+{
+        return _pannable->pan_azimuth_control->get_value ();
 }
 
 void
-Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
+Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
 {
        assert (obufs.count().n_audio() == 2);
 
@@ -100,17 +136,13 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
        Sample* dst;
        pan_t pan;
 
-       if (_muted) {
-               return;
-       }
-
        Sample* const src = srcbuf.data();
         
        /* LEFT OUTPUT */
 
        dst = obufs.get_audio(0).data();
 
-       if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc
+       if (fabsf ((delta = (left - desired_left))) > 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 */
@@ -121,23 +153,23 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
                delta = -(delta / (float) (limit));
 
                for (n = 0; n < limit; n++) {
-                       left_interp[which] = left_interp[which] + delta;
-                       left = left_interp[which] + 0.9 * (left[which] - left_interp[which]);
-                       dst[n] += src[n] * left[which] * gain_coeff;
+                       left_interp = left_interp + delta;
+                       left = left_interp + 0.9 * (left - left_interp);
+                       dst[n] += src[n] * left * gain_coeff;
                }
 
                /* then pan the rest of the buffer; no need for interpolation for this bit */
 
-               pan = left[which] * gain_coeff;
+               pan = left * gain_coeff;
 
                mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
 
        } else {
 
-               left[which] = desired_left[which];
-               left_interp[which] = left[which];
+               left = desired_left;
+               left_interp = left;
 
-               if ((pan = (left[which] * gain_coeff)) != 1.0f) {
+               if ((pan = (left * gain_coeff)) != 1.0f) {
 
                        if (pan != 0.0f) {
 
@@ -165,7 +197,7 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
 
        dst = obufs.get_audio(1).data();
 
-       if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc
+       if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
 
                /* we're moving the pan by an appreciable amount, so we must
                   interpolate over 64 frames or nframes, whichever is smaller */
@@ -176,14 +208,14 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
                delta = -(delta / (float) (limit));
 
                for (n = 0; n < limit; n++) {
-                       right_interp[which] = right_interp[which] + delta;
-                       right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
-                       dst[n] += src[n] * right[which] * gain_coeff;
+                       right_interp = right_interp + delta;
+                       right = right_interp + 0.9 * (right - right_interp);
+                       dst[n] += src[n] * right * gain_coeff;
                }
 
                /* then pan the rest of the buffer, no need for interpolation for this bit */
 
-               pan = right[which] * gain_coeff;
+               pan = right * gain_coeff;
 
                mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
 
@@ -191,10 +223,10 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
 
        } else {
 
-               right[which] = desired_right[which];
-               right_interp[which] = right[which];
+               right = desired_right;
+               right_interp = right;
 
-               if ((pan = (right[which] * gain_coeff)) != 1.0f) {
+               if ((pan = (right * gain_coeff)) != 1.0f) {
 
                        if (pan != 0.0f) {
 
@@ -217,19 +249,116 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
 
 }
 
+void
+Panner1in2out::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;
+       }
+
+       /* apply pan law to convert positional data into pan coefficients for
+          each buffer (output)
+       */
+
+       const float pan_law_attenuation = -3.0f;
+       const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+       for (pframes_t n = 0; n < nframes; ++n) {
+
+                float panR = position[n];
+                const float panL = 1 - panR;
+
+                /* note that are overwriting buffers, but its OK
+                   because we're finished with their old contents
+                   (position automation data) and are
+                   replacing it with panning/gain coefficients 
+                   that we need to actually process the data.
+                */
+                
+                buffers[0][n] = panL * (scale * panL + 1.0f - scale);
+                buffers[1][n] = panR * (scale * panR + 1.0f - scale);
+        }
+
+       /* LEFT OUTPUT */
+
+       dst = obufs.get_audio(0).data();
+       pbuf = buffers[0];
+
+       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 */
+
+       /* RIGHT OUTPUT */
+
+       dst = obufs.get_audio(1).data();
+       pbuf = buffers[1];
+
+       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*
+Panner1in2out::factory (boost::shared_ptr<Pannable> p, Speakers& /* ignored */)
+{
+       return new Panner1in2out (p);
+}
+
+XMLNode&
+Panner1in2out::get_state (void)
+{
+       return state (true);
+}
+
+XMLNode&
+Panner1in2out::state (bool /*full_state*/)
+{
+       XMLNode& root (Panner::get_state ());
+       root.add_property (X_("type"), _descriptor.name);
+       return root;
+}
+
+int
+Panner1in2out::set_state (const XMLNode& node, int version)
+{
+       LocaleGuard lg (X_("POSIX"));
+       Panner::set_state (node, version);
+       return 0;
+}
+
+std::set<Evoral::Parameter> 
+Panner1in2out::what_can_be_automated() const
+{
+        set<Evoral::Parameter> s;
+        s.insert (Evoral::Parameter (PanAzimuthAutomation));
+        return s;
+}
+
 string
-Panner1in2out::describe_parameter (Evoral::Parameter param)
+Panner1in2out::describe_parameter (Evoral::Parameter p)
 {
-        switch (param.type()) {
-        case PanWidthAutomation:
-                return "Pan:width";
+        switch (p.type()) {
         case PanAzimuthAutomation:
-                return "Pan:position";
-        case PanElevationAutomation: 
-                error << X_("stereo panner should not have elevation control") << endmsg;
-                return "Pan:elevation";
-        } 
-        
-        return Automatable::describe_parameter (param);
+                return _("L/R");
+        default:
+                return _pannable->describe_parameter (p);
+        }
 }
-
index 152eb7156aa5d5924464cfe4cf1f350e8a04d3ef..ced467c11bb5fe21429cedc5a2854f9e642ffe1e 100644 (file)
 #include "pbd/cartesian.h"
 
 #include "ardour/types.h"
-#include "ardour/automation_control.h"
-#include "ardour/automatable.h"
+#include "ardour/panner.h"
 
 namespace ARDOUR {
 
-class PannerStereoBase : public class Panner
+class Panner1in2out : public Panner
 {
   public:
-       PannerStereoBase (Panner&);
-       ~PannerStereoBase ();
+       Panner1in2out (boost::shared_ptr<Pannable>);
+       ~Panner1in2out ();
 
         void set_position (double);
+        bool clamp_position (double&);
+
+        double position() const;
 
         ChanCount in() const { return ChanCount (DataType::AUDIO, 1); }
         ChanCount out() const { return ChanCount (DataType::AUDIO, 2); }
 
+        std::set<Evoral::Parameter> what_can_be_automated() const;
+
        /* this class just leaves the pan law itself to be defined
           by the update(), do_distribute_automated()
           methods. derived classes also need a factory method
           and a type name. See EqualPowerStereoPanner as an example.
        */
 
-       void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+        static Panner* factory (boost::shared_ptr<Pannable>, Speakers&);
+
+        std::string describe_parameter (Evoral::Parameter);
+
+       XMLNode& state (bool full_state); 
+       XMLNode& get_state (void); 
+       int      set_state (const XMLNode&, int version);
 
   protected:
-        boost::shared_ptr<AutomationControl> _position;
        float left;
        float right;
        float desired_left;
        float desired_right;
        float left_interp;
        float right_interp;
+
+       void distribute_one (AudioBuffer& src, 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);
+
+        void update ();
 };
 
-}
+} // namespace
 
 #endif /* __ardour_panner_1in2out_h__ */
diff --git a/libs/panners/1in2out/wscript b/libs/panners/1in2out/wscript
new file mode 100644 (file)
index 0000000..e7e4d29
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import 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_PAN1IN2OUT_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+srcdir = '.'
+blddir = 'build'
+
+def set_options(opt):
+       autowaf.set_options(opt)
+
+def configure(conf):
+       autowaf.configure(conf)
+
+def build(bld):
+       obj = bld.new_task_gen('cxx', 'shlib')
+       obj.source = [ 'panner_1in2out.cc' ]
+       obj.export_incdirs = ['.']
+       obj.cxxflags     = '-DPACKAGE="libardour_pan1in2out"'
+       obj.includes     = ['.']
+       obj.name         = 'libardour_pan1in2out'
+       obj.target       = 'pan1in2out'
+       obj.uselib_local = 'libardour libardour_cp libpbd'
+       obj.vnum         = LIBARDOUR_PAN1IN2OUT_LIB_VERSION
+       obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+       autowaf.shutdown()
+
index 6bc0f93a8f369f46ad47dfe9f8e5e0e084b00397..2c2856361c8014708609144880ac416ff45fd820 100644 (file)
@@ -71,21 +71,21 @@ extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
 Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p)
        : Panner (p)
 {
-        _pannable->pan_azimuth_control->set_value (0.5);
-        _pannable->pan_width_control->set_value (1.0);
-
-        /* LEFT SIGNAL, panned hard left */
-        left[0] = 1.0;
-        right[0] = 0.0;
-        desired_left[0] = left_interp[0] = left[0];
-        desired_right[0] = right_interp[0] = right[0];
-
-        /* RIGHT SIGNAL, panned hard right */
-        left[1] = 0;
-        right[1] = 1.0;
-        desired_left[1] = left_interp[1] = left[1];
-        desired_right[1] = right_interp[1] = right[1];
-
+        if (!_pannable->has_state()) {
+                _pannable->pan_azimuth_control->set_value (0.5);
+                _pannable->pan_width_control->set_value (1.0);
+        } 
+        
+        update ();
+        
+        /* LEFT SIGNAL */
+        left_interp[0] = left[0] = desired_left[0];
+        right_interp[0] = right[0] = desired_right[0]; 
+        
+        /* RIGHT SIGNAL */
+        left_interp[1] = left[1] = desired_left[1];
+        right_interp[1] = right[1] = desired_right[1];
+        
         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
         _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
 }
@@ -141,8 +141,6 @@ Panner2in2out::update ()
         const double width = _pannable->pan_width_control->get_value();
         const double direction_as_lr_fract = _pannable->pan_azimuth_control->get_value();
 
-        cerr << "new pan values width=" << width << " LR = " << direction_as_lr_fract << endl;
-
         if (width < 0.0) {
                 pos[0] = direction_as_lr_fract + (width/2.0); // left signal lr_fract
                 pos[1] = direction_as_lr_fract - (width/2.0); // right signal lr_fract
@@ -190,20 +188,14 @@ Panner2in2out::clamp_width (double& w)
 bool
 Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
 {
-        double r_pos = direction_as_lr_fract + (width/2.0);
-        double l_pos = direction_as_lr_fract - (width/2.0);
-        bool can_move_left = true;
-        bool can_move_right = true;
-
-        cerr << "Clamp pos = " << direction_as_lr_fract << " w = " << width << endl;
+        double r_pos;
+        double l_pos;
 
-        if (width > 1.0 || width < 1.0) {
-                return false;
-        }
+        width = max (min (width, 1.0), -1.0);
+        direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
 
-        if (direction_as_lr_fract > 1.0 || direction_as_lr_fract < 0.0) {
-                return false;
-        }
+        r_pos = direction_as_lr_fract + (width/2.0);
+        l_pos = direction_as_lr_fract - (width/2.0);
 
         if (width < 0.0) {
                 swap (r_pos, l_pos);
@@ -213,19 +205,20 @@ Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
            is already there, we're not moving the left signal. 
         */
         
-        if (l_pos <= 0.0 && desired_left[0] <= 0.0) {
-                can_move_left = false;
+        if (l_pos < 0.0) {
+                return false;
         }
 
         /* if the new right position is less than or equal to 1.0 (hard right) and the right panner
            is already there, we're not moving the right signal. 
         */
         
-        if (r_pos >= 1.0 && desired_right[1] >= 1.0) {
-                can_move_right = false;
+        if (r_pos > 1.0) {
+                return false;
+                
         }
 
-        return can_move_left && can_move_right;
+        return true;
 }
 
 void
@@ -459,3 +452,24 @@ Panner2in2out::set_state (const XMLNode& node, int version)
        return 0;
 }
 
+std::set<Evoral::Parameter> 
+Panner2in2out::what_can_be_automated() const
+{
+        set<Evoral::Parameter> s;
+        s.insert (Evoral::Parameter (PanAzimuthAutomation));
+        s.insert (Evoral::Parameter (PanWidthAutomation));
+        return s;
+}
+
+string
+Panner2in2out::describe_parameter (Evoral::Parameter p)
+{
+        switch (p.type()) {
+        case PanAzimuthAutomation:
+                return _("L/R");
+        case PanWidthAutomation:
+                return _("Width");
+        default:
+                return _pannable->describe_parameter (p);
+        }
+}
index 8d8d57d7094ab86cebfff931049b8ce766f09b79..0bb38fa27f79190ebc9152533fb5444eb5621d3f 100644 (file)
@@ -55,8 +55,12 @@ class Panner2in2out : public Panner
         double position () const;
         double width () const;
 
+       std::set<Evoral::Parameter> what_can_be_automated() const;
+
        static Panner* factory (boost::shared_ptr<Pannable>, Speakers&);
 
+        std::string describe_parameter (Evoral::Parameter);
+
        XMLNode& state (bool full_state); 
        XMLNode& get_state (void); 
        int      set_state (const XMLNode&, int version);
index 509848e5a6fa171f09a402762e1524214c7aa421..22d5c03726dd43658f5ac7a5209320de0c022767 100644 (file)
@@ -12,6 +12,12 @@ LIBARDOUR_PAN2IN2OUT_LIB_VERSION = '1.0.0'
 srcdir = '.'
 blddir = 'build'
 
+def set_options(opt):
+       autowaf.set_options(opt)
+
+def configure(conf):
+       autowaf.configure(conf)
+
 def build(bld):
        obj = bld.new_task_gen('cxx', 'shlib')
        obj.source = [ 'panner_2in2out.cc' ]
index 1876f4cf44e7a5c5ef204650abfe93f513887a55..6088a2d0d6dda3c39033ff60cd136b28a974d7e1 100644 (file)
 
 #include "ardour/pannable.h"
 #include "ardour/speakers.h"
-#include "ardour/vbap.h"
-#include "ardour/vbap_speakers.h"
 #include "ardour/audio_buffer.h"
 #include "ardour/buffer_set.h"
 #include "ardour/pan_controllable.h"
 
+#include "vbap.h"
+#include "vbap_speakers.h"
+
 using namespace PBD;
 using namespace ARDOUR;
 using namespace std;
 
 static PanPluginDescriptor _descriptor = {
         "VBAP 2D panner",
-        1, -1, 2, -1,
+        -1, -1,
         VBAPanner::factory
 };
 
 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
 
 VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n)
-        : azimuth_control (new PanControllable (session, string_compose (_("azimuth %1"), n+1), &p, Evoral::Parameter (PanAzimuthAutomation, 0, n)))
-        , elevation_control (new PanControllable (session, string_compose (_("elevation %1"), n+1), &p, Evoral::Parameter (PanElevationAutomation, 0, n)))
 {
         gains[0] = gains[1] = gains[2] = 0;
         desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
@@ -40,44 +39,86 @@ VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n)
 
 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, Speakers& s)
        : Panner (p)
-       , _dirty (true)
        , _speakers (VBAPSpeakers::instance (s))
 {
+        _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
+        _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
+
+        update ();
 }
 
 VBAPanner::~VBAPanner ()
+{
+        clear_signals ();
+}
+
+void
+VBAPanner::clear_signals ()
 {
         for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
                 delete *i;
         }
+        _signals.clear ();
 }
 
 void
-VBAPanner::configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */)
+VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
 {
         uint32_t n = in.n_audio();
 
-        /* 2d panning: spread signals equally around a circle */
-        
-        double degree_step = 360.0 / _speakers.n_speakers();
-        double deg;
-        
-        /* even number of signals? make sure the top two are either side of "top".
-           otherwise, just start at the "top" (90.0 degrees) and rotate around
-        */
-        
-        if (n % 2) {
-                deg = 90.0 - degree_step;
-        } else {
-                deg = 90.0;
-        }
+        clear_signals ();
 
-        _signals.clear ();
-        
         for (uint32_t i = 0; i < n; ++i) {
                 _signals.push_back (new Signal (_pannable->session(), *this, i));
-                _signals[i]->direction = AngularVector (deg, 0.0);
-                deg += degree_step;
+        }
+
+        update ();
+}
+
+void
+VBAPanner::update ()
+{
+        /* recompute signal directions based on panner azimuth and width (diffusion) parameters)
+         */
+
+        /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees
+         */
+
+        double center = _pannable->pan_azimuth_control->get_value() * 360.0;
+
+        /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees
+           so that a width of 1 corresponds to a signal equally present from all directions, 
+           and a width of zero corresponds to a point source from the "center" (above)
+        */
+
+        double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
+
+        double min_dir = center - w;
+        min_dir = max (min (min_dir, 360.0), 0.0);
+
+        double max_dir = center + w;
+        max_dir = max (min (max_dir, 360.0), 0.0);
+
+        double degree_step_per_signal = (max_dir - min_dir) / _signals.size();
+        double signal_direction = min_dir;
+
+        for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
+
+                Signal* signal = *s;
+
+                signal->direction = AngularVector (signal_direction, 0.0);
+
+                compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
+                        cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele
+                             << " Outputs: "
+                             << signal->desired_outputs[0] + 1 << ' '
+                             << signal->desired_outputs[1] + 1 << ' '
+                             << " Gains "
+                             << signal->desired_gains[0] << ' '
+                             << signal->desired_gains[1] << ' '
+                             << endl;
+
+                signal_direction += degree_step_per_signal;
         }
 }
 
@@ -141,49 +182,29 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
                gains[1] /= power;
                gains[2] /= power;
        }
-
-       _dirty = false;
 }
 
 void
-VBAPanner::do_distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
+VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
 {
-       bool was_dirty = _dirty;
         uint32_t n;
         vector<Signal*>::iterator s;
 
         assert (inbufs.count().n_audio() == _signals.size());
 
-        /* XXX need to handle mono case */
-
         for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
 
                 Signal* signal (*s);
 
-                if (was_dirty) {
-                        compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
-                        cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele
-                             << " Outputs: "
-                             << signal->desired_outputs[0] + 1 << ' '
-                             << signal->desired_outputs[1] + 1 << ' '
-                             << " Gains "
-                             << signal->desired_gains[0] << ' '
-                             << signal->desired_gains[1] << ' '
-                             << endl;
-                }
-                
-                do_distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
-
-                if (was_dirty) {
-                        memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains));
-                        memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
-                }
+                distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
+
+                memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains));
+                memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
         }
 }
 
-
 void
-VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
+VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
 {
        Sample* const src = srcbuf.data();
        Sample* dst;
@@ -228,8 +249,8 @@ VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 }
 
 void 
-VBAPanner::do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
-                                        framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
+VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
+                                     framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
 {
 }
 
@@ -253,46 +274,12 @@ VBAPanner::set_state (const XMLNode& node, int /*version*/)
        return 0;
 }
 
-boost::shared_ptr<AutomationControl>
-VBAPanner::azimuth_control (uint32_t n)
-{
-        if (n >= _signals.size()) {
-                return boost::shared_ptr<AutomationControl>();
-        }
-        return _signals[n]->azimuth_control;
-}
-
-boost::shared_ptr<AutomationControl>
-VBAPanner::evelation_control (uint32_t n)
-{
-        if (n >= _signals.size()) {
-                return boost::shared_ptr<AutomationControl>();
-        }
-        return _signals[n]->elevation_control;
-}
-
 Panner*
 VBAPanner::factory (boost::shared_ptr<Pannable> p, Speakers& s)
 {
        return new VBAPanner (p, s);
 }
 
-string
-VBAPanner::describe_parameter (Evoral::Parameter param)
-{
-        stringstream ss;
-        switch (param.type()) {
-        case PanElevationAutomation:
-                return string_compose ( _("Pan:elevation %1"), param.id() + 1);
-        case PanWidthAutomation:
-                return string_compose ( _("Pan:diffusion %1"), param.id() + 1);
-        case PanAzimuthAutomation:
-                return string_compose ( _("Pan:azimuth %1"), param.id() + 1);
-        }
-
-        return Automatable::describe_parameter (param);
-}
-
 ChanCount
 VBAPanner::in() const
 {
@@ -304,3 +291,25 @@ VBAPanner::out() const
 {
         return ChanCount (DataType::AUDIO, _speakers.n_speakers());
 }
+
+std::set<Evoral::Parameter> 
+VBAPanner::what_can_be_automated() const
+{
+        set<Evoral::Parameter> s;
+        s.insert (Evoral::Parameter (PanAzimuthAutomation));
+        s.insert (Evoral::Parameter (PanWidthAutomation));
+        return s;
+}
+        
+string
+VBAPanner::describe_parameter (Evoral::Parameter p)
+{
+        switch (p.type()) {
+        case PanAzimuthAutomation:
+                return _("Direction");
+        case PanWidthAutomation:
+                return _("Diffusion");
+        default:
+                return _pannable->describe_parameter (p);
+        }
+}
index aacff8894ce182bf5c33586e8e50744134424550..2b80b032cb7a54b082ae6a06f4cebaf2a824c2ad 100644 (file)
@@ -26,7 +26,8 @@
 
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
-#include "ardour/vbap_speakers.h"
+
+#include "vbap_speakers.h"
 
 namespace ARDOUR {
 
@@ -39,26 +40,24 @@ public:
        VBAPanner (boost::shared_ptr<Pannable>, Speakers& s);
        ~VBAPanner ();
 
-        void configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */);
+        void configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */);
         ChanCount in() const;
         ChanCount out() const;
 
+        std::set<Evoral::Parameter> what_can_be_automated() const;
+
        static Panner* factory (boost::shared_ptr<Pannable>, Speakers& s);
 
-       void do_distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
-       void do_distribute_automated (BufferSet& ibufs, BufferSet& obufs,
-                                     framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers);
+       void distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
 
        void set_azimuth_elevation (double azimuth, double elevation);
 
+        std::string describe_parameter (Evoral::Parameter);
+
        XMLNode& state (bool full_state);
        XMLNode& get_state ();
        int set_state (const XMLNode&, int version);
 
-        boost::shared_ptr<AutomationControl> azimuth_control (uint32_t signal);
-        boost::shared_ptr<AutomationControl> evelation_control (uint32_t signal);
-
-        std::string describe_parameter (Evoral::Parameter param);
 
 private:
         struct Signal {
@@ -67,20 +66,19 @@ private:
             double desired_gains[3];
             int    outputs[3];
             int    desired_outputs[3];
-            boost::shared_ptr<AutomationControl> azimuth_control;
-            boost::shared_ptr<AutomationControl> elevation_control;
 
             Signal (Session&, VBAPanner&, uint32_t which);
         };
 
         std::vector<Signal*> _signals;
-       bool                _dirty;
         VBAPSpeakers&       _speakers;
         
        void compute_gains (double g[3], int ls[3], int azi, int ele);
+        void update ();
+        void clear_signals ();
 
-       void do_distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
-       void do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
+       void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
+       void distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
                                           framepos_t start, framepos_t end, pframes_t nframes, 
                                           pan_t** buffers, uint32_t which);
 };
index 9090ed65e108abd6af0754b6165b60a30fd48771..7e70e5df66b6d3d8fb7f0c35849d3c74b428208d 100644 (file)
@@ -36,7 +36,8 @@
 #include <stdlib.h>
 
 #include "pbd/cartesian.h"
-#include "ardour/vbap_speakers.h"
+
+#include "vbap_speakers.h"
 
 using namespace ARDOUR;
 using namespace PBD;
diff --git a/libs/panners/vbap/wscript b/libs/panners/vbap/wscript
new file mode 100644 (file)
index 0000000..1399039
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import 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_PANVBAP_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+srcdir = '.'
+blddir = 'build'
+
+def set_options(opt):
+       autowaf.set_options(opt)
+
+def configure(conf):
+       autowaf.configure(conf)
+
+def build(bld):
+       obj = bld.new_task_gen('cxx', 'shlib')
+       obj.source = [ 'vbap_speakers.cc', 'vbap.cc'  ]
+       obj.export_incdirs = ['.']
+       obj.cxxflags     = '-DPACKAGE="libardour_panvbap"'
+       obj.includes     = ['.']
+       obj.name         = 'libardour_panvbap'
+       obj.target       = 'panvbap'
+       obj.uselib_local = 'libardour libardour_cp libpbd'
+       obj.vnum         = LIBARDOUR_PANVBAP_LIB_VERSION
+       obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+       autowaf.shutdown()
+
index 63b547d0939a4a11e6d019196d6a82e2ab986b69..192f27b7db0c574774714e92c35136c0236637c8 100644 (file)
@@ -6,13 +6,22 @@ import os
 srcdir = '.'
 blddir = 'build'
 
-#panners = [ '2in2out', 'vbap', '1in1out' ]
-panners = [ '2in2out' ]
+panners = [ '2in2out', '1in2out', 'vbap' ]
 
 def set_options(opt):
        autowaf.set_options(opt)
 
+def sub_config_and_use(conf, name, has_objects = True):
+       conf.sub_config(name)
+       autowaf.set_local_lib(conf, name, has_objects)
+
+def configure(conf):
+       autowaf.set_recursive()
+       autowaf.configure(conf)
+
+       for i in panners:
+               sub_config_and_use(conf, i)
+
 def build(bld):
         for i in panners:
             bld.add_subdirs(i)
-
index 1bf77055954304d50484ddf0cedd2407ad18c89e..6b0d733656e351b7324d5bcbbfdf3f9052334ec4 100644 (file)
@@ -39,6 +39,7 @@ public:
            Recenable,
            PanDirection,
            PanWidth,
+           PanElevation,
            Balance,
            SendGain,
            PluginParameter
index ce2b2088d2c6af7068b5174aeec804c8b769ace7..005d28f7aa4b1b57be9e015dc8fd4772acc5a997 100644 (file)
@@ -45,6 +45,7 @@
 #include "ardour/location.h"
 #include "ardour/midi_ui.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 #include "ardour/route.h"
 #include "ardour/session.h"
 #include "ardour/tempo.h"
@@ -832,19 +833,17 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
                // pot (jog wheel, external control)
                case Control::type_pot:
                        if (control.group().is_strip()) {
-                               if (route != 0 && route->panner())
-                               {
+                               if (route) {
+                                        boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
                                        // pan for mono input routes, or stereo linked panners
-                                       if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
-                                       {
-                                               // assume pan for now
-                                               AngularVector a = route->panner()->streampanner (0).get_effective_position ();
+                                        if (panner) {
+                                               double p = panner->position ();
                                                 
                                                // calculate new value, and adjust
-                                               a.azi += 180.0 * state.delta * state.sign;
-                                               a.azi = min (180.0, a.azi);
-                                               a.azi = max (0.0, a.azi);
-                                               route->panner()->streampanner (0).set_position (a);
+                                               p += state.delta * state.sign;
+                                               p = min (1.0, p);
+                                               p = max (0.0, p);
+                                               panner->set_position (p);
                                        }
                                }
                                else
@@ -1000,15 +999,13 @@ MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool f
        {
                Pot & pot = route_signal->strip().vpot();
                boost::shared_ptr<Panner> panner = route_signal->route()->panner();
-               if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
-               {
-                       AngularVector pos = route_signal->route()->panner()->streampanner(0).get_effective_position ();
-                       float fract = 1.0 - (pos.azi / 180.0); /* 1.0 = 0 degrees = right; 0.0 = 180 degrees = left */
+               if (panner) {
+                       double pos = panner->position ();
 
                        // cache the MidiByteArray here, because the mackie led control is much lower
                        // resolution than the panner control. So we save lots of byte
                        // sends in spite of more work on the comparison
-                       MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, fract), MackieMidiBuilder::midi_pot_mode_dot);
+                       MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot);
                        // check that something has actually changed
                        if (force_update || bytes != route_signal->last_pan_written())
                        {
index 2f6a6f7c5f8ff633a1be05ab193e14c1528f3b59..f6db125b8ca338599aeda318e55975c1d67943ff 100644 (file)
@@ -52,10 +52,6 @@ void RouteSignal::connect()
        
        if (_route->panner()) {
                _route->panner()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
-               
-               for ( unsigned int i = 0; i < _route->panner()->npanners(); ++i ) {
-                       _route->panner()->streampanner(i).Changed.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
-               }
        }
        
        boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
index 93b56a8a9769f2571bd068139177a170c9c0ea74..280959ae090580f87b3cc5e99787660181f967d2 100644 (file)
@@ -870,7 +870,7 @@ OSC::route_set_pan_stereo_position (int rid, float pos)
        if (r) {
                 boost::shared_ptr<Panner> panner = r->panner();
                 if (panner) {
-                        panner->set_stereo_position (pos);
+                        panner->set_position (pos);
                 }
        }
        
@@ -888,7 +888,7 @@ OSC::route_set_pan_stereo_width (int rid, float pos)
        if (r) {
                 boost::shared_ptr<Panner> panner = r->panner();
                 if (panner) {
-                        panner->set_stereo_width (pos);
+                        panner->set_width (pos);
                 }
        }
        
diff --git a/wscript b/wscript
index 4c86cc37c9bfafd53348464d2d904f69086a0153..9882898eb41aac9dbaed1f67bbaafbd22abb02c7 100644 (file)
--- a/wscript
+++ b/wscript
@@ -25,6 +25,7 @@ children = [
        'libs/taglib',
        'libs/rubberband',
        'libs/surfaces',
+       'libs/panners',
        'libs/timecode',
        'libs/ardour',
        'libs/gtkmm2ext',