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