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/error.h"
38 #include "pbd/failed_constructor.h"
39 #include "pbd/xml++.h"
40 #include "pbd/enumwriter.h"
42 #include "evoral/Curve.hpp"
44 #include "ardour/session.h"
45 #include "ardour/panner.h"
46 #include "ardour/utils.h"
47 #include "ardour/audio_buffer.h"
49 #include "ardour/runtime_functions.h"
50 #include "ardour/buffer_set.h"
51 #include "ardour/audio_buffer.h"
52 #include "ardour/vbap.h"
56 #include "pbd/mathfix.h"
59 using namespace ARDOUR;
62 float Panner::current_automation_version_number = 1.0;
64 string EqualPowerStereoPanner::name = "Equal Power Stereo";
66 /* this is a default mapper of control values to a pan position
67 others can be imagined.
70 static double direct_control_to_stereo_pan (double fract)
72 return BaseStereoPanner::lr_fract_to_azimuth (fract);
75 StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
78 assert (param.type() != NullAutomation);
83 /* get our AutomationControl from our parent Panner, creating it if required */
84 _control = boost::dynamic_pointer_cast<AutomationControl> (parent.control (param, true));
87 StreamPanner::~StreamPanner ()
92 StreamPanner::set_mono (bool yn)
101 Panner::PanControllable::set_value (double val)
103 panner.streampanner (parameter().id()).set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
104 AutomationControl::set_value(val);
108 Panner::PanControllable::get_value (void) const
110 return AutomationControl::get_value();
114 StreamPanner::set_muted (bool yn)
123 StreamPanner::set_position (const AngularVector& av, bool link_call)
125 if (!link_call && parent.linked()) {
126 parent.set_position (av, *this);
133 _control->Changed ();
138 StreamPanner::set_state (const XMLNode& node, int /*version*/)
140 const XMLProperty* prop;
141 XMLNodeConstIterator iter;
143 if ((prop = node.property (X_("muted")))) {
144 set_muted (string_is_affirmative (prop->value()));
147 if ((prop = node.property (X_("mono")))) {
148 set_mono (string_is_affirmative (prop->value()));
155 StreamPanner::add_state (XMLNode& node)
157 node.add_property (X_("muted"), (muted() ? "yes" : "no"));
158 node.add_property (X_("mono"), (_mono ? "yes" : "no"));
162 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
165 /* we're in mono mode, so just pan the input to all outputs equally */
166 int const N = parent.nouts ();
167 for (int i = 0; i < N; ++i) {
168 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
171 /* normal mode, call the `real' distribute method */
172 do_distribute (src, obufs, gain_coeff, nframes);
177 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
178 nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
181 /* we're in mono mode, so just pan the input to all outputs equally */
182 int const N = parent.nouts ();
183 for (int i = 0; i < N; ++i) {
184 mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
187 /* normal mode, call the `real' distribute method */
188 do_distribute_automated (src, obufs, start, end, nframes, buffers);
194 /*---------------------------------------------------------------------- */
196 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
197 : StreamPanner (p, param)
201 , right_interp (right)
205 BaseStereoPanner::~BaseStereoPanner ()
210 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
213 LocaleGuard lg (X_("POSIX"));
215 _control->list()->clear ();
217 while (in.getline (line, sizeof (line), '\n')) {
223 if (strcmp (line, "end") == 0) {
227 if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
228 warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
232 _control->list()->fast_simple_add (when, value);
235 /* now that we are done loading */
237 ((AutomationList*)_control->list().get())->StateChanged ();
243 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
245 assert(obufs.count().n_audio() == 2);
255 Sample* const src = srcbuf.data();
259 dst = obufs.get_audio(0).data();
261 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
263 /* we've moving the pan by an appreciable amount, so we must
264 interpolate over 64 frames or nframes, whichever is smaller */
266 nframes_t const limit = min ((nframes_t)64, nframes);
269 delta = -(delta / (float) (limit));
271 for (n = 0; n < limit; n++) {
272 left_interp = left_interp + delta;
273 left = left_interp + 0.9 * (left - left_interp);
274 dst[n] += src[n] * left * gain_coeff;
277 /* then pan the rest of the buffer; no need for interpolation for this bit */
279 pan = left * gain_coeff;
281 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
288 if ((pan = (left * gain_coeff)) != 1.0f) {
292 /* pan is 1 but also not 0, so we must do it "properly" */
294 mix_buffers_with_gain(dst,src,nframes,pan);
296 /* mark that we wrote into the buffer */
304 /* pan is 1 so we can just copy the input samples straight in */
306 mix_buffers_no_gain(dst,src,nframes);
308 /* mark that we wrote into the buffer */
316 dst = obufs.get_audio(1).data();
318 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
320 /* we're moving the pan by an appreciable amount, so we must
321 interpolate over 64 frames or nframes, whichever is smaller */
323 nframes_t const limit = min ((nframes_t)64, nframes);
326 delta = -(delta / (float) (limit));
328 for (n = 0; n < limit; n++) {
329 right_interp = right_interp + delta;
330 right = right_interp + 0.9 * (right - right_interp);
331 dst[n] += src[n] * right * gain_coeff;
334 /* then pan the rest of the buffer, no need for interpolation for this bit */
336 pan = right * gain_coeff;
338 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
340 /* XXX it would be nice to mark the buffer as written to */
344 right = desired_right;
345 right_interp = right;
347 if ((pan = (right * gain_coeff)) != 1.0f) {
351 /* pan is not 1 but also not 0, so we must do it "properly" */
353 mix_buffers_with_gain(dst,src,nframes,pan);
355 /* XXX it would be nice to mark the buffer as written to */
360 /* pan is 1 so we can just copy the input samples straight in */
362 mix_buffers_no_gain(dst,src,nframes);
364 /* XXX it would be nice to mark the buffer as written to */
369 /*---------------------------------------------------------------------- */
371 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
372 : BaseStereoPanner (p, param)
377 right = desired_right;
379 right_interp = right;
382 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
387 EqualPowerStereoPanner::update ()
389 /* it would be very nice to split this out into a virtual function
390 that can be accessed from BaseStereoPanner and used in do_distribute_automated().
392 but the place where its used in do_distribute_automated() is a tight inner loop,
393 and making "nframes" virtual function calls to compute values is an absurd
397 /* x == 0 => hard left = 180.0 degrees
398 x == 1 => hard right = 0.0 degrees
401 double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
403 float const panR = _x;
404 float const panL = 1 - panR;
406 float const pan_law_attenuation = -3.0f;
407 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
409 desired_left = panL * (scale * panL + 1.0f - scale);
410 desired_right = panR * (scale * panR + 1.0f - scale);
412 _effective_angles = _angles;
413 //_control->set_value(x);
417 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
418 nframes_t start, nframes_t end, nframes_t nframes,
421 assert (obufs.count().n_audio() == 2);
425 Sample* const src = srcbuf.data();
427 /* fetch positional data */
429 if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
432 do_distribute (srcbuf, obufs, 1.0, nframes);
437 /* store effective pan position. do this even if we are muted */
440 _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
447 /* apply pan law to convert positional data into pan coefficients for
451 const float pan_law_attenuation = -3.0f;
452 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
454 for (nframes_t n = 0; n < nframes; ++n) {
456 float const panR = buffers[0][n];
457 float const panL = 1 - panR;
459 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
460 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
465 dst = obufs.get_audio(0).data();
468 for (nframes_t n = 0; n < nframes; ++n) {
469 dst[n] += src[n] * pbuf[n];
472 /* XXX it would be nice to mark the buffer as written to */
476 dst = obufs.get_audio(1).data();
479 for (nframes_t n = 0; n < nframes; ++n) {
480 dst[n] += src[n] * pbuf[n];
483 /* XXX it would be nice to mark the buffer as written to */
487 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
489 return new EqualPowerStereoPanner (parent, param);
493 EqualPowerStereoPanner::get_state (void)
499 EqualPowerStereoPanner::state (bool /*full_state*/)
501 XMLNode* root = new XMLNode ("StreamPanner");
503 LocaleGuard lg (X_("POSIX"));
505 snprintf (buf, sizeof (buf), "%.12g", _angles.azi);
506 root->add_property (X_("azimuth"), buf);
507 root->add_property (X_("type"), EqualPowerStereoPanner::name);
509 // XXX: dont save automation here... its part of the automatable panner now.
511 StreamPanner::add_state (*root);
513 root->add_child_nocopy (_control->get_state ());
519 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
521 const XMLProperty* prop;
522 LocaleGuard lg (X_("POSIX"));
524 if ((prop = node.property (X_("azimuth")))) {
525 AngularVector a (atof (prop->value().c_str()), 0.0);
526 set_position (a, true);
527 } else if ((prop = node.property (X_("x")))) {
528 /* old school cartesian positioning */
530 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
531 set_position (a, true);
534 StreamPanner::set_state (node, version);
536 for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
538 if ((*iter)->name() == X_("Controllable")) {
539 if ((prop = (*iter)->property("name")) != 0 && prop->value() == "panner") {
540 _control->set_state (**iter, version);
543 } else if ((*iter)->name() == X_("Automation")) {
545 _control->alist()->set_state (*((*iter)->children().front()), version);
547 if (_control->alist()->automation_state() != Off) {
548 double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
549 set_position (AngularVector (degrees, 0.0));
557 Panner::Panner (string name, Session& s)
558 : SessionObject (s, name)
561 //set_name_old_auto (name);
565 _link_direction = SameDirection;
575 Panner::set_linked (bool yn)
579 _session.set_dirty ();
580 LinkStateChanged (); /* EMIT SIGNAL */
585 Panner::set_link_direction (LinkDirection ld)
587 if (ld != _link_direction) {
588 _link_direction = ld;
589 _session.set_dirty ();
590 LinkStateChanged (); /* EMIT SIGNAL */
596 Panner::set_bypassed (bool yn)
598 if (yn != _bypassed) {
606 Panner::reset_to_default ()
608 vector<float> positions;
610 switch (outputs.size()) {
616 if (outputs.size() == 2) {
618 switch (_streampanners.size()) {
620 a.azi = 90.0; /* "front" or "top", in degrees */
621 _streampanners.front()->set_position (a);
622 _streampanners.front()->pan_control()->list()->reset_default (0.5);
626 a.azi = 180.0; /* "left", in degrees */
627 _streampanners.front()->set_position (a);
628 _streampanners.front()->pan_control()->list()->reset_default (0.0);
629 a.azi = 0.0; /* "right", in degrees */
630 _streampanners.back()->set_position (a);
631 _streampanners.back()->pan_control()->list()->reset_default (1.0);
638 vector<Output>::iterator o;
639 vector<StreamPanner*>::iterator p;
641 for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
642 (*p)->set_position ((*o).position);
647 Panner::reset_streampanner (uint32_t which)
651 if (which >= _streampanners.size() || which >= outputs.size()) {
655 switch (outputs.size()) {
661 switch (_streampanners.size()) {
663 /* stereo out, 1 stream, default = middle */
664 a.azi = 90.0; /* "front" or "top", in degrees */
665 _streampanners.front()->set_position (a);
666 _streampanners.front()->pan_control()->list()->reset_default (0.5);
669 /* stereo out, 2 streams, default = hard left/right */
671 a.azi = 180.0; /* "left", in degrees */
672 _streampanners.front()->set_position (a);
673 _streampanners.front()->pan_control()->list()->reset_default (0.0);
675 a.azi = 0.0; /* "right", in degrees */
676 _streampanners.back()->set_position (a);
677 _streampanners.back()->pan_control()->list()->reset_default (1.0);
684 _streampanners[which]->set_position (outputs[which].position);
689 * Reset the panner with a given number of outs and panners (and hence inputs)
691 * \param nouts Number of outputs.
692 * \param npans Number of panners.
695 Panner::reset (uint32_t nouts, uint32_t npans)
698 bool changed = false;
699 bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
701 /* if new and old config don't need panning, or if
702 the config hasn't changed, we're done.
705 if (do_not_and_did_not_need_panning ||
706 ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
710 n = _streampanners.size();
725 /* no need for panning with less than 2 outputs */
727 Changed (); /* EMIT SIGNAL */
734 /* XXX: this can never happen */
738 /* XXX: this can never happen */
739 fatal << _("programming error:")
740 << X_("Panner::reset() called with a single output")
746 outputs.push_back (Output (AngularVector (180.0, 0.0)));
747 outputs.push_back (Output (AngularVector (0.0, 0,0)));
748 for (n = 0; n < npans; ++n) {
749 _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
754 setup_speakers (nouts);
755 for (n = 0; n < npans; ++n) {
756 _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
761 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
765 /* must emit Changed here, otherwise the changes to the pan_control below raise further
766 signals which the GUI is not prepared for until it has seen the Changed here.
770 Changed (); /* EMIT SIGNAL */
773 /* force hard left/right panning in a common case: 2in/2out
776 if (npans == 2 && outputs.size() == 2) {
778 /* Do this only if we changed configuration, or our configuration
779 appears to be the default set up (zero degrees)
785 left = _streampanners.front()->get_position ();
786 right = _streampanners.back()->get_position ();
788 if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
790 _streampanners.front()->set_position (AngularVector (180.0, 0.0));
791 _streampanners.front()->pan_control()->list()->reset_default (0.0);
793 _streampanners.back()->set_position (AngularVector (0.0, 0.0));
794 _streampanners.back()->pan_control()->list()->reset_default (1.0);
797 } else if (npans > 1 && outputs.size() > 2) {
799 /* 2d panning: spread signals equally around a circle */
801 double degree_step = 360.0 / nouts;
804 /* even number of signals? make sure the top two are either side of "top".
805 otherwise, just start at the "top" (90.0 degrees) and rotate around
809 deg = 90.0 - degree_step;
814 for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
815 (*x)->set_position (AngularVector (deg, 0.0));
822 Panner::remove (uint32_t which)
824 vector<StreamPanner*>::iterator i;
825 for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
827 if (i != _streampanners.end()) {
829 _streampanners.erase (i);
834 /** Remove all our StreamPanners */
836 Panner::clear_panners ()
838 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
842 _streampanners.clear ();
846 Panner::set_automation_style (AutoStyle style)
848 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
849 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
851 _session.set_dirty ();
855 Panner::set_automation_state (AutoState state)
857 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
858 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
860 _session.set_dirty ();
864 Panner::automation_state () const
866 boost::shared_ptr<AutomationList> l;
868 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
870 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
874 return l ? l->automation_state() : Off;
878 Panner::automation_style () const
880 boost::shared_ptr<AutomationList> l;
882 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
884 l = boost::dynamic_pointer_cast<AutomationList>(control->list());
888 return l ? l->automation_style() : Absolute;
894 StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
897 PanPlugins pan_plugins[] = {
898 { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
899 { VBAPanner::name, 3, VBAPanner::factory },
900 { string (""), 0, 0 }
904 Panner::get_state (void)
910 Panner::state (bool full)
912 XMLNode* node = new XMLNode ("Panner");
916 node->add_property (X_("linked"), (_linked ? "yes" : "no"));
917 node->add_property (X_("link_direction"), enum_2_string (_link_direction));
918 node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
920 for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
921 XMLNode* onode = new XMLNode (X_("Output"));
922 snprintf (buf, sizeof (buf), "%.12g", (*o).position.azi);
923 onode->add_property (X_("azimuth"), buf);
924 snprintf (buf, sizeof (buf), "%.12g", (*o).position.ele);
925 onode->add_property (X_("elevation"), buf);
926 node->add_child_nocopy (*onode);
929 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
930 node->add_child_nocopy ((*i)->state (full));
933 node->add_child_nocopy (get_automation_xml_state ());
939 Panner::set_state (const XMLNode& node, int version)
942 XMLNodeConstIterator niter;
943 const XMLProperty *prop;
945 uint32_t num_panners = 0;
947 LocaleGuard lg (X_("POSIX"));
951 ChanCount ins = ChanCount::ZERO;
952 ChanCount outs = ChanCount::ZERO;
954 // XXX: this might not be necessary anymore
957 if ((prop = node.property (X_("linked"))) != 0) {
958 set_linked (string_is_affirmative (prop->value()));
961 if ((prop = node.property (X_("bypassed"))) != 0) {
962 set_bypassed (string_is_affirmative (prop->value()));
965 if ((prop = node.property (X_("link_direction"))) != 0) {
966 LinkDirection ld; /* here to provide type information */
967 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
970 nlist = node.children();
972 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
973 if ((*niter)->name() == X_("Output")) {
977 if ((prop = (*niter)->property (X_("azimuth")))) {
978 sscanf (prop->value().c_str(), "%lg", &a.azi);
979 } else if ((prop = (*niter)->property (X_("x")))) {
980 /* old school cartesian */
981 a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
984 if ((prop = (*niter)->property (X_("elevation")))) {
985 sscanf (prop->value().c_str(), "%lg", &a.ele);
988 outputs.push_back (Output (a));
992 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
994 if ((*niter)->name() == X_("StreamPanner")) {
996 if ((prop = (*niter)->property (X_("type")))) {
998 for (i = 0; pan_plugins[i].factory; ++i) {
999 if (prop->value() == pan_plugins[i].name) {
1002 /* note that we assume that all the stream panners
1003 are of the same type. pretty good
1004 assumption, but it's still an assumption.
1007 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
1010 if (sp->set_state (**niter, version) == 0) {
1011 _streampanners.push_back (sp);
1019 if (!pan_plugins[i].factory) {
1020 error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1026 error << _("panner plugin node has no type information!")
1034 reset (outputs.size (), num_panners);
1035 /* don't try to do old-school automation loading if it wasn't marked as existing */
1037 if ((prop = node.property (X_("automation")))) {
1039 /* automation path is relative */
1041 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1044 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1045 if ((*niter)->name() == X_("Automation")) {
1046 set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
1054 Panner::touching () const
1056 for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1057 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1066 Panner::set_position (const AngularVector& a, StreamPanner& orig)
1068 AngularVector delta;
1069 AngularVector new_position;
1071 delta = orig.get_position() - a;
1073 if (_link_direction == SameDirection) {
1075 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1077 (*i)->set_position (a, true);
1079 new_position = (*i)->get_position() + delta;
1080 (*i)->set_position (new_position, true);
1086 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1088 (*i)->set_position (a, true);
1090 new_position = (*i)->get_position() - delta;
1091 (*i)->set_position (new_position, true);
1098 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
1100 if (outbufs.count().n_audio() == 0) {
1101 // Don't want to lose audio...
1102 assert(inbufs.count().n_audio() == 0);
1106 // We shouldn't be called in the first place...
1107 assert(!bypassed());
1111 if (outbufs.count().n_audio() == 1) {
1113 AudioBuffer& dst = outbufs.get_audio(0);
1115 if (gain_coeff == 0.0f) {
1117 /* only one output, and gain was zero, so make it silent */
1119 dst.silence (nframes);
1121 } else if (gain_coeff == 1.0f){
1123 /* mix all buffers into the output */
1126 dst.read_from(inbufs.get_audio(0), nframes);
1128 // accumulate starting with the second
1129 if (inbufs.count().n_audio() > 0) {
1130 BufferSet::audio_iterator i = inbufs.audio_begin();
1131 for (++i; i != inbufs.audio_end(); ++i) {
1132 dst.merge_from(*i, nframes);
1138 /* mix all buffers into the output, scaling them all by the gain */
1141 dst.read_from(inbufs.get_audio(0), nframes);
1143 // accumulate (with gain) starting with the second
1144 if (inbufs.count().n_audio() > 0) {
1145 BufferSet::audio_iterator i = inbufs.audio_begin();
1146 for (++i; i != inbufs.audio_end(); ++i) {
1147 dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1156 /* the terrible silence ... */
1157 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1158 i->silence(nframes);
1161 BufferSet::audio_iterator i = inbufs.audio_begin();
1163 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1164 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1169 Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
1171 if (outbufs.count().n_audio() == 0) {
1172 // Failing to deliver audio we were asked to deliver is a bug
1173 assert(inbufs.count().n_audio() == 0);
1177 // We shouldn't be called in the first place...
1178 assert(!bypassed());
1181 // If we shouldn't play automation defer to distribute_no_automation
1182 if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1185 gain_t gain_coeff = 1.0;
1187 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
1188 gain_coeff = speed_quietning;
1191 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1195 // Otherwise.. let the automation flow, baby
1197 if (outbufs.count().n_audio() == 1) {
1199 AudioBuffer& dst = outbufs.get_audio(0);
1201 // FIXME: apply gain automation?
1204 dst.read_from(inbufs.get_audio(0), nframes);
1206 // accumulate starting with the second
1207 BufferSet::audio_iterator i = inbufs.audio_begin();
1208 for (++i; i != inbufs.audio_end(); ++i) {
1209 dst.merge_from(*i, nframes);
1215 // More than 1 output, we should have 1 panner for each input
1216 //assert(_streampanners.size() == inbufs.count().n_audio());
1218 /* the terrible silence ... */
1219 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1220 i->silence(nframes);
1223 BufferSet::audio_iterator i = inbufs.audio_begin();
1224 for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1225 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1229 /* old school automation handling */
1233 Panner::set_name (string str)
1235 automation_path = Glib::build_filename(_session.automation_dir(),
1236 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1244 uint32_t linecnt = 0;
1246 vector<StreamPanner*>::iterator sp;
1247 LocaleGuard lg (X_("POSIX"));
1249 if (automation_path.length() == 0) {
1253 if (access (automation_path.c_str(), F_OK)) {
1257 ifstream in (automation_path.c_str());
1260 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1261 automation_path, strerror (errno))
1266 sp = _streampanners.begin();
1268 while (in.getline (line, sizeof(line), '\n')) {
1270 if (++linecnt == 1) {
1271 if (memcmp (line, X_("version"), 7) == 0) {
1272 if (sscanf (line, "version %f", &version) != 1) {
1273 error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1277 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1278 automation_path, line) << endmsg;
1285 if (strlen (line) == 0 || line[0] == '#') {
1289 if (strcmp (line, "begin") == 0) {
1291 if (sp == _streampanners.end()) {
1292 error << string_compose (_("too many panner states found in pan automation file %1"),
1298 if ((*sp)->load (in, automation_path, linecnt)) {
1310 Panner::set_mono (bool yn)
1317 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1318 (*i)->set_mono (yn);
1323 Panner::value_as_string (double v)
1325 if (Panner::equivalent (v, 0.5)) {
1327 } else if (equivalent (v, 0)) {
1329 } else if (equivalent (v, 1)) {
1331 } else if (v < 0.5) {
1333 s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
1337 s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
1345 Panner::setup_speakers (uint32_t nouts)
1349 /* top, bottom kind-of-left & bottom kind-of-right */
1350 outputs.push_back (AngularVector (90.0, 0.0));
1351 outputs.push_back (AngularVector (215.0, 0,0));
1352 outputs.push_back (AngularVector (335.0, 0,0));
1355 /* clockwise from top left */
1356 outputs.push_back (AngularVector (135.0, 0.0));
1357 outputs.push_back (AngularVector (45.0, 0.0));
1358 outputs.push_back (AngularVector (335.0, 0.0));
1359 outputs.push_back (AngularVector (215.0, 0.0));
1364 double degree_step = 360.0 / nouts;
1368 /* even number of speakers? make sure the top two are either side of "top".
1369 otherwise, just start at the "top" (90.0 degrees) and rotate around
1373 deg = 90.0 - degree_step;
1377 for (n = 0; n < nouts; ++n, deg += degree_step) {
1378 outputs.push_back (Output (AngularVector (deg, 0.0)));
1383 Speakers& speakers (_session.get_speakers());
1385 speakers.clear_speakers ();
1387 for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1388 speakers.add_speaker ((*o).position);