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