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