Add stem export dialog and make all different export dialogs save their config to...
[ardour.git] / libs / ardour / export_graph_builder.cc
1 #include "ardour/export_graph_builder.h"
2
3 #include "audiographer/process_context.h"
4 #include "audiographer/general/interleaver.h"
5 #include "audiographer/general/normalizer.h"
6 #include "audiographer/general/peak_reader.h"
7 #include "audiographer/general/sample_format_converter.h"
8 #include "audiographer/general/sr_converter.h"
9 #include "audiographer/general/silence_trimmer.h"
10 #include "audiographer/general/threader.h"
11 #include "audiographer/sndfile/tmp_file.h"
12 #include "audiographer/sndfile/sndfile_writer.h"
13
14 #include "ardour/audioengine.h"
15 #include "ardour/export_channel_configuration.h"
16 #include "ardour/export_filename.h"
17 #include "ardour/export_format_specification.h"
18 #include "ardour/export_timespan.h"
19 #include "ardour/sndfile_helpers.h"
20
21 #include "pbd/filesystem.h"
22
23 using namespace AudioGrapher;
24 using std::string;
25
26 namespace ARDOUR {
27
28 ExportGraphBuilder::ExportGraphBuilder (Session const & session)
29   : session (session)
30   , thread_pool (4) // FIXME thread amount to cores amount
31 {
32         process_buffer_frames = session.engine().frames_per_cycle();
33         process_buffer = new Sample[process_buffer_frames];
34 }
35
36 ExportGraphBuilder::~ExportGraphBuilder ()
37 {
38         delete [] process_buffer;
39 }
40
41 int
42 ExportGraphBuilder::process (framecnt_t frames, bool last_cycle)
43 {
44         assert(frames <= process_buffer_frames);
45         
46         for (ChannelMap::iterator it = channels.begin(); it != channels.end(); ++it) {
47                 it->first->read (process_buffer, frames);
48                 ProcessContext<Sample> context(process_buffer, frames, 1);
49                 if (last_cycle) { context.set_flag (ProcessContext<Sample>::EndOfInput); }
50                 it->second->process (context);
51         }
52         
53         return 0;
54 }
55
56 bool
57 ExportGraphBuilder::process_normalize ()
58 {
59         for (std::list<Normalizer *>::iterator it = normalizers.begin(); it != normalizers.end(); /* ++ in loop */) {
60                 if ((*it)->process()) {
61                         it = normalizers.erase (it);
62                 } else {
63                         ++it;
64                 }
65         }
66         
67         return normalizers.empty();
68 }
69
70 void
71 ExportGraphBuilder::reset ()
72 {
73         timespan.reset();
74         channel_configs.clear ();
75         channels.clear ();
76         normalizers.clear ();
77 }
78
79 void
80 ExportGraphBuilder::set_current_timespan (boost::shared_ptr<ExportTimespan> span)
81 {
82         timespan = span;
83 }
84
85 void
86 ExportGraphBuilder::add_config (FileSpec const & config)
87 {
88         // If the sample rate is "session rate", change it to the real value.
89         // However, we need to copy it to not change the config which is saved...
90         FileSpec new_config (config);
91         new_config.format.reset(new ExportFormatSpecification(*new_config.format));
92         if(new_config.format->sample_rate() == ExportFormatBase::SR_Session) {
93                 framecnt_t session_rate = session.nominal_frame_rate();
94                 new_config.format->set_sample_rate(ExportFormatBase::nearest_sample_rate(session_rate));
95         }
96         
97         
98         if (!new_config.channel_config->get_split ()) {
99                 add_split_config (new_config);
100                 return;
101         }
102         
103         // Split channel configurations are split into several channel configurations,
104         // each corresponding to a file, at this stage
105         typedef std::list<boost::shared_ptr<ExportChannelConfiguration> > ConfigList;
106         ConfigList file_configs;
107         new_config.channel_config->configurations_for_files (file_configs);
108         
109         unsigned chan = 1;
110         for (ConfigList::iterator it = file_configs.begin(); it != file_configs.end(); ++it, ++chan) {
111                 FileSpec copy = new_config;
112                 copy.channel_config = *it;
113                 
114                 copy.filename.reset (new ExportFilename (*copy.filename));
115                 copy.filename->include_channel = true;
116                 copy.filename->set_channel (chan);
117                 
118                 add_split_config (copy);
119         }
120 }
121
122 void
123 ExportGraphBuilder::add_split_config (FileSpec const & config)
124 {
125         for (ChannelConfigList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
126                 if (*it == config) {
127                         it->add_child (config);
128                         return;
129                 }
130         }
131         
132         // No duplicate channel config found, create new one
133         channel_configs.push_back (new ChannelConfig (*this, config, channels));
134 }
135
136 /* Encoder */
137
138 template <>
139 boost::shared_ptr<AudioGrapher::Sink<Sample> >
140 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
141 {
142         config = new_config;
143         init_writer (float_writer);
144         return float_writer;
145 }
146
147 template <>
148 boost::shared_ptr<AudioGrapher::Sink<int> >
149 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
150 {
151         config = new_config;
152         init_writer (int_writer);
153         return int_writer;
154 }
155
156 template <>
157 boost::shared_ptr<AudioGrapher::Sink<short> >
158 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
159 {
160         config = new_config;
161         init_writer (short_writer);
162         return short_writer;
163 }
164
165 void
166 ExportGraphBuilder::Encoder::add_child (FileSpec const & new_config)
167 {
168         filenames.push_back (new_config.filename);
169 }
170
171 bool
172 ExportGraphBuilder::Encoder::operator== (FileSpec const & other_config) const
173 {
174         return get_real_format (config) == get_real_format (other_config);
175 }
176
177 int
178 ExportGraphBuilder::Encoder::get_real_format (FileSpec const & config)
179 {
180         ExportFormatSpecification & format = *config.format;
181         return format.format_id() | format.sample_format() | format.endianness();
182 }
183
184 template<typename T>
185 void
186 ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::SndfileWriter<T> > & writer)
187 {
188         unsigned channels = config.channel_config->get_n_chans();
189         int format = get_real_format (config);
190         config.filename->set_channel_config(config.channel_config);
191         string filename = config.filename->get_path (config.format);
192         
193         writer.reset (new AudioGrapher::SndfileWriter<T> (filename, format, channels, config.format->sample_rate(), config.broadcast_info));
194         writer->FileWritten.connect_same_thread (copy_files_connection, boost::bind (&ExportGraphBuilder::Encoder::copy_files, this, _1));
195 }
196
197 void
198 ExportGraphBuilder::Encoder::copy_files (std::string orig_path)
199 {
200         while (filenames.size()) {
201                 FilenamePtr & filename = filenames.front();
202                 PBD::sys::copy_file (orig_path, filename->get_path (config.format).c_str());
203                 filenames.pop_front();
204         }
205 }
206
207 /* SFC */
208
209 ExportGraphBuilder::SFC::SFC (ExportGraphBuilder &, FileSpec const & new_config, framecnt_t max_frames)
210   : data_width(0)
211 {
212         config = new_config;
213         data_width = sndfile_data_width (Encoder::get_real_format (config));
214         unsigned channels = new_config.channel_config->get_n_chans();
215         
216         if (data_width == 8 || data_width == 16) {
217                 short_converter = ShortConverterPtr (new SampleFormatConverter<short> (channels));
218                 short_converter->init (max_frames, config.format->dither_type(), data_width);
219                 add_child (config);
220         } else if (data_width == 24 || data_width == 32) {
221                 int_converter = IntConverterPtr (new SampleFormatConverter<int> (channels));
222                 int_converter->init (max_frames, config.format->dither_type(), data_width);
223                 add_child (config);
224         } else {
225                 int actual_data_width = 8 * sizeof(Sample);
226                 float_converter = FloatConverterPtr (new SampleFormatConverter<Sample> (channels));
227                 float_converter->init (max_frames, config.format->dither_type(), actual_data_width);
228                 add_child (config);
229         }
230 }
231
232 ExportGraphBuilder::FloatSinkPtr
233 ExportGraphBuilder::SFC::sink ()
234 {
235         if (data_width == 8 || data_width == 16) {
236                 return short_converter;
237         } else if (data_width == 24 || data_width == 32) {
238                 return int_converter;
239         } else {
240                 return float_converter;
241         }
242 }
243
244 void
245 ExportGraphBuilder::SFC::add_child (FileSpec const & new_config)
246 {
247         for (boost::ptr_list<Encoder>::iterator it = children.begin(); it != children.end(); ++it) {
248                 if (*it == new_config) {
249                         it->add_child (new_config);
250                         return;
251                 }
252         }
253         
254         children.push_back (new Encoder());
255         Encoder & encoder = children.back();
256         
257         if (data_width == 8 || data_width == 16) {
258                 short_converter->add_output (encoder.init<short> (new_config));
259         } else if (data_width == 24 || data_width == 32) {
260                 int_converter->add_output (encoder.init<int> (new_config));
261         } else {
262                 float_converter->add_output (encoder.init<Sample> (new_config));
263         }
264 }
265
266 bool
267 ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const
268 {
269         return config.format->sample_format() == other_config.format->sample_format();
270 }
271
272 /* Normalizer */
273
274 ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t /*max_frames*/)
275   : parent (parent)
276 {
277         config = new_config;
278         max_frames_out = 4086; // TODO good chunk size
279         
280         buffer.reset (new AllocatingProcessContext<Sample> (max_frames_out, config.channel_config->get_n_chans()));
281         peak_reader.reset (new PeakReader ());
282         normalizer.reset (new AudioGrapher::Normalizer (config.format->normalize_target()));
283         threader.reset (new Threader<Sample> (parent.thread_pool));
284         
285         normalizer->alloc_buffer (max_frames_out);
286         normalizer->add_output (threader);
287         
288         int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float;
289         tmp_file.reset (new TmpFile<float> (format, config.channel_config->get_n_chans(), 
290                                             config.format->sample_rate()));
291         tmp_file->FileWritten.connect_same_thread (post_processing_connection, boost::bind (&Normalizer::start_post_processing, this));
292         
293         add_child (new_config);
294         
295         peak_reader->add_output (tmp_file);
296 }
297
298 ExportGraphBuilder::FloatSinkPtr
299 ExportGraphBuilder::Normalizer::sink ()
300 {
301         return peak_reader;
302 }
303
304 void
305 ExportGraphBuilder::Normalizer::add_child (FileSpec const & new_config)
306 {
307         for (boost::ptr_list<SFC>::iterator it = children.begin(); it != children.end(); ++it) {
308                 if (*it == new_config) {
309                         it->add_child (new_config);
310                         return;
311                 }
312         }
313         
314         children.push_back (new SFC (parent, new_config, max_frames_out));
315         threader->add_output (children.back().sink());
316 }
317
318 bool
319 ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const
320 {
321         return config.format->normalize() == other_config.format->normalize() &&
322                config.format->normalize_target() == other_config.format->normalize_target();
323 }
324
325 bool
326 ExportGraphBuilder::Normalizer::process()
327 {
328         framecnt_t frames_read = tmp_file->read (*buffer);
329         return frames_read != buffer->frames();
330 }
331
332 void
333 ExportGraphBuilder::Normalizer::start_post_processing()
334 {
335         normalizer->set_peak (peak_reader->get_peak());
336         tmp_file->seek (0, SEEK_SET);
337         tmp_file->add_output (normalizer);
338         parent.normalizers.push_back (this);
339 }
340
341 /* SRC */
342
343 ExportGraphBuilder::SRC::SRC (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t max_frames)
344   : parent (parent)
345 {
346         config = new_config;
347         converter.reset (new SampleRateConverter (new_config.channel_config->get_n_chans()));
348         ExportFormatSpecification & format = *new_config.format;
349         converter->init (parent.session.nominal_frame_rate(), format.sample_rate(), format.src_quality());
350         max_frames_out = converter->allocate_buffers (max_frames);
351         
352         add_child (new_config);
353 }
354
355 ExportGraphBuilder::FloatSinkPtr
356 ExportGraphBuilder::SRC::sink ()
357 {
358         return converter;
359 }
360
361 void
362 ExportGraphBuilder::SRC::add_child (FileSpec const & new_config)
363 {
364         if (new_config.format->normalize()) {
365                 add_child_to_list (new_config, normalized_children);
366         } else {
367                 add_child_to_list (new_config, children);
368         }
369 }
370
371 template<typename T>
372 void
373 ExportGraphBuilder::SRC::add_child_to_list (FileSpec const & new_config, boost::ptr_list<T> & list)
374 {
375         for (typename boost::ptr_list<T>::iterator it = list.begin(); it != list.end(); ++it) {
376                 if (*it == new_config) {
377                         it->add_child (new_config);
378                         return;
379                 }
380         }
381         
382         list.push_back (new T (parent, new_config, max_frames_out));
383         converter->add_output (list.back().sink ());
384 }
385
386 bool
387 ExportGraphBuilder::SRC::operator== (FileSpec const & other_config) const
388 {
389         return config.format->sample_rate() == other_config.format->sample_rate();
390 }
391
392 /* SilenceHandler */
393 ExportGraphBuilder::SilenceHandler::SilenceHandler (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t max_frames)
394   : parent (parent)
395 {
396         config = new_config;
397         max_frames_in = max_frames;
398         framecnt_t sample_rate = parent.session.nominal_frame_rate();
399         
400         silence_trimmer.reset (new SilenceTrimmer<Sample>(max_frames_in));
401         silence_trimmer->set_trim_beginning (config.format->trim_beginning());
402         silence_trimmer->set_trim_end (config.format->trim_end());
403         
404         framecnt_t sb = config.format->silence_beginning_at (parent.timespan->get_start(), sample_rate);
405         framecnt_t se = config.format->silence_end_at (parent.timespan->get_end(), sample_rate);
406         
407         silence_trimmer->add_silence_to_beginning (sb);
408         silence_trimmer->add_silence_to_end (se);
409         
410         add_child (new_config);
411 }
412
413 ExportGraphBuilder::FloatSinkPtr
414 ExportGraphBuilder::SilenceHandler::sink ()
415 {
416         return silence_trimmer;
417 }
418
419 void
420 ExportGraphBuilder::SilenceHandler::add_child (FileSpec const & new_config)
421 {
422         for (boost::ptr_list<SRC>::iterator it = children.begin(); it != children.end(); ++it) {
423                 if (*it == new_config) {
424                         it->add_child (new_config);
425                         return;
426                 }
427         }
428         
429         children.push_back (new SRC (parent, new_config, max_frames_in));
430         silence_trimmer->add_output (children.back().sink());
431 }
432
433 bool
434 ExportGraphBuilder::SilenceHandler::operator== (FileSpec const & other_config) const
435 {
436         ExportFormatSpecification & format = *config.format;
437         ExportFormatSpecification & other_format = *other_config.format;
438         return (format.trim_beginning() == other_format.trim_beginning()) &&
439                (format.trim_end() == other_format.trim_end()) &&
440                (format.silence_beginning_time() == other_format.silence_beginning_time()) &&
441                (format.silence_end_time() == other_format.silence_end_time());
442 }
443
444 /* ChannelConfig */
445
446 ExportGraphBuilder::ChannelConfig::ChannelConfig (ExportGraphBuilder & parent, FileSpec const & new_config, ChannelMap & channel_map)
447   : parent (parent)
448 {
449         typedef ExportChannelConfiguration::ChannelList ChannelList;
450         
451         config = new_config;
452         max_frames = parent.session.engine().frames_per_cycle();
453         
454         interleaver.reset (new Interleaver<Sample> ());
455         interleaver->init (new_config.channel_config->get_n_chans(), max_frames);
456         
457         ChannelList const & channel_list = config.channel_config->get_channels();
458         unsigned chan = 0;
459         for (ChannelList::const_iterator it = channel_list.begin(); it != channel_list.end(); ++it, ++chan) {
460                 ChannelMap::iterator map_it = channel_map.find (*it);
461                 if (map_it == channel_map.end()) {
462                         std::pair<ChannelMap::iterator, bool> result_pair =
463                                 channel_map.insert (std::make_pair (*it, IdentityVertexPtr (new IdentityVertex<Sample> ())));
464                         assert (result_pair.second);
465                         map_it = result_pair.first;
466                 }
467                 map_it->second->add_output (interleaver->input (chan));
468         }
469         
470         add_child (new_config);
471 }
472
473 void
474 ExportGraphBuilder::ChannelConfig::add_child (FileSpec const & new_config)
475 {
476         for (boost::ptr_list<SilenceHandler>::iterator it = children.begin(); it != children.end(); ++it) {
477                 if (*it == new_config) {
478                         it->add_child (new_config);
479                         return;
480                 }
481         }
482         
483         framecnt_t const max_frames_out = new_config.channel_config->get_n_chans() * max_frames;
484         children.push_back (new SilenceHandler (parent, new_config, max_frames_out));
485         interleaver->add_output (children.back().sink ());
486 }
487
488 bool
489 ExportGraphBuilder::ChannelConfig::operator== (FileSpec const & other_config) const
490 {
491         return config.channel_config == other_config.channel_config;
492 }
493
494 } // namespace ARDOUR