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);
127 val = max (min (val, 1.0), -1.0);
128 if (p.set_stereo_pan (p.direction_control()->get_value(), val)) {
129 AutomationControl::set_value(val);
134 streampanner->set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
135 AutomationControl::set_value(val);
142 StreamPanner::PanControllable::get_value (void) const
144 return AutomationControl::get_value();
148 StreamPanner::set_muted (bool yn)
157 StreamPanner::set_position (const AngularVector& av, bool link_call)
159 if (!link_call && parent.linked()) {
160 parent.set_position (av, *this);
167 _control->Changed ();
172 StreamPanner::set_state (const XMLNode& node, int version)
174 const XMLProperty* prop;
175 XMLNodeConstIterator iter;
177 if ((prop = node.property (X_("muted")))) {
178 set_muted (string_is_affirmative (prop->value()));
181 if ((prop = node.property (X_("mono")))) {
182 set_mono (string_is_affirmative (prop->value()));
185 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
186 if ((*iter)->name() == Controllable::xml_node_name) {
187 if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
188 _control->set_state (**iter, version);
197 StreamPanner::get_state ()
199 XMLNode* node = new XMLNode (X_("StreamPanner"));
200 node->add_property (X_("muted"), (muted() ? "yes" : "no"));
201 node->add_property (X_("mono"), (_mono ? "yes" : "no"));
202 node->add_child_nocopy (_control->get_state ());
207 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
210 /* we're in mono mode, so just pan the input to all outputs equally */
211 int const N = parent.nouts ();
212 for (int i = 0; i < N; ++i) {
213 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
216 /* normal mode, call the `real' distribute method */
217 do_distribute (src, obufs, gain_coeff, nframes);
222 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
223 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
226 /* we're in mono mode, so just pan the input to all outputs equally */
227 int const N = parent.nouts ();
228 for (int i = 0; i < N; ++i) {
229 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
232 /* normal mode, call the `real' distribute method */
233 do_distribute_automated (src, obufs, start, end, nframes, buffers);
239 /*---------------------------------------------------------------------- */
241 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
242 : StreamPanner (p, param)
246 , right_interp (right)
250 BaseStereoPanner::~BaseStereoPanner ()
255 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
258 LocaleGuard lg (X_("POSIX"));
260 _control->list()->clear ();
262 while (in.getline (line, sizeof (line), '\n')) {
268 if (strcmp (line, "end") == 0) {
272 if (sscanf (line, "%" PRIu64 " %lf", &when, &value) != 2) {
273 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
277 _control->list()->fast_simple_add (when, value);
280 /* now that we are done loading */
282 ((AutomationList*)_control->list().get())->StateChanged ();
288 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
290 assert(obufs.count().n_audio() == 2);
300 Sample* const src = srcbuf.data();
304 dst = obufs.get_audio(0).data();
306 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
308 /* we've moving the pan by an appreciable amount, so we must
309 interpolate over 64 frames or nframes, whichever is smaller */
311 pframes_t const limit = min ((pframes_t) 64, nframes);
314 delta = -(delta / (float) (limit));
316 for (n = 0; n < limit; n++) {
317 left_interp = left_interp + delta;
318 left = left_interp + 0.9 * (left - left_interp);
319 dst[n] += src[n] * left * gain_coeff;
322 /* then pan the rest of the buffer; no need for interpolation for this bit */
324 pan = left * gain_coeff;
326 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
333 if ((pan = (left * gain_coeff)) != 1.0f) {
337 /* pan is 1 but also not 0, so we must do it "properly" */
339 mix_buffers_with_gain(dst,src,nframes,pan);
341 /* mark that we wrote into the buffer */
349 /* pan is 1 so we can just copy the input samples straight in */
351 mix_buffers_no_gain(dst,src,nframes);
353 /* mark that we wrote into the buffer */
361 dst = obufs.get_audio(1).data();
363 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
365 /* we're moving the pan by an appreciable amount, so we must
366 interpolate over 64 frames or nframes, whichever is smaller */
368 pframes_t const limit = min ((pframes_t) 64, nframes);
371 delta = -(delta / (float) (limit));
373 for (n = 0; n < limit; n++) {
374 right_interp = right_interp + delta;
375 right = right_interp + 0.9 * (right - right_interp);
376 dst[n] += src[n] * right * gain_coeff;
379 /* then pan the rest of the buffer, no need for interpolation for this bit */
381 pan = right * gain_coeff;
383 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
385 /* XXX it would be nice to mark the buffer as written to */
389 right = desired_right;
390 right_interp = right;
392 if ((pan = (right * gain_coeff)) != 1.0f) {
396 /* pan is not 1 but also not 0, so we must do it "properly" */
398 mix_buffers_with_gain(dst,src,nframes,pan);
400 /* XXX it would be nice to mark the buffer as written to */
405 /* pan is 1 so we can just copy the input samples straight in */
407 mix_buffers_no_gain(dst,src,nframes);
409 /* XXX it would be nice to mark the buffer as written to */
414 /*---------------------------------------------------------------------- */
416 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
417 : BaseStereoPanner (p, param)
422 right = desired_right;
424 right_interp = right;
427 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
432 EqualPowerStereoPanner::update ()
434 /* it would be very nice to split this out into a virtual function
435 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
437 but the place where its used in do_distribute_automated() is a tight inner loop,
438 and making "nframes" virtual function calls to compute values is an absurd
442 /* x == 0 => hard left = 180.0 degrees
443 x == 1 => hard right = 0.0 degrees
446 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
448 float const panR = _x;
449 float const panL = 1 - panR;
451 float const pan_law_attenuation = -3.0f;
452 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
454 desired_left = panL * (scale * panL + 1.0f - scale);
455 desired_right = panR * (scale * panR + 1.0f - scale);
457 _effective_angles = _angles;
458 //_control->set_value(x);
462 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
463 framepos_t start, framepos_t end, pframes_t nframes,
466 assert (obufs.count().n_audio() == 2);
470 Sample* const src = srcbuf.data();
472 /* fetch positional data */
474 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
477 do_distribute (srcbuf, obufs, 1.0, nframes);
482 /* store effective pan position. do this even if we are muted */
485 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
492 /* apply pan law to convert positional data into pan coefficients for
496 const float pan_law_attenuation = -3.0f;
497 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
499 for (pframes_t n = 0; n < nframes; ++n) {
501 float const panR = buffers[0][n];
502 float const panL = 1 - panR;
504 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
505 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
510 dst = obufs.get_audio(0).data();
513 for (pframes_t n = 0; n < nframes; ++n) {
514 dst[n] += src[n] * pbuf[n];
517 /* XXX it would be nice to mark the buffer as written to */
521 dst = obufs.get_audio(1).data();
524 for (pframes_t n = 0; n < nframes; ++n) {
525 dst[n] += src[n] * pbuf[n];
528 /* XXX it would be nice to mark the buffer as written to */
532 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
534 return new EqualPowerStereoPanner (parent, param);
538 EqualPowerStereoPanner::get_state (void)
544 EqualPowerStereoPanner::state (bool /*full_state*/)
546 XMLNode& root (StreamPanner::get_state ());
547 root.add_property (X_("type"), EqualPowerStereoPanner::name);
552 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
554 LocaleGuard lg (X_("POSIX"));
556 StreamPanner::set_state (node, version);
558 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
560 if ((*iter)->name() == X_("Automation")) {
562 _control->alist()->set_state (*((*iter)->children().front()), version);
564 if (_control->alist()->automation_state() != Off) {
565 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
566 set_position (AngularVector (degrees, 0.0));
574 Panner::Panner (string name, Session& s)
575 : SessionObject (s, name)
578 //set_name_old_auto (name);
582 _link_direction = SameDirection;
592 Panner::set_linked (bool yn)
596 _session.set_dirty ();
597 LinkStateChanged (); /* EMIT SIGNAL */
602 Panner::set_link_direction (LinkDirection ld)
604 if (ld != _link_direction) {
605 _link_direction = ld;
606 _session.set_dirty ();
607 LinkStateChanged (); /* EMIT SIGNAL */
613 Panner::set_bypassed (bool yn)
615 if (yn != _bypassed) {
623 Panner::reset_to_default ()
625 vector<float> positions;
627 switch (outputs.size()) {
633 if (outputs.size() == 2) {
635 switch (_streampanners.size()) {
637 a.azi = 90.0; /* "front" or "top", in degrees */
638 _streampanners.front()->set_position (a);
639 _streampanners.front()->pan_control()->list()->reset_default (0.5);
643 a.azi = 180.0; /* "left", in degrees */
644 _streampanners.front()->set_position (a);
645 _streampanners.front()->pan_control()->list()->reset_default (0.0);
646 a.azi = 0.0; /* "right", in degrees */
647 _streampanners.back()->set_position (a);
648 _streampanners.back()->pan_control()->list()->reset_default (1.0);
655 vector<Output>::iterator o;
656 vector<StreamPanner*>::iterator p;
658 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
659 (*p)->set_position ((*o).position);
664 Panner::reset_streampanner (uint32_t which)
668 if (which >= _streampanners.size() || which >= outputs.size()) {
672 switch (outputs.size()) {
678 switch (_streampanners.size()) {
680 /* stereo out, 1 stream, default = middle */
681 a.azi = 90.0; /* "front" or "top", in degrees */
682 _streampanners.front()->set_position (a);
683 _streampanners.front()->pan_control()->list()->reset_default (0.5);
686 /* stereo out, 2 streams, default = hard left/right */
688 a.azi = 180.0; /* "left", in degrees */
689 _streampanners.front()->set_position (a);
690 _streampanners.front()->pan_control()->list()->reset_default (0.0);
692 a.azi = 0.0; /* "right", in degrees */
693 _streampanners.back()->set_position (a);
694 _streampanners.back()->pan_control()->list()->reset_default (1.0);
701 _streampanners[which]->set_position (outputs[which].position);
706 * Reset the panner with a given number of outs and panners (and hence inputs)
708 * \param nouts Number of outputs.
709 * \param npans Number of panners.
712 Panner::reset (uint32_t nouts, uint32_t npans)
715 bool changed = false;
716 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
718 /* if new and old config don't need panning, or if
719 the config hasn't changed, we're done.
722 if (do_not_and_did_not_need_panning ||
723 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
727 n = _streampanners.size();
742 /* no need for panning with less than 2 outputs */
744 Changed (); /* EMIT SIGNAL */
751 /* XXX: this can never happen */
755 /* XXX: this can never happen */
756 fatal << _("programming error:")
757 << X_("Panner::reset() called with a single output")
763 outputs.push_back (Output (AngularVector (180.0, 0.0)));
764 outputs.push_back (Output (AngularVector (0.0, 0,0)));
765 for (n = 0; n < npans; ++n) {
766 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
771 setup_speakers (nouts);
772 for (n = 0; n < npans; ++n) {
773 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
778 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
782 setup_meta_controls ();
784 /* must emit Changed here, otherwise the changes to the pan_control below raise further
785 signals which the GUI is not prepared for until it has seen the Changed here.
789 Changed (); /* EMIT SIGNAL */
792 /* force hard left/right panning in a common case: 2in/2out
795 if (npans == 2 && outputs.size() == 2) {
797 /* Do this only if we changed configuration, or our configuration
798 appears to be the default set up (zero degrees)
804 left = _streampanners.front()->get_position ();
805 right = _streampanners.back()->get_position ();
807 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
809 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
810 _streampanners.front()->pan_control()->list()->reset_default (0.0);
812 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
813 _streampanners.back()->pan_control()->list()->reset_default (1.0);
816 } else if (npans > 1 && outputs.size() > 2) {
818 /* 2d panning: spread signals equally around a circle */
820 double degree_step = 360.0 / nouts;
823 /* even number of signals? make sure the top two are either side of "top".
824 otherwise, just start at the "top" (90.0 degrees) and rotate around
828 deg = 90.0 - degree_step;
833 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
834 (*x)->set_position (AngularVector (deg, 0.0));
841 Panner::remove (uint32_t which)
843 vector<StreamPanner*>::iterator i;
844 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
846 if (i != _streampanners.end()) {
848 _streampanners.erase (i);
853 /** Remove all our StreamPanners */
855 Panner::clear_panners ()
857 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
861 _streampanners.clear ();
865 Panner::set_automation_style (AutoStyle style)
867 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
868 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
870 _session.set_dirty ();
874 Panner::set_automation_state (AutoState state)
876 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
877 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
879 _session.set_dirty ();
883 Panner::automation_state () const
885 boost::shared_ptr<AutomationList> l;
887 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
889 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
893 return l ? l->automation_state() : Off;
897 Panner::automation_style () const
899 boost::shared_ptr<AutomationList> l;
901 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
903 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
907 return l ? l->automation_style() : Absolute;
913 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
916 PanPlugins pan_plugins[] = {
917 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
918 { VBAPanner::name, 3, VBAPanner::factory },
919 { string (""), 0, 0 }
923 Panner::get_state (void)
929 Panner::state (bool full)
931 XMLNode* node = new XMLNode ("Panner");
935 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
936 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
937 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
938 snprintf (buf, sizeof (buf), "%zd", outputs.size());
939 node->add_property (X_("outputs"), buf);
941 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
942 node->add_child_nocopy ((*i)->state (full));
945 node->add_child_nocopy (get_automation_xml_state ());
951 Panner::set_state (const XMLNode& node, int version)
953 XMLNodeList nlist = node.children ();
954 XMLNodeConstIterator niter;
955 const XMLProperty *prop;
957 uint32_t num_panners = 0;
959 LocaleGuard lg (X_("POSIX"));
963 ChanCount ins = ChanCount::ZERO;
964 ChanCount outs = ChanCount::ZERO;
966 // XXX: this might not be necessary anymore
969 if ((prop = node.property (X_("linked"))) != 0) {
970 set_linked (string_is_affirmative (prop->value()));
973 if ((prop = node.property (X_("bypassed"))) != 0) {
974 set_bypassed (string_is_affirmative (prop->value()));
977 if ((prop = node.property (X_("link_direction"))) != 0) {
978 LinkDirection ld; /* here to provide type information */
979 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
982 if ((prop = node.property (X_("outputs"))) != 0) {
983 uint32_t n = atoi (prop->value());
986 AngularVector a; // value is irrelevant
987 outputs.push_back (Output (a));
994 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
995 if ((*niter)->name() == X_("Output")) {
999 if ((prop = (*niter)->property (X_("azimuth")))) {
1000 sscanf (prop->value().c_str(), "%lg", &a.azi);
1001 } else if ((prop = (*niter)->property (X_("x")))) {
1002 /* old school cartesian */
1003 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
1006 if ((prop = (*niter)->property (X_("elevation")))) {
1007 sscanf (prop->value().c_str(), "%lg", &a.ele);
1010 outputs.push_back (Output (a));
1015 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1017 if ((*niter)->name() == X_("StreamPanner")) {
1019 if ((prop = (*niter)->property (X_("type")))) {
1021 for (i = 0; pan_plugins[i].factory; ++i) {
1022 if (prop->value() == pan_plugins[i].name) {
1025 /* note that we assume that all the stream panners
1026 are of the same type. pretty good
1027 assumption, but it's still an assumption.
1030 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1033 if (sp->set_state (**niter, version) == 0) {
1034 _streampanners.push_back (sp);
1041 if (!pan_plugins[i].factory) {
1042 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1048 error << _("panner plugin node has no type information!")
1056 setup_meta_controls ();
1058 reset (outputs.size (), num_panners);
1060 /* don't try to do old-school automation loading if it wasn't marked as existing */
1062 if ((prop = node.property (X_("automation")))) {
1064 /* automation path is relative */
1066 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1069 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1070 if ((*niter)->name() == X_("Automation")) {
1071 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1079 Panner::touching () const
1081 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1082 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1091 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1093 AngularVector delta;
1094 AngularVector new_position;
1096 delta = orig.get_position() - a;
1098 if (_link_direction == SameDirection) {
1100 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1102 (*i)->set_position (a, true);
1104 new_position = (*i)->get_position() + delta;
1105 (*i)->set_position (new_position, true);
1111 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1113 (*i)->set_position (a, true);
1115 new_position = (*i)->get_position() - delta;
1116 (*i)->set_position (new_position, true);
1123 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff)
1125 if (outbufs.count().n_audio() == 0) {
1126 // Don't want to lose audio...
1127 assert(inbufs.count().n_audio() == 0);
1131 // We shouldn't be called in the first place...
1132 assert(!bypassed());
1136 if (outbufs.count().n_audio() == 1) {
1138 AudioBuffer& dst = outbufs.get_audio(0);
1140 if (gain_coeff == 0.0f) {
1142 /* only one output, and gain was zero, so make it silent */
1144 dst.silence (nframes);
1146 } else if (gain_coeff == 1.0f){
1148 /* mix all buffers into the output */
1151 dst.read_from(inbufs.get_audio(0), nframes);
1153 // accumulate starting with the second
1154 if (inbufs.count().n_audio() > 0) {
1155 BufferSet::audio_iterator i = inbufs.audio_begin();
1156 for (++i; i != inbufs.audio_end(); ++i) {
1157 dst.merge_from(*i, nframes);
1163 /* mix all buffers into the output, scaling them all by the gain */
1166 dst.read_from(inbufs.get_audio(0), nframes);
1168 // accumulate (with gain) starting with the second
1169 if (inbufs.count().n_audio() > 0) {
1170 BufferSet::audio_iterator i = inbufs.audio_begin();
1171 for (++i; i != inbufs.audio_end(); ++i) {
1172 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1181 /* the terrible silence ... */
1182 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1183 i->silence(nframes);
1186 BufferSet::audio_iterator i = inbufs.audio_begin();
1188 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1189 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1194 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
1196 if (outbufs.count().n_audio() == 0) {
1197 // Failing to deliver audio we were asked to deliver is a bug
1198 assert(inbufs.count().n_audio() == 0);
1202 // We shouldn't be called in the first place...
1203 assert(!bypassed());
1206 // If we shouldn't play automation defer to distribute_no_automation
1207 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1210 gain_t gain_coeff = 1.0;
1212 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1213 gain_coeff = speed_quietning;
1216 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1220 // Otherwise.. let the automation flow, baby
1222 if (outbufs.count().n_audio() == 1) {
1224 AudioBuffer& dst = outbufs.get_audio(0);
1226 // FIXME: apply gain automation?
1229 dst.read_from(inbufs.get_audio(0), nframes);
1231 // accumulate starting with the second
1232 BufferSet::audio_iterator i = inbufs.audio_begin();
1233 for (++i; i != inbufs.audio_end(); ++i) {
1234 dst.merge_from(*i, nframes);
1240 // More than 1 output, we should have 1 panner for each input
1241 //assert(_streampanners.size() == inbufs.count().n_audio());
1243 /* the terrible silence ... */
1244 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1245 i->silence(nframes);
1248 BufferSet::audio_iterator i = inbufs.audio_begin();
1249 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1250 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1254 /* old school automation handling */
1258 Panner::set_name (string str)
1260 automation_path = Glib::build_filename(_session.automation_dir(),
1261 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1269 uint32_t linecnt = 0;
1271 vector<StreamPanner*>::iterator sp;
1272 LocaleGuard lg (X_("POSIX"));
1274 if (automation_path.length() == 0) {
1278 if (access (automation_path.c_str(), F_OK)) {
1282 ifstream in (automation_path.c_str());
1285 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1286 automation_path, strerror (errno))
1291 sp = _streampanners.begin();
1293 while (in.getline (line, sizeof(line), '\n')) {
1295 if (++linecnt == 1) {
1296 if (memcmp (line, X_("version"), 7) == 0) {
1297 if (sscanf (line, "version %f", &version) != 1) {
1298 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1302 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1303 automation_path, line) << endmsg;
1310 if (strlen (line) == 0 || line[0] == '#') {
1314 if (strcmp (line, "begin") == 0) {
1316 if (sp == _streampanners.end()) {
1317 error << string_compose (_("too many panner states found in pan automation file %1"),
1323 if ((*sp)->load (in, automation_path, linecnt)) {
1335 Panner::set_mono (bool yn)
1342 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1343 (*i)->set_mono (yn);
1348 Panner::value_as_string (double v)
1350 if (Panner::equivalent (v, 0.5)) {
1352 } else if (equivalent (v, 0)) {
1354 } else if (equivalent (v, 1)) {
1356 } else if (v < 0.5) {
1358 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1362 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1370 Panner::setup_speakers (uint32_t nouts)
1374 /* top, bottom kind-of-left & bottom kind-of-right */
1375 outputs.push_back (AngularVector (90.0, 0.0));
1376 outputs.push_back (AngularVector (215.0, 0,0));
1377 outputs.push_back (AngularVector (335.0, 0,0));
1380 /* clockwise from top left */
1381 outputs.push_back (AngularVector (135.0, 0.0));
1382 outputs.push_back (AngularVector (45.0, 0.0));
1383 outputs.push_back (AngularVector (335.0, 0.0));
1384 outputs.push_back (AngularVector (215.0, 0.0));
1389 double degree_step = 360.0 / nouts;
1393 /* even number of speakers? make sure the top two are either side of "top".
1394 otherwise, just start at the "top" (90.0 degrees) and rotate around
1398 deg = 90.0 - degree_step;
1402 for (n = 0; n < nouts; ++n, deg += degree_step) {
1403 outputs.push_back (Output (AngularVector (deg, 0.0)));
1408 Speakers& speakers (_session.get_speakers());
1410 speakers.clear_speakers ();
1412 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1413 speakers.add_speaker ((*o).position);
1418 Panner::set_stereo_width (double val)
1420 boost::shared_ptr<AutomationControl> dc = direction_control();
1422 dc->set_value (val);
1427 Panner::set_stereo_position (double val)
1429 boost::shared_ptr<AutomationControl> wc = width_control();
1431 wc->set_value (val);
1436 Panner::set_stereo_pan (double direction_as_lr_fract, double width)
1438 AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0);
1439 /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing
1440 whether its "reversed" (i.e. left signal pans right and right signal pans left).
1441 full width = 180 degrees
1443 double spread = 2.0 * fabs(width/2.0) * 180.0;
1444 double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */
1445 double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */
1446 bool move_left = true;
1447 bool move_right = true;
1451 cerr << "New stereo pan pos = " << direction_as_lr_fract << " w = " << width;
1453 assert (_streampanners.size() > 1);
1456 swap (l_index, r_index);
1459 /* if the new right position is less than or equal to 180 (hard left) and the left panner
1460 is already there, we're not moving the left signal.
1463 if (l_pos > 180.0 && _streampanners[l_index]->get_position().azi == 180.0) {
1467 /* if the new right position is less than or equal to zero (hard right) and the right panner
1468 is already there, we're not moving the right signal.
1471 if (r_pos <= 0.0 && _streampanners[r_index]->get_position().azi == 0.0) {
1475 l_pos = max (min (l_pos, 180.0), 0.0);
1476 r_pos = max (min (r_pos, 180.0), 0.0);
1478 cerr << " left = " << l_pos << " right = " << r_pos << " moving ? " << move_left << '/' << move_right << endl;
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));
1484 // cerr << "left @ " << BaseStereoPanner::azimuth_to_lr_fract (l_pos) << " right @ " << BaseStereoPanner::azimuth_to_lr_fract (r_pos) << endl;
1487 return move_left && move_right;
1491 Panner::setup_meta_controls ()
1493 if (_streampanners.size() != 2 || outputs.size() != 2) {
1497 /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
1499 The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
1500 but they keep us out of the ordinary range of pan-related parameters.
1503 Evoral::Parameter lr_param (PanAutomation, 0, 100);
1504 Evoral::Parameter width_param (PanAutomation, 0, 200);
1505 boost::shared_ptr<AutomationControl> dc = automation_control (lr_param);
1506 boost::shared_ptr<AutomationControl> wc = automation_control (width_param);
1509 /* reset parent StreamPanner as the current one may have been deleted */
1510 boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (dc);
1512 p->streampanner = _streampanners.front ();
1514 dc.reset (new StreamPanner::PanControllable (_session, _("lr"), _streampanners.front(), lr_param));
1519 /* reset parent as above */
1520 boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (wc);
1522 p->streampanner = _streampanners.front ();
1524 wc.reset (new StreamPanner::PanControllable (_session, _("width"), _streampanners.front(), width_param));
1528 dc->set_value (0.5);
1529 wc->set_value (1.0); // full width
1533 Panner::describe_parameter (Evoral::Parameter param)
1535 if (param.type() == PanAutomation) {
1536 switch (param.id()) {
1538 return "Pan:position";
1542 if (_streampanners.size() == 2) {
1543 switch (param.id()) {
1551 ss << "Pan " << param.id() + 1;
1557 return Automatable::describe_parameter (param);