MCP: correctly link routes and strips; correctly set up timeouts using event loop...
[ardour.git] / libs / surfaces / mackie / surface_port.cc
1 /*
2         Copyright (C) 2006,2007 John Anderson
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 #include "surface_port.h"
19
20 #include "mackie_control_exception.h"
21 #include "controls.h"
22
23 #include "midi++/types.h"
24 #include "midi++/port.h"
25 #include "midi++/manager.h"
26 #include <sigc++/sigc++.h>
27 #include <boost/shared_array.hpp>
28
29 #include "i18n.h"
30
31 #include <sstream>
32
33 #include <cstring>
34 #include <cerrno>
35
36 using namespace std;
37 using namespace Mackie;
38
39 SurfacePort::SurfacePort()
40         : _input_port (0), _output_port (0), _number (0), _active (false)
41 {
42 }
43
44 /** @param input_port Input MIDI::Port; this object takes responsibility for removing it from
45  *  the MIDI::Manager and destroying it.
46  *  @param output_port Output MIDI::Port; responsibility similarly taken.
47  */
48 SurfacePort::SurfacePort (MIDI::Port & input_port, MIDI::Port & output_port, int number)
49         : _input_port (&input_port), _output_port (&output_port), _number (number), _active (false)
50 {
51 }
52
53 SurfacePort::~SurfacePort()
54 {
55 #ifdef PORT_DEBUG
56         cout << "~SurfacePort::SurfacePort()" << endl;
57 #endif
58         // make sure another thread isn't reading or writing as we close the port
59         Glib::RecMutex::Lock lock (_rwlock);
60         _active = false;
61
62         MIDI::Manager* mm = MIDI::Manager::instance ();
63         
64         if (_input_port) {
65                 mm->remove_port (_input_port);
66                 delete _input_port;
67         }
68
69         if (_output_port) {
70                 mm->remove_port (_output_port);
71                 delete _output_port;
72         }
73         
74 #ifdef PORT_DEBUG
75         cout << "~SurfacePort::SurfacePort() finished" << endl;
76 #endif
77 }
78
79 // wrapper for one day when strerror_r is working properly
80 string fetch_errmsg (int error_number)
81 {
82         char * msg = strerror (error_number);
83         return msg;
84 }
85         
86 MidiByteArray SurfacePort::read()
87 {
88         const int max_buf_size = 512;
89         MIDI::byte buf[max_buf_size];
90         MidiByteArray retval;
91
92         // check active. Mainly so that the destructor
93         // doesn't destroy the mutex while it's still locked
94         if  (!active()) {
95                 return retval;
96         }
97         
98         // return nothing read if the lock isn't acquired
99 #if 0
100         Glib::RecMutex::Lock lock (_rwlock, Glib::TRY_LOCK);
101                 
102         if  (!lock.locked()) {
103                 cout << "SurfacePort::read not locked" << endl;
104                 return retval;
105         }
106         
107         // check active again - destructor sequence
108         if  (!active()) return retval;
109 #endif
110         
111         // read port and copy to return value
112         int nread = input_port().read (buf, sizeof (buf));
113
114         if (nread >= 0) {
115                 retval.copy (nread, buf);
116                 if ((size_t) nread == sizeof (buf)) {
117 #ifdef PORT_DEBUG
118                         cout << "SurfacePort::read recursive" << endl;
119 #endif
120                         retval << read();
121                 }
122         } else {
123                 if  (errno != EAGAIN) {
124                         ostringstream os;
125                         os << "Surface: error reading from port: " << input_port().name();
126                         os << ": " << errno << fetch_errmsg (errno);
127
128                         cout << os.str() << endl;
129                         inactive_event();
130                         throw MackieControlException (os.str());
131                 }
132         }
133 #ifdef PORT_DEBUG
134         cout << "SurfacePort::read: " << retval << endl;
135 #endif
136         return retval;
137 }
138
139 void SurfacePort::write (const MidiByteArray & mba)
140 {
141         if (mba.empty()) {
142                 return;
143         }
144
145 #ifdef PORT_DEBUG
146         cout << "SurfacePort::write: " << mba << " to " << output_port().name() << endl;
147 #endif
148         
149         // check active before and after lock - to make sure
150         // that the destructor doesn't destroy the mutex while
151         // it's still in use
152         if (!active()) return;
153         Glib::RecMutex::Lock lock (_rwlock);
154         if (!active()) return;
155
156         int count = output_port().write (mba.bytes().get(), mba.size(), 0);
157         if  (count != (int)mba.size()) {
158                 if  (errno == 0) {
159                         cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl;
160                 } else if  (errno != EAGAIN) {
161                         ostringstream os;
162                         os << "Surface: couldn't write to port " << output_port().name();
163                         os << ", error: " << fetch_errmsg (errno) << "(" << errno << ")";
164                         
165                         cout << os.str() << endl;
166                         inactive_event();
167                 }
168         }
169 #ifdef PORT_DEBUG
170         cout << "SurfacePort::wrote " << count << endl;
171 #endif
172 }
173
174 void SurfacePort::write_sysex (const MidiByteArray & mba)
175 {
176         if (mba.empty()) {
177                 return;
178         }
179
180         MidiByteArray buf;
181         buf << sysex_hdr() << mba << MIDI::eox;
182         write (buf);
183 }
184
185 void SurfacePort::write_sysex (MIDI::byte msg)
186 {
187         MidiByteArray buf;
188         buf << sysex_hdr() << msg << MIDI::eox;
189         write (buf);
190 }
191
192 ostream & Mackie::operator <<  (ostream & os, const SurfacePort & port)
193 {
194         os << "{ ";
195         os << "name: " << port.input_port().name() << " " << port.output_port().name();
196         os << "; ";
197         os << " }";
198         return os;
199 }
200