new functionality to add MIDI ports from the options editor, not totally finished...
[ardour.git] / libs / midi++2 / midimanager.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 #include <midi++/port_request.h>
31
32 using namespace std;
33 using namespace MIDI;
34 using namespace PBD;
35
36 /* XXX check for strdup leaks */
37
38 Manager *Manager::theManager = 0;
39
40 Manager::Manager () 
41       
42 {
43         inputPort = 0;
44         outputPort = 0;
45         inputChannelNumber = 0;
46         outputChannelNumber = 0;
47 }
48
49 Manager::~Manager ()
50
51 {
52         PortMap::iterator i;
53
54         for (i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
55                 delete (*i).second;
56         }
57
58         ports_by_device.erase (ports_by_device.begin(), ports_by_device.end());
59         ports_by_tag.erase (ports_by_tag.begin(), ports_by_tag.end());
60
61         if (theManager == this) {
62                 theManager = 0;
63         }
64 }
65
66 Port *
67 Manager::add_port (PortRequest &req)
68
69 {
70         PortFactory factory;
71         Port *port;
72         PortMap::iterator existing;
73         pair<string, Port *> newpair;
74
75         if (!PortFactory::ignore_duplicate_devices (req.type)) {
76
77                 if ((existing = ports_by_device.find (req.devname)) != ports_by_device.end()) {
78                         
79                         port = (*existing).second;
80                         
81                         if (port->mode() == req.mode) {
82                                 
83                                 /* Same mode - reuse the port, and just
84                                    create a new tag entry.
85                                 */
86                                 
87                                 newpair.first = req.tagname;
88                                 newpair.second = port;
89                                 
90                                 ports_by_tag.insert (newpair);
91                                 return port;
92                         }
93                         
94                         /* If the existing is duplex, and this request
95                            is not, then fail, because most drivers won't
96                            allow opening twice with duplex and non-duplex
97                            operation.
98                         */
99                         
100                         if ((req.mode == O_RDWR && port->mode() != O_RDWR) ||
101                             (req.mode != O_RDWR && port->mode() == O_RDWR)) {
102                                 error << "MIDIManager: port tagged \""
103                                       << req.tagname
104                                       << "\" cannot be opened duplex and non-duplex"
105                                       << endmsg;
106                                 return 0;
107                         }
108                         
109                         /* modes must be different or complementary */
110                 }
111         }
112         
113         port = factory.create_port (req);
114         
115         if (port == 0) {
116                 return 0;
117         }
118
119         if (!port->ok()) {
120                 delete port;
121                 return 0;
122         }
123
124         newpair.first = port->name();
125         newpair.second = port;
126         ports_by_tag.insert (newpair);
127
128         newpair.first = port->device();
129         newpair.second = port;
130         ports_by_device.insert (newpair);
131
132         /* first port added becomes the default input
133            port.
134         */
135
136         if (inputPort == 0) {
137                 inputPort = port;
138         } 
139
140         if (outputPort == 0) {
141                 outputPort = port;
142         }
143
144         return port;
145 }
146
147 int 
148 Manager::remove_port (string name)
149 {
150         PortMap::iterator res;
151
152         if ((res = ports_by_device.find (name)) == ports_by_device.end()) {
153                 return -1;
154         }
155         
156         ports_by_device.erase (res);
157         ports_by_device.erase ((*res).second->name());
158
159         delete (*res).second;
160
161         return 0;
162 }
163
164 int
165 Manager::set_input_port (string tag)
166 {
167         PortMap::iterator res;
168         bool found = false;
169
170         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
171                 if (tag == (*res).first) {
172                         found = true;
173                         break;
174                 }
175         }
176         
177         if (!found) {
178                 return -1;
179         }
180
181         inputPort = (*res).second;
182
183         return 0;
184 }
185
186 int
187 Manager::set_input_port (size_t portnum)
188
189 {
190         PortMap::iterator res;
191
192         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
193                 if ((*res).second->number() == portnum) {
194                         inputPort = (*res).second;
195                         return 0;
196                 }
197         }
198
199         return -1;
200 }
201
202 int
203 Manager::set_output_port (string tag)
204
205 {
206         PortMap::iterator res;
207         bool found = false;
208
209         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
210                 if (tag == (*res).first) {
211                         found = true;
212                         break;
213                 }
214         }
215         
216         if (!found) {
217                 return -1;
218         }
219
220         // XXX send a signal to say we're about to change output ports
221
222         if (outputPort) {
223                 for (channel_t chan = 0; chan < 16; chan++) {
224                         outputPort->channel (chan)->all_notes_off ();
225                 }
226         }
227         outputPort = (*res).second;
228
229         // XXX send a signal to say we've changed output ports
230
231         return 0;
232 }
233
234 int
235 Manager::set_output_port (size_t portnum)
236
237 {
238         PortMap::iterator res;
239
240         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
241                 if ((*res).second->number() == portnum) {
242                         outputPort = (*res).second;
243                         return 0;
244                 }
245         }
246
247         return -1;
248 }
249
250 Port *
251 Manager::port (string name)
252 {
253         PortMap::iterator res;
254
255         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
256                 if (name == (*res).first) {
257                         return (*res).second;
258                 }
259         }
260
261         return 0;
262 }
263
264 Port *
265 Manager::port (size_t portnum)
266
267 {
268         PortMap::iterator res;
269
270         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
271                 if ((*res).second->number() == portnum) {
272                         return (*res).second;
273                 }
274         }
275
276         return 0;
277 }
278
279 int
280 Manager::foreach_port (int (*func)(const Port &, size_t, void *),
281                            void *arg)
282
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 int
300 Manager::parse_port_request (string str, Port::Type type)
301 {
302         PortRequest *req;
303         string::size_type colon;
304         string tag;
305
306         if (str.length() == 0) {
307                 error << "MIDI: missing port specification" << endmsg;
308                 return -1;
309         }
310
311         /* Port specifications look like:
312
313            devicename
314            devicename:tagname
315            devicename:tagname:mode
316
317            where 
318
319            "devicename" is the full path to the requested file
320            
321            "tagname" (optional) is the name used to refer to the
322                          port. If not given, g_path_get_basename (devicename)
323                          will be used.
324
325            "mode" (optional) is either "r" or "w" or something else.
326                         if it is "r", the port will be opened
327                         read-only, if "w", the port will be opened
328                         write-only. Any other value, or no mode
329                         specification at all, will cause the port to
330                         be opened for reading and writing.
331         */
332                         
333         req = new PortRequest;
334         colon = str.find_first_of (':');
335
336         if (colon != string::npos) {
337                 req->devname = strdup (str.substr (0, colon).c_str());
338         } else {
339                 req->devname = strdup (str.c_str());
340         }
341
342         if (colon < str.length()) {
343
344                 tag = str.substr (colon+1);
345
346                 /* see if there is a mode specification in the tag part */
347                 
348                 colon = tag.find_first_of (':');
349
350                 if (colon != string::npos) {
351                         string modestr;
352
353                         req->tagname = strdup (tag.substr (0, colon).c_str());
354
355                         modestr = tag.substr (colon+1);
356                         if (modestr == "r") {
357                                 req->mode = O_RDONLY;
358                         } else if (modestr == "w") {
359                                 req->mode = O_WRONLY;
360                         } else {
361                                 req->mode = O_RDWR;
362                         }
363
364                 } else {
365                         req->tagname = strdup (tag.c_str());
366                         req->mode = O_RDWR;
367                 }
368
369         } else {
370                 // check when tagname is freed
371                 req->tagname = g_path_get_basename (req->devname);
372                 req->mode = O_RDWR;
373         }
374
375         req->type = type;
376
377         if (MIDI::Manager::instance()->add_port (*req) == 0) {
378                 return -1;
379         }
380
381         return 0;
382 }
383
384 int
385 Manager::get_known_ports (vector<PortSet>& ports)
386 {
387         return PortFactory::get_known_ports (ports);
388 }