2 Copyright (C) 2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include "pbd/cartesian.h"
36 #include "pbd/error.h"
37 #include "pbd/failed_constructor.h"
38 #include "pbd/xml++.h"
39 #include "pbd/enumwriter.h"
41 #include "evoral/Curve.hpp"
43 #include "ardour/session.h"
44 #include "ardour/panner.h"
45 #include "ardour/utils.h"
46 #include "ardour/audio_buffer.h"
48 #include "ardour/runtime_functions.h"
49 #include "ardour/buffer_set.h"
50 #include "ardour/audio_buffer.h"
51 #include "ardour/vbap.h"
55 #include "pbd/mathfix.h"
58 using namespace ARDOUR;
61 float Panner::current_automation_version_number = 1.0;
63 string EqualPowerStereoPanner::name = "Equal Power Stereo";
65 /* this is a default mapper of control values to a pan position
66 others can be imagined.
69 static double direct_control_to_stereo_pan (double fract)
71 return BaseStereoPanner::lr_fract_to_azimuth (fract);
74 StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
77 assert (param.type() != NullAutomation);
82 /* get our AutomationControl from our parent Panner, creating it if required */
83 _control = boost::dynamic_pointer_cast<AutomationControl> (parent.control (param, true));
86 StreamPanner::~StreamPanner ()
91 StreamPanner::set_mono (bool yn)
100 Panner::PanControllable::set_value (double val)
102 panner.streampanner (parameter().id()).set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
103 AutomationControl::set_value(val);
107 Panner::PanControllable::get_value (void) const
109 return AutomationControl::get_value();
113 StreamPanner::set_muted (bool yn)
122 StreamPanner::set_position (const AngularVector& av, bool link_call)
124 if (!link_call && parent.linked()) {
125 parent.set_position (av, *this);
132 _control->Changed ();
137 StreamPanner::set_state (const XMLNode& node, int /*version*/)
139 const XMLProperty* prop;
140 XMLNodeConstIterator iter;
142 if ((prop = node.property (X_("muted")))) {
143 set_muted (string_is_affirmative (prop->value()));
146 if ((prop = node.property (X_("mono")))) {
147 set_mono (string_is_affirmative (prop->value()));
154 StreamPanner::add_state (XMLNode& node)
156 node.add_property (X_("muted"), (muted() ? "yes" : "no"));
157 node.add_property (X_("mono"), (_mono ? "yes" : "no"));
161 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
164 /* we're in mono mode, so just pan the input to all outputs equally */
165 int const N = parent.nouts ();
166 for (int i = 0; i < N; ++i) {
167 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
170 /* normal mode, call the `real' distribute method */
171 do_distribute (src, obufs, gain_coeff, nframes);
176 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
177 nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
180 /* we're in mono mode, so just pan the input to all outputs equally */
181 int const N = parent.nouts ();
182 for (int i = 0; i < N; ++i) {
183 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
186 /* normal mode, call the `real' distribute method */
187 do_distribute_automated (src, obufs, start, end, nframes, buffers);
193 /*---------------------------------------------------------------------- */
195 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
196 : StreamPanner (p, param)
200 , right_interp (right)
204 BaseStereoPanner::~BaseStereoPanner ()
209 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
212 LocaleGuard lg (X_("POSIX"));
214 _control->list()->clear ();
216 while (in.getline (line, sizeof (line), '\n')) {
222 if (strcmp (line, "end") == 0) {
226 if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
227 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
231 _control->list()->fast_simple_add (when, value);
234 /* now that we are done loading */
236 ((AutomationList*)_control->list().get())->StateChanged ();
242 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
244 assert(obufs.count().n_audio() == 2);
254 Sample* const src = srcbuf.data();
258 dst = obufs.get_audio(0).data();
260 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
262 /* we've moving the pan by an appreciable amount, so we must
263 interpolate over 64 frames or nframes, whichever is smaller */
265 nframes_t const limit = min ((nframes_t)64, nframes);
268 delta = -(delta / (float) (limit));
270 for (n = 0; n < limit; n++) {
271 left_interp = left_interp + delta;
272 left = left_interp + 0.9 * (left - left_interp);
273 dst[n] += src[n] * left * gain_coeff;
276 /* then pan the rest of the buffer; no need for interpolation for this bit */
278 pan = left * gain_coeff;
280 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
287 if ((pan = (left * gain_coeff)) != 1.0f) {
291 /* pan is 1 but also not 0, so we must do it "properly" */
293 mix_buffers_with_gain(dst,src,nframes,pan);
295 /* mark that we wrote into the buffer */
303 /* pan is 1 so we can just copy the input samples straight in */
305 mix_buffers_no_gain(dst,src,nframes);
307 /* mark that we wrote into the buffer */
315 dst = obufs.get_audio(1).data();
317 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
319 /* we're moving the pan by an appreciable amount, so we must
320 interpolate over 64 frames or nframes, whichever is smaller */
322 nframes_t const limit = min ((nframes_t)64, nframes);
325 delta = -(delta / (float) (limit));
327 for (n = 0; n < limit; n++) {
328 right_interp = right_interp + delta;
329 right = right_interp + 0.9 * (right - right_interp);
330 dst[n] += src[n] * right * gain_coeff;
333 /* then pan the rest of the buffer, no need for interpolation for this bit */
335 pan = right * gain_coeff;
337 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
339 /* XXX it would be nice to mark the buffer as written to */
343 right = desired_right;
344 right_interp = right;
346 if ((pan = (right * gain_coeff)) != 1.0f) {
350 /* pan is not 1 but also not 0, so we must do it "properly" */
352 mix_buffers_with_gain(dst,src,nframes,pan);
354 /* XXX it would be nice to mark the buffer as written to */
359 /* pan is 1 so we can just copy the input samples straight in */
361 mix_buffers_no_gain(dst,src,nframes);
363 /* XXX it would be nice to mark the buffer as written to */
368 /*---------------------------------------------------------------------- */
370 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
371 : BaseStereoPanner (p, param)
376 right = desired_right;
378 right_interp = right;
381 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
386 EqualPowerStereoPanner::update ()
388 /* it would be very nice to split this out into a virtual function
389 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
391 but the place where its used in do_distribute_automated() is a tight inner loop,
392 and making "nframes" virtual function calls to compute values is an absurd
396 /* x == 0 => hard left = 180.0 degrees
397 x == 1 => hard right = 0.0 degrees
400 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
402 float const panR = _x;
403 float const panL = 1 - panR;
405 float const pan_law_attenuation = -3.0f;
406 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
408 desired_left = panL * (scale * panL + 1.0f - scale);
409 desired_right = panR * (scale * panR + 1.0f - scale);
411 _effective_angles = _angles;
412 //_control->set_value(x);
416 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
417 nframes_t start, nframes_t end, nframes_t nframes,
420 assert (obufs.count().n_audio() == 2);
424 Sample* const src = srcbuf.data();
426 /* fetch positional data */
428 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
431 do_distribute (srcbuf, obufs, 1.0, nframes);
436 /* store effective pan position. do this even if we are muted */
439 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
446 /* apply pan law to convert positional data into pan coefficients for
450 const float pan_law_attenuation = -3.0f;
451 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
453 for (nframes_t n = 0; n < nframes; ++n) {
455 float const panR = buffers[0][n];
456 float const panL = 1 - panR;
458 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
459 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
464 dst = obufs.get_audio(0).data();
467 for (nframes_t n = 0; n < nframes; ++n) {
468 dst[n] += src[n] * pbuf[n];
471 /* XXX it would be nice to mark the buffer as written to */
475 dst = obufs.get_audio(1).data();
478 for (nframes_t n = 0; n < nframes; ++n) {
479 dst[n] += src[n] * pbuf[n];
482 /* XXX it would be nice to mark the buffer as written to */
486 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
488 return new EqualPowerStereoPanner (parent, param);
492 EqualPowerStereoPanner::get_state (void)
498 EqualPowerStereoPanner::state (bool /*full_state*/)
500 XMLNode* root = new XMLNode ("StreamPanner");
502 LocaleGuard lg (X_("POSIX"));
504 snprintf (buf, sizeof (buf), "%.12g", _angles.azi);
505 root->add_property (X_("azimuth"), buf);
506 root->add_property (X_("type"), EqualPowerStereoPanner::name);
508 // XXX: dont save automation here... its part of the automatable panner now.
510 StreamPanner::add_state (*root);
512 root->add_child_nocopy (_control->get_state ());
518 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
520 const XMLProperty* prop;
521 LocaleGuard lg (X_("POSIX"));
523 if ((prop = node.property (X_("azimuth")))) {
524 AngularVector a (atof (prop->value().c_str()), 0.0);
525 set_position (a, true);
526 } else if ((prop = node.property (X_("x")))) {
527 /* old school cartesian positioning */
529 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
530 set_position (a, true);
533 StreamPanner::set_state (node, version);
535 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
537 if ((*iter)->name() == X_("Controllable")) {
538 if ((prop = (*iter)->property("name")) != 0 && prop->value() == "panner") {
539 _control->set_state (**iter, version);
542 } else if ((*iter)->name() == X_("Automation")) {
544 _control->alist()->set_state (*((*iter)->children().front()), version);
546 if (_control->alist()->automation_state() != Off) {
547 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
548 set_position (AngularVector (degrees, 0.0));
556 Panner::Panner (string name, Session& s)
557 : SessionObject (s, name)
560 //set_name_old_auto (name);
564 _link_direction = SameDirection;
574 Panner::set_linked (bool yn)
578 _session.set_dirty ();
579 LinkStateChanged (); /* EMIT SIGNAL */
584 Panner::set_link_direction (LinkDirection ld)
586 if (ld != _link_direction) {
587 _link_direction = ld;
588 _session.set_dirty ();
589 LinkStateChanged (); /* EMIT SIGNAL */
595 Panner::set_bypassed (bool yn)
597 if (yn != _bypassed) {
605 Panner::reset_to_default ()
607 vector<float> positions;
609 switch (outputs.size()) {
615 if (outputs.size() == 2) {
617 switch (_streampanners.size()) {
619 a.azi = 90.0; /* "front" or "top", in degrees */
620 _streampanners.front()->set_position (a);
621 _streampanners.front()->pan_control()->list()->reset_default (0.5);
625 a.azi = 180.0; /* "left", in degrees */
626 _streampanners.front()->set_position (a);
627 _streampanners.front()->pan_control()->list()->reset_default (0.0);
628 a.azi = 0.0; /* "right", in degrees */
629 _streampanners.back()->set_position (a);
630 _streampanners.back()->pan_control()->list()->reset_default (1.0);
637 vector<Output>::iterator o;
638 vector<StreamPanner*>::iterator p;
640 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
641 (*p)->set_position ((*o).position);
646 Panner::reset_streampanner (uint32_t which)
650 if (which >= _streampanners.size() || which >= outputs.size()) {
654 switch (outputs.size()) {
660 switch (_streampanners.size()) {
662 /* stereo out, 1 stream, default = middle */
663 a.azi = 90.0; /* "front" or "top", in degrees */
664 _streampanners.front()->set_position (a);
665 _streampanners.front()->pan_control()->list()->reset_default (0.5);
668 /* stereo out, 2 streams, default = hard left/right */
670 a.azi = 180.0; /* "left", in degrees */
671 _streampanners.front()->set_position (a);
672 _streampanners.front()->pan_control()->list()->reset_default (0.0);
674 a.azi = 0.0; /* "right", in degrees */
675 _streampanners.back()->set_position (a);
676 _streampanners.back()->pan_control()->list()->reset_default (1.0);
683 _streampanners[which]->set_position (outputs[which].position);
688 * Reset the panner with a given number of outs and panners (and hence inputs)
690 * \param nouts Number of outputs.
691 * \param npans Number of panners.
694 Panner::reset (uint32_t nouts, uint32_t npans)
697 bool changed = false;
698 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
700 /* if new and old config don't need panning, or if
701 the config hasn't changed, we're done.
704 if (do_not_and_did_not_need_panning ||
705 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
709 n = _streampanners.size();
724 /* no need for panning with less than 2 outputs */
726 Changed (); /* EMIT SIGNAL */
733 /* XXX: this can never happen */
737 /* XXX: this can never happen */
738 fatal << _("programming error:")
739 << X_("Panner::reset() called with a single output")
745 outputs.push_back (Output (AngularVector (180.0, 0.0)));
746 outputs.push_back (Output (AngularVector (0.0, 0,0)));
747 for (n = 0; n < npans; ++n) {
748 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
753 setup_speakers (nouts);
754 for (n = 0; n < npans; ++n) {
755 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
760 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
764 /* must emit Changed here, otherwise the changes to the pan_control below raise further
765 signals which the GUI is not prepared for until it has seen the Changed here.
769 Changed (); /* EMIT SIGNAL */
772 /* force hard left/right panning in a common case: 2in/2out
775 if (npans == 2 && outputs.size() == 2) {
777 /* Do this only if we changed configuration, or our configuration
778 appears to be the default set up (zero degrees)
784 left = _streampanners.front()->get_position ();
785 right = _streampanners.back()->get_position ();
787 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
789 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
790 _streampanners.front()->pan_control()->list()->reset_default (0.0);
792 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
793 _streampanners.back()->pan_control()->list()->reset_default (1.0);
796 } else if (npans > 1 && outputs.size() > 2) {
798 /* 2d panning: spread signals equally around a circle */
800 double degree_step = 360.0 / nouts;
803 /* even number of signals? make sure the top two are either side of "top".
804 otherwise, just start at the "top" (90.0 degrees) and rotate around
808 deg = 90.0 - degree_step;
813 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
814 (*x)->set_position (AngularVector (deg, 0.0));
821 Panner::remove (uint32_t which)
823 vector<StreamPanner*>::iterator i;
824 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
826 if (i != _streampanners.end()) {
828 _streampanners.erase (i);
833 /** Remove all our StreamPanners */
835 Panner::clear_panners ()
837 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
841 _streampanners.clear ();
845 Panner::set_automation_style (AutoStyle style)
847 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
848 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
850 _session.set_dirty ();
854 Panner::set_automation_state (AutoState state)
856 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
857 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
859 _session.set_dirty ();
863 Panner::automation_state () const
865 boost::shared_ptr<AutomationList> l;
867 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
869 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
873 return l ? l->automation_state() : Off;
877 Panner::automation_style () const
879 boost::shared_ptr<AutomationList> l;
881 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
883 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
887 return l ? l->automation_style() : Absolute;
893 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
896 PanPlugins pan_plugins[] = {
897 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
898 { VBAPanner::name, 3, VBAPanner::factory },
899 { string (""), 0, 0 }
903 Panner::get_state (void)
909 Panner::state (bool full)
911 XMLNode* node = new XMLNode ("Panner");
915 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
916 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
917 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
919 for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
920 XMLNode* onode = new XMLNode (X_("Output"));
921 snprintf (buf, sizeof (buf), "%.12g", (*o).position.azi);
922 onode->add_property (X_("azimuth"), buf);
923 snprintf (buf, sizeof (buf), "%.12g", (*o).position.ele);
924 onode->add_property (X_("elevation"), buf);
925 node->add_child_nocopy (*onode);
928 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
929 node->add_child_nocopy ((*i)->state (full));
932 node->add_child_nocopy (get_automation_xml_state ());
938 Panner::set_state (const XMLNode& node, int version)
941 XMLNodeConstIterator niter;
942 const XMLProperty *prop;
944 uint32_t num_panners = 0;
946 LocaleGuard lg (X_("POSIX"));
950 ChanCount ins = ChanCount::ZERO;
951 ChanCount outs = ChanCount::ZERO;
953 // XXX: this might not be necessary anymore
956 if ((prop = node.property (X_("linked"))) != 0) {
957 set_linked (string_is_affirmative (prop->value()));
960 if ((prop = node.property (X_("bypassed"))) != 0) {
961 set_bypassed (string_is_affirmative (prop->value()));
964 if ((prop = node.property (X_("link_direction"))) != 0) {
965 LinkDirection ld; /* here to provide type information */
966 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
969 nlist = node.children();
971 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
972 if ((*niter)->name() == X_("Output")) {
976 if ((prop = (*niter)->property (X_("azimuth")))) {
977 sscanf (prop->value().c_str(), "%lg", &a.azi);
978 } else if ((prop = (*niter)->property (X_("x")))) {
979 /* old school cartesian */
980 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
983 if ((prop = (*niter)->property (X_("elevation")))) {
984 sscanf (prop->value().c_str(), "%lg", &a.ele);
987 outputs.push_back (Output (a));
991 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
993 if ((*niter)->name() == X_("StreamPanner")) {
995 if ((prop = (*niter)->property (X_("type")))) {
997 for (i = 0; pan_plugins[i].factory; ++i) {
998 if (prop->value() == pan_plugins[i].name) {
1001 /* note that we assume that all the stream panners
1002 are of the same type. pretty good
1003 assumption, but it's still an assumption.
1006 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1009 if (sp->set_state (**niter, version) == 0) {
1010 _streampanners.push_back (sp);
1018 if (!pan_plugins[i].factory) {
1019 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1025 error << _("panner plugin node has no type information!")
1033 reset (outputs.size (), num_panners);
1034 /* don't try to do old-school automation loading if it wasn't marked as existing */
1036 if ((prop = node.property (X_("automation")))) {
1038 /* automation path is relative */
1040 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1043 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1044 if ((*niter)->name() == X_("Automation")) {
1045 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1053 Panner::touching () const
1055 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1056 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1065 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1067 AngularVector delta;
1068 AngularVector new_position;
1070 delta = orig.get_position() - a;
1072 if (_link_direction == SameDirection) {
1074 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1076 (*i)->set_position (a, true);
1078 new_position = (*i)->get_position() + delta;
1079 (*i)->set_position (new_position, true);
1085 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1087 (*i)->set_position (a, true);
1089 new_position = (*i)->get_position() - delta;
1090 (*i)->set_position (new_position, true);
1097 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
1099 if (outbufs.count().n_audio() == 0) {
1100 // Don't want to lose audio...
1101 assert(inbufs.count().n_audio() == 0);
1105 // We shouldn't be called in the first place...
1106 assert(!bypassed());
1110 if (outbufs.count().n_audio() == 1) {
1112 AudioBuffer& dst = outbufs.get_audio(0);
1114 if (gain_coeff == 0.0f) {
1116 /* only one output, and gain was zero, so make it silent */
1118 dst.silence (nframes);
1120 } else if (gain_coeff == 1.0f){
1122 /* mix all buffers into the output */
1125 dst.read_from(inbufs.get_audio(0), nframes);
1127 // accumulate starting with the second
1128 if (inbufs.count().n_audio() > 0) {
1129 BufferSet::audio_iterator i = inbufs.audio_begin();
1130 for (++i; i != inbufs.audio_end(); ++i) {
1131 dst.merge_from(*i, nframes);
1137 /* mix all buffers into the output, scaling them all by the gain */
1140 dst.read_from(inbufs.get_audio(0), nframes);
1142 // accumulate (with gain) starting with the second
1143 if (inbufs.count().n_audio() > 0) {
1144 BufferSet::audio_iterator i = inbufs.audio_begin();
1145 for (++i; i != inbufs.audio_end(); ++i) {
1146 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1155 /* the terrible silence ... */
1156 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1157 i->silence(nframes);
1160 BufferSet::audio_iterator i = inbufs.audio_begin();
1162 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1163 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1168 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
1170 if (outbufs.count().n_audio() == 0) {
1171 // Failing to deliver audio we were asked to deliver is a bug
1172 assert(inbufs.count().n_audio() == 0);
1176 // We shouldn't be called in the first place...
1177 assert(!bypassed());
1180 // If we shouldn't play automation defer to distribute_no_automation
1181 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1184 gain_t gain_coeff = 1.0;
1186 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1187 gain_coeff = speed_quietning;
1190 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1194 // Otherwise.. let the automation flow, baby
1196 if (outbufs.count().n_audio() == 1) {
1198 AudioBuffer& dst = outbufs.get_audio(0);
1200 // FIXME: apply gain automation?
1203 dst.read_from(inbufs.get_audio(0), nframes);
1205 // accumulate starting with the second
1206 BufferSet::audio_iterator i = inbufs.audio_begin();
1207 for (++i; i != inbufs.audio_end(); ++i) {
1208 dst.merge_from(*i, nframes);
1214 // More than 1 output, we should have 1 panner for each input
1215 //assert(_streampanners.size() == inbufs.count().n_audio());
1217 /* the terrible silence ... */
1218 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1219 i->silence(nframes);
1222 BufferSet::audio_iterator i = inbufs.audio_begin();
1223 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1224 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1228 /* old school automation handling */
1232 Panner::set_name (string str)
1234 automation_path = Glib::build_filename(_session.automation_dir(),
1235 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1243 uint32_t linecnt = 0;
1245 vector<StreamPanner*>::iterator sp;
1246 LocaleGuard lg (X_("POSIX"));
1248 if (automation_path.length() == 0) {
1252 if (access (automation_path.c_str(), F_OK)) {
1256 ifstream in (automation_path.c_str());
1259 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1260 automation_path, strerror (errno))
1265 sp = _streampanners.begin();
1267 while (in.getline (line, sizeof(line), '\n')) {
1269 if (++linecnt == 1) {
1270 if (memcmp (line, X_("version"), 7) == 0) {
1271 if (sscanf (line, "version %f", &version) != 1) {
1272 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1276 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1277 automation_path, line) << endmsg;
1284 if (strlen (line) == 0 || line[0] == '#') {
1288 if (strcmp (line, "begin") == 0) {
1290 if (sp == _streampanners.end()) {
1291 error << string_compose (_("too many panner states found in pan automation file %1"),
1297 if ((*sp)->load (in, automation_path, linecnt)) {
1309 Panner::set_mono (bool yn)
1316 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1317 (*i)->set_mono (yn);
1322 Panner::value_as_string (double v)
1324 if (Panner::equivalent (v, 0.5)) {
1326 } else if (equivalent (v, 0)) {
1328 } else if (equivalent (v, 1)) {
1330 } else if (v < 0.5) {
1332 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1336 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1344 Panner::setup_speakers (uint32_t nouts)
1348 /* top, bottom kind-of-left & bottom kind-of-right */
1349 outputs.push_back (AngularVector (90.0, 0.0));
1350 outputs.push_back (AngularVector (215.0, 0,0));
1351 outputs.push_back (AngularVector (335.0, 0,0));
1354 /* clockwise from top left */
1355 outputs.push_back (AngularVector (135.0, 0.0));
1356 outputs.push_back (AngularVector (45.0, 0.0));
1357 outputs.push_back (AngularVector (335.0, 0.0));
1358 outputs.push_back (AngularVector (215.0, 0.0));
1363 double degree_step = 360.0 / nouts;
1367 /* even number of speakers? make sure the top two are either side of "top".
1368 otherwise, just start at the "top" (90.0 degrees) and rotate around
1372 deg = 90.0 - degree_step;
1376 for (n = 0; n < nouts; ++n, deg += degree_step) {
1377 outputs.push_back (Output (AngularVector (deg, 0.0)));
1382 Speakers& speakers (_session.get_speakers());
1384 speakers.clear_speakers ();
1386 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1387 speakers.add_speaker ((*o).position);