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