Committed filthy mess of a working copy solely for moving between machines.
[ardour.git] / libs / midi++2 / fd_midiport.cc
1 /*
2     Copyright (C) 1999 Paul Barton-Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <fcntl.h>
22 #include <cerrno>
23
24 #include <pbd/error.h>
25 #include <pbd/pathscanner.h>
26
27 #include <midi++/types.h>
28 #include <midi++/fd_midiport.h>
29
30 using namespace std;
31 using namespace MIDI;
32
33 string *FD_MidiPort::midi_dirpath = 0;
34 string *FD_MidiPort::midi_filename_pattern = 0;
35
36 FD_MidiPort::FD_MidiPort (PortRequest &req, 
37                           const string &dirpath,
38                           const string &pattern) 
39         : Port (req)
40 {       
41         open (req);
42
43         if (_fd < 0) {
44                 switch (errno) {
45                 case EBUSY:
46                         error << "MIDI: port device in use" << endmsg;
47                         req.status = PortRequest::Busy;
48                         break;
49                 case ENOENT:
50                         error << "MIDI: no such port device" << endmsg;
51                         req.status = PortRequest::NoSuchFile;
52                         break;
53                 case EACCES:
54                         error << "MIDI: access to port denied" << endmsg;
55                         req.status = PortRequest::NotAllowed;
56                         break;
57                 default:
58                         req.status = PortRequest::Unknown;
59                 } 
60         } else {
61                 _ok = true;
62                 req.status = PortRequest::OK;
63
64                 if (midi_dirpath == 0) {
65                         midi_dirpath = new string (dirpath);
66                         midi_filename_pattern = new string (pattern);
67                 }
68
69                 if (req.mode & O_NONBLOCK == 0) {
70                         /* we unconditionally set O_NONBLOCK during
71                            open, but the request didn't ask for it,
72                            so remove it.
73                         */
74
75                         int flags = fcntl (_fd, F_GETFL, 0);
76                         fcntl (_fd, F_SETFL, flags & ~(O_NONBLOCK));
77                 }
78         }
79 }
80
81 void
82 FD_MidiPort::open (PortRequest &req)
83
84 {
85         int mode = req.mode | O_NONBLOCK;
86         _fd = ::open (req.devname, mode);
87 }
88
89 vector<string *> *
90 FD_MidiPort::list_devices ()
91
92 {
93         PathScanner scanner;
94
95         return scanner (*midi_dirpath, *midi_filename_pattern, false, true);
96 }       
97
98 int
99 FD_MidiPort::selectable () const
100
101 {
102         long flags;
103         
104         /* turn on non-blocking mode, since we plan to use select/poll
105            to tell us when there is data to read.
106         */
107
108         flags = fcntl (_fd, F_GETFL);
109         flags |= O_NONBLOCK;
110
111         if (fcntl (_fd, F_SETFL, flags)) {
112                 error << "FD_MidiPort: could not turn on non-blocking mode"
113                       << " (" << strerror (errno) 
114                       << ')'
115                       << endmsg;
116
117                 return -1;
118         }
119
120         return _fd;
121 }
122
123
124 int
125 FD_MidiPort::do_slow_write (byte *msg, unsigned int msglen)
126
127 {
128         size_t n;
129         size_t i;
130
131         for (n = 0; n < msglen; n++) {
132
133                 if (::write (_fd, &msg[n], 1) != 1) {
134                         break;
135                 }
136
137                 bytes_written++;
138                 for (i = 0; i < slowdown * 10000; i++);
139         }
140
141
142         if (n && output_parser) {
143                 output_parser->raw_preparse (*output_parser, msg, n);
144                 for (unsigned int i = 0; i < n; i++) {
145                         output_parser->scanner (msg[i]);
146                 }
147                 output_parser->raw_postparse (*output_parser, msg, n);
148         }
149         
150         return n;
151 }
152
153 int
154 FD_MidiPort::read (byte* buf, size_t max, timestamp_t timestamp)
155 {
156         int nread;
157         
158         if ((_mode & O_ACCMODE) == O_WRONLY) {
159                 return -EACCES;
160         }
161         
162         // cerr << "MIDI: read up to " << max << " from " << _fd << " (" << name() << ')' << endl;
163         
164         if ((nread = ::read (_fd, buf, max)) > 0) {
165                 bytes_read += nread;
166
167                 // cerr << " read " << nread << endl;
168
169                 if (input_parser) {
170                         input_parser->raw_preparse 
171                                 (*input_parser, buf, nread);
172                         for (int i = 0; i < nread; i++) {
173                                 input_parser->scanner (buf[i]);
174                         }       
175                         input_parser->raw_postparse 
176                                 (*input_parser, buf, nread);
177                 }
178         }
179         
180         return nread;
181 }