85c8e403e94cfcc9d24d3b59befd99bbca248dc2
[ardour.git] / libs / ardour / panner.cc
1 /*
2     Copyright (C) 2004 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22
23 #include <cmath>
24 #include <cerrno>
25 #include <fstream>
26 #include <cstdlib>
27 #include <string>
28 #include <cstdio>
29 #include <locale.h>
30 #include <unistd.h>
31 #include <float.h>
32
33 #include <glibmm.h>
34
35 #include "pbd/error.h"
36 #include "pbd/failed_constructor.h"
37 #include "pbd/xml++.h"
38 #include "pbd/enumwriter.h"
39
40 #include "evoral/Curve.hpp"
41
42 #include "ardour/session.h"
43 #include "ardour/panner.h"
44 #include "ardour/utils.h"
45 #include "ardour/audio_buffer.h"
46
47 #include "ardour/runtime_functions.h"
48 #include "ardour/buffer_set.h"
49 #include "ardour/audio_buffer.h"
50
51 #include "i18n.h"
52
53 #include "pbd/mathfix.h"
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58
59 float Panner::current_automation_version_number = 1.0;
60
61 string EqualPowerStereoPanner::name = "Equal Power Stereo";
62 string Multi2dPanner::name = "Multiple (2D)";
63
64 /* this is a default mapper of  control values to a pan position
65    others can be imagined.
66 */
67
68 static pan_t direct_control_to_pan (double fract) {
69         return fract;
70 }
71
72
73 //static double direct_pan_to_control (pan_t val) {
74 //      return val;
75 //}
76
77 StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
78         : parent (p)
79 {
80         assert(param.type() != NullAutomation);
81
82         _muted = false;
83         _mono = false;
84
85         _control = boost::dynamic_pointer_cast<AutomationControl>( parent.control( param, true ) );
86
87         x = 0.5;
88         y = 0.5;
89         z = 0.5;
90 }
91
92 StreamPanner::~StreamPanner ()
93 {
94 }
95
96 void
97 StreamPanner::set_mono (bool yn)
98 {
99         if (yn != _mono) {
100                 _mono = yn;
101                 StateChanged ();
102         }
103 }
104
105 void
106 Panner::PanControllable::set_value (float val)
107 {
108         panner.streampanner(parameter().id()).set_position (direct_control_to_pan (val));
109         AutomationControl::set_value(val);
110 }
111
112 float
113 Panner::PanControllable::get_value (void) const
114 {
115         return AutomationControl::get_value();
116 }
117
118 void
119 StreamPanner::set_muted (bool yn)
120 {
121         if (yn != _muted) {
122                 _muted = yn;
123                 StateChanged ();
124         }
125 }
126
127 void
128 StreamPanner::set_position (float xpos, bool link_call)
129 {
130         if (!link_call && parent.linked()) {
131                 parent.set_position (xpos, *this);
132         }
133
134         if (x != xpos) {
135                 x = xpos;
136                 update ();
137                 Changed ();
138                 _control->Changed ();
139         }
140 }
141
142 void
143 StreamPanner::set_position (float xpos, float ypos, bool link_call)
144 {
145         if (!link_call && parent.linked()) {
146                 parent.set_position (xpos, ypos, *this);
147         }
148
149         if (x != xpos || y != ypos) {
150
151                 x = xpos;
152                 y = ypos;
153                 update ();
154                 Changed ();
155         }
156 }
157
158 void
159 StreamPanner::set_position (float xpos, float ypos, float zpos, bool link_call)
160 {
161         if (!link_call && parent.linked()) {
162                 parent.set_position (xpos, ypos, zpos, *this);
163         }
164
165         if (x != xpos || y != ypos || z != zpos) {
166                 x = xpos;
167                 y = ypos;
168                 z = zpos;
169                 update ();
170                 Changed ();
171         }
172 }
173
174 int
175 StreamPanner::set_state (const XMLNode& node, int /*version*/)
176 {
177         const XMLProperty* prop;
178         XMLNodeConstIterator iter;
179
180         if ((prop = node.property (X_("muted")))) {
181                 set_muted (string_is_affirmative (prop->value()));
182         }
183
184         return 0;
185 }
186
187 void
188 StreamPanner::add_state (XMLNode& node)
189 {
190         node.add_property (X_("muted"), (muted() ? "yes" : "no"));
191 }
192
193 void
194 StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
195 {
196         if (_mono) {
197                 /* we're in mono mode, so just pan the input to all outputs equally */
198                 int const N = parent.nouts ();
199                 for (int i = 0; i < N; ++i) {
200                         mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
201                 }
202         } else {
203                 /* normal mode, call the `real' distribute method */
204                 do_distribute (src, obufs, gain_coeff, nframes);
205         }
206 }
207
208 void
209 StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
210                                     nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
211 {
212         if (_mono) {
213                 /* we're in mono mode, so just pan the input to all outputs equally */
214                 int const N = parent.nouts ();
215                 for (int i = 0; i < N; ++i) {
216                         mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
217                 }
218         } else {
219                 /* normal mode, call the `real' distribute method */
220                 do_distribute_automated (src, obufs, start, end, nframes, buffers);
221         }
222         
223 }
224
225
226 /*---------------------------------------------------------------------- */
227
228 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
229         : StreamPanner (p, param)
230 {
231 }
232
233 BaseStereoPanner::~BaseStereoPanner ()
234 {
235 }
236
237 int
238 BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
239 {
240         char line[128];
241         LocaleGuard lg (X_("POSIX"));
242
243         _control->list()->clear ();
244
245         while (in.getline (line, sizeof (line), '\n')) {
246                 nframes_t when;
247                 double value;
248
249                 ++linecnt;
250
251                 if (strcmp (line, "end") == 0) {
252                         break;
253                 }
254
255                 if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
256                         warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
257                         continue;
258                 }
259
260                 _control->list()->fast_simple_add (when, value);
261         }
262
263         /* now that we are done loading */
264
265         ((AutomationList*)_control->list().get())->StateChanged ();
266
267         return 0;
268 }
269
270 void
271 BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
272 {
273         assert(obufs.count().n_audio() == 2);
274
275         pan_t delta;
276         Sample* dst;
277         pan_t pan;
278
279         if (_muted) {
280                 return;
281         }
282
283         Sample* const src = srcbuf.data();
284
285         /* LEFT */
286
287         dst = obufs.get_audio(0).data();
288
289         if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
290
291                 /* we've moving the pan by an appreciable amount, so we must
292                    interpolate over 64 frames or nframes, whichever is smaller */
293
294                 nframes_t const limit = min ((nframes_t)64, nframes);
295                 nframes_t n;
296
297                 delta = -(delta / (float) (limit));
298
299                 for (n = 0; n < limit; n++) {
300                         left_interp = left_interp + delta;
301                         left = left_interp + 0.9 * (left - left_interp);
302                         dst[n] += src[n] * left * gain_coeff;
303                 }
304
305                 /* then pan the rest of the buffer; no need for interpolation for this bit */
306
307                 pan = left * gain_coeff;
308
309                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
310
311         } else {
312
313                 left = desired_left;
314                 left_interp = left;
315
316                 if ((pan = (left * gain_coeff)) != 1.0f) {
317
318                         if (pan != 0.0f) {
319
320                                 /* pan is 1 but also not 0, so we must do it "properly" */
321
322                                 mix_buffers_with_gain(dst,src,nframes,pan);
323
324                                 /* mark that we wrote into the buffer */
325
326                                 // obufs[0] = 0;
327
328                         }
329
330                 } else {
331
332                         /* pan is 1 so we can just copy the input samples straight in */
333
334                         mix_buffers_no_gain(dst,src,nframes);
335
336                         /* mark that we wrote into the buffer */
337
338                         // obufs[0] = 0;
339                 }
340         }
341
342         /* RIGHT */
343
344         dst = obufs.get_audio(1).data();
345
346         if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
347
348                 /* we're moving the pan by an appreciable amount, so we must
349                    interpolate over 64 frames or nframes, whichever is smaller */
350
351                 nframes_t const limit = min ((nframes_t)64, nframes);
352                 nframes_t n;
353
354                 delta = -(delta / (float) (limit));
355
356                 for (n = 0; n < limit; n++) {
357                         right_interp = right_interp + delta;
358                         right = right_interp + 0.9 * (right - right_interp);
359                         dst[n] += src[n] * right * gain_coeff;
360                 }
361
362                 /* then pan the rest of the buffer, no need for interpolation for this bit */
363
364                 pan = right * gain_coeff;
365
366                 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
367
368                 /* XXX it would be nice to mark the buffer as written to */
369
370         } else {
371
372                 right = desired_right;
373                 right_interp = right;
374
375                 if ((pan = (right * gain_coeff)) != 1.0f) {
376
377                         if (pan != 0.0f) {
378
379                                 /* pan is not 1 but also not 0, so we must do it "properly" */
380                                 
381                                 mix_buffers_with_gain(dst,src,nframes,pan);
382
383                                 /* XXX it would be nice to mark the buffer as written to */
384                         }
385
386                 } else {
387
388                         /* pan is 1 so we can just copy the input samples straight in */
389                         
390                         mix_buffers_no_gain(dst,src,nframes);
391
392                         /* XXX it would be nice to mark the buffer as written to */
393                 }
394         }
395 }
396
397 /*---------------------------------------------------------------------- */
398
399 EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
400         : BaseStereoPanner (p, param)
401 {
402         update ();
403
404         left = desired_left;
405         right = desired_right;
406         left_interp = left;
407         right_interp = right;
408 }
409
410 EqualPowerStereoPanner::~EqualPowerStereoPanner ()
411 {
412 }
413
414 void
415 EqualPowerStereoPanner::update ()
416 {
417         /* it would be very nice to split this out into a virtual function
418            that can be accessed from BaseStereoPanner and used in do_distribute_automated().
419
420            but the place where its used in do_distribute_automated() is a tight inner loop,
421            and making "nframes" virtual function calls to compute values is an absurd
422            overhead.
423         */
424
425         /* x == 0 => hard left
426            x == 1 => hard right
427         */
428
429         float panR = x;
430         float panL = 1 - panR;
431
432         const float pan_law_attenuation = -3.0f;
433         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
434
435         desired_left = panL * (scale * panL + 1.0f - scale);
436         desired_right = panR * (scale * panR + 1.0f - scale);
437
438         effective_x = x;
439         //_control->set_value(x);
440 }
441
442 void
443 EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
444                                                  nframes_t start, nframes_t end, nframes_t nframes,
445                                                  pan_t** buffers)
446 {
447         assert(obufs.count().n_audio() == 2);
448
449         Sample* dst;
450         pan_t* pbuf;
451         Sample* const src = srcbuf.data();
452
453         /* fetch positional data */
454
455         if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
456                 /* fallback */
457                 if (!_muted) {
458                         do_distribute (srcbuf, obufs, 1.0, nframes);
459                 }
460                 return;
461         }
462
463         /* store effective pan position. do this even if we are muted */
464
465         if (nframes > 0) {
466                 effective_x = buffers[0][nframes-1];
467         }
468
469         if (_muted) {
470                 return;
471         }
472
473         /* apply pan law to convert positional data into pan coefficients for
474            each buffer (output)
475         */
476
477         const float pan_law_attenuation = -3.0f;
478         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
479
480         for (nframes_t n = 0; n < nframes; ++n) {
481
482                 float panR = buffers[0][n];
483                 float panL = 1 - panR;
484
485                 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
486                 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
487         }
488
489         /* LEFT */
490
491         dst = obufs.get_audio(0).data();
492         pbuf = buffers[0];
493
494         for (nframes_t n = 0; n < nframes; ++n) {
495                 dst[n] += src[n] * pbuf[n];
496         }
497
498         /* XXX it would be nice to mark the buffer as written to */
499
500         /* RIGHT */
501
502         dst = obufs.get_audio(1).data();
503         pbuf = buffers[1];
504
505         for (nframes_t n = 0; n < nframes; ++n) {
506                 dst[n] += src[n] * pbuf[n];
507         }
508
509         /* XXX it would be nice to mark the buffer as written to */
510 }
511
512 StreamPanner*
513 EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param)
514 {
515         return new EqualPowerStereoPanner (parent, param);
516 }
517
518 XMLNode&
519 EqualPowerStereoPanner::get_state (void)
520 {
521         return state (true);
522 }
523
524 XMLNode&
525 EqualPowerStereoPanner::state (bool /*full_state*/)
526 {
527         XMLNode* root = new XMLNode ("StreamPanner");
528         char buf[64];
529         LocaleGuard lg (X_("POSIX"));
530
531         snprintf (buf, sizeof (buf), "%.12g", x);
532         root->add_property (X_("x"), buf);
533         root->add_property (X_("type"), EqualPowerStereoPanner::name);
534
535         // XXX: dont save automation here... its part of the automatable panner now.
536
537         StreamPanner::add_state (*root);
538
539         root->add_child_nocopy (_control->get_state ());
540
541         return *root;
542 }
543
544 int
545 EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
546 {
547         const XMLProperty* prop;
548         float pos;
549         LocaleGuard lg (X_("POSIX"));
550
551         if ((prop = node.property (X_("x")))) {
552                 pos = atof (prop->value().c_str());
553                 set_position (pos, true);
554         }
555
556         StreamPanner::set_state (node, version);
557
558         for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
559
560                 if ((*iter)->name() == X_("Controllable")) {
561                         if ((prop = (*iter)->property("name")) != 0 && prop->value() == "panner") {
562                                 _control->set_state (**iter, version);
563                         }
564
565                 } else if ((*iter)->name() == X_("Automation")) {
566
567                         _control->alist()->set_state (*((*iter)->children().front()), version);
568
569                         if (_control->alist()->automation_state() != Off) {
570                                 set_position (_control->list()->eval (parent.session().transport_frame()));
571                         }
572                 }
573         }
574
575         return 0;
576 }
577
578 /*----------------------------------------------------------------------*/
579
580 Multi2dPanner::Multi2dPanner (Panner& p, Evoral::Parameter param)
581         : StreamPanner (p, param)
582 {
583         update ();
584 }
585
586 Multi2dPanner::~Multi2dPanner ()
587 {
588 }
589
590 void
591 Multi2dPanner::update ()
592 {
593         static const float BIAS = FLT_MIN;
594         uint32_t i;
595         uint32_t nouts = parent.outputs.size();
596         float dsq[nouts];
597         float f, fr;
598         vector<pan_t> pans;
599
600         f = 0.0f;
601
602         for (i = 0; i < nouts; i++) {
603                 dsq[i] = ((x - parent.outputs[i].x) * (x - parent.outputs[i].x) + (y - parent.outputs[i].y) * (y - parent.outputs[i].y) + BIAS);
604                 if (dsq[i] < 0.0) {
605                         dsq[i] = 0.0;
606                 }
607                 f += dsq[i] * dsq[i];
608         }
609 #ifdef __APPLE__
610         // terrible hack to support OSX < 10.3.9 builds
611         fr = (float) (1.0 / sqrt((double)f));
612 #else
613         fr = 1.0 / sqrtf(f);
614 #endif
615         for (i = 0; i < nouts; ++i) {
616                 parent.outputs[i].desired_pan = 1.0f - (dsq[i] * fr);
617         }
618
619         effective_x = x;
620 }
621
622 void
623 Multi2dPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
624 {
625         Sample* dst;
626         pan_t pan;
627         vector<Panner::Output>::iterator o;
628         uint32_t n;
629
630         if (_muted) {
631                 return;
632         }
633
634         Sample* const src = srcbuf.data();
635
636
637         for (n = 0, o = parent.outputs.begin(); o != parent.outputs.end(); ++o, ++n) {
638
639                 dst = obufs.get_audio(n).data();
640
641 #ifdef CAN_INTERP
642                 if (fabsf ((delta = (left_interp - desired_left))) > 0.002) { // about 1 degree of arc
643
644                         /* interpolate over 64 frames or nframes, whichever is smaller */
645
646                         nframes_t limit = min ((nframes_t)64, nframes);
647                         nframes_t n;
648
649                         delta = -(delta / (float) (limit));
650
651                         for (n = 0; n < limit; n++) {
652                                 left_interp = left_interp + delta;
653                                 left = left_interp + 0.9 * (left - left_interp);
654                                 dst[n] += src[n] * left * gain_coeff;
655                         }
656
657                         pan = left * gain_coeff;
658                         mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
659
660                 } else {
661
662 #else
663                         pan = (*o).desired_pan;
664
665                         if ((pan *= gain_coeff) != 1.0f) {
666
667                                 if (pan != 0.0f) {
668                                         mix_buffers_with_gain(dst,src,nframes,pan);
669                                 }
670                         } else {
671                                         mix_buffers_no_gain(dst,src,nframes);
672                         }
673 #endif
674 #ifdef CAN_INTERP
675                 }
676 #endif
677         }
678
679         return;
680 }
681
682 void
683 Multi2dPanner::do_distribute_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
684                                         nframes_t /*start*/, nframes_t /*end*/, nframes_t /*nframes*/,
685                                         pan_t** /*buffers*/)
686 {
687         if (_muted) {
688                 return;
689         }
690
691         /* what ? */
692
693         return;
694 }
695
696 StreamPanner*
697 Multi2dPanner::factory (Panner& p, Evoral::Parameter param)
698 {
699         return new Multi2dPanner (p, param);
700 }
701
702 int
703 Multi2dPanner::load (istream& /*in*/, string /*path*/, uint32_t& /*linecnt*/)
704 {
705         return 0;
706 }
707
708 XMLNode&
709 Multi2dPanner::get_state (void)
710 {
711         return state (true);
712 }
713
714 XMLNode&
715 Multi2dPanner::state (bool /*full_state*/)
716 {
717         XMLNode* root = new XMLNode ("StreamPanner");
718         char buf[64];
719         LocaleGuard lg (X_("POSIX"));
720
721         snprintf (buf, sizeof (buf), "%.12g", x);
722         root->add_property (X_("x"), buf);
723         snprintf (buf, sizeof (buf), "%.12g", y);
724         root->add_property (X_("y"), buf);
725         root->add_property (X_("type"), Multi2dPanner::name);
726
727         /* XXX no meaningful automation yet */
728
729         return *root;
730 }
731
732 int
733 Multi2dPanner::set_state (const XMLNode& node, int /*version*/)
734 {
735         const XMLProperty* prop;
736         float newx,newy;
737         LocaleGuard lg (X_("POSIX"));
738
739         newx = -1;
740         newy = -1;
741
742         if ((prop = node.property (X_("x")))) {
743                 newx = atof (prop->value().c_str());
744         }
745
746         if ((prop = node.property (X_("y")))) {
747                 newy = atof (prop->value().c_str());
748         }
749
750         if (x < 0 || y < 0) {
751                 error << _("badly-formed positional data for Multi2dPanner - ignored")
752                       << endmsg;
753                 return -1;
754         }
755
756         set_position (newx, newy);
757         return 0;
758 }
759
760 /*---------------------------------------------------------------------- */
761
762 Panner::Panner (string name, Session& s)
763         : SessionObject (s, name)
764         , AutomatableControls (s)
765 {
766         //set_name_old_auto (name);
767         set_name (name);
768
769         _linked = false;
770         _link_direction = SameDirection;
771         _bypassed = false;
772         _mono = false;
773 }
774
775 Panner::~Panner ()
776 {
777 }
778
779 void
780 Panner::set_linked (bool yn)
781 {
782         if (yn != _linked) {
783                 _linked = yn;
784                 _session.set_dirty ();
785                 LinkStateChanged (); /* EMIT SIGNAL */
786         }
787 }
788
789 void
790 Panner::set_link_direction (LinkDirection ld)
791 {
792         if (ld != _link_direction) {
793                 _link_direction = ld;
794                 _session.set_dirty ();
795                 LinkStateChanged (); /* EMIT SIGNAL */
796         }
797 }
798
799
800 void
801 Panner::set_bypassed (bool yn)
802 {
803         if (yn != _bypassed) {
804                 _bypassed = yn;
805                 StateChanged ();
806         }
807 }
808
809
810 void
811 Panner::reset_to_default ()
812 {
813         vector<float> positions;
814
815         switch (outputs.size()) {
816         case 0:
817         case 1:
818                 return;
819         }
820
821         if (outputs.size() == 2) {
822                 switch (_streampanners.size()) {
823                 case 1:
824                         _streampanners.front()->set_position (0.5);
825                         _streampanners.front()->pan_control()->list()->reset_default (0.5);
826                         return;
827                         break;
828                 case 2:
829                         _streampanners.front()->set_position (0.0);
830                         _streampanners.front()->pan_control()->list()->reset_default (0.0);
831                         _streampanners.back()->set_position (1.0);
832                         _streampanners.back()->pan_control()->list()->reset_default (1.0);
833                         return;
834                 default:
835                         break;
836                 }
837         }
838
839         vector<Output>::iterator o;
840         vector<StreamPanner*>::iterator p;
841
842         for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
843                 (*p)->set_position ((*o).x, (*o).y);
844         }
845 }
846
847 void
848 Panner::reset_streampanner (uint32_t which)
849 {
850         if (which >= _streampanners.size() || which >= outputs.size()) {
851                 return;
852         }
853
854         switch (outputs.size()) {
855         case 0:
856         case 1:
857                 return;
858
859         case 2:
860                 switch (_streampanners.size()) {
861                 case 1:
862                         /* stereo out, 1 stream, default = middle */
863                         _streampanners.front()->set_position (0.5);
864                         _streampanners.front()->pan_control()->list()->reset_default (0.5);
865                         break;
866                 case 2:
867                         /* stereo out, 2 streams, default = hard left/right */
868                         if (which == 0) {
869                                 _streampanners.front()->set_position (0.0);
870                                 _streampanners.front()->pan_control()->list()->reset_default (0.0);
871                         } else {
872                                 _streampanners.back()->set_position (1.0);
873                                 _streampanners.back()->pan_control()->list()->reset_default (1.0);
874                         }
875                         break;
876                 }
877                 return;
878
879         default:
880                 _streampanners[which]->set_position (outputs[which].x, outputs[which].y);
881         }
882 }
883
884 /**
885  *    Reset the panner with a given number of outs and panners (and hence inputs)
886  *
887  *    \param nouts Number of outputs.
888  *    \param npans Number of panners.
889  */
890 void
891 Panner::reset (uint32_t nouts, uint32_t npans)
892 {
893         uint32_t n;
894         bool changed = false;
895         bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
896
897         //cout << "Reset panner for " << nouts << " " << npans << "\n";
898
899         /* if new and old config don't need panning, or if
900            the config hasn't changed, we're done.
901         */
902
903         if (do_not_and_did_not_need_panning ||
904             ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
905                 return;
906         }
907
908         n = _streampanners.size();
909         clear_panners ();
910
911         if (n != npans) {
912                 changed = true;
913         }
914
915         n = outputs.size();
916         outputs.clear ();
917
918         if (n != nouts) {
919                 changed = true;
920         }
921
922         if (nouts < 2) {
923                 /* no need for panning with less than 2 outputs */
924                 goto send_changed;
925         }
926
927         switch (nouts) {
928         case 0:
929                 /* XXX: this can never happen */
930                 break;
931
932         case 1:
933                 /* XXX: this can never happen */
934                 fatal << _("programming error:")
935                       << X_("Panner::reset() called with a single output")
936                       << endmsg;
937                 /*NOTREACHED*/
938                 break;
939
940         case 2: // line
941                 outputs.push_back (Output (0, 0));
942                 outputs.push_back (Output (1.0, 0));
943
944                 for (n = 0; n < npans; ++n) {
945                         _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
946                 }
947                 break;
948
949         case 3: // triangle
950                 outputs.push_back (Output  (0.5, 0));
951                 outputs.push_back (Output  (0, 1.0));
952                 outputs.push_back (Output  (1.0, 1.0));
953
954                 for (n = 0; n < npans; ++n) {
955                         _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
956                 }
957
958                 break;
959
960         case 4: // square
961                 outputs.push_back (Output  (0, 0));
962                 outputs.push_back (Output  (1.0, 0));
963                 outputs.push_back (Output  (1.0, 1.0));
964                 outputs.push_back (Output  (0, 1.0));
965
966                 for (n = 0; n < npans; ++n) {
967                         _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
968                 }
969
970                 break;
971
972         case 5: //square+offcenter center
973                 outputs.push_back (Output  (0, 0));
974                 outputs.push_back (Output  (1.0, 0));
975                 outputs.push_back (Output  (1.0, 1.0));
976                 outputs.push_back (Output  (0, 1.0));
977                 outputs.push_back (Output  (0.5, 0.75));
978
979                 for (n = 0; n < npans; ++n) {
980                         _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
981                 }
982
983                 break;
984
985         default:
986                 /* XXX horrible placement. FIXME */
987                 for (n = 0; n < nouts; ++n) {
988                         outputs.push_back (Output (0.1 * n, 0.1 * n));
989                 }
990
991                 for (n = 0; n < npans; ++n) {
992                         _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
993                 }
994
995                 break;
996         }
997
998         for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
999                 (*x)->update ();
1000         }
1001
1002         /* force hard left/right panning in a common case: 2in/2out
1003         */
1004
1005         if (npans == 2 && outputs.size() == 2) {
1006
1007                 /* Do this only if we changed configuration, or our configuration
1008                    appears to be the default set up (center).
1009                 */
1010
1011                 float left;
1012                 float right;
1013
1014                 _streampanners.front()->get_position (left);
1015                 _streampanners.back()->get_position (right);
1016
1017                 if (changed || ((left == 0.5) && (right == 0.5))) {
1018
1019                         _streampanners.front()->set_position (0.0);
1020                         _streampanners.front()->pan_control()->list()->reset_default (0.0);
1021
1022                         _streampanners.back()->set_position (1.0);
1023                         _streampanners.back()->pan_control()->list()->reset_default (1.0);
1024
1025                         changed = true;
1026                 }
1027         }
1028
1029   send_changed:
1030         if (changed) {
1031                 Changed (); /* EMIT SIGNAL */
1032         }
1033
1034         return;
1035 }
1036
1037 void
1038 Panner::remove (uint32_t which)
1039 {
1040         vector<StreamPanner*>::iterator i;
1041         for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
1042
1043         if (i != _streampanners.end()) {
1044                 delete *i;
1045                 _streampanners.erase (i);
1046         }
1047 }
1048
1049
1050 /** Remove all our StreamPanners */
1051 void
1052 Panner::clear_panners ()
1053 {
1054         for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1055                 delete *i;
1056         }
1057
1058         _streampanners.clear ();
1059 }
1060
1061 void
1062 Panner::set_automation_style (AutoStyle style)
1063 {
1064         for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1065                 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
1066         }
1067         _session.set_dirty ();
1068 }
1069
1070 void
1071 Panner::set_automation_state (AutoState state)
1072 {
1073         for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1074                 ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
1075         }
1076         _session.set_dirty ();
1077 }
1078
1079 AutoState
1080 Panner::automation_state () const
1081 {
1082         boost::shared_ptr<AutomationList> l;
1083         if (!empty()) {
1084                 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
1085                 if (control) {
1086                         l = boost::dynamic_pointer_cast<AutomationList>(control->list());
1087                 }
1088         }
1089
1090         return l ? l->automation_state() : Off;
1091 }
1092
1093 AutoStyle
1094 Panner::automation_style () const
1095 {
1096         boost::shared_ptr<AutomationList> l;
1097         if (!empty()) {
1098                 boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
1099                 if (control) {
1100                         l = boost::dynamic_pointer_cast<AutomationList>(control->list());
1101                 }
1102         }
1103
1104         return l ? l->automation_style() : Absolute;
1105 }
1106
1107 struct PanPlugins {
1108     string name;
1109     uint32_t nouts;
1110     StreamPanner* (*factory)(Panner&, Evoral::Parameter);
1111 };
1112
1113 PanPlugins pan_plugins[] = {
1114         { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
1115         { Multi2dPanner::name, 3, Multi2dPanner::factory },
1116         { string (""), 0, 0 }
1117 };
1118
1119 XMLNode&
1120 Panner::get_state (void)
1121 {
1122         return state (true);
1123 }
1124
1125 XMLNode&
1126 Panner::state (bool full)
1127 {
1128         XMLNode* node = new XMLNode ("Panner");
1129
1130         char buf[32];
1131
1132         node->add_property (X_("linked"), (_linked ? "yes" : "no"));
1133         node->add_property (X_("link_direction"), enum_2_string (_link_direction));
1134         node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
1135
1136         for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
1137                 XMLNode* onode = new XMLNode (X_("Output"));
1138                 snprintf (buf, sizeof (buf), "%.12g", (*o).x);
1139                 onode->add_property (X_("x"), buf);
1140                 snprintf (buf, sizeof (buf), "%.12g", (*o).y);
1141                 onode->add_property (X_("y"), buf);
1142                 node->add_child_nocopy (*onode);
1143         }
1144
1145         for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1146                 node->add_child_nocopy ((*i)->state (full));
1147         }
1148
1149
1150         return *node;
1151 }
1152
1153 int
1154 Panner::set_state (const XMLNode& node, int version)
1155 {
1156         XMLNodeList nlist;
1157         XMLNodeConstIterator niter;
1158         const XMLProperty *prop;
1159         uint32_t i;
1160         uint32_t num_panners = 0;
1161         StreamPanner* sp;
1162         LocaleGuard lg (X_("POSIX"));
1163
1164         clear_panners ();
1165
1166         ChanCount ins = ChanCount::ZERO;
1167         ChanCount outs = ChanCount::ZERO;
1168
1169         // XXX: this might not be necessary anymore
1170         outputs.clear ();
1171
1172         if ((prop = node.property (X_("linked"))) != 0) {
1173                 set_linked (string_is_affirmative (prop->value()));
1174         }
1175
1176
1177         if ((prop = node.property (X_("bypassed"))) != 0) {
1178                 set_bypassed (string_is_affirmative (prop->value()));
1179         }
1180
1181         if ((prop = node.property (X_("link_direction"))) != 0) {
1182                 LinkDirection ld; /* here to provide type information */
1183                 set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
1184         }
1185
1186         nlist = node.children();
1187
1188         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1189                 if ((*niter)->name() == X_("Output")) {
1190
1191                         float x, y;
1192
1193                         prop = (*niter)->property (X_("x"));
1194                         sscanf (prop->value().c_str(), "%g", &x);
1195
1196                         prop = (*niter)->property (X_("y"));
1197                         sscanf (prop->value().c_str(), "%g", &y);
1198
1199                         outputs.push_back (Output (x, y));
1200                 }
1201         }
1202
1203         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1204
1205                 if ((*niter)->name() == X_("StreamPanner")) {
1206
1207                         if ((prop = (*niter)->property (X_("type")))) {
1208
1209                                 for (i = 0; pan_plugins[i].factory; ++i) {
1210                                         if (prop->value() == pan_plugins[i].name) {
1211
1212
1213                                                 /* note that we assume that all the stream panners
1214                                                    are of the same type. pretty good
1215                                                    assumption, but it's still an assumption.
1216                                                 */
1217
1218                                                 sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners));
1219                                                 num_panners++;
1220
1221                                                 if (sp->set_state (**niter, version) == 0) {
1222                                                         _streampanners.push_back (sp);
1223                                                 }
1224
1225                                                 break;
1226                                         }
1227                                 }
1228
1229
1230                                 if (!pan_plugins[i].factory) {
1231                                         error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
1232                                                           prop->value())
1233                                               << endmsg;
1234                                 }
1235
1236                         } else {
1237                                 error << _("panner plugin node has no type information!")
1238                                       << endmsg;
1239                                 return -1;
1240                         }
1241
1242                 }
1243         }
1244
1245         reset (outputs.size (), num_panners);
1246         /* don't try to do old-school automation loading if it wasn't marked as existing */
1247
1248         if ((prop = node.property (X_("automation")))) {
1249
1250                 /* automation path is relative */
1251
1252                 automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
1253         }
1254
1255         return 0;
1256 }
1257
1258 bool
1259 Panner::touching () const
1260 {
1261         for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1262                 if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
1263                         return true;
1264                 }
1265         }
1266
1267         return false;
1268 }
1269
1270 void
1271 Panner::set_position (float xpos, StreamPanner& orig)
1272 {
1273         float xnow;
1274         float xdelta ;
1275         float xnew;
1276
1277         orig.get_position (xnow);
1278         xdelta = xpos - xnow;
1279
1280         if (_link_direction == SameDirection) {
1281
1282                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1283                         if (*i == &orig) {
1284                                 (*i)->set_position (xpos, true);
1285                         } else {
1286                                 (*i)->get_position (xnow);
1287                                 xnew = min (1.0f, xnow + xdelta);
1288                                 xnew = max (0.0f, xnew);
1289                                 (*i)->set_position (xnew, true);
1290                         }
1291                 }
1292
1293         } else {
1294
1295                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1296                         if (*i == &orig) {
1297                                 (*i)->set_position (xpos, true);
1298                         } else {
1299                                 (*i)->get_position (xnow);
1300                                 xnew = min (1.0f, xnow - xdelta);
1301                                 xnew = max (0.0f, xnew);
1302                                 (*i)->set_position (xnew, true);
1303                         }
1304                 }
1305         }
1306 }
1307
1308 void
1309 Panner::set_position (float xpos, float ypos, StreamPanner& orig)
1310 {
1311         float xnow, ynow;
1312         float xdelta, ydelta;
1313         float xnew, ynew;
1314
1315         orig.get_position (xnow, ynow);
1316         xdelta = xpos - xnow;
1317         ydelta = ypos - ynow;
1318
1319         if (_link_direction == SameDirection) {
1320
1321                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1322                         if (*i == &orig) {
1323                                 (*i)->set_position (xpos, ypos, true);
1324                         } else {
1325                                 (*i)->get_position (xnow, ynow);
1326
1327                                 xnew = min (1.0f, xnow + xdelta);
1328                                 xnew = max (0.0f, xnew);
1329
1330                                 ynew = min (1.0f, ynow + ydelta);
1331                                 ynew = max (0.0f, ynew);
1332
1333                                 (*i)->set_position (xnew, ynew, true);
1334                         }
1335                 }
1336
1337         } else {
1338
1339                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1340                         if (*i == &orig) {
1341                                 (*i)->set_position (xpos, ypos, true);
1342                         } else {
1343                                 (*i)->get_position (xnow, ynow);
1344
1345                                 xnew = min (1.0f, xnow - xdelta);
1346                                 xnew = max (0.0f, xnew);
1347
1348                                 ynew = min (1.0f, ynow - ydelta);
1349                                 ynew = max (0.0f, ynew);
1350
1351                                 (*i)->set_position (xnew, ynew, true);
1352                         }
1353                 }
1354         }
1355 }
1356
1357 void
1358 Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig)
1359 {
1360         float xnow, ynow, znow;
1361         float xdelta, ydelta, zdelta;
1362         float xnew, ynew, znew;
1363
1364         orig.get_position (xnow, ynow, znow);
1365         xdelta = xpos - xnow;
1366         ydelta = ypos - ynow;
1367         zdelta = zpos - znow;
1368
1369         if (_link_direction == SameDirection) {
1370
1371                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1372                         if (*i == &orig) {
1373                                 (*i)->set_position (xpos, ypos, zpos, true);
1374                         } else {
1375                                 (*i)->get_position (xnow, ynow, znow);
1376
1377                                 xnew = min (1.0f, xnow + xdelta);
1378                                 xnew = max (0.0f, xnew);
1379
1380                                 ynew = min (1.0f, ynow + ydelta);
1381                                 ynew = max (0.0f, ynew);
1382
1383                                 znew = min (1.0f, znow + zdelta);
1384                                 znew = max (0.0f, znew);
1385
1386                                 (*i)->set_position (xnew, ynew, znew, true);
1387                         }
1388                 }
1389
1390         } else {
1391
1392                 for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1393                         if (*i == &orig) {
1394                                 (*i)->set_position (xpos, ypos, true);
1395                         } else {
1396                                 (*i)->get_position (xnow, ynow, znow);
1397
1398                                 xnew = min (1.0f, xnow - xdelta);
1399                                 xnew = max (0.0f, xnew);
1400
1401                                 ynew = min (1.0f, ynow - ydelta);
1402                                 ynew = max (0.0f, ynew);
1403
1404                                 znew = min (1.0f, znow + zdelta);
1405                                 znew = max (0.0f, znew);
1406
1407                                 (*i)->set_position (xnew, ynew, znew, true);
1408                         }
1409                 }
1410         }
1411 }
1412
1413 void
1414 Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, gain_t gain_coeff)
1415 {
1416         if (outbufs.count().n_audio() == 0) {
1417                 // Don't want to lose audio...
1418                 assert(inbufs.count().n_audio() == 0);
1419                 return;
1420         }
1421
1422         // We shouldn't be called in the first place...
1423         assert(!bypassed());
1424         assert(!empty());
1425
1426
1427         if (outbufs.count().n_audio() == 1) {
1428
1429                 AudioBuffer& dst = outbufs.get_audio(0);
1430
1431                 if (gain_coeff == 0.0f) {
1432
1433                         /* only one output, and gain was zero, so make it silent */
1434
1435                         dst.silence (nframes);
1436
1437                 } else if (gain_coeff == 1.0f){
1438
1439                         /* mix all buffers into the output */
1440
1441                         // copy the first
1442                         dst.read_from(inbufs.get_audio(0), nframes);
1443
1444                         // accumulate starting with the second
1445                         if (inbufs.count().n_audio() > 0) {
1446                                 BufferSet::audio_iterator i = inbufs.audio_begin();
1447                                 for (++i; i != inbufs.audio_end(); ++i) {
1448                                         dst.merge_from(*i, nframes);
1449                                 }
1450                         }
1451
1452                 } else {
1453
1454                         /* mix all buffers into the output, scaling them all by the gain */
1455
1456                         // copy the first
1457                         dst.read_from(inbufs.get_audio(0), nframes);
1458
1459                         // accumulate (with gain) starting with the second
1460                         if (inbufs.count().n_audio() > 0) {
1461                                 BufferSet::audio_iterator i = inbufs.audio_begin();
1462                                 for (++i; i != inbufs.audio_end(); ++i) {
1463                                         dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
1464                                 }
1465                         }
1466
1467                 }
1468
1469                 return;
1470         }
1471
1472         /* the terrible silence ... */
1473         for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1474                 i->silence(nframes);
1475         }
1476
1477         BufferSet::audio_iterator i = inbufs.audio_begin();
1478
1479         for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
1480                 (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
1481         }
1482 }
1483
1484 void
1485 Panner::run (BufferSet& inbufs, BufferSet& outbufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes)
1486 {
1487         if (outbufs.count().n_audio() == 0) {
1488                 // Failing to deliver audio we were asked to deliver is a bug
1489                 assert(inbufs.count().n_audio() == 0);
1490                 return;
1491         }
1492
1493         // We shouldn't be called in the first place...
1494         assert(!bypassed());
1495         assert(!empty());
1496
1497         // If we shouldn't play automation defer to distribute_no_automation
1498         if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
1499
1500                 // Speed quietning
1501                 gain_t gain_coeff = 1.0;
1502
1503                 if (fabsf(_session.transport_speed()) > 1.5f) {
1504                         gain_coeff = speed_quietning;
1505                 }
1506
1507                 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
1508                 return;
1509         }
1510
1511         // Otherwise.. let the automation flow, baby
1512
1513         if (outbufs.count().n_audio() == 1) {
1514
1515                 AudioBuffer& dst = outbufs.get_audio(0);
1516
1517                 // FIXME: apply gain automation?
1518
1519                 // copy the first
1520                 dst.read_from(inbufs.get_audio(0), nframes);
1521
1522                 // accumulate starting with the second
1523                 BufferSet::audio_iterator i = inbufs.audio_begin();
1524                 for (++i; i != inbufs.audio_end(); ++i) {
1525                         dst.merge_from(*i, nframes);
1526                 }
1527
1528                 return;
1529         }
1530
1531         // More than 1 output, we should have 1 panner for each input
1532         //assert(_streampanners.size() == inbufs.count().n_audio());
1533
1534         /* the terrible silence ... */
1535         for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
1536                 i->silence(nframes);
1537         }
1538
1539         BufferSet::audio_iterator i = inbufs.audio_begin();
1540         for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
1541                 (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
1542         }
1543 }
1544
1545 /* old school automation handling */
1546
1547 /*
1548 void
1549 Panner::set_name (string str)
1550 {
1551         automation_path = Glib::build_filename(_session.automation_dir(),
1552                 _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
1553 }
1554 */
1555
1556 int
1557 Panner::load ()
1558 {
1559         char line[128];
1560         uint32_t linecnt = 0;
1561         float version;
1562         vector<StreamPanner*>::iterator sp;
1563         LocaleGuard lg (X_("POSIX"));
1564
1565         if (automation_path.length() == 0) {
1566                 return 0;
1567         }
1568
1569         if (access (automation_path.c_str(), F_OK)) {
1570                 return 0;
1571         }
1572
1573         ifstream in (automation_path.c_str());
1574
1575         if (!in) {
1576                 error << string_compose (_("cannot open pan automation file %1 (%2)"),
1577                                   automation_path, strerror (errno))
1578                       << endmsg;
1579                 return -1;
1580         }
1581
1582         sp = _streampanners.begin();
1583
1584         while (in.getline (line, sizeof(line), '\n')) {
1585
1586                 if (++linecnt == 1) {
1587                         if (memcmp (line, X_("version"), 7) == 0) {
1588                                 if (sscanf (line, "version %f", &version) != 1) {
1589                                         error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
1590                                         return -1;
1591                                 }
1592                         } else {
1593                                 error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
1594                                                  automation_path, line) << endmsg;
1595                                 return -1;
1596                         }
1597
1598                         continue;
1599                 }
1600
1601                 if (strlen (line) == 0 || line[0] == '#') {
1602                         continue;
1603                 }
1604
1605                 if (strcmp (line, "begin") == 0) {
1606
1607                         if (sp == _streampanners.end()) {
1608                                 error << string_compose (_("too many panner states found in pan automation file %1"),
1609                                                   automation_path)
1610                                       << endmsg;
1611                                 return -1;
1612                         }
1613
1614                         if ((*sp)->load (in, automation_path, linecnt)) {
1615                                 return -1;
1616                         }
1617
1618                         ++sp;
1619                 }
1620         }
1621
1622         return 0;
1623 }
1624
1625 void
1626 Panner::set_mono (bool yn)
1627 {
1628         if (yn != _mono) {
1629                 _mono = yn;
1630                 StateChanged ();
1631         }
1632
1633         for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
1634                 (*i)->set_mono (yn);
1635         }
1636 }