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