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