1 /*
2 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2008-2013 Sakari Bergen <sakari.bergen@beatwaves.net>
5 * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
6 * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23
24 #include "pbd/error.h"
25 #include <glibmm/threads.h>
26 #include <glibmm/timer.h>
27
28 #include <midi++/mmc.h>
29
30 #include "ardour/audioengine.h"
31 #include "ardour/butler.h"
32 #include "ardour/export_handler.h"
33 #include "ardour/export_status.h"
34 #include "ardour/process_thread.h"
35 #include "ardour/session.h"
36 #include "ardour/track.h"
37 #include "ardour/transport_fsm.h"
38
39 #include "pbd/i18n.h"
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44
45 #define TFSM_ROLL() { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StartTransport)); }
46 #define TFSM_SPEED(speed,as_default) { _transport_fsm->enqueue (new TransportFSM::Event (speed,as_default)); }
47
48 boost::shared_ptr<ExportHandler>
get_export_handler()49 Session::get_export_handler ()
50 {
51 if (!export_handler) {
52 export_handler.reset (new ExportHandler (*this));
53 }
54
55 return export_handler;
56 }
57
58 boost::shared_ptr<ExportStatus>
get_export_status()59 Session::get_export_status ()
60 {
61 if (!export_status) {
62 export_status.reset (new ExportStatus ());
63 }
64
65 return export_status;
66 }
67
68
69 int
pre_export()70 Session::pre_export ()
71 {
72 get_export_status (); // Init export_status
73
74 /* take everyone out of awrite to avoid disasters */
75
76 {
77 boost::shared_ptr<RouteList> r = routes.reader ();
78
79 for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
80 (*i)->protect_automation ();
81 }
82 }
83
84 /* prepare transport */
85
86 realtime_stop (true, true);
87
88 if (get_record_enabled()) {
89 disable_record (false, true);
90 }
91
92 unset_play_loop ();
93
94 /* no slaving */
95
96 post_export_sync = config.get_external_sync ();
97 post_export_position = _transport_sample;
98
99 config.set_external_sync (false);
100
101 _export_xruns = 0;
102 _exporting = true;
103 export_status->set_running (true);
104 export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this, _1));
105
106 /* disable MMC output early */
107
108 _pre_export_mmc_enabled = _mmc->send_enabled ();
109 _mmc->enable_send (false);
110
111 return 0;
112 }
113
114 /** Called for each range that is being exported */
115 int
start_audio_export(samplepos_t position,bool realtime,bool region_export)116 Session::start_audio_export (samplepos_t position, bool realtime, bool region_export)
117 {
118 assert (!engine().in_process_thread ());
119
120 if (!_exporting) {
121 pre_export ();
122 } else if (_transport_fsm->transport_speed() != 0) {
123 realtime_stop (true, true);
124 }
125
126 _region_export = region_export;
127
128 if (region_export) {
129 _export_preroll = 0;
130 }
131 else if (realtime) {
132 _export_preroll = nominal_sample_rate ();
133 } else {
134 _export_preroll = Config->get_export_preroll() * nominal_sample_rate ();
135 }
136
137 if (_export_preroll == 0) {
138 // must be > 0 so that transport is started in sync.
139 _export_preroll = 1;
140 }
141
142 /* realtime_stop will have queued butler work (and TSFM),
143 * but the butler may not run immediately, so well have
144 * to wait for it to wake up and call
145 * non_realtime_stop ().
146 */
147 int sleeptm = std::max (40000, engine().usecs_per_cycle ());
148 int timeout = std::max (100, 8000000 / sleeptm);
149 do {
150 Glib::usleep (sleeptm);
151 sched_yield ();
152 } while (_transport_fsm->waiting_for_butler() && --timeout > 0);
153
154 if (timeout == 0) {
155 error << _("Cannot prepare transport for export") << endmsg;
156 return -1;
157 }
158
159 /* We're about to call Track::seek, so the butler must have finished everything
160 up otherwise it could be doing do_refill in its thread while we are doing
161 it here.
162 */
163
164 {
165 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
166 _butler->wait_until_finished ();
167
168 /* get everyone to the right position */
169
170 boost::shared_ptr<RouteList> rl = routes.reader();
171
172 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
173 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
174 if (tr && tr->seek (position, true)) {
175 error << string_compose (_("%1: cannot seek to %2 for export"),
176 (*i)->name(), position)
177 << endmsg;
178 return -1;
179 }
180 }
181 }
182
183 /* we just did the core part of a locate call above, but
184 for the sake of any GUI, put the _transport_sample in
185 the right place too.
186 */
187
188 _transport_sample = position;
189
190 if (!region_export) {
191 _remaining_latency_preroll = worst_latency_preroll_buffer_size_ceil ();
192 } else {
193 _remaining_latency_preroll = 0;
194 }
195
196 /* get transport ready. note how this is calling butler functions
197 from a non-butler thread. we waited for the butler to stop
198 what it was doing earlier in Session::pre_export() and nothing
199 since then has re-awakened it.
200 */
201
202 /* we are ready to go ... */
203
204 if (!_engine.running()) {
205 return -1;
206 }
207
208 assert (!_engine.freewheeling ());
209 assert (!_engine.in_process_thread ());
210
211 if (realtime) {
212 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
213 _export_rolling = true;
214 _realtime_export = true;
215 export_status->stop = false;
216 process_function = &Session::process_export_fw;
217 /* this is required for ExportGraphBuilder::Intermediate::start_post_processing */
218 _engine.Freewheel.connect_same_thread (export_freewheel_connection, boost::bind (&Session::process_export_fw, this, _1));
219 reset_xrun_count ();
220 return 0;
221 } else {
222 if (_realtime_export) {
223 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
224 process_function = &Session::process_with_events;
225 }
226 _realtime_export = false;
227 _export_rolling = true;
228 export_status->stop = false;
229 _engine.Freewheel.connect_same_thread (export_freewheel_connection, boost::bind (&Session::process_export_fw, this, _1));
230 reset_xrun_count ();
231 return _engine.freewheel (true);
232 }
233 }
234
235 void
process_export(pframes_t nframes)236 Session::process_export (pframes_t nframes)
237 {
238 if (_export_rolling && export_status->stop) {
239 stop_audio_export ();
240 }
241
242 /* for Region Raw or Fades, we can skip this
243 * RegionExportChannelFactory::update_buffers() does not care
244 * about anything done here
245 */
246 if (!_region_export) {
247 if (_export_rolling) {
248 if (!_realtime_export) {
249 /* make sure we've caught up with disk i/o, since
250 * we're running faster than realtime c/o JACK.
251 */
252 _butler->wait_until_finished ();
253 }
254
255 /* do the usual stuff */
256
257 process_without_events (nframes);
258
259 } else if (_realtime_export) {
260 fail_roll (nframes); // somehow we need to silence _ALL_ output buffers
261 }
262 }
263
264 try {
265 /* handle export - XXX what about error handling? */
266
267 if (ProcessExport (nframes).value_or (0) > 0) {
268 /* last cycle completed */
269 assert (_export_rolling);
270 stop_audio_export ();
271 }
272
273 } catch (std::exception & e) {
274 error << string_compose (_("Export ended unexpectedly: %1"), e.what()) << endmsg;
275 export_status->abort (true);
276 }
277 }
278
279 void
process_export_fw(pframes_t nframes)280 Session::process_export_fw (pframes_t nframes)
281 {
282 if (!_export_rolling) {
283 try {
284 ProcessExport (0);
285 } catch (std::exception & e) {
286 /* pre-roll export must not throw */
287 assert (0);
288 export_status->abort (true);
289 }
290 return;
291 }
292
293 const bool need_buffers = _engine.freewheeling ();
294 if (_export_preroll > 0) {
295
296 if (need_buffers) {
297 _engine.main_thread()->get_buffers ();
298 }
299 fail_roll (nframes);
300 if (need_buffers) {
301 _engine.main_thread()->drop_buffers ();
302 }
303
304 _export_preroll -= std::min ((samplepos_t)nframes, _export_preroll);
305
306 if (_export_preroll > 0) {
307 // clear out buffers (reverb tails etc).
308 return;
309 }
310
311 TFSM_SPEED (1.0, false);
312 TFSM_ROLL ();
313 _butler->schedule_transport_work ();
314
315 /* Session::process_with_events () sets _remaining_latency_preroll = 0
316 * when being called with _transport_fsm->transport_speed() == 0.
317 *
318 * This can happen wit JACK, there is a process-callback before
319 * freewheeling becomes active, after Session::start_audio_export().
320 */
321 if (!_region_export) {
322 _remaining_latency_preroll = worst_latency_preroll_buffer_size_ceil ();
323 }
324
325 return;
326 }
327
328 /* wait for butler to complete schedule_transport_work(),
329 * compare to Session::process */
330 if (non_realtime_work_pending ()) {
331 if (_butler->transport_work_requested ()) {
332 /* butler is still processing */
333 return;
334 }
335 butler_completed_transport_work ();
336 }
337
338 if (_remaining_latency_preroll > 0) {
339 samplepos_t remain = std::min ((samplepos_t)nframes, _remaining_latency_preroll);
340
341 if (need_buffers) {
342 _engine.main_thread()->get_buffers ();
343 }
344
345 assert (_count_in_samples == 0);
346 while (remain > 0) {
347 samplecnt_t ns = calc_preroll_subcycle (remain);
348
349 bool session_needs_butler = false;
350 if (process_routes (ns, session_needs_butler)) {
351 fail_roll (ns);
352 }
353
354 try {
355 ProcessExport (ns);
356 } catch (std::exception & e) {
357 /* pre-roll export must not throw */
358 assert (0);
359 export_status->abort (true);
360 }
361
362 _remaining_latency_preroll -= ns;
363 remain -= ns;
364 nframes -= ns;
365
366 if (remain != 0) {
367 _engine.split_cycle (ns);
368 }
369 }
370
371 if (need_buffers) {
372 _engine.main_thread()->drop_buffers ();
373 }
374
375 if (nframes == 0) {
376 return;
377 }
378 }
379
380 if (need_buffers) {
381 _engine.main_thread()->get_buffers ();
382 }
383 process_export (nframes);
384 if (need_buffers) {
385 _engine.main_thread()->drop_buffers ();
386 }
387
388 return;
389 }
390
391 int
stop_audio_export()392 Session::stop_audio_export ()
393 {
394 /* can't use stop_transport() here because we need
395 an synchronous halt and don't require all the declick
396 stuff that stop_transport() implements.
397 */
398
399 realtime_stop (true, true);
400 flush_all_inserts ();
401 _export_rolling = false;
402 _butler->schedule_transport_work ();
403 reset_xrun_count ();
404
405 return 0;
406 }
407
408 void
finalize_audio_export(TransportRequestSource trs)409 Session::finalize_audio_export (TransportRequestSource trs)
410 {
411 /* This is called as a handler for the Finished signal, which is
412 emitted by a UI component once the ExportStatus object associated
413 with this export indicates that it has finished. It runs in the UI
414 thread that emits the signal.
415 */
416
417 _exporting = false;
418
419 if (_export_rolling) {
420 stop_audio_export ();
421 }
422
423 /* Clean up */
424
425 if (_realtime_export) {
426 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
427 process_function = &Session::process_with_events;
428 }
429 _engine.freewheel (false);
430 export_freewheel_connection.disconnect();
431
432 _mmc->enable_send (_pre_export_mmc_enabled);
433
434 /* maybe write CUE/TOC */
435
436 export_handler.reset();
437 export_status.reset();
438
439 /* restart slaving */
440
441 if (post_export_sync) {
442 config.set_external_sync (true);
443 } else {
444 request_locate (post_export_position, MustStop, trs);
445 }
446 }
447