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