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);
148 Glib::Mutex::Lock lm (io_lock);
150 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
151 _session.engine().unregister_port (*i);
154 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
155 _session.engine().unregister_port (*i);
158 m_meter_connection.disconnect();
162 delete _output_buffers;
166 IO::silence (jack_nframes_t nframes, jack_nframes_t offset)
168 /* io_lock, not taken: function must be called from Session::process() calltree */
170 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
171 i->silence (nframes, offset);
175 /** Deliver bufs to the IO's Jack outputs.
177 * This function should automatically do whatever it necessary to correctly deliver bufs
178 * to the outputs, eg applying gain or pan or whatever else needs to be done.
181 IO::deliver_output (BufferSet& bufs, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset)
183 // FIXME: type specific code doesn't actually need to be here, it will go away in time
186 /* ********** AUDIO ********** */
188 // Apply gain if gain automation isn't playing
189 if ( ! apply_gain_automation) {
191 gain_t dg = _gain; // desired gain
194 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
201 Amp::run(bufs, nframes, _gain, dg, _phase_invert);
204 // Use the panner to distribute audio to output port buffers
205 if (_panner && !_panner->empty() && !_panner->bypassed()) {
206 _panner->distribute(bufs, output_buffers(), start_frame, end_frame, nframes, offset);
210 /* ********** MIDI ********** */
212 // No MIDI, we're done here
213 if (bufs.count().get(DataType::MIDI) == 0) {
217 const DataType type = DataType::MIDI;
219 // Just dump any MIDI 1-to-1, we're not at all clever with MIDI routing yet
220 BufferSet::iterator o = output_buffers().begin(type);
221 for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) {
222 o->read_from(*i, nframes, offset);
227 IO::collect_input (BufferSet& outs, jack_nframes_t nframes, jack_nframes_t offset)
229 outs.set_count(n_inputs());
231 if (outs.count() == ChanCount::ZERO)
234 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
236 BufferSet::iterator o = outs.begin(*t);
237 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
238 o->read_from(i->get_buffer(), nframes, offset);
245 IO::just_meter_input (jack_nframes_t start_frame, jack_nframes_t end_frame,
246 jack_nframes_t nframes, jack_nframes_t offset)
248 BufferSet& bufs = _session.get_scratch_buffers ();
249 ChanCount nbufs = n_process_buffers ();
251 collect_input (bufs, nframes, offset);
253 _meter->run(bufs, nframes);
257 IO::drop_input_connection ()
259 _input_connection = 0;
260 input_connection_configuration_connection.disconnect();
261 input_connection_connection_connection.disconnect();
262 _session.set_dirty ();
266 IO::drop_output_connection ()
268 _output_connection = 0;
269 output_connection_configuration_connection.disconnect();
270 output_connection_connection_connection.disconnect();
271 _session.set_dirty ();
275 IO::disconnect_input (Port* our_port, string other_port, void* src)
277 if (other_port.length() == 0 || our_port == 0) {
282 Glib::Mutex::Lock em (_session.engine().process_lock());
285 Glib::Mutex::Lock lm (io_lock);
287 /* check that our_port is really one of ours */
289 if ( ! _inputs.contains(our_port)) {
293 /* disconnect it from the source */
295 if (_session.engine().disconnect (other_port, our_port->name())) {
296 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
300 drop_input_connection();
304 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
305 _session.set_dirty ();
311 IO::connect_input (Port* our_port, string other_port, void* src)
313 if (other_port.length() == 0 || our_port == 0) {
318 Glib::Mutex::Lock em(_session.engine().process_lock());
321 Glib::Mutex::Lock lm (io_lock);
323 /* check that our_port is really one of ours */
325 if ( ! _inputs.contains(our_port) ) {
329 /* connect it to the source */
331 if (_session.engine().connect (other_port, our_port->name())) {
335 drop_input_connection ();
339 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
340 _session.set_dirty ();
345 IO::disconnect_output (Port* our_port, string other_port, void* src)
347 if (other_port.length() == 0 || our_port == 0) {
352 Glib::Mutex::Lock em(_session.engine().process_lock());
355 Glib::Mutex::Lock lm (io_lock);
357 /* check that our_port is really one of ours */
359 if ( ! _outputs.contains(our_port) ) {
363 /* disconnect it from the destination */
365 if (_session.engine().disconnect (our_port->name(), other_port)) {
366 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
370 drop_output_connection ();
374 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
375 _session.set_dirty ();
380 IO::connect_output (Port* our_port, string other_port, void* src)
382 if (other_port.length() == 0 || our_port == 0) {
387 Glib::Mutex::Lock em(_session.engine().process_lock());
390 Glib::Mutex::Lock lm (io_lock);
392 /* check that our_port is really one of ours */
394 if ( ! _outputs.contains(our_port) ) {
398 /* connect it to the destination */
400 if (_session.engine().connect (our_port->name(), other_port)) {
404 drop_output_connection ();
408 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
409 _session.set_dirty ();
414 IO::set_input (Port* other_port, void* src)
416 /* this removes all but one ports, and connects that one port
417 to the specified source.
420 if (_input_minimum.get_total() > 1) {
421 /* sorry, you can't do this */
425 if (other_port == 0) {
426 if (_input_minimum == ChanCount::ZERO) {
427 return ensure_inputs (0, false, true, src);
433 if (ensure_inputs (1, true, true, src)) {
437 return connect_input (_inputs.port(0), other_port->name(), src);
441 IO::remove_output_port (Port* port, void* src)
445 IOChange change (NoChange);
448 Glib::Mutex::Lock em(_session.engine().process_lock());
451 Glib::Mutex::Lock lm (io_lock);
453 if (_noutputs - 1 == (uint32_t) _output_minimum) {
454 /* sorry, you can't do this */
458 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
460 change = IOChange (change|ConfigurationChanged);
461 if (port->connected()) {
462 change = IOChange (change|ConnectionsChanged);
465 _session.engine().unregister_port (*i);
468 drop_output_connection ();
474 if (change != NoChange) {
475 setup_peak_meters ();
481 if (change != NoChange) {
482 output_changed (change, src); /* EMIT SIGNAL */
483 _session.set_dirty ();
490 /** Add an output port.
492 * @param destination Name of input port to connect new port to.
493 * @param src Source for emitted ConfigurationChanged signal.
494 * @param type Data type of port. Default value (NIL) will use this IO's default type.
497 IO::add_output_port (string destination, void* src, DataType type)
502 if (type == DataType::NIL)
503 type = _default_type;
506 Glib::Mutex::Lock em(_session.engine().process_lock());
509 Glib::Mutex::Lock lm (io_lock);
511 if (n_outputs() >= _output_maximum) {
515 /* Create a new output port */
517 // FIXME: naming scheme for differently typed ports?
518 if (_output_maximum.get_total() == 1) {
519 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
521 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
524 if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
525 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
529 _outputs.add_port (our_port);
530 drop_output_connection ();
531 setup_peak_meters ();
535 MoreChannels (n_outputs()); /* EMIT SIGNAL */
538 if (destination.length()) {
539 if (_session.engine().connect (our_port->name(), destination)) {
544 // pan_changed (src); /* EMIT SIGNAL */
545 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
546 _session.set_dirty ();
552 IO::remove_input_port (Port* port, void* src)
556 IOChange change (NoChange);
559 Glib::Mutex::Lock em(_session.engine().process_lock());
562 Glib::Mutex::Lock lm (io_lock);
564 if (((int)_ninputs - 1) < _input_minimum) {
565 /* sorry, you can't do this */
568 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
571 change = IOChange (change|ConfigurationChanged);
573 if (port->connected()) {
574 change = IOChange (change|ConnectionsChanged);
577 _session.engine().unregister_port (*i);
580 drop_input_connection ();
586 if (change != NoChange) {
587 setup_peak_meters ();
593 if (change != NoChange) {
594 input_changed (change, src);
595 _session.set_dirty ();
603 /** Add an input port.
605 * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
606 * @param destination Name of input port to connect new port to.
607 * @param src Source for emitted ConfigurationChanged signal.
610 IO::add_input_port (string source, void* src, DataType type)
615 if (type == DataType::NIL)
616 type = _default_type;
619 Glib::Mutex::Lock em (_session.engine().process_lock());
622 Glib::Mutex::Lock lm (io_lock);
624 if (n_inputs() >= _input_maximum) {
628 /* Create a new input port */
630 // FIXME: naming scheme for differently typed ports?
631 if (_input_maximum.get_total() == 1) {
632 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
634 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
637 if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
638 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
642 _inputs.add_port(our_port);
643 drop_input_connection ();
644 setup_peak_meters ();
648 MoreChannels (n_inputs()); /* EMIT SIGNAL */
651 if (source.length()) {
653 if (_session.engine().connect (source, our_port->name())) {
658 // pan_changed (src); /* EMIT SIGNAL */
659 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
660 _session.set_dirty ();
666 IO::disconnect_inputs (void* src)
669 Glib::Mutex::Lock em (_session.engine().process_lock());
672 Glib::Mutex::Lock lm (io_lock);
674 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
675 _session.engine().disconnect (*i);
678 drop_input_connection ();
682 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
688 IO::disconnect_outputs (void* src)
691 Glib::Mutex::Lock em (_session.engine().process_lock());
694 Glib::Mutex::Lock lm (io_lock);
696 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
697 _session.engine().disconnect (*i);
700 drop_output_connection ();
704 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
705 _session.set_dirty ();
711 IO::ensure_inputs_locked (uint32_t n, bool clear, void* src)
714 bool changed = false;
716 /* remove unused ports */
718 while (n_inputs().get(_default_type) > n) {
721 _session.engine().unregister_port (_inputs.back());
728 /* create any necessary new ports */
730 while (n_inputs().get(_default_type) < n) {
734 /* Create a new input port (of the default type) */
736 if (_input_maximum.get_total() == 1) {
737 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
740 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
745 if ((input_port = _session.engine().register_input_port (_default_type, buf)) == 0) {
746 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
751 catch (AudioEngine::PortRegistrationFailure& err) {
752 setup_peak_meters ();
758 _inputs.add_port (input_port);
763 drop_input_connection ();
764 setup_peak_meters ();
766 MoreChannels (n_inputs()); /* EMIT SIGNAL */
767 _session.set_dirty ();
771 /* disconnect all existing ports so that we get a fresh start */
773 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
774 _session.engine().disconnect (*i);
781 /** Attach output_buffers to port buffers.
783 * Connected to IOs own MoreChannels signal.
786 IO::attach_buffers(ChanCount ignored)
788 _output_buffers->attach_buffers(_outputs);
792 IO::ensure_io (const ChanCount& in, const ChanCount& out, bool clear, void* src)
795 uint32_t nin = in.get(_default_type);
796 uint32_t nout = out.get(_default_type);
798 // We only deal with one type still. Sorry about your luck.
799 assert(nin == in.get_total());
800 assert(nout == out.get_total());
802 return ensure_io(nin, nout, clear, src);
806 IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
808 bool in_changed = false;
809 bool out_changed = false;
812 nin = min (_input_maximum.get(_default_type), static_cast<size_t>(nin));
814 nout = min (_output_maximum.get(_default_type), static_cast<size_t>(nout));
816 if (nin == n_inputs().get(_default_type) && nout == n_outputs().get(_default_type) && !clear) {
821 Glib::Mutex::Lock em (_session.engine().process_lock());
822 Glib::Mutex::Lock lm (io_lock);
826 if (n_outputs().get(_default_type) == nout) {
827 need_pan_reset = false;
829 need_pan_reset = true;
832 /* remove unused ports */
834 while (n_inputs().get(_default_type) > nin) {
837 _session.engine().unregister_port (_inputs.back());
843 while (n_outputs().get(_default_type) > nout) {
846 _session.engine().unregister_port (_outputs.back());
849 out_changed = true;*/
852 /* create any necessary new ports (of the default type) */
854 while (n_inputs().get(_default_type) < nin) {
858 /* Create a new input port */
860 if (_input_maximum.get_total() == 1) {
861 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
864 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
868 if ((port = _session.engine().register_input_port (_default_type, buf)) == 0) {
869 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
874 catch (AudioEngine::PortRegistrationFailure& err) {
875 setup_peak_meters ();
881 _inputs.add_port (port);
885 /* create any necessary new ports */
887 while (n_outputs().get(_default_type) < nout) {
891 /* Create a new output port */
893 if (_output_maximum.get_total() == 1) {
894 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
896 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
900 if ((port = _session.engine().register_output_port (_default_type, buf)) == 0) {
901 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
906 catch (AudioEngine::PortRegistrationFailure& err) {
907 setup_peak_meters ();
913 _outputs.add_port (port);
919 /* disconnect all existing ports so that we get a fresh start */
921 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
922 _session.engine().disconnect (*i);
925 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
926 _session.engine().disconnect (*i);
930 if (in_changed || out_changed) {
931 setup_peak_meters ();
937 drop_output_connection ();
938 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
942 drop_input_connection ();
943 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
946 if (in_changed || out_changed) {
947 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
948 _session.set_dirty ();
955 IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
957 bool changed = false;
959 n = min (_input_maximum.get(_default_type), static_cast<size_t>(n));
961 if (n == n_inputs().get(_default_type) && !clear) {
966 Glib::Mutex::Lock em (_session.engine().process_lock());
967 Glib::Mutex::Lock im (io_lock);
968 changed = ensure_inputs_locked (n, clear, src);
970 changed = ensure_inputs_locked (n, clear, src);
974 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
975 _session.set_dirty ();
981 IO::ensure_outputs_locked (uint32_t n, bool clear, void* src)
984 bool changed = false;
987 if (n_outputs().get(_default_type) == n) {
988 need_pan_reset = false;
990 need_pan_reset = true;
993 /* remove unused ports */
995 while (n_outputs().get(_default_type) > n) {
998 _session.engine().unregister_port (_outputs.back());
1005 /* create any necessary new ports */
1007 while (n_outputs().get(_default_type) < n) {
1011 /* Create a new output port */
1013 if (_output_maximum.get(_default_type) == 1) {
1014 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1016 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1019 if ((output_port = _session.engine().register_output_port (_default_type, buf)) == 0) {
1020 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1024 _outputs.add_port (output_port);
1026 setup_peak_meters ();
1028 if (need_pan_reset) {
1034 drop_output_connection ();
1035 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1036 _session.set_dirty ();
1040 /* disconnect all existing ports so that we get a fresh start */
1042 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1043 _session.engine().disconnect (*i);
1051 IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
1053 bool changed = false;
1055 if (_output_maximum < ChanCount::INFINITE) {
1056 n = min (_output_maximum.get(_default_type), static_cast<size_t>(n));
1057 if (n == n_outputs().get(_default_type) && !clear) {
1062 /* XXX caller should hold io_lock, but generally doesn't */
1065 Glib::Mutex::Lock em (_session.engine().process_lock());
1066 Glib::Mutex::Lock im (io_lock);
1067 changed = ensure_outputs_locked (n, clear, src);
1069 changed = ensure_outputs_locked (n, clear, src);
1073 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1079 IO::effective_gain () const
1081 if (gain_automation_playback()) {
1082 return _effective_gain;
1084 return _desired_gain;
1091 if (panners_legal) {
1092 if (!no_panner_reset) {
1093 _panner->reset (n_outputs().get(_default_type), pans_required());
1096 panner_legal_c.disconnect ();
1097 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1102 IO::panners_became_legal ()
1104 _panner->reset (n_outputs().get(_default_type), pans_required());
1105 _panner->load (); // automation
1106 panner_legal_c.disconnect ();
1111 IO::defer_pan_reset ()
1113 no_panner_reset = true;
1117 IO::allow_pan_reset ()
1119 no_panner_reset = false;
1125 IO::get_state (void)
1127 return state (true);
1131 IO::state (bool full_state)
1133 XMLNode* node = new XMLNode (state_node_name);
1136 bool need_ins = true;
1137 bool need_outs = true;
1138 LocaleGuard lg (X_("POSIX"));
1139 Glib::Mutex::Lock lm (io_lock);
1141 node->add_property("name", _name);
1143 node->add_property("id", buf);
1147 if (_input_connection) {
1148 node->add_property ("input-connection", _input_connection->name());
1152 if (_output_connection) {
1153 node->add_property ("output-connection", _output_connection->name());
1158 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1160 const char **connections = i->get_connections();
1162 if (connections && connections[0]) {
1165 for (int n = 0; connections && connections[n]; ++n) {
1170 /* if its a connection to our own port,
1171 return only the port name, not the
1172 whole thing. this allows connections
1173 to be re-established even when our
1174 client name is different.
1177 str += _session.engine().make_port_name_relative (connections[n]);
1189 node->add_property ("inputs", str);
1195 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1197 const char **connections = i->get_connections();
1199 if (connections && connections[0]) {
1203 for (int n = 0; connections[n]; ++n) {
1208 str += _session.engine().make_port_name_relative (connections[n]);
1220 node->add_property ("outputs", str);
1223 node->add_child_nocopy (_panner->state (full_state));
1225 snprintf (buf, sizeof(buf), "%2.12f", gain());
1226 node->add_property ("gain", buf);
1228 const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1229 const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1230 const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1231 const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1233 snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1235 node->add_property ("iolimits", buf);
1240 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1242 /* never store anything except Off for automation state in a template */
1243 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
1245 node->add_property ("automation-state", buf);
1246 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
1247 node->add_property ("automation-style", buf);
1249 /* XXX same for pan etc. */
1255 IO::connecting_became_legal ()
1259 if (pending_state_node == 0) {
1260 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1265 connection_legal_c.disconnect ();
1267 ret = make_connections (*pending_state_node);
1270 delete pending_state_node;
1271 pending_state_node = 0;
1278 IO::ports_became_legal ()
1282 if (pending_state_node == 0) {
1283 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1288 port_legal_c.disconnect ();
1290 ret = create_ports (*pending_state_node);
1292 if (connecting_legal) {
1293 delete pending_state_node;
1294 pending_state_node = 0;
1301 IO::set_state (const XMLNode& node)
1303 const XMLProperty* prop;
1304 XMLNodeConstIterator iter;
1305 LocaleGuard lg (X_("POSIX"));
1307 /* force use of non-localized representation of decimal point,
1308 since we use it a lot in XML files and so forth.
1311 if (node.name() != state_node_name) {
1312 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1316 if ((prop = node.property ("name")) != 0) {
1317 _name = prop->value();
1318 _panner->set_name (_name);
1321 if ((prop = node.property ("id")) != 0) {
1322 _id = prop->value ();
1327 size_t out_min = -1;
1328 size_t out_max = -1;
1330 if ((prop = node.property ("iolimits")) != 0) {
1331 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1332 &in_min, &in_max, &out_min, &out_max);
1333 _input_minimum = ChanCount(_default_type, in_min);
1334 _input_maximum = ChanCount(_default_type, in_max);
1335 _output_minimum = ChanCount(_default_type, out_min);
1336 _output_maximum = ChanCount(_default_type, out_max);
1339 if ((prop = node.property ("gain")) != 0) {
1340 set_gain (atof (prop->value().c_str()), this);
1341 _gain = _desired_gain;
1344 for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1345 if ((*iter)->name() == "Panner") {
1346 _panner->set_state (**iter);
1350 if ((prop = node.property ("automation-state")) != 0) {
1353 x = strtol (prop->value().c_str(), 0, 16);
1354 set_gain_automation_state (AutoState (x));
1357 if ((prop = node.property ("automation-style")) != 0) {
1360 x = strtol (prop->value().c_str(), 0, 16);
1361 set_gain_automation_style (AutoStyle (x));
1366 if (create_ports (node)) {
1372 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1375 if (panners_legal) {
1378 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1381 if (connecting_legal) {
1383 if (make_connections (node)) {
1389 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1392 if (!ports_legal || !connecting_legal) {
1393 pending_state_node = new XMLNode (node);
1400 IO::create_ports (const XMLNode& node)
1402 const XMLProperty* prop;
1404 int num_outputs = 0;
1406 if ((prop = node.property ("input-connection")) != 0) {
1408 Connection* c = _session.connection_by_name (prop->value());
1411 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1413 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1414 error << _("No input connections available as a replacement")
1418 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1423 num_inputs = c->nports();
1425 } else if ((prop = node.property ("inputs")) != 0) {
1427 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1430 if ((prop = node.property ("output-connection")) != 0) {
1431 Connection* c = _session.connection_by_name (prop->value());
1434 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1436 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1437 error << _("No output connections available as a replacement")
1441 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1446 num_outputs = c->nports ();
1448 } else if ((prop = node.property ("outputs")) != 0) {
1449 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1452 no_panner_reset = true;
1454 if (ensure_io (num_inputs, num_outputs, true, this)) {
1455 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1459 no_panner_reset = false;
1461 set_deferred_state ();
1469 IO::make_connections (const XMLNode& node)
1471 const XMLProperty* prop;
1473 if ((prop = node.property ("input-connection")) != 0) {
1474 Connection* c = _session.connection_by_name (prop->value());
1477 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1479 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1480 error << _("No input connections available as a replacement")
1484 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1489 use_input_connection (*c, this);
1491 } else if ((prop = node.property ("inputs")) != 0) {
1492 if (set_inputs (prop->value())) {
1493 error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1498 if ((prop = node.property ("output-connection")) != 0) {
1499 Connection* c = _session.connection_by_name (prop->value());
1502 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1504 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1505 error << _("No output connections available as a replacement")
1509 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1514 use_output_connection (*c, this);
1516 } else if ((prop = node.property ("outputs")) != 0) {
1517 if (set_outputs (prop->value())) {
1518 error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1527 IO::set_inputs (const string& str)
1529 vector<string> ports;
1534 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1538 if (ensure_inputs (nports, true, true, this)) {
1542 string::size_type start, end, ostart;
1549 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1552 if ((end = str.find_first_of ('}', start)) == string::npos) {
1553 error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1557 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1558 error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1564 for (int x = 0; x < n; ++x) {
1565 connect_input (input (i), ports[x], this);
1577 IO::set_outputs (const string& str)
1579 vector<string> ports;
1584 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1588 if (ensure_outputs (nports, true, true, this)) {
1592 string::size_type start, end, ostart;
1599 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1602 if ((end = str.find_first_of ('}', start)) == string::npos) {
1603 error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1607 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1608 error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1614 for (int x = 0; x < n; ++x) {
1615 connect_output (output (i), ports[x], this);
1627 IO::parse_io_string (const string& str, vector<string>& ports)
1629 string::size_type pos, opos;
1631 if (str.length() == 0) {
1640 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1641 ports.push_back (str.substr (opos, pos - opos));
1645 if (opos < str.length()) {
1646 ports.push_back (str.substr(opos));
1649 return ports.size();
1653 IO::parse_gain_string (const string& str, vector<string>& ports)
1655 string::size_type pos, opos;
1661 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1662 ports.push_back (str.substr (opos, pos - opos));
1666 if (opos < str.length()) {
1667 ports.push_back (str.substr(opos));
1670 return ports.size();
1674 IO::set_name (string name, void* src)
1676 if (name == _name) {
1680 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1681 string current_name = i->short_name();
1682 current_name.replace (current_name.find (_name), _name.length(), name);
1683 i->set_name (current_name);
1686 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1687 string current_name = i->short_name();
1688 current_name.replace (current_name.find (_name), _name.length(), name);
1689 i->set_name (current_name);
1693 name_changed (src); /* EMIT SIGNAL */
1699 IO::set_input_minimum (int n)
1702 _input_minimum = ChanCount::ZERO;
1704 _input_minimum = ChanCount(_default_type, n);
1708 IO::set_input_maximum (int n)
1711 _input_maximum = ChanCount::INFINITE;
1713 _input_maximum = ChanCount(_default_type, n);
1717 IO::set_output_minimum (int n)
1720 _output_minimum = ChanCount::ZERO;
1722 _output_minimum = ChanCount(_default_type, n);
1726 IO::set_output_maximum (int n)
1729 _output_maximum = ChanCount::INFINITE;
1731 _output_maximum = ChanCount(_default_type, n);
1735 IO::set_input_minimum (ChanCount n)
1741 IO::set_input_maximum (ChanCount n)
1747 IO::set_output_minimum (ChanCount n)
1749 _output_minimum = n;
1753 IO::set_output_maximum (ChanCount n)
1755 _output_maximum = n;
1759 IO::set_port_latency (jack_nframes_t nframes)
1761 Glib::Mutex::Lock lm (io_lock);
1763 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1764 i->set_latency (nframes);
1769 IO::output_latency () const
1771 jack_nframes_t max_latency;
1772 jack_nframes_t latency;
1776 /* io lock not taken - must be protected by other means */
1778 for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1779 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1780 max_latency = latency;
1788 IO::input_latency () const
1790 jack_nframes_t max_latency;
1791 jack_nframes_t latency;
1795 /* io lock not taken - must be protected by other means */
1797 for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1798 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1799 max_latency = latency;
1807 IO::use_input_connection (Connection& c, void* src)
1812 Glib::Mutex::Lock lm (_session.engine().process_lock());
1813 Glib::Mutex::Lock lm2 (io_lock);
1817 drop_input_connection ();
1819 if (ensure_inputs (limit, false, false, src)) {
1823 /* first pass: check the current state to see what's correctly
1824 connected, and drop anything that we don't want.
1827 for (uint32_t n = 0; n < limit; ++n) {
1828 const Connection::PortList& pl = c.port_connections (n);
1830 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1832 if (!_inputs.port(n)->connected_to ((*i))) {
1834 /* clear any existing connections */
1836 _session.engine().disconnect (*_inputs.port(n));
1838 } else if (_inputs.port(n)->connected() > 1) {
1840 /* OK, it is connected to the port we want,
1841 but its also connected to other ports.
1842 Change that situation.
1845 /* XXX could be optimized to not drop
1849 _session.engine().disconnect (*_inputs.port(n));
1855 /* second pass: connect all requested ports where necessary */
1857 for (uint32_t n = 0; n < limit; ++n) {
1858 const Connection::PortList& pl = c.port_connections (n);
1860 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1862 if (!_inputs.port(n)->connected_to ((*i))) {
1864 if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1872 _input_connection = &c;
1874 input_connection_configuration_connection = c.ConfigurationChanged.connect
1875 (mem_fun (*this, &IO::input_connection_configuration_changed));
1876 input_connection_connection_connection = c.ConnectionsChanged.connect
1877 (mem_fun (*this, &IO::input_connection_connection_changed));
1880 input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
1885 IO::use_output_connection (Connection& c, void* src)
1890 Glib::Mutex::Lock lm (_session.engine().process_lock());
1891 Glib::Mutex::Lock lm2 (io_lock);
1895 drop_output_connection ();
1897 if (ensure_outputs (limit, false, false, src)) {
1901 /* first pass: check the current state to see what's correctly
1902 connected, and drop anything that we don't want.
1905 for (uint32_t n = 0; n < limit; ++n) {
1907 const Connection::PortList& pl = c.port_connections (n);
1909 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1911 if (!_outputs.port(n)->connected_to ((*i))) {
1913 /* clear any existing connections */
1915 _session.engine().disconnect (*_outputs.port(n));
1917 } else if (_outputs.port(n)->connected() > 1) {
1919 /* OK, it is connected to the port we want,
1920 but its also connected to other ports.
1921 Change that situation.
1924 /* XXX could be optimized to not drop
1928 _session.engine().disconnect (*_outputs.port(n));
1933 /* second pass: connect all requested ports where necessary */
1935 for (uint32_t n = 0; n < limit; ++n) {
1937 const Connection::PortList& pl = c.port_connections (n);
1939 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1941 if (!_outputs.port(n)->connected_to ((*i))) {
1943 if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
1950 _output_connection = &c;
1952 output_connection_configuration_connection = c.ConfigurationChanged.connect
1953 (mem_fun (*this, &IO::output_connection_configuration_changed));
1954 output_connection_connection_connection = c.ConnectionsChanged.connect
1955 (mem_fun (*this, &IO::output_connection_connection_changed));
1958 output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
1964 IO::disable_connecting ()
1966 connecting_legal = false;
1971 IO::enable_connecting ()
1973 connecting_legal = true;
1974 return ConnectingLegal ();
1978 IO::disable_ports ()
1980 ports_legal = false;
1988 return PortsLegal ();
1992 IO::disable_panners (void)
1994 panners_legal = false;
1999 IO::reset_panners ()
2001 panners_legal = true;
2002 return PannersLegal ();
2006 IO::input_connection_connection_changed (int ignored)
2008 use_input_connection (*_input_connection, this);
2012 IO::input_connection_configuration_changed ()
2014 use_input_connection (*_input_connection, this);
2018 IO::output_connection_connection_changed (int ignored)
2020 use_output_connection (*_output_connection, this);
2024 IO::output_connection_configuration_changed ()
2026 use_output_connection (*_output_connection, this);
2030 IO::GainControllable::set_value (float val)
2032 io.set_gain (direct_control_to_gain (val), this);
2036 IO::GainControllable::get_value (void) const
2038 return direct_gain_to_control (io.effective_gain());
2042 IO::get_memento() const
2044 return sigc::bind (mem_fun (*(const_cast<IO *>(this)), &StateManager::use_state), _current_state_id);
2048 IO::restore_state (StateManager::State& state)
2053 StateManager::State*
2054 IO::state_factory (std::string why) const
2056 StateManager::State* state = new StateManager::State (why);
2061 IO::setup_peak_meters()
2063 _meter->setup(std::max(_inputs.count(), _outputs.count()));
2067 Update the peak meters.
2069 The meter signal lock is taken to prevent modification of the
2070 Meter signal while updating the meters, taking the meter signal
2071 lock prior to taking the io_lock ensures that all IO will remain
2072 valid while metering.
2077 Glib::Mutex::Lock guard (m_meter_signal_lock);
2079 Meter(); /* EMIT SIGNAL */
2085 // FIXME: Remove this function and just connect signal directly to PeakMeter::meter
2087 Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2092 IO::save_automation (const string& path)
2097 fullpath = _session.automation_dir();
2100 out.open (fullpath.c_str());
2103 error << string_compose(_("%1: could not open automation event file \"%2\""), _name, fullpath) << endmsg;
2107 out << X_("version ") << current_automation_version_number << endl;
2109 /* XXX use apply_to_points to get thread safety */
2111 for (AutomationList::iterator i = _gain_automation_curve.begin(); i != _gain_automation_curve.end(); ++i) {
2112 out << "g " << (jack_nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl;
2121 IO::load_automation (const string& path)
2126 uint32_t linecnt = 0;
2128 LocaleGuard lg (X_("POSIX"));
2130 fullpath = _session.automation_dir();
2133 in.open (fullpath.c_str());
2136 fullpath = _session.automation_dir();
2137 fullpath += _session.snap_name();
2140 in.open (fullpath.c_str());
2142 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
2147 clear_automation ();
2149 while (in.getline (line, sizeof(line), '\n')) {
2151 jack_nframes_t when;
2154 if (++linecnt == 1) {
2155 if (memcmp (line, "version", 7) == 0) {
2156 if (sscanf (line, "version %f", &version) != 1) {
2157 error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
2161 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
2165 if (version != current_automation_version_number) {
2166 error << string_compose(_("mismatched automation event file version (%1)"), version) << endmsg;
2173 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
2174 warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
2180 _gain_automation_curve.add (when, value, true);
2190 /* older (pre-1.0) versions of ardour used this */
2194 warning << _("dubious automation event found (and ignored)") << endmsg;
2198 _gain_automation_curve.save_state (_("loaded from disk"));
2204 IO::clear_automation ()
2206 Glib::Mutex::Lock lm (automation_lock);
2207 _gain_automation_curve.clear ();
2208 _panner->clear_automation ();
2212 IO::set_gain_automation_state (AutoState state)
2214 bool changed = false;
2217 Glib::Mutex::Lock lm (automation_lock);
2219 if (state != _gain_automation_curve.automation_state()) {
2221 last_automation_snapshot = 0;
2222 _gain_automation_curve.set_automation_state (state);
2225 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2231 _session.set_dirty ();
2232 gain_automation_state_changed (); /* EMIT SIGNAL */
2237 IO::set_gain_automation_style (AutoStyle style)
2239 bool changed = false;
2242 Glib::Mutex::Lock lm (automation_lock);
2244 if (style != _gain_automation_curve.automation_style()) {
2246 _gain_automation_curve.set_automation_style (style);
2251 gain_automation_style_changed (); /* EMIT SIGNAL */
2255 IO::inc_gain (gain_t factor, void *src)
2257 if (_desired_gain == 0.0f)
2258 set_gain (0.000001f + (0.000001f * factor), src);
2260 set_gain (_desired_gain + (_desired_gain * factor), src);
2264 IO::set_gain (gain_t val, void *src)
2266 // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2267 if (val>1.99526231f) val=1.99526231f;
2270 Glib::Mutex::Lock dm (declick_lock);
2271 _desired_gain = val;
2274 if (_session.transport_stopped()) {
2275 _effective_gain = val;
2280 _gain_control.Changed (); /* EMIT SIGNAL */
2282 if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2283 _gain_automation_curve.add (_session.transport_frame(), val);
2287 _session.set_dirty();
2291 IO::start_gain_touch ()
2293 _gain_automation_curve.start_touch ();
2297 IO::end_gain_touch ()
2299 _gain_automation_curve.stop_touch ();
2303 IO::start_pan_touch (uint32_t which)
2305 if (which < _panner->size()) {
2306 (*_panner)[which]->automation().start_touch();
2311 IO::end_pan_touch (uint32_t which)
2313 if (which < _panner->size()) {
2314 (*_panner)[which]->automation().stop_touch();
2320 IO::automation_snapshot (jack_nframes_t now)
2322 if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2324 if (gain_automation_recording()) {
2325 _gain_automation_curve.rt_add (now, gain());
2328 _panner->snapshot (now);
2330 last_automation_snapshot = now;
2335 IO::transport_stopped (jack_nframes_t frame)
2337 _gain_automation_curve.reposition_for_rt_add (frame);
2339 if (_gain_automation_curve.automation_state() != Off) {
2341 if (gain_automation_recording()) {
2342 _gain_automation_curve.save_state (_("automation write/touch"));
2345 /* the src=0 condition is a special signal to not propagate
2346 automation gain changes into the mix group when locating.
2349 set_gain (_gain_automation_curve.eval (frame), 0);
2352 _panner->transport_stopped (frame);
2356 IO::find_input_port_hole ()
2358 /* CALLER MUST HOLD IO LOCK */
2362 if (_inputs.empty()) {
2366 for (n = 1; n < UINT_MAX; ++n) {
2367 char buf[jack_port_name_size()];
2368 PortSet::iterator i = _inputs.begin();
2370 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2372 for ( ; i != _inputs.end(); ++i) {
2373 if (i->short_name() == buf) {
2378 if (i == _inputs.end()) {
2386 IO::find_output_port_hole ()
2388 /* CALLER MUST HOLD IO LOCK */
2392 if (_outputs.empty()) {
2396 for (n = 1; n < UINT_MAX; ++n) {
2397 char buf[jack_port_name_size()];
2398 PortSet::iterator i = _outputs.begin();
2400 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2402 for ( ; i != _outputs.end(); ++i) {
2403 if (i->short_name() == buf) {
2408 if (i == _outputs.end()) {
2417 IO::audio_input(uint32_t n) const
2419 return dynamic_cast<AudioPort*>(input(n));
2423 IO::audio_output(uint32_t n) const
2425 return dynamic_cast<AudioPort*>(output(n));
2429 IO::midi_input(uint32_t n) const
2431 return dynamic_cast<MidiPort*>(input(n));
2435 IO::midi_output(uint32_t n) const
2437 return dynamic_cast<MidiPort*>(output(n));
2441 IO::set_phase_invert (bool yn, void *src)
2443 if (_phase_invert != yn) {
2446 // phase_invert_changed (src); /* EMIT SIGNAL */