3 Copyright (C) 2004 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 #include "pbd/cartesian.h"
37 #include "pbd/convert.h"
38 #include "pbd/error.h"
39 #include "pbd/failed_constructor.h"
40 #include "pbd/xml++.h"
41 #include "pbd/enumwriter.h"
43 #include "evoral/Curve.hpp"
45 #include "ardour/session.h"
46 #include "ardour/panner.h"
47 #include "ardour/utils.h"
48 #include "ardour/audio_buffer.h"
50 #include "ardour/runtime_functions.h"
51 #include "ardour/buffer_set.h"
52 #include "ardour/audio_buffer.h"
53 #include "ardour/vbap.h"
57 #include "pbd/mathfix.h"
60 using namespace ARDOUR;
63 float Panner::current_automation_version_number = 1.0;
65 string EqualPowerStereoPanner::name = "Equal Power Stereo";
67 /* this is a default mapper of control values to a pan position
68 others can be imagined.
71 static double direct_control_to_stereo_pan (double fract)
73 return BaseStereoPanner::lr_fract_to_azimuth (fract);
76 StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
78 , _control (new PanControllable (parent.session(), _("direction"), this, param))
80 assert (param.type() != NullAutomation);
85 p.add_control (_control);
88 StreamPanner::~StreamPanner ()
93 StreamPanner::set_mono (bool yn)
102 StreamPanner::PanControllable::lower () const
104 switch (parameter().id()) {
105 case 200: /* width */
113 StreamPanner::PanControllable::set_value (double val)
115 Panner& p (streampanner->get_parent());
116 switch (parameter().id()) {
119 val = max (min (val, 1.0), 0.0);
120 if (p.set_stereo_pan (val, p.width_control()->get_value())) {
121 AutomationControl::set_value(val);
122 p.session().set_dirty ();
128 val = max (min (val, 1.0), -1.0);
129 if (p.set_stereo_pan (p.direction_control()->get_value(), val)) {
130 AutomationControl::set_value(val);
131 p.session().set_dirty ();
135 default: /* positional */
136 val = max (min (val, 1.0), 0.0);
137 streampanner->set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
138 AutomationControl::set_value(val);
139 p.session().set_dirty ();
146 StreamPanner::PanControllable::get_value (void) const
148 return AutomationControl::get_value();
152 StreamPanner::set_muted (bool yn)
161 StreamPanner::set_position (const AngularVector& av, bool link_call)
163 if (!link_call && parent.linked()) {
164 parent.set_position (av, *this);
171 _control->Changed ();
176 StreamPanner::set_state (const XMLNode& node, int version)
178 const XMLProperty* prop;
179 XMLNodeConstIterator iter;
181 if ((prop = node.property (X_("muted")))) {
182 set_muted (string_is_affirmative (prop->value()));
185 if ((prop = node.property (X_("mono")))) {
186 set_mono (string_is_affirmative (prop->value()));
189 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
190 if ((*iter)->name() == Controllable::xml_node_name) {
191 if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
192 _control->set_state (**iter, version);
201 StreamPanner::get_state ()
203 XMLNode* node = new XMLNode (X_("StreamPanner"));
204 node->add_property (X_("muted"), (muted() ? "yes" : "no"));
205 node->add_property (X_("mono"), (_mono ? "yes" : "no"));
206 node->add_child_nocopy (_control->get_state ());
211 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
214 /* we're in mono mode, so just pan the input to all outputs equally */
215 int const N = parent.nouts ();
216 for (int i = 0; i < N; ++i) {
217 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
220 /* normal mode, call the `real' distribute method */
221 do_distribute (src, obufs, gain_coeff, nframes);
226 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
227 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
230 /* we're in mono mode, so just pan the input to all outputs equally */
231 int const N = parent.nouts ();
232 for (int i = 0; i < N; ++i) {
233 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
236 /* normal mode, call the `real' distribute method */
237 do_distribute_automated (src, obufs, start, end, nframes, buffers);
243 /*---------------------------------------------------------------------- */
245 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
246 : StreamPanner (p, param)
250 , right_interp (right)
254 BaseStereoPanner::~BaseStereoPanner ()
259 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
262 LocaleGuard lg (X_("POSIX"));
264 _control->list()->clear ();
266 while (in.getline (line, sizeof (line), '\n')) {
272 if (strcmp (line, "end") == 0) {
276 if (sscanf (line, "%" PRIu64 " %lf", &when, &value) != 2) {
277 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
281 _control->list()->fast_simple_add (when, value);
284 /* now that we are done loading */
286 ((AutomationList*)_control->list().get())->StateChanged ();
292 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
294 assert(obufs.count().n_audio() == 2);
304 Sample* const src = srcbuf.data();
308 dst = obufs.get_audio(0).data();
310 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
312 /* we've moving the pan by an appreciable amount, so we must
313 interpolate over 64 frames or nframes, whichever is smaller */
315 pframes_t const limit = min ((pframes_t) 64, nframes);
318 delta = -(delta / (float) (limit));
320 for (n = 0; n < limit; n++) {
321 left_interp = left_interp + delta;
322 left = left_interp + 0.9 * (left - left_interp);
323 dst[n] += src[n] * left * gain_coeff;
326 /* then pan the rest of the buffer; no need for interpolation for this bit */
328 pan = left * gain_coeff;
330 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
337 if ((pan = (left * gain_coeff)) != 1.0f) {
341 /* pan is 1 but also not 0, so we must do it "properly" */
343 mix_buffers_with_gain(dst,src,nframes,pan);
345 /* mark that we wrote into the buffer */
353 /* pan is 1 so we can just copy the input samples straight in */
355 mix_buffers_no_gain(dst,src,nframes);
357 /* mark that we wrote into the buffer */
365 dst = obufs.get_audio(1).data();
367 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
369 /* we're moving the pan by an appreciable amount, so we must
370 interpolate over 64 frames or nframes, whichever is smaller */
372 pframes_t const limit = min ((pframes_t) 64, nframes);
375 delta = -(delta / (float) (limit));
377 for (n = 0; n < limit; n++) {
378 right_interp = right_interp + delta;
379 right = right_interp + 0.9 * (right - right_interp);
380 dst[n] += src[n] * right * gain_coeff;
383 /* then pan the rest of the buffer, no need for interpolation for this bit */
385 pan = right * gain_coeff;
387 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
389 /* XXX it would be nice to mark the buffer as written to */
393 right = desired_right;
394 right_interp = right;
396 if ((pan = (right * gain_coeff)) != 1.0f) {
400 /* pan is not 1 but also not 0, so we must do it "properly" */
402 mix_buffers_with_gain(dst,src,nframes,pan);
404 /* XXX it would be nice to mark the buffer as written to */
409 /* pan is 1 so we can just copy the input samples straight in */
411 mix_buffers_no_gain(dst,src,nframes);
413 /* XXX it would be nice to mark the buffer as written to */
418 /*---------------------------------------------------------------------- */
420 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
421 : BaseStereoPanner (p, param)
426 right = desired_right;
428 right_interp = right;
431 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
436 EqualPowerStereoPanner::update ()
438 /* it would be very nice to split this out into a virtual function
439 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
441 but the place where its used in do_distribute_automated() is a tight inner loop,
442 and making "nframes" virtual function calls to compute values is an absurd
446 /* x == 0 => hard left = 180.0 degrees
447 x == 1 => hard right = 0.0 degrees
450 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
452 float const panR = _x;
453 float const panL = 1 - panR;
455 float const pan_law_attenuation = -3.0f;
456 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
458 desired_left = panL * (scale * panL + 1.0f - scale);
459 desired_right = panR * (scale * panR + 1.0f - scale);
461 _effective_angles = _angles;
462 //_control->set_value(x);
466 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
467 framepos_t start, framepos_t end, pframes_t nframes,
470 assert (obufs.count().n_audio() == 2);
474 Sample* const src = srcbuf.data();
476 /* fetch positional data */
478 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
481 do_distribute (srcbuf, obufs, 1.0, nframes);
486 /* store effective pan position. do this even if we are muted */
489 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
496 /* apply pan law to convert positional data into pan coefficients for
500 const float pan_law_attenuation = -3.0f;
501 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
503 for (pframes_t n = 0; n < nframes; ++n) {
505 float const panR = buffers[0][n];
506 float const panL = 1 - panR;
508 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
509 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
514 dst = obufs.get_audio(0).data();
517 for (pframes_t n = 0; n < nframes; ++n) {
518 dst[n] += src[n] * pbuf[n];
521 /* XXX it would be nice to mark the buffer as written to */
525 dst = obufs.get_audio(1).data();
528 for (pframes_t n = 0; n < nframes; ++n) {
529 dst[n] += src[n] * pbuf[n];
532 /* XXX it would be nice to mark the buffer as written to */
536 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
538 return new EqualPowerStereoPanner (parent, param);
542 EqualPowerStereoPanner::get_state (void)
548 EqualPowerStereoPanner::state (bool /*full_state*/)
550 XMLNode& root (StreamPanner::get_state ());
551 root.add_property (X_("type"), EqualPowerStereoPanner::name);
556 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
558 LocaleGuard lg (X_("POSIX"));
560 StreamPanner::set_state (node, version);
562 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
564 if ((*iter)->name() == X_("Automation")) {
566 _control->alist()->set_state (*((*iter)->children().front()), version);
568 if (_control->alist()->automation_state() != Off) {
569 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
570 set_position (AngularVector (degrees, 0.0));
578 Panner::Panner (string name, Session& s)
579 : SessionObject (s, name)
582 //set_name_old_auto (name);
586 _link_direction = SameDirection;
596 Panner::set_linked (bool yn)
600 _session.set_dirty ();
601 LinkStateChanged (); /* EMIT SIGNAL */
606 Panner::set_link_direction (LinkDirection ld)
608 if (ld != _link_direction) {
609 _link_direction = ld;
610 _session.set_dirty ();
611 LinkStateChanged (); /* EMIT SIGNAL */
617 Panner::set_bypassed (bool yn)
619 if (yn != _bypassed) {
627 Panner::reset_to_default ()
629 vector<float> positions;
631 switch (outputs.size()) {
637 if (outputs.size() == 2) {
639 switch (_streampanners.size()) {
641 a.azi = 90.0; /* "front" or "top", in degrees */
642 _streampanners.front()->set_position (a);
643 _streampanners.front()->pan_control()->list()->reset_default (0.5);
647 a.azi = 180.0; /* "left", in degrees */
648 _streampanners.front()->set_position (a);
649 _streampanners.front()->pan_control()->list()->reset_default (0.0);
650 a.azi = 0.0; /* "right", in degrees */
651 _streampanners.back()->set_position (a);
652 _streampanners.back()->pan_control()->list()->reset_default (1.0);
659 vector<Output>::iterator o;
660 vector<StreamPanner*>::iterator p;
662 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
663 (*p)->set_position ((*o).position);
668 Panner::reset_streampanner (uint32_t which)
672 if (which >= _streampanners.size() || which >= outputs.size()) {
676 switch (outputs.size()) {
682 switch (_streampanners.size()) {
684 /* stereo out, 1 stream, default = middle */
685 a.azi = 90.0; /* "front" or "top", in degrees */
686 _streampanners.front()->set_position (a);
687 _streampanners.front()->pan_control()->list()->reset_default (0.5);
690 /* stereo out, 2 streams, default = hard left/right */
692 a.azi = 180.0; /* "left", in degrees */
693 _streampanners.front()->set_position (a);
694 _streampanners.front()->pan_control()->list()->reset_default (0.0);
696 a.azi = 0.0; /* "right", in degrees */
697 _streampanners.back()->set_position (a);
698 _streampanners.back()->pan_control()->list()->reset_default (1.0);
705 _streampanners[which]->set_position (outputs[which].position);
710 * Reset the panner with a given number of outs and panners (and hence inputs)
712 * \param nouts Number of outputs.
713 * \param npans Number of panners.
716 Panner::reset (uint32_t nouts, uint32_t npans)
719 bool changed = false;
720 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
722 /* if new and old config don't need panning, or if
723 the config hasn't changed, we're done.
726 if (do_not_and_did_not_need_panning ||
727 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
731 n = _streampanners.size();
746 /* no need for panning with less than 2 outputs */
748 Changed (); /* EMIT SIGNAL */
755 /* XXX: this can never happen */
759 /* XXX: this can never happen */
760 fatal << _("programming error:")
761 << X_("Panner::reset() called with a single output")
767 outputs.push_back (Output (AngularVector (180.0, 0.0)));
768 outputs.push_back (Output (AngularVector (0.0, 0,0)));
769 for (n = 0; n < npans; ++n) {
770 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
775 setup_speakers (nouts);
776 for (n = 0; n < npans; ++n) {
777 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
782 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
786 setup_meta_controls ();
788 /* must emit Changed here, otherwise the changes to the pan_control below raise further
789 signals which the GUI is not prepared for until it has seen the Changed here.
793 Changed (); /* EMIT SIGNAL */
796 /* force hard left/right panning in a common case: 2in/2out
799 if (npans == 2 && outputs.size() == 2) {
801 /* Do this only if we changed configuration, or our configuration
802 appears to be the default set up (zero degrees)
808 left = _streampanners.front()->get_position ();
809 right = _streampanners.back()->get_position ();
811 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
813 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
814 _streampanners.front()->pan_control()->list()->reset_default (0.0);
816 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
817 _streampanners.back()->pan_control()->list()->reset_default (1.0);
820 } else if (npans > 1 && outputs.size() > 2) {
822 /* 2d panning: spread signals equally around a circle */
824 double degree_step = 360.0 / nouts;
827 /* even number of signals? make sure the top two are either side of "top".
828 otherwise, just start at the "top" (90.0 degrees) and rotate around
832 deg = 90.0 - degree_step;
837 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
838 (*x)->set_position (AngularVector (deg, 0.0));
845 Panner::remove (uint32_t which)
847 vector<StreamPanner*>::iterator i;
848 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
850 if (i != _streampanners.end()) {
852 _streampanners.erase (i);
857 /** Remove all our StreamPanners */
859 Panner::clear_panners ()
861 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
865 _streampanners.clear ();
869 Panner::set_automation_style (AutoStyle style)
871 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
872 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
874 _session.set_dirty ();
878 Panner::set_automation_state (AutoState state)
880 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
881 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
883 _session.set_dirty ();
887 Panner::automation_state () const
889 boost::shared_ptr<AutomationList> l;
891 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
893 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
897 return l ? l->automation_state() : Off;
901 Panner::automation_style () const
903 boost::shared_ptr<AutomationList> l;
905 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
907 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
911 return l ? l->automation_style() : Absolute;
917 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
920 PanPlugins pan_plugins[] = {
921 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
922 { VBAPanner::name, 3, VBAPanner::factory },
923 { string (""), 0, 0 }
927 Panner::get_state (void)
933 Panner::state (bool full)
935 XMLNode* node = new XMLNode ("Panner");
939 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
940 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
941 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
942 snprintf (buf, sizeof (buf), "%zd", outputs.size());
943 node->add_property (X_("outputs"), buf);
945 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
946 node->add_child_nocopy ((*i)->state (full));
949 node->add_child_nocopy (get_automation_xml_state ());
955 Panner::set_state (const XMLNode& node, int version)
957 XMLNodeList nlist = node.children ();
958 XMLNodeConstIterator niter;
959 const XMLProperty *prop;
961 uint32_t num_panners = 0;
963 LocaleGuard lg (X_("POSIX"));
967 ChanCount ins = ChanCount::ZERO;
968 ChanCount outs = ChanCount::ZERO;
970 // XXX: this might not be necessary anymore
973 if ((prop = node.property (X_("linked"))) != 0) {
974 set_linked (string_is_affirmative (prop->value()));
977 if ((prop = node.property (X_("bypassed"))) != 0) {
978 set_bypassed (string_is_affirmative (prop->value()));
981 if ((prop = node.property (X_("link_direction"))) != 0) {
982 LinkDirection ld; /* here to provide type information */
983 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
986 if ((prop = node.property (X_("outputs"))) != 0) {
987 uint32_t n = atoi (prop->value());
990 AngularVector a; // value is irrelevant
991 outputs.push_back (Output (a));
998 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
999 if ((*niter)->name() == X_("Output")) {
1003 if ((prop = (*niter)->property (X_("azimuth")))) {
1004 sscanf (prop->value().c_str(), "%lg", &a.azi);
1005 } else if ((prop = (*niter)->property (X_("x")))) {
1006 /* old school cartesian */
1007 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
1010 if ((prop = (*niter)->property (X_("elevation")))) {
1011 sscanf (prop->value().c_str(), "%lg", &a.ele);
1014 outputs.push_back (Output (a));
1019 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1021 if ((*niter)->name() == X_("StreamPanner")) {
1023 if ((prop = (*niter)->property (X_("type")))) {
1025 for (i = 0; pan_plugins[i].factory; ++i) {
1026 if (prop->value() == pan_plugins[i].name) {
1029 /* note that we assume that all the stream panners
1030 are of the same type. pretty good
1031 assumption, but it's still an assumption.
1034 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1037 if (sp->set_state (**niter, version) == 0) {
1038 _streampanners.push_back (sp);
1045 if (!pan_plugins[i].factory) {
1046 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1052 error << _("panner plugin node has no type information!")
1060 setup_meta_controls ();
1062 reset (outputs.size (), num_panners);
1064 /* don't try to do old-school automation loading if it wasn't marked as existing */
1066 if ((prop = node.property (X_("automation")))) {
1068 /* automation path is relative */
1070 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1073 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1074 if ((*niter)->name() == X_("Automation")) {
1075 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1083 Panner::touching () const
1085 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1086 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1095 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1097 AngularVector delta;
1098 AngularVector new_position;
1100 delta = orig.get_position() - a;
1102 if (_link_direction == SameDirection) {
1104 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1106 (*i)->set_position (a, true);
1108 new_position = (*i)->get_position() + delta;
1109 (*i)->set_position (new_position, true);
1115 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1117 (*i)->set_position (a, true);
1119 new_position = (*i)->get_position() - delta;
1120 (*i)->set_position (new_position, true);
1127 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff)
1129 if (outbufs.count().n_audio() == 0) {
1130 // Don't want to lose audio...
1131 assert(inbufs.count().n_audio() == 0);
1135 // We shouldn't be called in the first place...
1136 assert(!bypassed());
1140 if (outbufs.count().n_audio() == 1) {
1142 AudioBuffer& dst = outbufs.get_audio(0);
1144 if (gain_coeff == 0.0f) {
1146 /* only one output, and gain was zero, so make it silent */
1148 dst.silence (nframes);
1150 } else if (gain_coeff == 1.0f){
1152 /* mix all buffers into the output */
1155 dst.read_from(inbufs.get_audio(0), nframes);
1157 // accumulate starting with the second
1158 if (inbufs.count().n_audio() > 0) {
1159 BufferSet::audio_iterator i = inbufs.audio_begin();
1160 for (++i; i != inbufs.audio_end(); ++i) {
1161 dst.merge_from(*i, nframes);
1167 /* mix all buffers into the output, scaling them all by the gain */
1170 dst.read_from(inbufs.get_audio(0), nframes);
1172 // accumulate (with gain) starting with the second
1173 if (inbufs.count().n_audio() > 0) {
1174 BufferSet::audio_iterator i = inbufs.audio_begin();
1175 for (++i; i != inbufs.audio_end(); ++i) {
1176 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1185 /* the terrible silence ... */
1186 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1187 i->silence(nframes);
1190 BufferSet::audio_iterator i = inbufs.audio_begin();
1192 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1193 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1198 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
1200 if (outbufs.count().n_audio() == 0) {
1201 // Failing to deliver audio we were asked to deliver is a bug
1202 assert(inbufs.count().n_audio() == 0);
1206 // We shouldn't be called in the first place...
1207 assert(!bypassed());
1210 // If we shouldn't play automation defer to distribute_no_automation
1211 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1214 gain_t gain_coeff = 1.0;
1216 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1217 gain_coeff = speed_quietning;
1220 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1224 // Otherwise.. let the automation flow, baby
1226 if (outbufs.count().n_audio() == 1) {
1228 AudioBuffer& dst = outbufs.get_audio(0);
1230 // FIXME: apply gain automation?
1233 dst.read_from(inbufs.get_audio(0), nframes);
1235 // accumulate starting with the second
1236 BufferSet::audio_iterator i = inbufs.audio_begin();
1237 for (++i; i != inbufs.audio_end(); ++i) {
1238 dst.merge_from(*i, nframes);
1244 // More than 1 output, we should have 1 panner for each input
1245 //assert(_streampanners.size() == inbufs.count().n_audio());
1247 /* the terrible silence ... */
1248 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1249 i->silence(nframes);
1252 BufferSet::audio_iterator i = inbufs.audio_begin();
1253 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1254 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1258 /* old school automation handling */
1262 Panner::set_name (string str)
1264 automation_path = Glib::build_filename(_session.automation_dir(),
1265 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1273 uint32_t linecnt = 0;
1275 vector<StreamPanner*>::iterator sp;
1276 LocaleGuard lg (X_("POSIX"));
1278 if (automation_path.length() == 0) {
1282 if (access (automation_path.c_str(), F_OK)) {
1286 ifstream in (automation_path.c_str());
1289 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1290 automation_path, strerror (errno))
1295 sp = _streampanners.begin();
1297 while (in.getline (line, sizeof(line), '\n')) {
1299 if (++linecnt == 1) {
1300 if (memcmp (line, X_("version"), 7) == 0) {
1301 if (sscanf (line, "version %f", &version) != 1) {
1302 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1306 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1307 automation_path, line) << endmsg;
1314 if (strlen (line) == 0 || line[0] == '#') {
1318 if (strcmp (line, "begin") == 0) {
1320 if (sp == _streampanners.end()) {
1321 error << string_compose (_("too many panner states found in pan automation file %1"),
1327 if ((*sp)->load (in, automation_path, linecnt)) {
1339 Panner::set_mono (bool yn)
1346 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1347 (*i)->set_mono (yn);
1352 Panner::value_as_string (double v)
1354 if (Panner::equivalent (v, 0.5)) {
1356 } else if (equivalent (v, 0)) {
1358 } else if (equivalent (v, 1)) {
1360 } else if (v < 0.5) {
1362 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1366 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1374 Panner::setup_speakers (uint32_t nouts)
1378 /* top, bottom kind-of-left & bottom kind-of-right */
1379 outputs.push_back (AngularVector (90.0, 0.0));
1380 outputs.push_back (AngularVector (215.0, 0,0));
1381 outputs.push_back (AngularVector (335.0, 0,0));
1384 /* clockwise from top left */
1385 outputs.push_back (AngularVector (135.0, 0.0));
1386 outputs.push_back (AngularVector (45.0, 0.0));
1387 outputs.push_back (AngularVector (335.0, 0.0));
1388 outputs.push_back (AngularVector (215.0, 0.0));
1393 double degree_step = 360.0 / nouts;
1397 /* even number of speakers? make sure the top two are either side of "top".
1398 otherwise, just start at the "top" (90.0 degrees) and rotate around
1402 deg = 90.0 - degree_step;
1406 for (n = 0; n < nouts; ++n, deg += degree_step) {
1407 outputs.push_back (Output (AngularVector (deg, 0.0)));
1412 Speakers& speakers (_session.get_speakers());
1414 speakers.clear_speakers ();
1416 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1417 speakers.add_speaker ((*o).position);
1422 Panner::set_stereo_width (double val)
1424 boost::shared_ptr<AutomationControl> dc = direction_control();
1426 dc->set_value (val);
1431 Panner::set_stereo_position (double val)
1433 boost::shared_ptr<AutomationControl> wc = width_control();
1435 wc->set_value (val);
1440 Panner::set_stereo_pan (double direction_as_lr_fract, double width)
1442 AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0);
1443 /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing
1444 whether its "reversed" (i.e. left signal pans right and right signal pans left).
1445 full width = 180 degrees
1447 double spread = 2.0 * fabs(width/2.0) * 180.0;
1448 double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */
1449 double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */
1450 bool move_left = true;
1451 bool move_right = true;
1455 assert (_streampanners.size() > 1);
1458 swap (l_index, r_index);
1461 l_pos = max (min (l_pos, 180.0), 0.0);
1462 r_pos = max (min (r_pos, 180.0), 0.0);
1464 /* if the new left position is less than or equal to 180 (hard left) and the left panner
1465 is already there, we're not moving the left signal.
1468 if (l_pos >= 180.0 &&_streampanners[l_index]->get_position().azi == 180.0) {
1472 /* if the new right position is less than or equal to zero (hard right) and the right panner
1473 is already there, we're not moving the right signal.
1476 if (r_pos <= 0.0 && _streampanners[r_index]->get_position().azi == 0.0) {
1480 if (move_left && move_right) {
1481 _streampanners[l_index]->set_position (AngularVector (l_pos, 0.0));
1482 _streampanners[r_index]->set_position (AngularVector (r_pos, 0.0));
1485 return move_left && move_right;
1489 Panner::setup_meta_controls ()
1491 if (_streampanners.size() != 2 || outputs.size() != 2) {
1495 /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
1497 The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
1498 but they keep us out of the ordinary range of pan-related parameters.
1501 Evoral::Parameter lr_param (PanAutomation, 0, 100);
1502 Evoral::Parameter width_param (PanAutomation, 0, 200);
1503 boost::shared_ptr<AutomationControl> dc = automation_control (lr_param);
1504 boost::shared_ptr<AutomationControl> wc = automation_control (width_param);
1507 /* reset parent StreamPanner as the current one may have been deleted */
1508 boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (dc);
1510 p->streampanner = _streampanners.front ();
1512 dc.reset (new StreamPanner::PanControllable (_session, _("lr"), _streampanners.front(), lr_param));
1517 /* reset parent as above */
1518 boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (wc);
1520 p->streampanner = _streampanners.front ();
1522 wc.reset (new StreamPanner::PanControllable (_session, _("width"), _streampanners.front(), width_param));
1526 dc->set_value (0.5);
1527 wc->set_value (1.0); // full width
1531 Panner::describe_parameter (Evoral::Parameter param)
1533 if (param.type() == PanAutomation) {
1534 switch (param.id()) {
1536 return "Pan:position";
1540 if (_streampanners.size() == 2) {
1541 switch (param.id()) {
1549 ss << "Pan " << param.id() + 1;
1555 return Automatable::describe_parameter (param);