2008-06-02 21:41:35 +00:00
|
|
|
/*
|
2009-10-14 16:10:01 +00:00
|
|
|
Copyright (C) 2000-2006 Paul Davis
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <locale.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
#include <glibmm.h>
|
2008-06-02 21:41:35 +00:00
|
|
|
#include <glibmm/thread.h>
|
|
|
|
|
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "pbd/xml++.h"
|
|
|
|
|
#include "pbd/replace_all.h"
|
|
|
|
|
#include "pbd/unknown_type.h"
|
2009-06-09 20:21:19 +00:00
|
|
|
#include "pbd/enumwriter.h"
|
2009-02-25 18:26:51 +00:00
|
|
|
|
|
|
|
|
#include "ardour/audioengine.h"
|
2009-06-09 20:21:19 +00:00
|
|
|
#include "ardour/buffer.h"
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "ardour/io.h"
|
|
|
|
|
#include "ardour/route.h"
|
|
|
|
|
#include "ardour/port.h"
|
|
|
|
|
#include "ardour/audio_port.h"
|
|
|
|
|
#include "ardour/midi_port.h"
|
|
|
|
|
#include "ardour/session.h"
|
|
|
|
|
#include "ardour/cycle_timer.h"
|
|
|
|
|
#include "ardour/buffer_set.h"
|
|
|
|
|
#include "ardour/meter.h"
|
|
|
|
|
#include "ardour/amp.h"
|
|
|
|
|
#include "ardour/user_bundle.h"
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
#include "i18n.h"
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
/*
|
2009-10-14 16:10:01 +00:00
|
|
|
A bug in OS X's cmath that causes isnan() and isinf() to be
|
2008-06-02 21:41:35 +00:00
|
|
|
"undeclared". the following works around that
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
const string IO::state_node_name = "IO";
|
|
|
|
|
bool IO::connecting_legal = false;
|
2009-12-19 20:26:31 +00:00
|
|
|
PBD::Signal0<int> IO::ConnectingLegal;
|
|
|
|
|
PBD::Signal1<void,ChanCount> IO::PortCountChanged;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/** @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).
|
|
|
|
|
*/
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::IO (Session& s, const string& name, Direction dir, DataType default_type)
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
: SessionObject (s, name)
|
2009-06-09 20:21:19 +00:00
|
|
|
, _direction (dir)
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
, _default_type (default_type)
|
|
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
_active = true;
|
2008-06-02 21:41:35 +00:00
|
|
|
pending_state_node = 0;
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IO::IO (Session& s, const XMLNode& node, DataType dt)
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
: SessionObject(s, "unnamed io")
|
2009-06-09 20:21:19 +00:00
|
|
|
, _direction (Input)
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
, _default_type (dt)
|
|
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
_active = true;
|
|
|
|
|
pending_state_node = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-10-15 18:56:11 +00:00
|
|
|
set_state (node, Stateful::loading_state_version);
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IO::~IO ()
|
|
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
|
|
|
|
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.engine().unregister_port (*i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-04-23 17:48:37 +00:00
|
|
|
IO::silence (nframes_t nframes)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
/* io_lock, not taken: function must be called from Session::process() calltree */
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2009-04-23 17:48:37 +00:00
|
|
|
i->get_buffer(nframes).silence (nframes);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::check_bundles_connected ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
check_bundles (_bundles_connected, ports());
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-12-17 18:24:23 +00:00
|
|
|
IO::check_bundles (std::vector<UserBundleInfo*>& list, const PortSet& ports)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-12-17 18:24:23 +00:00
|
|
|
std::vector<UserBundleInfo*> new_list;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-12-17 18:24:23 +00:00
|
|
|
for (std::vector<UserBundleInfo*>::iterator i = list.begin(); i != list.end(); ++i) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-12-17 18:24:23 +00:00
|
|
|
uint32_t const N = (*i)->bundle->nchannels ();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_ports.num_ports (default_type()) < N) {
|
2008-06-02 21:41:35 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ok = true;
|
2008-09-10 15:03:30 +00:00
|
|
|
|
2009-01-14 23:54:46 +00:00
|
|
|
for (uint32_t j = 0; j < N; ++j) {
|
2008-06-02 21:41:35 +00:00
|
|
|
/* Every port on bundle channel j must be connected to our input j */
|
2009-12-17 18:24:23 +00:00
|
|
|
Bundle::PortList const pl = (*i)->bundle->channel_ports (j);
|
2008-06-02 21:41:35 +00:00
|
|
|
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 {
|
2009-12-17 18:24:23 +00:00
|
|
|
delete *i;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list = new_list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::disconnect (Port* our_port, string other_port, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
if (other_port.length() == 0 || our_port == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
{
|
2008-06-02 21:41:35 +00:00
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* check that our_port is really one of ours */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ( ! _ports.contains(our_port)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* disconnect it from the source */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-02-10 00:49:19 +00:00
|
|
|
if (our_port->disconnect (other_port)) {
|
2009-06-09 20:21:19 +00:00
|
|
|
error << string_compose(_("IO: cannot disconnect port %1 from %2"), our_port->name(), other_port) << endmsg;
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
check_bundles_connected ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (ConnectionsChanged, src); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.set_dirty ();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::connect (Port* our_port, string other_port, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
if (other_port.length() == 0 || our_port == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* check that our_port is really one of ours */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ( ! _ports.contains(our_port) ) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* connect it to the source */
|
|
|
|
|
|
2009-02-10 00:49:19 +00:00
|
|
|
if (our_port->connect (other_port)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (ConnectionsChanged, src); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.set_dirty ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::remove_port (Port* port, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
IOChange change (NoChange);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_ports.remove(port)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
change = IOChange (change|ConfigurationChanged);
|
|
|
|
|
|
|
|
|
|
if (port->connected()) {
|
|
|
|
|
change = IOChange (change|ConnectionsChanged);
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
_session.engine().unregister_port (*port);
|
2009-06-09 20:21:19 +00:00
|
|
|
check_bundles_connected ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
PortCountChanged (n_ports()); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-20 00:22:09 +00:00
|
|
|
if (change & ConfigurationChanged) {
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (change != NoChange) {
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (change, src);
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.set_dirty ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Add an output port.
|
|
|
|
|
*
|
|
|
|
|
* @param destination Name of input port to connect new port to.
|
|
|
|
|
* @param src Source for emitted ConfigurationChanged signal.
|
|
|
|
|
* @param type Data type of port. Default value (NIL) will use this IO's default type.
|
|
|
|
|
*/
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::add_port (string destination, void* src, DataType type)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
Port* our_port;
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (type == DataType::NIL) {
|
2008-06-02 21:41:35 +00:00
|
|
|
type = _default_type;
|
2009-06-09 20:21:19 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
{
|
2008-06-02 21:41:35 +00:00
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* Create a new output port */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
string portname = build_legal_port_name (type);
|
|
|
|
|
|
|
|
|
|
if (_direction == Input) {
|
|
|
|
|
if ((our_port = _session.engine().register_input_port (type, portname)) == 0) {
|
|
|
|
|
error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ((our_port = _session.engine().register_output_port (type, portname)) == 0) {
|
|
|
|
|
error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
|
|
|
|
|
_ports.add (our_port);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
PortCountChanged (n_ports()); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (destination.length()) {
|
2009-02-10 00:49:19 +00:00
|
|
|
if (our_port->connect (destination)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
// pan_changed (src); /* EMIT SIGNAL */
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (ConfigurationChanged, src); /* EMIT SIGNAL */
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.set_dirty ();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::disconnect (void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-10-14 16:10:01 +00:00
|
|
|
{
|
2008-06-02 21:41:35 +00:00
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
|
|
|
|
i->disconnect_all ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
check_bundles_connected ();
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (ConnectionsChanged, src); /* EMIT SIGNAL */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
return 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
bool
|
2009-07-21 15:55:17 +00:00
|
|
|
IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
Port* port = 0;
|
|
|
|
|
bool changed = false;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
const size_t n = count.get(*t);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* remove unused ports */
|
|
|
|
|
for (size_t i = n_ports().get(*t); i > n; --i) {
|
|
|
|
|
port = _ports.port(*t, i-1);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
assert(port);
|
|
|
|
|
_ports.remove(port);
|
|
|
|
|
_session.engine().unregister_port (*port);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed = true;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* create any necessary new ports */
|
|
|
|
|
while (n_ports().get(*t) < n) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
string portname = build_legal_port_name (*t);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
try {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_direction == Input) {
|
2009-02-11 00:58:24 +00:00
|
|
|
if ((port = _session.engine().register_input_port (*t, portname)) == 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
} else {
|
2009-02-11 00:58:24 +00:00
|
|
|
if ((port = _session.engine().register_output_port (*t, portname)) == 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
catch (AudioEngine::PortRegistrationFailure& err) {
|
|
|
|
|
/* pass it on */
|
|
|
|
|
throw AudioEngine::PortRegistrationFailure();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
_ports.add (port);
|
2008-06-02 21:41:35 +00:00
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (changed) {
|
2009-06-09 20:21:19 +00:00
|
|
|
check_bundles_connected ();
|
|
|
|
|
PortCountChanged (n_ports()); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
_session.set_dirty ();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (clear) {
|
|
|
|
|
/* disconnect all existing ports so that we get a fresh start */
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2009-02-10 00:49:19 +00:00
|
|
|
i->disconnect_all ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::ensure_ports (ChanCount count, bool clear, bool lockit, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
bool changed = false;
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (count == n_ports() && !clear) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (lockit) {
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
Glib::Mutex::Lock im (io_lock);
|
2009-06-09 20:21:19 +00:00
|
|
|
changed = ensure_ports_locked (count, clear, src);
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2009-06-09 20:21:19 +00:00
|
|
|
changed = ensure_ports_locked (count, clear, src);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
2009-06-09 20:21:19 +00:00
|
|
|
this->changed (ConfigurationChanged, src); /* EMIT SIGNAL */
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2009-06-09 20:21:19 +00:00
|
|
|
_session.set_dirty ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::ensure_io (ChanCount count, bool clear, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
return ensure_ports (count, clear, true, src);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
|
IO::get_state (void)
|
|
|
|
|
{
|
|
|
|
|
return state (true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XMLNode&
|
2009-07-21 15:55:17 +00:00
|
|
|
IO::state (bool /*full_state*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
XMLNode* node = new XMLNode (state_node_name);
|
|
|
|
|
char buf[64];
|
|
|
|
|
string str;
|
|
|
|
|
vector<string>::iterator ci;
|
|
|
|
|
int n;
|
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
|
|
|
|
|
|
|
|
|
node->add_property("name", _name);
|
|
|
|
|
id().print (buf, sizeof (buf));
|
|
|
|
|
node->add_property("id", buf);
|
2009-06-09 20:21:19 +00:00
|
|
|
node->add_property ("direction", enum_2_string (_direction));
|
|
|
|
|
node->add_property ("default-type", _default_type.to_string());
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-12-17 18:24:23 +00:00
|
|
|
for (std::vector<UserBundleInfo*>::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) {
|
2009-06-09 20:21:19 +00:00
|
|
|
XMLNode* n = new XMLNode ("Bundle");
|
2009-12-17 18:24:23 +00:00
|
|
|
n->add_property ("name", (*i)->bundle->name ());
|
2008-06-02 21:41:35 +00:00
|
|
|
node->add_child_nocopy (*n);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
vector<string> connections;
|
|
|
|
|
|
2009-06-09 21:30:32 +00:00
|
|
|
XMLNode* pnode = new XMLNode (X_("Port"));
|
2009-06-09 20:21:19 +00:00
|
|
|
pnode->add_property (X_("type"), i->type().to_string());
|
2009-06-23 02:43:35 +00:00
|
|
|
pnode->add_property (X_("name"), i->name());
|
2009-06-09 20:21:19 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (i->get_connections (connections)) {
|
|
|
|
|
|
|
|
|
|
for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
|
2009-06-09 20:21:19 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* 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.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-23 02:43:35 +00:00
|
|
|
XMLNode* cnode = new XMLNode (X_("Connection"));
|
|
|
|
|
|
|
|
|
|
cnode->add_property (X_("other"), _session.engine().make_port_name_relative (*ci));
|
|
|
|
|
pnode->add_child_nocopy (*cnode);
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
node->add_child_nocopy (*pnode);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-10-15 00:57:55 +00:00
|
|
|
IO::set_state (const XMLNode& node, int version)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-10-15 00:57:55 +00:00
|
|
|
/* callers for version < 3000 need to call set_state_2X directly, as A3 IOs
|
|
|
|
|
* are input OR output, not both, so the direction needs to be specified
|
|
|
|
|
* by the caller.
|
|
|
|
|
*/
|
|
|
|
|
assert (version >= 3000);
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
const XMLProperty* prop;
|
|
|
|
|
XMLNodeConstIterator iter;
|
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
|
|
|
|
|
/* force use of non-localized representation of decimal point,
|
|
|
|
|
since we use it a lot in XML files and so forth.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (node.name() != state_node_name) {
|
|
|
|
|
error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((prop = node.property ("name")) != 0) {
|
2009-06-09 20:21:19 +00:00
|
|
|
set_name (prop->value());
|
2009-02-14 17:28:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((prop = node.property (X_("default-type"))) != 0) {
|
|
|
|
|
_default_type = DataType(prop->value());
|
|
|
|
|
assert(_default_type != DataType::NIL);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((prop = node.property ("id")) != 0) {
|
|
|
|
|
_id = prop->value ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((prop = node.property ("direction")) != 0) {
|
|
|
|
|
_direction = (Direction) string_2_enum (prop->value(), _direction);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if (create_ports (node, version)) {
|
2009-06-09 20:21:19 +00:00
|
|
|
return -1;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (connecting_legal) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if (make_connections (node, version, false)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2009-06-23 02:43:35 +00:00
|
|
|
|
|
|
|
|
pending_state_node = new XMLNode (node);
|
2009-10-15 00:57:55 +00:00
|
|
|
pending_state_node_version = version;
|
|
|
|
|
pending_state_node_in = false;
|
2009-12-21 18:23:07 +00:00
|
|
|
ConnectingLegal.connect_same_thread (connection_legal_c, boost::bind (&IO::connecting_became_legal, this));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
int
|
|
|
|
|
IO::set_state_2X (const XMLNode& node, int version, bool in)
|
|
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
|
|
|
|
XMLNodeConstIterator iter;
|
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
|
|
|
|
|
/* force use of non-localized representation of decimal point,
|
|
|
|
|
since we use it a lot in XML files and so forth.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (node.name() != state_node_name) {
|
|
|
|
|
error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((prop = node.property ("name")) != 0) {
|
|
|
|
|
set_name (prop->value());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("default-type"))) != 0) {
|
|
|
|
|
_default_type = DataType(prop->value());
|
|
|
|
|
assert(_default_type != DataType::NIL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((prop = node.property ("id")) != 0) {
|
|
|
|
|
_id = prop->value ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_direction = in ? Input : Output;
|
|
|
|
|
|
|
|
|
|
if (create_ports (node, version)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (connecting_legal) {
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if (make_connections_2X (node, version, in)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
pending_state_node = new XMLNode (node);
|
|
|
|
|
pending_state_node_version = version;
|
|
|
|
|
pending_state_node_in = in;
|
2009-12-21 18:23:07 +00:00
|
|
|
ConnectingLegal.connect_same_thread (connection_legal_c, boost::bind (&IO::connecting_became_legal, this));
|
2009-10-15 00:57:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
|
|
|
|
IO::connecting_became_legal ()
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
assert (pending_state_node);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
connection_legal_c.disconnect ();
|
|
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
ret = make_connections (*pending_state_node, pending_state_node_version, pending_state_node_in);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
delete pending_state_node;
|
|
|
|
|
pending_state_node = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
boost::shared_ptr<Bundle>
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::find_possible_bundle (const string &desired_name)
|
2008-09-10 15:03:30 +00:00
|
|
|
{
|
|
|
|
|
static const string digits = "0123456789";
|
2009-06-09 20:21:19 +00:00
|
|
|
const string &default_name = (_direction == Input ? _("in") : _("out"));
|
|
|
|
|
const string &bundle_type_name = (_direction == Input ? _("input") : _("output"));
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
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;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
// see if it's a stereo connection e.g. "in 3+4"
|
|
|
|
|
|
|
|
|
|
if (last_non_digit_pos > 1 && desired_name[last_non_digit_pos] == '+') {
|
|
|
|
|
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) {
|
2010-02-23 20:25:53 +00:00
|
|
|
int left_bundle_number = 0;
|
2008-09-10 15:03:30 +00:00
|
|
|
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;
|
2009-02-26 00:58:35 +00:00
|
|
|
while ((mask <= bundle_number) && (mask <<= 1)) {}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
// "wrap" bundle number into largest possible power of 2
|
2008-09-10 15:03:30 +00:00
|
|
|
// that works...
|
|
|
|
|
|
|
|
|
|
while (mask) {
|
|
|
|
|
|
|
|
|
|
if (bundle_number & mask) {
|
|
|
|
|
bundle_number &= ~mask;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
stringstream s;
|
|
|
|
|
s << default_name << " " << bundle_number + 1;
|
|
|
|
|
|
|
|
|
|
if (stereo) {
|
|
|
|
|
s << "+" << bundle_number + 2;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
2009-10-21 00:15:42 +00:00
|
|
|
IO::get_port_counts_2X (XMLNode const & node, int /*version*/, ChanCount& n, boost::shared_ptr<Bundle>& /*c*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-10-15 00:57:55 +00:00
|
|
|
XMLProperty const * prop;
|
|
|
|
|
XMLNodeList children = node.children ();
|
|
|
|
|
|
|
|
|
|
uint32_t n_audio = 0;
|
2009-10-19 14:13:03 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
|
|
|
|
|
|
|
|
|
|
if ((prop = node.property ("inputs")) != 0 && _direction == Input) {
|
|
|
|
|
n_audio = count (prop->value().begin(), prop->value().end(), '{');
|
2010-03-28 19:07:23 +00:00
|
|
|
} else if ((prop = node.property ("input-connection")) != 0 && _direction == Input) {
|
|
|
|
|
n_audio = 1;
|
2009-10-15 00:57:55 +00:00
|
|
|
} else if ((prop = node.property ("outputs")) != 0 && _direction == Output) {
|
|
|
|
|
n_audio = count (prop->value().begin(), prop->value().end(), '{');
|
2010-03-28 19:07:23 +00:00
|
|
|
} else if ((prop = node.property ("output-connection")) != 0 && _direction == Output) {
|
|
|
|
|
n_audio = 2;
|
2009-10-15 00:57:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChanCount cnt;
|
|
|
|
|
cnt.set_audio (n_audio);
|
|
|
|
|
n = ChanCount::max (n, cnt);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
IO::get_port_counts (const XMLNode& node, int version, ChanCount& n, boost::shared_ptr<Bundle>& c)
|
|
|
|
|
{
|
|
|
|
|
if (version < 3000) {
|
|
|
|
|
return get_port_counts_2X (node, version, n, c);
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
XMLProperty const * prop;
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
XMLNodeConstIterator iter;
|
2009-06-09 20:21:19 +00:00
|
|
|
uint32_t n_audio = 0;
|
|
|
|
|
uint32_t n_midi = 0;
|
|
|
|
|
ChanCount cnt;
|
2009-05-16 02:51:17 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
n = n_ports();
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((prop = node.property ("connection")) != 0) {
|
|
|
|
|
|
|
|
|
|
if ((c = find_possible_bundle (prop->value())) != 0) {
|
|
|
|
|
n = ChanCount::max (n, ChanCount(c->type(), c->nchannels()));
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
return 0;
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
|
2008-09-10 15:03:30 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((*iter)->name() == X_("Bundle")) {
|
|
|
|
|
if ((c = find_possible_bundle (prop->value())) != 0) {
|
|
|
|
|
n = ChanCount::max (n, ChanCount(c->type(), c->nchannels()));
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-03-01 19:45:35 +00:00
|
|
|
}
|
2008-09-10 15:03:30 +00:00
|
|
|
|
2009-06-09 21:30:32 +00:00
|
|
|
if ((*iter)->name() == X_("Port")) {
|
2009-06-09 20:21:19 +00:00
|
|
|
prop = (*iter)->property (X_("type"));
|
2008-09-10 15:03:30 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (!prop) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prop->value() == X_("audio")) {
|
|
|
|
|
cnt.set_audio (++n_audio);
|
|
|
|
|
} else if (prop->value() == X_("midi")) {
|
|
|
|
|
cnt.set_midi (++n_midi);
|
|
|
|
|
}
|
2009-03-01 19:45:35 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
n = ChanCount::max (n, cnt);
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-10-15 00:57:55 +00:00
|
|
|
IO::create_ports (const XMLNode& node, int version)
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
ChanCount n;
|
|
|
|
|
boost::shared_ptr<Bundle> c;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
get_port_counts (node, version, n, c);
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (ensure_ports (n, true, true, this)) {
|
|
|
|
|
error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
|
|
|
|
|
return -1;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* XXX use c */
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2009-10-15 00:57:55 +00:00
|
|
|
IO::make_connections (const XMLNode& node, int version, bool in)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-10-15 00:57:55 +00:00
|
|
|
if (version < 3000) {
|
|
|
|
|
return make_connections_2X (node, version, in);
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
const XMLProperty* prop;
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) {
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((*i)->name() == "Bundle") {
|
2008-06-02 21:41:35 +00:00
|
|
|
XMLProperty const * prop = (*i)->property ("name");
|
|
|
|
|
if (prop) {
|
2009-06-09 20:21:19 +00:00
|
|
|
boost::shared_ptr<Bundle> b = find_possible_bundle (prop->value());
|
2008-06-02 21:41:35 +00:00
|
|
|
if (b) {
|
2009-06-09 20:21:19 +00:00
|
|
|
connect_ports_to_bundle (b, this);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 21:30:32 +00:00
|
|
|
if ((*i)->name() == "Port") {
|
2009-06-23 02:43:35 +00:00
|
|
|
|
|
|
|
|
prop = (*i)->property (X_("name"));
|
|
|
|
|
|
|
|
|
|
if (!prop) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-23 02:43:35 +00:00
|
|
|
Port* p = port_by_name (prop->value());
|
|
|
|
|
|
|
|
|
|
if (p) {
|
2009-10-14 16:10:01 +00:00
|
|
|
for (XMLNodeConstIterator c = (*i)->children().begin(); c != (*i)->children().end(); ++c) {
|
2009-06-23 02:43:35 +00:00
|
|
|
|
|
|
|
|
XMLNode* cnode = (*c);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-23 02:43:35 +00:00
|
|
|
if (cnode->name() != X_("Connection")) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-23 02:43:35 +00:00
|
|
|
if ((prop = cnode->property (X_("other"))) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-23 02:43:35 +00:00
|
|
|
if (prop) {
|
|
|
|
|
p->connect (prop->value());
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
|
|
|
|
|
int
|
2009-10-21 00:15:42 +00:00
|
|
|
IO::make_connections_2X (const XMLNode& node, int /*version*/, bool in)
|
2009-10-15 00:57:55 +00:00
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
|
|
|
|
|
|
|
|
|
/* XXX: bundles ("connections" as was) */
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((prop = node.property ("inputs")) != 0 && in) {
|
|
|
|
|
|
|
|
|
|
string::size_type ostart = 0;
|
|
|
|
|
string::size_type start = 0;
|
|
|
|
|
string::size_type end = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
int n;
|
|
|
|
|
vector<string> ports;
|
|
|
|
|
|
|
|
|
|
string const str = prop->value ();
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
while ((start = str.find_first_of ('{', ostart)) != string::npos) {
|
|
|
|
|
start += 1;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((end = str.find_first_of ('}', start)) == string::npos) {
|
|
|
|
|
error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
|
|
|
|
|
error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
return -1;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
} else if (n > 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int x = 0; x < n; ++x) {
|
|
|
|
|
/* XXX: this is a bit of a hack; need to check if it's always valid */
|
2009-10-20 00:50:43 +00:00
|
|
|
string::size_type const p = ports[x].find ("/out");
|
2009-10-15 00:57:55 +00:00
|
|
|
if (p != string::npos) {
|
|
|
|
|
ports[x].replace (p, 4, "/audio_out");
|
|
|
|
|
}
|
|
|
|
|
nth(i)->connect (ports[x]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
ostart = end+1;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((prop = node.property ("outputs")) != 0 && !in) {
|
|
|
|
|
|
|
|
|
|
string::size_type ostart = 0;
|
|
|
|
|
string::size_type start = 0;
|
|
|
|
|
string::size_type end = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
int n;
|
|
|
|
|
vector<string> ports;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
string const str = prop->value ();
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
while ((start = str.find_first_of ('{', ostart)) != string::npos) {
|
|
|
|
|
start += 1;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((end = str.find_first_of ('}', start)) == string::npos) {
|
|
|
|
|
error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
|
|
|
|
|
error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
return -1;
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
} else if (n > 0) {
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
for (int x = 0; x < n; ++x) {
|
|
|
|
|
/* XXX: this is a bit of a hack; need to check if it's always valid */
|
2009-10-20 00:50:43 +00:00
|
|
|
string::size_type const p = ports[x].find ("/in");
|
2009-10-15 00:57:55 +00:00
|
|
|
if (p != string::npos) {
|
|
|
|
|
ports[x].replace (p, 3, "/audio_in");
|
|
|
|
|
}
|
|
|
|
|
nth(i)->connect (ports[x]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-20 00:50:43 +00:00
|
|
|
|
2009-10-15 00:57:55 +00:00
|
|
|
ostart = end+1;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::set_ports (const string& str)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
vector<string> ports;
|
|
|
|
|
int i;
|
|
|
|
|
int n;
|
|
|
|
|
uint32_t nports;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((nports = count (str.begin(), str.end(), '{')) == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME: audio-only
|
2009-06-09 20:21:19 +00:00
|
|
|
if (ensure_ports (ChanCount(DataType::AUDIO, nports), true, true, this)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string::size_type start, end, ostart;
|
|
|
|
|
|
|
|
|
|
ostart = 0;
|
|
|
|
|
start = 0;
|
|
|
|
|
end = 0;
|
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
|
|
while ((start = str.find_first_of ('{', ostart)) != string::npos) {
|
|
|
|
|
start += 1;
|
|
|
|
|
|
|
|
|
|
if ((end = str.find_first_of ('}', start)) == string::npos) {
|
|
|
|
|
error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
|
|
|
|
|
error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
|
|
|
|
|
|
|
|
|
|
return -1;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} else if (n > 0) {
|
|
|
|
|
|
|
|
|
|
for (int x = 0; x < n; ++x) {
|
2009-06-09 20:21:19 +00:00
|
|
|
connect (nth (i), ports[x], this);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ostart = end+1;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
IO::parse_io_string (const string& str, vector<string>& ports)
|
|
|
|
|
{
|
|
|
|
|
string::size_type pos, opos;
|
|
|
|
|
|
|
|
|
|
if (str.length() == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
|
opos = 0;
|
|
|
|
|
|
|
|
|
|
ports.clear ();
|
|
|
|
|
|
|
|
|
|
while ((pos = str.find_first_of (',', opos)) != string::npos) {
|
|
|
|
|
ports.push_back (str.substr (opos, pos - opos));
|
|
|
|
|
opos = pos + 1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (opos < str.length()) {
|
|
|
|
|
ports.push_back (str.substr(opos));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ports.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
IO::parse_gain_string (const string& str, vector<string>& ports)
|
|
|
|
|
{
|
|
|
|
|
string::size_type pos, opos;
|
|
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
|
opos = 0;
|
|
|
|
|
ports.clear ();
|
|
|
|
|
|
|
|
|
|
while ((pos = str.find_first_of (',', opos)) != string::npos) {
|
|
|
|
|
ports.push_back (str.substr (opos, pos - opos));
|
|
|
|
|
opos = pos + 1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (opos < str.length()) {
|
|
|
|
|
ports.push_back (str.substr(opos));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ports.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
IO::set_name (const string& requested_name)
|
|
|
|
|
{
|
2009-06-25 20:46:39 +00:00
|
|
|
string name = requested_name;
|
|
|
|
|
|
2010-02-11 17:08:34 +00:00
|
|
|
if (_name == name) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* replace all colons in the name. i wish we didn't have to do this */
|
|
|
|
|
|
|
|
|
|
if (replace_all (name, ":", "-")) {
|
|
|
|
|
warning << _("you cannot use colons to name objects with I/O connections") << endmsg;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2009-01-21 02:27:21 +00:00
|
|
|
string current_name = i->name();
|
2010-02-11 23:10:29 +00:00
|
|
|
current_name.replace (current_name.find (_name), _name.val().length(), name);
|
2008-06-02 21:41:35 +00:00
|
|
|
i->set_name (current_name);
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-25 20:46:39 +00:00
|
|
|
bool const r = SessionObject::set_name (name);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-11-25 23:24:02 +00:00
|
|
|
setup_bundle ();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
IO::set_port_latency (nframes_t nframes)
|
|
|
|
|
{
|
|
|
|
|
Glib::Mutex::Lock lm (io_lock);
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2008-06-02 21:41:35 +00:00
|
|
|
i->set_latency (nframes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nframes_t
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::latency () const
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
nframes_t max_latency;
|
|
|
|
|
nframes_t latency;
|
|
|
|
|
|
|
|
|
|
max_latency = 0;
|
|
|
|
|
|
|
|
|
|
/* io lock not taken - must be protected by other means */
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
2009-01-21 02:27:21 +00:00
|
|
|
if ((latency = i->total_latency ()) > max_latency) {
|
2008-06-02 21:41:35 +00:00
|
|
|
max_latency = latency;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return max_latency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::update_port_total_latencies ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
/* io_lock, not taken: function must be called from Session::process() calltree */
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
|
|
|
|
_session.engine().update_total_latency (*i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
int
|
|
|
|
|
IO::connect_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
Glib::Mutex::Lock lm2 (io_lock);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
c->connect (_bundle, _session.engine());
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* If this is a UserBundle, make a note of what we've done */
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
|
|
|
|
|
if (ub) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* See if we already know about this one */
|
2009-12-17 18:24:23 +00:00
|
|
|
std::vector<UserBundleInfo*>::iterator i = _bundles_connected.begin();
|
|
|
|
|
while (i != _bundles_connected.end() && (*i)->bundle != ub) {
|
2009-06-09 20:21:19 +00:00
|
|
|
++i;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (i == _bundles_connected.end()) {
|
|
|
|
|
/* We don't, so make a note */
|
2009-12-17 18:24:23 +00:00
|
|
|
_bundles_connected.push_back (new UserBundleInfo (this, ub));
|
2009-06-09 20:21:19 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
|
|
|
|
|
return 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
int
|
|
|
|
|
IO::disconnect_ports_from_bundle (boost::shared_ptr<Bundle> c, void* src)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
{
|
|
|
|
|
BLOCK_PROCESS_CALLBACK ();
|
|
|
|
|
Glib::Mutex::Lock lm2 (io_lock);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
c->disconnect (_bundle, _session.engine());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/* If this is a UserBundle, make a note of what we've done */
|
2009-01-30 07:40:13 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
|
|
|
|
|
if (ub) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-12-17 18:24:23 +00:00
|
|
|
std::vector<UserBundleInfo*>::iterator i = _bundles_connected.begin();
|
|
|
|
|
while (i != _bundles_connected.end() && (*i)->bundle != ub) {
|
2009-06-09 20:21:19 +00:00
|
|
|
++i;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (i != _bundles_connected.end()) {
|
2009-12-17 18:24:23 +00:00
|
|
|
delete *i;
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundles_connected.erase (i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
|
|
|
|
|
return 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
int
|
|
|
|
|
IO::disable_connecting ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
connecting_legal = false;
|
|
|
|
|
return 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
int
|
|
|
|
|
IO::enable_connecting ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
connecting_legal = true;
|
2009-12-22 23:02:44 +00:00
|
|
|
boost::optional<int> r = ConnectingLegal ();
|
|
|
|
|
return r.get_value_or (0);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-07-21 15:55:17 +00:00
|
|
|
IO::bundle_changed (Bundle::Change /*c*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
//XXX
|
|
|
|
|
// connect_input_ports_to_bundle (_input_bundle, this);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
string
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::build_legal_port_name (DataType type)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
const int name_size = jack_port_name_size();
|
|
|
|
|
int limit;
|
|
|
|
|
string suffix;
|
|
|
|
|
|
|
|
|
|
if (type == DataType::AUDIO) {
|
|
|
|
|
suffix = _("audio");
|
|
|
|
|
} else if (type == DataType::MIDI) {
|
|
|
|
|
suffix = _("midi");
|
|
|
|
|
} else {
|
|
|
|
|
throw unknown_type();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-08-10 19:29:29 +00:00
|
|
|
/* note that if "in" or "out" are translated it will break a session
|
|
|
|
|
across locale switches because a port's connection list will
|
|
|
|
|
show (old) translated names, but the current port name will
|
|
|
|
|
use the (new) translated name.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_direction == Input) {
|
2009-08-10 19:29:29 +00:00
|
|
|
suffix += X_("_in");
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2009-08-10 19:29:29 +00:00
|
|
|
suffix += X_("_out");
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-05-16 02:51:17 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
// 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];
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-02-11 23:10:29 +00:00
|
|
|
snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.val().c_str(), suffix.c_str());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
int port_number = find_port_hole (buf1);
|
2008-06-02 21:41:35 +00:00
|
|
|
snprintf (buf2, name_size+1, "%s %d", buf1, port_number);
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return string (buf2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::find_port_hole (const char* base)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
/* CALLER MUST HOLD IO LOCK */
|
|
|
|
|
|
|
|
|
|
uint32_t n;
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_ports.empty()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we only allow up to 4 characters for the port number
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (n = 1; n < 9999; ++n) {
|
|
|
|
|
char buf[jack_port_name_size()];
|
2009-06-09 20:21:19 +00:00
|
|
|
PortSet::iterator i = _ports.begin();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
for ( ; i != _ports.end(); ++i) {
|
2009-01-21 02:27:21 +00:00
|
|
|
if (i->name() == buf) {
|
2008-06-02 21:41:35 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (i == _ports.end()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AudioPort*
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::audio(uint32_t n) const
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
return _ports.nth_audio_port (n);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MidiPort*
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::midi(uint32_t n) const
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
return _ports.nth_midi_port (n);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2009-11-25 23:24:02 +00:00
|
|
|
* Setup a bundle that describe our inputs or outputs. Also creates the bundle if necessary.
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2009-11-25 23:24:02 +00:00
|
|
|
IO::setup_bundle ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
char buf[32];
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (!_bundle) {
|
2009-08-02 00:33:50 +00:00
|
|
|
_bundle.reset (new Bundle (_direction == Input));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundle->suspend_signals ();
|
2009-02-09 03:18:10 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundle->set_type (default_type ());
|
2009-02-09 03:18:10 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundle->remove_channels ();
|
2009-02-09 03:18:10 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_direction == Input) {
|
2010-02-11 23:10:29 +00:00
|
|
|
snprintf(buf, sizeof (buf), _("%s in"), _name.val().c_str());
|
2009-06-09 20:21:19 +00:00
|
|
|
} else {
|
2010-02-11 23:10:29 +00:00
|
|
|
snprintf(buf, sizeof (buf), _("%s out"), _name.val().c_str());
|
2009-02-09 03:18:10 +00:00
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundle->set_name (buf);
|
|
|
|
|
uint32_t const ni = _ports.num_ports();
|
|
|
|
|
for (uint32_t i = 0; i < ni; ++i) {
|
|
|
|
|
_bundle->add_channel (bundle_channel_name (i, ni));
|
|
|
|
|
_bundle->set_port (i, _session.engine().make_port_name_non_relative (_ports.port(i)->name()));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2009-02-09 03:18:10 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
_bundle->resume_signals ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
/** @return Bundles connected to our ports */
|
2009-01-25 06:47:11 +00:00
|
|
|
BundleList
|
2009-06-09 20:21:19 +00:00
|
|
|
IO::bundles_connected ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-01-25 06:47:11 +00:00
|
|
|
BundleList bundles;
|
2009-08-02 00:33:50 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* User bundles */
|
2009-12-17 18:24:23 +00:00
|
|
|
for (std::vector<UserBundleInfo*>::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) {
|
|
|
|
|
bundles.push_back ((*i)->bundle);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-02-11 02:14:01 +00:00
|
|
|
/* Session bundles */
|
2009-01-25 06:47:11 +00:00
|
|
|
boost::shared_ptr<ARDOUR::BundleList> b = _session.bundles ();
|
|
|
|
|
for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
|
2009-06-09 20:21:19 +00:00
|
|
|
if ((*i)->connected_to (_bundle, _session.engine())) {
|
2009-02-11 02:14:01 +00:00
|
|
|
bundles.push_back (*i);
|
2009-01-25 06:47:11 +00:00
|
|
|
}
|
2009-02-11 02:14:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-02-11 02:14:01 +00:00
|
|
|
/* Route bundles */
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
boost::shared_ptr<ARDOUR::RouteList> r = _session.get_routes ();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_direction == Input) {
|
|
|
|
|
for (ARDOUR::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
|
if ((*i)->output()->bundle()->connected_to (_bundle, _session.engine())) {
|
|
|
|
|
bundles.push_back ((*i)->output()->bundle());
|
|
|
|
|
}
|
2009-01-25 06:47:11 +00:00
|
|
|
}
|
2009-06-09 20:21:19 +00:00
|
|
|
} else {
|
|
|
|
|
for (ARDOUR::RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
|
|
|
|
if ((*i)->input()->bundle()->connected_to (_bundle, _session.engine())) {
|
|
|
|
|
bundles.push_back ((*i)->input()->bundle());
|
|
|
|
|
}
|
2009-01-25 06:47:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
return bundles;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr<UserBundle> b)
|
|
|
|
|
{
|
|
|
|
|
bundle = b;
|
2009-12-21 18:23:07 +00:00
|
|
|
b->Changed.connect_same_thread (changed, boost::bind (&IO::bundle_changed, io, _1));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-27 18:36:40 +00:00
|
|
|
std::string
|
|
|
|
|
IO::bundle_channel_name (uint32_t c, uint32_t n) const
|
|
|
|
|
{
|
|
|
|
|
char buf[32];
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-01-27 18:36:40 +00:00
|
|
|
switch (n) {
|
|
|
|
|
case 1:
|
|
|
|
|
return _("mono");
|
|
|
|
|
case 2:
|
|
|
|
|
return c == 0 ? _("L") : _("R");
|
|
|
|
|
default:
|
|
|
|
|
snprintf (buf, sizeof(buf), _("%d"), (c + 1));
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2009-01-30 07:40:13 +00:00
|
|
|
|
2009-03-02 16:52:40 +00:00
|
|
|
string
|
|
|
|
|
IO::name_from_state (const XMLNode& node)
|
|
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-03-02 16:52:40 +00:00
|
|
|
if ((prop = node.property ("name")) != 0) {
|
|
|
|
|
return prop->value();
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-02 16:52:40 +00:00
|
|
|
return string();
|
|
|
|
|
}
|
2009-01-30 07:40:13 +00:00
|
|
|
|
2009-03-02 16:52:40 +00:00
|
|
|
void
|
|
|
|
|
IO::set_name_in_state (XMLNode& node, const string& new_name)
|
|
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-03-02 16:52:40 +00:00
|
|
|
if ((prop = node.property ("name")) != 0) {
|
|
|
|
|
node.add_property ("name", new_name);
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2009-03-02 16:52:40 +00:00
|
|
|
}
|
2009-04-23 17:48:37 +00:00
|
|
|
|
2010-03-21 17:02:30 +00:00
|
|
|
bool
|
|
|
|
|
IO::connected () const
|
|
|
|
|
{
|
|
|
|
|
/* do we have any connections at all? */
|
|
|
|
|
|
|
|
|
|
for (PortSet::const_iterator p = _ports.begin(); p != _ports.end(); ++p) {
|
|
|
|
|
if (p->connected()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-17 02:08:13 +00:00
|
|
|
bool
|
|
|
|
|
IO::connected_to (boost::shared_ptr<const IO> other) const
|
|
|
|
|
{
|
2009-07-01 23:14:27 +00:00
|
|
|
if (!other) {
|
2010-03-21 17:02:30 +00:00
|
|
|
return connected ();
|
2009-07-01 23:14:27 +00:00
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
assert (_direction != other->direction());
|
|
|
|
|
|
2009-05-17 02:08:13 +00:00
|
|
|
uint32_t i, j;
|
2009-06-09 20:21:19 +00:00
|
|
|
uint32_t no = n_ports().n_total();
|
|
|
|
|
uint32_t ni = other->n_ports ().n_total();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-05-17 02:08:13 +00:00
|
|
|
for (i = 0; i < no; ++i) {
|
|
|
|
|
for (j = 0; j < ni; ++j) {
|
2009-06-09 20:21:19 +00:00
|
|
|
if (nth(i)->connected_to (other->nth(j)->name())) {
|
2009-05-17 02:08:13 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
void
|
|
|
|
|
IO::process_input (boost::shared_ptr<Processor> proc, sframes_t start_frame, sframes_t end_frame, nframes_t nframes)
|
|
|
|
|
{
|
|
|
|
|
BufferSet bufs;
|
|
|
|
|
|
|
|
|
|
/* don't read the data into new buffers - just use the port buffers directly */
|
|
|
|
|
|
|
|
|
|
bufs.attach_buffers (_ports, nframes, 0);
|
2009-11-25 23:29:52 +00:00
|
|
|
proc->run (bufs, start_frame, end_frame, nframes, true);
|
2009-06-09 20:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
IO::collect_input (BufferSet& bufs, nframes_t nframes, ChanCount offset)
|
|
|
|
|
{
|
|
|
|
|
assert(bufs.available() >= _ports.count());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_ports.count() == ChanCount::ZERO) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bufs.set_count (_ports.count());
|
|
|
|
|
|
|
|
|
|
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
|
|
|
|
|
PortSet::iterator i = _ports.begin(*t);
|
|
|
|
|
BufferSet::iterator b = bufs.begin(*t);
|
|
|
|
|
|
|
|
|
|
for (uint32_t off = 0; off < offset.get(*t); ++off, ++b) {
|
|
|
|
|
if (b == bufs.end(*t)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( ; i != _ports.end(*t); ++i, ++b) {
|
|
|
|
|
Buffer& bb (i->get_buffer (nframes));
|
|
|
|
|
b->read_from (bb, nframes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes, nframes_t offset)
|
|
|
|
|
{
|
|
|
|
|
// Copy any buffers 1:1 to outputs
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
PortSet::iterator o = _ports.begin(type);
|
|
|
|
|
BufferSet::iterator i = bufs.begin(type);
|
|
|
|
|
BufferSet::iterator prev = i;
|
|
|
|
|
|
|
|
|
|
while (i != bufs.end(type) && o != _ports.end (type)) {
|
|
|
|
|
Buffer& port_buffer (o->get_buffer (nframes));
|
|
|
|
|
port_buffer.read_from (*i, nframes, offset);
|
|
|
|
|
prev = i;
|
|
|
|
|
++i;
|
|
|
|
|
++o;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
// Copy last buffer to any extra outputs
|
|
|
|
|
|
|
|
|
|
while (o != _ports.end(type)) {
|
|
|
|
|
Buffer& port_buffer (o->get_buffer (nframes));
|
|
|
|
|
port_buffer.read_from (*prev, nframes, offset);
|
|
|
|
|
++o;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-23 02:43:35 +00:00
|
|
|
|
|
|
|
|
Port*
|
|
|
|
|
IO::port_by_name (const std::string& str) const
|
|
|
|
|
{
|
|
|
|
|
/* to be called only from ::set_state() - no locking */
|
|
|
|
|
|
|
|
|
|
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
|
|
|
|
|
|
|
|
|
const Port& p(*i);
|
|
|
|
|
|
|
|
|
|
if (p.name() == str) {
|
|
|
|
|
return const_cast<Port*>(&p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|