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