#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>
-#include <ardour/bundle.h>
#include <ardour/session.h>
#include <ardour/cycle_timer.h>
#include <ardour/panner.h>
#include <ardour/buffer_set.h>
#include <ardour/meter.h>
#include <ardour/amp.h>
+#include <ardour/user_bundle.h>
#include "i18n.h"
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;
sigc::signal<int> IO::ConnectingLegal;
sigc::signal<int> IO::PortsLegal;
sigc::signal<int> IO::PannersLegal;
-sigc::signal<void,ChanCount> IO::MoreChannels;
+sigc::signal<void,ChanCount> IO::PortCountChanged;
sigc::signal<int> IO::PortsCreated;
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 */
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),
+ : SessionObject(s, name),
+ AutomatableControls (s),
+ _output_buffers (new BufferSet()),
+ _active(true),
+ _default_type (default_type),
_input_minimum (ChanCount::ZERO),
_input_maximum (ChanCount::INFINITE),
_output_minimum (ChanCount::ZERO),
_gain = 1.0;
_desired_gain = 1.0;
- _input_bundle = 0;
- _output_bundle = 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<AutomationList> gl(
+ new AutomationList(Evoral::Parameter(GainAutomation)));
+
+ _gain_control = boost::shared_ptr<GainControl>( new GainControl( X_("gaincontrol"), this, Evoral::Parameter(GainAutomation), gl ));
- _gain_automation_state = Off;
- _gain_automation_style = Absolute;
+ add_control(_gain_control);
+ apply_gain_automation = false;
+
{
// IO::Meter is emitted from another thread so the
// Meter signal must be protected.
m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
}
- // 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);
+ setup_bundles_for_inputs_and_outputs ();
}
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()
+ : SessionObject(s, "unnamed io"),
+ AutomatableControls (s),
+ _output_buffers (new BufferSet()),
+ _active(true),
+ _default_type (dt)
{
- // FIXME: hack
_meter = new PeakMeter (_session);
-
_panner = 0;
deferred_state = 0;
no_panner_reset = false;
_desired_gain = 1.0;
_gain = 1.0;
- _input_bundle = 0;
- _output_bundle = 0;
apply_gain_automation = false;
+
+ boost::shared_ptr<AutomationList> gl(
+ new AutomationList(Evoral::Parameter(GainAutomation)));
+
+ _gain_control = boost::shared_ptr<GainControl>(
+ new GainControl( X_("gaincontrol"), this, Evoral::Parameter(GainAutomation), gl));
+
+ add_control(_gain_control);
set_state (node);
m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
}
- // 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);
+ setup_bundles_for_inputs_and_outputs ();
}
IO::~IO ()
{
Glib::Mutex::Lock guard (m_meter_signal_lock);
-
Glib::Mutex::Lock lm (io_lock);
+ BLOCK_PROCESS_CALLBACK ();
+
for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
_session.engine().unregister_port (*i);
}
delete _meter;
delete _panner;
- delete _output_buffers;
}
void
/* io_lock, not taken: function must be called from Session::process() calltree */
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- i->get_buffer().silence (nframes, offset);
+ i->get_buffer(nframes,offset).silence (nframes, offset);
}
}
-/** Deliver bufs to the IO's Jack outputs.
+/** Deliver bufs to the IO's output ports
*
* This function should automatically do whatever it necessary to correctly deliver bufs
* to the outputs, eg applying gain or pan or whatever else needs to be done.
}
- if (dg != _gain || dg != 1.0)
- Amp::run(bufs, nframes, _gain, dg, _phase_invert);
+ if (dg != _gain || dg != 1.0) {
+ Amp::run_in_place(bufs, nframes, _gain, dg, _phase_invert);
+ _gain = dg;
+ }
}
+ /* do this so that any processing that comes after deliver_outputs()
+ can use the output buffers.
+ */
+
+ output_buffers().attach_buffers (_outputs, nframes, offset);
+
// 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);
- } else {
- const DataType type = DataType::AUDIO;
-
- // Copy any audio 1:1 to outputs
-
- BufferSet::iterator o = output_buffers().begin(type);
- BufferSet::iterator i = bufs.begin(type);
- BufferSet::iterator prev = i;
+
+ if (0 && _panner && _panner->npanners() && !_panner->bypassed()) {
+
+ /* blech .. we shouldn't be creating and tearing this down every process()
+ cycle. XXX fix me to not waste cycles and do memory allocation etc.
+ */
- while (i != bufs.end(type) && o != output_buffers().end (type)) {
- o->read_from(*i, nframes, offset);
- prev = i;
- ++i;
- ++o;
- }
+ _panner->run_out_of_place(bufs, output_buffers(), start_frame, end_frame, nframes, offset);
+
+ } else {
- /* extra outputs get a copy of the last buffer */
+ /* do a 1:1 copy of data to output ports */
- while (o != output_buffers().end(type)) {
- o->read_from(*prev, nframes, offset);
- ++o;
+ if (bufs.count().n_audio() > 0 && _outputs.count().n_audio () > 0) {
+ copy_to_outputs (bufs, DataType::AUDIO, nframes, offset);
+ }
+ if (bufs.count().n_midi() > 0 && _outputs.count().n_midi () > 0) {
+ copy_to_outputs (bufs, DataType::MIDI, nframes, offset);
}
}
+}
- /* ********** MIDI ********** */
+void
+IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes, nframes_t offset)
+{
+ // Copy any buffers 1:1 to outputs
+
+ PortSet::iterator o = _outputs.begin(type);
+ BufferSet::iterator i = bufs.begin(type);
+ BufferSet::iterator prev = i;
+
+ while (i != bufs.end(type) && o != _outputs.end (type)) {
+
+ Buffer& port_buffer (o->get_buffer (nframes, offset));
+ port_buffer.read_from (*i, nframes, offset);
- // No MIDI, we're done here
- if (bufs.count().n_midi() == 0) {
- return;
+ prev = i;
+ ++i;
+ ++o;
}
-
- const DataType type = DataType::MIDI;
-
- // Copy any MIDI 1:1 to outputs
- 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);
+
+ /* extra outputs get a copy of the last buffer */
+
+ while (o != _outputs.end(type)) {
+ Buffer& port_buffer (o->get_buffer (nframes, offset));
+ port_buffer.read_from(*prev, nframes, offset);
+ ++o;
}
}
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) {
- o->read_from(i->get_buffer(), nframes, offset);
+ PortSet::iterator e = _inputs.end (*t);
+ for (PortSet::iterator i = _inputs.begin(*t); i != e; ++i, ++o) {
+ Buffer& b (i->get_buffer (nframes,offset));
+ o->read_from (b, nframes, offset);
}
}
collect_input (bufs, nframes, offset);
- _meter->run(bufs, nframes);
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
}
+
void
-IO::drop_input_bundle ()
+IO::check_bundles_connected_to_inputs ()
{
- _input_bundle = 0;
- input_bundle_configuration_connection.disconnect();
- input_bundle_connection_connection.disconnect();
- _session.set_dirty ();
+ check_bundles (_bundles_connected_to_inputs, inputs());
}
void
-IO::drop_output_bundle ()
+IO::check_bundles_connected_to_outputs ()
{
- _output_bundle = 0;
- output_bundle_configuration_connection.disconnect();
- output_bundle_connection_connection.disconnect();
- _session.set_dirty ();
+ check_bundles (_bundles_connected_to_outputs, outputs());
+}
+
+void
+IO::check_bundles (std::vector<UserBundleInfo>& list, const PortSet& ports)
+{
+ std::vector<UserBundleInfo> new_list;
+
+ for (std::vector<UserBundleInfo>::iterator i = list.begin(); i != list.end(); ++i) {
+
+ uint32_t const N = i->bundle->nchannels ();
+
+ if (ports.num_ports (default_type()) < N) {
+ continue;
+ }
+
+ bool ok = true;
+
+ for (uint32_t j = 0; j < N; ++j) {
+ /* Every port on bundle channel j must be connected to our input j */
+ Bundle::PortList const pl = i->bundle->channel_ports (j);
+ for (uint32_t k = 0; k < pl.size(); ++k) {
+ if (ports.port(j)->connected_to (pl[k]) == false) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok == false) {
+ break;
+ }
+ }
+
+ if (ok) {
+ new_list.push_back (*i);
+ } else {
+ i->changed.disconnect ();
+ }
+ }
+
+ list = new_list;
}
+
int
IO::disconnect_input (Port* our_port, string other_port, void* src)
{
/* disconnect it from the source */
- if (_session.engine().disconnect (other_port, our_port->name())) {
+ if (our_port->disconnect (other_port)) {
error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
return -1;
}
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
}
}
/* connect it to the source */
- if (_session.engine().connect (other_port, our_port->name())) {
+ if (our_port->connect (other_port)) {
return -1;
}
-
- drop_input_bundle ();
}
}
/* disconnect it from the destination */
- if (_session.engine().disconnect (our_port->name(), other_port)) {
+ if (our_port->disconnect (other_port)) {
error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
return -1;
}
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
}
}
/* connect it to the destination */
- if (_session.engine().connect (our_port->name(), other_port)) {
+ if (our_port->connect (other_port)) {
return -1;
}
-
- drop_output_bundle ();
}
}
to the specified source.
*/
- if (_input_minimum.get_total() > 1) {
+ if (_input_minimum.n_total() > 1) {
/* sorry, you can't do this */
return -1;
}
}
_session.engine().unregister_port (*port);
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
setup_peak_meters ();
reset_panner ();
}
}
+
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
+ }
+
+ if (change == ConfigurationChanged) {
+ setup_bundle_for_outputs ();
}
if (change != NoChange) {
output_changed (change, src);
_session.set_dirty ();
return 0;
- }
-
+ }
+
return -1;
}
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)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), name) << endmsg;
+ if ((our_port = _session.engine().register_output_port (type, portname)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
_outputs.add (our_port);
- drop_output_bundle ();
setup_peak_meters ();
reset_panner ();
}
- MoreChannels (n_outputs()); /* EMIT SIGNAL */
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
}
if (destination.length()) {
- if (_session.engine().connect (our_port->name(), destination)) {
+ if (our_port->connect (destination)) {
return -1;
}
}
// pan_changed (src); /* EMIT SIGNAL */
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_outputs ();
_session.set_dirty ();
-
+
return 0;
}
}
_session.engine().unregister_port (*port);
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
setup_peak_meters ();
reset_panner ();
}
}
+
+ PortCountChanged (n_inputs ()); /* EMIT SIGNAL */
+ }
+
+ if (change == ConfigurationChanged) {
+ setup_bundle_for_inputs ();
}
if (change != NoChange) {
/** Add an input port.
*
- * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created.
+ * @param type Data type of port. The appropriate port type, and @ref Port will be created.
* @param destination Name of input port to connect new port to.
* @param src Source for emitted ConfigurationChanged signal.
*/
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());
- }
-
- if ((our_port = _session.engine().register_input_port (type, name)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), name) << endmsg;
+ string portname = build_legal_port_name (type, true);
+
+ if ((our_port = _session.engine().register_input_port (type, portname)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
return -1;
}
_inputs.add (our_port);
- drop_input_bundle ();
setup_peak_meters ();
reset_panner ();
}
- MoreChannels (n_inputs()); /* EMIT SIGNAL */
+ PortCountChanged (n_inputs()); /* EMIT SIGNAL */
}
if (source.length()) {
- if (_session.engine().connect (source, our_port->name())) {
+ if (our_port->connect (source)) {
return -1;
}
}
// pan_changed (src); /* EMIT SIGNAL */
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_inputs ();
_session.set_dirty ();
return 0;
Glib::Mutex::Lock lm (io_lock);
for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
}
}
Glib::Mutex::Lock lm (io_lock);
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
}
}
/* 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)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
+ if ((input_port = _session.engine().register_input_port (*t, portname)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
return -1;
}
}
}
if (changed) {
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
setup_peak_meters ();
reset_panner ();
- MoreChannels (n_inputs()); /* EMIT SIGNAL */
+ PortCountChanged (n_inputs()); /* EMIT SIGNAL */
_session.set_dirty ();
}
if (clear) {
/* disconnect all existing ports so that we get a fresh start */
for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
}
return changed;
}
-/** Attach output_buffers to port buffers.
- *
- * Connected to IO's own MoreChannels signal.
- */
-void
-IO::attach_buffers(ChanCount ignored)
-{
- _output_buffers->attach_buffers(_outputs);
-}
-
int
IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src)
{
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)) == 0) {
- error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg;
+ if ((port = _session.engine().register_input_port (*t, portname)) == 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)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
+ if ((port = _session.engine().register_output_port (*t, portname)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
}
/* disconnect all existing ports so that we get a fresh start */
for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
}
}
if (out_changed) {
- drop_output_bundle ();
+ check_bundles_connected_to_outputs ();
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_outputs ();
}
if (in_changed) {
- drop_input_bundle ();
+ check_bundles_connected_to_inputs ();
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_inputs ();
}
if (in_changed || out_changed) {
- MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
+ PortCountChanged (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
_session.set_dirty ();
}
if (changed) {
input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_inputs ();
_session.set_dirty ();
}
return 0;
/* create any necessary new ports */
while (n_outputs().get(*t) < n) {
- char buf[64];
-
- 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);
- if ((output_port = _session.engine().register_output_port (*t, buf)) == 0) {
- error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg;
+ if ((output_port = _session.engine().register_output_port (*t, portname)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
return -1;
}
}
if (changed) {
- drop_output_bundle ();
- MoreChannels (n_outputs()); /* EMIT SIGNAL */
+ check_bundles_connected_to_outputs ();
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
_session.set_dirty ();
}
if (clear) {
/* disconnect all existing ports so that we get a fresh start */
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- _session.engine().disconnect (*i);
+ i->disconnect_all ();
}
}
if (changed) {
output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundle_for_outputs ();
}
return 0;
gain_t
IO::effective_gain () const
{
- if (gain_automation_playback()) {
- return _effective_gain;
+ if (_gain_control->automation_playback()) {
+ return _gain_control->get_value();
} else {
return _desired_gain;
}
XMLNode* node = new XMLNode (state_node_name);
char buf[64];
string str;
- bool need_ins = true;
- bool need_outs = true;
+ vector<string>::iterator ci;
+ int n;
LocaleGuard lg (X_("POSIX"));
Glib::Mutex::Lock lm (io_lock);
id().print (buf, sizeof (buf));
node->add_property("id", buf);
- str = "";
-
- if (_input_bundle) {
- node->add_property ("input-connection", _input_bundle->name());
- need_ins = false;
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ i != _bundles_connected_to_inputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("InputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
}
- if (_output_bundle) {
- node->add_property ("output-connection", _output_bundle->name());
- need_outs = false;
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ i != _bundles_connected_to_outputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("OutputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
}
+
+ str = "";
- if (need_ins) {
- for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- const char **connections = i->get_connections();
+ vector<string> connections;
+
+ if (i->get_connections (connections)) {
+
+ str += '{';
- if (connections && connections[0]) {
- str += '{';
+ for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
+ if (n) {
+ str += ',';
+ }
- for (int n = 0; connections && connections[n]; ++n) {
- if (n) {
- str += ',';
- }
-
- /* if its a connection to our own port,
- return only the port name, not the
- whole thing. this allows connections
- to be re-established even when our
- client name is different.
- */
-
- str += _session.engine().make_port_name_relative (connections[n]);
- }
-
- str += '}';
+ /* if its a connection to our own port,
+ return only the port name, not the
+ whole thing. this allows connections
+ to be re-established even when our
+ client name is different.
+ */
- free (connections);
- }
- else {
- str += "{}";
- }
+ str += _session.engine().make_port_name_relative (*ci);
+ }
+
+ str += '}';
+
+ } else {
+ str += "{}";
}
-
- node->add_property ("inputs", str);
}
+
+ node->add_property ("inputs", str);
- if (need_outs) {
- str = "";
+ str = "";
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ vector<string> connections;
+
+ if (i->get_connections (connections)) {
- const char **connections = i->get_connections();
+ str += '{';
- if (connections && connections[0]) {
-
- str += '{';
-
- for (int n = 0; connections[n]; ++n) {
- if (n) {
- str += ',';
- }
-
- str += _session.engine().make_port_name_relative (connections[n]);
+ for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
+ if (n) {
+ str += ',';
}
-
- str += '}';
- free (connections);
- }
- else {
- str += "{}";
+ str += _session.engine().make_port_name_relative (*ci);
}
+
+ str += '}';
+
+ } else {
+ str += "{}";
}
-
- node->add_property ("outputs", str);
}
+
+ node->add_property ("outputs", str);
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);
- // FIXME: this is NOT sufficient!
- const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type);
- const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type);
- const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type);
- const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type);
+ /* To make backwards compatibility a bit easier, write ChanCount::INFINITE to the session file
+ as -1.
+ */
+
+ int const in_max = _input_maximum == ChanCount::INFINITE ? -1 : _input_maximum.get(_default_type);
+ int const out_max = _output_maximum == ChanCount::INFINITE ? -1 : _output_maximum.get(_default_type);
- snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max);
+ snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max);
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;
}
if ((prop = node.property ("name")) != 0) {
_name = prop->value();
/* used to set panner name with this, but no more */
- }
+ }
if ((prop = node.property ("id")) != 0) {
_id = prop->value ();
}
- size_t in_min = -1;
- size_t in_max = -1;
- size_t out_min = -1;
- size_t out_max = -1;
+ int in_min = -1;
+ int in_max = -1;
+ int out_min = -1;
+ int out_max = -1;
if ((prop = node.property ("iolimits")) != 0) {
- sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd",
+ sscanf (prop->value().c_str(), "%d,%d,%d,%d",
&in_min, &in_max, &out_min, &out_max);
- _input_minimum = ChanCount(_default_type, in_min);
- _input_maximum = ChanCount(_default_type, in_max);
- _output_minimum = ChanCount(_default_type, out_min);
- _output_maximum = ChanCount(_default_type, out_max);
+
+ /* Correct for the difference between the way we write things to session files and the
+ way things are described by ChanCount; see comments in io.h about what the different
+ ChanCount values mean. */
+
+ if (in_min < 0) {
+ _input_minimum = ChanCount::ZERO;
+ } else {
+ _input_minimum = ChanCount (_default_type, in_min);
+ }
+
+ if (in_max < 0) {
+ _input_maximum = ChanCount::INFINITE;
+ } else {
+ _input_maximum = ChanCount (_default_type, in_max);
+ }
+
+ if (out_min < 0) {
+ _output_minimum = ChanCount::ZERO;
+ } else {
+ _output_minimum = ChanCount (_default_type, out_min);
+ }
+
+ if (out_max < 0) {
+ _output_maximum = ChanCount::INFINITE;
+ } else {
+ _output_maximum = ChanCount (_default_type, out_max);
+ }
}
if ((prop = node.property ("gain")) != 0) {
for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
+ // Old school Panner.
if ((*iter)->name() == "Panner") {
if (_panner == 0) {
_panner = new Panner (_name, _session);
_panner->set_state (**iter);
}
+ if ((*iter)->name() == "Processor") {
+ if ((*iter)->property ("type") && ((*iter)->property ("type")->value() == "panner" ) ) {
+ if (_panner == 0) {
+ _panner = new Panner (_name, _session);
+ }
+ _panner->set_state (**iter);
+ }
+ }
+
if ((*iter)->name() == X_("Automation")) {
- set_automation_state (*(*iter)->children().front());
+ set_automation_state (*(*iter), Evoral::Parameter(GainAutomation));
}
if ((*iter)->name() == X_("controllable")) {
if ((prop = (*iter)->property("name")) != 0 && prop->value() == "gaincontrol") {
- _gain_control.set_state (**iter);
+ _gain_control->set_state (**iter);
}
}
}
port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
}
+ if( !_panner )
+ _panner = new Panner( _name, _session );
if (panners_legal) {
reset_panner ();
} else {
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)
{
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());
switch (type) {
case 'g':
- _gain_automation_curve.fast_simple_add (when, value);
+ _gain_control->list()->fast_simple_add (when, value);
break;
case 's':
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)
{
- const XMLProperty* prop;
- int num_inputs = 0;
- int num_outputs = 0;
+ XMLProperty const * prop;
+ uint32_t num_inputs = 0;
+ uint32_t 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) {
- Bundle* c = _session.bundle_by_name (prop->value());
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("in"), _("input"));
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
-
- if ((c = _session.bundle_by_name (_("in 1"))) == 0) {
- error << _("No input bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value())
- << endmsg;
- }
+ if (!c) {
+ return -1;
}
num_inputs = c->nchannels();
}
if ((prop = node.property ("output-connection")) != 0) {
- Bundle* c = _session.bundle_by_name (prop->value());
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
+ boost::shared_ptr<Bundle> c = find_possible_bundle(prop->value(), _("out"), _("output"));
- if ((c = _session.bundle_by_name (_("out 1"))) == 0) {
- error << _("No output bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value())
- << endmsg;
- }
+ if (!c) {
+ return -1;
}
num_outputs = c->nchannels ();
} else if ((prop = node.property ("outputs")) != 0) {
+
num_outputs = 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 (ChanCount (_default_type, num_inputs),
+ ChanCount (_default_type, num_outputs),
+ true, this)) {
+
error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
return -1;
}
int
IO::make_connections (const XMLNode& node)
{
+
const XMLProperty* prop;
if ((prop = node.property ("input-connection")) != 0) {
- Bundle* c = _session.bundle_by_name (prop->value());
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("in"), _("input"));
- if (c == 0) {
- error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
-
- if ((c = _session.bundle_by_name (_("in 1"))) == 0) {
- error << _("No input connections available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value())
- << endmsg;
- }
+ if (!c) {
+ return -1;
}
- use_input_bundle (*c, this);
+ connect_input_ports_to_bundle (c, this);
} else if ((prop = node.property ("inputs")) != 0) {
if (set_inputs (prop->value())) {
return -1;
}
}
-
- if ((prop = node.property ("output-bundle")) != 0) {
- Bundle* c = _session.bundle_by_name (prop->value());
-
- if (c == 0) {
- error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
- if ((c = _session.bundle_by_name (_("out 1"))) == 0) {
- error << _("No output bundles available as a replacement")
- << endmsg;
- return -1;
- } else {
- info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value())
- << endmsg;
- }
+ if ((prop = node.property ("output-connection")) != 0) {
+ boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("out"), _("output"));
+
+ if (!c) {
+ return -1;
}
-
- use_output_bundle (*c, this);
+
+ connect_output_ports_to_bundle (c, this);
} else if ((prop = node.property ("outputs")) != 0) {
if (set_outputs (prop->value())) {
return -1;
}
}
+
+ for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) {
+
+ if ((*i)->name() == "InputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = find_possible_bundle (prop->value(), _("in"), _("input"));
+ if (b) {
+ connect_input_ports_to_bundle (b, this);
+ }
+ }
+
+ } else if ((*i)->name() == "OutputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = find_possible_bundle (prop->value(), _("out"), _("output"));
+ if (b) {
+ connect_output_ports_to_bundle (b, this);
+ }
+ }
+ }
+ }
return 0;
}
return ports.size();
}
-int
-IO::set_name (string name, void* src)
+bool
+IO::set_name (const string& requested_name)
{
- if (name == _name) {
- return 0;
+ 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 */
if (replace_all (name, ":", "-")) {
}
for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- string current_name = i->short_name();
+ string current_name = i->name();
current_name.replace (current_name.find (_name), _name.length(), name);
i->set_name (current_name);
}
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- string current_name = i->short_name();
+ string current_name = i->name();
current_name.replace (current_name.find (_name), _name.length(), name);
i->set_name (current_name);
}
- _name = name;
- name_changed (src); /* EMIT SIGNAL */
+ bool const r = SessionObject::set_name(name);
+
+ setup_bundles_for_inputs_and_outputs ();
- return 0;
+ return r;
}
void
/* io lock not taken - must be protected by other means */
for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
+ if ((latency = i->total_latency ()) > max_latency) {
max_latency = latency;
}
}
/* io lock not taken - must be protected by other means */
for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
+ if ((latency = i->total_latency ()) > max_latency) {
max_latency = latency;
- }
+ }
}
return max_latency;
}
int
-IO::use_input_bundle (Bundle& c, void* src)
+IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
{
- uint32_t limit;
-
{
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
-
- limit = c.nchannels();
-
- drop_input_bundle ();
-
- // FIXME bundles only work for audio-only
- if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
- return -1;
- }
- /* first pass: check the current state to see what's correctly
- connected, and drop anything that we don't want.
- */
-
- for (uint32_t n = 0; n < limit; ++n) {
- const Bundle::PortList& pl = c.channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_inputs.port(n)->connected_to ((*i))) {
-
- /* clear any existing connections */
-
- _session.engine().disconnect (*_inputs.port(n));
-
- } else if (_inputs.port(n)->connected() > 1) {
-
- /* OK, it is connected to the port we want,
- but its also connected to other ports.
- Change that situation.
- */
-
- /* XXX could be optimized to not drop
- the one we want.
- */
-
- _session.engine().disconnect (*_inputs.port(n));
-
- }
+ c->connect (_bundle_for_inputs, _session.engine());
+
+ /* If this is a UserBundle, make a note of what we've done */
+
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ while (i != _bundles_connected_to_inputs.end() && i->bundle != ub) {
+ ++i;
}
- }
-
- /* second pass: connect all requested ports where necessary */
-
- for (uint32_t n = 0; n < limit; ++n) {
- const Bundle::PortList& pl = c.channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_inputs.port(n)->connected_to ((*i))) {
-
- if (_session.engine().connect (*i, _inputs.port(n)->name())) {
- return -1;
- }
- }
-
+
+ if (i == _bundles_connected_to_inputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_inputs.push_back (UserBundleInfo (this, ub));
}
}
-
- _input_bundle = &c;
-
- 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 */
}
int
-IO::use_output_bundle (Bundle& c, void* src)
+IO::disconnect_input_ports_from_bundle (boost::shared_ptr<Bundle> c, void* src)
{
- uint32_t limit;
-
{
BLOCK_PROCESS_CALLBACK ();
Glib::Mutex::Lock lm2 (io_lock);
- limit = c.nchannels();
+ c->disconnect (_bundle_for_inputs, _session.engine());
- drop_output_bundle ();
+ /* If this is a UserBundle, make a note of what we've done */
- // FIXME: audio-only
- if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) {
- return -1;
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ while (i != _bundles_connected_to_inputs.end() && i->bundle != ub) {
+ ++i;
+ }
+
+ if (i != _bundles_connected_to_inputs.end()) {
+ _bundles_connected_to_inputs.erase (i);
+ }
}
+ }
- /* first pass: check the current state to see what's correctly
- connected, and drop anything that we don't want.
- */
-
- for (uint32_t n = 0; n < limit; ++n) {
+ input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
+ return 0;
+}
- const Bundle::PortList& pl = c.channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_outputs.port(n)->connected_to ((*i))) {
+int
+IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock lm2 (io_lock);
- /* clear any existing connections */
+ c->connect (_bundle_for_outputs, _session.engine());
- _session.engine().disconnect (*_outputs.port(n));
+ /* If this is a UserBundle, make a note of what we've done */
- } else if (_outputs.port(n)->connected() > 1) {
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
- /* OK, it is connected to the port we want,
- but its also connected to other ports.
- Change that situation.
- */
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) {
+ ++i;
+ }
- /* XXX could be optimized to not drop
- the one we want.
- */
-
- _session.engine().disconnect (*_outputs.port(n));
- }
+ if (i == _bundles_connected_to_outputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_outputs.push_back (UserBundleInfo (this, ub));
}
}
+ }
- /* second pass: connect all requested ports where necessary */
+ output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
- for (uint32_t n = 0; n < limit; ++n) {
+ return 0;
+}
- const Bundle::PortList& pl = c.channel_ports (n);
-
- for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
-
- if (!_outputs.port(n)->connected_to ((*i))) {
-
- if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
- return -1;
- }
- }
- }
- }
+int
+IO::disconnect_output_ports_from_bundle (boost::shared_ptr<Bundle> c, void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock lm2 (io_lock);
- _output_bundle = &c;
+ c->disconnect (_bundle_for_outputs, _session.engine());
+
+ /* If this is a UserBundle, make a note of what we've done */
- 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));
- }
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
- output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) {
+ ++i;
+ }
+ if (i != _bundles_connected_to_outputs.end()) {
+ _bundles_connected_to_outputs.erase (i);
+ }
+ }
+ }
+
+ output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
return 0;
}
+
int
IO::disable_connecting ()
{
}
void
-IO::input_bundle_connection_changed (int ignored)
-{
- use_input_bundle (*_input_bundle, this);
-}
-
-void
-IO::input_bundle_configuration_changed ()
-{
- use_input_bundle (*_input_bundle, this);
-}
-
-void
-IO::output_bundle_connection_changed (int ignored)
+IO::bundle_changed (Bundle::Change c)
{
- use_output_bundle (*_output_bundle, this);
+ //XXX
+// connect_input_ports_to_bundle (_input_bundle, this);
}
void
-IO::output_bundle_configuration_changed ()
+IO::GainControl::set_value (float val)
{
- use_output_bundle (*_output_bundle, this);
-}
+ // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
+ if (val > 1.99526231f)
+ val = 1.99526231f;
-void
-IO::GainControllable::set_value (float val)
-{
- io.set_gain (direct_control_to_gain (val), this);
+ _io->set_gain (val, this);
+
+ AutomationControl::set_value(val);
}
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);
}
/**
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 ()
{
- Glib::Mutex::Lock lm (automation_lock);
- _gain_automation_curve.clear ();
- _panner->clear_automation ();
+ data().clear (); // clears gain automation
+ _panner->data().clear();
}
void
-IO::set_gain_automation_state (AutoState state)
+IO::set_parameter_automation_state (Evoral::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 (control_lock());
-void
-IO::set_gain_automation_style (AutoStyle style)
-{
- bool changed = false;
+ boost::shared_ptr<AutomationList> gain_auto
+ = boost::dynamic_pointer_cast<AutomationList>(_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 {
+ AutomatableControls::set_parameter_automation_state(param, state);
}
}
+
void
IO::inc_gain (gain_t factor, void *src)
{
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;
+ }
+
+ cerr << "set desired gain to " << val << " when curgain = " << _gain_control->get_value () << endl;
+
+ 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);
}
if (_session.transport_stopped()) {
- _effective_gain = val;
- _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->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();
+ if (which < _panner->npanners()) {
+ (*_panner).pan_control(which)->start_touch();
}
}
void
IO::end_pan_touch (uint32_t which)
{
- if (which < _panner->size()) {
- (*_panner)[which]->automation().stop_touch();
+ if (which < _panner->npanners()) {
+ (*_panner).pan_control(which)->stop_touch();
}
}
void
-IO::automation_snapshot (nframes_t now)
+IO::automation_snapshot (nframes_t now, bool force)
{
- if (last_automation_snapshot > now || (now - last_automation_snapshot) > _automation_interval) {
-
- if (gain_automation_recording()) {
- _gain_automation_curve.rt_add (now, gain());
- }
-
- _panner->snapshot (now);
+ AutomatableControls::automation_snapshot (now, force);
+ // XXX: This seems to be wrong.
+ // drobilla: shouldnt automation_snapshot for panner be called
+ // "automagically" because its an Automatable now ?
+ //
+ // we could dump this whole method then. <3
- last_automation_snapshot = now;
+ if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
+ _panner->automation_snapshot (now, force);
}
+
+ _panner->automation_snapshot (now, force);
+ _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->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);
}
+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) {
+ if (i->name() == buf) {
break;
}
}
}
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) {
+ if (i->name() == buf) {
break;
}
}
return n;
}
+void
+IO::set_active (bool yn)
+{
+ _active = yn;
+ active_changed(); /* EMIT SIGNAL */
+}
+
AudioPort*
IO::audio_input(uint32_t n) const
{
{
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);
+ }
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().update_total_latency (*i);
+ }
+}
+
+
+/**
+ * Setup bundles that describe our inputs and outputs. Also creates bundles if necessary.
+ */
+
+void
+IO::setup_bundles_for_inputs_and_outputs ()
+{
+ setup_bundle_for_inputs ();
+ setup_bundle_for_outputs ();
+}
+
+
+void
+IO::setup_bundle_for_inputs ()
+{
+ char buf[32];
+
+ if (!_bundle_for_inputs) {
+ _bundle_for_inputs.reset (new Bundle (true));
+ }
+
+ _bundle_for_inputs->suspend_signals ();
+
+ _bundle_for_inputs->set_type (default_type ());
+
+ _bundle_for_inputs->remove_channels ();
+
+ snprintf(buf, sizeof (buf), _("%s in"), _name.c_str());
+ _bundle_for_inputs->set_name (buf);
+ uint32_t const ni = inputs().num_ports();
+ for (uint32_t i = 0; i < ni; ++i) {
+ _bundle_for_inputs->add_channel (bundle_channel_name (i, ni));
+ _bundle_for_inputs->set_port (i, _session.engine().make_port_name_non_relative (inputs().port(i)->name()));
+ }
+
+ _bundle_for_inputs->resume_signals ();
+}
+
+
+void
+IO::setup_bundle_for_outputs ()
+{
+ char buf[32];
+
+ if (!_bundle_for_outputs) {
+ _bundle_for_outputs.reset (new Bundle (false));
+ }
+
+ _bundle_for_outputs->suspend_signals ();
+
+ _bundle_for_outputs->set_type (default_type ());
+
+ _bundle_for_outputs->remove_channels ();
+
+ snprintf(buf, sizeof (buf), _("%s out"), _name.c_str());
+ _bundle_for_outputs->set_name (buf);
+ uint32_t const no = outputs().num_ports();
+ for (uint32_t i = 0; i < no; ++i) {
+ _bundle_for_outputs->add_channel (bundle_channel_name (i, no));
+ _bundle_for_outputs->set_port (i, _session.engine().make_port_name_non_relative (outputs().port(i)->name()));
+ }
+
+ _bundle_for_outputs->resume_signals ();
+}
+
+
+/** @return Bundles connected to our inputs */
+BundleList
+IO::bundles_connected_to_inputs ()
+{
+ BundleList bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin(); i != _bundles_connected_to_inputs.end(); ++i) {
+ bundles.push_back (i->bundle);
}
- // phase_invert_changed (src); /* EMIT SIGNAL */
+
+ /* Session bundles */
+ boost::shared_ptr<ARDOUR::BundleList> b = _session.bundles ();
+ for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
+ if ((*i)->connected_to (_bundle_for_inputs, _session.engine())) {
+ bundles.push_back (*i);
+ }
+ }
+
+ /* Route bundles */
+ boost::shared_ptr<ARDOUR::RouteList> r = _session.get_routes ();
+ for (ARDOUR::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->bundle_for_outputs()->connected_to (_bundle_for_inputs, _session.engine())) {
+ bundles.push_back ((*i)->bundle_for_outputs());
+ }
+ }
+
+ return bundles;
}
+
+/* @return Bundles connected to our outputs */
+BundleList
+IO::bundles_connected_to_outputs ()
+{
+ BundleList bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin(); i != _bundles_connected_to_outputs.end(); ++i) {
+ bundles.push_back (i->bundle);
+ }
+
+ /* Session bundles */
+ boost::shared_ptr<ARDOUR::BundleList> b = _session.bundles ();
+ for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
+ if ((*i)->connected_to (_bundle_for_outputs, _session.engine())) {
+ bundles.push_back (*i);
+ }
+ }
+
+ /* Route bundles */
+ boost::shared_ptr<ARDOUR::RouteList> r = _session.get_routes ();
+ for (ARDOUR::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->bundle_for_inputs()->connected_to (_bundle_for_outputs, _session.engine())) {
+ bundles.push_back ((*i)->bundle_for_inputs());
+ }
+ }
+
+ return bundles;
+}
+
+
+IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr<UserBundle> b)
+{
+ bundle = b;
+ changed = b->Changed.connect (
+ sigc::mem_fun (*io, &IO::bundle_changed)
+ );
+}
+
+void
+IO::prepare_inputs (nframes_t nframes, nframes_t offset)
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+}
+
+void
+IO::flush_outputs (nframes_t nframes, nframes_t offset)
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+
+ /* Only run cycle_start() on output ports, because
+ inputs must be done in the correct processing order,
+ which requires interleaving with route processing.
+ */
+
+ (*i).flush_buffers (nframes, offset);
+ }
+
+}
+
+std::string
+IO::bundle_channel_name (uint32_t c, uint32_t n) const
+{
+ char buf[32];
+
+ switch (n) {
+ case 1:
+ return _("mono");
+ case 2:
+ return c == 0 ? _("L") : _("R");
+ default:
+ snprintf (buf, sizeof(buf), _("%d"), (c + 1));
+ return buf;
+ }
+
+ return "";
+}
+
+