#include <sigc++/bind.h>
+#include <glibmm.h>
#include <glibmm/thread.h>
#include <pbd/xml++.h>
#include <pbd/replace_all.h>
+#include <pbd/unknown_type.h>
#include <ardour/audioengine.h>
#include <ardour/io.h>
+#include <ardour/route.h>
#include <ardour/port.h>
#include <ardour/audio_port.h>
#include <ardour/midi_port.h>
IO::IO (Session& s, const string& name,
int input_min, int input_max, int output_min, int output_max,
DataType default_type, bool public_ports)
- : Automatable (s, name),
+ : SessionObject(s, name),
+ AutomatableControls (s),
_output_buffers (new BufferSet()),
+ _active(true),
_default_type (default_type),
_public_ports (public_ports),
_input_minimum (ChanCount::ZERO),
deferred_state = 0;
boost::shared_ptr<AutomationList> gl(
- new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+ new AutomationList(Evoral::Parameter(GainAutomation)));
_gain_control = boost::shared_ptr<GainControl>(
new GainControl(X_("gaincontrol"), *this, gl));
}
IO::IO (Session& s, const XMLNode& node, DataType dt)
- : Automatable (s, "unnamed io"),
- _output_buffers (new BufferSet()),
+ : SessionObject(s, "unnamed io"),
+ AutomatableControls (s),
+ _output_buffers (new BufferSet()),
+ _active(true),
_default_type (dt)
{
_meter = new PeakMeter (_session);
apply_gain_automation = false;
boost::shared_ptr<AutomationList> gl(
- new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+ new AutomationList(Evoral::Parameter(GainAutomation)));
_gain_control = boost::shared_ptr<GainControl>(
new GainControl(X_("gaincontrol"), *this, gl));
IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset)
{
assert(outs.available() >= n_inputs());
-
- outs.set_count(n_inputs());
- if (outs.count() == ChanCount::ZERO)
+ if (n_inputs() == ChanCount::ZERO)
return;
+ outs.set_count(n_inputs());
+
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
BufferSet::iterator o = outs.begin(*t);
for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
+ (*i).cycle_start (nframes, offset);
o->read_from(i->get_buffer(), nframes, offset);
}
collect_input (bufs, nframes, offset);
- _meter->run(bufs, start_frame, end_frame, nframes, offset);
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
}
for (std::vector<UserBundleInfo>::iterator i = list.begin(); i != list.end(); ++i) {
- uint32_t const N = i->bundle->nchannels ();
+ ChanCount const N = i->bundle->nchannels ();
- if (ports.num_ports() < N) {
+ if (ports.num_ports (default_type()) < N.get (default_type())) {
continue;
}
bool ok = true;
- for (uint32_t j = 0; j < N; ++j) {
+ uint32_t n = N.get (default_type());
+
+ for (uint32_t j = 0; j < n; ++j) {
/* Every port on bundle channel j must be connected to our input j */
PortList const pl = i->bundle->channel_ports (j);
for (uint32_t k = 0; k < pl.size(); ++k) {
IO::add_output_port (string destination, void* src, DataType type)
{
Port* our_port;
- char name[64];
if (type == DataType::NIL)
type = _default_type;
/* Create a new output port */
- // FIXME: naming scheme for differently typed ports?
- if (_output_maximum.get(type) == 1) {
- snprintf (name, sizeof (name), _("%s/out"), _name.c_str());
- } else {
- snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole());
- }
+ string portname = build_legal_port_name (type, false);
- if ((our_port = _session.engine().register_output_port (type, name, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
+ if ((our_port = _session.engine().register_output_port (type, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
IO::add_input_port (string source, void* src, DataType type)
{
Port* our_port;
- char name[64];
if (type == DataType::NIL)
type = _default_type;
{
Glib::Mutex::Lock lm (io_lock);
-
- if (n_inputs() >= _input_maximum) {
+
+ if (_input_maximum.get(type) >= 0 && n_inputs().get (type) >= _input_maximum.get (type)) {
return -1;
}
/* Create a new input port */
- // FIXME: naming scheme for differently typed ports?
- if (_input_maximum.get(type) == 1) {
- snprintf (name, sizeof (name), _("%s/in"), _name.c_str());
- } else {
- snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole());
- }
+ string portname = build_legal_port_name (type, true);
- if ((our_port = _session.engine().register_input_port (type, name, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
+ if ((our_port = _session.engine().register_input_port (type, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
return -1;
}
/* create any necessary new ports */
while (n_inputs().get(*t) < n) {
- char buf[64];
-
- if (_input_maximum.get(*t) == 1) {
- snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
- } else {
- snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
- }
+ string portname = build_legal_port_name (*t, true);
try {
- if ((input_port = _session.engine().register_input_port (*t, buf, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
+ if ((input_port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
return -1;
}
}
while (n_inputs().get(*t) < nin) {
- char buf[64];
-
- /* Create a new input port */
-
- if (_input_maximum.get(*t) == 1) {
- snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str());
- } else {
- snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole());
- }
+ string portname = build_legal_port_name (*t, true);
try {
- if ((port = _session.engine().register_input_port (*t, buf, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
+ if ((port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
return -1;
}
}
while (n_outputs().get(*t) < nout) {
- char buf[64];
-
- /* Create a new output port */
-
- if (_output_maximum.get(*t) == 1) {
- snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
- } else {
- snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
- }
-
+ string portname = build_legal_port_name (*t, false);
+
try {
- if ((port = _session.engine().register_output_port (*t, buf, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
+ if ((port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
}
/* create any necessary new ports */
while (n_outputs().get(*t) < n) {
- char buf[64];
+ string portname = build_legal_port_name (*t, false);
- if (_output_maximum.get(*t) == 1) {
- snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str());
- } else {
- snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole());
- }
-
- if ((output_port = _session.engine().register_output_port (*t, buf, _public_ports)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
+ if ((output_port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
gain_t
IO::effective_gain () const
{
- if (_gain_control->list()->automation_playback()) {
+ if (_gain_control->automation_playback()) {
return _gain_control->get_value();
} else {
return _desired_gain;
if ((*iter)->name() == X_("Automation")) {
- set_automation_state (*(*iter), Parameter(GainAutomation));
+ set_automation_state (*(*iter), Evoral::Parameter(GainAutomation));
}
if ((*iter)->name() == X_("controllable")) {
float version;
LocaleGuard lg (X_("POSIX"));
- fullpath = _session.automation_dir();
- fullpath += path;
+ fullpath = Glib::build_filename(_session.automation_dir(), path);
in.open (fullpath.c_str());
if (!in) {
- fullpath = _session.automation_dir();
- fullpath += _session.snap_name();
- fullpath += '-';
- fullpath += path;
+ fullpath = Glib::build_filename(_session.automation_dir(), _session.snap_name() + '-' + path);
in.open (fullpath.c_str());
return ret;
}
+boost::shared_ptr<Bundle>
+IO::find_possible_bundle (const string &desired_name, const string &default_name, const string &bundle_type_name)
+{
+ static const string digits = "0123456789";
+
+ boost::shared_ptr<Bundle> c = _session.bundle_by_name (desired_name);
+
+ if (!c) {
+ int bundle_number, mask;
+ string possible_name;
+ bool stereo = false;
+ string::size_type last_non_digit_pos;
+
+ error << string_compose(_("Unknown bundle \"%1\" listed for %2 of %3"), desired_name, bundle_type_name, _name)
+ << endmsg;
+
+ // find numeric suffix of desired name
+ bundle_number = 0;
+
+ last_non_digit_pos = desired_name.find_last_not_of(digits);
+
+ if (last_non_digit_pos != string::npos) {
+ stringstream s;
+ s << desired_name.substr(last_non_digit_pos);
+ s >> bundle_number;
+ }
+
+ // see if it's a stereo connection e.g. "in 3+4"
+
+ if (last_non_digit_pos > 1 && desired_name[last_non_digit_pos] == '+') {
+ int left_bundle_number = 0;
+ string::size_type left_last_non_digit_pos;
+
+ left_last_non_digit_pos = desired_name.find_last_not_of(digits, last_non_digit_pos-1);
+
+ if (left_last_non_digit_pos != string::npos) {
+ stringstream s;
+ s << desired_name.substr(left_last_non_digit_pos, last_non_digit_pos-1);
+ s >> left_bundle_number;
+
+ if (left_bundle_number > 0 && left_bundle_number + 1 == bundle_number) {
+ bundle_number--;
+ stereo = true;
+ }
+ }
+ }
+
+ // make 0-based
+ if (bundle_number)
+ bundle_number--;
+
+ // find highest set bit
+ mask = 1;
+ while ((mask <= bundle_number) && (mask <<= 1));
+
+ // "wrap" bundle number into largest possible power of 2
+ // that works...
+
+ while (mask) {
+
+ if (bundle_number & mask) {
+ bundle_number &= ~mask;
+
+ stringstream s;
+ s << default_name << " " << bundle_number + 1;
+
+ if (stereo) {
+ s << "+" << bundle_number + 2;
+ }
+
+ possible_name = s.str();
+
+ if ((c = _session.bundle_by_name (possible_name)) != 0) {
+ break;
+ }
+ }
+ mask >>= 1;
+ }
+ if (c) {
+ info << string_compose (_("Bundle %1 was not available - \"%2\" used instead"), desired_name, possible_name)
+ << endmsg;
+ } else {
+ error << string_compose(_("No %1 bundles available as a replacement"), bundle_type_name)
+ << endmsg;
+ }
+
+ }
+
+ return c;
+
+}
+
int
IO::create_ports (const XMLNode& node)
{
XMLProperty const * prop;
- int num_inputs = 0;
- int num_outputs = 0;
+ ChanCount num_inputs;
+ ChanCount num_outputs;
+
+ if ((prop = node.property ("input-connection")) != 0) {
+
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("in"), _("input"));
+
+ if (!c) {
+ return -1;
+ }
+
+ num_inputs = c->nchannels();
+
+ } else if ((prop = node.property ("inputs")) != 0) {
+
+ num_inputs.set (default_type(), count (prop->value().begin(), prop->value().end(), '{'));
+ }
+
+ if ((prop = node.property ("output-connection")) != 0) {
- if ((prop = node.property ("inputs")) != 0) {
- num_inputs = count (prop->value().begin(), prop->value().end(), '{');
+ boost::shared_ptr<Bundle> c = find_possible_bundle(prop->value(), _("out"), _("output"));
+
+ if (!c) {
+ return -1;
+ }
+
+ num_outputs = c->nchannels ();
+
} else if ((prop = node.property ("outputs")) != 0) {
- num_outputs = count (prop->value().begin(), prop->value().end(), '{');
+
+ num_outputs.set (default_type(), count (prop->value().begin(), prop->value().end(), '{'));
}
no_panner_reset = true;
- // FIXME: audio-only
- if (ensure_io (ChanCount(DataType::AUDIO, num_inputs), ChanCount(DataType::AUDIO, num_outputs), true, this)) {
+ if (ensure_io (num_inputs, num_outputs, true, this)) {
error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
return -1;
}
int
IO::make_connections (const XMLNode& node)
{
- XMLProperty const * prop;
-
- if ((prop = node.property ("inputs")) != 0) {
+
+ const XMLProperty* prop;
+
+ if ((prop = node.property ("input-connection")) != 0) {
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("in"), _("input"));
+
+ if (!c) {
+ return -1;
+ }
+
+ connect_input_ports_to_bundle (c, this);
+
+ } else if ((prop = node.property ("inputs")) != 0) {
if (set_inputs (prop->value())) {
error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
return -1;
}
}
-
- if ((prop = node.property ("outputs")) != 0) {
+ if ((prop = node.property ("output-connection")) != 0) {
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("out"), _("output"));
+
+ if (!c) {
+ return -1;
+ }
+
+ connect_output_ports_to_bundle (c, this);
+
+ } else if ((prop = node.property ("outputs")) != 0) {
if (set_outputs (prop->value())) {
error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
return -1;
if ((*i)->name() == "InputBundle") {
XMLProperty const * prop = (*i)->property ("name");
if (prop) {
- boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ boost::shared_ptr<Bundle> b = find_possible_bundle (prop->value(), _("in"), _("input"));
if (b) {
connect_input_ports_to_bundle (b, this);
- } else {
- error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
}
}
} else if ((*i)->name() == "OutputBundle") {
XMLProperty const * prop = (*i)->property ("name");
if (prop) {
- boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ boost::shared_ptr<Bundle> b = find_possible_bundle (prop->value(), _("out"), _("output"));
if (b) {
connect_output_ports_to_bundle (b, this);
- } else {
- error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
- }
+ }
}
}
}
}
bool
-IO::set_name (const string& str)
+IO::set_name (const string& requested_name)
{
- if (str == _name) {
+ if (requested_name == _name) {
return true;
}
+ string name;
+ Route *rt;
+ if ( (rt = dynamic_cast<Route *>(this))) {
+ name = Route::ensure_track_or_route_name(requested_name, _session);
+ } else {
+ name = requested_name;
+ }
+
+
/* 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;
/* Connect to the bundle, not worrying about any connections
that are already made. */
- uint32_t const channels = c->nchannels ();
-
- for (uint32_t n = 0; n < channels; ++n) {
+ ChanCount const channels = c->nchannels ();
+ uint32_t cnt = channels.get (default_type());
+
+ for (uint32_t n = 0; n < cnt; ++n) {
const PortList& pl = c->channel_ports (n);
for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
/* Connect to the bundle, not worrying about any connections
that are already made. */
- uint32_t const channels = c->nchannels ();
+ ChanCount const channels = c->nchannels ();
+ uint32_t cnt = channels.get (default_type());
- for (uint32_t n = 0; n < channels; ++n) {
+ for (uint32_t n = 0; n < cnt; ++n) {
const PortList& pl = c->channel_ports (n);
void
IO::setup_peak_meters()
{
- ChanCount max_streams = std::max(_inputs.count(), _outputs.count());
- _meter->configure_io(max_streams, max_streams);
+ ChanCount max_streams = std::max (_inputs.count(), _outputs.count());
+ _meter->configure_io (max_streams, max_streams);
}
/**
void
IO::update_meters()
{
- Glib::Mutex::Lock guard (m_meter_signal_lock);
-
- Meter(); /* EMIT SIGNAL */
+ Glib::Mutex::Lock guard (m_meter_signal_lock);
+ Meter(); /* EMIT SIGNAL */
}
void
void
IO::clear_automation ()
{
- Automatable::clear_automation (); // clears gain automation
+ data().clear (); // clears gain automation
_panner->clear_automation ();
}
void
-IO::set_parameter_automation_state (Parameter param, AutoState state)
+IO::set_parameter_automation_state (Evoral::Parameter param, AutoState state)
{
// XXX: would be nice to get rid of this special hack
bool changed = false;
{
- Glib::Mutex::Lock lm (_automation_lock);
+ Glib::Mutex::Lock lm (control_lock());
- boost::shared_ptr<AutomationList> gain_auto = _gain_control->list();
+ boost::shared_ptr<AutomationList> gain_auto
+ = boost::dynamic_pointer_cast<AutomationList>(_gain_control->list());
if (state != gain_auto->automation_state()) {
changed = true;
}
} else {
- Automatable::set_parameter_automation_state(param, state);
+ AutomatableControls::set_parameter_automation_state(param, state);
}
}
_gain = val;
}
- if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) {
+ if (_session.transport_stopped() && src != 0 && src != this && _gain_control->automation_write()) {
_gain_control->list()->add (_session.transport_frame(), val);
}
IO::start_pan_touch (uint32_t which)
{
if (which < _panner->size()) {
- (*_panner)[which]->pan_control()->list()->start_touch();
+ (*_panner)[which]->pan_control()->start_touch();
}
}
IO::end_pan_touch (uint32_t which)
{
if (which < _panner->size()) {
- (*_panner)[which]->pan_control()->list()->stop_touch();
+ (*_panner)[which]->pan_control()->stop_touch();
}
}
void
-IO::automation_snapshot (nframes_t now)
+IO::automation_snapshot (nframes_t now, bool force)
{
- Automatable::automation_snapshot (now);
+ AutomatableControls::automation_snapshot (now, force);
if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
_panner->snapshot (now);
}
+
+ _panner->snapshot (now);
+ _last_automation_snapshot = now;
}
void
{
_gain_control->list()->reposition_for_rt_add (frame);
- if (_gain_control->list()->automation_state() != Off) {
+ if (_gain_control->automation_state() != Off) {
/* the src=0 condition is a special signal to not propagate
automation gain changes into the mix group when locating.
_panner->transport_stopped (frame);
}
+string
+IO::build_legal_port_name (DataType type, bool in)
+{
+ const int name_size = jack_port_name_size();
+ int limit;
+ string suffix;
+ int maxports;
+
+ if (type == DataType::AUDIO) {
+ suffix = _("audio");
+ } else if (type == DataType::MIDI) {
+ suffix = _("midi");
+ } else {
+ throw unknown_type();
+ }
+
+ if (in) {
+ suffix += _("_in");
+ maxports = _input_maximum.get(type);
+ } else {
+ suffix += _("_out");
+ maxports = _output_maximum.get(type);
+ }
+
+ if (maxports == 1) {
+ // allow space for the slash + the suffix
+ limit = name_size - _session.engine().client_name().length() - (suffix.length() + 1);
+ char buf[name_size+1];
+ snprintf (buf, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str());
+ return string (buf);
+ }
+
+ // allow up to 4 digits for the output port number, plus the slash, suffix and extra space
+
+ limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5);
+
+ char buf1[name_size+1];
+ char buf2[name_size+1];
+
+ snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str());
+
+ int port_number;
+
+ if (in) {
+ port_number = find_input_port_hole (buf1);
+ } else {
+ port_number = find_output_port_hole (buf1);
+ }
+
+ snprintf (buf2, name_size+1, "%s %d", buf1, port_number);
+
+ return string (buf2);
+}
+
int32_t
-IO::find_input_port_hole ()
+IO::find_input_port_hole (const char* base)
{
/* CALLER MUST HOLD IO LOCK */
return 1;
}
- for (n = 1; n < UINT_MAX; ++n) {
+ /* we only allow up to 4 characters for the port number
+ */
+
+ for (n = 1; n < 9999; ++n) {
char buf[jack_port_name_size()];
PortSet::iterator i = _inputs.begin();
- snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n);
+ snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
for ( ; i != _inputs.end(); ++i) {
if (i->short_name() == buf) {
}
int32_t
-IO::find_output_port_hole ()
+IO::find_output_port_hole (const char* base)
{
/* CALLER MUST HOLD IO LOCK */
return 1;
}
- for (n = 1; n < UINT_MAX; ++n) {
+ /* we only allow up to 4 characters for the port number
+ */
+
+ for (n = 1; n < 9999; ++n) {
char buf[jack_port_name_size()];
PortSet::iterator i = _outputs.begin();
- snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n);
+ snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
for ( ; i != _outputs.end(); ++i) {
if (i->short_name() == buf) {
return n;
}
+void
+IO::set_active (bool yn)
+{
+ _active = yn;
+ active_changed(); /* EMIT SIGNAL */
+}
+
+
AudioPort*
IO::audio_input(uint32_t n) const
{
IO::maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles)
{
boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b);
+
if (ab == 0 || ab->ports_are_outputs() == false) {
return;
}
-
- if (ab->nchannels () != n_inputs().n_total ()) {
+
+ if (ab->nchannels().get (default_type()) != n_inputs().n_total ()) {
return;
}
return;
}
- if (ab->nchannels () != n_outputs().n_total ()) {
+ if (ab->nchannels ().get (default_type()) != n_outputs().n_total ()) {
return;
}
sigc::mem_fun (*io, &IO::bundle_ports_have_changed)
);
}
+
+void
+IO::prepare_inputs (nframes_t nframes, nframes_t offset)
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ (*i).cycle_start (nframes, offset);
+ }
+}