5b5ec16b363774122529620d4285bd2201a36cc2
[ardour.git] / libs / ardour / export_graph_builder.cc
1 #include "ardour/export_graph_builder.h"
2
3 #include "audiographer/interleaver.h"
4 #include "audiographer/normalizer.h"
5 #include "audiographer/peak_reader.h"
6 #include "audiographer/process_context.h"
7 #include "audiographer/sample_format_converter.h"
8 #include "audiographer/sndfile_writer.h"
9 #include "audiographer/sr_converter.h"
10 #include "audiographer/silence_trimmer.h"
11 #include "audiographer/threader.h"
12 #include "audiographer/tmp_file.h"
13 #include "audiographer/utils.h"
14
15 #include "ardour/audioengine.h"
16 #include "ardour/export_channel_configuration.h"
17 #include "ardour/export_filename.h"
18 #include "ardour/export_format_specification.h"
19 #include "ardour/sndfile_helpers.h"
20
21 #include "pbd/filesystem.h"
22
23 using namespace AudioGrapher;
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         // TODO move and/or use global silent buffers
35         AudioGrapher::Utils::init_zeros<Sample> (process_buffer_frames);
36 }
37
38 ExportGraphBuilder::~ExportGraphBuilder ()
39 {
40         delete [] process_buffer;
41         
42         // TODO see bove
43         AudioGrapher::Utils::free_resources();
44 }
45
46 int
47 ExportGraphBuilder::process (nframes_t frames, bool last_cycle)
48 {
49         for (ChannelMap::iterator it = channels.begin(); it != channels.end(); ++it) {
50                 it->first->read (process_buffer, process_buffer_frames);
51                 ProcessContext<Sample> context(process_buffer, process_buffer_frames, 1);
52                 if (last_cycle) { context.set_flag (ProcessContext<Sample>::EndOfInput); }
53                 it->second->process (context);
54         }
55         
56         return 0;
57 }
58
59 void
60 ExportGraphBuilder::reset ()
61 {
62         channel_configs.clear ();
63         channels.clear ();
64 }
65
66 void
67 ExportGraphBuilder::add_config (FileSpec const & config)
68 {
69         for (ChannelConfigList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
70                 if (*it == config) {
71                         it->add_child (config);
72                         return;
73                 }
74         }
75         
76         // No duplicate channel config found, create new one
77         channel_configs.push_back (ChannelConfig (*this));
78         ChannelConfig & c_config (channel_configs.back());
79         c_config.init (config, channels);
80 }
81
82 /* Encoder */
83
84 template <>
85 boost::shared_ptr<AudioGrapher::Sink<Sample> >
86 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
87 {
88         config = new_config;
89         init_writer (float_writer);
90         return float_writer;
91 }
92
93 template <>
94 boost::shared_ptr<AudioGrapher::Sink<int> >
95 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
96 {
97         config = new_config;
98         init_writer (int_writer);
99         return int_writer;
100 }
101
102 template <>
103 boost::shared_ptr<AudioGrapher::Sink<short> >
104 ExportGraphBuilder::Encoder::init (FileSpec const & new_config)
105 {
106         config = new_config;
107         init_writer (short_writer);
108         return short_writer;
109 }
110
111 void
112 ExportGraphBuilder::Encoder::add_child (FileSpec const & new_config)
113 {
114         filenames.push_back (new_config.filename);
115 }
116
117 bool
118 ExportGraphBuilder::Encoder::operator== (FileSpec const & other_config) const
119 {
120         return get_real_format (config) == get_real_format (other_config);
121 }
122
123 int
124 ExportGraphBuilder::Encoder::get_real_format (FileSpec const & config)
125 {
126         ExportFormatSpecification & format = *config.format;
127         return format.format_id() | format.sample_format() | format.endianness();
128 }
129
130 template<typename T>
131 void
132 ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::SndfileWriter<T> > & writer)
133 {
134         unsigned channels = config.channel_config->get_n_chans();
135         int format = get_real_format (config);
136         Glib::ustring filename = config.filename->get_path (config.format);
137         
138         writer.reset (new AudioGrapher::SndfileWriter<T> (channels, config.format->sample_rate(), format, filename));
139         writer->FileWritten.connect (sigc::mem_fun (*this, &ExportGraphBuilder::Encoder::copy_files));
140 }
141
142 void
143 ExportGraphBuilder::Encoder::copy_files (std::string orig_path)
144 {
145         while (filenames.size()) {
146                 FilenamePtr & filename = filenames.front();
147                 PBD::sys::copy_file (orig_path, filename->get_path (config.format).c_str());
148                 filenames.pop_front();
149         }
150 }
151
152 /* SFC */
153
154 ExportGraphBuilder::FloatSinkPtr
155 ExportGraphBuilder::SFC::init (FileSpec const & new_config, nframes_t max_frames)
156 {
157         config = new_config;
158         data_width = sndfile_data_width (Encoder::get_real_format (config));
159         unsigned channels = new_config.channel_config->get_n_chans();
160         
161         if (data_width == 8 || data_width == 16) {
162                 short_converter = ShortConverterPtr (new SampleFormatConverter<short> (channels));
163                 short_converter->init (max_frames, config.format->dither_type(), data_width);
164                 add_child (config);
165                 return short_converter;
166         } else if (data_width == 24 || data_width == 32) {
167                 int_converter = IntConverterPtr (new SampleFormatConverter<int> (channels));
168                 int_converter->init (max_frames, config.format->dither_type(), data_width);
169                 add_child (config);
170                 return int_converter;
171         } else {
172                 float_converter = FloatConverterPtr (new SampleFormatConverter<Sample> (channels));
173                 float_converter->init (max_frames, config.format->dither_type(), data_width);
174                 add_child (config);
175                 return float_converter;
176         }
177 }
178
179 void
180 ExportGraphBuilder::SFC::add_child (FileSpec const & new_config)
181 {
182         for (std::list<Encoder>::iterator it = children.begin(); it != children.end(); ++it) {
183                 if (*it == new_config) {
184                         it->add_child (new_config);
185                         return;
186                 }
187         }
188         
189         children.push_back (Encoder());
190         Encoder & encoder = children.back();
191         
192         if (data_width == 8 || data_width == 16) {
193                 short_converter->add_output (encoder.init<short> (new_config));
194         } else if (data_width == 24 || data_width == 32) {
195                 int_converter->add_output (encoder.init<int> (new_config));
196         } else {
197                 float_converter->add_output (encoder.init<Sample> (new_config));
198         }
199 }
200
201 bool
202 ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const
203 {
204         return config.format->sample_format() == other_config.format->sample_format();
205 }
206
207 /* Normalizer */
208
209 ExportGraphBuilder::FloatSinkPtr
210 ExportGraphBuilder::Normalizer::init (FileSpec const & new_config, nframes_t /*max_frames*/)
211 {
212         config = new_config;
213         max_frames_out = 4086; // TODO good chunk size
214         
215         buffer.reset (new AllocatingProcessContext<Sample> (max_frames_out, config.channel_config->get_n_chans()));
216         peak_reader.reset (new PeakReader ());
217         normalizer.reset (new AudioGrapher::Normalizer (config.format->normalize_target()));
218         threader.reset (new Threader<Sample> (parent.thread_pool));
219         
220         normalizer->alloc_buffer (max_frames_out);
221         normalizer->add_output (threader);
222         
223         int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float;
224         tmp_file.reset (new TmpFile<float> (config.channel_config->get_n_chans(), 
225                                             config.format->sample_rate(), format));
226         tmp_file->FileWritten.connect (sigc::hide (sigc::mem_fun (*this, &Normalizer::start_post_processing)));
227         
228         add_child (new_config);
229         
230         peak_reader->add_output (tmp_file);
231         return peak_reader;
232 }
233
234 void
235 ExportGraphBuilder::Normalizer::add_child (FileSpec const & new_config)
236 {
237         for (std::list<SFC>::iterator it = children.begin(); it != children.end(); ++it) {
238                 if (*it == new_config) {
239                         it->add_child (new_config);
240                         return;
241                 }
242         }
243         
244         children.push_back (SFC (parent));
245         threader->add_output (children.back().init (new_config, max_frames_out));
246 }
247
248 bool
249 ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const
250 {
251         return config.format->normalize() == other_config.format->normalize() &&
252                config.format->normalize_target() == other_config.format->normalize_target();
253 }
254
255 void
256 ExportGraphBuilder::Normalizer::start_post_processing()
257 {
258         normalizer->set_peak (peak_reader->get_peak());
259         tmp_file->seek (0, SndfileReader<Sample>::SeekBeginning);
260         parent.thread_pool.push (sigc::mem_fun (*this, &Normalizer::do_post_processing));
261 }
262
263 void
264 ExportGraphBuilder::Normalizer::do_post_processing()
265 {
266         while (tmp_file->read (*buffer) == buffer->frames()) {
267                 normalizer->process (*buffer);
268         }
269 }
270
271 /* SRC */
272
273 ExportGraphBuilder::FloatSinkPtr
274 ExportGraphBuilder::SRC::init (FileSpec const & new_config, nframes_t max_frames)
275 {
276         config = new_config;
277         converter.reset (new SampleRateConverter (new_config.channel_config->get_n_chans()));
278         ExportFormatSpecification & format = *new_config.format;
279         converter->init (parent.session.nominal_frame_rate(), format.sample_rate(), format.src_quality());
280         max_frames_out = converter->allocate_buffers (max_frames);
281         
282         add_child (new_config);
283         
284         return converter;
285 }
286
287 void
288 ExportGraphBuilder::SRC::add_child (FileSpec const & new_config)
289 {
290         if (new_config.format->normalize()) {
291                 add_child_to_list (new_config, normalized_children);
292         } else {
293                 add_child_to_list (new_config, children);
294         }
295 }
296
297 template<typename T>
298 void
299 ExportGraphBuilder::SRC::add_child_to_list (FileSpec const & new_config, std::list<T> & list)
300 {
301         for (typename std::list<T>::iterator it = list.begin(); it != list.end(); ++it) {
302                 if (*it == new_config) {
303                         it->add_child (new_config);
304                         return;
305                 }
306         }
307         
308         list.push_back (T (parent));
309         converter->add_output (list.back().init (new_config, max_frames_out));
310 }
311
312 bool
313 ExportGraphBuilder::SRC::operator== (FileSpec const & other_config) const
314 {
315         return config.format->sample_rate() == other_config.format->sample_rate();
316 }
317
318 /* SilenceHandler */
319 ExportGraphBuilder::FloatSinkPtr
320 ExportGraphBuilder::SilenceHandler::init (FileSpec const & new_config, nframes_t max_frames)
321 {
322         config = new_config;
323         max_frames_in = max_frames;
324         nframes_t sample_rate = parent.session.nominal_frame_rate();
325         
326         silence_trimmer.reset (new SilenceTrimmer<Sample>());
327         silence_trimmer->set_trim_beginning (config.format->trim_beginning());
328         silence_trimmer->set_trim_end (config.format->trim_end());
329         silence_trimmer->add_silence_to_beginning (config.format->silence_beginning(sample_rate));
330         silence_trimmer->add_silence_to_end (config.format->silence_end(sample_rate));
331         silence_trimmer->limit_output_size (max_frames_in);
332         
333         add_child (new_config);
334         
335         return silence_trimmer;
336 }
337
338 void
339 ExportGraphBuilder::SilenceHandler::add_child (FileSpec const & new_config)
340 {
341         for (std::list<SRC>::iterator it = children.begin(); it != children.end(); ++it) {
342                 if (*it == new_config) {
343                         it->add_child (new_config);
344                         return;
345                 }
346         }
347         
348         children.push_back (SRC (parent));
349         silence_trimmer->add_output (children.back().init (new_config, max_frames_in));
350 }
351
352 bool
353 ExportGraphBuilder::SilenceHandler::operator== (FileSpec const & other_config) const
354 {
355         ExportFormatSpecification & format = *config.format;
356         ExportFormatSpecification & other_format = *other_config.format;
357         return (format.trim_beginning() == other_format.trim_beginning()) &&
358                (format.trim_end() == other_format.trim_end()) &&
359                (format.silence_beginning() == other_format.silence_beginning()) &&
360                (format.silence_end() == other_format.silence_end());
361 }
362
363 /* ChannelConfig */
364
365 void
366 ExportGraphBuilder::ChannelConfig::init (FileSpec const & new_config, ChannelMap & channel_map)
367 {
368         typedef ExportChannelConfiguration::ChannelList ChannelList;
369         
370         config = new_config;
371         max_frames = parent.session.engine().frames_per_cycle();
372         
373         interleaver.reset (new Interleaver<Sample> ());
374         interleaver->init (new_config.channel_config->get_n_chans(), max_frames);
375         
376         ChannelList const & channel_list = config.channel_config->get_channels();
377         unsigned chan = 0;
378         for (ChannelList::const_iterator it = channel_list.begin(); it != channel_list.end(); ++it, ++chan) {
379                 ChannelMap::iterator map_it = channel_map.find (*it);
380                 if (map_it == channel_map.end()) {
381                         std::pair<ChannelMap::iterator, bool> result_pair =
382                                 channel_map.insert (std::make_pair (*it, IdentityVertexPtr (new IdentityVertex<Sample> ())));
383                         assert (result_pair.second);
384                         map_it = result_pair.first;
385                 }
386                 map_it->second->add_output (interleaver->input (chan));
387         }
388         
389         add_child (new_config);
390 }
391
392 void
393 ExportGraphBuilder::ChannelConfig::add_child (FileSpec const & new_config)
394 {
395         for (std::list<SilenceHandler>::iterator it = children.begin(); it != children.end(); ++it) {
396                 if (*it == new_config) {
397                         it->add_child (new_config);
398                         return;
399                 }
400         }
401         
402         children.push_back (SilenceHandler (parent));
403         nframes_t max_frames_out = new_config.channel_config->get_n_chans() * max_frames;
404         interleaver->add_output (children.back().init (new_config, max_frames_out));
405 }
406
407 bool
408 ExportGraphBuilder::ChannelConfig::operator== (FileSpec const & other_config) const
409 {
410         return config.channel_config == other_config.channel_config;
411 }
412
413 } // namespace ARDOUR