2 Copyright (C) 2000-2006 Paul Davis
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.
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.
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.
24 #include <sigc++/bind.h>
26 #include <glibmm/thread.h>
28 #include <pbd/xml++.h>
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 #include <ardour/amp.h>
48 A bug in OS X's cmath that causes isnan() and isinf() to be
49 "undeclared". the following works around that
52 #if defined(__APPLE__) && defined(__MACH__)
53 extern "C" int isnan (double);
54 extern "C" int isinf (double);
59 using namespace ARDOUR;
63 static float current_automation_version_number = 1.0;
65 jack_nframes_t IO::_automation_interval = 0;
66 const string IO::state_node_name = "IO";
67 bool IO::connecting_legal = false;
68 bool IO::ports_legal = false;
69 bool IO::panners_legal = false;
70 sigc::signal<void> IO::Meter;
71 sigc::signal<int> IO::ConnectingLegal;
72 sigc::signal<int> IO::PortsLegal;
73 sigc::signal<int> IO::PannersLegal;
74 sigc::signal<void,ChanCount> IO::MoreChannels;
75 sigc::signal<int> IO::PortsCreated;
77 Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT;
79 /* this is a default mapper of [0 .. 1.0] control values to a gain coefficient.
80 others can be imagined.
83 static gain_t direct_control_to_gain (double fract) {
84 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
85 /* this maxes at +6dB */
86 return pow (2.0,(sqrt(sqrt(sqrt(fract)))*198.0-192.0)/6.0);
89 static double direct_gain_to_control (gain_t gain) {
90 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
91 if (gain == 0) return 0.0;
93 return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0);
97 /** @param default_type The type of port that will be created by ensure_io
98 * and friends if no type is explicitly requested (to avoid breakage).
100 IO::IO (Session& s, string name,
101 int input_min, int input_max, int output_min, int output_max,
102 DataType default_type)
104 _output_buffers(new BufferSet()),
106 _default_type(default_type),
107 _gain_control (*this),
108 _gain_automation_curve (0.0, 2.0, 1.0),
109 _input_minimum (_default_type, input_min),
110 _input_maximum (_default_type, input_max),
111 _output_minimum (_default_type, output_min),
112 _output_maximum (_default_type, output_max)
114 _panner = new Panner (name, _session);
115 _meter = new PeakMeter (_session);
119 _input_connection = 0;
120 _output_connection = 0;
121 pending_state_node = 0;
122 no_panner_reset = false;
123 _phase_invert = false;
126 apply_gain_automation = false;
128 last_automation_snapshot = 0;
130 _gain_automation_state = Off;
131 _gain_automation_style = Absolute;
134 // IO::Meter is emitted from another thread so the
135 // Meter signal must be protected.
136 Glib::Mutex::Lock guard (m_meter_signal_lock);
137 m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
140 // Connect to our own MoreChannels signal to connect output buffers
141 IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
146 Glib::Mutex::Lock guard (m_meter_signal_lock);
147 Glib::Mutex::Lock lm (io_lock);
149 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
150 _session.engine().unregister_port (*i);
153 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
154 _session.engine().unregister_port (*i);
157 m_meter_connection.disconnect();
161 delete _output_buffers;
165 IO::silence (jack_nframes_t nframes, jack_nframes_t offset)
167 /* io_lock, not taken: function must be called from Session::process() calltree */
169 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
170 i->silence (nframes, offset);
174 /** Deliver bufs to the IO's Jack outputs.
176 * This function should automatically do whatever it necessary to correctly deliver bufs
177 * to the outputs, eg applying gain or pan or whatever else needs to be done.
180 IO::deliver_output (BufferSet& bufs, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset)
182 // FIXME: type specific code doesn't actually need to be here, it will go away in time
185 /* ********** AUDIO ********** */
187 // Apply gain if gain automation isn't playing
188 if ( ! apply_gain_automation) {
190 gain_t dg = _gain; // desired gain
193 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
200 Amp::run(bufs, nframes, _gain, dg, _phase_invert);
203 // Use the panner to distribute audio to output port buffers
204 if (_panner && !_panner->empty() && !_panner->bypassed()) {
205 _panner->distribute(bufs, output_buffers(), start_frame, end_frame, nframes, offset);
209 /* ********** MIDI ********** */
211 // No MIDI, we're done here
212 if (bufs.count().get(DataType::MIDI) == 0) {
216 const DataType type = DataType::MIDI; // type type type type...
218 // Just dump any MIDI 1-to-1, we're not at all clever with MIDI routing yet
219 BufferSet::iterator o = output_buffers().begin(type);
220 for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i) {
222 for (PortSet::iterator i = _inputs.begin(type); i != _inputs.end(type); ++i, ++o) {
223 o->read_from(i->get_buffer(), nframes, offset);
229 IO::collect_input (BufferSet& outs, jack_nframes_t nframes, jack_nframes_t offset)
231 outs.set_count(n_inputs());
233 if (outs.count() == ChanCount::ZERO)
236 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
238 BufferSet::iterator o = outs.begin(*t);
239 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
240 o->read_from(i->get_buffer(), nframes, offset);
247 IO::just_meter_input (jack_nframes_t start_frame, jack_nframes_t end_frame,
248 jack_nframes_t nframes, jack_nframes_t offset)
250 BufferSet& bufs = _session.get_scratch_buffers ();
251 ChanCount nbufs = n_process_buffers ();
253 collect_input (bufs, nframes, offset);
255 _meter->run(bufs, nframes);
259 IO::drop_input_connection ()
261 _input_connection = 0;
262 input_connection_configuration_connection.disconnect();
263 input_connection_connection_connection.disconnect();
264 _session.set_dirty ();
268 IO::drop_output_connection ()
270 _output_connection = 0;
271 output_connection_configuration_connection.disconnect();
272 output_connection_connection_connection.disconnect();
273 _session.set_dirty ();
277 IO::disconnect_input (Port* our_port, string other_port, void* src)
279 if (other_port.length() == 0 || our_port == 0) {
284 Glib::Mutex::Lock em (_session.engine().process_lock());
287 Glib::Mutex::Lock lm (io_lock);
289 /* check that our_port is really one of ours */
291 if ( ! _inputs.contains(our_port)) {
295 /* disconnect it from the source */
297 if (_session.engine().disconnect (other_port, our_port->name())) {
298 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
302 drop_input_connection();
306 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
307 _session.set_dirty ();
313 IO::connect_input (Port* our_port, string other_port, void* src)
315 if (other_port.length() == 0 || our_port == 0) {
320 Glib::Mutex::Lock em(_session.engine().process_lock());
323 Glib::Mutex::Lock lm (io_lock);
325 /* check that our_port is really one of ours */
327 if ( ! _inputs.contains(our_port) ) {
331 /* connect it to the source */
333 if (_session.engine().connect (other_port, our_port->name())) {
337 drop_input_connection ();
341 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
342 _session.set_dirty ();
347 IO::disconnect_output (Port* our_port, string other_port, void* src)
349 if (other_port.length() == 0 || our_port == 0) {
354 Glib::Mutex::Lock em(_session.engine().process_lock());
357 Glib::Mutex::Lock lm (io_lock);
359 /* check that our_port is really one of ours */
361 if ( ! _outputs.contains(our_port) ) {
365 /* disconnect it from the destination */
367 if (_session.engine().disconnect (our_port->name(), other_port)) {
368 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
372 drop_output_connection ();
376 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
377 _session.set_dirty ();
382 IO::connect_output (Port* our_port, string other_port, void* src)
384 if (other_port.length() == 0 || our_port == 0) {
389 Glib::Mutex::Lock em(_session.engine().process_lock());
392 Glib::Mutex::Lock lm (io_lock);
394 /* check that our_port is really one of ours */
396 if ( ! _outputs.contains(our_port) ) {
400 /* connect it to the destination */
402 if (_session.engine().connect (our_port->name(), other_port)) {
406 drop_output_connection ();
410 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
411 _session.set_dirty ();
416 IO::set_input (Port* other_port, void* src)
418 /* this removes all but one ports, and connects that one port
419 to the specified source.
422 if (_input_minimum.get_total() > 1) {
423 /* sorry, you can't do this */
427 if (other_port == 0) {
428 if (_input_minimum == ChanCount::ZERO) {
429 return ensure_inputs (0, false, true, src);
435 if (ensure_inputs (1, true, true, src)) {
439 return connect_input (_inputs.port(0), other_port->name(), src);
443 IO::remove_output_port (Port* port, void* src)
447 IOChange change (NoChange);
450 Glib::Mutex::Lock em(_session.engine().process_lock());
453 Glib::Mutex::Lock lm (io_lock);
455 if (_noutputs - 1 == (uint32_t) _output_minimum) {
456 /* sorry, you can't do this */
460 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
462 change = IOChange (change|ConfigurationChanged);
463 if (port->connected()) {
464 change = IOChange (change|ConnectionsChanged);
467 _session.engine().unregister_port (*i);
470 drop_output_connection ();
476 if (change != NoChange) {
477 setup_peak_meters ();
483 if (change != NoChange) {
484 output_changed (change, src); /* EMIT SIGNAL */
485 _session.set_dirty ();
492 /** Add an output port.
494 * @param destination Name of input port to connect new port to.
495 * @param src Source for emitted ConfigurationChanged signal.
496 * @param type Data type of port. Default value (NIL) will use this IO's default type.
499 IO::add_output_port (string destination, void* src, DataType type)
504 if (type == DataType::NIL)
505 type = _default_type;
508 Glib::Mutex::Lock em(_session.engine().process_lock());
511 Glib::Mutex::Lock lm (io_lock);
513 if (n_outputs() >= _output_maximum) {
517 /* Create a new output port */
519 // FIXME: naming scheme for differently typed ports?
520 if (_output_maximum.get_total() == 1) {
521 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
523 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
526 if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
527 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
531 _outputs.add_port (our_port);
532 drop_output_connection ();
533 setup_peak_meters ();
537 MoreChannels (n_outputs()); /* EMIT SIGNAL */
540 if (destination.length()) {
541 if (_session.engine().connect (our_port->name(), destination)) {
546 // pan_changed (src); /* EMIT SIGNAL */
547 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
548 _session.set_dirty ();
554 IO::remove_input_port (Port* port, void* src)
558 IOChange change (NoChange);
561 Glib::Mutex::Lock em(_session.engine().process_lock());
564 Glib::Mutex::Lock lm (io_lock);
566 if (((int)_ninputs - 1) < _input_minimum) {
567 /* sorry, you can't do this */
570 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
573 change = IOChange (change|ConfigurationChanged);
575 if (port->connected()) {
576 change = IOChange (change|ConnectionsChanged);
579 _session.engine().unregister_port (*i);
582 drop_input_connection ();
588 if (change != NoChange) {
589 setup_peak_meters ();
595 if (change != NoChange) {
596 input_changed (change, src);
597 _session.set_dirty ();
605 /** Add an input port.
607 * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
608 * @param destination Name of input port to connect new port to.
609 * @param src Source for emitted ConfigurationChanged signal.
612 IO::add_input_port (string source, void* src, DataType type)
617 if (type == DataType::NIL)
618 type = _default_type;
621 Glib::Mutex::Lock em (_session.engine().process_lock());
624 Glib::Mutex::Lock lm (io_lock);
626 if (n_inputs() >= _input_maximum) {
630 /* Create a new input port */
632 // FIXME: naming scheme for differently typed ports?
633 if (_input_maximum.get_total() == 1) {
634 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
636 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
639 if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
640 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
644 _inputs.add_port(our_port);
645 drop_input_connection ();
646 setup_peak_meters ();
650 MoreChannels (n_inputs()); /* EMIT SIGNAL */
653 if (source.length()) {
655 if (_session.engine().connect (source, our_port->name())) {
660 // pan_changed (src); /* EMIT SIGNAL */
661 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
662 _session.set_dirty ();
668 IO::disconnect_inputs (void* src)
671 Glib::Mutex::Lock em (_session.engine().process_lock());
674 Glib::Mutex::Lock lm (io_lock);
676 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
677 _session.engine().disconnect (*i);
680 drop_input_connection ();
684 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
690 IO::disconnect_outputs (void* src)
693 Glib::Mutex::Lock em (_session.engine().process_lock());
696 Glib::Mutex::Lock lm (io_lock);
698 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
699 _session.engine().disconnect (*i);
702 drop_output_connection ();
706 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
707 _session.set_dirty ();
713 IO::ensure_inputs_locked (uint32_t n, bool clear, void* src)
716 bool changed = false;
718 /* remove unused ports */
720 while (n_inputs().get(_default_type) > n) {
723 _session.engine().unregister_port (_inputs.back());
730 /* create any necessary new ports */
732 while (n_inputs().get(_default_type) < n) {
736 /* Create a new input port (of the default type) */
738 if (_input_maximum.get_total() == 1) {
739 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
742 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
747 if ((input_port = _session.engine().register_input_port (_default_type, buf)) == 0) {
748 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
753 catch (AudioEngine::PortRegistrationFailure& err) {
754 setup_peak_meters ();
760 _inputs.add_port (input_port);
765 drop_input_connection ();
766 setup_peak_meters ();
768 MoreChannels (n_inputs()); /* EMIT SIGNAL */
769 _session.set_dirty ();
773 /* disconnect all existing ports so that we get a fresh start */
775 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
776 _session.engine().disconnect (*i);
783 /** Attach output_buffers to port buffers.
785 * Connected to IOs own MoreChannels signal.
788 IO::attach_buffers(ChanCount ignored)
790 _output_buffers->attach_buffers(_outputs);
794 IO::ensure_io (const ChanCount& in, const ChanCount& out, bool clear, void* src)
797 uint32_t nin = in.get(_default_type);
798 uint32_t nout = out.get(_default_type);
800 // We only deal with one type still. Sorry about your luck.
801 assert(nin == in.get_total());
802 assert(nout == out.get_total());
804 return ensure_io(nin, nout, clear, src);
808 IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
810 bool in_changed = false;
811 bool out_changed = false;
814 nin = min (_input_maximum.get(_default_type), static_cast<size_t>(nin));
816 nout = min (_output_maximum.get(_default_type), static_cast<size_t>(nout));
818 if (nin == n_inputs().get(_default_type) && nout == n_outputs().get(_default_type) && !clear) {
823 Glib::Mutex::Lock em (_session.engine().process_lock());
824 Glib::Mutex::Lock lm (io_lock);
828 if (n_outputs().get(_default_type) == nout) {
829 need_pan_reset = false;
831 need_pan_reset = true;
834 /* remove unused ports */
836 while (n_inputs().get(_default_type) > nin) {
839 _session.engine().unregister_port (_inputs.back());
845 while (n_outputs().get(_default_type) > nout) {
848 _session.engine().unregister_port (_outputs.back());
851 out_changed = true;*/
854 /* create any necessary new ports (of the default type) */
856 while (n_inputs().get(_default_type) < nin) {
860 /* Create a new input port */
862 if (_input_maximum.get_total() == 1) {
863 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
866 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
870 if ((port = _session.engine().register_input_port (_default_type, buf)) == 0) {
871 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
876 catch (AudioEngine::PortRegistrationFailure& err) {
877 setup_peak_meters ();
883 _inputs.add_port (port);
887 /* create any necessary new ports */
889 while (n_outputs().get(_default_type) < nout) {
893 /* Create a new output port */
895 if (_output_maximum.get_total() == 1) {
896 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
898 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
902 if ((port = _session.engine().register_output_port (_default_type, buf)) == 0) {
903 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
908 catch (AudioEngine::PortRegistrationFailure& err) {
909 setup_peak_meters ();
915 _outputs.add_port (port);
921 /* disconnect all existing ports so that we get a fresh start */
923 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
924 _session.engine().disconnect (*i);
927 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
928 _session.engine().disconnect (*i);
932 if (in_changed || out_changed) {
933 setup_peak_meters ();
939 drop_output_connection ();
940 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
944 drop_input_connection ();
945 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
948 if (in_changed || out_changed) {
949 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
950 _session.set_dirty ();
957 IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
959 bool changed = false;
961 n = min (_input_maximum.get(_default_type), static_cast<size_t>(n));
963 if (n == n_inputs().get(_default_type) && !clear) {
968 Glib::Mutex::Lock em (_session.engine().process_lock());
969 Glib::Mutex::Lock im (io_lock);
970 changed = ensure_inputs_locked (n, clear, src);
972 changed = ensure_inputs_locked (n, clear, src);
976 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
977 _session.set_dirty ();
983 IO::ensure_outputs_locked (uint32_t n, bool clear, void* src)
986 bool changed = false;
989 if (n_outputs().get(_default_type) == n) {
990 need_pan_reset = false;
992 need_pan_reset = true;
995 /* remove unused ports */
997 while (n_outputs().get(_default_type) > n) {
1000 _session.engine().unregister_port (_outputs.back());
1001 _outputs.pop_back();
1007 /* create any necessary new ports */
1009 while (n_outputs().get(_default_type) < n) {
1013 /* Create a new output port */
1015 if (_output_maximum.get(_default_type) == 1) {
1016 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1018 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1021 if ((output_port = _session.engine().register_output_port (_default_type, buf)) == 0) {
1022 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1026 _outputs.add_port (output_port);
1028 setup_peak_meters ();
1030 if (need_pan_reset) {
1036 drop_output_connection ();
1037 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1038 _session.set_dirty ();
1042 /* disconnect all existing ports so that we get a fresh start */
1044 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1045 _session.engine().disconnect (*i);
1053 IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
1055 bool changed = false;
1057 if (_output_maximum < ChanCount::INFINITE) {
1058 n = min (_output_maximum.get(_default_type), static_cast<size_t>(n));
1059 if (n == n_outputs().get(_default_type) && !clear) {
1064 /* XXX caller should hold io_lock, but generally doesn't */
1067 Glib::Mutex::Lock em (_session.engine().process_lock());
1068 Glib::Mutex::Lock im (io_lock);
1069 changed = ensure_outputs_locked (n, clear, src);
1071 changed = ensure_outputs_locked (n, clear, src);
1075 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1081 IO::effective_gain () const
1083 if (gain_automation_playback()) {
1084 return _effective_gain;
1086 return _desired_gain;
1093 if (panners_legal) {
1094 if (!no_panner_reset) {
1095 _panner->reset (n_outputs().get(_default_type), pans_required());
1098 panner_legal_c.disconnect ();
1099 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1104 IO::panners_became_legal ()
1106 _panner->reset (n_outputs().get(_default_type), pans_required());
1107 _panner->load (); // automation
1108 panner_legal_c.disconnect ();
1113 IO::defer_pan_reset ()
1115 no_panner_reset = true;
1119 IO::allow_pan_reset ()
1121 no_panner_reset = false;
1127 IO::get_state (void)
1129 return state (true);
1133 IO::state (bool full_state)
1135 XMLNode* node = new XMLNode (state_node_name);
1138 bool need_ins = true;
1139 bool need_outs = true;
1140 LocaleGuard lg (X_("POSIX"));
1141 Glib::Mutex::Lock lm (io_lock);
1143 node->add_property("name", _name);
1145 node->add_property("id", buf);
1149 if (_input_connection) {
1150 node->add_property ("input-connection", _input_connection->name());
1154 if (_output_connection) {
1155 node->add_property ("output-connection", _output_connection->name());
1160 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1162 const char **connections = i->get_connections();
1164 if (connections && connections[0]) {
1167 for (int n = 0; connections && connections[n]; ++n) {
1172 /* if its a connection to our own port,
1173 return only the port name, not the
1174 whole thing. this allows connections
1175 to be re-established even when our
1176 client name is different.
1179 str += _session.engine().make_port_name_relative (connections[n]);
1191 node->add_property ("inputs", str);
1197 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1199 const char **connections = i->get_connections();
1201 if (connections && connections[0]) {
1205 for (int n = 0; connections[n]; ++n) {
1210 str += _session.engine().make_port_name_relative (connections[n]);
1222 node->add_property ("outputs", str);
1225 node->add_child_nocopy (_panner->state (full_state));
1227 snprintf (buf, sizeof(buf), "%2.12f", gain());
1228 node->add_property ("gain", buf);
1230 const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1231 const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1232 const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1233 const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1235 snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1237 node->add_property ("iolimits", buf);
1242 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1244 /* never store anything except Off for automation state in a template */
1245 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
1247 node->add_property ("automation-state", buf);
1248 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
1249 node->add_property ("automation-style", buf);
1251 /* XXX same for pan etc. */
1257 IO::connecting_became_legal ()
1261 if (pending_state_node == 0) {
1262 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1267 connection_legal_c.disconnect ();
1269 ret = make_connections (*pending_state_node);
1272 delete pending_state_node;
1273 pending_state_node = 0;
1280 IO::ports_became_legal ()
1284 if (pending_state_node == 0) {
1285 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1290 port_legal_c.disconnect ();
1292 ret = create_ports (*pending_state_node);
1294 if (connecting_legal) {
1295 delete pending_state_node;
1296 pending_state_node = 0;
1303 IO::set_state (const XMLNode& node)
1305 const XMLProperty* prop;
1306 XMLNodeConstIterator iter;
1307 LocaleGuard lg (X_("POSIX"));
1309 /* force use of non-localized representation of decimal point,
1310 since we use it a lot in XML files and so forth.
1313 if (node.name() != state_node_name) {
1314 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1318 if ((prop = node.property ("name")) != 0) {
1319 _name = prop->value();
1320 _panner->set_name (_name);
1323 if ((prop = node.property ("id")) != 0) {
1324 _id = prop->value ();
1329 size_t out_min = -1;
1330 size_t out_max = -1;
1332 if ((prop = node.property ("iolimits")) != 0) {
1333 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1334 &in_min, &in_max, &out_min, &out_max);
1335 _input_minimum = ChanCount(_default_type, in_min);
1336 _input_maximum = ChanCount(_default_type, in_max);
1337 _output_minimum = ChanCount(_default_type, out_min);
1338 _output_maximum = ChanCount(_default_type, out_max);
1341 if ((prop = node.property ("gain")) != 0) {
1342 set_gain (atof (prop->value().c_str()), this);
1343 _gain = _desired_gain;
1346 for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1347 if ((*iter)->name() == "Panner") {
1348 _panner->set_state (**iter);
1352 if ((prop = node.property ("automation-state")) != 0) {
1355 x = strtol (prop->value().c_str(), 0, 16);
1356 set_gain_automation_state (AutoState (x));
1359 if ((prop = node.property ("automation-style")) != 0) {
1362 x = strtol (prop->value().c_str(), 0, 16);
1363 set_gain_automation_style (AutoStyle (x));
1368 if (create_ports (node)) {
1374 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1377 if (panners_legal) {
1380 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1383 if (connecting_legal) {
1385 if (make_connections (node)) {
1391 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1394 if (!ports_legal || !connecting_legal) {
1395 pending_state_node = new XMLNode (node);
1402 IO::create_ports (const XMLNode& node)
1404 const XMLProperty* prop;
1406 int num_outputs = 0;
1408 if ((prop = node.property ("input-connection")) != 0) {
1410 Connection* c = _session.connection_by_name (prop->value());
1413 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1415 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1416 error << _("No input connections available as a replacement")
1420 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1425 num_inputs = c->nports();
1427 } else if ((prop = node.property ("inputs")) != 0) {
1429 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1432 if ((prop = node.property ("output-connection")) != 0) {
1433 Connection* c = _session.connection_by_name (prop->value());
1436 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1438 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1439 error << _("No output connections available as a replacement")
1443 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1448 num_outputs = c->nports ();
1450 } else if ((prop = node.property ("outputs")) != 0) {
1451 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1454 no_panner_reset = true;
1456 if (ensure_io (num_inputs, num_outputs, true, this)) {
1457 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1461 no_panner_reset = false;
1463 set_deferred_state ();
1471 IO::make_connections (const XMLNode& node)
1473 const XMLProperty* prop;
1475 if ((prop = node.property ("input-connection")) != 0) {
1476 Connection* c = _session.connection_by_name (prop->value());
1479 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1481 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1482 error << _("No input connections available as a replacement")
1486 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1491 use_input_connection (*c, this);
1493 } else if ((prop = node.property ("inputs")) != 0) {
1494 if (set_inputs (prop->value())) {
1495 error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1500 if ((prop = node.property ("output-connection")) != 0) {
1501 Connection* c = _session.connection_by_name (prop->value());
1504 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1506 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1507 error << _("No output connections available as a replacement")
1511 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1516 use_output_connection (*c, this);
1518 } else if ((prop = node.property ("outputs")) != 0) {
1519 if (set_outputs (prop->value())) {
1520 error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1529 IO::set_inputs (const string& str)
1531 vector<string> ports;
1536 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1540 if (ensure_inputs (nports, true, true, this)) {
1544 string::size_type start, end, ostart;
1551 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1554 if ((end = str.find_first_of ('}', start)) == string::npos) {
1555 error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1559 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1560 error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1566 for (int x = 0; x < n; ++x) {
1567 connect_input (input (i), ports[x], this);
1579 IO::set_outputs (const string& str)
1581 vector<string> ports;
1586 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1590 if (ensure_outputs (nports, true, true, this)) {
1594 string::size_type start, end, ostart;
1601 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1604 if ((end = str.find_first_of ('}', start)) == string::npos) {
1605 error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1609 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1610 error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1616 for (int x = 0; x < n; ++x) {
1617 connect_output (output (i), ports[x], this);
1629 IO::parse_io_string (const string& str, vector<string>& ports)
1631 string::size_type pos, opos;
1633 if (str.length() == 0) {
1642 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1643 ports.push_back (str.substr (opos, pos - opos));
1647 if (opos < str.length()) {
1648 ports.push_back (str.substr(opos));
1651 return ports.size();
1655 IO::parse_gain_string (const string& str, vector<string>& ports)
1657 string::size_type pos, opos;
1663 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1664 ports.push_back (str.substr (opos, pos - opos));
1668 if (opos < str.length()) {
1669 ports.push_back (str.substr(opos));
1672 return ports.size();
1676 IO::set_name (string name, void* src)
1678 if (name == _name) {
1682 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1683 string current_name = i->short_name();
1684 current_name.replace (current_name.find (_name), _name.length(), name);
1685 i->set_name (current_name);
1688 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1689 string current_name = i->short_name();
1690 current_name.replace (current_name.find (_name), _name.length(), name);
1691 i->set_name (current_name);
1695 name_changed (src); /* EMIT SIGNAL */
1701 IO::set_input_minimum (int n)
1704 _input_minimum = ChanCount::ZERO;
1706 _input_minimum = ChanCount(_default_type, n);
1710 IO::set_input_maximum (int n)
1713 _input_maximum = ChanCount::INFINITE;
1715 _input_maximum = ChanCount(_default_type, n);
1719 IO::set_output_minimum (int n)
1722 _output_minimum = ChanCount::ZERO;
1724 _output_minimum = ChanCount(_default_type, n);
1728 IO::set_output_maximum (int n)
1731 _output_maximum = ChanCount::INFINITE;
1733 _output_maximum = ChanCount(_default_type, n);
1737 IO::set_input_minimum (ChanCount n)
1743 IO::set_input_maximum (ChanCount n)
1749 IO::set_output_minimum (ChanCount n)
1751 _output_minimum = n;
1755 IO::set_output_maximum (ChanCount n)
1757 _output_maximum = n;
1761 IO::set_port_latency (jack_nframes_t nframes)
1763 Glib::Mutex::Lock lm (io_lock);
1765 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1766 i->set_latency (nframes);
1771 IO::output_latency () const
1773 jack_nframes_t max_latency;
1774 jack_nframes_t latency;
1778 /* io lock not taken - must be protected by other means */
1780 for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1781 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1782 max_latency = latency;
1790 IO::input_latency () const
1792 jack_nframes_t max_latency;
1793 jack_nframes_t latency;
1797 /* io lock not taken - must be protected by other means */
1799 for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1800 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1801 max_latency = latency;
1809 IO::use_input_connection (Connection& c, void* src)
1814 Glib::Mutex::Lock lm (_session.engine().process_lock());
1815 Glib::Mutex::Lock lm2 (io_lock);
1819 drop_input_connection ();
1821 if (ensure_inputs (limit, false, false, src)) {
1825 /* first pass: check the current state to see what's correctly
1826 connected, and drop anything that we don't want.
1829 for (uint32_t n = 0; n < limit; ++n) {
1830 const Connection::PortList& pl = c.port_connections (n);
1832 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1834 if (!_inputs.port(n)->connected_to ((*i))) {
1836 /* clear any existing connections */
1838 _session.engine().disconnect (*_inputs.port(n));
1840 } else if (_inputs.port(n)->connected() > 1) {
1842 /* OK, it is connected to the port we want,
1843 but its also connected to other ports.
1844 Change that situation.
1847 /* XXX could be optimized to not drop
1851 _session.engine().disconnect (*_inputs.port(n));
1857 /* second pass: connect all requested ports where necessary */
1859 for (uint32_t n = 0; n < limit; ++n) {
1860 const Connection::PortList& pl = c.port_connections (n);
1862 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1864 if (!_inputs.port(n)->connected_to ((*i))) {
1866 if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1874 _input_connection = &c;
1876 input_connection_configuration_connection = c.ConfigurationChanged.connect
1877 (mem_fun (*this, &IO::input_connection_configuration_changed));
1878 input_connection_connection_connection = c.ConnectionsChanged.connect
1879 (mem_fun (*this, &IO::input_connection_connection_changed));
1882 input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
1887 IO::use_output_connection (Connection& c, void* src)
1892 Glib::Mutex::Lock lm (_session.engine().process_lock());
1893 Glib::Mutex::Lock lm2 (io_lock);
1897 drop_output_connection ();
1899 if (ensure_outputs (limit, false, false, src)) {
1903 /* first pass: check the current state to see what's correctly
1904 connected, and drop anything that we don't want.
1907 for (uint32_t n = 0; n < limit; ++n) {
1909 const Connection::PortList& pl = c.port_connections (n);
1911 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1913 if (!_outputs.port(n)->connected_to ((*i))) {
1915 /* clear any existing connections */
1917 _session.engine().disconnect (*_outputs.port(n));
1919 } else if (_outputs.port(n)->connected() > 1) {
1921 /* OK, it is connected to the port we want,
1922 but its also connected to other ports.
1923 Change that situation.
1926 /* XXX could be optimized to not drop
1930 _session.engine().disconnect (*_outputs.port(n));
1935 /* second pass: connect all requested ports where necessary */
1937 for (uint32_t n = 0; n < limit; ++n) {
1939 const Connection::PortList& pl = c.port_connections (n);
1941 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1943 if (!_outputs.port(n)->connected_to ((*i))) {
1945 if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
1952 _output_connection = &c;
1954 output_connection_configuration_connection = c.ConfigurationChanged.connect
1955 (mem_fun (*this, &IO::output_connection_configuration_changed));
1956 output_connection_connection_connection = c.ConnectionsChanged.connect
1957 (mem_fun (*this, &IO::output_connection_connection_changed));
1960 output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
1966 IO::disable_connecting ()
1968 connecting_legal = false;
1973 IO::enable_connecting ()
1975 connecting_legal = true;
1976 return ConnectingLegal ();
1980 IO::disable_ports ()
1982 ports_legal = false;
1990 return PortsLegal ();
1994 IO::disable_panners (void)
1996 panners_legal = false;
2001 IO::reset_panners ()
2003 panners_legal = true;
2004 return PannersLegal ();
2008 IO::input_connection_connection_changed (int ignored)
2010 use_input_connection (*_input_connection, this);
2014 IO::input_connection_configuration_changed ()
2016 use_input_connection (*_input_connection, this);
2020 IO::output_connection_connection_changed (int ignored)
2022 use_output_connection (*_output_connection, this);
2026 IO::output_connection_configuration_changed ()
2028 use_output_connection (*_output_connection, this);
2032 IO::GainControllable::set_value (float val)
2034 io.set_gain (direct_control_to_gain (val), this);
2038 IO::GainControllable::get_value (void) const
2040 return direct_gain_to_control (io.effective_gain());
2044 IO::get_memento() const
2046 return sigc::bind (mem_fun (*(const_cast<IO *>(this)), &StateManager::use_state), _current_state_id);
2050 IO::restore_state (StateManager::State& state)
2055 StateManager::State*
2056 IO::state_factory (std::string why) const
2058 StateManager::State* state = new StateManager::State (why);
2063 IO::setup_peak_meters()
2065 _meter->setup(std::max(_inputs.count(), _outputs.count()));
2069 Update the peak meters.
2071 The meter signal lock is taken to prevent modification of the
2072 Meter signal while updating the meters, taking the meter signal
2073 lock prior to taking the io_lock ensures that all IO will remain
2074 valid while metering.
2079 Glib::Mutex::Lock guard (m_meter_signal_lock);
2081 Meter(); /* EMIT SIGNAL */
2087 // FIXME: Remove this function and just connect signal directly to PeakMeter::meter
2089 Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2094 IO::save_automation (const string& path)
2099 fullpath = _session.automation_dir();
2102 out.open (fullpath.c_str());
2105 error << string_compose(_("%1: could not open automation event file \"%2\""), _name, fullpath) << endmsg;
2109 out << X_("version ") << current_automation_version_number << endl;
2111 /* XXX use apply_to_points to get thread safety */
2113 for (AutomationList::iterator i = _gain_automation_curve.begin(); i != _gain_automation_curve.end(); ++i) {
2114 out << "g " << (jack_nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl;
2123 IO::load_automation (const string& path)
2128 uint32_t linecnt = 0;
2130 LocaleGuard lg (X_("POSIX"));
2132 fullpath = _session.automation_dir();
2135 in.open (fullpath.c_str());
2138 fullpath = _session.automation_dir();
2139 fullpath += _session.snap_name();
2142 in.open (fullpath.c_str());
2144 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
2149 clear_automation ();
2151 while (in.getline (line, sizeof(line), '\n')) {
2153 jack_nframes_t when;
2156 if (++linecnt == 1) {
2157 if (memcmp (line, "version", 7) == 0) {
2158 if (sscanf (line, "version %f", &version) != 1) {
2159 error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
2163 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
2167 if (version != current_automation_version_number) {
2168 error << string_compose(_("mismatched automation event file version (%1)"), version) << endmsg;
2175 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
2176 warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
2182 _gain_automation_curve.add (when, value, true);
2192 /* older (pre-1.0) versions of ardour used this */
2196 warning << _("dubious automation event found (and ignored)") << endmsg;
2200 _gain_automation_curve.save_state (_("loaded from disk"));
2206 IO::clear_automation ()
2208 Glib::Mutex::Lock lm (automation_lock);
2209 _gain_automation_curve.clear ();
2210 _panner->clear_automation ();
2214 IO::set_gain_automation_state (AutoState state)
2216 bool changed = false;
2219 Glib::Mutex::Lock lm (automation_lock);
2221 if (state != _gain_automation_curve.automation_state()) {
2223 last_automation_snapshot = 0;
2224 _gain_automation_curve.set_automation_state (state);
2227 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2233 _session.set_dirty ();
2234 gain_automation_state_changed (); /* EMIT SIGNAL */
2239 IO::set_gain_automation_style (AutoStyle style)
2241 bool changed = false;
2244 Glib::Mutex::Lock lm (automation_lock);
2246 if (style != _gain_automation_curve.automation_style()) {
2248 _gain_automation_curve.set_automation_style (style);
2253 gain_automation_style_changed (); /* EMIT SIGNAL */
2257 IO::inc_gain (gain_t factor, void *src)
2259 if (_desired_gain == 0.0f)
2260 set_gain (0.000001f + (0.000001f * factor), src);
2262 set_gain (_desired_gain + (_desired_gain * factor), src);
2266 IO::set_gain (gain_t val, void *src)
2268 // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2269 if (val>1.99526231f) val=1.99526231f;
2272 Glib::Mutex::Lock dm (declick_lock);
2273 _desired_gain = val;
2276 if (_session.transport_stopped()) {
2277 _effective_gain = val;
2282 _gain_control.Changed (); /* EMIT SIGNAL */
2284 if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2285 _gain_automation_curve.add (_session.transport_frame(), val);
2289 _session.set_dirty();
2293 IO::start_gain_touch ()
2295 _gain_automation_curve.start_touch ();
2299 IO::end_gain_touch ()
2301 _gain_automation_curve.stop_touch ();
2305 IO::start_pan_touch (uint32_t which)
2307 if (which < _panner->size()) {
2308 (*_panner)[which]->automation().start_touch();
2313 IO::end_pan_touch (uint32_t which)
2315 if (which < _panner->size()) {
2316 (*_panner)[which]->automation().stop_touch();
2322 IO::automation_snapshot (jack_nframes_t now)
2324 if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2326 if (gain_automation_recording()) {
2327 _gain_automation_curve.rt_add (now, gain());
2330 _panner->snapshot (now);
2332 last_automation_snapshot = now;
2337 IO::transport_stopped (jack_nframes_t frame)
2339 _gain_automation_curve.reposition_for_rt_add (frame);
2341 if (_gain_automation_curve.automation_state() != Off) {
2343 if (gain_automation_recording()) {
2344 _gain_automation_curve.save_state (_("automation write/touch"));
2347 /* the src=0 condition is a special signal to not propagate
2348 automation gain changes into the mix group when locating.
2351 set_gain (_gain_automation_curve.eval (frame), 0);
2354 _panner->transport_stopped (frame);
2358 IO::find_input_port_hole ()
2360 /* CALLER MUST HOLD IO LOCK */
2364 if (_inputs.empty()) {
2368 for (n = 1; n < UINT_MAX; ++n) {
2369 char buf[jack_port_name_size()];
2370 PortSet::iterator i = _inputs.begin();
2372 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2374 for ( ; i != _inputs.end(); ++i) {
2375 if (i->short_name() == buf) {
2380 if (i == _inputs.end()) {
2388 IO::find_output_port_hole ()
2390 /* CALLER MUST HOLD IO LOCK */
2394 if (_outputs.empty()) {
2398 for (n = 1; n < UINT_MAX; ++n) {
2399 char buf[jack_port_name_size()];
2400 PortSet::iterator i = _outputs.begin();
2402 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2404 for ( ; i != _outputs.end(); ++i) {
2405 if (i->short_name() == buf) {
2410 if (i == _outputs.end()) {
2419 IO::audio_input(uint32_t n) const
2421 return dynamic_cast<AudioPort*>(input(n));
2425 IO::audio_output(uint32_t n) const
2427 return dynamic_cast<AudioPort*>(output(n));
2431 IO::midi_input(uint32_t n) const
2433 return dynamic_cast<MidiPort*>(input(n));
2437 IO::midi_output(uint32_t n) const
2439 return dynamic_cast<MidiPort*>(output(n));
2443 IO::set_phase_invert (bool yn, void *src)
2445 if (_phase_invert != yn) {
2448 // phase_invert_changed (src); /* EMIT SIGNAL */