Towards MIDI:
[ardour.git] / libs / ardour / io.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <fstream>
20 #include <algorithm>
21 #include <unistd.h>
22 #include <locale.h>
23
24 #include <sigc++/bind.h>
25
26 #include <glibmm/thread.h>
27
28 #include <pbd/xml++.h>
29
30 #include <ardour/audioengine.h>
31 #include <ardour/io.h>
32 #include <ardour/port.h>
33 #include <ardour/audio_port.h>
34 #include <ardour/midi_port.h>
35 #include <ardour/connection.h>
36 #include <ardour/session.h>
37 #include <ardour/cycle_timer.h>
38 #include <ardour/panner.h>
39 #include <ardour/buffer_set.h>
40 #include <ardour/meter.h>
41
42 #include "i18n.h"
43
44 #include <cmath>
45
46 /*
47   A bug in OS X's cmath that causes isnan() and isinf() to be 
48   "undeclared". the following works around that
49 */
50
51 #if defined(__APPLE__) && defined(__MACH__)
52 extern "C" int isnan (double);
53 extern "C" int isinf (double);
54 #endif
55
56
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace PBD;
60
61
62 static float current_automation_version_number = 1.0;
63
64 jack_nframes_t               IO::_automation_interval = 0;
65 const string                 IO::state_node_name = "IO";
66 bool                         IO::connecting_legal = false;
67 bool                         IO::ports_legal = false;
68 bool                         IO::panners_legal = false;
69 sigc::signal<void>           IO::Meter;
70 sigc::signal<int>            IO::ConnectingLegal;
71 sigc::signal<int>            IO::PortsLegal;
72 sigc::signal<int>            IO::PannersLegal;
73 sigc::signal<void,ChanCount> IO::MoreChannels;
74 sigc::signal<int>            IO::PortsCreated;
75
76 Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT;
77
78 /* this is a default mapper of [0 .. 1.0] control values to a gain coefficient.
79    others can be imagined. 
80 */
81
82 static gain_t direct_control_to_gain (double fract) { 
83         /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
84         /* this maxes at +6dB */
85         return pow (2.0,(sqrt(sqrt(sqrt(fract)))*198.0-192.0)/6.0);
86 }
87
88 static double direct_gain_to_control (gain_t gain) { 
89         /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
90         if (gain == 0) return 0.0;
91         
92         return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0);
93 }
94
95
96 /** @param default_type The type of port that will be created by ensure_io
97  * and friends if no type is explicitly requested (to avoid breakage).
98  */
99 IO::IO (Session& s, string name,
100         int input_min, int input_max, int output_min, int output_max,
101         DataType default_type)
102         : _session (s),
103       _output_buffers(new BufferSet()),
104           _name (name),
105           _default_type(default_type),
106           _gain_control (*this),
107           _gain_automation_curve (0.0, 2.0, 1.0),
108           _input_minimum (_default_type, input_min),
109           _input_maximum (_default_type, input_max),
110           _output_minimum (_default_type, output_min),
111           _output_maximum (_default_type, output_max)
112 {
113         _panner = new Panner (name, _session);
114         _meter = new PeakMeter (_session);
115
116         _gain = 1.0;
117         _desired_gain = 1.0;
118         _input_connection = 0;
119         _output_connection = 0;
120         pending_state_node = 0;
121         no_panner_reset = false;
122         deferred_state = 0;
123
124         apply_gain_automation = false;
125
126         last_automation_snapshot = 0;
127
128         _gain_automation_state = Off;
129         _gain_automation_style = Absolute;
130     
131     {
132         // IO::Meter is emitted from another thread so the
133         // Meter signal must be protected.
134         Glib::Mutex::Lock guard (m_meter_signal_lock);
135         m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
136     }
137         
138         // Connect to our own MoreChannels signal to connect output buffers
139         IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
140 }
141
142 IO::~IO ()
143 {
144     Glib::Mutex::Lock guard (m_meter_signal_lock);
145         Glib::Mutex::Lock lm (io_lock);
146
147         for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
148                 _session.engine().unregister_port (*i);
149         }
150
151         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
152                 _session.engine().unregister_port (*i);
153         }
154
155     m_meter_connection.disconnect();
156
157         delete _meter;
158         delete _panner;
159         delete _output_buffers;
160 }
161
162 void
163 IO::silence (jack_nframes_t nframes, jack_nframes_t offset)
164 {
165         /* io_lock, not taken: function must be called from Session::process() calltree */
166
167         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
168                 i->silence (nframes, offset);
169         }
170 }
171
172 void
173 IO::pan_automated (BufferSet& bufs, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset)
174 {
175         _panner->distribute_automated(bufs, output_buffers(), start_frame, end_frame, nframes, offset);
176 }
177
178 void
179 IO::pan (BufferSet& bufs, jack_nframes_t nframes, jack_nframes_t offset, gain_t gain_coeff)
180 {
181         /* the panner can be empty if there are no inputs to the route, but still outputs */
182         if (_panner->bypassed() || _panner->empty()) {
183                 // FIXME: gain
184                 deliver_output_no_pan (bufs, nframes, offset);
185                 return;
186         } else {
187                 _panner->distribute(bufs, output_buffers(), nframes, offset, gain_coeff);
188         }
189 }
190
191 void
192 IO::deliver_output (BufferSet& bufs, jack_nframes_t nframes, jack_nframes_t offset)
193 {
194         throw;
195 #if 0
196         /* io_lock, not taken: function must be called from Session::process() calltree */
197
198         if (n_outputs().get(DataType::AUDIO) == 0) {
199                 return;
200         }
201         
202         if (_panner->bypassed() || _panner->empty()) {
203                 deliver_output_no_pan (bufs, nbufs, nframes, offset);
204                 return;
205         }
206
207
208         gain_t dg;
209         gain_t pangain = _gain;
210         
211         {
212                 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
213                 
214                 if (dm.locked()) {
215                         dg = _desired_gain;
216                 } else {
217                         dg = _gain;
218                 }
219         }
220
221         if (dg != _gain) {
222                 Declicker::run (bufs, nbufs, nframes, _gain, dg, false);
223                 _gain = dg;
224                 pangain = 1.0f;
225         } 
226
227         /* simple, non-automation panning to outputs */
228
229         if (_session.transport_speed() > 1.5f || _session.transport_speed() < -1.5f) {
230                 pan (bufs, nbufs, nframes, offset, pangain * speed_quietning);
231         } else {
232                 pan (bufs, nbufs, nframes, offset, pangain);
233         }
234 #endif
235 }
236
237 void
238 IO::deliver_output_no_pan (BufferSet& bufs, jack_nframes_t nframes, jack_nframes_t offset)
239 {
240         throw;
241 #if 0
242         /* io_lock, not taken: function must be called from Session::process() calltree */
243
244         if (n_outputs().get(DataType::AUDIO) == 0) {
245                 return;
246         }
247
248         gain_t dg;
249         gain_t old_gain = _gain;
250
251         if (apply_gain_automation) {
252
253                 /* gain has already been applied by automation code. do nothing here except
254                    speed quietning.
255                 */
256
257                 _gain = 1.0f;
258                 dg = _gain;
259                 
260         } else {
261
262                 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
263                 
264                 if (dm.locked()) {
265                         dg = _desired_gain;
266                 } else {
267                         dg = _gain;
268                 }
269         }
270
271         Sample* src;
272         Sample* dst;
273         uint32_t i;
274         vector<Sample*> outs;
275         gain_t actual_gain;
276
277         if (dg != _gain) {
278                 /* unlikely condition */
279                 i = 0;
280                 for (PortSet::audio_iterator o = _outputs.audio_begin(); o != _outputs.audio_end(); ++o, ++i) {
281                         outs.push_back ((*o)->get_audio_buffer().data (nframes, offset));
282                 }
283         }
284
285         /* reduce nbufs to the index of the last input buffer */
286
287         nbufs--;
288
289         if (_session.transport_speed() > 1.5f || _session.transport_speed() < -1.5f) {
290                 actual_gain = _gain * speed_quietning;
291         } else {
292                 actual_gain = _gain;
293         }
294         
295         i = 0;
296         for (PortSet::audio_iterator o = _outputs.audio_begin(); o != _outputs.audio_end(); ++o, ++i) {
297
298                 dst = (*o)->get_audio_buffer().data(nframes, offset);
299                 src = bufs[min(nbufs,i)];
300
301                 if (dg != _gain || actual_gain == 1.0f) {
302                         memcpy (dst, src, sizeof (Sample) * nframes);
303                 } else if (actual_gain == 0.0f) {
304                         memset (dst, 0, sizeof (Sample) * nframes);
305                 } else {
306                         for (jack_nframes_t x = 0; x < nframes; ++x) {
307                                 dst[x] = src[x] * actual_gain;
308                         }
309                 }
310                 
311                 (*o)->mark_silence (false);
312         }
313
314         if (dg != _gain) {
315                 Declicker::run (outs, outs.size(), nframes, _gain, dg, false);
316                 _gain = dg;
317         }
318
319         if (apply_gain_automation) {
320                 _gain = old_gain;
321         }
322 #endif
323 }
324
325 void
326 IO::collect_input (BufferSet& outs, jack_nframes_t nframes, jack_nframes_t offset)
327 {
328         outs.set_count(n_inputs());
329         
330         if (outs.count() == ChanCount::ZERO)
331                 return;
332
333         for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
334                 
335                 BufferSet::iterator o = outs.begin(*t);
336                 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
337                         o->read_from(i->get_buffer(), nframes, offset);
338                 }
339
340         }
341 }
342
343 void
344 IO::just_meter_input (jack_nframes_t start_frame, jack_nframes_t end_frame, 
345                       jack_nframes_t nframes, jack_nframes_t offset)
346 {
347         BufferSet& bufs = _session.get_scratch_buffers ();
348         ChanCount nbufs = n_process_buffers ();
349
350         collect_input (bufs, nframes, offset);
351
352         _meter->run(bufs, nframes);
353 }
354
355 void
356 IO::drop_input_connection ()
357 {
358         _input_connection = 0;
359         input_connection_configuration_connection.disconnect();
360         input_connection_connection_connection.disconnect();
361         _session.set_dirty ();
362 }
363
364 void
365 IO::drop_output_connection ()
366 {
367         _output_connection = 0;
368         output_connection_configuration_connection.disconnect();
369         output_connection_connection_connection.disconnect();
370         _session.set_dirty ();
371 }
372
373 int
374 IO::disconnect_input (Port* our_port, string other_port, void* src)
375 {
376         if (other_port.length() == 0 || our_port == 0) {
377                 return 0;
378         }
379
380         { 
381                 Glib::Mutex::Lock em (_session.engine().process_lock());
382                 
383                 {
384                         Glib::Mutex::Lock lm (io_lock);
385                         
386                         /* check that our_port is really one of ours */
387                         
388                         if ( ! _inputs.contains(our_port)) {
389                                 return -1;
390                         }
391                         
392                         /* disconnect it from the source */
393                         
394                         if (_session.engine().disconnect (other_port, our_port->name())) {
395                                 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
396                                 return -1;
397                         }
398
399                         drop_input_connection();
400                 }
401         }
402
403         input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
404         _session.set_dirty ();
405
406         return 0;
407 }
408
409 int
410 IO::connect_input (Port* our_port, string other_port, void* src)
411 {
412         if (other_port.length() == 0 || our_port == 0) {
413                 return 0;
414         }
415
416         {
417                 Glib::Mutex::Lock em(_session.engine().process_lock());
418                 
419                 {
420                         Glib::Mutex::Lock lm (io_lock);
421                         
422                         /* check that our_port is really one of ours */
423                         
424                         if ( ! _inputs.contains(our_port) ) {
425                                 return -1;
426                         }
427                         
428                         /* connect it to the source */
429
430                         if (_session.engine().connect (other_port, our_port->name())) {
431                                 return -1;
432                         }
433                         
434                         drop_input_connection ();
435                 }
436         }
437
438         input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
439         _session.set_dirty ();
440         return 0;
441 }
442
443 int
444 IO::disconnect_output (Port* our_port, string other_port, void* src)
445 {
446         if (other_port.length() == 0 || our_port == 0) {
447                 return 0;
448         }
449
450         {
451                 Glib::Mutex::Lock em(_session.engine().process_lock());
452                 
453                 {
454                         Glib::Mutex::Lock lm (io_lock);
455                         
456                         /* check that our_port is really one of ours */
457                         
458                         if ( ! _outputs.contains(our_port) ) {
459                                 return -1;
460                         }
461                         
462                         /* disconnect it from the destination */
463                         
464                         if (_session.engine().disconnect (our_port->name(), other_port)) {
465                                 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
466                                 return -1;
467                         }
468
469                         drop_output_connection ();
470                 }
471         }
472
473         output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
474         _session.set_dirty ();
475         return 0;
476 }
477
478 int
479 IO::connect_output (Port* our_port, string other_port, void* src)
480 {
481         if (other_port.length() == 0 || our_port == 0) {
482                 return 0;
483         }
484
485         {
486                 Glib::Mutex::Lock em(_session.engine().process_lock());
487                 
488                 {
489                         Glib::Mutex::Lock lm (io_lock);
490                         
491                         /* check that our_port is really one of ours */
492                         
493                         if ( ! _outputs.contains(our_port) ) {
494                                 return -1;
495                         }
496                         
497                         /* connect it to the destination */
498                         
499                         if (_session.engine().connect (our_port->name(), other_port)) {
500                                 return -1;
501                         }
502
503                         drop_output_connection ();
504                 }
505         }
506
507         output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
508         _session.set_dirty ();
509         return 0;
510 }
511
512 int
513 IO::set_input (Port* other_port, void* src)
514 {
515         /* this removes all but one ports, and connects that one port
516            to the specified source.
517         */
518
519         if (_input_minimum.get_total() > 1) {
520                 /* sorry, you can't do this */
521                 return -1;
522         }
523
524         if (other_port == 0) {
525                 if (_input_minimum == ChanCount::ZERO) {
526                         return ensure_inputs (0, false, true, src);
527                 } else {
528                         return -1;
529                 }
530         }
531
532         if (ensure_inputs (1, true, true, src)) {
533                 return -1;
534         }
535
536         return connect_input (_inputs.port(0), other_port->name(), src);
537 }
538
539 int
540 IO::remove_output_port (Port* port, void* src)
541 {
542         throw; // FIXME
543 #if 0
544         IOChange change (NoChange);
545
546         {
547                 Glib::Mutex::Lock em(_session.engine().process_lock());
548                 
549                 {
550                         Glib::Mutex::Lock lm (io_lock);
551                         
552                         if (_noutputs - 1 == (uint32_t) _output_minimum) {
553                                 /* sorry, you can't do this */
554                                 return -1;
555                         }
556                         
557                         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
558                                 if (*i == port) {
559                                         change = IOChange (change|ConfigurationChanged);
560                                         if (port->connected()) {
561                                                 change = IOChange (change|ConnectionsChanged);
562                                         } 
563
564                                         _session.engine().unregister_port (*i);
565                                         _outputs.erase (i);
566                                         _noutputs--;
567                                         drop_output_connection ();
568
569                                         break;
570                                 }
571                         }
572
573                         if (change != NoChange) {
574                                 setup_peak_meters ();
575                                 reset_panner ();
576                         }
577                 }
578         }
579         
580         if (change != NoChange) {
581                 output_changed (change, src); /* EMIT SIGNAL */
582                 _session.set_dirty ();
583                 return 0;
584         }
585 #endif
586         return -1;
587 }
588
589 /** Add an output port.
590  *
591  * @param destination Name of input port to connect new port to.
592  * @param src Source for emitted ConfigurationChanged signal.
593  * @param type Data type of port.  Default value (NIL) will use this IO's default type.
594  */
595 int
596 IO::add_output_port (string destination, void* src, DataType type)
597 {
598         Port* our_port;
599         char name[64];
600
601         if (type == DataType::NIL)
602                 type = _default_type;
603
604         {
605                 Glib::Mutex::Lock em(_session.engine().process_lock());
606                 
607                 { 
608                         Glib::Mutex::Lock lm (io_lock);
609                         
610                         if (n_outputs() >= _output_maximum) {
611                                 return -1;
612                         }
613                 
614                         /* Create a new output port */
615                         
616                         // FIXME: naming scheme for differently typed ports?
617                         if (_output_maximum.get_total() == 1) {
618                                 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
619                         } else {
620                                 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
621                         }
622                         
623                         if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
624                                 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
625                                 return -1;
626                         }
627                         
628                         _outputs.add_port (our_port);
629                         drop_output_connection ();
630                         setup_peak_meters ();
631                         reset_panner ();
632                 }
633
634                 MoreChannels (n_outputs()); /* EMIT SIGNAL */
635         }
636
637         if (destination.length()) {
638                 if (_session.engine().connect (our_port->name(), destination)) {
639                         return -1;
640                 }
641         }
642         
643         // pan_changed (src); /* EMIT SIGNAL */
644         output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
645         _session.set_dirty ();
646         
647         return 0;
648 }
649
650 int
651 IO::remove_input_port (Port* port, void* src)
652 {
653         throw; // FIXME
654 #if 0
655         IOChange change (NoChange);
656
657         {
658                 Glib::Mutex::Lock em(_session.engine().process_lock());
659                 
660                 {
661                         Glib::Mutex::Lock lm (io_lock);
662
663                         if (((int)_ninputs - 1) < _input_minimum) {
664                                 /* sorry, you can't do this */
665                                 return -1;
666                         }
667                         for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
668
669                                 if (*i == port) {
670                                         change = IOChange (change|ConfigurationChanged);
671
672                                         if (port->connected()) {
673                                                 change = IOChange (change|ConnectionsChanged);
674                                         } 
675
676                                         _session.engine().unregister_port (*i);
677                                         _inputs.erase (i);
678                                         _ninputs--;
679                                         drop_input_connection ();
680
681                                         break;
682                                 }
683                         }
684                         
685                         if (change != NoChange) {
686                                 setup_peak_meters ();
687                                 reset_panner ();
688                         }
689                 }
690         }
691
692         if (change != NoChange) {
693                 input_changed (change, src);
694                 _session.set_dirty ();
695                 return 0;
696         } 
697 #endif
698         return -1;
699 }
700
701
702 /** Add an input port.
703  *
704  * @param type Data type of port.  The appropriate Jack port type, and @ref Port will be created.
705  * @param destination Name of input port to connect new port to.
706  * @param src Source for emitted ConfigurationChanged signal.
707  */
708 int
709 IO::add_input_port (string source, void* src, DataType type)
710 {
711         Port* our_port;
712         char name[64];
713         
714         if (type == DataType::NIL)
715                 type = _default_type;
716
717         {
718                 Glib::Mutex::Lock em (_session.engine().process_lock());
719                 
720                 { 
721                         Glib::Mutex::Lock lm (io_lock);
722                         
723                         if (n_inputs() >= _input_maximum) {
724                                 return -1;
725                         }
726
727                         /* Create a new input port */
728                         
729                         // FIXME: naming scheme for differently typed ports?
730                         if (_input_maximum.get_total() == 1) {
731                                 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
732                         } else {
733                                 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
734                         }
735                         
736                         if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
737                                 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
738                                 return -1;
739                         }
740                         
741                         _inputs.add_port(our_port);
742                         drop_input_connection ();
743                         setup_peak_meters ();
744                         reset_panner ();
745                 }
746
747                 MoreChannels (n_inputs()); /* EMIT SIGNAL */
748         }
749
750         if (source.length()) {
751
752                 if (_session.engine().connect (source, our_port->name())) {
753                         return -1;
754                 }
755         } 
756
757         // pan_changed (src); /* EMIT SIGNAL */
758         input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
759         _session.set_dirty ();
760         
761         return 0;
762 }
763
764 int
765 IO::disconnect_inputs (void* src)
766 {
767         { 
768                 Glib::Mutex::Lock em (_session.engine().process_lock());
769                 
770                 {
771                         Glib::Mutex::Lock lm (io_lock);
772                         
773                         for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
774                                 _session.engine().disconnect (*i);
775                         }
776
777                         drop_input_connection ();
778                 }
779         }
780         
781         input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
782         
783         return 0;
784 }
785
786 int
787 IO::disconnect_outputs (void* src)
788 {
789         {
790                 Glib::Mutex::Lock em (_session.engine().process_lock());
791                 
792                 {
793                         Glib::Mutex::Lock lm (io_lock);
794                         
795                         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
796                                 _session.engine().disconnect (*i);
797                         }
798
799                         drop_output_connection ();
800                 }
801         }
802
803         output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
804         _session.set_dirty ();
805         
806         return 0;
807 }
808
809 bool
810 IO::ensure_inputs_locked (uint32_t n, bool clear, void* src)
811 {
812         Port* input_port;
813         bool changed = false;
814         
815         /* remove unused ports */
816
817         while (n_inputs().get(_default_type) > n) {
818                 throw; // FIXME
819                 /*
820                 _session.engine().unregister_port (_inputs.back());
821                 _inputs.pop_back();
822                 _ninputs--;
823                 changed = true;
824                 */
825         }
826                 
827         /* create any necessary new ports */
828                 
829         while (n_inputs().get(_default_type) < n) {
830                 
831                 char buf[64];
832                 
833                 /* Create a new input port (of the default type) */
834                 
835                 if (_input_maximum.get_total() == 1) {
836                         snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
837                 }
838                 else {
839                         snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
840                 }
841                 
842                 try {
843                         
844                         if ((input_port = _session.engine().register_input_port (_default_type, buf)) == 0) {
845                                 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
846                                 return -1;
847                         }
848                 }
849
850                 catch (AudioEngine::PortRegistrationFailure& err) {
851                         setup_peak_meters ();
852                         reset_panner ();
853                         /* pass it on */
854                         throw err;
855                 }
856                 
857                 _inputs.add_port (input_port);
858                 changed = true;
859         }
860         
861         if (changed) {
862                 drop_input_connection ();
863                 setup_peak_meters ();
864                 reset_panner ();
865                 MoreChannels (n_inputs()); /* EMIT SIGNAL */
866                 _session.set_dirty ();
867         }
868         
869         if (clear) {
870                 /* disconnect all existing ports so that we get a fresh start */
871                         
872                 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
873                         _session.engine().disconnect (*i);
874                 }
875         }
876
877         return changed;
878 }
879
880 /** Attach output_buffers to port buffers.
881  * 
882  * Connected to IOs own MoreChannels signal.
883  */
884 void
885 IO::attach_buffers(ChanCount ignored)
886 {
887         _output_buffers->attach_buffers(_outputs);
888 }
889
890 int
891 IO::ensure_io (const ChanCount& in, const ChanCount& out, bool clear, void* src)
892 {
893         // FIXME: TYPE
894         uint32_t nin = in.get(_default_type);
895         uint32_t nout = out.get(_default_type);
896
897         // We only deal with one type still.  Sorry about your luck.
898         assert(nin == in.get_total());
899         assert(nout == out.get_total());
900
901         return ensure_io(nin, nout, clear, src);
902 }
903
904 int
905 IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
906 {
907         bool in_changed = false;
908         bool out_changed = false;
909         bool need_pan_reset;
910
911         nin = min (_input_maximum.get(_default_type), static_cast<size_t>(nin));
912
913         nout = min (_output_maximum.get(_default_type), static_cast<size_t>(nout));
914
915         if (nin == n_inputs().get(_default_type) && nout == n_outputs().get(_default_type) && !clear) {
916                 return 0;
917         }
918
919         {
920                 Glib::Mutex::Lock em (_session.engine().process_lock());
921                 Glib::Mutex::Lock lm (io_lock);
922
923                 Port* port;
924                 
925                 if (n_outputs().get(_default_type) == nout) {
926                         need_pan_reset = false;
927                 } else {
928                         need_pan_reset = true;
929                 }
930                 
931                 /* remove unused ports */
932                 
933                 while (n_inputs().get(_default_type) > nin) {
934                         throw; // FIXME
935                         /*
936                         _session.engine().unregister_port (_inputs.back());
937                         _inputs.pop_back();
938                         _ninputs--;
939                         in_changed = true;*/
940                 }
941                 
942                 while (n_outputs().get(_default_type) > nout) {
943                         throw; // FIXME
944                         /*
945                         _session.engine().unregister_port (_outputs.back());
946                         _outputs.pop_back();
947                         _noutputs--;
948                         out_changed = true;*/
949                 }
950                 
951                 /* create any necessary new ports (of the default type) */
952                 
953                 while (n_inputs().get(_default_type) < nin) {
954                         
955                         char buf[64];
956
957                         /* Create a new input port */
958                         
959                         if (_input_maximum.get_total() == 1) {
960                                 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
961                         }
962                         else {
963                                 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
964                         }
965                         
966                         try {
967                                 if ((port = _session.engine().register_input_port (_default_type, buf)) == 0) {
968                                         error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
969                                         return -1;
970                                 }
971                         }
972
973                         catch (AudioEngine::PortRegistrationFailure& err) {
974                                 setup_peak_meters ();
975                                 reset_panner ();
976                                 /* pass it on */
977                                 throw err;
978                         }
979                 
980                         _inputs.add_port (port);
981                         in_changed = true;
982                 }
983
984                 /* create any necessary new ports */
985                 
986                 while (n_outputs().get(_default_type) < nout) {
987                         
988                         char buf[64];
989                         
990                         /* Create a new output port */
991                         
992                         if (_output_maximum.get_total() == 1) {
993                                 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
994                         } else {
995                                 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
996                         }
997                         
998                         try { 
999                                 if ((port = _session.engine().register_output_port (_default_type, buf)) == 0) {
1000                                         error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1001                                         return -1;
1002                                 }
1003                         }
1004                         
1005                         catch (AudioEngine::PortRegistrationFailure& err) {
1006                                 setup_peak_meters ();
1007                                 reset_panner ();
1008                                 /* pass it on */
1009                                 throw err;
1010                         }
1011                 
1012                         _outputs.add_port (port);
1013                         out_changed = true;
1014                 }
1015                 
1016                 if (clear) {
1017                         
1018                         /* disconnect all existing ports so that we get a fresh start */
1019                         
1020                         for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1021                                 _session.engine().disconnect (*i);
1022                         }
1023                         
1024                         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1025                                 _session.engine().disconnect (*i);
1026                         }
1027                 }
1028                 
1029                 if (in_changed || out_changed) {
1030                         setup_peak_meters ();
1031                         reset_panner ();
1032                 }
1033         }
1034
1035         if (out_changed) {
1036                 drop_output_connection ();
1037                 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1038         }
1039         
1040         if (in_changed) {
1041                 drop_input_connection ();
1042                 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1043         }
1044
1045         if (in_changed || out_changed) {
1046                 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
1047                 _session.set_dirty ();
1048         }
1049
1050         return 0;
1051 }
1052
1053 int
1054 IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
1055 {
1056         bool changed = false;
1057
1058         n = min (_input_maximum.get(_default_type), static_cast<size_t>(n));
1059
1060         if (n == n_inputs().get(_default_type) && !clear) {
1061                 return 0;
1062         }
1063
1064         if (lockit) {
1065                 Glib::Mutex::Lock em (_session.engine().process_lock());
1066                 Glib::Mutex::Lock im (io_lock);
1067                 changed = ensure_inputs_locked (n, clear, src);
1068         } else {
1069                 changed = ensure_inputs_locked (n, clear, src);
1070         }
1071
1072         if (changed) {
1073                 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1074                 _session.set_dirty ();
1075         }
1076         return 0;
1077 }
1078
1079 bool
1080 IO::ensure_outputs_locked (uint32_t n, bool clear, void* src)
1081 {
1082         Port* output_port;
1083         bool changed = false;
1084         bool need_pan_reset;
1085
1086         if (n_outputs().get(_default_type) == n) {
1087                 need_pan_reset = false;
1088         } else {
1089                 need_pan_reset = true;
1090         }
1091         
1092         /* remove unused ports */
1093         
1094         while (n_outputs().get(_default_type) > n) {
1095                 throw; // FIXME
1096                 /*
1097                 _session.engine().unregister_port (_outputs.back());
1098                 _outputs.pop_back();
1099                 _noutputs--;
1100                 changed = true;
1101                 */
1102         }
1103         
1104         /* create any necessary new ports */
1105         
1106         while (n_outputs().get(_default_type) < n) {
1107                 
1108                 char buf[64];
1109                 
1110                 /* Create a new output port */
1111                 
1112                 if (_output_maximum.get(_default_type) == 1) {
1113                         snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1114                 } else {
1115                         snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1116                 }
1117                 
1118                 if ((output_port = _session.engine().register_output_port (_default_type, buf)) == 0) {
1119                         error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1120                         return -1;
1121                 }
1122                 
1123                 _outputs.add_port (output_port);
1124                 changed = true;
1125                 setup_peak_meters ();
1126
1127                 if (need_pan_reset) {
1128                         reset_panner ();
1129                 }
1130         }
1131         
1132         if (changed) {
1133                 drop_output_connection ();
1134                 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1135                 _session.set_dirty ();
1136         }
1137         
1138         if (clear) {
1139                 /* disconnect all existing ports so that we get a fresh start */
1140                 
1141                 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1142                         _session.engine().disconnect (*i);
1143                 }
1144         }
1145
1146         return changed;
1147 }
1148
1149 int
1150 IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
1151 {
1152         bool changed = false;
1153
1154         if (_output_maximum < ChanCount::INFINITE) {
1155                 n = min (_output_maximum.get(_default_type), static_cast<size_t>(n));
1156                 if (n == n_outputs().get(_default_type) && !clear) {
1157                         return 0;
1158                 }
1159         }
1160
1161         /* XXX caller should hold io_lock, but generally doesn't */
1162
1163         if (lockit) {
1164                 Glib::Mutex::Lock em (_session.engine().process_lock());
1165                 Glib::Mutex::Lock im (io_lock);
1166                 changed = ensure_outputs_locked (n, clear, src);
1167         } else {
1168                 changed = ensure_outputs_locked (n, clear, src);
1169         }
1170
1171         if (changed) {
1172                  output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1173         }
1174         return 0;
1175 }
1176
1177 gain_t
1178 IO::effective_gain () const
1179 {
1180         if (gain_automation_playback()) {
1181                 return _effective_gain;
1182         } else {
1183                 return _desired_gain;
1184         }
1185 }
1186
1187 void
1188 IO::reset_panner ()
1189 {
1190         if (panners_legal) {
1191                 if (!no_panner_reset) {
1192                         _panner->reset (n_outputs().get(_default_type), pans_required());
1193                 }
1194         } else {
1195                 panner_legal_c.disconnect ();
1196                 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1197         }
1198 }
1199
1200 int
1201 IO::panners_became_legal ()
1202 {
1203         _panner->reset (n_outputs().get(_default_type), pans_required());
1204         _panner->load (); // automation
1205         panner_legal_c.disconnect ();
1206         return 0;
1207 }
1208
1209 void
1210 IO::defer_pan_reset ()
1211 {
1212         no_panner_reset = true;
1213 }
1214
1215 void
1216 IO::allow_pan_reset ()
1217 {
1218         no_panner_reset = false;
1219         reset_panner ();
1220 }
1221
1222
1223 XMLNode&
1224 IO::get_state (void)
1225 {
1226         return state (true);
1227 }
1228
1229 XMLNode&
1230 IO::state (bool full_state)
1231 {
1232         XMLNode* node = new XMLNode (state_node_name);
1233         char buf[64];
1234         string str;
1235         bool need_ins = true;
1236         bool need_outs = true;
1237         LocaleGuard lg (X_("POSIX"));
1238         Glib::Mutex::Lock lm (io_lock);
1239
1240         node->add_property("name", _name);
1241         id().print (buf);
1242         node->add_property("id", buf);
1243
1244         str = "";
1245
1246         if (_input_connection) {
1247                 node->add_property ("input-connection", _input_connection->name());
1248                 need_ins = false;
1249         }
1250
1251         if (_output_connection) {
1252                 node->add_property ("output-connection", _output_connection->name());
1253                 need_outs = false;
1254         }
1255
1256         if (need_ins) {
1257                 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1258                         
1259                         const char **connections = i->get_connections();
1260                         
1261                         if (connections && connections[0]) {
1262                                 str += '{';
1263                                 
1264                                 for (int n = 0; connections && connections[n]; ++n) {
1265                                         if (n) {
1266                                                 str += ',';
1267                                         }
1268                                         
1269                                         /* if its a connection to our own port,
1270                                            return only the port name, not the
1271                                            whole thing. this allows connections
1272                                            to be re-established even when our
1273                                            client name is different.
1274                                         */
1275                                         
1276                                         str += _session.engine().make_port_name_relative (connections[n]);
1277                                 }       
1278
1279                                 str += '}';
1280                                 
1281                                 free (connections);
1282                         }
1283                         else {
1284                                 str += "{}";
1285                         }
1286                 }
1287                 
1288                 node->add_property ("inputs", str);
1289         }
1290
1291         if (need_outs) {
1292                 str = "";
1293                 
1294                 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1295                         
1296                         const char **connections = i->get_connections();
1297                         
1298                         if (connections && connections[0]) {
1299                                 
1300                                 str += '{';
1301                                 
1302                                 for (int n = 0; connections[n]; ++n) {
1303                                         if (n) {
1304                                                 str += ',';
1305                                         }
1306
1307                                         str += _session.engine().make_port_name_relative (connections[n]);
1308                                 }
1309
1310                                 str += '}';
1311                                 
1312                                 free (connections);
1313                         }
1314                         else {
1315                                 str += "{}";
1316                         }
1317                 }
1318                 
1319                 node->add_property ("outputs", str);
1320         }
1321
1322         node->add_child_nocopy (_panner->state (full_state));
1323
1324         snprintf (buf, sizeof(buf), "%2.12f", gain());
1325         node->add_property ("gain", buf);
1326
1327         const int in_min  = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1328         const int in_max  = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1329         const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1330         const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1331
1332         snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1333
1334         node->add_property ("iolimits", buf);
1335
1336         /* automation */
1337
1338         if (full_state) {
1339                 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1340         } else {
1341                 /* never store anything except Off for automation state in a template */
1342                 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); 
1343         }
1344         node->add_property ("automation-state", buf);
1345         snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
1346         node->add_property ("automation-style", buf);
1347
1348         /* XXX same for pan etc. */
1349
1350         return *node;
1351 }
1352
1353 int
1354 IO::connecting_became_legal ()
1355 {
1356         int ret;
1357
1358         if (pending_state_node == 0) {
1359                 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1360                 /*NOTREACHED*/
1361                 return -1;
1362         }
1363
1364         connection_legal_c.disconnect ();
1365
1366         ret = make_connections (*pending_state_node);
1367
1368         if (ports_legal) {
1369                 delete pending_state_node;
1370                 pending_state_node = 0;
1371         }
1372
1373         return ret;
1374 }
1375
1376 int
1377 IO::ports_became_legal ()
1378 {
1379         int ret;
1380
1381         if (pending_state_node == 0) {
1382                 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1383                 /*NOTREACHED*/
1384                 return -1;
1385         }
1386
1387         port_legal_c.disconnect ();
1388
1389         ret = create_ports (*pending_state_node);
1390
1391         if (connecting_legal) {
1392                 delete pending_state_node;
1393                 pending_state_node = 0;
1394         }
1395
1396         return ret;
1397 }
1398
1399 int
1400 IO::set_state (const XMLNode& node)
1401 {
1402         const XMLProperty* prop;
1403         XMLNodeConstIterator iter;
1404         LocaleGuard lg (X_("POSIX"));
1405
1406         /* force use of non-localized representation of decimal point,
1407            since we use it a lot in XML files and so forth.
1408         */
1409
1410         if (node.name() != state_node_name) {
1411                 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1412                 return -1;
1413         }
1414
1415         if ((prop = node.property ("name")) != 0) {
1416                 _name = prop->value();
1417                 _panner->set_name (_name);
1418         } 
1419
1420         if ((prop = node.property ("id")) != 0) {
1421                 _id = prop->value ();
1422         }
1423
1424         size_t in_min =  -1;
1425         size_t in_max  = -1;
1426         size_t out_min = -1;
1427         size_t out_max = -1;
1428
1429         if ((prop = node.property ("iolimits")) != 0) {
1430                 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1431                         &in_min, &in_max, &out_min, &out_max);
1432                 _input_minimum = ChanCount(_default_type, in_min);
1433                 _input_maximum = ChanCount(_default_type, in_max);
1434                 _output_minimum = ChanCount(_default_type, out_min);
1435                 _output_maximum = ChanCount(_default_type, out_max);
1436         }
1437         
1438         if ((prop = node.property ("gain")) != 0) {
1439                 set_gain (atof (prop->value().c_str()), this);
1440                 _gain = _desired_gain;
1441         }
1442
1443         for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1444                 if ((*iter)->name() == "Panner") {
1445                         _panner->set_state (**iter);
1446                 }
1447         }
1448
1449         if ((prop = node.property ("automation-state")) != 0) {
1450
1451                 long int x;
1452                 x = strtol (prop->value().c_str(), 0, 16);
1453                 set_gain_automation_state (AutoState (x));
1454         }
1455
1456         if ((prop = node.property ("automation-style")) != 0) {
1457
1458                long int x;
1459                 x = strtol (prop->value().c_str(), 0, 16);
1460                 set_gain_automation_style (AutoStyle (x));
1461         }
1462         
1463         if (ports_legal) {
1464
1465                 if (create_ports (node)) {
1466                         return -1;
1467                 }
1468
1469         } else {
1470
1471                 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1472         }
1473
1474         if (panners_legal) {
1475                 reset_panner ();
1476         } else {
1477                 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1478         }
1479
1480         if (connecting_legal) {
1481
1482                 if (make_connections (node)) {
1483                         return -1;
1484                 }
1485
1486         } else {
1487                 
1488                 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1489         }
1490
1491         if (!ports_legal || !connecting_legal) {
1492                 pending_state_node = new XMLNode (node);
1493         }
1494
1495         return 0;
1496 }
1497
1498 int
1499 IO::create_ports (const XMLNode& node)
1500 {
1501         const XMLProperty* prop;
1502         int num_inputs = 0;
1503         int num_outputs = 0;
1504
1505         if ((prop = node.property ("input-connection")) != 0) {
1506
1507                 Connection* c = _session.connection_by_name (prop->value());
1508                 
1509                 if (c == 0) {
1510                         error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1511
1512                         if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1513                                 error << _("No input connections available as a replacement")
1514                                       << endmsg;
1515                                 return -1;
1516                         }  else {
1517                                 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1518                                      << endmsg;
1519                         }
1520                 } 
1521
1522                 num_inputs = c->nports();
1523
1524         } else if ((prop = node.property ("inputs")) != 0) {
1525
1526                 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1527         }
1528         
1529         if ((prop = node.property ("output-connection")) != 0) {
1530                 Connection* c = _session.connection_by_name (prop->value());
1531
1532                 if (c == 0) {
1533                         error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1534
1535                         if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1536                                 error << _("No output connections available as a replacement")
1537                                       << endmsg;
1538                                 return -1;
1539                         }  else {
1540                                 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1541                                      << endmsg;
1542                         }
1543                 } 
1544
1545                 num_outputs = c->nports ();
1546                 
1547         } else if ((prop = node.property ("outputs")) != 0) {
1548                 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1549         }
1550
1551         no_panner_reset = true;
1552
1553         if (ensure_io (num_inputs, num_outputs, true, this)) {
1554                 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1555                 return -1;
1556         }
1557
1558         no_panner_reset = false;
1559
1560         set_deferred_state ();
1561
1562         PortsCreated();
1563         return 0;
1564 }
1565
1566
1567 int
1568 IO::make_connections (const XMLNode& node)
1569 {
1570         const XMLProperty* prop;
1571
1572         if ((prop = node.property ("input-connection")) != 0) {
1573                 Connection* c = _session.connection_by_name (prop->value());
1574                 
1575                 if (c == 0) {
1576                         error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1577
1578                         if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1579                                 error << _("No input connections available as a replacement")
1580                                       << endmsg;
1581                                 return -1;
1582                         } else {
1583                                 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1584                                      << endmsg;
1585                         }
1586                 } 
1587
1588                 use_input_connection (*c, this);
1589
1590         } else if ((prop = node.property ("inputs")) != 0) {
1591                 if (set_inputs (prop->value())) {
1592                         error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1593                         return -1;
1594                 }
1595         }
1596         
1597         if ((prop = node.property ("output-connection")) != 0) {
1598                 Connection* c = _session.connection_by_name (prop->value());
1599                 
1600                 if (c == 0) {
1601                         error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1602
1603                         if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1604                                 error << _("No output connections available as a replacement")
1605                                       << endmsg;
1606                                 return -1;
1607                         }  else {
1608                                 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1609                                      << endmsg;
1610                         }
1611                 } 
1612
1613                 use_output_connection (*c, this);
1614                 
1615         } else if ((prop = node.property ("outputs")) != 0) {
1616                 if (set_outputs (prop->value())) {
1617                         error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1618                         return -1;
1619                 }
1620         }
1621         
1622         return 0;
1623 }
1624
1625 int
1626 IO::set_inputs (const string& str)
1627 {
1628         vector<string> ports;
1629         int i;
1630         int n;
1631         uint32_t nports;
1632         
1633         if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1634                 return 0;
1635         }
1636
1637         if (ensure_inputs (nports, true, true, this)) {
1638                 return -1;
1639         }
1640
1641         string::size_type start, end, ostart;
1642
1643         ostart = 0;
1644         start = 0;
1645         end = 0;
1646         i = 0;
1647
1648         while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1649                 start += 1;
1650
1651                 if ((end = str.find_first_of ('}', start)) == string::npos) {
1652                         error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1653                         return -1;
1654                 }
1655
1656                 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1657                         error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1658
1659                         return -1;
1660                         
1661                 } else if (n > 0) {
1662
1663                         for (int x = 0; x < n; ++x) {
1664                                 connect_input (input (i), ports[x], this);
1665                         }
1666                 }
1667
1668                 ostart = end+1;
1669                 i++;
1670         }
1671
1672         return 0;
1673 }
1674
1675 int
1676 IO::set_outputs (const string& str)
1677 {
1678         vector<string> ports;
1679         int i;
1680         int n;
1681         uint32_t nports;
1682         
1683         if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1684                 return 0;
1685         }
1686
1687         if (ensure_outputs (nports, true, true, this)) {
1688                 return -1;
1689         }
1690
1691         string::size_type start, end, ostart;
1692
1693         ostart = 0;
1694         start = 0;
1695         end = 0;
1696         i = 0;
1697
1698         while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1699                 start += 1;
1700
1701                 if ((end = str.find_first_of ('}', start)) == string::npos) {
1702                         error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1703                         return -1;
1704                 }
1705
1706                 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1707                         error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1708
1709                         return -1;
1710                         
1711                 } else if (n > 0) {
1712
1713                         for (int x = 0; x < n; ++x) {
1714                                 connect_output (output (i), ports[x], this);
1715                         }
1716                 }
1717
1718                 ostart = end+1;
1719                 i++;
1720         }
1721
1722         return 0;
1723 }
1724
1725 int
1726 IO::parse_io_string (const string& str, vector<string>& ports)
1727 {
1728         string::size_type pos, opos;
1729
1730         if (str.length() == 0) {
1731                 return 0;
1732         }
1733
1734         pos = 0;
1735         opos = 0;
1736
1737         ports.clear ();
1738
1739         while ((pos = str.find_first_of (',', opos)) != string::npos) {
1740                 ports.push_back (str.substr (opos, pos - opos));
1741                 opos = pos + 1;
1742         }
1743         
1744         if (opos < str.length()) {
1745                 ports.push_back (str.substr(opos));
1746         }
1747
1748         return ports.size();
1749 }
1750
1751 int
1752 IO::parse_gain_string (const string& str, vector<string>& ports)
1753 {
1754         string::size_type pos, opos;
1755
1756         pos = 0;
1757         opos = 0;
1758         ports.clear ();
1759
1760         while ((pos = str.find_first_of (',', opos)) != string::npos) {
1761                 ports.push_back (str.substr (opos, pos - opos));
1762                 opos = pos + 1;
1763         }
1764         
1765         if (opos < str.length()) {
1766                 ports.push_back (str.substr(opos));
1767         }
1768
1769         return ports.size();
1770 }
1771
1772 int
1773 IO::set_name (string name, void* src)
1774 {
1775         if (name == _name) {
1776                 return 0;
1777         }
1778
1779         for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1780                 string current_name = i->short_name();
1781                 current_name.replace (current_name.find (_name), _name.length(), name);
1782                 i->set_name (current_name);
1783         }
1784
1785         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1786                 string current_name = i->short_name();
1787                 current_name.replace (current_name.find (_name), _name.length(), name);
1788                 i->set_name (current_name);
1789         }
1790
1791         _name = name;
1792          name_changed (src); /* EMIT SIGNAL */
1793
1794          return 0;
1795 }
1796
1797 void
1798 IO::set_input_minimum (int n)
1799 {
1800         if (n < 0)
1801                 _input_minimum = ChanCount::ZERO;
1802         else
1803                 _input_minimum = ChanCount(_default_type, n);
1804 }
1805
1806 void
1807 IO::set_input_maximum (int n)
1808 {
1809         if (n < 0)
1810                 _input_maximum = ChanCount::INFINITE;
1811         else
1812                 _input_maximum = ChanCount(_default_type, n);
1813 }
1814
1815 void
1816 IO::set_output_minimum (int n)
1817 {
1818         if (n < 0)
1819                 _output_minimum = ChanCount::ZERO;
1820         else
1821                 _output_minimum = ChanCount(_default_type, n);
1822 }
1823
1824 void
1825 IO::set_output_maximum (int n)
1826 {
1827         if (n < 0)
1828                 _output_maximum = ChanCount::INFINITE;
1829         else
1830                 _output_maximum = ChanCount(_default_type, n);
1831 }
1832
1833 void
1834 IO::set_input_minimum (ChanCount n)
1835 {
1836         _input_minimum = n;
1837 }
1838
1839 void
1840 IO::set_input_maximum (ChanCount n)
1841 {
1842         _input_maximum = n;
1843 }
1844
1845 void
1846 IO::set_output_minimum (ChanCount n)
1847 {
1848         _output_minimum = n;
1849 }
1850
1851 void
1852 IO::set_output_maximum (ChanCount n)
1853 {
1854         _output_maximum = n;
1855 }
1856
1857 void
1858 IO::set_port_latency (jack_nframes_t nframes)
1859 {
1860         Glib::Mutex::Lock lm (io_lock);
1861
1862         for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1863                 i->set_latency (nframes);
1864         }
1865 }
1866
1867 jack_nframes_t
1868 IO::output_latency () const
1869 {
1870         jack_nframes_t max_latency;
1871         jack_nframes_t latency;
1872
1873         max_latency = 0;
1874
1875         /* io lock not taken - must be protected by other means */
1876
1877         for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1878                 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1879                         max_latency = latency;
1880                 }
1881         }
1882
1883         return max_latency;
1884 }
1885
1886 jack_nframes_t
1887 IO::input_latency () const
1888 {
1889         jack_nframes_t max_latency;
1890         jack_nframes_t latency;
1891
1892         max_latency = 0;
1893
1894         /* io lock not taken - must be protected by other means */
1895
1896         for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1897                 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1898                         max_latency = latency;
1899                 }
1900         }
1901
1902         return max_latency;
1903 }
1904
1905 int
1906 IO::use_input_connection (Connection& c, void* src)
1907 {
1908         uint32_t limit;
1909
1910         {
1911                 Glib::Mutex::Lock lm (_session.engine().process_lock());
1912                 Glib::Mutex::Lock lm2 (io_lock);
1913                 
1914                 limit = c.nports();
1915                 
1916                 drop_input_connection ();
1917                 
1918                 if (ensure_inputs (limit, false, false, src)) {
1919                         return -1;
1920                 }
1921
1922                 /* first pass: check the current state to see what's correctly
1923                    connected, and drop anything that we don't want.
1924                 */
1925                 
1926                 for (uint32_t n = 0; n < limit; ++n) {
1927                         const Connection::PortList& pl = c.port_connections (n);
1928                         
1929                         for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1930                                 
1931                                 if (!_inputs.port(n)->connected_to ((*i))) {
1932                                         
1933                                         /* clear any existing connections */
1934                                         
1935                                         _session.engine().disconnect (*_inputs.port(n));
1936                                         
1937                                 } else if (_inputs.port(n)->connected() > 1) {
1938                                         
1939                                         /* OK, it is connected to the port we want,
1940                                            but its also connected to other ports.
1941                                            Change that situation.
1942                                         */
1943                                         
1944                                         /* XXX could be optimized to not drop
1945                                            the one we want.
1946                                         */
1947                                         
1948                                         _session.engine().disconnect (*_inputs.port(n));
1949                                         
1950                                 }
1951                         }
1952                 }
1953                 
1954                 /* second pass: connect all requested ports where necessary */
1955                 
1956                 for (uint32_t n = 0; n < limit; ++n) {
1957                         const Connection::PortList& pl = c.port_connections (n);
1958                         
1959                         for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1960                                 
1961                                 if (!_inputs.port(n)->connected_to ((*i))) {
1962                                         
1963                                         if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1964                                                 return -1;
1965                                         }
1966                                 }
1967                                 
1968                         }
1969                 }
1970                 
1971                 _input_connection = &c;
1972                 
1973                 input_connection_configuration_connection = c.ConfigurationChanged.connect
1974                         (mem_fun (*this, &IO::input_connection_configuration_changed));
1975                 input_connection_connection_connection = c.ConnectionsChanged.connect
1976                         (mem_fun (*this, &IO::input_connection_connection_changed));
1977         }
1978
1979         input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
1980         return 0;
1981 }
1982
1983 int
1984 IO::use_output_connection (Connection& c, void* src)
1985 {
1986         uint32_t limit; 
1987
1988         {
1989                 Glib::Mutex::Lock lm (_session.engine().process_lock());
1990                 Glib::Mutex::Lock lm2 (io_lock);
1991
1992                 limit = c.nports();
1993                         
1994                 drop_output_connection ();
1995
1996                 if (ensure_outputs (limit, false, false, src)) {
1997                         return -1;
1998                 }
1999
2000                 /* first pass: check the current state to see what's correctly
2001                    connected, and drop anything that we don't want.
2002                 */
2003                         
2004                 for (uint32_t n = 0; n < limit; ++n) {
2005
2006                         const Connection::PortList& pl = c.port_connections (n);
2007                                 
2008                         for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2009                                         
2010                                 if (!_outputs.port(n)->connected_to ((*i))) {
2011
2012                                         /* clear any existing connections */
2013
2014                                         _session.engine().disconnect (*_outputs.port(n));
2015
2016                                 } else if (_outputs.port(n)->connected() > 1) {
2017
2018                                         /* OK, it is connected to the port we want,
2019                                            but its also connected to other ports.
2020                                            Change that situation.
2021                                         */
2022
2023                                         /* XXX could be optimized to not drop
2024                                            the one we want.
2025                                         */
2026                                                 
2027                                         _session.engine().disconnect (*_outputs.port(n));
2028                                 }
2029                         }
2030                 }
2031
2032                 /* second pass: connect all requested ports where necessary */
2033
2034                 for (uint32_t n = 0; n < limit; ++n) {
2035
2036                         const Connection::PortList& pl = c.port_connections (n);
2037                                 
2038                         for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2039                                         
2040                                 if (!_outputs.port(n)->connected_to ((*i))) {
2041                                                 
2042                                         if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
2043                                                 return -1;
2044                                         }
2045                                 }
2046                         }
2047                 }
2048
2049                 _output_connection = &c;
2050
2051                 output_connection_configuration_connection = c.ConfigurationChanged.connect
2052                         (mem_fun (*this, &IO::output_connection_configuration_changed));
2053                 output_connection_connection_connection = c.ConnectionsChanged.connect
2054                         (mem_fun (*this, &IO::output_connection_connection_changed));
2055         }
2056
2057         output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
2058
2059         return 0;
2060 }
2061
2062 int
2063 IO::disable_connecting ()
2064 {
2065         connecting_legal = false;
2066         return 0;
2067 }
2068
2069 int
2070 IO::enable_connecting ()
2071 {
2072         connecting_legal = true;
2073         return ConnectingLegal ();
2074 }
2075
2076 int
2077 IO::disable_ports ()
2078 {
2079         ports_legal = false;
2080         return 0;
2081 }
2082
2083 int
2084 IO::enable_ports ()
2085 {
2086         ports_legal = true;
2087         return PortsLegal ();
2088 }
2089
2090 int
2091 IO::disable_panners (void)
2092 {
2093         panners_legal = false;
2094         return 0;
2095 }
2096
2097 int
2098 IO::reset_panners ()
2099 {
2100         panners_legal = true;
2101         return PannersLegal ();
2102 }
2103
2104 void
2105 IO::input_connection_connection_changed (int ignored)
2106 {
2107         use_input_connection (*_input_connection, this);
2108 }
2109
2110 void
2111 IO::input_connection_configuration_changed ()
2112 {
2113         use_input_connection (*_input_connection, this);
2114 }
2115
2116 void
2117 IO::output_connection_connection_changed (int ignored)
2118 {
2119         use_output_connection (*_output_connection, this);
2120 }
2121
2122 void
2123 IO::output_connection_configuration_changed ()
2124 {
2125         use_output_connection (*_output_connection, this);
2126 }
2127
2128 void
2129 IO::GainControllable::set_value (float val)
2130 {
2131         io.set_gain (direct_control_to_gain (val), this);
2132 }
2133
2134 float
2135 IO::GainControllable::get_value (void) const
2136 {
2137         return direct_gain_to_control (io.effective_gain());
2138 }
2139
2140 UndoAction
2141 IO::get_memento() const
2142 {
2143   return sigc::bind (mem_fun (*(const_cast<IO *>(this)), &StateManager::use_state), _current_state_id);
2144 }
2145
2146 Change
2147 IO::restore_state (StateManager::State& state)
2148 {
2149         return Change (0);
2150 }
2151
2152 StateManager::State*
2153 IO::state_factory (std::string why) const
2154 {
2155         StateManager::State* state = new StateManager::State (why);
2156         return state;
2157 }
2158
2159 void
2160 IO::setup_peak_meters()
2161 {
2162         _meter->setup(std::max(_inputs.count(), _outputs.count()));
2163 }
2164
2165 /**
2166     Update the peak meters.
2167
2168     The meter signal lock is taken to prevent modification of the 
2169     Meter signal while updating the meters, taking the meter signal
2170     lock prior to taking the io_lock ensures that all IO will remain 
2171     valid while metering.
2172 */   
2173 void
2174 IO::update_meters()
2175 {
2176     Glib::Mutex::Lock guard (m_meter_signal_lock);
2177     
2178     Meter(); /* EMIT SIGNAL */
2179 }
2180
2181 void
2182 IO::meter ()
2183 {
2184         // FIXME: Remove this function and just connect signal directly to PeakMeter::meter
2185         
2186         Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2187         _meter->meter();
2188 }
2189
2190 int
2191 IO::save_automation (const string& path)
2192 {
2193         string fullpath;
2194         ofstream out;
2195
2196         fullpath = _session.automation_dir();
2197         fullpath += path;
2198
2199         out.open (fullpath.c_str());
2200
2201         if (!out) {
2202                 error << string_compose(_("%1: could not open automation event file \"%2\""), _name, fullpath) << endmsg;
2203                 return -1;
2204         }
2205
2206         out << X_("version ") << current_automation_version_number << endl;
2207
2208         /* XXX use apply_to_points to get thread safety */
2209         
2210         for (AutomationList::iterator i = _gain_automation_curve.begin(); i != _gain_automation_curve.end(); ++i) {
2211                 out << "g " << (jack_nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl;
2212         }
2213
2214         _panner->save ();
2215
2216         return 0;
2217 }
2218
2219 int
2220 IO::load_automation (const string& path)
2221 {
2222         string fullpath;
2223         ifstream in;
2224         char line[128];
2225         uint32_t linecnt = 0;
2226         float version;
2227         LocaleGuard lg (X_("POSIX"));
2228
2229         fullpath = _session.automation_dir();
2230         fullpath += path;
2231
2232         in.open (fullpath.c_str());
2233
2234         if (!in) {
2235                 fullpath = _session.automation_dir();
2236                 fullpath += _session.snap_name();
2237                 fullpath += '-';
2238                 fullpath += path;
2239                 in.open (fullpath.c_str());
2240                 if (!in) {
2241                                 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
2242                                 return -1;
2243                 }
2244         }
2245
2246         clear_automation ();
2247
2248         while (in.getline (line, sizeof(line), '\n')) {
2249                 char type;
2250                 jack_nframes_t when;
2251                 double value;
2252
2253                 if (++linecnt == 1) {
2254                         if (memcmp (line, "version", 7) == 0) {
2255                                 if (sscanf (line, "version %f", &version) != 1) {
2256                                         error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
2257                                         return -1;
2258                                 }
2259                         } else {
2260                                 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
2261                                 return -1;
2262                         }
2263
2264                         if (version != current_automation_version_number) {
2265                                 error << string_compose(_("mismatched automation event file version (%1)"), version) << endmsg;
2266                                 return -1;
2267                         }
2268
2269                         continue;
2270                 }
2271
2272                 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
2273                         warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
2274                         continue;
2275                 }
2276
2277                 switch (type) {
2278                 case 'g':
2279                         _gain_automation_curve.add (when, value, true);
2280                         break;
2281
2282                 case 's':
2283                         break;
2284
2285                 case 'm':
2286                         break;
2287
2288                 case 'p':
2289                         /* older (pre-1.0) versions of ardour used this */
2290                         break;
2291
2292                 default:
2293                         warning << _("dubious automation event found (and ignored)") << endmsg;
2294                 }
2295         }
2296
2297         _gain_automation_curve.save_state (_("loaded from disk"));
2298
2299         return 0;
2300 }
2301         
2302 void
2303 IO::clear_automation ()
2304 {
2305         Glib::Mutex::Lock lm (automation_lock);
2306         _gain_automation_curve.clear ();
2307         _panner->clear_automation ();
2308 }
2309
2310 void
2311 IO::set_gain_automation_state (AutoState state)
2312 {
2313         bool changed = false;
2314
2315         {
2316                 Glib::Mutex::Lock lm (automation_lock);
2317
2318                 if (state != _gain_automation_curve.automation_state()) {
2319                         changed = true;
2320                         last_automation_snapshot = 0;
2321                         _gain_automation_curve.set_automation_state (state);
2322                         
2323                         if (state != Off) {
2324                                 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2325                         }
2326                 }
2327         }
2328
2329         if (changed) {
2330                 _session.set_dirty ();
2331                 gain_automation_state_changed (); /* EMIT SIGNAL */
2332         }
2333 }
2334
2335 void
2336 IO::set_gain_automation_style (AutoStyle style)
2337 {
2338         bool changed = false;
2339
2340         {
2341                 Glib::Mutex::Lock lm (automation_lock);
2342
2343                 if (style != _gain_automation_curve.automation_style()) {
2344                         changed = true;
2345                         _gain_automation_curve.set_automation_style (style);
2346                 }
2347         }
2348
2349         if (changed) {
2350                 gain_automation_style_changed (); /* EMIT SIGNAL */
2351         }
2352 }
2353 void
2354 IO::inc_gain (gain_t factor, void *src)
2355 {
2356         if (_desired_gain == 0.0f)
2357                 set_gain (0.000001f + (0.000001f * factor), src);
2358         else
2359                 set_gain (_desired_gain + (_desired_gain * factor), src);
2360 }
2361
2362 void
2363 IO::set_gain (gain_t val, void *src)
2364 {
2365         // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2366         if (val>1.99526231f) val=1.99526231f;
2367
2368         {
2369                 Glib::Mutex::Lock dm (declick_lock);
2370                 _desired_gain = val;
2371         }
2372
2373         if (_session.transport_stopped()) {
2374                 _effective_gain = val;
2375                 _gain = val;
2376         }
2377
2378         gain_changed (src);
2379         _gain_control.Changed (); /* EMIT SIGNAL */
2380         
2381         if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2382                 _gain_automation_curve.add (_session.transport_frame(), val);
2383                 
2384         }
2385
2386         _session.set_dirty();
2387 }
2388
2389 void
2390 IO::start_gain_touch ()
2391 {
2392         _gain_automation_curve.start_touch ();
2393 }
2394
2395 void
2396 IO::end_gain_touch ()
2397 {
2398         _gain_automation_curve.stop_touch ();
2399 }
2400
2401 void
2402 IO::start_pan_touch (uint32_t which)
2403 {
2404         if (which < _panner->size()) {
2405                 (*_panner)[which]->automation().start_touch();
2406         }
2407 }
2408
2409 void
2410 IO::end_pan_touch (uint32_t which)
2411 {
2412         if (which < _panner->size()) {
2413                 (*_panner)[which]->automation().stop_touch();
2414         }
2415
2416 }
2417
2418 void
2419 IO::automation_snapshot (jack_nframes_t now)
2420 {
2421         if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2422
2423                 if (gain_automation_recording()) {
2424                         _gain_automation_curve.rt_add (now, gain());
2425                 }
2426                 
2427                 _panner->snapshot (now);
2428
2429                 last_automation_snapshot = now;
2430         }
2431 }
2432
2433 void
2434 IO::transport_stopped (jack_nframes_t frame)
2435 {
2436         _gain_automation_curve.reposition_for_rt_add (frame);
2437
2438         if (_gain_automation_curve.automation_state() != Off) {
2439                 
2440                 if (gain_automation_recording()) {
2441                         _gain_automation_curve.save_state (_("automation write/touch"));
2442                 }
2443
2444                 /* the src=0 condition is a special signal to not propagate 
2445                    automation gain changes into the mix group when locating.
2446                 */
2447
2448                 set_gain (_gain_automation_curve.eval (frame), 0);
2449         }
2450
2451         _panner->transport_stopped (frame);
2452 }
2453
2454 int32_t
2455 IO::find_input_port_hole ()
2456 {
2457         /* CALLER MUST HOLD IO LOCK */
2458
2459         uint32_t n;
2460
2461         if (_inputs.empty()) {
2462                 return 1;
2463         }
2464
2465         for (n = 1; n < UINT_MAX; ++n) {
2466                 char buf[jack_port_name_size()];
2467                 PortSet::iterator i = _inputs.begin();
2468
2469                 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2470
2471                 for ( ; i != _inputs.end(); ++i) {
2472                         if (i->short_name() == buf) {
2473                                 break;
2474                         }
2475                 }
2476
2477                 if (i == _inputs.end()) {
2478                         break;
2479                 }
2480         }
2481         return n;
2482 }
2483
2484 int32_t
2485 IO::find_output_port_hole ()
2486 {
2487         /* CALLER MUST HOLD IO LOCK */
2488
2489         uint32_t n;
2490
2491         if (_outputs.empty()) {
2492                 return 1;
2493         }
2494
2495         for (n = 1; n < UINT_MAX; ++n) {
2496                 char buf[jack_port_name_size()];
2497                 PortSet::iterator i = _outputs.begin();
2498
2499                 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2500
2501                 for ( ; i != _outputs.end(); ++i) {
2502                         if (i->short_name() == buf) {
2503                                 break;
2504                         }
2505                 }
2506
2507                 if (i == _outputs.end()) {
2508                         break;
2509                 }
2510         }
2511         
2512         return n;
2513 }
2514
2515 AudioPort*
2516 IO::audio_input(uint32_t n) const
2517 {
2518         return dynamic_cast<AudioPort*>(input(n));
2519 }
2520
2521 AudioPort*
2522 IO::audio_output(uint32_t n) const
2523 {
2524         return dynamic_cast<AudioPort*>(output(n));
2525 }
2526
2527 MidiPort*
2528 IO::midi_input(uint32_t n) const
2529 {
2530         return dynamic_cast<MidiPort*>(input(n));
2531 }
2532
2533 MidiPort*
2534 IO::midi_output(uint32_t n) const
2535 {
2536         return dynamic_cast<MidiPort*>(output(n));
2537 }
2538