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::set_value (double val)
104 switch (parameter().id()) {
107 streampanner.get_parent().set_stereo_position (val);
111 streampanner.get_parent().set_stereo_width (val);
114 streampanner.set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
117 AutomationControl::set_value(val);
121 StreamPanner::PanControllable::get_value (void) const
123 return AutomationControl::get_value();
127 StreamPanner::set_muted (bool yn)
136 StreamPanner::set_position (const AngularVector& av, bool link_call)
138 if (!link_call && parent.linked()) {
139 parent.set_position (av, *this);
146 _control->Changed ();
151 StreamPanner::set_state (const XMLNode& node, int version)
153 const XMLProperty* prop;
154 XMLNodeConstIterator iter;
156 if ((prop = node.property (X_("muted")))) {
157 set_muted (string_is_affirmative (prop->value()));
160 if ((prop = node.property (X_("mono")))) {
161 set_mono (string_is_affirmative (prop->value()));
164 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
165 if ((*iter)->name() == Controllable::xml_node_name) {
166 if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
167 _control->set_state (**iter, version);
176 StreamPanner::get_state ()
178 XMLNode* node = new XMLNode (X_("StreamPanner"));
179 node->add_property (X_("muted"), (muted() ? "yes" : "no"));
180 node->add_property (X_("mono"), (_mono ? "yes" : "no"));
181 node->add_child_nocopy (_control->get_state ());
186 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
189 /* we're in mono mode, so just pan the input to all outputs equally */
190 int const N = parent.nouts ();
191 for (int i = 0; i < N; ++i) {
192 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
195 /* normal mode, call the `real' distribute method */
196 do_distribute (src, obufs, gain_coeff, nframes);
201 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
202 nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
205 /* we're in mono mode, so just pan the input to all outputs equally */
206 int const N = parent.nouts ();
207 for (int i = 0; i < N; ++i) {
208 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
211 /* normal mode, call the `real' distribute method */
212 do_distribute_automated (src, obufs, start, end, nframes, buffers);
218 /*---------------------------------------------------------------------- */
220 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
221 : StreamPanner (p, param)
225 , right_interp (right)
229 BaseStereoPanner::~BaseStereoPanner ()
234 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
237 LocaleGuard lg (X_("POSIX"));
239 _control->list()->clear ();
241 while (in.getline (line, sizeof (line), '\n')) {
247 if (strcmp (line, "end") == 0) {
251 if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
252 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
256 _control->list()->fast_simple_add (when, value);
259 /* now that we are done loading */
261 ((AutomationList*)_control->list().get())->StateChanged ();
267 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
269 assert(obufs.count().n_audio() == 2);
279 Sample* const src = srcbuf.data();
283 dst = obufs.get_audio(0).data();
285 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
287 /* we've moving the pan by an appreciable amount, so we must
288 interpolate over 64 frames or nframes, whichever is smaller */
290 nframes_t const limit = min ((nframes_t)64, nframes);
293 delta = -(delta / (float) (limit));
295 for (n = 0; n < limit; n++) {
296 left_interp = left_interp + delta;
297 left = left_interp + 0.9 * (left - left_interp);
298 dst[n] += src[n] * left * gain_coeff;
301 /* then pan the rest of the buffer; no need for interpolation for this bit */
303 pan = left * gain_coeff;
305 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
312 if ((pan = (left * gain_coeff)) != 1.0f) {
316 /* pan is 1 but also not 0, so we must do it "properly" */
318 mix_buffers_with_gain(dst,src,nframes,pan);
320 /* mark that we wrote into the buffer */
328 /* pan is 1 so we can just copy the input samples straight in */
330 mix_buffers_no_gain(dst,src,nframes);
332 /* mark that we wrote into the buffer */
340 dst = obufs.get_audio(1).data();
342 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
344 /* we're moving the pan by an appreciable amount, so we must
345 interpolate over 64 frames or nframes, whichever is smaller */
347 nframes_t const limit = min ((nframes_t)64, nframes);
350 delta = -(delta / (float) (limit));
352 for (n = 0; n < limit; n++) {
353 right_interp = right_interp + delta;
354 right = right_interp + 0.9 * (right - right_interp);
355 dst[n] += src[n] * right * gain_coeff;
358 /* then pan the rest of the buffer, no need for interpolation for this bit */
360 pan = right * gain_coeff;
362 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
364 /* XXX it would be nice to mark the buffer as written to */
368 right = desired_right;
369 right_interp = right;
371 if ((pan = (right * gain_coeff)) != 1.0f) {
375 /* pan is not 1 but also not 0, so we must do it "properly" */
377 mix_buffers_with_gain(dst,src,nframes,pan);
379 /* XXX it would be nice to mark the buffer as written to */
384 /* pan is 1 so we can just copy the input samples straight in */
386 mix_buffers_no_gain(dst,src,nframes);
388 /* XXX it would be nice to mark the buffer as written to */
393 /*---------------------------------------------------------------------- */
395 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
396 : BaseStereoPanner (p, param)
401 right = desired_right;
403 right_interp = right;
406 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
411 EqualPowerStereoPanner::update ()
413 /* it would be very nice to split this out into a virtual function
414 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
416 but the place where its used in do_distribute_automated() is a tight inner loop,
417 and making "nframes" virtual function calls to compute values is an absurd
421 /* x == 0 => hard left = 180.0 degrees
422 x == 1 => hard right = 0.0 degrees
425 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
427 float const panR = _x;
428 float const panL = 1 - panR;
430 float const pan_law_attenuation = -3.0f;
431 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
433 desired_left = panL * (scale * panL + 1.0f - scale);
434 desired_right = panR * (scale * panR + 1.0f - scale);
436 _effective_angles = _angles;
437 //_control->set_value(x);
441 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
442 nframes_t start, nframes_t end, nframes_t nframes,
445 assert (obufs.count().n_audio() == 2);
449 Sample* const src = srcbuf.data();
451 /* fetch positional data */
453 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
456 do_distribute (srcbuf, obufs, 1.0, nframes);
461 /* store effective pan position. do this even if we are muted */
464 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
471 /* apply pan law to convert positional data into pan coefficients for
475 const float pan_law_attenuation = -3.0f;
476 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
478 for (nframes_t n = 0; n < nframes; ++n) {
480 float const panR = buffers[0][n];
481 float const panL = 1 - panR;
483 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
484 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
489 dst = obufs.get_audio(0).data();
492 for (nframes_t n = 0; n < nframes; ++n) {
493 dst[n] += src[n] * pbuf[n];
496 /* XXX it would be nice to mark the buffer as written to */
500 dst = obufs.get_audio(1).data();
503 for (nframes_t n = 0; n < nframes; ++n) {
504 dst[n] += src[n] * pbuf[n];
507 /* XXX it would be nice to mark the buffer as written to */
511 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
513 return new EqualPowerStereoPanner (parent, param);
517 EqualPowerStereoPanner::get_state (void)
523 EqualPowerStereoPanner::state (bool /*full_state*/)
525 XMLNode& root (StreamPanner::get_state ());
526 root.add_property (X_("type"), EqualPowerStereoPanner::name);
531 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
533 LocaleGuard lg (X_("POSIX"));
535 StreamPanner::set_state (node, version);
537 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
539 if ((*iter)->name() == X_("Automation")) {
541 _control->alist()->set_state (*((*iter)->children().front()), version);
543 if (_control->alist()->automation_state() != Off) {
544 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
545 set_position (AngularVector (degrees, 0.0));
553 Panner::Panner (string name, Session& s)
554 : SessionObject (s, name)
557 //set_name_old_auto (name);
561 _link_direction = SameDirection;
571 Panner::set_linked (bool yn)
575 _session.set_dirty ();
576 LinkStateChanged (); /* EMIT SIGNAL */
581 Panner::set_link_direction (LinkDirection ld)
583 if (ld != _link_direction) {
584 _link_direction = ld;
585 _session.set_dirty ();
586 LinkStateChanged (); /* EMIT SIGNAL */
592 Panner::set_bypassed (bool yn)
594 if (yn != _bypassed) {
602 Panner::reset_to_default ()
604 vector<float> positions;
606 switch (outputs.size()) {
612 if (outputs.size() == 2) {
614 switch (_streampanners.size()) {
616 a.azi = 90.0; /* "front" or "top", in degrees */
617 _streampanners.front()->set_position (a);
618 _streampanners.front()->pan_control()->list()->reset_default (0.5);
622 a.azi = 180.0; /* "left", in degrees */
623 _streampanners.front()->set_position (a);
624 _streampanners.front()->pan_control()->list()->reset_default (0.0);
625 a.azi = 0.0; /* "right", in degrees */
626 _streampanners.back()->set_position (a);
627 _streampanners.back()->pan_control()->list()->reset_default (1.0);
634 vector<Output>::iterator o;
635 vector<StreamPanner*>::iterator p;
637 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
638 (*p)->set_position ((*o).position);
643 Panner::reset_streampanner (uint32_t which)
647 if (which >= _streampanners.size() || which >= outputs.size()) {
651 switch (outputs.size()) {
657 switch (_streampanners.size()) {
659 /* stereo out, 1 stream, default = middle */
660 a.azi = 90.0; /* "front" or "top", in degrees */
661 _streampanners.front()->set_position (a);
662 _streampanners.front()->pan_control()->list()->reset_default (0.5);
665 /* stereo out, 2 streams, default = hard left/right */
667 a.azi = 180.0; /* "left", in degrees */
668 _streampanners.front()->set_position (a);
669 _streampanners.front()->pan_control()->list()->reset_default (0.0);
671 a.azi = 0.0; /* "right", in degrees */
672 _streampanners.back()->set_position (a);
673 _streampanners.back()->pan_control()->list()->reset_default (1.0);
680 _streampanners[which]->set_position (outputs[which].position);
685 * Reset the panner with a given number of outs and panners (and hence inputs)
687 * \param nouts Number of outputs.
688 * \param npans Number of panners.
691 Panner::reset (uint32_t nouts, uint32_t npans)
694 bool changed = false;
695 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
697 /* if new and old config don't need panning, or if
698 the config hasn't changed, we're done.
701 if (do_not_and_did_not_need_panning ||
702 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
706 n = _streampanners.size();
721 /* no need for panning with less than 2 outputs */
723 Changed (); /* EMIT SIGNAL */
730 /* XXX: this can never happen */
734 /* XXX: this can never happen */
735 fatal << _("programming error:")
736 << X_("Panner::reset() called with a single output")
742 outputs.push_back (Output (AngularVector (180.0, 0.0)));
743 outputs.push_back (Output (AngularVector (0.0, 0,0)));
744 for (n = 0; n < npans; ++n) {
745 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
750 setup_speakers (nouts);
751 for (n = 0; n < npans; ++n) {
752 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
757 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
761 setup_meta_controls ();
763 /* must emit Changed here, otherwise the changes to the pan_control below raise further
764 signals which the GUI is not prepared for until it has seen the Changed here.
768 Changed (); /* EMIT SIGNAL */
771 /* force hard left/right panning in a common case: 2in/2out
774 if (npans == 2 && outputs.size() == 2) {
776 /* Do this only if we changed configuration, or our configuration
777 appears to be the default set up (zero degrees)
783 left = _streampanners.front()->get_position ();
784 right = _streampanners.back()->get_position ();
786 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
788 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
789 _streampanners.front()->pan_control()->list()->reset_default (0.0);
791 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
792 _streampanners.back()->pan_control()->list()->reset_default (1.0);
795 } else if (npans > 1 && outputs.size() > 2) {
797 /* 2d panning: spread signals equally around a circle */
799 double degree_step = 360.0 / nouts;
802 /* even number of signals? make sure the top two are either side of "top".
803 otherwise, just start at the "top" (90.0 degrees) and rotate around
807 deg = 90.0 - degree_step;
812 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
813 (*x)->set_position (AngularVector (deg, 0.0));
820 Panner::remove (uint32_t which)
822 vector<StreamPanner*>::iterator i;
823 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
825 if (i != _streampanners.end()) {
827 _streampanners.erase (i);
832 /** Remove all our StreamPanners */
834 Panner::clear_panners ()
836 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
840 _streampanners.clear ();
844 Panner::set_automation_style (AutoStyle style)
846 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
847 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
849 _session.set_dirty ();
853 Panner::set_automation_state (AutoState state)
855 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
856 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
858 _session.set_dirty ();
862 Panner::automation_state () const
864 boost::shared_ptr<AutomationList> l;
866 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
868 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
872 return l ? l->automation_state() : Off;
876 Panner::automation_style () const
878 boost::shared_ptr<AutomationList> l;
880 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
882 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
886 return l ? l->automation_style() : Absolute;
892 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
895 PanPlugins pan_plugins[] = {
896 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
897 { VBAPanner::name, 3, VBAPanner::factory },
898 { string (""), 0, 0 }
902 Panner::get_state (void)
908 Panner::state (bool full)
910 XMLNode* node = new XMLNode ("Panner");
914 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
915 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
916 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
917 snprintf (buf, sizeof (buf), "%zd", outputs.size());
918 node->add_property (X_("outputs"), buf);
920 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
921 node->add_child_nocopy ((*i)->state (full));
924 node->add_child_nocopy (get_automation_xml_state ());
930 Panner::set_state (const XMLNode& node, int version)
932 XMLNodeList nlist = node.children ();
933 XMLNodeConstIterator niter;
934 const XMLProperty *prop;
936 uint32_t num_panners = 0;
938 LocaleGuard lg (X_("POSIX"));
942 ChanCount ins = ChanCount::ZERO;
943 ChanCount outs = ChanCount::ZERO;
945 // XXX: this might not be necessary anymore
948 if ((prop = node.property (X_("linked"))) != 0) {
949 set_linked (string_is_affirmative (prop->value()));
952 if ((prop = node.property (X_("bypassed"))) != 0) {
953 set_bypassed (string_is_affirmative (prop->value()));
956 if ((prop = node.property (X_("link_direction"))) != 0) {
957 LinkDirection ld; /* here to provide type information */
958 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
961 if ((prop = node.property (X_("outputs"))) != 0) {
962 uint32_t n = atoi (prop->value());
965 AngularVector a; // value is irrelevant
966 outputs.push_back (Output (a));
973 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
974 if ((*niter)->name() == X_("Output")) {
978 if ((prop = (*niter)->property (X_("azimuth")))) {
979 sscanf (prop->value().c_str(), "%lg", &a.azi);
980 } else if ((prop = (*niter)->property (X_("x")))) {
981 /* old school cartesian */
982 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
985 if ((prop = (*niter)->property (X_("elevation")))) {
986 sscanf (prop->value().c_str(), "%lg", &a.ele);
989 outputs.push_back (Output (a));
994 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
996 if ((*niter)->name() == X_("StreamPanner")) {
998 if ((prop = (*niter)->property (X_("type")))) {
1000 for (i = 0; pan_plugins[i].factory; ++i) {
1001 if (prop->value() == pan_plugins[i].name) {
1004 /* note that we assume that all the stream panners
1005 are of the same type. pretty good
1006 assumption, but it's still an assumption.
1009 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1012 if (sp->set_state (**niter, version) == 0) {
1013 _streampanners.push_back (sp);
1020 if (!pan_plugins[i].factory) {
1021 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1027 error << _("panner plugin node has no type information!")
1035 setup_meta_controls ();
1037 reset (outputs.size (), num_panners);
1039 /* don't try to do old-school automation loading if it wasn't marked as existing */
1041 if ((prop = node.property (X_("automation")))) {
1043 /* automation path is relative */
1045 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1048 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1049 if ((*niter)->name() == X_("Automation")) {
1050 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1058 Panner::touching () const
1060 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1061 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1070 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1072 AngularVector delta;
1073 AngularVector new_position;
1075 delta = orig.get_position() - a;
1077 if (_link_direction == SameDirection) {
1079 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1081 (*i)->set_position (a, true);
1083 new_position = (*i)->get_position() + delta;
1084 (*i)->set_position (new_position, true);
1090 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1092 (*i)->set_position (a, true);
1094 new_position = (*i)->get_position() - delta;
1095 (*i)->set_position (new_position, true);
1102 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
1104 if (outbufs.count().n_audio() == 0) {
1105 // Don't want to lose audio...
1106 assert(inbufs.count().n_audio() == 0);
1110 // We shouldn't be called in the first place...
1111 assert(!bypassed());
1115 if (outbufs.count().n_audio() == 1) {
1117 AudioBuffer& dst = outbufs.get_audio(0);
1119 if (gain_coeff == 0.0f) {
1121 /* only one output, and gain was zero, so make it silent */
1123 dst.silence (nframes);
1125 } else if (gain_coeff == 1.0f){
1127 /* mix all buffers into the output */
1130 dst.read_from(inbufs.get_audio(0), nframes);
1132 // accumulate starting with the second
1133 if (inbufs.count().n_audio() > 0) {
1134 BufferSet::audio_iterator i = inbufs.audio_begin();
1135 for (++i; i != inbufs.audio_end(); ++i) {
1136 dst.merge_from(*i, nframes);
1142 /* mix all buffers into the output, scaling them all by the gain */
1145 dst.read_from(inbufs.get_audio(0), nframes);
1147 // accumulate (with gain) starting with the second
1148 if (inbufs.count().n_audio() > 0) {
1149 BufferSet::audio_iterator i = inbufs.audio_begin();
1150 for (++i; i != inbufs.audio_end(); ++i) {
1151 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1160 /* the terrible silence ... */
1161 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1162 i->silence(nframes);
1165 BufferSet::audio_iterator i = inbufs.audio_begin();
1167 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1168 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1173 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
1175 if (outbufs.count().n_audio() == 0) {
1176 // Failing to deliver audio we were asked to deliver is a bug
1177 assert(inbufs.count().n_audio() == 0);
1181 // We shouldn't be called in the first place...
1182 assert(!bypassed());
1185 // If we shouldn't play automation defer to distribute_no_automation
1186 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1189 gain_t gain_coeff = 1.0;
1191 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1192 gain_coeff = speed_quietning;
1195 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1199 // Otherwise.. let the automation flow, baby
1201 if (outbufs.count().n_audio() == 1) {
1203 AudioBuffer& dst = outbufs.get_audio(0);
1205 // FIXME: apply gain automation?
1208 dst.read_from(inbufs.get_audio(0), nframes);
1210 // accumulate starting with the second
1211 BufferSet::audio_iterator i = inbufs.audio_begin();
1212 for (++i; i != inbufs.audio_end(); ++i) {
1213 dst.merge_from(*i, nframes);
1219 // More than 1 output, we should have 1 panner for each input
1220 //assert(_streampanners.size() == inbufs.count().n_audio());
1222 /* the terrible silence ... */
1223 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1224 i->silence(nframes);
1227 BufferSet::audio_iterator i = inbufs.audio_begin();
1228 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1229 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1233 /* old school automation handling */
1237 Panner::set_name (string str)
1239 automation_path = Glib::build_filename(_session.automation_dir(),
1240 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1248 uint32_t linecnt = 0;
1250 vector<StreamPanner*>::iterator sp;
1251 LocaleGuard lg (X_("POSIX"));
1253 if (automation_path.length() == 0) {
1257 if (access (automation_path.c_str(), F_OK)) {
1261 ifstream in (automation_path.c_str());
1264 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1265 automation_path, strerror (errno))
1270 sp = _streampanners.begin();
1272 while (in.getline (line, sizeof(line), '\n')) {
1274 if (++linecnt == 1) {
1275 if (memcmp (line, X_("version"), 7) == 0) {
1276 if (sscanf (line, "version %f", &version) != 1) {
1277 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1281 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1282 automation_path, line) << endmsg;
1289 if (strlen (line) == 0 || line[0] == '#') {
1293 if (strcmp (line, "begin") == 0) {
1295 if (sp == _streampanners.end()) {
1296 error << string_compose (_("too many panner states found in pan automation file %1"),
1302 if ((*sp)->load (in, automation_path, linecnt)) {
1314 Panner::set_mono (bool yn)
1321 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1322 (*i)->set_mono (yn);
1327 Panner::value_as_string (double v)
1329 if (Panner::equivalent (v, 0.5)) {
1331 } else if (equivalent (v, 0)) {
1333 } else if (equivalent (v, 1)) {
1335 } else if (v < 0.5) {
1337 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1341 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1349 Panner::setup_speakers (uint32_t nouts)
1353 /* top, bottom kind-of-left & bottom kind-of-right */
1354 outputs.push_back (AngularVector (90.0, 0.0));
1355 outputs.push_back (AngularVector (215.0, 0,0));
1356 outputs.push_back (AngularVector (335.0, 0,0));
1359 /* clockwise from top left */
1360 outputs.push_back (AngularVector (135.0, 0.0));
1361 outputs.push_back (AngularVector (45.0, 0.0));
1362 outputs.push_back (AngularVector (335.0, 0.0));
1363 outputs.push_back (AngularVector (215.0, 0.0));
1368 double degree_step = 360.0 / nouts;
1372 /* even number of speakers? make sure the top two are either side of "top".
1373 otherwise, just start at the "top" (90.0 degrees) and rotate around
1377 deg = 90.0 - degree_step;
1381 for (n = 0; n < nouts; ++n, deg += degree_step) {
1382 outputs.push_back (Output (AngularVector (deg, 0.0)));
1387 Speakers& speakers (_session.get_speakers());
1389 speakers.clear_speakers ();
1391 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1392 speakers.add_speaker ((*o).position);
1397 Panner::set_stereo_width (double val)
1399 cerr << "Set stereo width to " << val << endl;
1403 Panner::set_stereo_position (double val)
1405 cerr << "Set stereo position to " << val << endl;
1409 Panner::setup_meta_controls ()
1411 if (_streampanners.size() != 2 || outputs.size() != 2) {
1415 /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
1417 The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
1418 but they keep us out of the ordinary range of pan-related parameters.
1421 Evoral::Parameter lr_param (PanAutomation, 0, 100);
1422 Evoral::Parameter width_param (PanAutomation, 0, 200);
1424 if (!automation_control (lr_param)) {
1425 boost::shared_ptr<AutomationControl> c (new StreamPanner::PanControllable (_session, _("lr"), *_streampanners.front(), lr_param));
1429 if (!automation_control (width_param)) {
1430 boost::shared_ptr<AutomationControl> c (new StreamPanner::PanControllable (_session, _("width"), *_streampanners.front(), width_param));