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