poll for automation
[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 <midi++/port_request.h>
13 #include "i18n.h"
14
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <poll.h>
18 #include <errno.h>
19
20 #include <iostream>
21 #include <string>
22 #include <vector>
23
24 using namespace std;
25 using namespace Mackie;
26 using namespace PBD;
27
28 const char * MackieControlProtocol::default_port_name = "mcu";
29
30 bool MackieControlProtocol::probe()
31 {
32         return MIDI::Manager::instance()->port( default_port_name ) != 0;
33 }
34
35 void * MackieControlProtocol::monitor_work()
36 {
37         cout << "MackieControlProtocol::monitor_work" << endl;
38         // What does ThreadCreatedWithRequestSize do?
39         PBD::ThreadCreated (pthread_self(), X_("Mackie"));
40
41 #if 0
42         // it seems to do the "block" on poll less often
43         // with this code disabled
44         struct sched_param rtparam;
45         memset (&rtparam, 0, sizeof (rtparam));
46         rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
47         
48         int err;
49         if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
50                 // do we care? not particularly.
51                 PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror( errno )) << endmsg;
52         } 
53 #endif
54         
55         pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
56         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
57
58         // read from midi ports
59         cout << "start poll cycle" << endl;
60         while ( true )
61         {
62                 update_ports();
63                 if ( poll_ports() )
64                 {
65                         try { read_ports(); }
66                         catch ( exception & e ) {
67                                 cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
68                                 _ports_changed = true;
69                                 update_ports();
70                         }
71                 }
72                 // provide a cancellation point
73                 pthread_testcancel();
74                 
75                 // poll for automation data from the routes
76                 poll_automation();
77         }
78
79         // these never get called
80         cout << "MackieControlProtocol::poll_ports exiting" << endl;
81         
82         delete[] pfd;
83
84         return (void*) 0;
85 }
86
87 void MackieControlProtocol::update_ports()
88 {
89         // create pollfd structures if necessary
90         if ( _ports_changed )
91         {
92                 cout << "MackieControlProtocol::update_ports changed 1" << endl;
93                 Glib::Mutex::Lock ul( update_mutex );
94                 // yes, this is a double-test locking paradigm, or whatever it's called
95                 // because we don't *always* need to acquire the lock for the first test
96                 if ( _ports_changed )
97                 {
98                         cout << "MackieControlProtocol::update_ports updating" << endl;
99                         if ( pfd != 0 ) delete[] pfd;
100                         // TODO This might be a memory leak. How does thread cancellation cleanup work?
101                         pfd = new pollfd[_ports.size()];
102                         nfds = 0;
103
104                         for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
105                         {
106                                 cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
107                                 pfd[nfds].fd = (*it)->port().selectable();
108                                 pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
109                                 ++nfds;
110                         }
111                         _ports_changed = false;
112                 }
113                 cout << "MackieControlProtocol::update_ports signalling" << endl;
114                 update_cond.signal();
115                 cout << "MackieControlProtocol::update_ports finished" << endl;
116         }
117 }
118
119 void MackieControlProtocol::read_ports()
120 {
121         /* now read any data on the ports */
122         Glib::Mutex::Lock lock( update_mutex );
123         for ( int p = 0; p < nfds; ++p )
124         {
125                 // this will cause handle_midi_any in the MackiePort to be triggered
126                 if ( pfd[p].revents & POLLIN > 0 )
127                 {
128                         lock.release();
129                         _ports[p]->read();
130                         lock.acquire();
131                 }
132         }
133 }
134
135 bool MackieControlProtocol::poll_ports()
136 {
137         int timeout = 10; // milliseconds
138         int no_ports_sleep = 1000; // milliseconds
139
140         Glib::Mutex::Lock lock( update_mutex );
141         // if there are no ports
142         if ( nfds < 1 )
143         {
144                 lock.release();
145                 cout << "poll_ports no ports" << endl;
146                 usleep( no_ports_sleep * 1000 );
147                 return false;
148         }
149
150         int retval = poll( pfd, nfds, timeout );
151         if ( retval < 0 )
152         {
153                 // gdb at work, perhaps
154                 if ( errno != EINTR )
155                 {
156                         error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
157                 }
158                 return false;
159         }
160         
161         return retval > 0;
162 }
163
164 void MackieControlProtocol::handle_port_changed( SurfacePort * port, bool active )
165 {
166         cout << "MackieControlProtocol::handle_port_changed port: " << *port << " active: " << active << endl;
167         if ( active == false )
168         {
169                 // port gone away. So stop polling it ASAP
170                 {
171                         // delete the port instance
172                         Glib::Mutex::Lock lock( update_mutex );
173                         MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
174                         if ( it != _ports.end() )
175                         {
176                                 delete *it;
177                                 _ports.erase( it );
178                         }
179                 }
180                 _ports_changed = true;
181                 update_ports();
182         }
183         else
184         {
185 cout << __FILE__ << ':' << __LINE__ << endl;
186                 _ports_changed = true;
187 cout << __FILE__ << ':' << __LINE__ << endl;
188                 // port added
189                 update_ports();
190 cout << __FILE__ << ':' << __LINE__ << endl;
191                 update_surface();
192 cout << __FILE__ << ':' << __LINE__ << endl;
193                 
194                 // TODO update bank size
195                 
196                 // rebuild surface
197         }
198 }
199
200 void MackieControlProtocol::handle_port_init( Mackie::SurfacePort * sport )
201 {
202         cout << "MackieControlProtocol::handle_port_init" << endl;
203         _ports_changed = true;
204         update_ports();
205         cout << "MackieControlProtocol::handle_port_init finished" << endl;
206 }