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