bcdcddf7a4c754b01baf2ba37611c0f11422e9ac
[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         : api_data(NULL)
37 {
38 }
39
40 Manager::~Manager ()
41
42 {
43         PortMap::iterator i;
44
45         for (i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
46                 delete (*i).second;
47         }
48
49         ports_by_device.erase (ports_by_device.begin(), ports_by_device.end());
50         ports_by_tag.erase (ports_by_tag.begin(), ports_by_tag.end());
51
52         if (theManager == this) {
53                 theManager = 0;
54         }
55 }
56
57 Port *
58 Manager::add_port (PortRequest &req)
59
60 {
61         PortFactory factory;
62         Port *port;
63         PortMap::iterator existing;
64         pair<string, Port *> newpair;
65
66         if ((existing = ports_by_device.find (req.devname)) !=
67             ports_by_device.end()) {
68                 
69                 port = (*existing).second;
70                 if (port->mode() == req.mode) {
71                         
72                         /* Same mode - reuse the port, and just
73                            create a new tag entry.
74                         */
75
76                         newpair.first = req.tagname;
77                         newpair.second = port;
78
79                         ports_by_tag.insert (newpair);
80                         return port;
81                 }
82
83                 /* If the existing is duplex, and this request
84                    is not, then fail, because most drivers won't
85                    allow opening twice with duplex and non-duplex
86                    operation.
87                 */
88                 
89                 if ((req.mode == O_RDWR && port->mode() != O_RDWR) ||
90                     (req.mode != O_RDWR && port->mode() == O_RDWR)) {
91                         error << "MIDIManager: port tagged \""
92                               << req.tagname
93                               << "\" cannot be opened duplex and non-duplex"
94                               << endmsg;
95                         return 0;
96                 }
97
98                 /* modes must be different or complementary */
99         }
100                         
101         port = factory.create_port (req, api_data);
102         
103         if (port == 0) {
104                 return 0;
105         }
106
107         if (!port->ok()) {
108                 delete port;
109                 return 0;
110         }
111
112         newpair.first = port->name();
113         newpair.second = port;
114         ports_by_tag.insert (newpair);
115
116         newpair.first = port->device();
117         newpair.second = port;
118         ports_by_device.insert (newpair);
119
120         return port;
121 }
122
123 int 
124 Manager::remove_port (string name)
125 {
126         PortMap::iterator res;
127
128         if ((res = ports_by_device.find (name)) == ports_by_device.end()) {
129                 return -1;
130         }
131         
132         ports_by_device.erase (res);
133         ports_by_device.erase ((*res).second->name());
134
135         delete (*res).second;
136
137         return 0;
138 }
139
140 Port *
141 Manager::port (string name)
142 {
143         PortMap::iterator res;
144
145         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
146                 if (name == (*res).first) {
147                         return (*res).second;
148                 }
149         }
150
151         return 0;
152 }
153
154 Port *
155 Manager::port (size_t portnum)
156
157 {
158         PortMap::iterator res;
159
160         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
161                 if ((*res).second->number() == portnum) {
162                         return (*res).second;
163                 }
164         }
165
166         return 0;
167 }
168
169 int
170 Manager::foreach_port (int (*func)(const Port &, size_t, void *),
171                            void *arg)
172
173 {
174         PortMap::const_iterator i;
175         int retval;
176         int n;
177                 
178         for (n = 0, i = ports_by_device.begin(); 
179                     i != ports_by_device.end(); i++, n++) {
180
181                 if ((retval = func (*((*i).second), n, arg)) != 0) {
182                         return retval;
183                 }
184         }
185
186         return 0;
187 }
188
189 int
190 Manager::parse_port_request (string str, Port::Type type)
191 {
192         PortRequest *req;
193         string::size_type colon;
194         string tag;
195
196         if (str.length() == 0) {
197                 error << "MIDI: missing port specification" << endmsg;
198                 return -1;
199         }
200
201         /* Port specifications look like:
202
203            devicename
204            devicename:tagname
205            devicename:tagname:mode
206
207            where 
208
209            "devicename" is the full path to the requested file
210            
211            "tagname" (optional) is the name used to refer to the
212                          port. If not given, PBD::basename (devicename)
213                          will be used.
214
215            "mode" (optional) is either "r" or "w" or something else.
216                         if it is "r", the port will be opened
217                         read-only, if "w", the port will be opened
218                         write-only. Any other value, or no mode
219                         specification at all, will cause the port to
220                         be opened for reading and writing.
221         */
222                         
223         req = new PortRequest;
224         colon = str.find_first_of (':');
225
226         if (colon != string::npos) {
227                 req->devname = strdup (str.substr (0, colon).c_str());
228         } else {
229                 req->devname = strdup (str.c_str());
230         }
231
232         if (colon < str.length()) {
233
234                 tag = str.substr (colon+1);
235
236                 /* see if there is a mode specification in the tag part */
237                 
238                 colon = tag.find_first_of (':');
239
240                 if (colon != string::npos) {
241                         string modestr;
242
243                         req->tagname = strdup (tag.substr (0, colon).c_str());
244
245                         modestr = tag.substr (colon+1);
246                         if (modestr == "r") {
247                                 req->mode = O_RDONLY;
248                         } else if (modestr == "w") {
249                                 req->mode = O_WRONLY;
250                         } else {
251                                 req->mode = O_RDWR;
252                         }
253
254                 } else {
255                         req->tagname = strdup (tag.c_str());
256                         req->mode = O_RDWR;
257                 }
258
259         } else {
260                 req->tagname = strdup (PBD::basename (req->devname));
261                 req->mode = O_RDWR;
262         }
263
264         req->type = type;
265
266         if (MIDI::Manager::instance()->add_port (*req) == 0) {
267                 return -1;
268         }
269
270         return 0;
271 }
272
273 void
274 Manager::cycle_start(nframes_t nframes)
275 {
276         for (PortMap::iterator i = ports_by_device.begin(); 
277                     i != ports_by_device.end(); i++)
278                 (*i).second->cycle_start(nframes);
279 }
280
281 void
282 Manager::cycle_end()
283 {
284         for (PortMap::iterator i = ports_by_device.begin(); 
285                     i != ports_by_device.end(); i++)
286                 (*i).second->cycle_end();
287 }
288