X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fio.cc;h=0c1ba32c74ab39a057fe0a97ac66d16e111ad7d5;hb=4d66204f4eb96ca802e9b301293ff4bd922717d0;hp=60e7ec3f4283aa5f4bad4339fb963042e675f4a6;hpb=ef6b25432d9c46d71b08c0f7d5f2686df428c4e8;p=ardour.git diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 60e7ec3f42..0c1ba32c74 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -27,13 +27,14 @@ #include #include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -55,13 +56,12 @@ extern "C" int isnan (double); extern "C" int isinf (double); #endif +#define BLOCK_PROCESS_CALLBACK() Glib::Mutex::Lock em (_session.engine().process_lock()) using namespace std; using namespace ARDOUR; using namespace PBD; -nframes_t IO::_automation_interval = 0; - const string IO::state_node_name = "IO"; bool IO::connecting_legal = false; bool IO::ports_legal = false; @@ -79,6 +79,7 @@ Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT; others can be imagined. */ +#if 0 static gain_t direct_control_to_gain (double fract) { /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */ /* this maxes at +6dB */ @@ -91,20 +92,17 @@ static double direct_gain_to_control (gain_t gain) { return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0); } - +#endif /** @param default_type The type of port that will be created by ensure_io * and friends if no type is explicitly requested (to avoid breakage). */ -IO::IO (Session& s, string name, +IO::IO (Session& s, const string& name, int input_min, int input_max, int output_min, int output_max, DataType default_type) - : _session (s), - _output_buffers(new BufferSet()), - _name (name), - _default_type(default_type), - _gain_control (X_("gaincontrol"), *this), - _gain_automation_curve (0.0, 2.0, 1.0), + : Automatable (s, name), + _output_buffers (new BufferSet()), + _default_type (default_type), _input_minimum (ChanCount::ZERO), _input_maximum (ChanCount::INFINITE), _output_minimum (ChanCount::ZERO), @@ -128,20 +126,21 @@ IO::IO (Session& s, string name, _gain = 1.0; _desired_gain = 1.0; - _input_connection = 0; - _output_connection = 0; pending_state_node = 0; no_panner_reset = false; _phase_invert = false; deferred_state = 0; - apply_gain_automation = false; - - last_automation_snapshot = 0; + boost::shared_ptr gl( + new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0)); - _gain_automation_state = Off; - _gain_automation_style = Absolute; + _gain_control = boost::shared_ptr( + new GainControl(X_("gaincontrol"), *this, gl)); + add_control(_gain_control); + + apply_gain_automation = false; + { // IO::Meter is emitted from another thread so the // Meter signal must be protected. @@ -152,17 +151,16 @@ IO::IO (Session& s, string name, // Connect to our own MoreChannels signal to connect output buffers IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); - _session.add_controllable (&_gain_control); + _session.add_controllable (_gain_control); + + create_bundles (); } IO::IO (Session& s, const XMLNode& node, DataType dt) - : _session (s), - _output_buffers(new BufferSet()), - _default_type (dt), - _gain_control (X_("gaincontrol"), *this), - _gain_automation_curve (0, 0, 0) // all reset in set_state() + : Automatable (s, "unnamed io"), + _output_buffers (new BufferSet()), + _default_type (dt) { - // FIXME: hack _meter = new PeakMeter (_session); _panner = 0; @@ -170,10 +168,16 @@ IO::IO (Session& s, const XMLNode& node, DataType dt) no_panner_reset = false; _desired_gain = 1.0; _gain = 1.0; - _input_connection = 0; - _output_connection = 0; apply_gain_automation = false; + + boost::shared_ptr gl( + new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0)); + + _gain_control = boost::shared_ptr( + new GainControl(X_("gaincontrol"), *this, gl)); + + add_control(_gain_control); set_state (node); @@ -187,7 +191,9 @@ IO::IO (Session& s, const XMLNode& node, DataType dt) // Connect to our own MoreChannels signal to connect output buffers IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); - _session.add_controllable (&_gain_control); + _session.add_controllable (_gain_control); + + create_bundles (); } IO::~IO () @@ -247,35 +253,48 @@ IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, } - Amp::run(bufs, nframes, _gain, dg, _phase_invert); + if (dg != _gain || dg != 1.0) + Amp::run_in_place(bufs, nframes, _gain, dg, _phase_invert); } // Use the panner to distribute audio to output port buffers if (_panner && !_panner->empty() && !_panner->bypassed()) { - _panner->distribute(bufs, output_buffers(), start_frame, end_frame, nframes, offset); + _panner->distribute (bufs, output_buffers(), start_frame, end_frame, nframes, offset); } else { const DataType type = DataType::AUDIO; - + // Copy any audio 1:1 to outputs - assert(bufs.count().get(DataType::AUDIO) == output_buffers().count().get(DataType::AUDIO)); + BufferSet::iterator o = output_buffers().begin(type); - for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) { + BufferSet::iterator i = bufs.begin(type); + BufferSet::iterator prev = i; + + while (i != bufs.end(type) && o != output_buffers().end (type)) { o->read_from(*i, nframes, offset); + prev = i; + ++i; + ++o; } - } + /* extra outputs get a copy of the last buffer */ + + while (o != output_buffers().end(type)) { + o->read_from(*prev, nframes, offset); + ++o; + } + } /* ********** MIDI ********** */ // No MIDI, we're done here - if (bufs.count().get(DataType::MIDI) == 0) { + if (bufs.count().n_midi() == 0) { return; } const DataType type = DataType::MIDI; // Copy any MIDI 1:1 to outputs - assert(bufs.count().get(DataType::MIDI) == output_buffers().count().get(DataType::MIDI)); + assert(bufs.count().n_midi() == output_buffers().count().n_midi()); BufferSet::iterator o = output_buffers().begin(type); for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) { o->read_from(*i, nframes, offset); @@ -310,24 +329,24 @@ IO::just_meter_input (nframes_t start_frame, nframes_t end_frame, collect_input (bufs, nframes, offset); - _meter->run(bufs, nframes); + _meter->run(bufs, start_frame, end_frame, nframes, offset); } void -IO::drop_input_connection () +IO::drop_input_bundle () { - _input_connection = 0; - input_connection_configuration_connection.disconnect(); - input_connection_connection_connection.disconnect(); + _input_bundle.reset (); + input_bundle_configuration_connection.disconnect(); + input_bundle_connection_connection.disconnect(); _session.set_dirty (); } void -IO::drop_output_connection () +IO::drop_output_bundle () { - _output_connection = 0; - output_connection_configuration_connection.disconnect(); - output_connection_connection_connection.disconnect(); + _output_bundle.reset (); + output_bundle_configuration_connection.disconnect(); + output_bundle_connection_connection.disconnect(); _session.set_dirty (); } @@ -339,7 +358,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -357,7 +376,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src) return -1; } - drop_input_connection(); + drop_input_bundle (); } } @@ -375,7 +394,7 @@ IO::connect_input (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -392,7 +411,7 @@ IO::connect_input (Port* our_port, string other_port, void* src) return -1; } - drop_input_connection (); + drop_input_bundle (); } } @@ -409,7 +428,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -427,7 +446,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src) return -1; } - drop_output_connection (); + drop_output_bundle (); } } @@ -444,7 +463,8 @@ IO::connect_output (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -461,7 +481,7 @@ IO::connect_output (Port* our_port, string other_port, void* src) return -1; } - drop_output_connection (); + drop_output_bundle (); } } @@ -477,7 +497,7 @@ IO::set_input (Port* other_port, void* src) to the specified source. */ - if (_input_minimum.get_total() > 1) { + if (_input_minimum.n_total() > 1) { /* sorry, you can't do this */ return -1; } @@ -503,7 +523,8 @@ IO::remove_output_port (Port* port, void* src) IOChange change (NoChange); { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -521,7 +542,7 @@ IO::remove_output_port (Port* port, void* src) } _session.engine().unregister_port (*port); - drop_output_connection (); + drop_output_bundle (); setup_peak_meters (); reset_panner (); @@ -529,6 +550,10 @@ IO::remove_output_port (Port* port, void* src) } } + if (change == ConnectionsChanged) { + setup_bundles (); + } + if (change != NoChange) { output_changed (change, src); _session.set_dirty (); @@ -554,7 +579,8 @@ IO::add_output_port (string destination, void* src, DataType type) type = _default_type; { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -578,7 +604,7 @@ IO::add_output_port (string destination, void* src, DataType type) } _outputs.add (our_port); - drop_output_connection (); + drop_output_bundle (); setup_peak_meters (); reset_panner (); } @@ -594,6 +620,7 @@ IO::add_output_port (string destination, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles (); _session.set_dirty (); return 0; @@ -605,7 +632,8 @@ IO::remove_input_port (Port* port, void* src) IOChange change (NoChange); { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -623,7 +651,7 @@ IO::remove_input_port (Port* port, void* src) } _session.engine().unregister_port (*port); - drop_input_connection (); + drop_input_bundle (); setup_peak_meters (); reset_panner (); @@ -631,6 +659,10 @@ IO::remove_input_port (Port* port, void* src) } } + if (change == ConfigurationChanged) { + setup_bundles (); + } + if (change != NoChange) { input_changed (change, src); _session.set_dirty (); @@ -657,7 +689,7 @@ IO::add_input_port (string source, void* src, DataType type) type = _default_type; { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -674,14 +706,14 @@ IO::add_input_port (string source, void* src, DataType type) } else { snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole()); } - + if ((our_port = _session.engine().register_input_port (type, name)) == 0) { error << string_compose(_("IO: cannot register input port %1"), name) << endmsg; return -1; } _inputs.add (our_port); - drop_input_connection (); + drop_input_bundle (); setup_peak_meters (); reset_panner (); } @@ -698,6 +730,7 @@ IO::add_input_port (string source, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles (); _session.set_dirty (); return 0; @@ -707,7 +740,7 @@ int IO::disconnect_inputs (void* src) { { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -716,7 +749,7 @@ IO::disconnect_inputs (void* src) _session.engine().disconnect (*i); } - drop_input_connection (); + drop_input_bundle (); } } @@ -729,7 +762,7 @@ int IO::disconnect_outputs (void* src) { { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -738,7 +771,7 @@ IO::disconnect_outputs (void* src) _session.engine().disconnect (*i); } - drop_output_connection (); + drop_output_bundle (); } } @@ -793,7 +826,7 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure(); } _inputs.add (input_port); @@ -802,7 +835,7 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) } if (changed) { - drop_input_connection (); + drop_input_bundle (); setup_peak_meters (); reset_panner (); MoreChannels (n_inputs()); /* EMIT SIGNAL */ @@ -845,7 +878,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) } { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm (io_lock); Port* port; @@ -904,12 +937,12 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) return -1; } } - + catch (AudioEngine::PortRegistrationFailure& err) { setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure(); } _inputs.add (port); @@ -941,7 +974,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure (); } _outputs.add (port); @@ -969,17 +1002,18 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) } if (out_changed) { - drop_output_connection (); + drop_output_bundle (); output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ } if (in_changed) { - drop_input_connection (); + drop_input_bundle (); input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ } if (in_changed || out_changed) { MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */ + setup_bundles (); _session.set_dirty (); } @@ -998,7 +1032,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src) } if (lockit) { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock im (io_lock); changed = ensure_inputs_locked (count, clear, src); } else { @@ -1007,6 +1041,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles (); _session.set_dirty (); } return 0; @@ -1065,7 +1100,7 @@ IO::ensure_outputs_locked (ChanCount count, bool clear, void* src) } if (changed) { - drop_output_connection (); + drop_output_bundle (); MoreChannels (n_outputs()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -1095,7 +1130,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) /* XXX caller should hold io_lock, but generally doesn't */ if (lockit) { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock im (io_lock); changed = ensure_outputs_locked (count, clear, src); } else { @@ -1104,6 +1139,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles (); } return 0; @@ -1112,8 +1148,8 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) gain_t IO::effective_gain () const { - if (gain_automation_playback()) { - return _effective_gain; + if (_gain_control->list()->automation_playback()) { + return _gain_control->get_value(); } else { return _desired_gain; } @@ -1124,7 +1160,7 @@ IO::reset_panner () { if (panners_legal) { if (!no_panner_reset) { - _panner->reset (n_outputs().get(DataType::AUDIO), pans_required()); + _panner->reset (n_outputs().n_audio(), pans_required()); } } else { panner_legal_c.disconnect (); @@ -1135,7 +1171,7 @@ IO::reset_panner () int IO::panners_became_legal () { - _panner->reset (n_outputs().get(DataType::AUDIO), pans_required()); + _panner->reset (n_outputs().n_audio(), pans_required()); _panner->load (); // automation panner_legal_c.disconnect (); return 0; @@ -1178,13 +1214,13 @@ IO::state (bool full_state) str = ""; - if (_input_connection) { - node->add_property ("input-connection", _input_connection->name()); + if (_input_bundle && !_input_bundle->dynamic()) { + node->add_property ("input-connection", _input_bundle->name()); need_ins = false; } - if (_output_connection) { - node->add_property ("output-connection", _output_connection->name()); + if (_output_bundle && !_output_bundle->dynamic()) { + node->add_property ("output-connection", _output_bundle->name()); need_outs = false; } @@ -1255,7 +1291,7 @@ IO::state (bool full_state) } node->add_child_nocopy (_panner->state (full_state)); - node->add_child_nocopy (_gain_control.get_state ()); + node->add_child_nocopy (_gain_control->get_state ()); snprintf (buf, sizeof(buf), "%2.12f", gain()); node->add_property ("gain", buf); @@ -1271,18 +1307,9 @@ IO::state (bool full_state) node->add_property ("iolimits", buf); /* automation */ - - if (full_state) { - - XMLNode* autonode = new XMLNode (X_("Automation")); - autonode->add_child_nocopy (get_automation_state()); - node->add_child_nocopy (*autonode); - - snprintf (buf, sizeof (buf), "0x%x", (int) _gain_automation_curve.automation_state()); - } else { - /* never store anything except Off for automation state in a template */ - snprintf (buf, sizeof (buf), "0x%x", ARDOUR::Off); - } + + if (full_state) + node->add_child_nocopy (get_automation_state()); return *node; } @@ -1346,11 +1373,13 @@ IO::set_state (const XMLNode& node) if ((*iter)->name() == X_("Automation")) { - set_automation_state (*(*iter)->children().front()); + set_automation_state (*(*iter), Parameter(GainAutomation)); } - if ((*iter)->name() == X_("gaincontrol")) { - _gain_control.set_state (**iter); + if ((*iter)->name() == X_("controllable")) { + if ((prop = (*iter)->property("name")) != 0 && prop->value() == "gaincontrol") { + _gain_control->set_state (**iter); + } } } @@ -1386,23 +1415,9 @@ IO::set_state (const XMLNode& node) pending_state_node = new XMLNode (node); } - last_automation_snapshot = 0; - return 0; } -int -IO::set_automation_state (const XMLNode& node) -{ - return _gain_automation_curve.set_state (node); -} - -XMLNode& -IO::get_automation_state () -{ - return (_gain_automation_curve.get_state ()); -} - int IO::load_automation (string path) { @@ -1436,7 +1451,7 @@ IO::load_automation (string path) while (in.getline (line, sizeof(line), '\n')) { char type; - jack_nframes_t when; + nframes_t when; double value; if (++linecnt == 1) { @@ -1460,7 +1475,7 @@ IO::load_automation (string path) switch (type) { case 'g': - _gain_automation_curve.fast_simple_add (when, value); + _gain_control->list()->fast_simple_add (when, value); break; case 's': @@ -1533,24 +1548,27 @@ IO::create_ports (const XMLNode& node) int num_inputs = 0; int num_outputs = 0; + /* XXX: we could change *-connection to *-bundle, but it seems a bit silly to + * break the session file format. + */ if ((prop = node.property ("input-connection")) != 0) { - Connection* c = _session.connection_by_name (prop->value()); + boost::shared_ptr c = _session.bundle_by_name (prop->value()); if (c == 0) { - error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; + error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; - if ((c = _session.connection_by_name (_("in 1"))) == 0) { - error << _("No input connections available as a replacement") + if ((c = _session.bundle_by_name (_("in 1"))) == 0) { + error << _("No input bundles available as a replacement") << endmsg; return -1; } else { - info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value()) + info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value()) << endmsg; } } - num_inputs = c->nports(); + num_inputs = c->nchannels(); } else if ((prop = node.property ("inputs")) != 0) { @@ -1558,22 +1576,22 @@ IO::create_ports (const XMLNode& node) } if ((prop = node.property ("output-connection")) != 0) { - Connection* c = _session.connection_by_name (prop->value()); + boost::shared_ptr c = _session.bundle_by_name (prop->value()); if (c == 0) { - error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; + error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; - if ((c = _session.connection_by_name (_("out 1"))) == 0) { - error << _("No output connections available as a replacement") + if ((c = _session.bundle_by_name (_("out 1"))) == 0) { + error << _("No output bundles available as a replacement") << endmsg; return -1; } else { - info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value()) + info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value()) << endmsg; } } - num_outputs = c->nports (); + num_outputs = c->nchannels (); } else if ((prop = node.property ("outputs")) != 0) { num_outputs = count (prop->value().begin(), prop->value().end(), '{'); @@ -1602,22 +1620,22 @@ IO::make_connections (const XMLNode& node) const XMLProperty* prop; if ((prop = node.property ("input-connection")) != 0) { - Connection* c = _session.connection_by_name (prop->value()); + boost::shared_ptr c = _session.bundle_by_name (prop->value()); if (c == 0) { error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; - if ((c = _session.connection_by_name (_("in 1"))) == 0) { + if ((c = _session.bundle_by_name (_("in 1"))) == 0) { error << _("No input connections available as a replacement") << endmsg; return -1; } else { - info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value()) + info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value()) << endmsg; } } - use_input_connection (*c, this); + connect_input_ports_to_bundle (c, this); } else if ((prop = node.property ("inputs")) != 0) { if (set_inputs (prop->value())) { @@ -1626,23 +1644,23 @@ IO::make_connections (const XMLNode& node) } } - if ((prop = node.property ("output-connection")) != 0) { - Connection* c = _session.connection_by_name (prop->value()); + if ((prop = node.property ("output-bundle")) != 0) { + boost::shared_ptr c = _session.bundle_by_name (prop->value()); if (c == 0) { - error << string_compose(_("Unknown connection \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; + error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; - if ((c = _session.connection_by_name (_("out 1"))) == 0) { - error << _("No output connections available as a replacement") + if ((c = _session.bundle_by_name (_("out 1"))) == 0) { + error << _("No output bundles available as a replacement") << endmsg; return -1; } else { - info << string_compose (_("Connection %1 was not available - \"out 1\" used instead"), prop->value()) + info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value()) << endmsg; } } - use_output_connection (*c, this); + connect_output_ports_to_bundle (c, this); } else if ((prop = node.property ("outputs")) != 0) { if (set_outputs (prop->value())) { @@ -1803,11 +1821,18 @@ IO::parse_gain_string (const string& str, vector& ports) return ports.size(); } -int -IO::set_name (string name, void* src) +bool +IO::set_name (const string& str) { - if (name == _name) { - return 0; + if (str == _name) { + return true; + } + + /* replace all colons in the name. i wish we didn't have to do this */ + string name = str; + + if (replace_all (name, ":", "-")) { + warning << _("you cannot use colons to name objects with I/O connections") << endmsg; } for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { @@ -1822,10 +1847,11 @@ IO::set_name (string name, void* src) i->set_name (current_name); } - _name = name; - name_changed (src); /* EMIT SIGNAL */ + bool const r = SessionObject::set_name(name); + + setup_bundles (); - return 0; + return r; } void @@ -1894,26 +1920,26 @@ IO::input_latency () const for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) { if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) { max_latency = latency; - } + } } return max_latency; } int -IO::use_input_connection (Connection& c, void* src) +IO::connect_input_ports_to_bundle (boost::shared_ptr c, void* src) { uint32_t limit; { - Glib::Mutex::Lock lm (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); - limit = c.nports(); + limit = c->nchannels(); - drop_input_connection (); + drop_input_bundle (); - // FIXME connections only work for audio-only + // FIXME bundles only work for audio-only if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) { return -1; } @@ -1923,9 +1949,9 @@ IO::use_input_connection (Connection& c, void* src) */ for (uint32_t n = 0; n < limit; ++n) { - const Connection::PortList& pl = c.port_connections (n); + const Bundle::PortList& pl = c->channel_ports (n); - for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { + for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { if (!_inputs.port(n)->connected_to ((*i))) { @@ -1953,9 +1979,9 @@ IO::use_input_connection (Connection& c, void* src) /* second pass: connect all requested ports where necessary */ for (uint32_t n = 0; n < limit; ++n) { - const Connection::PortList& pl = c.port_connections (n); + const Bundle::PortList& pl = c->channel_ports (n); - for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { + for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { if (!_inputs.port(n)->connected_to ((*i))) { @@ -1967,12 +1993,12 @@ IO::use_input_connection (Connection& c, void* src) } } - _input_connection = &c; + _input_bundle = c; - input_connection_configuration_connection = c.ConfigurationChanged.connect - (mem_fun (*this, &IO::input_connection_configuration_changed)); - input_connection_connection_connection = c.ConnectionsChanged.connect - (mem_fun (*this, &IO::input_connection_connection_changed)); + input_bundle_configuration_connection = c->ConfigurationChanged.connect + (mem_fun (*this, &IO::input_bundle_configuration_changed)); + input_bundle_connection_connection = c->PortsChanged.connect + (mem_fun (*this, &IO::input_bundle_connection_changed)); } input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */ @@ -1980,17 +2006,17 @@ IO::use_input_connection (Connection& c, void* src) } int -IO::use_output_connection (Connection& c, void* src) +IO::connect_output_ports_to_bundle (boost::shared_ptr c, void* src) { uint32_t limit; { - Glib::Mutex::Lock lm (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); - limit = c.nports(); + limit = c->nchannels(); - drop_output_connection (); + drop_output_bundle (); // FIXME: audio-only if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) { @@ -2003,9 +2029,9 @@ IO::use_output_connection (Connection& c, void* src) for (uint32_t n = 0; n < limit; ++n) { - const Connection::PortList& pl = c.port_connections (n); + const Bundle::PortList& pl = c->channel_ports (n); - for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { + for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { if (!_outputs.port(n)->connected_to ((*i))) { @@ -2033,9 +2059,9 @@ IO::use_output_connection (Connection& c, void* src) for (uint32_t n = 0; n < limit; ++n) { - const Connection::PortList& pl = c.port_connections (n); + const Bundle::PortList& pl = c->channel_ports (n); - for (Connection::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { + for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { if (!_outputs.port(n)->connected_to ((*i))) { @@ -2046,12 +2072,12 @@ IO::use_output_connection (Connection& c, void* src) } } - _output_connection = &c; + _output_bundle = c; - output_connection_configuration_connection = c.ConfigurationChanged.connect - (mem_fun (*this, &IO::output_connection_configuration_changed)); - output_connection_connection_connection = c.ConnectionsChanged.connect - (mem_fun (*this, &IO::output_connection_connection_changed)); + output_bundle_configuration_connection = c->ConfigurationChanged.connect + (mem_fun (*this, &IO::output_bundle_configuration_changed)); + output_bundle_connection_connection = c->PortsChanged.connect + (mem_fun (*this, &IO::output_bundle_connection_changed)); } output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */ @@ -2102,45 +2128,53 @@ IO::reset_panners () } void -IO::input_connection_connection_changed (int ignored) +IO::input_bundle_connection_changed (int ignored) { - use_input_connection (*_input_connection, this); + connect_input_ports_to_bundle (_input_bundle, this); } void -IO::input_connection_configuration_changed () +IO::input_bundle_configuration_changed () { - use_input_connection (*_input_connection, this); + connect_input_ports_to_bundle (_input_bundle, this); } void -IO::output_connection_connection_changed (int ignored) +IO::output_bundle_connection_changed (int ignored) { - use_output_connection (*_output_connection, this); + connect_output_ports_to_bundle (_output_bundle, this); } void -IO::output_connection_configuration_changed () +IO::output_bundle_configuration_changed () { - use_output_connection (*_output_connection, this); + connect_output_ports_to_bundle (_output_bundle, this); } void -IO::GainControllable::set_value (float val) +IO::GainControl::set_value (float val) { - io.set_gain (direct_control_to_gain (val), this); + // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05)) + if (val > 1.99526231f) + val = 1.99526231f; + + _user_value = val; + _io.set_gain (val, this); + + Changed(); /* EMIT SIGNAL */ } float -IO::GainControllable::get_value (void) const +IO::GainControl::get_value (void) const { - return direct_gain_to_control (io.effective_gain()); + return AutomationControl::get_value(); } void IO::setup_peak_meters() { - _meter->setup(std::max(_inputs.count(), _outputs.count())); + ChanCount max_streams = std::max(_inputs.count(), _outputs.count()); + _meter->configure_io(max_streams, max_streams); } /** @@ -2171,54 +2205,45 @@ IO::meter () void IO::clear_automation () { - Glib::Mutex::Lock lm (automation_lock); - _gain_automation_curve.clear (); + Automatable::clear_automation (); // clears gain automation _panner->clear_automation (); } void -IO::set_gain_automation_state (AutoState state) +IO::set_parameter_automation_state (Parameter param, AutoState state) { - bool changed = false; + // XXX: would be nice to get rid of this special hack - { - Glib::Mutex::Lock lm (automation_lock); + if (param.type() == GainAutomation) { - if (state != _gain_automation_curve.automation_state()) { - changed = true; - last_automation_snapshot = 0; - _gain_automation_curve.set_automation_state (state); - - if (state != Off) { - set_gain (_gain_automation_curve.eval (_session.transport_frame()), this); - } - } - } + bool changed = false; - if (changed) { - _session.set_dirty (); - gain_automation_state_changed (); /* EMIT SIGNAL */ - } -} + { + Glib::Mutex::Lock lm (_automation_lock); -void -IO::set_gain_automation_style (AutoStyle style) -{ - bool changed = false; + boost::shared_ptr gain_auto = _gain_control->list(); - { - Glib::Mutex::Lock lm (automation_lock); + if (state != gain_auto->automation_state()) { + changed = true; + _last_automation_snapshot = 0; + gain_auto->set_automation_state (state); - if (style != _gain_automation_curve.automation_style()) { - changed = true; - _gain_automation_curve.set_automation_style (style); + if (state != Off) { + // FIXME: shouldn't this use Curve? + set_gain (gain_auto->eval (_session.transport_frame()), this); + } + } } - } - if (changed) { - gain_automation_style_changed (); /* EMIT SIGNAL */ + if (changed) { + _session.set_dirty (); + } + + } else { + Automatable::set_parameter_automation_state(param, state); } } + void IO::inc_gain (gain_t factor, void *src) { @@ -2232,7 +2257,15 @@ void IO::set_gain (gain_t val, void *src) { // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05)) - if (val>1.99526231f) val=1.99526231f; + if (val > 1.99526231f) + val = 1.99526231f; + + if (src != _gain_control.get()) { + _gain_control->set_value(val); + // bit twisty, this will come back and call us again + // (this keeps control in sync with reality) + return; + } { Glib::Mutex::Lock dm (declick_lock); @@ -2240,38 +2273,22 @@ IO::set_gain (gain_t val, void *src) } if (_session.transport_stopped()) { - _effective_gain = val; _gain = val; } - - gain_changed (src); - _gain_control.Changed (); /* EMIT SIGNAL */ - if (_session.transport_stopped() && src != 0 && src != this && gain_automation_recording()) { - _gain_automation_curve.add (_session.transport_frame(), val); + if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) { + _gain_control->list()->add (_session.transport_frame(), val); } _session.set_dirty(); } -void -IO::start_gain_touch () -{ - _gain_automation_curve.start_touch (); -} - -void -IO::end_gain_touch () -{ - _gain_automation_curve.stop_touch (); -} - void IO::start_pan_touch (uint32_t which) { if (which < _panner->size()) { - (*_panner)[which]->automation().start_touch(); + (*_panner)[which]->pan_control()->list()->start_touch(); } } @@ -2279,7 +2296,7 @@ void IO::end_pan_touch (uint32_t which) { if (which < _panner->size()) { - (*_panner)[which]->automation().stop_touch(); + (*_panner)[which]->pan_control()->list()->stop_touch(); } } @@ -2287,30 +2304,26 @@ IO::end_pan_touch (uint32_t which) void IO::automation_snapshot (nframes_t now) { - if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) { + Automatable::automation_snapshot (now); - if (gain_automation_recording()) { - _gain_automation_curve.rt_add (now, gain()); - } - + if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { _panner->snapshot (now); - - last_automation_snapshot = now; } } void IO::transport_stopped (nframes_t frame) { - _gain_automation_curve.reposition_for_rt_add (frame); + _gain_control->list()->reposition_for_rt_add (frame); - if (_gain_automation_curve.automation_state() != Off) { + if (_gain_control->list()->automation_state() != Off) { /* the src=0 condition is a special signal to not propagate automation gain changes into the mix group when locating. */ - set_gain (_gain_automation_curve.eval (frame), 0); + // FIXME: shouldn't this use Curve? + set_gain (_gain_control->list()->eval (frame), 0); } _panner->transport_stopped (frame); @@ -2406,7 +2419,126 @@ IO::set_phase_invert (bool yn, void *src) { if (_phase_invert != yn) { _phase_invert = yn; + // phase_invert_changed (src); /* EMIT SIGNAL */ + } +} + +void +IO::set_denormal_protection (bool yn, void *src) +{ + if (_denormal_protection != yn) { + _denormal_protection = yn; + // denormal_protection_changed (src); /* EMIT SIGNAL */ + } +} + +void +IO::update_port_total_latencies () +{ + /* io_lock, not taken: function must be called from Session::process() calltree */ + + for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + _session.engine().update_total_latency (*i); } - // phase_invert_changed (src); /* EMIT SIGNAL */ + + for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) { + _session.engine().update_total_latency (*i); + } +} + + +/** + * Setup bundles that describe our inputs and outputs. + */ + +void +IO::setup_bundles () +{ + char buf[32]; + + snprintf(buf, sizeof (buf), _("%s in"), _name.c_str()); + _bundle_for_inputs->set_name (buf, 0); + int const ins = n_inputs().n_total(); + _bundle_for_inputs->set_nchannels (ins); + + for (int i = 0; i < ins; ++i) { + _bundle_for_inputs->add_port_to_channel (i, _inputs.port(i)->name ()); + } + + snprintf(buf, sizeof (buf), _("%s out"), _name.c_str()); + _bundle_for_outputs->set_name (buf, 0); + int const outs = n_outputs().n_total(); + _bundle_for_outputs->set_nchannels (outs); + + for (int i = 0; i < outs; ++i) { + _bundle_for_outputs->add_port_to_channel (i, _outputs.port(i)->name ()); + } } + +/** + * Create and setup bundles that describe our inputs and outputs. + */ + +void +IO::create_bundles () +{ + _bundle_for_inputs = boost::shared_ptr ( + new InputBundle ("", true) + ); + + _bundle_for_outputs = boost::shared_ptr ( + new OutputBundle ("", true) + ); + + setup_bundles (); +} + +boost::shared_ptr +IO::input_bundle() +{ + if (_input_bundle) { + return _input_bundle; + } + + /* XXX: will only report the first bundle found; should really return a list, I think */ + + /* check that _input_bundle is right wrt the connections that are currently made */ + + /* make a vector of the first output connected to each of our inputs */ + std::vector connected; + for (uint32_t i = 0; i < _inputs.num_ports(); ++i) { + const char** c = _inputs.port(i)->get_connections (); + if (c) { + connected.push_back (c[0]); + } + } + + _input_bundle = _session.bundle_by_ports (connected); + return _input_bundle; +} + + +boost::shared_ptr +IO::output_bundle() +{ + if (_output_bundle) { + return _output_bundle; + } + + /* XXX: will only report the first bundle found; should really return a list, I think */ + + /* check that _output_bundle is right wrt the connections that are currently made */ + + /* make a vector of the first input connected to each of our outputs */ + std::vector connected; + for (uint32_t i = 0; i < _outputs.num_ports(); ++i) { + const char** c = _outputs.port(i)->get_connections (); + if (c) { + connected.push_back (c[0]); + } + } + + _output_bundle = _session.bundle_by_ports (connected); + return _output_bundle; +}