cross-thread handling of SessionEvent allocation/deallocation, with widespread conseq...
[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 #include <string.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         if ( MIDI::Manager::instance()->port( default_port_name ) == 0 )
33         {
34                 info << "Mackie: No MIDI port called " << default_port_name << endmsg;
35                 return false;
36         }
37         else
38         {
39                 return true;
40         }
41 }
42
43 void * MackieControlProtocol::monitor_work()
44 {
45         register_thread (X_("MCU"));
46
47         pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
48         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
49
50         // read from midi ports
51         while ( _polling )
52         {
53                 try
54                 {
55                         if ( poll_ports() )
56                         {
57                                 try { read_ports(); }
58                                 catch ( exception & e ) {
59                                         cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
60                                         _ports_changed = true;
61                                         update_ports();
62                                 }
63                         }
64                         // poll for session data that needs to go to the unit
65                         poll_session_data();
66                 }
67                 catch ( exception & e )
68                 {
69                         cout << "caught exception in MackieControlProtocol::monitor_work " << e.what() << endl;
70                 }
71         }
72
73         // TODO ports and pfd and nfds should be in a separate class
74         delete[] pfd;
75         pfd = 0;
76         nfds = 0;
77
78         return (void*) 0;
79 }
80
81 void MackieControlProtocol::update_ports()
82 {
83 #ifdef DEBUG
84         cout << "MackieControlProtocol::update_ports" << endl;
85 #endif
86         if ( _ports_changed )
87         {
88                 Glib::Mutex::Lock ul( update_mutex );
89                 // yes, this is a double-test locking paradigm, or whatever it's called
90                 // because we don't *always* need to acquire the lock for the first test
91 #ifdef DEBUG
92                 cout << "MackieControlProtocol::update_ports lock acquired" << endl;
93 #endif
94                 if ( _ports_changed )
95                 {
96                         // create new pollfd structures
97                         delete[] pfd;
98                         pfd = new pollfd[_ports.size()];
99 #ifdef DEBUG
100                         cout << "pfd: " << pfd << endl;
101 #endif
102                         nfds = 0;
103                         for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
104                         {
105                                 // add the port any handler
106                                 (*it)->connect_any();
107 #ifdef DEBUG
108                                 cout << "adding pollfd for port " << (*it)->port().name() << " to pollfd " << nfds << endl;
109 #endif
110                                 pfd[nfds].fd = (*it)->port().selectable();
111                                 pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
112                                 ++nfds;
113                         }
114                         _ports_changed = false;
115                 }
116 #ifdef DEBUG
117                 cout << "MackieControlProtocol::update_ports signal" << endl;
118 #endif
119                 update_cond.signal();
120         }
121 #ifdef DEBUG
122         cout << "MackieControlProtocol::update_ports finish" << endl;
123 #endif
124 }
125
126 void MackieControlProtocol::read_ports()
127 {
128         /* now read any data on the ports */
129         Glib::Mutex::Lock lock( update_mutex );
130         for ( int p = 0; p < nfds; ++p )
131         {
132                 // this will cause handle_midi_any in the MackiePort to be triggered
133                 // for alsa/raw ports
134                 // alsa/sequencer ports trigger the midi parser off poll
135                 if ( (pfd[p].revents & POLLIN) > 0 )
136                 {
137                         // avoid deadlocking?
138                         // doesn't seem to make a difference
139                         //lock.release();
140                         _ports[p]->read();
141                         //lock.acquire();
142                 }
143         }
144 }
145
146 bool MackieControlProtocol::poll_ports()
147 {
148         int timeout = 10; // milliseconds
149         int no_ports_sleep = 1000; // milliseconds
150
151         Glib::Mutex::Lock lock( update_mutex );
152         // if there are no ports
153         if ( nfds < 1 )
154         {
155                 lock.release();
156 #ifdef DEBUG
157                 cout << "poll_ports no ports" << endl;
158 #endif
159                 usleep( no_ports_sleep * 1000 );
160                 return false;
161         }
162
163         int retval = ::poll( pfd, nfds, timeout );
164         if ( retval < 0 )
165         {
166                 // gdb at work, perhaps
167                 if ( errno != EINTR )
168                 {
169                         error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
170                 }
171                 return false;
172         }
173         
174         return retval > 0;
175 }
176
177 void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
178 {
179         // port gone away. So stop polling it ASAP
180         {
181                 // delete the port instance
182                 Glib::Mutex::Lock lock( update_mutex );
183                 MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
184                 if ( it != _ports.end() )
185                 {
186                         delete *it;
187                         _ports.erase( it );
188                 }
189         }
190         _ports_changed = true;
191         update_ports();
192         
193         // TODO all the rebuilding of surfaces and so on
194 }
195
196 void MackieControlProtocol::handle_port_active (SurfacePort *)
197 {
198         // no need to re-add port because it was already added
199         // during the init phase. So just update the local surface
200         // representation and send the representation to 
201         // all existing ports
202         
203         // TODO update bank size
204         
205         // TODO rebuild surface, to have new units
206         
207         // finally update session state to the surface
208         // TODO but this is also done in set_active, and
209         // in fact update_surface won't execute unless
210 #ifdef DEBUG
211         cout << "update_surface in handle_port_active" << endl;
212 #endif
213         // _active == true
214         update_surface();
215 }
216
217 void MackieControlProtocol::handle_port_init (Mackie::SurfacePort *)
218 {
219 #ifdef DEBUG
220         cout << "MackieControlProtocol::handle_port_init" << endl;
221 #endif
222         _ports_changed = true;
223         update_ports();
224 #ifdef DEBUG
225         cout << "MackieControlProtocol::handle_port_init finish" << endl;
226 #endif
227 }