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.
25 #include <sigc++/bind.h>
27 #include <glibmm/thread.h>
29 #include <pbd/xml++.h>
31 #include <ardour/audioengine.h>
32 #include <ardour/io.h>
33 #include <ardour/port.h>
34 #include <ardour/audio_port.h>
35 #include <ardour/midi_port.h>
36 #include <ardour/connection.h>
37 #include <ardour/session.h>
38 #include <ardour/cycle_timer.h>
39 #include <ardour/panner.h>
40 #include <ardour/buffer_set.h>
41 #include <ardour/meter.h>
42 #include <ardour/amp.h>
49 A bug in OS X's cmath that causes isnan() and isinf() to be
50 "undeclared". the following works around that
53 #if defined(__APPLE__) && defined(__MACH__)
54 extern "C" int isnan (double);
55 extern "C" int isinf (double);
60 using namespace ARDOUR;
63 nframes_t IO::_automation_interval = 0;
65 const string IO::state_node_name = "IO";
66 bool IO::connecting_legal = false;
67 bool IO::ports_legal = false;
68 bool IO::panners_legal = false;
69 sigc::signal<void> IO::Meter;
70 sigc::signal<int> IO::ConnectingLegal;
71 sigc::signal<int> IO::PortsLegal;
72 sigc::signal<int> IO::PannersLegal;
73 sigc::signal<void,ChanCount> IO::MoreChannels;
74 sigc::signal<int> IO::PortsCreated;
76 Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT;
78 /* this is a default mapper of [0 .. 1.0] control values to a gain coefficient.
79 others can be imagined.
82 static gain_t direct_control_to_gain (double fract) {
83 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
84 /* this maxes at +6dB */
85 return pow (2.0,(sqrt(sqrt(sqrt(fract)))*198.0-192.0)/6.0);
88 static double direct_gain_to_control (gain_t gain) {
89 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
90 if (gain == 0) return 0.0;
92 return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0);
96 /** @param default_type The type of port that will be created by ensure_io
97 * and friends if no type is explicitly requested (to avoid breakage).
99 IO::IO (Session& s, string name,
100 int input_min, int input_max, int output_min, int output_max,
101 DataType default_type)
103 _output_buffers(new BufferSet()),
105 _default_type(default_type),
106 _gain_control (X_("gaincontrol"), *this),
107 _gain_automation_curve (0.0, 2.0, 1.0),
108 _input_minimum (ChanCount::ZERO),
109 _input_maximum (ChanCount::INFINITE),
110 _output_minimum (ChanCount::ZERO),
111 _output_maximum (ChanCount::INFINITE)
113 _panner = new Panner (name, _session);
114 _meter = new PeakMeter (_session);
117 _input_minimum = ChanCount(_default_type, input_min);
119 if (input_max >= 0) {
120 _input_maximum = ChanCount(_default_type, input_max);
122 if (output_min > 0) {
123 _output_minimum = ChanCount(_default_type, output_min);
125 if (output_max >= 0) {
126 _output_maximum = ChanCount(_default_type, output_max);
131 _input_connection = 0;
132 _output_connection = 0;
133 pending_state_node = 0;
134 no_panner_reset = false;
135 _phase_invert = false;
138 apply_gain_automation = false;
140 last_automation_snapshot = 0;
142 _gain_automation_state = Off;
143 _gain_automation_style = Absolute;
146 // IO::Meter is emitted from another thread so the
147 // Meter signal must be protected.
148 Glib::Mutex::Lock guard (m_meter_signal_lock);
149 m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
152 // Connect to our own MoreChannels signal to connect output buffers
153 IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
155 _session.add_controllable (&_gain_control);
158 IO::IO (Session& s, const XMLNode& node, DataType dt)
160 _output_buffers(new BufferSet()),
162 _gain_control (X_("gaincontrol"), *this),
163 _gain_automation_curve (0, 0, 0) // all reset in set_state()
166 _meter = new PeakMeter (_session);
170 no_panner_reset = false;
173 _input_connection = 0;
174 _output_connection = 0;
176 apply_gain_automation = false;
181 // IO::Meter is emitted from another thread so the
182 // Meter signal must be protected.
183 Glib::Mutex::Lock guard (m_meter_signal_lock);
184 m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
187 // Connect to our own MoreChannels signal to connect output buffers
188 IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
190 _session.add_controllable (&_gain_control);
195 Glib::Mutex::Lock guard (m_meter_signal_lock);
197 Glib::Mutex::Lock lm (io_lock);
199 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
200 _session.engine().unregister_port (*i);
203 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
204 _session.engine().unregister_port (*i);
207 m_meter_connection.disconnect();
211 delete _output_buffers;
215 IO::silence (nframes_t nframes, nframes_t offset)
217 /* io_lock, not taken: function must be called from Session::process() calltree */
219 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
220 i->get_buffer().silence (nframes, offset);
224 /** Deliver bufs to the IO's Jack outputs.
226 * This function should automatically do whatever it necessary to correctly deliver bufs
227 * to the outputs, eg applying gain or pan or whatever else needs to be done.
230 IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
232 // FIXME: type specific code doesn't actually need to be here, it will go away in time
234 /* ********** AUDIO ********** */
236 // Apply gain if gain automation isn't playing
237 if ( ! apply_gain_automation) {
239 gain_t dg = _gain; // desired gain
242 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
250 Amp::run(bufs, nframes, _gain, dg, _phase_invert);
253 // Use the panner to distribute audio to output port buffers
254 if (_panner && !_panner->empty() && !_panner->bypassed()) {
255 _panner->distribute(bufs, output_buffers(), start_frame, end_frame, nframes, offset);
257 const DataType type = DataType::AUDIO;
259 // Copy any audio 1:1 to outputs
260 assert(bufs.count().get(DataType::AUDIO) == output_buffers().count().get(DataType::AUDIO));
261 BufferSet::iterator o = output_buffers().begin(type);
262 for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) {
263 o->read_from(*i, nframes, offset);
268 /* ********** MIDI ********** */
270 // No MIDI, we're done here
271 if (bufs.count().get(DataType::MIDI) == 0) {
275 const DataType type = DataType::MIDI;
277 // Copy any MIDI 1:1 to outputs
278 assert(bufs.count().get(DataType::MIDI) == output_buffers().count().get(DataType::MIDI));
279 BufferSet::iterator o = output_buffers().begin(type);
280 for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) {
281 o->read_from(*i, nframes, offset);
286 IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset)
288 assert(outs.available() >= n_inputs());
290 outs.set_count(n_inputs());
292 if (outs.count() == ChanCount::ZERO)
295 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
297 BufferSet::iterator o = outs.begin(*t);
298 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
299 o->read_from(i->get_buffer(), nframes, offset);
306 IO::just_meter_input (nframes_t start_frame, nframes_t end_frame,
307 nframes_t nframes, nframes_t offset)
309 BufferSet& bufs = _session.get_scratch_buffers (n_inputs());
311 collect_input (bufs, nframes, offset);
313 _meter->run(bufs, nframes);
317 IO::drop_input_connection ()
319 _input_connection = 0;
320 input_connection_configuration_connection.disconnect();
321 input_connection_connection_connection.disconnect();
322 _session.set_dirty ();
326 IO::drop_output_connection ()
328 _output_connection = 0;
329 output_connection_configuration_connection.disconnect();
330 output_connection_connection_connection.disconnect();
331 _session.set_dirty ();
335 IO::disconnect_input (Port* our_port, string other_port, void* src)
337 if (other_port.length() == 0 || our_port == 0) {
342 Glib::Mutex::Lock em (_session.engine().process_lock());
345 Glib::Mutex::Lock lm (io_lock);
347 /* check that our_port is really one of ours */
349 if ( ! _inputs.contains(our_port)) {
353 /* disconnect it from the source */
355 if (_session.engine().disconnect (other_port, our_port->name())) {
356 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
360 drop_input_connection();
364 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
365 _session.set_dirty ();
371 IO::connect_input (Port* our_port, string other_port, void* src)
373 if (other_port.length() == 0 || our_port == 0) {
378 Glib::Mutex::Lock em(_session.engine().process_lock());
381 Glib::Mutex::Lock lm (io_lock);
383 /* check that our_port is really one of ours */
385 if ( ! _inputs.contains(our_port) ) {
389 /* connect it to the source */
391 if (_session.engine().connect (other_port, our_port->name())) {
395 drop_input_connection ();
399 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
400 _session.set_dirty ();
405 IO::disconnect_output (Port* our_port, string other_port, void* src)
407 if (other_port.length() == 0 || our_port == 0) {
412 Glib::Mutex::Lock em(_session.engine().process_lock());
415 Glib::Mutex::Lock lm (io_lock);
417 /* check that our_port is really one of ours */
419 if ( ! _outputs.contains(our_port) ) {
423 /* disconnect it from the destination */
425 if (_session.engine().disconnect (our_port->name(), other_port)) {
426 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
430 drop_output_connection ();
434 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
435 _session.set_dirty ();
440 IO::connect_output (Port* our_port, string other_port, void* src)
442 if (other_port.length() == 0 || our_port == 0) {
447 Glib::Mutex::Lock em(_session.engine().process_lock());
450 Glib::Mutex::Lock lm (io_lock);
452 /* check that our_port is really one of ours */
454 if ( ! _outputs.contains(our_port) ) {
458 /* connect it to the destination */
460 if (_session.engine().connect (our_port->name(), other_port)) {
464 drop_output_connection ();
468 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
469 _session.set_dirty ();
474 IO::set_input (Port* other_port, void* src)
476 /* this removes all but one ports, and connects that one port
477 to the specified source.
480 if (_input_minimum.get_total() > 1) {
481 /* sorry, you can't do this */
485 if (other_port == 0) {
486 if (_input_minimum == ChanCount::ZERO) {
487 return ensure_inputs (ChanCount::ZERO, false, true, src);
493 if (ensure_inputs (ChanCount(other_port->type(), 1), true, true, src)) {
497 return connect_input (_inputs.port(0), other_port->name(), src);
501 IO::remove_output_port (Port* port, void* src)
503 IOChange change (NoChange);
506 Glib::Mutex::Lock em(_session.engine().process_lock());
509 Glib::Mutex::Lock lm (io_lock);
511 if (n_outputs() <= _output_minimum) {
512 /* sorry, you can't do this */
516 if (_outputs.remove(port)) {
517 change = IOChange (change|ConfigurationChanged);
519 if (port->connected()) {
520 change = IOChange (change|ConnectionsChanged);
523 _session.engine().unregister_port (*port);
524 drop_output_connection ();
526 setup_peak_meters ();
532 if (change != NoChange) {
533 output_changed (change, src);
534 _session.set_dirty ();
541 /** Add an output port.
543 * @param destination Name of input port to connect new port to.
544 * @param src Source for emitted ConfigurationChanged signal.
545 * @param type Data type of port. Default value (NIL) will use this IO's default type.
548 IO::add_output_port (string destination, void* src, DataType type)
553 if (type == DataType::NIL)
554 type = _default_type;
557 Glib::Mutex::Lock em(_session.engine().process_lock());
560 Glib::Mutex::Lock lm (io_lock);
562 if (n_outputs() >= _output_maximum) {
566 /* Create a new output port */
568 // FIXME: naming scheme for differently typed ports?
569 if (_output_maximum.get(type) == 1) {
570 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
572 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
575 if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
576 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
580 _outputs.add (our_port);
581 drop_output_connection ();
582 setup_peak_meters ();
586 MoreChannels (n_outputs()); /* EMIT SIGNAL */
589 if (destination.length()) {
590 if (_session.engine().connect (our_port->name(), destination)) {
595 // pan_changed (src); /* EMIT SIGNAL */
596 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
597 _session.set_dirty ();
603 IO::remove_input_port (Port* port, void* src)
605 IOChange change (NoChange);
608 Glib::Mutex::Lock em(_session.engine().process_lock());
611 Glib::Mutex::Lock lm (io_lock);
613 if (n_inputs() <= _input_minimum) {
614 /* sorry, you can't do this */
618 if (_inputs.remove(port)) {
619 change = IOChange (change|ConfigurationChanged);
621 if (port->connected()) {
622 change = IOChange (change|ConnectionsChanged);
625 _session.engine().unregister_port (*port);
626 drop_input_connection ();
628 setup_peak_meters ();
634 if (change != NoChange) {
635 input_changed (change, src);
636 _session.set_dirty ();
644 /** Add an input port.
646 * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
647 * @param destination Name of input port to connect new port to.
648 * @param src Source for emitted ConfigurationChanged signal.
651 IO::add_input_port (string source, void* src, DataType type)
656 if (type == DataType::NIL)
657 type = _default_type;
660 Glib::Mutex::Lock em (_session.engine().process_lock());
663 Glib::Mutex::Lock lm (io_lock);
665 if (n_inputs() >= _input_maximum) {
669 /* Create a new input port */
671 // FIXME: naming scheme for differently typed ports?
672 if (_input_maximum.get(type) == 1) {
673 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
675 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
678 if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
679 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
683 _inputs.add (our_port);
684 drop_input_connection ();
685 setup_peak_meters ();
689 MoreChannels (n_inputs()); /* EMIT SIGNAL */
692 if (source.length()) {
694 if (_session.engine().connect (source, our_port->name())) {
699 // pan_changed (src); /* EMIT SIGNAL */
700 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
701 _session.set_dirty ();
707 IO::disconnect_inputs (void* src)
710 Glib::Mutex::Lock em (_session.engine().process_lock());
713 Glib::Mutex::Lock lm (io_lock);
715 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
716 _session.engine().disconnect (*i);
719 drop_input_connection ();
723 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
729 IO::disconnect_outputs (void* src)
732 Glib::Mutex::Lock em (_session.engine().process_lock());
735 Glib::Mutex::Lock lm (io_lock);
737 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
738 _session.engine().disconnect (*i);
741 drop_output_connection ();
745 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
746 _session.set_dirty ();
752 IO::ensure_inputs_locked (ChanCount count, bool clear, void* src)
754 Port* input_port = 0;
755 bool changed = false;
758 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
760 const size_t n = count.get(*t);
762 /* remove unused ports */
763 for (size_t i = n_inputs().get(*t); i > n; --i) {
764 input_port = _inputs.port(*t, i-1);
767 _inputs.remove(input_port);
768 _session.engine().unregister_port (*input_port);
773 /* create any necessary new ports */
774 while (n_inputs().get(*t) < n) {
778 if (_input_maximum.get(*t) == 1) {
779 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
781 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
786 if ((input_port = _session.engine().register_input_port (*t, buf)) == 0) {
787 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
792 catch (AudioEngine::PortRegistrationFailure& err) {
793 setup_peak_meters ();
799 _inputs.add (input_port);
805 drop_input_connection ();
806 setup_peak_meters ();
808 MoreChannels (n_inputs()); /* EMIT SIGNAL */
809 _session.set_dirty ();
813 /* disconnect all existing ports so that we get a fresh start */
814 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
815 _session.engine().disconnect (*i);
822 /** Attach output_buffers to port buffers.
824 * Connected to IO's own MoreChannels signal.
827 IO::attach_buffers(ChanCount ignored)
829 _output_buffers->attach_buffers(_outputs);
833 IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src)
835 bool in_changed = false;
836 bool out_changed = false;
837 bool need_pan_reset = false;
839 in = min (_input_maximum, in);
841 out = min (_output_maximum, out);
843 if (in == n_inputs() && out == n_outputs() && !clear) {
848 Glib::Mutex::Lock em (_session.engine().process_lock());
849 Glib::Mutex::Lock lm (io_lock);
853 if (n_outputs() != out) {
854 need_pan_reset = true;
857 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
859 const size_t nin = in.get(*t);
860 const size_t nout = out.get(*t);
862 Port* output_port = 0;
863 Port* input_port = 0;
865 /* remove unused output ports */
866 for (size_t i = n_outputs().get(*t); i > nout; --i) {
867 output_port = _outputs.port(*t, i-1);
870 _outputs.remove(output_port);
871 _session.engine().unregister_port (*output_port);
876 /* remove unused input ports */
877 for (size_t i = n_inputs().get(*t); i > nin; --i) {
878 input_port = _inputs.port(*t, i-1);
881 _inputs.remove(input_port);
882 _session.engine().unregister_port (*input_port);
887 /* create any necessary new input ports */
889 while (n_inputs().get(*t) < nin) {
893 /* Create a new input port */
895 if (_input_maximum.get(*t) == 1) {
896 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
898 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
902 if ((port = _session.engine().register_input_port (*t, buf)) == 0) {
903 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
908 catch (AudioEngine::PortRegistrationFailure& err) {
909 setup_peak_meters ();
919 /* create any necessary new output ports */
921 while (n_outputs().get(*t) < nout) {
925 /* Create a new output port */
927 if (_output_maximum.get(*t) == 1) {
928 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
930 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
934 if ((port = _session.engine().register_output_port (*t, buf)) == 0) {
935 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
940 catch (AudioEngine::PortRegistrationFailure& err) {
941 setup_peak_meters ();
954 /* disconnect all existing ports so that we get a fresh start */
956 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
957 _session.engine().disconnect (*i);
960 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
961 _session.engine().disconnect (*i);
965 if (in_changed || out_changed) {
966 setup_peak_meters ();
972 drop_output_connection ();
973 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
977 drop_input_connection ();
978 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
981 if (in_changed || out_changed) {
982 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
983 _session.set_dirty ();
990 IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src)
992 bool changed = false;
994 count = min (_input_maximum, count);
996 if (count == n_inputs() && !clear) {
1001 Glib::Mutex::Lock em (_session.engine().process_lock());
1002 Glib::Mutex::Lock im (io_lock);
1003 changed = ensure_inputs_locked (count, clear, src);
1005 changed = ensure_inputs_locked (count, clear, src);
1009 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1010 _session.set_dirty ();
1016 IO::ensure_outputs_locked (ChanCount count, bool clear, void* src)
1018 Port* output_port = 0;
1019 bool changed = false;
1020 bool need_pan_reset = false;
1022 if (n_outputs() != count) {
1023 need_pan_reset = true;
1026 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1028 const size_t n = count.get(*t);
1030 /* remove unused ports */
1031 for (size_t i = n_outputs().get(*t); i > n; --i) {
1032 output_port = _outputs.port(*t, i-1);
1034 assert(output_port);
1035 _outputs.remove(output_port);
1036 _session.engine().unregister_port (*output_port);
1041 /* create any necessary new ports */
1042 while (n_outputs().get(*t) < n) {
1046 if (_output_maximum.get(*t) == 1) {
1047 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1049 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1052 if ((output_port = _session.engine().register_output_port (*t, buf)) == 0) {
1053 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1057 _outputs.add (output_port);
1059 setup_peak_meters ();
1061 if (need_pan_reset) {
1068 drop_output_connection ();
1069 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1070 _session.set_dirty ();
1074 /* disconnect all existing ports so that we get a fresh start */
1075 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1076 _session.engine().disconnect (*i);
1084 IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
1086 bool changed = false;
1088 if (_output_maximum < ChanCount::INFINITE) {
1089 count = min (_output_maximum, count);
1090 if (count == n_outputs() && !clear) {
1095 /* XXX caller should hold io_lock, but generally doesn't */
1098 Glib::Mutex::Lock em (_session.engine().process_lock());
1099 Glib::Mutex::Lock im (io_lock);
1100 changed = ensure_outputs_locked (count, clear, src);
1102 changed = ensure_outputs_locked (count, clear, src);
1106 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1113 IO::effective_gain () const
1115 if (gain_automation_playback()) {
1116 return _effective_gain;
1118 return _desired_gain;
1125 if (panners_legal) {
1126 if (!no_panner_reset) {
1127 _panner->reset (n_outputs().get(DataType::AUDIO), pans_required());
1130 panner_legal_c.disconnect ();
1131 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1136 IO::panners_became_legal ()
1138 _panner->reset (n_outputs().get(DataType::AUDIO), pans_required());
1139 _panner->load (); // automation
1140 panner_legal_c.disconnect ();
1145 IO::defer_pan_reset ()
1147 no_panner_reset = true;
1151 IO::allow_pan_reset ()
1153 no_panner_reset = false;
1159 IO::get_state (void)
1161 return state (true);
1165 IO::state (bool full_state)
1167 XMLNode* node = new XMLNode (state_node_name);
1170 bool need_ins = true;
1171 bool need_outs = true;
1172 LocaleGuard lg (X_("POSIX"));
1173 Glib::Mutex::Lock lm (io_lock);
1175 node->add_property("name", _name);
1176 id().print (buf, sizeof (buf));
1177 node->add_property("id", buf);
1181 if (_input_connection) {
1182 node->add_property ("input-connection", _input_connection->name());
1186 if (_output_connection) {
1187 node->add_property ("output-connection", _output_connection->name());
1192 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1194 const char **connections = i->get_connections();
1196 if (connections && connections[0]) {
1199 for (int n = 0; connections && connections[n]; ++n) {
1204 /* if its a connection to our own port,
1205 return only the port name, not the
1206 whole thing. this allows connections
1207 to be re-established even when our
1208 client name is different.
1211 str += _session.engine().make_port_name_relative (connections[n]);
1223 node->add_property ("inputs", str);
1229 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1231 const char **connections = i->get_connections();
1233 if (connections && connections[0]) {
1237 for (int n = 0; connections[n]; ++n) {
1242 str += _session.engine().make_port_name_relative (connections[n]);
1254 node->add_property ("outputs", str);
1257 node->add_child_nocopy (_panner->state (full_state));
1258 node->add_child_nocopy (_gain_control.get_state ());
1260 snprintf (buf, sizeof(buf), "%2.12f", gain());
1261 node->add_property ("gain", buf);
1263 // FIXME: this is NOT sufficient!
1264 const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1265 const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1266 const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1267 const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1269 snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1271 node->add_property ("iolimits", buf);
1277 XMLNode* autonode = new XMLNode (X_("Automation"));
1278 autonode->add_child_nocopy (get_automation_state());
1279 node->add_child_nocopy (*autonode);
1281 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1283 /* never store anything except Off for automation state in a template */
1284 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
1291 IO::set_state (const XMLNode& node)
1293 const XMLProperty* prop;
1294 XMLNodeConstIterator iter;
1295 LocaleGuard lg (X_("POSIX"));
1297 /* force use of non-localized representation of decimal point,
1298 since we use it a lot in XML files and so forth.
1301 if (node.name() != state_node_name) {
1302 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1306 if ((prop = node.property ("name")) != 0) {
1307 _name = prop->value();
1308 /* used to set panner name with this, but no more */
1311 if ((prop = node.property ("id")) != 0) {
1312 _id = prop->value ();
1317 size_t out_min = -1;
1318 size_t out_max = -1;
1320 if ((prop = node.property ("iolimits")) != 0) {
1321 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1322 &in_min, &in_max, &out_min, &out_max);
1323 _input_minimum = ChanCount(_default_type, in_min);
1324 _input_maximum = ChanCount(_default_type, in_max);
1325 _output_minimum = ChanCount(_default_type, out_min);
1326 _output_maximum = ChanCount(_default_type, out_max);
1329 if ((prop = node.property ("gain")) != 0) {
1330 set_gain (atof (prop->value().c_str()), this);
1331 _gain = _desired_gain;
1334 if ((prop = node.property ("automation-state")) != 0 || (prop = node.property ("automation-style")) != 0) {
1335 /* old school automation handling */
1338 for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1340 if ((*iter)->name() == "Panner") {
1342 _panner = new Panner (_name, _session);
1344 _panner->set_state (**iter);
1347 if ((*iter)->name() == X_("Automation")) {
1349 set_automation_state (*(*iter)->children().front());
1352 if ((*iter)->name() == X_("gaincontrol")) {
1353 _gain_control.set_state (**iter);
1359 if (create_ports (node)) {
1365 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1368 if (panners_legal) {
1371 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1374 if (connecting_legal) {
1376 if (make_connections (node)) {
1382 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1385 if (!ports_legal || !connecting_legal) {
1386 pending_state_node = new XMLNode (node);
1389 last_automation_snapshot = 0;
1395 IO::set_automation_state (const XMLNode& node)
1397 return _gain_automation_curve.set_state (node);
1401 IO::get_automation_state ()
1403 return (_gain_automation_curve.get_state ());
1407 IO::load_automation (string path)
1412 uint32_t linecnt = 0;
1414 LocaleGuard lg (X_("POSIX"));
1416 fullpath = _session.automation_dir();
1419 in.open (fullpath.c_str());
1422 fullpath = _session.automation_dir();
1423 fullpath += _session.snap_name();
1427 in.open (fullpath.c_str());
1430 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
1435 clear_automation ();
1437 while (in.getline (line, sizeof(line), '\n')) {
1439 jack_nframes_t when;
1442 if (++linecnt == 1) {
1443 if (memcmp (line, "version", 7) == 0) {
1444 if (sscanf (line, "version %f", &version) != 1) {
1445 error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
1449 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
1456 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
1457 warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
1463 _gain_automation_curve.fast_simple_add (when, value);
1473 /* older (pre-1.0) versions of ardour used this */
1477 warning << _("dubious automation event found (and ignored)") << endmsg;
1485 IO::connecting_became_legal ()
1489 if (pending_state_node == 0) {
1490 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1495 connection_legal_c.disconnect ();
1497 ret = make_connections (*pending_state_node);
1500 delete pending_state_node;
1501 pending_state_node = 0;
1507 IO::ports_became_legal ()
1511 if (pending_state_node == 0) {
1512 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1517 port_legal_c.disconnect ();
1519 ret = create_ports (*pending_state_node);
1521 if (connecting_legal) {
1522 delete pending_state_node;
1523 pending_state_node = 0;
1530 IO::create_ports (const XMLNode& node)
1532 const XMLProperty* prop;
1534 int num_outputs = 0;
1536 if ((prop = node.property ("input-connection")) != 0) {
1538 Connection* c = _session.connection_by_name (prop->value());
1541 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1543 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1544 error << _("No input connections available as a replacement")
1548 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1553 num_inputs = c->nports();
1555 } else if ((prop = node.property ("inputs")) != 0) {
1557 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1560 if ((prop = node.property ("output-connection")) != 0) {
1561 Connection* c = _session.connection_by_name (prop->value());
1564 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1566 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1567 error << _("No output connections available as a replacement")
1571 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1576 num_outputs = c->nports ();
1578 } else if ((prop = node.property ("outputs")) != 0) {
1579 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1582 no_panner_reset = true;
1584 // FIXME: audio-only
1585 if (ensure_io (ChanCount(DataType::AUDIO, num_inputs), ChanCount(DataType::AUDIO, num_outputs), true, this)) {
1586 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1590 no_panner_reset = false;
1592 set_deferred_state ();
1600 IO::make_connections (const XMLNode& node)
1602 const XMLProperty* prop;
1604 if ((prop = node.property ("input-connection")) != 0) {
1605 Connection* c = _session.connection_by_name (prop->value());
1608 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1610 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1611 error << _("No input connections available as a replacement")
1615 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1620 use_input_connection (*c, this);
1622 } else if ((prop = node.property ("inputs")) != 0) {
1623 if (set_inputs (prop->value())) {
1624 error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1629 if ((prop = node.property ("output-connection")) != 0) {
1630 Connection* c = _session.connection_by_name (prop->value());
1633 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1635 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1636 error << _("No output connections available as a replacement")
1640 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1645 use_output_connection (*c, this);
1647 } else if ((prop = node.property ("outputs")) != 0) {
1648 if (set_outputs (prop->value())) {
1649 error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1658 IO::set_inputs (const string& str)
1660 vector<string> ports;
1665 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1669 // FIXME: audio-only
1670 if (ensure_inputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
1674 string::size_type start, end, ostart;
1681 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1684 if ((end = str.find_first_of ('}', start)) == string::npos) {
1685 error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1689 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1690 error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1696 for (int x = 0; x < n; ++x) {
1697 connect_input (input (i), ports[x], this);
1709 IO::set_outputs (const string& str)
1711 vector<string> ports;
1716 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1720 // FIXME: audio-only
1721 if (ensure_outputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
1725 string::size_type start, end, ostart;
1732 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1735 if ((end = str.find_first_of ('}', start)) == string::npos) {
1736 error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1740 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1741 error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1747 for (int x = 0; x < n; ++x) {
1748 connect_output (output (i), ports[x], this);
1760 IO::parse_io_string (const string& str, vector<string>& ports)
1762 string::size_type pos, opos;
1764 if (str.length() == 0) {
1773 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1774 ports.push_back (str.substr (opos, pos - opos));
1778 if (opos < str.length()) {
1779 ports.push_back (str.substr(opos));
1782 return ports.size();
1786 IO::parse_gain_string (const string& str, vector<string>& ports)
1788 string::size_type pos, opos;
1794 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1795 ports.push_back (str.substr (opos, pos - opos));
1799 if (opos < str.length()) {
1800 ports.push_back (str.substr(opos));
1803 return ports.size();
1807 IO::set_name (string name, void* src)
1809 if (name == _name) {
1813 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1814 string current_name = i->short_name();
1815 current_name.replace (current_name.find (_name), _name.length(), name);
1816 i->set_name (current_name);
1819 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1820 string current_name = i->short_name();
1821 current_name.replace (current_name.find (_name), _name.length(), name);
1822 i->set_name (current_name);
1826 name_changed (src); /* EMIT SIGNAL */
1832 IO::set_input_minimum (ChanCount n)
1838 IO::set_input_maximum (ChanCount n)
1844 IO::set_output_minimum (ChanCount n)
1846 _output_minimum = n;
1850 IO::set_output_maximum (ChanCount n)
1852 _output_maximum = n;
1856 IO::set_port_latency (nframes_t nframes)
1858 Glib::Mutex::Lock lm (io_lock);
1860 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1861 i->set_latency (nframes);
1866 IO::output_latency () const
1868 nframes_t max_latency;
1873 /* io lock not taken - must be protected by other means */
1875 for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1876 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1877 max_latency = latency;
1885 IO::input_latency () const
1887 nframes_t max_latency;
1892 /* io lock not taken - must be protected by other means */
1894 for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1895 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1896 max_latency = latency;
1904 IO::use_input_connection (Connection& c, void* src)
1909 Glib::Mutex::Lock lm (_session.engine().process_lock());
1910 Glib::Mutex::Lock lm2 (io_lock);
1914 drop_input_connection ();
1916 // FIXME connections only work for audio-only
1917 if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
1921 /* first pass: check the current state to see what's correctly
1922 connected, and drop anything that we don't want.
1925 for (uint32_t n = 0; n < limit; ++n) {
1926 const Connection::PortList& pl = c.port_connections (n);
1928 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1930 if (!_inputs.port(n)->connected_to ((*i))) {
1932 /* clear any existing connections */
1934 _session.engine().disconnect (*_inputs.port(n));
1936 } else if (_inputs.port(n)->connected() > 1) {
1938 /* OK, it is connected to the port we want,
1939 but its also connected to other ports.
1940 Change that situation.
1943 /* XXX could be optimized to not drop
1947 _session.engine().disconnect (*_inputs.port(n));
1953 /* second pass: connect all requested ports where necessary */
1955 for (uint32_t n = 0; n < limit; ++n) {
1956 const Connection::PortList& pl = c.port_connections (n);
1958 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1960 if (!_inputs.port(n)->connected_to ((*i))) {
1962 if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1970 _input_connection = &c;
1972 input_connection_configuration_connection = c.ConfigurationChanged.connect
1973 (mem_fun (*this, &IO::input_connection_configuration_changed));
1974 input_connection_connection_connection = c.ConnectionsChanged.connect
1975 (mem_fun (*this, &IO::input_connection_connection_changed));
1978 input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
1983 IO::use_output_connection (Connection& c, void* src)
1988 Glib::Mutex::Lock lm (_session.engine().process_lock());
1989 Glib::Mutex::Lock lm2 (io_lock);
1993 drop_output_connection ();
1995 // FIXME: audio-only
1996 if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
2000 /* first pass: check the current state to see what's correctly
2001 connected, and drop anything that we don't want.
2004 for (uint32_t n = 0; n < limit; ++n) {
2006 const Connection::PortList& pl = c.port_connections (n);
2008 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2010 if (!_outputs.port(n)->connected_to ((*i))) {
2012 /* clear any existing connections */
2014 _session.engine().disconnect (*_outputs.port(n));
2016 } else if (_outputs.port(n)->connected() > 1) {
2018 /* OK, it is connected to the port we want,
2019 but its also connected to other ports.
2020 Change that situation.
2023 /* XXX could be optimized to not drop
2027 _session.engine().disconnect (*_outputs.port(n));
2032 /* second pass: connect all requested ports where necessary */
2034 for (uint32_t n = 0; n < limit; ++n) {
2036 const Connection::PortList& pl = c.port_connections (n);
2038 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2040 if (!_outputs.port(n)->connected_to ((*i))) {
2042 if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
2049 _output_connection = &c;
2051 output_connection_configuration_connection = c.ConfigurationChanged.connect
2052 (mem_fun (*this, &IO::output_connection_configuration_changed));
2053 output_connection_connection_connection = c.ConnectionsChanged.connect
2054 (mem_fun (*this, &IO::output_connection_connection_changed));
2057 output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
2063 IO::disable_connecting ()
2065 connecting_legal = false;
2070 IO::enable_connecting ()
2072 connecting_legal = true;
2073 return ConnectingLegal ();
2077 IO::disable_ports ()
2079 ports_legal = false;
2087 return PortsLegal ();
2091 IO::disable_panners (void)
2093 panners_legal = false;
2098 IO::reset_panners ()
2100 panners_legal = true;
2101 return PannersLegal ();
2105 IO::input_connection_connection_changed (int ignored)
2107 use_input_connection (*_input_connection, this);
2111 IO::input_connection_configuration_changed ()
2113 use_input_connection (*_input_connection, this);
2117 IO::output_connection_connection_changed (int ignored)
2119 use_output_connection (*_output_connection, this);
2123 IO::output_connection_configuration_changed ()
2125 use_output_connection (*_output_connection, this);
2129 IO::GainControllable::set_value (float val)
2131 io.set_gain (direct_control_to_gain (val), this);
2135 IO::GainControllable::get_value (void) const
2137 return direct_gain_to_control (io.effective_gain());
2141 IO::setup_peak_meters()
2143 _meter->setup(std::max(_inputs.count(), _outputs.count()));
2147 Update the peak meters.
2149 The meter signal lock is taken to prevent modification of the
2150 Meter signal while updating the meters, taking the meter signal
2151 lock prior to taking the io_lock ensures that all IO will remain
2152 valid while metering.
2157 Glib::Mutex::Lock guard (m_meter_signal_lock);
2159 Meter(); /* EMIT SIGNAL */
2165 // FIXME: Ugly. Meter should manage the lock, if it's necessary
2167 Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2172 IO::clear_automation ()
2174 Glib::Mutex::Lock lm (automation_lock);
2175 _gain_automation_curve.clear ();
2176 _panner->clear_automation ();
2180 IO::set_gain_automation_state (AutoState state)
2182 bool changed = false;
2185 Glib::Mutex::Lock lm (automation_lock);
2187 if (state != _gain_automation_curve.automation_state()) {
2189 last_automation_snapshot = 0;
2190 _gain_automation_curve.set_automation_state (state);
2193 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2199 _session.set_dirty ();
2200 gain_automation_state_changed (); /* EMIT SIGNAL */
2205 IO::set_gain_automation_style (AutoStyle style)
2207 bool changed = false;
2210 Glib::Mutex::Lock lm (automation_lock);
2212 if (style != _gain_automation_curve.automation_style()) {
2214 _gain_automation_curve.set_automation_style (style);
2219 gain_automation_style_changed (); /* EMIT SIGNAL */
2223 IO::inc_gain (gain_t factor, void *src)
2225 if (_desired_gain == 0.0f)
2226 set_gain (0.000001f + (0.000001f * factor), src);
2228 set_gain (_desired_gain + (_desired_gain * factor), src);
2232 IO::set_gain (gain_t val, void *src)
2234 // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2235 if (val>1.99526231f) val=1.99526231f;
2238 Glib::Mutex::Lock dm (declick_lock);
2239 _desired_gain = val;
2242 if (_session.transport_stopped()) {
2243 _effective_gain = val;
2248 _gain_control.Changed (); /* EMIT SIGNAL */
2250 if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2251 _gain_automation_curve.add (_session.transport_frame(), val);
2255 _session.set_dirty();
2259 IO::start_gain_touch ()
2261 _gain_automation_curve.start_touch ();
2265 IO::end_gain_touch ()
2267 _gain_automation_curve.stop_touch ();
2271 IO::start_pan_touch (uint32_t which)
2273 if (which < _panner->size()) {
2274 (*_panner)[which]->automation().start_touch();
2279 IO::end_pan_touch (uint32_t which)
2281 if (which < _panner->size()) {
2282 (*_panner)[which]->automation().stop_touch();
2288 IO::automation_snapshot (nframes_t now)
2290 if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2292 if (gain_automation_recording()) {
2293 _gain_automation_curve.rt_add (now, gain());
2296 _panner->snapshot (now);
2298 last_automation_snapshot = now;
2303 IO::transport_stopped (nframes_t frame)
2305 _gain_automation_curve.reposition_for_rt_add (frame);
2307 if (_gain_automation_curve.automation_state() != Off) {
2309 /* the src=0 condition is a special signal to not propagate
2310 automation gain changes into the mix group when locating.
2313 set_gain (_gain_automation_curve.eval (frame), 0);
2316 _panner->transport_stopped (frame);
2320 IO::find_input_port_hole ()
2322 /* CALLER MUST HOLD IO LOCK */
2326 if (_inputs.empty()) {
2330 for (n = 1; n < UINT_MAX; ++n) {
2331 char buf[jack_port_name_size()];
2332 PortSet::iterator i = _inputs.begin();
2334 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2336 for ( ; i != _inputs.end(); ++i) {
2337 if (i->short_name() == buf) {
2342 if (i == _inputs.end()) {
2350 IO::find_output_port_hole ()
2352 /* CALLER MUST HOLD IO LOCK */
2356 if (_outputs.empty()) {
2360 for (n = 1; n < UINT_MAX; ++n) {
2361 char buf[jack_port_name_size()];
2362 PortSet::iterator i = _outputs.begin();
2364 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2366 for ( ; i != _outputs.end(); ++i) {
2367 if (i->short_name() == buf) {
2372 if (i == _outputs.end()) {
2381 IO::audio_input(uint32_t n) const
2383 return dynamic_cast<AudioPort*>(input(n));
2387 IO::audio_output(uint32_t n) const
2389 return dynamic_cast<AudioPort*>(output(n));
2393 IO::midi_input(uint32_t n) const
2395 return dynamic_cast<MidiPort*>(input(n));
2399 IO::midi_output(uint32_t n) const
2401 return dynamic_cast<MidiPort*>(output(n));
2405 IO::set_phase_invert (bool yn, void *src)
2407 if (_phase_invert != yn) {
2410 // phase_invert_changed (src); /* EMIT SIGNAL */