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>
30 #include <pbd/replace_all.h>
32 #include <ardour/audioengine.h>
33 #include <ardour/io.h>
34 #include <ardour/port.h>
35 #include <ardour/audio_port.h>
36 #include <ardour/midi_port.h>
37 #include <ardour/connection.h>
38 #include <ardour/session.h>
39 #include <ardour/cycle_timer.h>
40 #include <ardour/panner.h>
41 #include <ardour/buffer_set.h>
42 #include <ardour/meter.h>
43 #include <ardour/amp.h>
50 A bug in OS X's cmath that causes isnan() and isinf() to be
51 "undeclared". the following works around that
54 #if defined(__APPLE__) && defined(__MACH__)
55 extern "C" int isnan (double);
56 extern "C" int isinf (double);
59 #define BLOCK_PROCESS_CALLBACK() Glib::Mutex::Lock em (_session.engine().process_lock())
62 using namespace ARDOUR;
65 nframes_t IO::_automation_interval = 0;
67 const string IO::state_node_name = "IO";
68 bool IO::connecting_legal = false;
69 bool IO::ports_legal = false;
70 bool IO::panners_legal = false;
71 sigc::signal<void> IO::Meter;
72 sigc::signal<int> IO::ConnectingLegal;
73 sigc::signal<int> IO::PortsLegal;
74 sigc::signal<int> IO::PannersLegal;
75 sigc::signal<void,ChanCount> IO::MoreChannels;
76 sigc::signal<int> IO::PortsCreated;
78 Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT;
80 /* this is a default mapper of [0 .. 1.0] control values to a gain coefficient.
81 others can be imagined.
84 static gain_t direct_control_to_gain (double fract) {
85 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
86 /* this maxes at +6dB */
87 return pow (2.0,(sqrt(sqrt(sqrt(fract)))*198.0-192.0)/6.0);
90 static double direct_gain_to_control (gain_t gain) {
91 /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
92 if (gain == 0) return 0.0;
94 return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0);
98 /** @param default_type The type of port that will be created by ensure_io
99 * and friends if no type is explicitly requested (to avoid breakage).
101 IO::IO (Session& s, string name,
102 int input_min, int input_max, int output_min, int output_max,
103 DataType default_type)
105 _output_buffers(new BufferSet()),
107 _default_type(default_type),
108 _gain_control (X_("gaincontrol"), *this),
109 _gain_automation_curve (0.0, 2.0, 1.0),
110 _input_minimum (ChanCount::ZERO),
111 _input_maximum (ChanCount::INFINITE),
112 _output_minimum (ChanCount::ZERO),
113 _output_maximum (ChanCount::INFINITE)
115 _panner = new Panner (name, _session);
116 _meter = new PeakMeter (_session);
119 _input_minimum = ChanCount(_default_type, input_min);
121 if (input_max >= 0) {
122 _input_maximum = ChanCount(_default_type, input_max);
124 if (output_min > 0) {
125 _output_minimum = ChanCount(_default_type, output_min);
127 if (output_max >= 0) {
128 _output_maximum = ChanCount(_default_type, output_max);
133 _input_connection = 0;
134 _output_connection = 0;
135 pending_state_node = 0;
136 no_panner_reset = false;
137 _phase_invert = false;
140 apply_gain_automation = false;
142 last_automation_snapshot = 0;
144 _gain_automation_state = Off;
145 _gain_automation_style = Absolute;
148 // IO::Meter is emitted from another thread so the
149 // Meter signal must be protected.
150 Glib::Mutex::Lock guard (m_meter_signal_lock);
151 m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
154 // Connect to our own MoreChannels signal to connect output buffers
155 IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
157 _session.add_controllable (&_gain_control);
160 IO::IO (Session& s, const XMLNode& node, DataType dt)
162 _output_buffers(new BufferSet()),
164 _gain_control (X_("gaincontrol"), *this),
165 _gain_automation_curve (0, 0, 0) // all reset in set_state()
168 _meter = new PeakMeter (_session);
172 no_panner_reset = false;
175 _input_connection = 0;
176 _output_connection = 0;
178 apply_gain_automation = false;
183 // IO::Meter is emitted from another thread so the
184 // Meter signal must be protected.
185 Glib::Mutex::Lock guard (m_meter_signal_lock);
186 m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
189 // Connect to our own MoreChannels signal to connect output buffers
190 IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers));
192 _session.add_controllable (&_gain_control);
197 Glib::Mutex::Lock guard (m_meter_signal_lock);
199 Glib::Mutex::Lock lm (io_lock);
201 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
202 _session.engine().unregister_port (*i);
205 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
206 _session.engine().unregister_port (*i);
209 m_meter_connection.disconnect();
213 delete _output_buffers;
217 IO::silence (nframes_t nframes, nframes_t offset)
219 /* io_lock, not taken: function must be called from Session::process() calltree */
221 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
222 i->get_buffer().silence (nframes, offset);
226 /** Deliver bufs to the IO's Jack outputs.
228 * This function should automatically do whatever it necessary to correctly deliver bufs
229 * to the outputs, eg applying gain or pan or whatever else needs to be done.
232 IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
234 // FIXME: type specific code doesn't actually need to be here, it will go away in time
236 /* ********** AUDIO ********** */
238 // Apply gain if gain automation isn't playing
239 if ( ! apply_gain_automation) {
241 gain_t dg = _gain; // desired gain
244 Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
252 if (dg != _gain || dg != 1.0)
253 Amp::run(bufs, nframes, _gain, dg, _phase_invert);
256 // Use the panner to distribute audio to output port buffers
257 if (_panner && !_panner->empty() && !_panner->bypassed()) {
258 _panner->distribute (bufs, output_buffers(), start_frame, end_frame, nframes, offset);
260 const DataType type = DataType::AUDIO;
262 // Copy any audio 1:1 to outputs
264 BufferSet::iterator o = output_buffers().begin(type);
265 BufferSet::iterator i = bufs.begin(type);
266 BufferSet::iterator prev = i;
268 while (i != bufs.end(type) && o != output_buffers().end (type)) {
269 o->read_from(*i, nframes, offset);
275 /* extra outputs get a copy of the last buffer */
277 while (o != output_buffers().end(type)) {
278 o->read_from(*prev, nframes, offset);
283 /* ********** MIDI ********** */
285 // No MIDI, we're done here
286 if (bufs.count().n_midi() == 0) {
290 const DataType type = DataType::MIDI;
292 // Copy any MIDI 1:1 to outputs
293 assert(bufs.count().n_midi() == output_buffers().count().n_midi());
294 BufferSet::iterator o = output_buffers().begin(type);
295 for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) {
296 o->read_from(*i, nframes, offset);
301 IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset)
303 assert(outs.available() >= n_inputs());
305 outs.set_count(n_inputs());
307 if (outs.count() == ChanCount::ZERO)
310 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
312 BufferSet::iterator o = outs.begin(*t);
313 for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
314 o->read_from(i->get_buffer(), nframes, offset);
321 IO::just_meter_input (nframes_t start_frame, nframes_t end_frame,
322 nframes_t nframes, nframes_t offset)
324 BufferSet& bufs = _session.get_scratch_buffers (n_inputs());
326 collect_input (bufs, nframes, offset);
328 _meter->run(bufs, nframes);
332 IO::drop_input_connection ()
334 _input_connection = 0;
335 input_connection_configuration_connection.disconnect();
336 input_connection_connection_connection.disconnect();
337 _session.set_dirty ();
341 IO::drop_output_connection ()
343 _output_connection = 0;
344 output_connection_configuration_connection.disconnect();
345 output_connection_connection_connection.disconnect();
346 _session.set_dirty ();
350 IO::disconnect_input (Port* our_port, string other_port, void* src)
352 if (other_port.length() == 0 || our_port == 0) {
357 BLOCK_PROCESS_CALLBACK ();
360 Glib::Mutex::Lock lm (io_lock);
362 /* check that our_port is really one of ours */
364 if ( ! _inputs.contains(our_port)) {
368 /* disconnect it from the source */
370 if (_session.engine().disconnect (other_port, our_port->name())) {
371 error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
375 drop_input_connection();
379 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
380 _session.set_dirty ();
386 IO::connect_input (Port* our_port, string other_port, void* src)
388 if (other_port.length() == 0 || our_port == 0) {
393 BLOCK_PROCESS_CALLBACK ();
396 Glib::Mutex::Lock lm (io_lock);
398 /* check that our_port is really one of ours */
400 if ( ! _inputs.contains(our_port) ) {
404 /* connect it to the source */
406 if (_session.engine().connect (other_port, our_port->name())) {
410 drop_input_connection ();
414 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
415 _session.set_dirty ();
420 IO::disconnect_output (Port* our_port, string other_port, void* src)
422 if (other_port.length() == 0 || our_port == 0) {
427 BLOCK_PROCESS_CALLBACK ();
430 Glib::Mutex::Lock lm (io_lock);
432 /* check that our_port is really one of ours */
434 if ( ! _outputs.contains(our_port) ) {
438 /* disconnect it from the destination */
440 if (_session.engine().disconnect (our_port->name(), other_port)) {
441 error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
445 drop_output_connection ();
449 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
450 _session.set_dirty ();
455 IO::connect_output (Port* our_port, string other_port, void* src)
457 if (other_port.length() == 0 || our_port == 0) {
462 BLOCK_PROCESS_CALLBACK ();
466 Glib::Mutex::Lock lm (io_lock);
468 /* check that our_port is really one of ours */
470 if ( ! _outputs.contains(our_port) ) {
474 /* connect it to the destination */
476 if (_session.engine().connect (our_port->name(), other_port)) {
480 drop_output_connection ();
484 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
485 _session.set_dirty ();
490 IO::set_input (Port* other_port, void* src)
492 /* this removes all but one ports, and connects that one port
493 to the specified source.
496 if (_input_minimum.get_total() > 1) {
497 /* sorry, you can't do this */
501 if (other_port == 0) {
502 if (_input_minimum == ChanCount::ZERO) {
503 return ensure_inputs (ChanCount::ZERO, false, true, src);
509 if (ensure_inputs (ChanCount(other_port->type(), 1), true, true, src)) {
513 return connect_input (_inputs.port(0), other_port->name(), src);
517 IO::remove_output_port (Port* port, void* src)
519 IOChange change (NoChange);
522 BLOCK_PROCESS_CALLBACK ();
526 Glib::Mutex::Lock lm (io_lock);
528 if (n_outputs() <= _output_minimum) {
529 /* sorry, you can't do this */
533 if (_outputs.remove(port)) {
534 change = IOChange (change|ConfigurationChanged);
536 if (port->connected()) {
537 change = IOChange (change|ConnectionsChanged);
540 _session.engine().unregister_port (*port);
541 drop_output_connection ();
543 setup_peak_meters ();
549 if (change != NoChange) {
550 output_changed (change, src);
551 _session.set_dirty ();
558 /** Add an output port.
560 * @param destination Name of input port to connect new port to.
561 * @param src Source for emitted ConfigurationChanged signal.
562 * @param type Data type of port. Default value (NIL) will use this IO's default type.
565 IO::add_output_port (string destination, void* src, DataType type)
570 if (type == DataType::NIL)
571 type = _default_type;
574 BLOCK_PROCESS_CALLBACK ();
578 Glib::Mutex::Lock lm (io_lock);
580 if (n_outputs() >= _output_maximum) {
584 /* Create a new output port */
586 // FIXME: naming scheme for differently typed ports?
587 if (_output_maximum.get(type) == 1) {
588 snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
590 snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
593 if ((our_port = _session.engine().register_output_port (type, name)) == 0) {
594 error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
598 _outputs.add (our_port);
599 drop_output_connection ();
600 setup_peak_meters ();
604 MoreChannels (n_outputs()); /* EMIT SIGNAL */
607 if (destination.length()) {
608 if (_session.engine().connect (our_port->name(), destination)) {
613 // pan_changed (src); /* EMIT SIGNAL */
614 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
615 _session.set_dirty ();
621 IO::remove_input_port (Port* port, void* src)
623 IOChange change (NoChange);
626 BLOCK_PROCESS_CALLBACK ();
630 Glib::Mutex::Lock lm (io_lock);
632 if (n_inputs() <= _input_minimum) {
633 /* sorry, you can't do this */
637 if (_inputs.remove(port)) {
638 change = IOChange (change|ConfigurationChanged);
640 if (port->connected()) {
641 change = IOChange (change|ConnectionsChanged);
644 _session.engine().unregister_port (*port);
645 drop_input_connection ();
647 setup_peak_meters ();
653 if (change != NoChange) {
654 input_changed (change, src);
655 _session.set_dirty ();
663 /** Add an input port.
665 * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
666 * @param destination Name of input port to connect new port to.
667 * @param src Source for emitted ConfigurationChanged signal.
670 IO::add_input_port (string source, void* src, DataType type)
675 if (type == DataType::NIL)
676 type = _default_type;
679 BLOCK_PROCESS_CALLBACK ();
682 Glib::Mutex::Lock lm (io_lock);
684 if (n_inputs() >= _input_maximum) {
688 /* Create a new input port */
690 // FIXME: naming scheme for differently typed ports?
691 if (_input_maximum.get(type) == 1) {
692 snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
694 snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
697 if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
698 error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
702 _inputs.add (our_port);
703 drop_input_connection ();
704 setup_peak_meters ();
708 MoreChannels (n_inputs()); /* EMIT SIGNAL */
711 if (source.length()) {
713 if (_session.engine().connect (source, our_port->name())) {
718 // pan_changed (src); /* EMIT SIGNAL */
719 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
720 _session.set_dirty ();
726 IO::disconnect_inputs (void* src)
729 BLOCK_PROCESS_CALLBACK ();
732 Glib::Mutex::Lock lm (io_lock);
734 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
735 _session.engine().disconnect (*i);
738 drop_input_connection ();
742 input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
748 IO::disconnect_outputs (void* src)
751 BLOCK_PROCESS_CALLBACK ();
754 Glib::Mutex::Lock lm (io_lock);
756 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
757 _session.engine().disconnect (*i);
760 drop_output_connection ();
764 output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
765 _session.set_dirty ();
771 IO::ensure_inputs_locked (ChanCount count, bool clear, void* src)
773 Port* input_port = 0;
774 bool changed = false;
777 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
779 const size_t n = count.get(*t);
781 /* remove unused ports */
782 for (size_t i = n_inputs().get(*t); i > n; --i) {
783 input_port = _inputs.port(*t, i-1);
786 _inputs.remove(input_port);
787 _session.engine().unregister_port (*input_port);
792 /* create any necessary new ports */
793 while (n_inputs().get(*t) < n) {
797 if (_input_maximum.get(*t) == 1) {
798 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
800 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
805 if ((input_port = _session.engine().register_input_port (*t, buf)) == 0) {
806 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
811 catch (AudioEngine::PortRegistrationFailure& err) {
812 setup_peak_meters ();
815 throw AudioEngine::PortRegistrationFailure();
818 _inputs.add (input_port);
824 drop_input_connection ();
825 setup_peak_meters ();
827 MoreChannels (n_inputs()); /* EMIT SIGNAL */
828 _session.set_dirty ();
832 /* disconnect all existing ports so that we get a fresh start */
833 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
834 _session.engine().disconnect (*i);
841 /** Attach output_buffers to port buffers.
843 * Connected to IO's own MoreChannels signal.
846 IO::attach_buffers(ChanCount ignored)
848 _output_buffers->attach_buffers(_outputs);
852 IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src)
854 bool in_changed = false;
855 bool out_changed = false;
856 bool need_pan_reset = false;
858 in = min (_input_maximum, in);
860 out = min (_output_maximum, out);
862 if (in == n_inputs() && out == n_outputs() && !clear) {
867 BLOCK_PROCESS_CALLBACK ();
868 Glib::Mutex::Lock lm (io_lock);
872 if (n_outputs() != out) {
873 need_pan_reset = true;
876 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
878 const size_t nin = in.get(*t);
879 const size_t nout = out.get(*t);
881 Port* output_port = 0;
882 Port* input_port = 0;
884 /* remove unused output ports */
885 for (size_t i = n_outputs().get(*t); i > nout; --i) {
886 output_port = _outputs.port(*t, i-1);
889 _outputs.remove(output_port);
890 _session.engine().unregister_port (*output_port);
895 /* remove unused input ports */
896 for (size_t i = n_inputs().get(*t); i > nin; --i) {
897 input_port = _inputs.port(*t, i-1);
900 _inputs.remove(input_port);
901 _session.engine().unregister_port (*input_port);
906 /* create any necessary new input ports */
908 while (n_inputs().get(*t) < nin) {
912 /* Create a new input port */
914 if (_input_maximum.get(*t) == 1) {
915 snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
917 snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
921 if ((port = _session.engine().register_input_port (*t, buf)) == 0) {
922 error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
927 catch (AudioEngine::PortRegistrationFailure& err) {
928 setup_peak_meters ();
931 throw AudioEngine::PortRegistrationFailure();
938 /* create any necessary new output ports */
940 while (n_outputs().get(*t) < nout) {
944 /* Create a new output port */
946 if (_output_maximum.get(*t) == 1) {
947 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
949 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
953 if ((port = _session.engine().register_output_port (*t, buf)) == 0) {
954 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
959 catch (AudioEngine::PortRegistrationFailure& err) {
960 setup_peak_meters ();
963 throw AudioEngine::PortRegistrationFailure ();
973 /* disconnect all existing ports so that we get a fresh start */
975 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
976 _session.engine().disconnect (*i);
979 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
980 _session.engine().disconnect (*i);
984 if (in_changed || out_changed) {
985 setup_peak_meters ();
991 drop_output_connection ();
992 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
996 drop_input_connection ();
997 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1000 if (in_changed || out_changed) {
1001 MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
1002 _session.set_dirty ();
1009 IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src)
1011 bool changed = false;
1013 count = min (_input_maximum, count);
1015 if (count == n_inputs() && !clear) {
1020 BLOCK_PROCESS_CALLBACK ();
1021 Glib::Mutex::Lock im (io_lock);
1022 changed = ensure_inputs_locked (count, clear, src);
1024 changed = ensure_inputs_locked (count, clear, src);
1028 input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1029 _session.set_dirty ();
1035 IO::ensure_outputs_locked (ChanCount count, bool clear, void* src)
1037 Port* output_port = 0;
1038 bool changed = false;
1039 bool need_pan_reset = false;
1041 if (n_outputs() != count) {
1042 need_pan_reset = true;
1045 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1047 const size_t n = count.get(*t);
1049 /* remove unused ports */
1050 for (size_t i = n_outputs().get(*t); i > n; --i) {
1051 output_port = _outputs.port(*t, i-1);
1053 assert(output_port);
1054 _outputs.remove(output_port);
1055 _session.engine().unregister_port (*output_port);
1060 /* create any necessary new ports */
1061 while (n_outputs().get(*t) < n) {
1065 if (_output_maximum.get(*t) == 1) {
1066 snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
1068 snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
1071 if ((output_port = _session.engine().register_output_port (*t, buf)) == 0) {
1072 error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
1076 _outputs.add (output_port);
1078 setup_peak_meters ();
1080 if (need_pan_reset) {
1087 drop_output_connection ();
1088 MoreChannels (n_outputs()); /* EMIT SIGNAL */
1089 _session.set_dirty ();
1093 /* disconnect all existing ports so that we get a fresh start */
1094 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1095 _session.engine().disconnect (*i);
1103 IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
1105 bool changed = false;
1107 if (_output_maximum < ChanCount::INFINITE) {
1108 count = min (_output_maximum, count);
1109 if (count == n_outputs() && !clear) {
1114 /* XXX caller should hold io_lock, but generally doesn't */
1117 BLOCK_PROCESS_CALLBACK ();
1118 Glib::Mutex::Lock im (io_lock);
1119 changed = ensure_outputs_locked (count, clear, src);
1121 changed = ensure_outputs_locked (count, clear, src);
1125 output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
1132 IO::effective_gain () const
1134 if (gain_automation_playback()) {
1135 return _effective_gain;
1137 return _desired_gain;
1144 if (panners_legal) {
1145 if (!no_panner_reset) {
1146 _panner->reset (n_outputs().n_audio(), pans_required());
1149 panner_legal_c.disconnect ();
1150 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1155 IO::panners_became_legal ()
1157 _panner->reset (n_outputs().n_audio(), pans_required());
1158 _panner->load (); // automation
1159 panner_legal_c.disconnect ();
1164 IO::defer_pan_reset ()
1166 no_panner_reset = true;
1170 IO::allow_pan_reset ()
1172 no_panner_reset = false;
1178 IO::get_state (void)
1180 return state (true);
1184 IO::state (bool full_state)
1186 XMLNode* node = new XMLNode (state_node_name);
1189 bool need_ins = true;
1190 bool need_outs = true;
1191 LocaleGuard lg (X_("POSIX"));
1192 Glib::Mutex::Lock lm (io_lock);
1194 node->add_property("name", _name);
1195 id().print (buf, sizeof (buf));
1196 node->add_property("id", buf);
1200 if (_input_connection) {
1201 node->add_property ("input-connection", _input_connection->name());
1205 if (_output_connection) {
1206 node->add_property ("output-connection", _output_connection->name());
1211 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1213 const char **connections = i->get_connections();
1215 if (connections && connections[0]) {
1218 for (int n = 0; connections && connections[n]; ++n) {
1223 /* if its a connection to our own port,
1224 return only the port name, not the
1225 whole thing. this allows connections
1226 to be re-established even when our
1227 client name is different.
1230 str += _session.engine().make_port_name_relative (connections[n]);
1242 node->add_property ("inputs", str);
1248 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1250 const char **connections = i->get_connections();
1252 if (connections && connections[0]) {
1256 for (int n = 0; connections[n]; ++n) {
1261 str += _session.engine().make_port_name_relative (connections[n]);
1273 node->add_property ("outputs", str);
1276 node->add_child_nocopy (_panner->state (full_state));
1277 node->add_child_nocopy (_gain_control.get_state ());
1279 snprintf (buf, sizeof(buf), "%2.12f", gain());
1280 node->add_property ("gain", buf);
1282 // FIXME: this is NOT sufficient!
1283 const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
1284 const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
1285 const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
1286 const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
1288 snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
1290 node->add_property ("iolimits", buf);
1296 XMLNode* autonode = new XMLNode (X_("Automation"));
1297 autonode->add_child_nocopy (get_automation_state());
1298 node->add_child_nocopy (*autonode);
1300 snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state());
1302 /* never store anything except Off for automation state in a template */
1303 snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off);
1310 IO::set_state (const XMLNode& node)
1312 const XMLProperty* prop;
1313 XMLNodeConstIterator iter;
1314 LocaleGuard lg (X_("POSIX"));
1316 /* force use of non-localized representation of decimal point,
1317 since we use it a lot in XML files and so forth.
1320 if (node.name() != state_node_name) {
1321 error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
1325 if ((prop = node.property ("name")) != 0) {
1326 _name = prop->value();
1327 /* used to set panner name with this, but no more */
1330 if ((prop = node.property ("id")) != 0) {
1331 _id = prop->value ();
1336 size_t out_min = -1;
1337 size_t out_max = -1;
1339 if ((prop = node.property ("iolimits")) != 0) {
1340 sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
1341 &in_min, &in_max, &out_min, &out_max);
1342 _input_minimum = ChanCount(_default_type, in_min);
1343 _input_maximum = ChanCount(_default_type, in_max);
1344 _output_minimum = ChanCount(_default_type, out_min);
1345 _output_maximum = ChanCount(_default_type, out_max);
1348 if ((prop = node.property ("gain")) != 0) {
1349 set_gain (atof (prop->value().c_str()), this);
1350 _gain = _desired_gain;
1353 if ((prop = node.property ("automation-state")) != 0 || (prop = node.property ("automation-style")) != 0) {
1354 /* old school automation handling */
1357 for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
1359 if ((*iter)->name() == "Panner") {
1361 _panner = new Panner (_name, _session);
1363 _panner->set_state (**iter);
1366 if ((*iter)->name() == X_("Automation")) {
1368 set_automation_state (*(*iter)->children().front());
1371 if ((*iter)->name() == X_("controllable")) {
1372 if ((prop = (*iter)->property("name")) != 0 && prop->value() == "gaincontrol") {
1373 _gain_control.set_state (**iter);
1380 if (create_ports (node)) {
1386 port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
1389 if (panners_legal) {
1392 panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
1395 if (connecting_legal) {
1397 if (make_connections (node)) {
1403 connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
1406 if (!ports_legal || !connecting_legal) {
1407 pending_state_node = new XMLNode (node);
1410 last_automation_snapshot = 0;
1416 IO::set_automation_state (const XMLNode& node)
1418 return _gain_automation_curve.set_state (node);
1422 IO::get_automation_state ()
1424 return (_gain_automation_curve.get_state ());
1428 IO::load_automation (string path)
1433 uint32_t linecnt = 0;
1435 LocaleGuard lg (X_("POSIX"));
1437 fullpath = _session.automation_dir();
1440 in.open (fullpath.c_str());
1443 fullpath = _session.automation_dir();
1444 fullpath += _session.snap_name();
1448 in.open (fullpath.c_str());
1451 error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
1456 clear_automation ();
1458 while (in.getline (line, sizeof(line), '\n')) {
1463 if (++linecnt == 1) {
1464 if (memcmp (line, "version", 7) == 0) {
1465 if (sscanf (line, "version %f", &version) != 1) {
1466 error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
1470 error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
1477 if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
1478 warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
1484 _gain_automation_curve.fast_simple_add (when, value);
1494 /* older (pre-1.0) versions of ardour used this */
1498 warning << _("dubious automation event found (and ignored)") << endmsg;
1506 IO::connecting_became_legal ()
1510 if (pending_state_node == 0) {
1511 fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
1516 connection_legal_c.disconnect ();
1518 ret = make_connections (*pending_state_node);
1521 delete pending_state_node;
1522 pending_state_node = 0;
1528 IO::ports_became_legal ()
1532 if (pending_state_node == 0) {
1533 fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
1538 port_legal_c.disconnect ();
1540 ret = create_ports (*pending_state_node);
1542 if (connecting_legal) {
1543 delete pending_state_node;
1544 pending_state_node = 0;
1551 IO::create_ports (const XMLNode& node)
1553 const XMLProperty* prop;
1555 int num_outputs = 0;
1557 if ((prop = node.property ("input-connection")) != 0) {
1559 Connection* c = _session.connection_by_name (prop->value());
1562 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1564 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1565 error << _("No input connections available as a replacement")
1569 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1574 num_inputs = c->nports();
1576 } else if ((prop = node.property ("inputs")) != 0) {
1578 num_inputs = count (prop->value().begin(), prop->value().end(), '{');
1581 if ((prop = node.property ("output-connection")) != 0) {
1582 Connection* c = _session.connection_by_name (prop->value());
1585 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1587 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1588 error << _("No output connections available as a replacement")
1592 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1597 num_outputs = c->nports ();
1599 } else if ((prop = node.property ("outputs")) != 0) {
1600 num_outputs = count (prop->value().begin(), prop->value().end(), '{');
1603 no_panner_reset = true;
1605 // FIXME: audio-only
1606 if (ensure_io (ChanCount(DataType::AUDIO, num_inputs), ChanCount(DataType::AUDIO, num_outputs), true, this)) {
1607 error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
1611 no_panner_reset = false;
1613 set_deferred_state ();
1621 IO::make_connections (const XMLNode& node)
1623 const XMLProperty* prop;
1625 if ((prop = node.property ("input-connection")) != 0) {
1626 Connection* c = _session.connection_by_name (prop->value());
1629 error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
1631 if ((c = _session.connection_by_name (_("in 1"))) == 0) {
1632 error << _("No input connections available as a replacement")
1636 info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
1641 use_input_connection (*c, this);
1643 } else if ((prop = node.property ("inputs")) != 0) {
1644 if (set_inputs (prop->value())) {
1645 error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
1650 if ((prop = node.property ("output-connection")) != 0) {
1651 Connection* c = _session.connection_by_name (prop->value());
1654 error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
1656 if ((c = _session.connection_by_name (_("out 1"))) == 0) {
1657 error << _("No output connections available as a replacement")
1661 info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value())
1666 use_output_connection (*c, this);
1668 } else if ((prop = node.property ("outputs")) != 0) {
1669 if (set_outputs (prop->value())) {
1670 error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
1679 IO::set_inputs (const string& str)
1681 vector<string> ports;
1686 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1690 // FIXME: audio-only
1691 if (ensure_inputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
1695 string::size_type start, end, ostart;
1702 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1705 if ((end = str.find_first_of ('}', start)) == string::npos) {
1706 error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
1710 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1711 error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
1717 for (int x = 0; x < n; ++x) {
1718 connect_input (input (i), ports[x], this);
1730 IO::set_outputs (const string& str)
1732 vector<string> ports;
1737 if ((nports = count (str.begin(), str.end(), '{')) == 0) {
1741 // FIXME: audio-only
1742 if (ensure_outputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
1746 string::size_type start, end, ostart;
1753 while ((start = str.find_first_of ('{', ostart)) != string::npos) {
1756 if ((end = str.find_first_of ('}', start)) == string::npos) {
1757 error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
1761 if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
1762 error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
1768 for (int x = 0; x < n; ++x) {
1769 connect_output (output (i), ports[x], this);
1781 IO::parse_io_string (const string& str, vector<string>& ports)
1783 string::size_type pos, opos;
1785 if (str.length() == 0) {
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::parse_gain_string (const string& str, vector<string>& ports)
1809 string::size_type pos, opos;
1815 while ((pos = str.find_first_of (',', opos)) != string::npos) {
1816 ports.push_back (str.substr (opos, pos - opos));
1820 if (opos < str.length()) {
1821 ports.push_back (str.substr(opos));
1824 return ports.size();
1828 IO::set_name (string name, void* src)
1830 if (name == _name) {
1834 /* replace all colons in the name. i wish we didn't have to do this */
1836 if (replace_all (name, ":", "-")) {
1837 warning << _("you cannot use colons to name objects with I/O connections") << endmsg;
1840 for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1841 string current_name = i->short_name();
1842 current_name.replace (current_name.find (_name), _name.length(), name);
1843 i->set_name (current_name);
1846 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1847 string current_name = i->short_name();
1848 current_name.replace (current_name.find (_name), _name.length(), name);
1849 i->set_name (current_name);
1853 name_changed (src); /* EMIT SIGNAL */
1859 IO::set_input_minimum (ChanCount n)
1865 IO::set_input_maximum (ChanCount n)
1871 IO::set_output_minimum (ChanCount n)
1873 _output_minimum = n;
1877 IO::set_output_maximum (ChanCount n)
1879 _output_maximum = n;
1883 IO::set_port_latency (nframes_t nframes)
1885 Glib::Mutex::Lock lm (io_lock);
1887 for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1888 i->set_latency (nframes);
1893 IO::output_latency () const
1895 nframes_t max_latency;
1900 /* io lock not taken - must be protected by other means */
1902 for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
1903 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1904 max_latency = latency;
1912 IO::input_latency () const
1914 nframes_t max_latency;
1919 /* io lock not taken - must be protected by other means */
1921 for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
1922 if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
1923 max_latency = latency;
1931 IO::use_input_connection (Connection& c, void* src)
1936 BLOCK_PROCESS_CALLBACK ();
1937 Glib::Mutex::Lock lm2 (io_lock);
1941 drop_input_connection ();
1943 // FIXME connections only work for audio-only
1944 if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
1948 /* first pass: check the current state to see what's correctly
1949 connected, and drop anything that we don't want.
1952 for (uint32_t n = 0; n < limit; ++n) {
1953 const Connection::PortList& pl = c.port_connections (n);
1955 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1957 if (!_inputs.port(n)->connected_to ((*i))) {
1959 /* clear any existing connections */
1961 _session.engine().disconnect (*_inputs.port(n));
1963 } else if (_inputs.port(n)->connected() > 1) {
1965 /* OK, it is connected to the port we want,
1966 but its also connected to other ports.
1967 Change that situation.
1970 /* XXX could be optimized to not drop
1974 _session.engine().disconnect (*_inputs.port(n));
1980 /* second pass: connect all requested ports where necessary */
1982 for (uint32_t n = 0; n < limit; ++n) {
1983 const Connection::PortList& pl = c.port_connections (n);
1985 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
1987 if (!_inputs.port(n)->connected_to ((*i))) {
1989 if (_session.engine().connect (*i, _inputs.port(n)->name())) {
1997 _input_connection = &c;
1999 input_connection_configuration_connection = c.ConfigurationChanged.connect
2000 (mem_fun (*this, &IO::input_connection_configuration_changed));
2001 input_connection_connection_connection = c.ConnectionsChanged.connect
2002 (mem_fun (*this, &IO::input_connection_connection_changed));
2005 input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
2010 IO::use_output_connection (Connection& c, void* src)
2015 BLOCK_PROCESS_CALLBACK ();
2016 Glib::Mutex::Lock lm2 (io_lock);
2020 drop_output_connection ();
2022 // FIXME: audio-only
2023 if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
2027 /* first pass: check the current state to see what's correctly
2028 connected, and drop anything that we don't want.
2031 for (uint32_t n = 0; n < limit; ++n) {
2033 const Connection::PortList& pl = c.port_connections (n);
2035 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2037 if (!_outputs.port(n)->connected_to ((*i))) {
2039 /* clear any existing connections */
2041 _session.engine().disconnect (*_outputs.port(n));
2043 } else if (_outputs.port(n)->connected() > 1) {
2045 /* OK, it is connected to the port we want,
2046 but its also connected to other ports.
2047 Change that situation.
2050 /* XXX could be optimized to not drop
2054 _session.engine().disconnect (*_outputs.port(n));
2059 /* second pass: connect all requested ports where necessary */
2061 for (uint32_t n = 0; n < limit; ++n) {
2063 const Connection::PortList& pl = c.port_connections (n);
2065 for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
2067 if (!_outputs.port(n)->connected_to ((*i))) {
2069 if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
2076 _output_connection = &c;
2078 output_connection_configuration_connection = c.ConfigurationChanged.connect
2079 (mem_fun (*this, &IO::output_connection_configuration_changed));
2080 output_connection_connection_connection = c.ConnectionsChanged.connect
2081 (mem_fun (*this, &IO::output_connection_connection_changed));
2084 output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
2090 IO::disable_connecting ()
2092 connecting_legal = false;
2097 IO::enable_connecting ()
2099 connecting_legal = true;
2100 return ConnectingLegal ();
2104 IO::disable_ports ()
2106 ports_legal = false;
2114 return PortsLegal ();
2118 IO::disable_panners (void)
2120 panners_legal = false;
2125 IO::reset_panners ()
2127 panners_legal = true;
2128 return PannersLegal ();
2132 IO::input_connection_connection_changed (int ignored)
2134 use_input_connection (*_input_connection, this);
2138 IO::input_connection_configuration_changed ()
2140 use_input_connection (*_input_connection, this);
2144 IO::output_connection_connection_changed (int ignored)
2146 use_output_connection (*_output_connection, this);
2150 IO::output_connection_configuration_changed ()
2152 use_output_connection (*_output_connection, this);
2156 IO::GainControllable::set_value (float val)
2158 io.set_gain (direct_control_to_gain (val), this);
2162 IO::GainControllable::get_value (void) const
2164 return direct_gain_to_control (io.effective_gain());
2168 IO::setup_peak_meters()
2170 _meter->setup(std::max(_inputs.count(), _outputs.count()));
2174 Update the peak meters.
2176 The meter signal lock is taken to prevent modification of the
2177 Meter signal while updating the meters, taking the meter signal
2178 lock prior to taking the io_lock ensures that all IO will remain
2179 valid while metering.
2184 Glib::Mutex::Lock guard (m_meter_signal_lock);
2186 Meter(); /* EMIT SIGNAL */
2192 // FIXME: Ugly. Meter should manage the lock, if it's necessary
2194 Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
2199 IO::clear_automation ()
2201 Glib::Mutex::Lock lm (automation_lock);
2202 _gain_automation_curve.clear ();
2203 _panner->clear_automation ();
2207 IO::set_gain_automation_state (AutoState state)
2209 bool changed = false;
2212 Glib::Mutex::Lock lm (automation_lock);
2214 if (state != _gain_automation_curve.automation_state()) {
2216 last_automation_snapshot = 0;
2217 _gain_automation_curve.set_automation_state (state);
2220 set_gain (_gain_automation_curve.eval (_session.transport_frame()), this);
2226 _session.set_dirty ();
2227 gain_automation_state_changed (); /* EMIT SIGNAL */
2232 IO::set_gain_automation_style (AutoStyle style)
2234 bool changed = false;
2237 Glib::Mutex::Lock lm (automation_lock);
2239 if (style != _gain_automation_curve.automation_style()) {
2241 _gain_automation_curve.set_automation_style (style);
2246 gain_automation_style_changed (); /* EMIT SIGNAL */
2250 IO::inc_gain (gain_t factor, void *src)
2252 if (_desired_gain == 0.0f)
2253 set_gain (0.000001f + (0.000001f * factor), src);
2255 set_gain (_desired_gain + (_desired_gain * factor), src);
2259 IO::set_gain (gain_t val, void *src)
2261 // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
2262 if (val>1.99526231f) val=1.99526231f;
2265 Glib::Mutex::Lock dm (declick_lock);
2266 _desired_gain = val;
2269 if (_session.transport_stopped()) {
2270 _effective_gain = val;
2275 _gain_control.Changed (); /* EMIT SIGNAL */
2277 if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) {
2278 _gain_automation_curve.add (_session.transport_frame(), val);
2282 _session.set_dirty();
2286 IO::start_gain_touch ()
2288 _gain_automation_curve.start_touch ();
2292 IO::end_gain_touch ()
2294 _gain_automation_curve.stop_touch ();
2298 IO::start_pan_touch (uint32_t which)
2300 if (which < _panner->size()) {
2301 (*_panner)[which]->automation().start_touch();
2306 IO::end_pan_touch (uint32_t which)
2308 if (which < _panner->size()) {
2309 (*_panner)[which]->automation().stop_touch();
2315 IO::automation_snapshot (nframes_t now)
2317 if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
2319 if (gain_automation_recording()) {
2320 _gain_automation_curve.rt_add (now, gain());
2323 _panner->snapshot (now);
2325 last_automation_snapshot = now;
2330 IO::transport_stopped (nframes_t frame)
2332 _gain_automation_curve.reposition_for_rt_add (frame);
2334 if (_gain_automation_curve.automation_state() != Off) {
2336 /* the src=0 condition is a special signal to not propagate
2337 automation gain changes into the mix group when locating.
2340 set_gain (_gain_automation_curve.eval (frame), 0);
2343 _panner->transport_stopped (frame);
2347 IO::find_input_port_hole ()
2349 /* CALLER MUST HOLD IO LOCK */
2353 if (_inputs.empty()) {
2357 for (n = 1; n < UINT_MAX; ++n) {
2358 char buf[jack_port_name_size()];
2359 PortSet::iterator i = _inputs.begin();
2361 snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
2363 for ( ; i != _inputs.end(); ++i) {
2364 if (i->short_name() == buf) {
2369 if (i == _inputs.end()) {
2377 IO::find_output_port_hole ()
2379 /* CALLER MUST HOLD IO LOCK */
2383 if (_outputs.empty()) {
2387 for (n = 1; n < UINT_MAX; ++n) {
2388 char buf[jack_port_name_size()];
2389 PortSet::iterator i = _outputs.begin();
2391 snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
2393 for ( ; i != _outputs.end(); ++i) {
2394 if (i->short_name() == buf) {
2399 if (i == _outputs.end()) {
2408 IO::audio_input(uint32_t n) const
2410 return dynamic_cast<AudioPort*>(input(n));
2414 IO::audio_output(uint32_t n) const
2416 return dynamic_cast<AudioPort*>(output(n));
2420 IO::midi_input(uint32_t n) const
2422 return dynamic_cast<MidiPort*>(input(n));
2426 IO::midi_output(uint32_t n) const
2428 return dynamic_cast<MidiPort*>(output(n));
2432 IO::set_phase_invert (bool yn, void *src)
2434 if (_phase_invert != yn) {
2437 // phase_invert_changed (src); /* EMIT SIGNAL */