90% done with external sync design changes (GUI now has toggle switch for ext/int...
[ardour.git] / libs / midi++2 / manager.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-Davis 
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
17     $Id$
18 */
19
20 #include <fcntl.h>
21
22 #include <glib.h>
23
24 #include "pbd/error.h"
25
26 #include "midi++/types.h"
27 #include "midi++/manager.h"
28 #include "midi++/factory.h"
29 #include "midi++/channel.h"
30
31 using namespace std;
32 using namespace MIDI;
33 using namespace PBD;
34
35 /* XXX check for strdup leaks */
36
37 Manager *Manager::theManager = 0;
38
39 Manager::Manager () 
40 {
41         inputPort = 0;
42         outputPort = 0;
43         inputChannelNumber = 0;
44         outputChannelNumber = 0;
45         api_data = 0;
46 }
47
48 Manager::~Manager ()
49 {
50         PortMap::iterator i;
51
52         for (i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
53                 delete (*i).second;
54         }
55
56         ports_by_device.erase (ports_by_device.begin(), ports_by_device.end());
57         ports_by_tag.erase (ports_by_tag.begin(), ports_by_tag.end());
58
59         if (theManager == this) {
60                 theManager = 0;
61         }
62 }
63
64 Port *
65 Manager::add_port (const XMLNode& node)
66 {
67         Port::Descriptor desc (node);
68         PortFactory factory;
69         Port *port;
70         PortMap::iterator existing;
71         pair<string, Port *> newpair;
72
73         if ((existing = ports_by_tag.find (desc.tag)) != ports_by_tag.end()) {
74
75                 port = (*existing).second;
76                 
77                 if (port->mode() == desc.mode) {
78                         
79                         /* Same mode - reuse the port, and just
80                            create a new tag entry.
81                         */
82                         
83                         newpair.first = desc.tag;
84                         newpair.second = port;
85                         
86                         ports_by_tag.insert (newpair);
87                         return port;
88                 }
89                 
90                 /* If the existing is duplex, and this request
91                    is not, then fail, because most drivers won't
92                    allow opening twice with duplex and non-duplex
93                    operation.
94                 */
95                 
96                 if ((desc.mode == O_RDWR && port->mode() != O_RDWR) ||
97                     (desc.mode != O_RDWR && port->mode() == O_RDWR)) {
98                         error << "MIDIManager: port tagged \""
99                               << desc.tag
100                               << "\" cannot be opened duplex and non-duplex"
101                               << endmsg;
102                         return 0;
103                 }
104                 
105                 /* modes must be different or complementary */
106         }
107
108         if (!PortFactory::ignore_duplicate_devices (desc.type)) {
109
110                 if ((existing = ports_by_device.find (desc.device)) != ports_by_device.end()) {
111                         
112                         port = (*existing).second;
113                         
114                         if (port->mode() == desc.mode) {
115                                 
116                                 /* Same mode - reuse the port, and just
117                                    create a new tag entry.
118                                 */
119                                 
120                                 newpair.first = desc.tag;
121                                 newpair.second = port;
122                                 
123                                 ports_by_tag.insert (newpair);
124                                 return port;
125                         }
126                         
127                         /* If the existing is duplex, and this request
128                            is not, then fail, because most drivers won't
129                            allow opening twice with duplex and non-duplex
130                            operation.
131                         */
132                         
133                         if ((desc.mode == O_RDWR && port->mode() != O_RDWR) ||
134                             (desc.mode != O_RDWR && port->mode() == O_RDWR)) {
135                                 error << "MIDIManager: port tagged \""
136                                       << desc.tag
137                                       << "\" cannot be opened duplex and non-duplex"
138                                       << endmsg;
139                                 return 0;
140                         }
141                         
142                         /* modes must be different or complementary */
143                 }
144         }
145         
146         port = factory.create_port (node, api_data);
147
148         
149         if (port == 0) {
150                 return 0;
151         }
152
153         if (!port->ok()) {
154                 delete port;
155                 return 0;
156         }
157
158         newpair.first = port->name();
159         newpair.second = port;
160         ports_by_tag.insert (newpair);
161
162         newpair.first = port->device();
163         newpair.second = port;
164         ports_by_device.insert (newpair);
165
166         /* first port added becomes the default input
167            port.
168         */
169
170         if (inputPort == 0) {
171                 inputPort = port;
172         } 
173
174         if (outputPort == 0) {
175                 outputPort = port;
176         }
177
178         return port;
179 }
180
181 int 
182 Manager::remove_port (Port* port)
183 {
184         PortMap::iterator res;
185
186         for (res = ports_by_device.begin(); res != ports_by_device.end(); ) {
187                 PortMap::iterator tmp;
188                 tmp = res;
189                 ++tmp;
190                 if (res->second == port) {
191                         ports_by_device.erase (res);
192                 } 
193                 res = tmp;
194         }
195
196
197         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); ) {
198                 PortMap::iterator tmp;
199                 tmp = res;
200                 ++tmp;
201                 if (res->second == port) {
202                         ports_by_tag.erase (res);
203                 } 
204                 res = tmp;
205         }
206         
207         delete port;
208
209         return 0;
210 }
211
212 int
213 Manager::set_input_port (string tag)
214 {
215         PortMap::iterator res;
216         bool found = false;
217
218         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
219                 if (tag == (*res).first) {
220                         found = true;
221                         break;
222                 }
223         }
224         
225         if (!found) {
226                 return -1;
227         }
228
229         inputPort = (*res).second;
230
231         return 0;
232 }
233
234 int
235 Manager::set_output_port (string tag)
236
237 {
238         PortMap::iterator res;
239         bool found = false;
240
241         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
242                 if (tag == (*res).first) {
243                         found = true;
244                         break;
245                 }
246         }
247         
248         if (!found) {
249                 return -1;
250         }
251
252         // XXX send a signal to say we're about to change output ports
253
254         if (outputPort) {
255                 for (channel_t chan = 0; chan < 16; chan++) {
256                         outputPort->channel (chan)->all_notes_off (0);
257                 }
258         }
259         outputPort = (*res).second;
260
261         // XXX send a signal to say we've changed output ports
262
263         return 0;
264 }
265
266 Port *
267 Manager::port (string name)
268 {
269         PortMap::iterator res;
270
271         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
272                 if (name == (*res).first) {
273                         return (*res).second;
274                 }
275         }
276
277         return 0;
278 }
279
280 int
281 Manager::foreach_port (int (*func)(const Port &, size_t, void *),
282                            void *arg)
283 {
284         PortMap::const_iterator i;
285         int retval;
286         int n;
287                 
288         for (n = 0, i = ports_by_device.begin(); 
289                     i != ports_by_device.end(); i++, n++) {
290
291                 if ((retval = func (*((*i).second), n, arg)) != 0) {
292                         return retval;
293                 }
294         }
295
296         return 0;
297 }
298
299 void
300 Manager::cycle_start(nframes_t nframes)
301 {
302         for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
303                 (*i).second->cycle_start (nframes);
304         }
305 }
306
307 void
308 Manager::cycle_end()
309 {
310         for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
311                 (*i).second->cycle_end ();
312         }
313 }
314
315
316 int
317 Manager::get_known_ports (vector<PortSet>& ports)
318 {
319         return PortFactory::get_known_ports (ports);
320 }