Add function PBD::sys::copy_file intended to replace PBD::copy_file
[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 (Port* port)
130 {
131         PortMap::iterator res;
132
133         for (res = ports_by_device.begin(); res != ports_by_device.end(); ) {
134                 PortMap::iterator tmp;
135                 tmp = res;
136                 ++tmp;
137                 if (res->second == port) {
138                         ports_by_device.erase (res);
139                 } 
140                 res = tmp;
141         }
142
143
144         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); ) {
145                 PortMap::iterator tmp;
146                 tmp = res;
147                 ++tmp;
148                 if (res->second == port) {
149                         ports_by_tag.erase (res);
150                 } 
151                 res = tmp;
152         }
153         
154         delete port;
155
156         return 0;
157 }
158
159 Port *
160 Manager::port (string name)
161 {
162         PortMap::iterator res;
163
164         for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
165                 if (name == (*res).first) {
166                         return (*res).second;
167                 }
168         }
169
170         return 0;
171 }
172
173 int
174 Manager::foreach_port (int (*func)(const Port &, size_t, void *),
175                            void *arg)
176
177 {
178         PortMap::const_iterator i;
179         int retval;
180         int n;
181                 
182         for (n = 0, i = ports_by_device.begin(); 
183                     i != ports_by_device.end(); i++, n++) {
184
185                 if ((retval = func (*((*i).second), n, arg)) != 0) {
186                         return retval;
187                 }
188         }
189
190         return 0;
191 }
192
193 int
194 Manager::parse_port_request (string str, Port::Type type)
195 {
196         PortRequest *req;
197         string::size_type colon;
198         string tag;
199
200         if (str.length() == 0) {
201                 error << "MIDI: missing port specification" << endmsg;
202                 return -1;
203         }
204
205         /* Port specifications look like:
206
207            devicename
208            devicename:tagname
209            devicename:tagname:mode
210
211            where 
212
213            "devicename" is the full path to the requested file
214            
215            "tagname" (optional) is the name used to refer to the
216                          port. If not given, g_path_get_basename (devicename)
217                          will be used.
218
219            "mode" (optional) is either "r" or "w" or something else.
220                         if it is "r", the port will be opened
221                         read-only, if "w", the port will be opened
222                         write-only. Any other value, or no mode
223                         specification at all, will cause the port to
224                         be opened for reading and writing.
225         */
226                         
227         req = new PortRequest;
228         colon = str.find_first_of (':');
229
230         if (colon != string::npos) {
231                 req->devname = strdup (str.substr (0, colon).c_str());
232         } else {
233                 req->devname = strdup (str.c_str());
234         }
235
236         if (colon < str.length()) {
237
238                 tag = str.substr (colon+1);
239
240                 /* see if there is a mode specification in the tag part */
241                 
242                 colon = tag.find_first_of (':');
243
244                 if (colon != string::npos) {
245                         string modestr;
246
247                         req->tagname = strdup (tag.substr (0, colon).c_str());
248
249                         modestr = tag.substr (colon+1);
250                         if (modestr == "r") {
251                                 req->mode = O_RDONLY;
252                         } else if (modestr == "w") {
253                                 req->mode = O_WRONLY;
254                         } else {
255                                 req->mode = O_RDWR;
256                         }
257
258                 } else {
259                         req->tagname = strdup (tag.c_str());
260                         req->mode = O_RDWR;
261                 }
262
263         } else {
264                 req->tagname = g_path_get_basename (req->devname);
265                 req->mode = O_RDWR;
266         }
267
268         req->type = type;
269
270         if (MIDI::Manager::instance()->add_port (*req) == 0) {
271                 return -1;
272         }
273
274         return 0;
275 }
276
277 void
278 Manager::cycle_start(nframes_t nframes)
279 {
280         for (PortMap::iterator i = ports_by_device.begin(); 
281                     i != ports_by_device.end(); i++)
282                 (*i).second->cycle_start(nframes);
283 }
284
285 void
286 Manager::cycle_end()
287 {
288         for (PortMap::iterator i = ports_by_device.begin(); 
289                     i != ports_by_device.end(); i++)
290                 (*i).second->cycle_end();
291 }
292