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 if (p.set_stereo_pan (val, p.width_control()->get_value())) {
120 AutomationControl::set_value(val);
126 if (p.set_stereo_pan (p.direction_control()->get_value(), val)) {
127 AutomationControl::set_value(val);
132 streampanner.set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
133 AutomationControl::set_value(val);
140 StreamPanner::PanControllable::get_value (void) const
142 return AutomationControl::get_value();
146 StreamPanner::set_muted (bool yn)
155 StreamPanner::set_position (const AngularVector& av, bool link_call)
157 if (!link_call && parent.linked()) {
158 parent.set_position (av, *this);
165 _control->Changed ();
170 StreamPanner::set_state (const XMLNode& node, int version)
172 const XMLProperty* prop;
173 XMLNodeConstIterator iter;
175 if ((prop = node.property (X_("muted")))) {
176 set_muted (string_is_affirmative (prop->value()));
179 if ((prop = node.property (X_("mono")))) {
180 set_mono (string_is_affirmative (prop->value()));
183 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
184 if ((*iter)->name() == Controllable::xml_node_name) {
185 if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
186 _control->set_state (**iter, version);
195 StreamPanner::get_state ()
197 XMLNode* node = new XMLNode (X_("StreamPanner"));
198 node->add_property (X_("muted"), (muted() ? "yes" : "no"));
199 node->add_property (X_("mono"), (_mono ? "yes" : "no"));
200 node->add_child_nocopy (_control->get_state ());
205 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
208 /* we're in mono mode, so just pan the input to all outputs equally */
209 int const N = parent.nouts ();
210 for (int i = 0; i < N; ++i) {
211 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
214 /* normal mode, call the `real' distribute method */
215 do_distribute (src, obufs, gain_coeff, nframes);
220 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
221 nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
224 /* we're in mono mode, so just pan the input to all outputs equally */
225 int const N = parent.nouts ();
226 for (int i = 0; i < N; ++i) {
227 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
230 /* normal mode, call the `real' distribute method */
231 do_distribute_automated (src, obufs, start, end, nframes, buffers);
237 /*---------------------------------------------------------------------- */
239 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
240 : StreamPanner (p, param)
244 , right_interp (right)
248 BaseStereoPanner::~BaseStereoPanner ()
253 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
256 LocaleGuard lg (X_("POSIX"));
258 _control->list()->clear ();
260 while (in.getline (line, sizeof (line), '\n')) {
266 if (strcmp (line, "end") == 0) {
270 if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
271 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
275 _control->list()->fast_simple_add (when, value);
278 /* now that we are done loading */
280 ((AutomationList*)_control->list().get())->StateChanged ();
286 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
288 assert(obufs.count().n_audio() == 2);
298 Sample* const src = srcbuf.data();
302 dst = obufs.get_audio(0).data();
304 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
306 /* we've moving the pan by an appreciable amount, so we must
307 interpolate over 64 frames or nframes, whichever is smaller */
309 nframes_t const limit = min ((nframes_t)64, nframes);
312 delta = -(delta / (float) (limit));
314 for (n = 0; n < limit; n++) {
315 left_interp = left_interp + delta;
316 left = left_interp + 0.9 * (left - left_interp);
317 dst[n] += src[n] * left * gain_coeff;
320 /* then pan the rest of the buffer; no need for interpolation for this bit */
322 pan = left * gain_coeff;
324 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
331 if ((pan = (left * gain_coeff)) != 1.0f) {
335 /* pan is 1 but also not 0, so we must do it "properly" */
337 mix_buffers_with_gain(dst,src,nframes,pan);
339 /* mark that we wrote into the buffer */
347 /* pan is 1 so we can just copy the input samples straight in */
349 mix_buffers_no_gain(dst,src,nframes);
351 /* mark that we wrote into the buffer */
359 dst = obufs.get_audio(1).data();
361 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
363 /* we're moving the pan by an appreciable amount, so we must
364 interpolate over 64 frames or nframes, whichever is smaller */
366 nframes_t const limit = min ((nframes_t)64, nframes);
369 delta = -(delta / (float) (limit));
371 for (n = 0; n < limit; n++) {
372 right_interp = right_interp + delta;
373 right = right_interp + 0.9 * (right - right_interp);
374 dst[n] += src[n] * right * gain_coeff;
377 /* then pan the rest of the buffer, no need for interpolation for this bit */
379 pan = right * gain_coeff;
381 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
383 /* XXX it would be nice to mark the buffer as written to */
387 right = desired_right;
388 right_interp = right;
390 if ((pan = (right * gain_coeff)) != 1.0f) {
394 /* pan is not 1 but also not 0, so we must do it "properly" */
396 mix_buffers_with_gain(dst,src,nframes,pan);
398 /* XXX it would be nice to mark the buffer as written to */
403 /* pan is 1 so we can just copy the input samples straight in */
405 mix_buffers_no_gain(dst,src,nframes);
407 /* XXX it would be nice to mark the buffer as written to */
412 /*---------------------------------------------------------------------- */
414 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
415 : BaseStereoPanner (p, param)
420 right = desired_right;
422 right_interp = right;
425 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
430 EqualPowerStereoPanner::update ()
432 /* it would be very nice to split this out into a virtual function
433 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
435 but the place where its used in do_distribute_automated() is a tight inner loop,
436 and making "nframes" virtual function calls to compute values is an absurd
440 /* x == 0 => hard left = 180.0 degrees
441 x == 1 => hard right = 0.0 degrees
444 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
446 float const panR = _x;
447 float const panL = 1 - panR;
449 float const pan_law_attenuation = -3.0f;
450 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
452 desired_left = panL * (scale * panL + 1.0f - scale);
453 desired_right = panR * (scale * panR + 1.0f - scale);
455 _effective_angles = _angles;
456 //_control->set_value(x);
460 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
461 nframes_t start, nframes_t end, nframes_t nframes,
464 assert (obufs.count().n_audio() == 2);
468 Sample* const src = srcbuf.data();
470 /* fetch positional data */
472 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
475 do_distribute (srcbuf, obufs, 1.0, nframes);
480 /* store effective pan position. do this even if we are muted */
483 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
490 /* apply pan law to convert positional data into pan coefficients for
494 const float pan_law_attenuation = -3.0f;
495 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
497 for (nframes_t n = 0; n < nframes; ++n) {
499 float const panR = buffers[0][n];
500 float const panL = 1 - panR;
502 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
503 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
508 dst = obufs.get_audio(0).data();
511 for (nframes_t n = 0; n < nframes; ++n) {
512 dst[n] += src[n] * pbuf[n];
515 /* XXX it would be nice to mark the buffer as written to */
519 dst = obufs.get_audio(1).data();
522 for (nframes_t n = 0; n < nframes; ++n) {
523 dst[n] += src[n] * pbuf[n];
526 /* XXX it would be nice to mark the buffer as written to */
530 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
532 return new EqualPowerStereoPanner (parent, param);
536 EqualPowerStereoPanner::get_state (void)
542 EqualPowerStereoPanner::state (bool /*full_state*/)
544 XMLNode& root (StreamPanner::get_state ());
545 root.add_property (X_("type"), EqualPowerStereoPanner::name);
550 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
552 LocaleGuard lg (X_("POSIX"));
554 StreamPanner::set_state (node, version);
556 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
558 if ((*iter)->name() == X_("Automation")) {
560 _control->alist()->set_state (*((*iter)->children().front()), version);
562 if (_control->alist()->automation_state() != Off) {
563 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
564 set_position (AngularVector (degrees, 0.0));
572 Panner::Panner (string name, Session& s)
573 : SessionObject (s, name)
576 //set_name_old_auto (name);
580 _link_direction = SameDirection;
590 Panner::set_linked (bool yn)
594 _session.set_dirty ();
595 LinkStateChanged (); /* EMIT SIGNAL */
600 Panner::set_link_direction (LinkDirection ld)
602 if (ld != _link_direction) {
603 _link_direction = ld;
604 _session.set_dirty ();
605 LinkStateChanged (); /* EMIT SIGNAL */
611 Panner::set_bypassed (bool yn)
613 if (yn != _bypassed) {
621 Panner::reset_to_default ()
623 vector<float> positions;
625 switch (outputs.size()) {
631 if (outputs.size() == 2) {
633 switch (_streampanners.size()) {
635 a.azi = 90.0; /* "front" or "top", in degrees */
636 _streampanners.front()->set_position (a);
637 _streampanners.front()->pan_control()->list()->reset_default (0.5);
641 a.azi = 180.0; /* "left", in degrees */
642 _streampanners.front()->set_position (a);
643 _streampanners.front()->pan_control()->list()->reset_default (0.0);
644 a.azi = 0.0; /* "right", in degrees */
645 _streampanners.back()->set_position (a);
646 _streampanners.back()->pan_control()->list()->reset_default (1.0);
653 vector<Output>::iterator o;
654 vector<StreamPanner*>::iterator p;
656 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
657 (*p)->set_position ((*o).position);
662 Panner::reset_streampanner (uint32_t which)
666 if (which >= _streampanners.size() || which >= outputs.size()) {
670 switch (outputs.size()) {
676 switch (_streampanners.size()) {
678 /* stereo out, 1 stream, default = middle */
679 a.azi = 90.0; /* "front" or "top", in degrees */
680 _streampanners.front()->set_position (a);
681 _streampanners.front()->pan_control()->list()->reset_default (0.5);
684 /* stereo out, 2 streams, default = hard left/right */
686 a.azi = 180.0; /* "left", in degrees */
687 _streampanners.front()->set_position (a);
688 _streampanners.front()->pan_control()->list()->reset_default (0.0);
690 a.azi = 0.0; /* "right", in degrees */
691 _streampanners.back()->set_position (a);
692 _streampanners.back()->pan_control()->list()->reset_default (1.0);
699 _streampanners[which]->set_position (outputs[which].position);
704 * Reset the panner with a given number of outs and panners (and hence inputs)
706 * \param nouts Number of outputs.
707 * \param npans Number of panners.
710 Panner::reset (uint32_t nouts, uint32_t npans)
713 bool changed = false;
714 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
716 /* if new and old config don't need panning, or if
717 the config hasn't changed, we're done.
720 if (do_not_and_did_not_need_panning ||
721 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
725 n = _streampanners.size();
740 /* no need for panning with less than 2 outputs */
742 Changed (); /* EMIT SIGNAL */
749 /* XXX: this can never happen */
753 /* XXX: this can never happen */
754 fatal << _("programming error:")
755 << X_("Panner::reset() called with a single output")
761 outputs.push_back (Output (AngularVector (180.0, 0.0)));
762 outputs.push_back (Output (AngularVector (0.0, 0,0)));
763 for (n = 0; n < npans; ++n) {
764 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
769 setup_speakers (nouts);
770 for (n = 0; n < npans; ++n) {
771 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
776 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
780 setup_meta_controls ();
782 /* must emit Changed here, otherwise the changes to the pan_control below raise further
783 signals which the GUI is not prepared for until it has seen the Changed here.
787 Changed (); /* EMIT SIGNAL */
790 /* force hard left/right panning in a common case: 2in/2out
793 if (npans == 2 && outputs.size() == 2) {
795 /* Do this only if we changed configuration, or our configuration
796 appears to be the default set up (zero degrees)
802 left = _streampanners.front()->get_position ();
803 right = _streampanners.back()->get_position ();
805 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
807 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
808 _streampanners.front()->pan_control()->list()->reset_default (0.0);
810 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
811 _streampanners.back()->pan_control()->list()->reset_default (1.0);
814 } else if (npans > 1 && outputs.size() > 2) {
816 /* 2d panning: spread signals equally around a circle */
818 double degree_step = 360.0 / nouts;
821 /* even number of signals? make sure the top two are either side of "top".
822 otherwise, just start at the "top" (90.0 degrees) and rotate around
826 deg = 90.0 - degree_step;
831 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
832 (*x)->set_position (AngularVector (deg, 0.0));
839 Panner::remove (uint32_t which)
841 vector<StreamPanner*>::iterator i;
842 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
844 if (i != _streampanners.end()) {
846 _streampanners.erase (i);
851 /** Remove all our StreamPanners */
853 Panner::clear_panners ()
855 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
859 _streampanners.clear ();
863 Panner::set_automation_style (AutoStyle style)
865 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
866 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
868 _session.set_dirty ();
872 Panner::set_automation_state (AutoState state)
874 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
875 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
877 _session.set_dirty ();
881 Panner::automation_state () const
883 boost::shared_ptr<AutomationList> l;
885 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
887 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
891 return l ? l->automation_state() : Off;
895 Panner::automation_style () const
897 boost::shared_ptr<AutomationList> l;
899 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
901 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
905 return l ? l->automation_style() : Absolute;
911 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
914 PanPlugins pan_plugins[] = {
915 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
916 { VBAPanner::name, 3, VBAPanner::factory },
917 { string (""), 0, 0 }
921 Panner::get_state (void)
927 Panner::state (bool full)
929 XMLNode* node = new XMLNode ("Panner");
933 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
934 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
935 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
936 snprintf (buf, sizeof (buf), "%zd", outputs.size());
937 node->add_property (X_("outputs"), buf);
939 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
940 node->add_child_nocopy ((*i)->state (full));
943 node->add_child_nocopy (get_automation_xml_state ());
949 Panner::set_state (const XMLNode& node, int version)
951 XMLNodeList nlist = node.children ();
952 XMLNodeConstIterator niter;
953 const XMLProperty *prop;
955 uint32_t num_panners = 0;
957 LocaleGuard lg (X_("POSIX"));
961 ChanCount ins = ChanCount::ZERO;
962 ChanCount outs = ChanCount::ZERO;
964 // XXX: this might not be necessary anymore
967 if ((prop = node.property (X_("linked"))) != 0) {
968 set_linked (string_is_affirmative (prop->value()));
971 if ((prop = node.property (X_("bypassed"))) != 0) {
972 set_bypassed (string_is_affirmative (prop->value()));
975 if ((prop = node.property (X_("link_direction"))) != 0) {
976 LinkDirection ld; /* here to provide type information */
977 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
980 if ((prop = node.property (X_("outputs"))) != 0) {
981 uint32_t n = atoi (prop->value());
984 AngularVector a; // value is irrelevant
985 outputs.push_back (Output (a));
992 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
993 if ((*niter)->name() == X_("Output")) {
997 if ((prop = (*niter)->property (X_("azimuth")))) {
998 sscanf (prop->value().c_str(), "%lg", &a.azi);
999 } else if ((prop = (*niter)->property (X_("x")))) {
1000 /* old school cartesian */
1001 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
1004 if ((prop = (*niter)->property (X_("elevation")))) {
1005 sscanf (prop->value().c_str(), "%lg", &a.ele);
1008 outputs.push_back (Output (a));
1013 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1015 if ((*niter)->name() == X_("StreamPanner")) {
1017 if ((prop = (*niter)->property (X_("type")))) {
1019 for (i = 0; pan_plugins[i].factory; ++i) {
1020 if (prop->value() == pan_plugins[i].name) {
1023 /* note that we assume that all the stream panners
1024 are of the same type. pretty good
1025 assumption, but it's still an assumption.
1028 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1031 if (sp->set_state (**niter, version) == 0) {
1032 _streampanners.push_back (sp);
1039 if (!pan_plugins[i].factory) {
1040 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1046 error << _("panner plugin node has no type information!")
1054 setup_meta_controls ();
1056 reset (outputs.size (), num_panners);
1058 /* don't try to do old-school automation loading if it wasn't marked as existing */
1060 if ((prop = node.property (X_("automation")))) {
1062 /* automation path is relative */
1064 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1067 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1068 if ((*niter)->name() == X_("Automation")) {
1069 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1077 Panner::touching () const
1079 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1080 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1089 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1091 AngularVector delta;
1092 AngularVector new_position;
1094 delta = orig.get_position() - a;
1096 if (_link_direction == SameDirection) {
1098 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1100 (*i)->set_position (a, true);
1102 new_position = (*i)->get_position() + delta;
1103 (*i)->set_position (new_position, true);
1109 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1111 (*i)->set_position (a, true);
1113 new_position = (*i)->get_position() - delta;
1114 (*i)->set_position (new_position, true);
1121 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
1123 if (outbufs.count().n_audio() == 0) {
1124 // Don't want to lose audio...
1125 assert(inbufs.count().n_audio() == 0);
1129 // We shouldn't be called in the first place...
1130 assert(!bypassed());
1134 if (outbufs.count().n_audio() == 1) {
1136 AudioBuffer& dst = outbufs.get_audio(0);
1138 if (gain_coeff == 0.0f) {
1140 /* only one output, and gain was zero, so make it silent */
1142 dst.silence (nframes);
1144 } else if (gain_coeff == 1.0f){
1146 /* mix all buffers into the output */
1149 dst.read_from(inbufs.get_audio(0), nframes);
1151 // accumulate starting with the second
1152 if (inbufs.count().n_audio() > 0) {
1153 BufferSet::audio_iterator i = inbufs.audio_begin();
1154 for (++i; i != inbufs.audio_end(); ++i) {
1155 dst.merge_from(*i, nframes);
1161 /* mix all buffers into the output, scaling them all by the gain */
1164 dst.read_from(inbufs.get_audio(0), nframes);
1166 // accumulate (with gain) starting with the second
1167 if (inbufs.count().n_audio() > 0) {
1168 BufferSet::audio_iterator i = inbufs.audio_begin();
1169 for (++i; i != inbufs.audio_end(); ++i) {
1170 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1179 /* the terrible silence ... */
1180 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1181 i->silence(nframes);
1184 BufferSet::audio_iterator i = inbufs.audio_begin();
1186 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1187 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1192 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
1194 if (outbufs.count().n_audio() == 0) {
1195 // Failing to deliver audio we were asked to deliver is a bug
1196 assert(inbufs.count().n_audio() == 0);
1200 // We shouldn't be called in the first place...
1201 assert(!bypassed());
1204 // If we shouldn't play automation defer to distribute_no_automation
1205 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1208 gain_t gain_coeff = 1.0;
1210 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1211 gain_coeff = speed_quietning;
1214 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1218 // Otherwise.. let the automation flow, baby
1220 if (outbufs.count().n_audio() == 1) {
1222 AudioBuffer& dst = outbufs.get_audio(0);
1224 // FIXME: apply gain automation?
1227 dst.read_from(inbufs.get_audio(0), nframes);
1229 // accumulate starting with the second
1230 BufferSet::audio_iterator i = inbufs.audio_begin();
1231 for (++i; i != inbufs.audio_end(); ++i) {
1232 dst.merge_from(*i, nframes);
1238 // More than 1 output, we should have 1 panner for each input
1239 //assert(_streampanners.size() == inbufs.count().n_audio());
1241 /* the terrible silence ... */
1242 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1243 i->silence(nframes);
1246 BufferSet::audio_iterator i = inbufs.audio_begin();
1247 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1248 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1252 /* old school automation handling */
1256 Panner::set_name (string str)
1258 automation_path = Glib::build_filename(_session.automation_dir(),
1259 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1267 uint32_t linecnt = 0;
1269 vector<StreamPanner*>::iterator sp;
1270 LocaleGuard lg (X_("POSIX"));
1272 if (automation_path.length() == 0) {
1276 if (access (automation_path.c_str(), F_OK)) {
1280 ifstream in (automation_path.c_str());
1283 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1284 automation_path, strerror (errno))
1289 sp = _streampanners.begin();
1291 while (in.getline (line, sizeof(line), '\n')) {
1293 if (++linecnt == 1) {
1294 if (memcmp (line, X_("version"), 7) == 0) {
1295 if (sscanf (line, "version %f", &version) != 1) {
1296 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1300 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1301 automation_path, line) << endmsg;
1308 if (strlen (line) == 0 || line[0] == '#') {
1312 if (strcmp (line, "begin") == 0) {
1314 if (sp == _streampanners.end()) {
1315 error << string_compose (_("too many panner states found in pan automation file %1"),
1321 if ((*sp)->load (in, automation_path, linecnt)) {
1333 Panner::set_mono (bool yn)
1340 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1341 (*i)->set_mono (yn);
1346 Panner::value_as_string (double v)
1348 if (Panner::equivalent (v, 0.5)) {
1350 } else if (equivalent (v, 0)) {
1352 } else if (equivalent (v, 1)) {
1354 } else if (v < 0.5) {
1356 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1360 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1368 Panner::setup_speakers (uint32_t nouts)
1372 /* top, bottom kind-of-left & bottom kind-of-right */
1373 outputs.push_back (AngularVector (90.0, 0.0));
1374 outputs.push_back (AngularVector (215.0, 0,0));
1375 outputs.push_back (AngularVector (335.0, 0,0));
1378 /* clockwise from top left */
1379 outputs.push_back (AngularVector (135.0, 0.0));
1380 outputs.push_back (AngularVector (45.0, 0.0));
1381 outputs.push_back (AngularVector (335.0, 0.0));
1382 outputs.push_back (AngularVector (215.0, 0.0));
1387 double degree_step = 360.0 / nouts;
1391 /* even number of speakers? make sure the top two are either side of "top".
1392 otherwise, just start at the "top" (90.0 degrees) and rotate around
1396 deg = 90.0 - degree_step;
1400 for (n = 0; n < nouts; ++n, deg += degree_step) {
1401 outputs.push_back (Output (AngularVector (deg, 0.0)));
1406 Speakers& speakers (_session.get_speakers());
1408 speakers.clear_speakers ();
1410 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1411 speakers.add_speaker ((*o).position);
1416 Panner::set_stereo_width (double val)
1418 boost::shared_ptr<AutomationControl> dc = direction_control();
1420 dc->set_value (val);
1425 Panner::set_stereo_position (double val)
1427 boost::shared_ptr<AutomationControl> wc = width_control();
1429 wc->set_value (val);
1434 Panner::set_stereo_pan (double direction_as_lr_fract, double width)
1436 AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0);
1437 /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing
1438 whether its "reversed" (i.e. left signal pans right and right signal pans left).
1439 full width = 180 degrees
1441 double spread = 2.0 * fabs(width/2.0) * 180.0;
1442 double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */
1443 double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */
1444 bool move_left = true;
1445 bool move_right = true;
1447 assert (_streampanners.size() > 1);
1450 swap (l_pos, r_pos);
1453 /* if the new right position is less than or equal to 180 (hard left) and the left panner
1454 is already there, we're not moving the left signal.
1457 if (l_pos > 180.0 && _streampanners[0]->get_position().azi == 180.0) {
1461 /* if the new right position is less than or equal to zero (hard right) and the right panner
1462 is already there, we're not moving the right signal.
1465 if (r_pos <= 0.0 && _streampanners[1]->get_position().azi == 0.0) {
1469 l_pos = max (min (l_pos, 180.0), 0.0);
1470 r_pos = max (min (r_pos, 180.0), 0.0);
1472 if (move_left && move_right) {
1473 _streampanners[0]->set_position (AngularVector (l_pos, 0.0));
1474 _streampanners[1]->set_position (AngularVector (r_pos, 0.0));
1477 return move_left && move_right;
1481 Panner::setup_meta_controls ()
1483 if (_streampanners.size() != 2 || outputs.size() != 2) {
1487 /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
1489 The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
1490 but they keep us out of the ordinary range of pan-related parameters.
1493 Evoral::Parameter lr_param (PanAutomation, 0, 100);
1494 Evoral::Parameter width_param (PanAutomation, 0, 200);
1495 boost::shared_ptr<AutomationControl> wc;
1496 boost::shared_ptr<AutomationControl> dc;
1498 if (!automation_control (lr_param)) {
1499 dc.reset (new StreamPanner::PanControllable (_session, _("lr"), *_streampanners.front(), lr_param));
1503 if (!automation_control (width_param)) {
1504 wc.reset (new StreamPanner::PanControllable (_session, _("width"), *_streampanners.front(), width_param));
1508 dc->set_value (0.5);
1509 wc->set_value (1.0); // full width
1514 Panner::describe_parameter (Evoral::Parameter param)
1516 if (param.type() == PanAutomation) {
1517 switch (param.id()) {
1519 return "Pan:position";
1523 if (_streampanners.size() == 2) {
1524 switch (param.id()) {
1532 ss << "Pan " << param.id() + 1;
1538 return Automatable::describe_parameter (param);