fix clicking when processors become active/inactive; reduce crazy 2.5sec delay for...
[ardour.git] / libs / ardour / export_channel_configuration.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3     Author: Sakari Bergen
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "ardour/export_channel_configuration.h"
22
23 #include "ardour/export_handler.h"
24 #include "ardour/export_filename.h"
25 #include "ardour/export_processor.h"
26 #include "ardour/export_timespan.h"
27
28 #include "ardour/audio_port.h"
29 #include "ardour/export_failed.h"
30 #include "ardour/midi_port.h"
31 #include "ardour/session.h"
32 #include "ardour/audioengine.h"
33
34 #include "pbd/convert.h"
35 #include "pbd/pthread_utils.h"
36
37 using namespace PBD;
38
39 namespace ARDOUR
40 {
41
42 /* ExportChannelConfiguration */
43
44 ExportChannelConfiguration::ExportChannelConfiguration (Session & session) :
45   session (session),
46   writer_thread (*this),
47   status (session.get_export_status ()),
48   files_written (false),
49   split (false)
50 {
51
52 }
53
54
55 XMLNode &
56 ExportChannelConfiguration::get_state ()
57 {
58         XMLNode * root = new XMLNode ("ExportChannelConfiguration");
59         XMLNode * channel;
60         
61         root->add_property ("split", get_split() ? "true" : "false");
62         root->add_property ("channels", to_string (get_n_chans(), std::dec));
63         
64         uint32_t i = 1;
65         for (ExportChannelConfiguration::ChannelList::const_iterator c_it = channels.begin(); c_it != channels.end(); ++c_it) {
66                 channel = root->add_child ("Channel");
67                 if (!channel) { continue; }
68                 
69                 channel->add_property ("number", to_string (i, std::dec));
70                 (*c_it)->get_state (channel);
71                 
72                 ++i;
73         }
74         
75         return *root;
76 }
77
78 int
79 ExportChannelConfiguration::set_state (const XMLNode & root)
80 {
81         XMLProperty const * prop;
82         
83         if ((prop = root.property ("split"))) {
84                 set_split (!prop->value().compare ("true"));
85         }
86
87         XMLNodeList channels = root.children ("Channel");
88         for (XMLNodeList::iterator it = channels.begin(); it != channels.end(); ++it) {
89                 ExportChannelPtr channel (new PortExportChannel ());
90                 channel->set_state (*it, session);
91                 register_channel (channel);
92         }
93
94         return 0;
95 }
96
97 bool
98 ExportChannelConfiguration::all_channels_have_ports () const
99 {
100         for (ChannelList::const_iterator it = channels.begin(); it != channels.end(); ++it) {
101                 if ((*it)->empty ()) { return false; }
102         }
103         
104         return true;
105 }
106
107 bool
108 ExportChannelConfiguration::write_files (boost::shared_ptr<ExportProcessor> new_processor)
109 {
110         if (files_written || writer_thread.running) {
111                 return false;
112         }
113         
114         files_written = true;
115
116         if (!timespan) {
117                 throw ExportFailed (X_("Programming error: No timespan registered to channel configuration when requesting files to be written"));
118         }
119         
120         /* Take a local copy of the processor to be used in the thread that is created below */
121         
122         processor.reset (new_processor->copy());
123         
124         /* Create new thread for post processing */
125         
126         pthread_create (&writer_thread.thread, 0, _write_files, &writer_thread);
127         writer_thread.running = true;
128         pthread_detach (writer_thread.thread);
129         
130         return true;
131 }
132
133 void
134 ExportChannelConfiguration::write_file ()
135 {
136         timespan->rewind ();
137         nframes_t progress = 0;
138         nframes_t timespan_length = timespan->get_length();
139
140         nframes_t frames = 2048; // TODO good block size ?
141         nframes_t frames_read = 0;
142         
143         float * channel_buffer = new float [frames];
144         float * file_buffer = new float [channels.size() * frames];
145         uint32_t channel_count = channels.size();
146         uint32_t channel;
147         
148         do {
149                 if (status->aborted()) { break; }
150         
151                 channel = 0;
152                 for (ChannelList::iterator it = channels.begin(); it != channels.end(); ++it) {
153                         
154                         /* Get channel data */
155                         
156                         frames_read = timespan->get_data (channel_buffer, frames, *it);
157                         
158                         /* Interleave into file buffer */
159                         
160                         for (uint32_t i = 0; i < frames_read; ++i) {
161                                 file_buffer[channel + (channel_count * i)] = channel_buffer[i];
162                         }
163                         
164                         ++channel;
165                 }
166                 
167                 progress += frames_read;
168                 status->progress = (float) progress / timespan_length;
169                 
170         } while (processor->process (file_buffer, frames_read) > 0);
171         
172         delete [] channel_buffer;
173         delete [] file_buffer;
174 }
175
176 void *
177 ExportChannelConfiguration::_write_files (void *arg)
178 {
179         notify_gui_about_thread_creation (pthread_self(), "Export post-processing");
180         
181         // cc can be trated like 'this'
182         WriterThread & cc (*((WriterThread *) arg));
183         
184         try {
185                 for (FileConfigList::iterator it = cc->file_configs.begin(); it != cc->file_configs.end(); ++it) {
186                         if (cc->status->aborted()) {
187                                 break;
188                         }
189                         cc->processor->prepare (it->first, it->second, cc->channels.size(), cc->split, cc->timespan->get_start());
190                         cc->write_file (); // Writes tempfile
191                         cc->processor->prepare_post_processors ();
192                         cc->processor->write_files();
193                 }
194         } catch (ExportFailed & e) {
195                 cc->status->abort (true);
196         }
197         
198         cc.running = false;
199         cc->files_written = true;
200         cc->FilesWritten();
201         
202         return 0; // avoid compiler warnings
203 }
204
205 void
206 ExportChannelConfiguration::register_with_timespan (TimespanPtr new_timespan)
207 {
208         timespan = new_timespan;
209         
210         for (ChannelList::iterator it = channels.begin(); it != channels.end(); ++it) {
211                 timespan->register_channel (*it);
212         }
213 }
214
215 void
216 ExportChannelConfiguration::unregister_all ()
217 {
218         timespan.reset();
219         processor.reset();
220         file_configs.clear();
221         files_written = false;
222 }
223
224 } // namespace ARDOUR