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