Fix precedence error.
[ardour.git] / libs / surfaces / mackie / mackie_control_protocol_poll.cc
1 #include "mackie_control_protocol.h"
2
3 #include "midi_byte_array.h"
4 #include "surface_port.h"
5
6 #include <pbd/pthread_utils.h>
7 #include <pbd/error.h>
8
9 #include <midi++/types.h>
10 #include <midi++/port.h>
11 #include <midi++/manager.h>
12 #include "i18n.h"
13
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <poll.h>
17 #include <errno.h>
18
19 #include <iostream>
20 #include <string>
21 #include <vector>
22
23 using namespace std;
24 using namespace Mackie;
25 using namespace PBD;
26
27 const char * MackieControlProtocol::default_port_name = "mcu";
28
29 bool MackieControlProtocol::probe()
30 {
31         return MIDI::Manager::instance()->port( default_port_name ) != 0;
32 }
33
34 void * MackieControlProtocol::monitor_work()
35 {
36         // What does ThreadCreatedWithRequestSize do?
37         PBD::ThreadCreated (pthread_self(), X_("Mackie"));
38
39         pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
40         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
41
42         // read from midi ports
43         while ( _polling )
44         {
45                 try
46                 {
47                         if ( poll_ports() )
48                         {
49                                 try { read_ports(); }
50                                 catch ( exception & e ) {
51                                         cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
52                                         _ports_changed = true;
53                                         update_ports();
54                                 }
55                         }
56                         // poll for automation data from the routes
57                         poll_automation();
58                 }
59                 catch ( exception & e )
60                 {
61                         cout << "caught exception in MackieControlProtocol::monitor_work " << e.what() << endl;
62                 }
63         }
64
65         // TODO ports and pfd and nfds should be in a separate class
66         delete[] pfd;
67         pfd = 0;
68         nfds = 0;
69
70         return (void*) 0;
71 }
72
73 void MackieControlProtocol::update_ports()
74 {
75         if ( _ports_changed )
76         {
77                 Glib::Mutex::Lock ul( update_mutex );
78                 // yes, this is a double-test locking paradigm, or whatever it's called
79                 // because we don't *always* need to acquire the lock for the first test
80                 if ( _ports_changed )
81                 {
82                         // create new pollfd structures
83                         if ( pfd != 0 ) delete[] pfd;
84                         // TODO This might be a memory leak. How does thread cancellation cleanup work?
85                         pfd = new pollfd[_ports.size()];
86                         nfds = 0;
87
88                         for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
89                         {
90                                 //cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
91                                 pfd[nfds].fd = (*it)->port().selectable();
92                                 pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
93                                 ++nfds;
94                         }
95                         _ports_changed = false;
96                 }
97                 update_cond.signal();
98         }
99 }
100
101 void MackieControlProtocol::read_ports()
102 {
103         /* now read any data on the ports */
104         Glib::Mutex::Lock lock( update_mutex );
105         for ( int p = 0; p < nfds; ++p )
106         {
107                 // this will cause handle_midi_any in the MackiePort to be triggered
108                 if ( (pfd[p].revents & POLLIN) > 0 )
109                 {
110                         // avoid deadlocking?
111                         // doesn't seem to make a difference
112                         //lock.release();
113                         _ports[p]->read();
114                         //lock.acquire();
115                 }
116         }
117 }
118
119 bool MackieControlProtocol::poll_ports()
120 {
121         int timeout = 10; // milliseconds
122         int no_ports_sleep = 1000; // milliseconds
123
124         Glib::Mutex::Lock lock( update_mutex );
125         // if there are no ports
126         if ( nfds < 1 )
127         {
128                 lock.release();
129                 //cout << "poll_ports no ports" << endl;
130                 usleep( no_ports_sleep * 1000 );
131                 return false;
132         }
133
134         int retval = poll( pfd, nfds, timeout );
135         if ( retval < 0 )
136         {
137                 // gdb at work, perhaps
138                 if ( errno != EINTR )
139                 {
140                         error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
141                 }
142                 return false;
143         }
144         
145         return retval > 0;
146 }
147
148 void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
149 {
150         // port gone away. So stop polling it ASAP
151         {
152                 // delete the port instance
153                 Glib::Mutex::Lock lock( update_mutex );
154                 MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
155                 if ( it != _ports.end() )
156                 {
157                         delete *it;
158                         _ports.erase( it );
159                 }
160         }
161         _ports_changed = true;
162         update_ports();
163         
164         // TODO all the rebuilding of surfaces and so on
165 }
166
167 void MackieControlProtocol::handle_port_active( SurfacePort * port )
168 {
169         // no need to re-add port because it was already added
170         // during the init phase. So just update the local surface
171         // representation and send the representation to 
172         // all existing ports
173         
174         // TODO update bank size
175         
176         // TODO rebuild surface, to have new units
177         
178         // finally update session state to the surface
179         // TODO but this is also done in set_active, and
180         // in fact update_surface won't execute unless
181         // _active == true
182         //cout << "update_surface in handle_port_active" << endl;
183         update_surface();
184 }
185
186 void MackieControlProtocol::handle_port_init( Mackie::SurfacePort * sport )
187 {
188         //cout << "MackieControlProtocol::handle_port_init" << endl;
189         _ports_changed = true;
190         update_ports();
191 }