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