committed RWlock fixes to libardour. added hw monitoring fixes from nick_m. minor...
[ardour.git] / libs / ardour / session_feedback.cc
1 /*
2     Copyright (C) 2004 Paul 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 <string>
22 #include <cmath>
23 #include <cerrno>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <poll.h>
27
28 #include <midi++/types.h>
29 #include <midi++/port.h>
30 #include <midi++/manager.h>
31 #include <pbd/error.h>
32 #include <pbd/lockmonitor.h>
33 #include <pbd/pthread_utils.h>
34
35 #include <ardour/configuration.h>
36 #include <ardour/audioengine.h>
37 #include <ardour/session.h>
38 #include <ardour/audio_track.h>
39 #include <ardour/diskstream.h>
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 //using namespace sigc;
46
47 int
48 Session::init_feedback ()
49 {
50         if (pipe (feedback_request_pipe) != 0) {
51                 error << string_compose (_("cannot create feedback request pipe (%1)"),
52                                   strerror (errno))
53                       << endmsg;
54                 return -1;
55         }
56
57         if (fcntl (feedback_request_pipe[0], F_SETFL, O_NONBLOCK)) {
58                 error << string_compose(_("UI: cannot set O_NONBLOCK on "    "signal read pipe (%1)"), strerror (errno)) << endmsg;
59                 return -1;
60         }
61
62         if (fcntl (feedback_request_pipe[1], F_SETFL, O_NONBLOCK)) {
63                 error << string_compose(_("UI: cannot set O_NONBLOCK on "    "signal write pipe (%1)"), strerror (errno)) << endmsg;
64                 return -1;
65         }
66
67         active_feedback = 0;
68         midi_feedback = false;
69
70         /* add possible feedback functions here */
71         
72         feedback_functions.push_back (mem_fun (*this, &Session::feedback_generic_midi_function));
73
74         if (pthread_create_and_store ("feedback", &feedback_thread, 0, _feedback_thread_work, this)) {
75                 error << _("Session: could not create feedback thread") << endmsg;
76                 return -1;
77         }
78
79         return 0;
80 }       
81
82 int
83 Session::poke_feedback (FeedbackRequest::Type why)
84 {
85         char c = (char) why;
86         return !(write (feedback_request_pipe[1], &c, 1) == 1);
87 }
88
89 int
90 Session::start_feedback ()
91 {
92         return poke_feedback (FeedbackRequest::Start);
93 }
94
95 int
96 Session::stop_feedback ()
97 {
98         return poke_feedback (FeedbackRequest::Stop);
99 }
100
101 void
102 Session::terminate_feedback ()
103 {
104         void* status;
105         poke_feedback (FeedbackRequest::Quit);
106         pthread_join (feedback_thread, &status);
107 }
108
109 void*
110 Session::_feedback_thread_work (void* arg)
111 {
112         return static_cast<Session*> (arg)->feedback_thread_work ();
113 }
114
115 void*
116 Session::feedback_thread_work ()
117 {
118         PBD::ThreadCreated (pthread_self(), X_("Feedback"));
119         struct pollfd pfd[1];
120         int timeout;
121
122         pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
123         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
124
125         if (active_feedback) {
126                 /* XXX use Config->feedback_interval_usecs()*/;
127                 timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
128         } else {
129                 timeout = -1;
130         }
131
132         while (1) {
133
134                 pfd[0].fd = feedback_request_pipe[0];
135                 pfd[0].events = POLLIN|POLLHUP|POLLERR;
136
137                 if (poll (pfd, 1, timeout) < 0) {
138                         if (errno == EINTR) {
139                                 continue;
140                         }
141                         error << string_compose (_("Feedback thread poll failed (%1)"),
142                                           strerror (errno))
143                               << endmsg;
144                         break;
145                 }
146
147                 if (pfd[0].revents & ~POLLIN) {
148                         error << _("Error on feedback thread request pipe") << endmsg;
149                         break;
150                 }
151
152                 if (pfd[0].revents & POLLIN) {
153
154                         char req;
155                         
156                         /* empty the pipe of all current requests */
157
158                         while (1) {
159                                 size_t nread = read (feedback_request_pipe[0], &req, sizeof (req));
160
161                                 if (nread == 1) {
162                                         switch ((FeedbackRequest::Type) req) {
163                                         
164                                         case FeedbackRequest::Start:
165                                                 timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
166                                                 active_feedback++;
167                                                 break;
168                                                 
169                                         case FeedbackRequest::Stop:
170                                                 timeout = -1;
171                                                 if (active_feedback) {
172                                                         active_feedback--;
173                                                 } 
174                                                 break;
175                                                 
176                                         case FeedbackRequest::Quit:
177                                                 pthread_exit_pbd (0);
178                                                 /*NOTREACHED*/
179                                                 break;
180                                                 
181                                         default:
182                                                 break;
183                                         }
184
185                                 } else if (nread == 0) {
186                                         break;
187                                 } else if (errno == EAGAIN) {
188                                         break;
189                                 } else {
190                                         fatal << _("Error reading from feedback request pipe") << endmsg;
191                                         /*NOTREACHED*/
192                                 }
193                         }
194                 }
195                 
196                 if (!active_feedback || transport_stopped()) {
197                         continue;
198                 }
199
200                 for (list<FeedbackFunctionPtr>::iterator i = feedback_functions.begin(); i != feedback_functions.end(); ) {
201                         
202                         list<FeedbackFunctionPtr>::iterator tmp;
203                         
204                         tmp = i;
205                         ++tmp;
206                         
207                         if ((*i)) {
208                                 feedback_functions.erase (i);
209                         }
210                         
211                         i = tmp;
212                 }
213         }
214         
215         return 0;
216 }
217
218 int
219 Session::feedback_generic_midi_function ()
220 {
221         const int32_t bufsize = 16 * 1024;
222         int32_t bsize = bufsize;
223         MIDI::byte* buf = new MIDI::byte[bufsize];
224         MIDI::byte* end = buf;
225
226         {
227                 RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
228                 
229                 for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
230                         end = (*i)->write_midi_feedback (end, bsize);
231                 }
232         }
233
234         if (end == buf) {
235                 delete [] buf;
236                 return 0;
237         } 
238         
239         deliver_midi (_midi_port, buf, (int32_t) (end - buf));
240
241         //cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n";
242                 
243
244         return 0;
245 }
246