Split butler into separate object (partially, just data so far...)
[ardour.git] / libs / ardour / session_export.cc
1 /*
2     Copyright (C) 1999-2008 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 <sigc++/bind.h>
21
22 #include "pbd/error.h"
23 #include <glibmm/thread.h>
24
25 #include "ardour/audio_diskstream.h"
26 #include "ardour/audioengine.h"
27 #include "ardour/butler.h"
28 #include "ardour/export_failed.h"
29 #include "ardour/export_file_io.h"
30 #include "ardour/export_handler.h"
31 #include "ardour/export_status.h"
32 #include "ardour/export_utilities.h"
33 #include "ardour/route.h"
34 #include "ardour/session.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 boost::shared_ptr<ExportHandler>
43 Session::get_export_handler ()
44 {
45         if (!export_handler) {
46                 export_handler.reset (new ExportHandler (*this));
47         }
48
49         return export_handler;
50 }
51
52 boost::shared_ptr<ExportStatus>
53 Session::get_export_status ()
54 {
55         if (!export_status) {
56                 export_status.reset (new ExportStatus ());
57         }
58
59         return export_status;
60 }
61
62
63 int
64 Session::pre_export ()
65 {
66         get_export_status (); // Init export_status
67
68         wait_till_butler_finished ();
69
70         /* take everyone out of awrite to avoid disasters */
71
72         {
73                 boost::shared_ptr<RouteList> r = routes.reader ();
74
75                 for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
76                         (*i)->protect_automation ();
77                 }
78         }
79
80         /* make sure we are actually rolling */
81
82         if (get_record_enabled()) {
83                 disable_record (false);
84         }
85
86         /* no slaving */
87
88         post_export_slave = Config->get_slave_source ();
89         post_export_position = _transport_frame;
90
91         Config->set_slave_source (None);
92
93         _exporting = true;
94         export_status->running = true;
95         export_status->Aborting.connect (sigc::hide_return (sigc::mem_fun (*this, &Session::stop_audio_export)));
96         export_status->Finished.connect (sigc::hide_return (sigc::mem_fun (*this, &Session::finalize_audio_export)));
97
98         return 0;
99 }
100
101 int
102 Session::start_audio_export (nframes_t position, bool realtime)
103 {
104         if (!_exporting) {
105                 pre_export ();
106         }
107
108         /* get everyone to the right position */
109
110         {
111                 boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
112
113                 for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
114                         if ((*i)-> seek (position, true)) {
115                                 error << string_compose (_("%1: cannot seek to %2 for export"),
116                                                   (*i)->name(), position)
117                                       << endmsg;
118                                 return -1;
119                         }
120                 }
121         }
122
123         /* we just did the core part of a locate() call above, but
124            for the sake of any GUI, put the _transport_frame in
125            the right place too.
126         */
127
128         _transport_frame = position;
129
130         _exporting_realtime = realtime;
131         export_status->stop = false;
132
133         /* get transport ready. note how this is calling butler functions
134            from a non-butler thread. we waited for the butler to stop
135            what it was doing earlier in Session::pre_export() and nothing
136            since then has re-awakened it.
137          */
138
139         set_transport_speed (1.0, false);
140         butler_transport_work ();
141         g_atomic_int_set (&butler->should_do_transport_work, 0);
142         post_transport ();
143
144         /* we are ready to go ... */
145
146         if (!_engine.connected()) {
147                 return -1;
148         }
149
150         if (realtime) {
151                 last_process_function = process_function;
152                 process_function = &Session::process_export;
153         } else {
154                 export_freewheel_connection = _engine.Freewheel.connect (sigc::mem_fun (*this, &Session::process_export_fw));
155                 return _engine.freewheel (true);
156         }
157
158         return 0;
159 }
160
161 void
162 Session::process_export (nframes_t nframes)
163 {
164         try {
165
166                 if (export_status->stop) {
167                         stop_audio_export ();
168                         return;
169                 }
170
171                 if (!_exporting_realtime) {
172                         /* make sure we've caught up with disk i/o, since
173                         we're running faster than realtime c/o JACK.
174                         */
175
176                         wait_till_butler_finished ();
177                 }
178
179                 /* do the usual stuff */
180
181                 process_without_events (nframes);
182
183                 /* handle export */
184
185                 ProcessExport (nframes);
186
187         } catch (ExportFailed e) {
188                 export_status->abort (true);
189         }
190 }
191
192 int
193 Session::process_export_fw (nframes_t nframes)
194 {
195         process_export (nframes);
196         return 0;
197 }
198
199 int
200 Session::stop_audio_export ()
201 {
202         if (_exporting_realtime) {
203                 process_function = last_process_function;
204         } else {
205                 export_freewheel_connection.disconnect();
206         }
207
208         /* can't use stop_transport() here because we need
209            an immediate halt and don't require all the declick
210            stuff that stop_transport() implements.
211         */
212
213         realtime_stop (true);
214         schedule_butler_transport_work ();
215
216         if (!export_status->aborted()) {
217                 ExportReadFinished ();
218         } else {
219                 finalize_audio_export ();
220         }
221
222         return 0;
223
224 }
225
226 void
227 Session::finalize_audio_export ()
228 {
229         _exporting = false;
230         export_status->running = false;
231
232         if (!_exporting_realtime) {
233                 _engine.freewheel (false);
234                 _exporting_realtime = false;
235         }
236
237         /* Clean up */
238
239         ProcessExport.clear();
240         ExportReadFinished.clear();
241         export_freewheel_connection.disconnect();
242         export_handler.reset();
243         export_status.reset();
244
245         /* restart slaving */
246
247         if (post_export_slave != None) {
248                 Config->set_slave_source (post_export_slave);
249         } else {
250                 locate (post_export_position, false, false, false);
251         }
252 }