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;
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, ++o) {
221 o->read_from(*i, nframes, offset);
226 IO::collect_input (BufferSet& outs, jack_nframes_t nframes, jack_nframes_t offset)
228 outs.set_count(n_inputs());
230 if (outs.count() == ChanCount::ZERO)
233 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
235 BufferSet::iterator o = outs.begin(*t);
236 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
237 o->read_from(i->get_buffer(), nframes, offset);
244 IO::just_meter_input (jack_nframes_t start_frame, jack_nframes_t end_frame,
245 jack_nframes_t nframes, jack_nframes_t offset)
247 BufferSet& bufs = _session.get_scratch_buffers ();
248 ChanCount nbufs = n_process_buffers ();
250 collect_input (bufs, nframes, offset);
252 _meter->run(bufs, nframes);
256 IO::drop_input_connection ()
258 _input_connection = 0;
259 input_connection_configuration_connection.disconnect();
260 input_connection_connection_connection.disconnect();
261 _session.set_dirty ();
265 IO::drop_output_connection ()
267 _output_connection = 0;
268 output_connection_configuration_connection.disconnect();
269 output_connection_connection_connection.disconnect();
270 _session.set_dirty ();
274 IO::disconnect_input (Port* our_port, string other_port, void* src)
276 if (other_port.length() == 0 || our_port == 0) {
281 Glib::Mutex::Lock em (_session.engine().process_lock());
284 Glib::Mutex::Lock lm (io_lock);
286 /* check that our_port is really one of ours */
288 if ( ! _inputs.contains(our_port)) {
292 /* disconnect it from the source */
294 if (_session.engine().disconnect (other_port, our_port->name())) {
295 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
299 drop_input_connection();
303 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
304 _session.set_dirty ();
310 IO::connect_input (Port* our_port, string other_port, void* src)
312 if (other_port.length() == 0 || our_port == 0) {
317 Glib::Mutex::Lock em(_session.engine().process_lock());
320 Glib::Mutex::Lock lm (io_lock);
322 /* check that our_port is really one of ours */
324 if ( ! _inputs.contains(our_port) ) {
328 /* connect it to the source */
330 if (_session.engine().connect (other_port, our_port->name())) {
334 drop_input_connection ();
338 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
339 _session.set_dirty ();
344 IO::disconnect_output (Port* our_port, string other_port, void* src)
346 if (other_port.length() == 0 || our_port == 0) {
351 Glib::Mutex::Lock em(_session.engine().process_lock());
354 Glib::Mutex::Lock lm (io_lock);
356 /* check that our_port is really one of ours */
358 if ( ! _outputs.contains(our_port) ) {
362 /* disconnect it from the destination */
364 if (_session.engine().disconnect (our_port->name(), other_port)) {
365 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
369 drop_output_connection ();
373 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
374 _session.set_dirty ();
379 IO::connect_output (Port* our_port, string other_port, void* src)
381 if (other_port.length() == 0 || our_port == 0) {
386 Glib::Mutex::Lock em(_session.engine().process_lock());
389 Glib::Mutex::Lock lm (io_lock);
391 /* check that our_port is really one of ours */
393 if ( ! _outputs.contains(our_port) ) {
397 /* connect it to the destination */
399 if (_session.engine().connect (our_port->name(), other_port)) {
403 drop_output_connection ();
407 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
408 _session.set_dirty ();
413 IO::set_input (Port* other_port, void* src)
415 /* this removes all but one ports, and connects that one port
416 to the specified source.
419 if (_input_minimum.get_total() > 1) {
420 /* sorry, you can't do this */
424 if (other_port == 0) {
425 if (_input_minimum == ChanCount::ZERO) {
426 return ensure_inputs (0, false, true, src);
432 if (ensure_inputs (1, true, true, src)) {
436 return connect_input (_inputs.port(0), other_port->name(), src);
440 IO::remove_output_port (Port* port, void* src)
444 IOChange change (NoChange);
447 Glib::Mutex::Lock em(_session.engine().process_lock());
450 Glib::Mutex::Lock lm (io_lock);
452 if (_noutputs - 1 == (uint32_t) _output_minimum) {
453 /* sorry, you can't do this */
457 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
459 change = IOChange (change|ConfigurationChanged);
460 if (port->connected()) {
461 change = IOChange (change|ConnectionsChanged);
464 _session.engine().unregister_port (*i);
467 drop_output_connection ();
473 if (change != NoChange) {
474 setup_peak_meters ();
480 if (change != NoChange) {
481 output_changed (change, src); /* EMIT SIGNAL */
482 _session.set_dirty ();
489 /** Add an output port.
491 * @param destination Name of input port to connect new port to.
492 * @param src Source for emitted ConfigurationChanged signal.
493 * @param type Data type of port. Default value (NIL) will use this IO's default type.
496 IO::add_output_port (string destination, void* src, DataType type)
501 if (type == DataType::NIL)
502 type = _default_type;
505 Glib::Mutex::Lock em(_session.engine().process_lock());
508 Glib::Mutex::Lock lm (io_lock);
510 if (n_outputs() >= _output_maximum) {
514 /* Create a new output port */
516 // FIXME: naming scheme for differently typed ports?
517 if (_output_maximum.get_total() == 1) {
518 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
520 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
523 if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
524 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
528 _outputs.add_port (our_port);
529 drop_output_connection ();
530 setup_peak_meters ();
534 MoreChannels (n_outputs()); /* EMIT SIGNAL */
537 if (destination.length()) {
538 if (_session.engine().connect (our_port->name(), destination)) {
543 // pan_changed (src); /* EMIT SIGNAL */
544 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
545 _session.set_dirty ();
551 IO::remove_input_port (Port* port, void* src)
555 IOChange change (NoChange);
558 Glib::Mutex::Lock em(_session.engine().process_lock());
561 Glib::Mutex::Lock lm (io_lock);
563 if (((int)_ninputs - 1) < _input_minimum) {
564 /* sorry, you can't do this */
567 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
570 change = IOChange (change|ConfigurationChanged);
572 if (port->connected()) {
573 change = IOChange (change|ConnectionsChanged);
576 _session.engine().unregister_port (*i);
579 drop_input_connection ();
585 if (change != NoChange) {
586 setup_peak_meters ();
592 if (change != NoChange) {
593 input_changed (change, src);
594 _session.set_dirty ();
602 /** Add an input port.
604 * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
605 * @param destination Name of input port to connect new port to.
606 * @param src Source for emitted ConfigurationChanged signal.
609 IO::add_input_port (string source, void* src, DataType type)
614 if (type == DataType::NIL)
615 type = _default_type;
618 Glib::Mutex::Lock em (_session.engine().process_lock());
621 Glib::Mutex::Lock lm (io_lock);
623 if (n_inputs() >= _input_maximum) {
627 /* Create a new input port */
629 // FIXME: naming scheme for differently typed ports?
630 if (_input_maximum.get_total() == 1) {
631 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
633 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
636 if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
637 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
641 _inputs.add_port(our_port);
642 drop_input_connection ();
643 setup_peak_meters ();
647 MoreChannels (n_inputs()); /* EMIT SIGNAL */
650 if (source.length()) {
652 if (_session.engine().connect (source, our_port->name())) {
657 // pan_changed (src); /* EMIT SIGNAL */
658 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
659 _session.set_dirty ();
665 IO::disconnect_inputs (void* src)
668 Glib::Mutex::Lock em (_session.engine().process_lock());
671 Glib::Mutex::Lock lm (io_lock);
673 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
674 _session.engine().disconnect (*i);
677 drop_input_connection ();
681 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
687 IO::disconnect_outputs (void* src)
690 Glib::Mutex::Lock em (_session.engine().process_lock());
693 Glib::Mutex::Lock lm (io_lock);
695 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
696 _session.engine().disconnect (*i);
699 drop_output_connection ();
703 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
704 _session.set_dirty ();
710 IO::ensure_inputs_locked (uint32_t n, bool clear, void* src)
713 bool changed = false;
715 /* remove unused ports */
717 while (n_inputs().get(_default_type) > n) {
720 _session.engine().unregister_port (_inputs.back());
727 /* create any necessary new ports */
729 while (n_inputs().get(_default_type) < n) {
733 /* Create a new input port (of the default type) */
735 if (_input_maximum.get_total() == 1) {
736 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
739 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
744 if ((input_port = _session.engine().register_input_port (_default_type, buf)) == 0) {
745 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
750 catch (AudioEngine::PortRegistrationFailure& err) {
751 setup_peak_meters ();
757 _inputs.add_port (input_port);
762 drop_input_connection ();
763 setup_peak_meters ();
765 MoreChannels (n_inputs()); /* EMIT SIGNAL */
766 _session.set_dirty ();
770 /* disconnect all existing ports so that we get a fresh start */
772 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
773 _session.engine().disconnect (*i);
780 /** Attach output_buffers to port buffers.
782 * Connected to IOs own MoreChannels signal.
785 IO::attach_buffers(ChanCount ignored)
787 _output_buffers->attach_buffers(_outputs);
791 IO::ensure_io (const ChanCount& in, const ChanCount& out, bool clear, void* src)
794 uint32_t nin = in.get(_default_type);
795 uint32_t nout = out.get(_default_type);
797 // We only deal with one type still. Sorry about your luck.
798 assert(nin == in.get_total());
799 assert(nout == out.get_total());
801 return ensure_io(nin, nout, clear, src);
805 IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
807 bool in_changed = false;
808 bool out_changed = false;
811 nin = min (_input_maximum.get(_default_type), static_cast<size_t>(nin));
813 nout = min (_output_maximum.get(_default_type), static_cast<size_t>(nout));
815 if (nin == n_inputs().get(_default_type) && nout == n_outputs().get(_default_type) && !clear) {
820 Glib::Mutex::Lock em (_session.engine().process_lock());
821 Glib::Mutex::Lock lm (io_lock);
825 if (n_outputs().get(_default_type) == nout) {
826 need_pan_reset = false;
828 need_pan_reset = true;
831 /* remove unused ports */
833 while (n_inputs().get(_default_type) > nin) {
836 _session.engine().unregister_port (_inputs.back());
842 while (n_outputs().get(_default_type) > nout) {
845 _session.engine().unregister_port (_outputs.back());
848 out_changed = true;*/
851 /* create any necessary new ports (of the default type) */
853 while (n_inputs().get(_default_type) < nin) {
857 /* Create a new input port */
859 if (_input_maximum.get_total() == 1) {
860 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
863 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
867 if ((port = _session.engine().register_input_port (_default_type, buf)) == 0) {
868 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
873 catch (AudioEngine::PortRegistrationFailure& err) {
874 setup_peak_meters ();
880 _inputs.add_port (port);
884 /* create any necessary new ports */
886 while (n_outputs().get(_default_type) < nout) {
890 /* Create a new output port */
892 if (_output_maximum.get_total() == 1) {
893 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
895 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
899 if ((port = _session.engine().register_output_port (_default_type, buf)) == 0) {
900 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
905 catch (AudioEngine::PortRegistrationFailure& err) {
906 setup_peak_meters ();
912 _outputs.add_port (port);
918 /* disconnect all existing ports so that we get a fresh start */
920 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
921 _session.engine().disconnect (*i);
924 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
925 _session.engine().disconnect (*i);
929 if (in_changed || out_changed) {
930 setup_peak_meters ();
936 drop_output_connection ();
937 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
941 drop_input_connection ();
942 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
945 if (in_changed || out_changed) {
946 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
947 _session.set_dirty ();
954 IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
956 bool changed = false;
958 n = min (_input_maximum.get(_default_type), static_cast<size_t>(n));
960 if (n == n_inputs().get(_default_type) && !clear) {
965 Glib::Mutex::Lock em (_session.engine().process_lock());
966 Glib::Mutex::Lock im (io_lock);
967 changed = ensure_inputs_locked (n, clear, src);
969 changed = ensure_inputs_locked (n, clear, src);
973 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
974 _session.set_dirty ();
980 IO::ensure_outputs_locked (uint32_t n, bool clear, void* src)
983 bool changed = false;
986 if (n_outputs().get(_default_type) == n) {
987 need_pan_reset = false;
989 need_pan_reset = true;
992 /* remove unused ports */
994 while (n_outputs().get(_default_type) > n) {
997 _session.engine().unregister_port (_outputs.back());
1004 /* create any necessary new ports */
1006 while (n_outputs().get(_default_type) < n) {
1010 /* Create a new output port */
1012 if (_output_maximum.get(_default_type) == 1) {
1013 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1015 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1018 if ((output_port = _session.engine().register_output_port (_default_type, buf)) == 0) {
1019 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1023 _outputs.add_port (output_port);
1025 setup_peak_meters ();
1027 if (need_pan_reset) {
1033 drop_output_connection ();
1034 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1035 _session.set_dirty ();
1039 /* disconnect all existing ports so that we get a fresh start */
1041 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1042 _session.engine().disconnect (*i);
1050 IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
1052 bool changed = false;
1054 if (_output_maximum < ChanCount::INFINITE) {
1055 n = min (_output_maximum.get(_default_type), static_cast<size_t>(n));
1056 if (n == n_outputs().get(_default_type) && !clear) {
1061 /* XXX caller should hold io_lock, but generally doesn't */
1064 Glib::Mutex::Lock em (_session.engine().process_lock());
1065 Glib::Mutex::Lock im (io_lock);
1066 changed = ensure_outputs_locked (n, clear, src);
1068 changed = ensure_outputs_locked (n, clear, src);
1072 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1078 IO::effective_gain () const
1080 if (gain_automation_playback()) {
1081 return _effective_gain;
1083 return _desired_gain;
1090 if (panners_legal) {
1091 if (!no_panner_reset) {
1092 _panner->reset (n_outputs().get(_default_type), pans_required());
1095 panner_legal_c.disconnect ();
1096 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1101 IO::panners_became_legal ()
1103 _panner->reset (n_outputs().get(_default_type), pans_required());
1104 _panner->load (); // automation
1105 panner_legal_c.disconnect ();
1110 IO::defer_pan_reset ()
1112 no_panner_reset = true;
1116 IO::allow_pan_reset ()
1118 no_panner_reset = false;
1124 IO::get_state (void)
1126 return state (true);
1130 IO::state (bool full_state)
1132 XMLNode* node = new XMLNode (state_node_name);
1135 bool need_ins = true;
1136 bool need_outs = true;
1137 LocaleGuard lg (X_("POSIX"));
1138 Glib::Mutex::Lock lm (io_lock);
1140 node->add_property("name", _name);
1142 node->add_property("id", buf);
1146 if (_input_connection) {
1147 node->add_property ("input-connection", _input_connection->name());
1151 if (_output_connection) {
1152 node->add_property ("output-connection", _output_connection->name());
1157 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1159 const char **connections = i->get_connections();
1161 if (connections && connections[0]) {
1164 for (int n = 0; connections && connections[n]; ++n) {
1169 /* if its a connection to our own port,
1170 return only the port name, not the
1171 whole thing. this allows connections
1172 to be re-established even when our
1173 client name is different.
1176 str += _session.engine().make_port_name_relative (connections[n]);
1188 node->add_property ("inputs", str);
1194 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1196 const char **connections = i->get_connections();
1198 if (connections && connections[0]) {
1202 for (int n = 0; connections[n]; ++n) {
1207 str += _session.engine().make_port_name_relative (connections[n]);
1219 node->add_property ("outputs", str);
1222 node->add_child_nocopy (_panner->state (full_state));
1224 snprintf (buf, sizeof(buf), "%2.12f", gain());
1225 node->add_property ("gain", buf);
1227 const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1228 const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1229 const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1230 const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1232 snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1234 node->add_property ("iolimits", buf);
1239 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1241 /* never store anything except Off for automation state in a template */
1242 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
1244 node->add_property ("automation-state", buf);
1245 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_style());
1246 node->add_property ("automation-style", buf);
1248 /* XXX same for pan etc. */
1254 IO::connecting_became_legal ()
1258 if (pending_state_node == 0) {
1259 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1264 connection_legal_c.disconnect ();
1266 ret = make_connections (*pending_state_node);
1269 delete pending_state_node;
1270 pending_state_node = 0;
1277 IO::ports_became_legal ()
1281 if (pending_state_node == 0) {
1282 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1287 port_legal_c.disconnect ();
1289 ret = create_ports (*pending_state_node);
1291 if (connecting_legal) {
1292 delete pending_state_node;
1293 pending_state_node = 0;
1300 IO::set_state (const XMLNode& node)
1302 const XMLProperty* prop;
1303 XMLNodeConstIterator iter;
1304 LocaleGuard lg (X_("POSIX"));
1306 /* force use of non-localized representation of decimal point,
1307 since we use it a lot in XML files and so forth.
1310 if (node.name() != state_node_name) {
1311 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1315 if ((prop = node.property ("name")) != 0) {
1316 _name = prop->value();
1317 _panner->set_name (_name);
1320 if ((prop = node.property ("id")) != 0) {
1321 _id = prop->value ();
1326 size_t out_min = -1;
1327 size_t out_max = -1;
1329 if ((prop = node.property ("iolimits")) != 0) {
1330 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1331 &in_min, &in_max, &out_min, &out_max);
1332 _input_minimum = ChanCount(_default_type, in_min);
1333 _input_maximum = ChanCount(_default_type, in_max);
1334 _output_minimum = ChanCount(_default_type, out_min);
1335 _output_maximum = ChanCount(_default_type, out_max);
1338 if ((prop = node.property ("gain")) != 0) {
1339 set_gain (atof (prop->value().c_str()), this);
1340 _gain = _desired_gain;
1343 for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1344 if ((*iter)->name() == "Panner") {
1345 _panner->set_state (**iter);
1349 if ((prop = node.property ("automation-state")) != 0) {
1352 x = strtol (prop->value().c_str(), 0, 16);
1353 set_gain_automation_state (AutoState (x));
1356 if ((prop = node.property ("automation-style")) != 0) {
1359 x = strtol (prop->value().c_str(), 0, 16);
1360 set_gain_automation_style (AutoStyle (x));
1365 if (create_ports (node)) {
1371 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1374 if (panners_legal) {
1377 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1380 if (connecting_legal) {
1382 if (make_connections (node)) {
1388 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1391 if (!ports_legal || !connecting_legal) {
1392 pending_state_node = new XMLNode (node);
1399 IO::create_ports (const XMLNode& node)
1401 const XMLProperty* prop;
1403 int num_outputs = 0;
1405 if ((prop = node.property ("input-connection")) != 0) {
1407 Connection* c = _session.connection_by_name (prop->value());
1410 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1412 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1413 error << _("No input connections available as a replacement")
1417 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1422 num_inputs = c->nports();
1424 } else if ((prop = node.property ("inputs")) != 0) {
1426 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1429 if ((prop = node.property ("output-connection")) != 0) {
1430 Connection* c = _session.connection_by_name (prop->value());
1433 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1435 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1436 error << _("No output connections available as a replacement")
1440 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1445 num_outputs = c->nports ();
1447 } else if ((prop = node.property ("outputs")) != 0) {
1448 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1451 no_panner_reset = true;
1453 if (ensure_io (num_inputs, num_outputs, true, this)) {
1454 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1458 no_panner_reset = false;
1460 set_deferred_state ();
1468 IO::make_connections (const XMLNode& node)
1470 const XMLProperty* prop;
1472 if ((prop = node.property ("input-connection")) != 0) {
1473 Connection* c = _session.connection_by_name (prop->value());
1476 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1478 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1479 error << _("No input connections available as a replacement")
1483 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1488 use_input_connection (*c, this);
1490 } else if ((prop = node.property ("inputs")) != 0) {
1491 if (set_inputs (prop->value())) {
1492 error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1497 if ((prop = node.property ("output-connection")) != 0) {
1498 Connection* c = _session.connection_by_name (prop->value());
1501 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1503 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1504 error << _("No output connections available as a replacement")
1508 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1513 use_output_connection (*c, this);
1515 } else if ((prop = node.property ("outputs")) != 0) {
1516 if (set_outputs (prop->value())) {
1517 error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1526 IO::set_inputs (const string& str)
1528 vector<string> ports;
1533 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1537 if (ensure_inputs (nports, true, true, this)) {
1541 string::size_type start, end, ostart;
1548 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1551 if ((end = str.find_first_of ('}', start)) == string::npos) {
1552 error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1556 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1557 error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1563 for (int x = 0; x < n; ++x) {
1564 connect_input (input (i), ports[x], this);
1576 IO::set_outputs (const string& str)
1578 vector<string> ports;
1583 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1587 if (ensure_outputs (nports, true, true, this)) {
1591 string::size_type start, end, ostart;
1598 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1601 if ((end = str.find_first_of ('}', start)) == string::npos) {
1602 error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1606 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1607 error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1613 for (int x = 0; x < n; ++x) {
1614 connect_output (output (i), ports[x], this);
1626 IO::parse_io_string (const string& str, vector<string>& ports)
1628 string::size_type pos, opos;
1630 if (str.length() == 0) {
1639 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1640 ports.push_back (str.substr (opos, pos - opos));
1644 if (opos < str.length()) {
1645 ports.push_back (str.substr(opos));
1648 return ports.size();
1652 IO::parse_gain_string (const string& str, vector<string>& ports)
1654 string::size_type pos, opos;
1660 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1661 ports.push_back (str.substr (opos, pos - opos));
1665 if (opos < str.length()) {
1666 ports.push_back (str.substr(opos));
1669 return ports.size();
1673 IO::set_name (string name, void* src)
1675 if (name == _name) {
1679 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1680 string current_name = i->short_name();
1681 current_name.replace (current_name.find (_name), _name.length(), name);
1682 i->set_name (current_name);
1685 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1686 string current_name = i->short_name();
1687 current_name.replace (current_name.find (_name), _name.length(), name);
1688 i->set_name (current_name);
1692 name_changed (src); /* EMIT SIGNAL */
1698 IO::set_input_minimum (int n)
1701 _input_minimum = ChanCount::ZERO;
1703 _input_minimum = ChanCount(_default_type, n);
1707 IO::set_input_maximum (int n)
1710 _input_maximum = ChanCount::INFINITE;
1712 _input_maximum = ChanCount(_default_type, n);
1716 IO::set_output_minimum (int n)
1719 _output_minimum = ChanCount::ZERO;
1721 _output_minimum = ChanCount(_default_type, n);
1725 IO::set_output_maximum (int n)
1728 _output_maximum = ChanCount::INFINITE;
1730 _output_maximum = ChanCount(_default_type, n);
1734 IO::set_input_minimum (ChanCount n)
1740 IO::set_input_maximum (ChanCount n)
1746 IO::set_output_minimum (ChanCount n)
1748 _output_minimum = n;
1752 IO::set_output_maximum (ChanCount n)
1754 _output_maximum = n;
1758 IO::set_port_latency (jack_nframes_t nframes)
1760 Glib::Mutex::Lock lm (io_lock);
1762 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1763 i->set_latency (nframes);
1768 IO::output_latency () const
1770 jack_nframes_t max_latency;
1771 jack_nframes_t latency;
1775 /* io lock not taken - must be protected by other means */
1777 for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1778 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1779 max_latency = latency;
1787 IO::input_latency () const
1789 jack_nframes_t max_latency;
1790 jack_nframes_t latency;
1794 /* io lock not taken - must be protected by other means */
1796 for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1797 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1798 max_latency = latency;
1806 IO::use_input_connection (Connection& c, void* src)
1811 Glib::Mutex::Lock lm (_session.engine().process_lock());
1812 Glib::Mutex::Lock lm2 (io_lock);
1816 drop_input_connection ();
1818 if (ensure_inputs (limit, false, false, src)) {
1822 /* first pass: check the current state to see what's correctly
1823 connected, and drop anything that we don't want.
1826 for (uint32_t n = 0; n < limit; ++n) {
1827 const Connection::PortList& pl = c.port_connections (n);
1829 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1831 if (!_inputs.port(n)->connected_to ((*i))) {
1833 /* clear any existing connections */
1835 _session.engine().disconnect (*_inputs.port(n));
1837 } else if (_inputs.port(n)->connected() > 1) {
1839 /* OK, it is connected to the port we want,
1840 but its also connected to other ports.
1841 Change that situation.
1844 /* XXX could be optimized to not drop
1848 _session.engine().disconnect (*_inputs.port(n));
1854 /* second pass: connect all requested ports where necessary */
1856 for (uint32_t n = 0; n < limit; ++n) {
1857 const Connection::PortList& pl = c.port_connections (n);
1859 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1861 if (!_inputs.port(n)->connected_to ((*i))) {
1863 if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1871 _input_connection = &c;
1873 input_connection_configuration_connection = c.ConfigurationChanged.connect
1874 (mem_fun (*this, &IO::input_connection_configuration_changed));
1875 input_connection_connection_connection = c.ConnectionsChanged.connect
1876 (mem_fun (*this, &IO::input_connection_connection_changed));
1879 input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
1884 IO::use_output_connection (Connection& c, void* src)
1889 Glib::Mutex::Lock lm (_session.engine().process_lock());
1890 Glib::Mutex::Lock lm2 (io_lock);
1894 drop_output_connection ();
1896 if (ensure_outputs (limit, false, false, src)) {
1900 /* first pass: check the current state to see what's correctly
1901 connected, and drop anything that we don't want.
1904 for (uint32_t n = 0; n < limit; ++n) {
1906 const Connection::PortList& pl = c.port_connections (n);
1908 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1910 if (!_outputs.port(n)->connected_to ((*i))) {
1912 /* clear any existing connections */
1914 _session.engine().disconnect (*_outputs.port(n));
1916 } else if (_outputs.port(n)->connected() > 1) {
1918 /* OK, it is connected to the port we want,
1919 but its also connected to other ports.
1920 Change that situation.
1923 /* XXX could be optimized to not drop
1927 _session.engine().disconnect (*_outputs.port(n));
1932 /* second pass: connect all requested ports where necessary */
1934 for (uint32_t n = 0; n < limit; ++n) {
1936 const Connection::PortList& pl = c.port_connections (n);
1938 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1940 if (!_outputs.port(n)->connected_to ((*i))) {
1942 if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
1949 _output_connection = &c;
1951 output_connection_configuration_connection = c.ConfigurationChanged.connect
1952 (mem_fun (*this, &IO::output_connection_configuration_changed));
1953 output_connection_connection_connection = c.ConnectionsChanged.connect
1954 (mem_fun (*this, &IO::output_connection_connection_changed));
1957 output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
1963 IO::disable_connecting ()
1965 connecting_legal = false;
1970 IO::enable_connecting ()
1972 connecting_legal = true;
1973 return ConnectingLegal ();
1977 IO::disable_ports ()
1979 ports_legal = false;
1987 return PortsLegal ();
1991 IO::disable_panners (void)
1993 panners_legal = false;
1998 IO::reset_panners ()
2000 panners_legal = true;
2001 return PannersLegal ();
2005 IO::input_connection_connection_changed (int ignored)
2007 use_input_connection (*_input_connection, this);
2011 IO::input_connection_configuration_changed ()
2013 use_input_connection (*_input_connection, this);
2017 IO::output_connection_connection_changed (int ignored)
2019 use_output_connection (*_output_connection, this);
2023 IO::output_connection_configuration_changed ()
2025 use_output_connection (*_output_connection, this);
2029 IO::GainControllable::set_value (float val)
2031 io.set_gain (direct_control_to_gain (val), this);
2035 IO::GainControllable::get_value (void) const
2037 return direct_gain_to_control (io.effective_gain());
2041 IO::get_memento() const
2043 return sigc::bind (mem_fun (*(const_cast<IO *>(this)), &StateManager::use_state), _current_state_id);
2047 IO::restore_state (StateManager::State& state)
2052 StateManager::State*
2053 IO::state_factory (std::string why) const
2055 StateManager::State* state = new StateManager::State (why);
2060 IO::setup_peak_meters()
2062 _meter->setup(std::max(_inputs.count(), _outputs.count()));
2066 Update the peak meters.
2068 The meter signal lock is taken to prevent modification of the
2069 Meter signal while updating the meters, taking the meter signal
2070 lock prior to taking the io_lock ensures that all IO will remain
2071 valid while metering.
2076 Glib::Mutex::Lock guard (m_meter_signal_lock);
2078 Meter(); /* EMIT SIGNAL */
2084 // FIXME: Remove this function and just connect signal directly to PeakMeter::meter
2086 Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2091 IO::save_automation (const string& path)
2096 fullpath = _session.automation_dir();
2099 out.open (fullpath.c_str());
2102 error << string_compose(_("%1: could not open automation event file \"%2\""), _name, fullpath) << endmsg;
2106 out << X_("version ") << current_automation_version_number << endl;
2108 /* XXX use apply_to_points to get thread safety */
2110 for (AutomationList::iterator i = _gain_automation_curve.begin(); i != _gain_automation_curve.end(); ++i) {
2111 out << "g " << (jack_nframes_t) floor ((*i)->when) << ' ' << (*i)->value << endl;
2120 IO::load_automation (const string& path)
2125 uint32_t linecnt = 0;
2127 LocaleGuard lg (X_("POSIX"));
2129 fullpath = _session.automation_dir();
2132 in.open (fullpath.c_str());
2135 fullpath = _session.automation_dir();
2136 fullpath += _session.snap_name();
2139 in.open (fullpath.c_str());
2141 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
2146 clear_automation ();
2148 while (in.getline (line, sizeof(line), '\n')) {
2150 jack_nframes_t when;
2153 if (++linecnt == 1) {
2154 if (memcmp (line, "version", 7) == 0) {
2155 if (sscanf (line, "version %f", &version) != 1) {
2156 error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
2160 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
2164 if (version != current_automation_version_number) {
2165 error << string_compose(_("mismatched automation event file version (%1)"), version) << endmsg;
2172 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
2173 warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
2179 _gain_automation_curve.add (when, value, true);
2189 /* older (pre-1.0) versions of ardour used this */
2193 warning << _("dubious automation event found (and ignored)") << endmsg;
2197 _gain_automation_curve.save_state (_("loaded from disk"));
2203 IO::clear_automation ()
2205 Glib::Mutex::Lock lm (automation_lock);
2206 _gain_automation_curve.clear ();
2207 _panner->clear_automation ();
2211 IO::set_gain_automation_state (AutoState state)
2213 bool changed = false;
2216 Glib::Mutex::Lock lm (automation_lock);
2218 if (state != _gain_automation_curve.automation_state()) {
2220 last_automation_snapshot = 0;
2221 _gain_automation_curve.set_automation_state (state);
2224 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2230 _session.set_dirty ();
2231 gain_automation_state_changed (); /* EMIT SIGNAL */
2236 IO::set_gain_automation_style (AutoStyle style)
2238 bool changed = false;
2241 Glib::Mutex::Lock lm (automation_lock);
2243 if (style != _gain_automation_curve.automation_style()) {
2245 _gain_automation_curve.set_automation_style (style);
2250 gain_automation_style_changed (); /* EMIT SIGNAL */
2254 IO::inc_gain (gain_t factor, void *src)
2256 if (_desired_gain == 0.0f)
2257 set_gain (0.000001f + (0.000001f * factor), src);
2259 set_gain (_desired_gain + (_desired_gain * factor), src);
2263 IO::set_gain (gain_t val, void *src)
2265 // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2266 if (val>1.99526231f) val=1.99526231f;
2269 Glib::Mutex::Lock dm (declick_lock);
2270 _desired_gain = val;
2273 if (_session.transport_stopped()) {
2274 _effective_gain = val;
2279 _gain_control.Changed (); /* EMIT SIGNAL */
2281 if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2282 _gain_automation_curve.add (_session.transport_frame(), val);
2286 _session.set_dirty();
2290 IO::start_gain_touch ()
2292 _gain_automation_curve.start_touch ();
2296 IO::end_gain_touch ()
2298 _gain_automation_curve.stop_touch ();
2302 IO::start_pan_touch (uint32_t which)
2304 if (which < _panner->size()) {
2305 (*_panner)[which]->automation().start_touch();
2310 IO::end_pan_touch (uint32_t which)
2312 if (which < _panner->size()) {
2313 (*_panner)[which]->automation().stop_touch();
2319 IO::automation_snapshot (jack_nframes_t now)
2321 if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2323 if (gain_automation_recording()) {
2324 _gain_automation_curve.rt_add (now, gain());
2327 _panner->snapshot (now);
2329 last_automation_snapshot = now;
2334 IO::transport_stopped (jack_nframes_t frame)
2336 _gain_automation_curve.reposition_for_rt_add (frame);
2338 if (_gain_automation_curve.automation_state() != Off) {
2340 if (gain_automation_recording()) {
2341 _gain_automation_curve.save_state (_("automation write/touch"));
2344 /* the src=0 condition is a special signal to not propagate
2345 automation gain changes into the mix group when locating.
2348 set_gain (_gain_automation_curve.eval (frame), 0);
2351 _panner->transport_stopped (frame);
2355 IO::find_input_port_hole ()
2357 /* CALLER MUST HOLD IO LOCK */
2361 if (_inputs.empty()) {
2365 for (n = 1; n < UINT_MAX; ++n) {
2366 char buf[jack_port_name_size()];
2367 PortSet::iterator i = _inputs.begin();
2369 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2371 for ( ; i != _inputs.end(); ++i) {
2372 if (i->short_name() == buf) {
2377 if (i == _inputs.end()) {
2385 IO::find_output_port_hole ()
2387 /* CALLER MUST HOLD IO LOCK */
2391 if (_outputs.empty()) {
2395 for (n = 1; n < UINT_MAX; ++n) {
2396 char buf[jack_port_name_size()];
2397 PortSet::iterator i = _outputs.begin();
2399 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2401 for ( ; i != _outputs.end(); ++i) {
2402 if (i->short_name() == buf) {
2407 if (i == _outputs.end()) {
2416 IO::audio_input(uint32_t n) const
2418 return dynamic_cast<AudioPort*>(input(n));
2422 IO::audio_output(uint32_t n) const
2424 return dynamic_cast<AudioPort*>(output(n));
2428 IO::midi_input(uint32_t n) const
2430 return dynamic_cast<MidiPort*>(input(n));
2434 IO::midi_output(uint32_t n) const
2436 return dynamic_cast<MidiPort*>(output(n));
2440 IO::set_phase_invert (bool yn, void *src)
2442 if (_phase_invert != yn) {
2445 // phase_invert_changed (src); /* EMIT SIGNAL */