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