Insert/Redirect refactoring, towards better MIDI support in mixer strip, and
[ardour.git] / libs / ardour / port_insert.cc
1 /*
2     Copyright (C) 2000,2007 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <string>
21
22 #include <sigc++/bind.h>
23
24 #include <pbd/failed_constructor.h>
25 #include <pbd/xml++.h>
26
27 #include <ardour/port_insert.h>
28 #include <ardour/plugin.h>
29 #include <ardour/port.h>
30 #include <ardour/route.h>
31 #include <ardour/buffer_set.h>
32
33 #include <ardour/audioengine.h>
34 #include <ardour/session.h>
35 #include <ardour/types.h>
36
37 #include "i18n.h"
38
39 using namespace std;
40 using namespace ARDOUR;
41 using namespace PBD;
42
43 PortInsert::PortInsert (Session& s, Placement p)
44         : Redirect (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1)
45 {
46         init ();
47         InsertCreated (this); /* EMIT SIGNAL */
48 }
49
50 PortInsert::PortInsert (const PortInsert& other)
51         : Redirect (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1)
52 {
53         init ();
54         InsertCreated (this); /* EMIT SIGNAL */
55 }
56
57 void
58 PortInsert::init ()
59 {
60         if (_io->add_input_port ("", this)) {
61                 error << _("PortInsert: cannot add input port") << endmsg;
62                 throw failed_constructor();
63         }
64         
65         if (_io->add_output_port ("", this)) {
66                 error << _("PortInsert: cannot add output port") << endmsg;
67                 throw failed_constructor();
68         }
69 }
70
71 PortInsert::PortInsert (Session& s, const XMLNode& node)
72         : Redirect (s, "unnamed port insert", PreFader)
73 {
74         if (set_state (node)) {
75                 throw failed_constructor();
76         }
77
78         InsertCreated (this); /* EMIT SIGNAL */
79 }
80
81 PortInsert::~PortInsert ()
82 {
83         GoingAway ();
84 }
85
86 void
87 PortInsert::run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
88 {
89         if (_io->n_outputs().n_total() == 0) {
90                 return;
91         }
92
93         if (!active()) {
94                 /* deliver silence */
95                 _io->silence (nframes, offset);
96                 return;
97         }
98
99         _io->deliver_output(bufs, start_frame, end_frame, nframes, offset);
100
101         _io->collect_input(bufs, nframes, offset);
102 }
103
104 XMLNode&
105 PortInsert::get_state(void)
106 {
107         return state (true);
108 }
109
110 XMLNode&
111 PortInsert::state (bool full)
112 {
113         XMLNode& node = Redirect::state(full);
114         char buf[32];
115         node.add_property ("type", "port");
116         snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
117         node.add_property ("bitslot", buf);
118
119         return node;
120 }
121
122 int
123 PortInsert::set_state(const XMLNode& node)
124 {
125         XMLNodeList nlist = node.children();
126         XMLNodeIterator niter;
127         XMLPropertyList plist;
128         const XMLProperty *prop;
129
130         if ((prop = node.property ("type")) == 0) {
131                 error << _("XML node describing insert is missing the `type' field") << endmsg;
132                 return -1;
133         }
134         
135         if (prop->value() != "port") {
136                 error << _("non-port insert XML used for port plugin insert") << endmsg;
137                 return -1;
138         }
139
140         if ((prop = node.property ("bitslot")) == 0) {
141                 bitslot = _session.next_insert_id();
142         } else {
143                 sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
144                 _session.mark_insert_id (bitslot);
145         }
146
147         const XMLNode* insert_node = &node;
148
149         // legacy sessions: search for child Redirect node
150         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
151                 if ((*niter)->name() == "Redirect") {
152                         insert_node = *niter;
153                         break;
154                 }
155         }
156         
157         Redirect::set_state (*insert_node);
158
159         return 0;
160 }
161
162 ARDOUR::nframes_t 
163 PortInsert::latency() 
164 {
165         /* because we deliver and collect within the same cycle,
166            all I/O is necessarily delayed by at least frames_per_cycle().
167
168            if the return port for insert has its own latency, we
169            need to take that into account too.
170         */
171
172         return _session.engine().frames_per_cycle() + _io->input_latency();
173 }
174
175 bool
176 PortInsert::can_support_input_configuration (ChanCount in) const
177 {
178         if (_io->input_maximum() == ChanCount::INFINITE && _io->output_maximum() == ChanCount::INFINITE) {
179
180                 /* not configured yet */
181
182                 return true; /* we can support anything the first time we're asked */
183
184         } else {
185
186                 /* the "input" config for a port insert corresponds to how
187                    many output ports it will have.
188                 */
189
190                 if (_io->output_maximum() == in) {
191
192                         return true;
193                 } 
194         }
195
196         return false;
197 }
198
199 ChanCount
200 PortInsert::output_for_input_configuration (ChanCount in) const
201 {
202         return in;
203 }
204
205 bool
206 PortInsert::configure_io (ChanCount in, ChanCount out)
207 {
208         /* do not allow configuration to be changed outside the range of
209            the last request config. or something like that.
210         */
211
212
213         /* this is a bit odd: 
214
215            the number of inputs we are required to handle corresponds 
216            to the number of output ports we need.
217
218            the number of outputs we are required to have corresponds
219            to the number of input ports we need.
220         */
221
222         _io->set_output_maximum (in);
223         _io->set_output_minimum (in);
224         _io->set_input_maximum (out);
225         _io->set_input_minimum (out);
226
227         bool success = (_io->ensure_io (out, in, false, this) == 0);
228
229         if (success)
230                 return Insert::configure_io(in, out);
231         else
232                 return false;
233 }
234
235 ChanCount
236 PortInsert::output_streams() const
237 {
238         return _io->n_inputs ();
239 }
240
241 ChanCount
242 PortInsert::input_streams() const
243 {
244         return _io->n_outputs ();
245 }
246