Add MIDI readahead to options editor.
[ardour.git] / libs / ardour / butler.cc
1 /*
2     Copyright (C) 1999-2009 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 */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #ifndef PLATFORM_WINDOWS
25 #include <poll.h>
26 #endif
27
28 #include "pbd/error.h"
29 #include "pbd/pthread_utils.h"
30 #include "ardour/debug.h"
31 #include "ardour/butler.h"
32 #include "ardour/io.h"
33 #include "ardour/midi_diskstream.h"
34 #include "ardour/session.h"
35 #include "ardour/track.h"
36 #include "ardour/auditioner.h"
37
38 #include "i18n.h"
39
40 using namespace PBD;
41
42 namespace ARDOUR {
43
44 Butler::Butler(Session& s)
45         : SessionHandleRef (s)
46         , thread()
47         , have_thread (false)
48         , audio_dstream_capture_buffer_size(0)
49         , audio_dstream_playback_buffer_size(0)
50         , midi_dstream_buffer_size(0)
51         , pool_trash(16)
52         , _xthread (true)
53 {
54         g_atomic_int_set(&should_do_transport_work, 0);
55         SessionEvent::pool->set_trash (&pool_trash);
56
57         Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Butler::config_changed, this, _1));
58 }
59
60 Butler::~Butler()
61 {
62         terminate_thread ();
63 }
64
65 void
66 Butler::config_changed (std::string p)
67 {
68         if (p == "playback-buffer-seconds") {
69                 /* size is in Samples, not bytes */
70                 audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
71                 _session.adjust_playback_buffering ();
72         } else if (p == "capture-buffer-seconds") {
73                 audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
74                 _session.adjust_capture_buffering ();
75         } else if (p == "midi-readahead") {
76                 MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * _session.frame_rate()));
77         }
78 }
79
80 int
81 Butler::start_thread()
82 {
83         const float rate = (float)_session.frame_rate();
84
85         /* size is in Samples, not bytes */
86         audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * rate);
87         audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * rate);
88
89         /* size is in bytes
90          * XXX: Jack needs to tell us the MIDI buffer size
91          * (i.e. how many MIDI bytes we might see in a cycle)
92          */
93         midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * rate);
94
95         MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
96
97         should_run = false;
98
99         if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
100                 error << _("Session: could not create butler thread") << endmsg;
101                 return -1;
102         }
103
104         //pthread_detach (thread);
105         have_thread = true;
106         return 0;
107 }
108
109 void
110 Butler::terminate_thread ()
111 {
112         if (have_thread) {
113                 void* status;
114                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: ask butler to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
115                 queue_request (Request::Quit);
116                 pthread_join (thread, &status);
117         }
118 }
119
120 void *
121 Butler::_thread_work (void* arg)
122 {
123         SessionEvent::create_per_thread_pool ("butler events", 4096);
124         pthread_set_name (X_("butler"));
125         return ((Butler *) arg)->thread_work ();
126 }
127
128 void *
129 Butler::thread_work ()
130 {
131         uint32_t err = 0;
132
133         bool disk_work_outstanding = false;
134         RouteList::iterator i;
135
136         while (true) {
137                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler main loop, disk work outstanding ? %2 @ %3\n", DEBUG_THREAD_SELF, disk_work_outstanding, g_get_monotonic_time()));
138
139                 if(!disk_work_outstanding) {
140                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler waits for requests @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
141
142                         char msg;
143                         /* empty the pipe of all current requests */
144                         if (_xthread.receive (msg, true) >= 0) {
145                                 Request::Type req = (Request::Type) msg;
146                                 switch (req) {
147
148                                         case Request::Run:
149                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
150                                                 should_run = true;
151                                                 break;
152
153                                         case Request::Pause:
154                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
155                                                 should_run = false;
156                                                 break;
157
158                                         case Request::Quit:
159                                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler asked to quit @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
160                                                 return 0;
161                                                 abort(); /*NOTREACHED*/
162                                                 break;
163
164                                         default:
165                                                 break;
166                                 }
167                         }
168                 }
169
170                 
171           restart:
172                 DEBUG_TRACE (DEBUG::Butler, "at restart for disk work\n");
173                 disk_work_outstanding = false;
174
175                 if (transport_work_requested()) {
176                         DEBUG_TRACE (DEBUG::Butler, string_compose ("do transport work @ %1\n", g_get_monotonic_time()));
177                         _session.butler_transport_work ();
178                         DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttransport work complete @ %1\n", g_get_monotonic_time()));
179                 }
180
181                 frameoffset_t audition_seek;
182                 if (should_run && _session.is_auditioning()
183                                 && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
184                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (_session.the_auditioner());
185                         DEBUG_TRACE (DEBUG::Butler, "seek the auditioner\n");
186                         tr->seek(audition_seek);
187                         _session.the_auditioner()->seek_response(audition_seek);
188                 }
189
190                 boost::shared_ptr<RouteList> rl = _session.get_routes();
191
192                 RouteList rl_with_auditioner = *rl;
193                 rl_with_auditioner.push_back (_session.the_auditioner());
194
195                 for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
196
197                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
198
199                         if (!tr) {
200                                 continue;
201                         }
202
203                         boost::shared_ptr<IO> io = tr->input ();
204
205                         if (io && !io->active()) {
206                                 /* don't read inactive tracks */
207                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
208                                 continue;
209                         }
210                         DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
211                         switch (tr->do_refill ()) {
212                         case 0:
213                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
214                                 break;
215                                 
216                         case 1:
217                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name()));
218                                 disk_work_outstanding = true;
219                                 break;
220
221                         default:
222                                 error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
223                                 std::cerr << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << std::endl;
224                                 break;
225                         }
226
227                 }
228
229                 if (i != rl_with_auditioner.begin() && i != rl_with_auditioner.end()) {
230                         /* we didn't get to all the streams */
231                         disk_work_outstanding = true;
232                 }
233
234                 if (!err && transport_work_requested()) {
235                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during refill, back to restart\n");
236                         goto restart;
237                 }
238
239                 for (i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
240                         // cerr << "write behind for " << (*i)->name () << endl;
241
242                         boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
243
244                         if (!tr) {
245                                 continue;
246                         }
247
248                         /* note that we still try to flush diskstreams attached to inactive routes
249                          */
250
251                         gint64 before, after;
252                         int ret;
253
254                         DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
255                         before = g_get_monotonic_time ();
256                         ret = tr->do_flush (ButlerContext);
257                         after = g_get_monotonic_time ();
258                         switch (ret) {
259                         case 0:
260                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1, %2 usecs\n", tr->name(), after - before));
261                                 break;
262                                 
263                         case 1:
264                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1, %2 usecs\n", tr->name(), after - before));
265                                 disk_work_outstanding = true;
266                                 break;
267
268                         default:
269                                 err++;
270                                 error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
271                                 std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
272                                 /* don't break - try to flush all streams in case they
273                                    are split across disks.
274                                 */
275                         }
276                 }
277
278                 if (err && _session.actively_recording()) {
279                         /* stop the transport and try to catch as much possible
280                            captured state as we can.
281                         */
282                         DEBUG_TRACE (DEBUG::Butler, "error occurred during recording - stop transport\n");
283                         _session.request_stop ();
284                 }
285
286                 if (i != rl->begin() && i != rl->end()) {
287                         /* we didn't get to all the streams */
288                         DEBUG_TRACE (DEBUG::Butler, "not all tracks processed, will need to go back for more\n");
289                         disk_work_outstanding = true;
290                 }
291
292                 if (!err && transport_work_requested()) {
293                         DEBUG_TRACE (DEBUG::Butler, "transport work requested during flush, back to restart\n");
294                         goto restart;
295                 }
296
297                 if (!disk_work_outstanding) {
298                         _session.refresh_disk_space ();
299                 }
300
301
302                 {
303                         Glib::Threads::Mutex::Lock lm (request_lock);
304
305                         if (should_run && (disk_work_outstanding || transport_work_requested())) {
306                                 DEBUG_TRACE (DEBUG::Butler, string_compose ("at end, should run %1 disk work %2 transport work %3 ... goto restart\n",
307                                                                             should_run, disk_work_outstanding, transport_work_requested()));
308                                 goto restart;
309                         }
310
311                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: butler signals pause @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
312                         paused.signal();
313                 }
314
315                 DEBUG_TRACE (DEBUG::Butler, "butler emptying pool trash\n");
316                 empty_pool_trash ();
317         }
318
319         return (0);
320 }
321
322 void
323 Butler::schedule_transport_work ()
324 {
325         g_atomic_int_inc (&should_do_transport_work);
326         summon ();
327 }
328
329 void
330 Butler::queue_request (Request::Type r)
331 {
332         char c = r;
333         if (_xthread.deliver (c) != 1) {
334                 /* the x-thread channel is non-blocking
335                  * write may fail, but we really don't want to wait
336                  * under normal circumstances.
337                  *
338                  * a lost "run" requests under normal RT operation
339                  * is mostly harmless.
340                  *
341                  * TODO if ardour is freehweeling, wait & retry.
342                  * ditto for Request::Type Quit
343                  */
344                 assert(1); // we're screwd
345         }
346 }
347
348 void
349 Butler::summon ()
350 {
351         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: summon butler to run @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
352         queue_request (Request::Run);
353 }
354
355 void
356 Butler::stop ()
357 {
358         Glib::Threads::Mutex::Lock lm (request_lock);
359         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: asking butler to stop @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
360         queue_request (Request::Pause);
361         paused.wait(request_lock);
362 }
363
364 void
365 Butler::wait_until_finished ()
366 {
367         Glib::Threads::Mutex::Lock lm (request_lock);
368         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: waiting for butler to finish @ %2\n", DEBUG_THREAD_SELF, g_get_monotonic_time()));
369         queue_request (Request::Pause);
370         paused.wait(request_lock);
371 }
372
373 bool
374 Butler::transport_work_requested () const
375 {
376         return g_atomic_int_get(&should_do_transport_work);
377 }
378
379 void
380 Butler::empty_pool_trash ()
381 {
382         /* look in the trash, deleting empty pools until we come to one that is not empty */
383
384         RingBuffer<CrossThreadPool*>::rw_vector vec;
385         pool_trash.get_read_vector (&vec);
386
387         guint deleted = 0;
388
389         for (int i = 0; i < 2; ++i) {
390                 for (guint j = 0; j < vec.len[i]; ++j) {
391                         if (vec.buf[i][j]->empty()) {
392                                 delete vec.buf[i][j];
393                                 ++deleted;
394                         } else {
395                                 /* found a non-empty pool, so stop deleting */
396                                 if (deleted) {
397                                         pool_trash.increment_read_idx (deleted);
398                                 }
399                                 return;
400                         }
401                 }
402         }
403
404         if (deleted) {
405                 pool_trash.increment_read_idx (deleted);
406         }
407 }
408
409 void
410 Butler::drop_references ()
411 {
412         std::cerr << "Butler drops pool trash\n";
413         SessionEvent::pool->set_trash (0);
414 }
415
416
417 } // namespace ARDOUR
418