ardour
export_graph_builder.cc
Go to the documentation of this file.
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 
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"
41 #include "ardour/export_filename.h"
43 #include "ardour/export_timespan.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 {
60 }
61 
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
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
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
108 {
109  timespan.reset();
110  channel_configs.clear ();
111  channels.clear ();
112  normalizers.clear ();
113 }
114 
115 void
117 {
118  timespan = span;
119 }
120 
121 void
123 {
125  config.channel_config->get_channels();
126  for(ExportChannelConfiguration::ChannelList::const_iterator it = channels.begin();
127  it != channels.end(); ++it) {
128  (*it)->set_max_buffer_size(process_buffer_frames);
129  }
130 
131  // If the sample rate is "session rate", change it to the real value.
132  // However, we need to copy it to not change the config which is saved...
133  FileSpec new_config (config);
134  new_config.format.reset(new ExportFormatSpecification(*new_config.format, false));
135  if(new_config.format->sample_rate() == ExportFormatBase::SR_Session) {
136  framecnt_t session_rate = session.nominal_frame_rate();
137  new_config.format->set_sample_rate(ExportFormatBase::nearest_sample_rate(session_rate));
138  }
139 
140 
141  if (!new_config.channel_config->get_split ()) {
142  add_split_config (new_config);
143  return;
144  }
145 
146  // Split channel configurations are split into several channel configurations,
147  // each corresponding to a file, at this stage
148  typedef std::list<boost::shared_ptr<ExportChannelConfiguration> > ConfigList;
149  ConfigList file_configs;
150  new_config.channel_config->configurations_for_files (file_configs);
151 
152  unsigned chan = 1;
153  for (ConfigList::iterator it = file_configs.begin(); it != file_configs.end(); ++it, ++chan) {
154  FileSpec copy = new_config;
155  copy.channel_config = *it;
156 
157  copy.filename.reset (new ExportFilename (*copy.filename));
158  copy.filename->include_channel = true;
159  copy.filename->set_channel (chan);
160 
161  add_split_config (copy);
162  }
163 }
164 
165 void
167 {
168  for (ChannelConfigList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
169  if (*it == config) {
170  it->add_child (config);
171  return;
172  }
173  }
174 
175  // No duplicate channel config found, create new one
176  channel_configs.push_back (new ChannelConfig (*this, config, channels));
177 }
178 
179 /* Encoder */
180 
181 template <>
184 {
185  config = new_config;
187  return float_writer;
188 }
189 
190 template <>
193 {
194  config = new_config;
195  init_writer (int_writer);
196  return int_writer;
197 }
198 
199 template <>
202 {
203  config = new_config;
204  init_writer (short_writer);
205  return short_writer;
206 }
207 
208 void
210 {
211  filenames.push_back (new_config.filename);
212 }
213 
214 bool
216 {
217  return get_real_format (config) == get_real_format (other_config);
218 }
219 
220 int
222 {
223  ExportFormatSpecification & format = *config.format;
224  return format.format_id() | format.sample_format() | format.endianness();
225 }
226 
227 template<typename T>
228 void
230 {
231  unsigned channels = config.channel_config->get_n_chans();
232  int format = get_real_format (config);
233  config.filename->set_channel_config(config.channel_config);
234  string filename = config.filename->get_path (config.format);
235 
236  writer.reset (new AudioGrapher::SndfileWriter<T> (filename, format, channels, config.format->sample_rate(), config.broadcast_info));
237  writer->FileWritten.connect_same_thread (copy_files_connection, boost::bind (&ExportGraphBuilder::Encoder::copy_files, this, _1));
238 }
239 
240 void
242 {
243  while (filenames.size()) {
244  ExportFilenamePtr & filename = filenames.front();
245  PBD::copy_file (orig_path, filename->get_path (config.format).c_str());
246  filenames.pop_front();
247  }
248 }
249 
250 /* SFC */
251 
253  : data_width(0)
254 {
255  config = new_config;
257  unsigned channels = new_config.channel_config->get_n_chans();
258 
259  if (data_width == 8 || data_width == 16) {
261  short_converter->init (max_frames, config.format->dither_type(), data_width);
262  add_child (config);
263  } else if (data_width == 24 || data_width == 32) {
265  int_converter->init (max_frames, config.format->dither_type(), data_width);
266  add_child (config);
267  } else {
268  int actual_data_width = 8 * sizeof(Sample);
270  float_converter->init (max_frames, config.format->dither_type(), actual_data_width);
271  add_child (config);
272  }
273 }
274 
277 {
278  if (data_width == 8 || data_width == 16) {
279  return short_converter;
280  } else if (data_width == 24 || data_width == 32) {
281  return int_converter;
282  } else {
283  return float_converter;
284  }
285 }
286 
287 void
289 {
290  for (boost::ptr_list<Encoder>::iterator it = children.begin(); it != children.end(); ++it) {
291  if (*it == new_config) {
292  it->add_child (new_config);
293  return;
294  }
295  }
296 
297  children.push_back (new Encoder());
298  Encoder & encoder = children.back();
299 
300  if (data_width == 8 || data_width == 16) {
301  short_converter->add_output (encoder.init<short> (new_config));
302  } else if (data_width == 24 || data_width == 32) {
303  int_converter->add_output (encoder.init<int> (new_config));
304  } else {
305  float_converter->add_output (encoder.init<Sample> (new_config));
306  }
307 }
308 
309 bool
311 {
312  return config.format->sample_format() == other_config.format->sample_format();
313 }
314 
315 /* Normalizer */
316 
318  : parent (parent)
319 {
320  std::string tmpfile_path = parent.session.session_directory().export_path();
321  tmpfile_path = Glib::build_filename(tmpfile_path, "XXXXXX");
322  std::vector<char> tmpfile_path_buf(tmpfile_path.size() + 1);
323  std::copy(tmpfile_path.begin(), tmpfile_path.end(), tmpfile_path_buf.begin());
324  tmpfile_path_buf[tmpfile_path.size()] = '\0';
325 
326  config = new_config;
327  uint32_t const channels = config.channel_config->get_n_chans();
328  max_frames_out = 4086 - (4086 % channels); // TODO good chunk size
329 
331  peak_reader.reset (new PeakReader ());
332  normalizer.reset (new AudioGrapher::Normalizer (config.format->normalize_target()));
334 
335  normalizer->alloc_buffer (max_frames_out);
336  normalizer->add_output (threader);
337 
339  tmp_file.reset (new TmpFile<float> (&tmpfile_path_buf[0], format, channels, config.format->sample_rate()));
340  tmp_file->FileWritten.connect_same_thread (post_processing_connection,
341  boost::bind (&Normalizer::start_post_processing, this));
342 
343  add_child (new_config);
344 
345  peak_reader->add_output (tmp_file);
346 }
347 
350 {
351  return peak_reader;
352 }
353 
354 void
356 {
357  for (boost::ptr_list<SFC>::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 (new SFC (parent, new_config, max_frames_out));
365  threader->add_output (children.back().sink());
366 }
367 
368 bool
370 {
371  return config.format->normalize() == other_config.format->normalize() &&
372  config.format->normalize_target() == other_config.format->normalize_target();
373 }
374 
375 unsigned
377 {
378  return static_cast<unsigned>(std::ceil(static_cast<float>(tmp_file->get_frames_written()) /
379  max_frames_out));
380 }
381 
382 bool
384 {
385  framecnt_t frames_read = tmp_file->read (*buffer);
386  return frames_read != buffer->frames();
387 }
388 
389 void
391 {
392  normalizer->set_peak (peak_reader->get_peak());
393  tmp_file->seek (0, SEEK_SET);
394  tmp_file->add_output (normalizer);
395  parent.normalizers.push_back (this);
396 }
397 
398 /* SRC */
399 
400 ExportGraphBuilder::SRC::SRC (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t max_frames)
401  : parent (parent)
402 {
403  config = new_config;
404  converter.reset (new SampleRateConverter (new_config.channel_config->get_n_chans()));
405  ExportFormatSpecification & format = *new_config.format;
406  converter->init (parent.session.nominal_frame_rate(), format.sample_rate(), format.src_quality());
407  max_frames_out = converter->allocate_buffers (max_frames);
408 
409  add_child (new_config);
410 }
411 
414 {
415  return converter;
416 }
417 
418 void
420 {
421  if (new_config.format->normalize()) {
422  add_child_to_list (new_config, normalized_children);
423  } else {
424  add_child_to_list (new_config, children);
425  }
426 }
427 
428 template<typename T>
429 void
430 ExportGraphBuilder::SRC::add_child_to_list (FileSpec const & new_config, boost::ptr_list<T> & list)
431 {
432  for (typename boost::ptr_list<T>::iterator it = list.begin(); it != list.end(); ++it) {
433  if (*it == new_config) {
434  it->add_child (new_config);
435  return;
436  }
437  }
438 
439  list.push_back (new T (parent, new_config, max_frames_out));
440  converter->add_output (list.back().sink ());
441 }
442 
443 bool
445 {
446  return config.format->sample_rate() == other_config.format->sample_rate();
447 }
448 
449 /* SilenceHandler */
451  : parent (parent)
452 {
453  config = new_config;
454  max_frames_in = max_frames;
455  framecnt_t sample_rate = parent.session.nominal_frame_rate();
456 
458  silence_trimmer->set_trim_beginning (config.format->trim_beginning());
459  silence_trimmer->set_trim_end (config.format->trim_end());
460 
461  framecnt_t sb = config.format->silence_beginning_at (parent.timespan->get_start(), sample_rate);
462  framecnt_t se = config.format->silence_end_at (parent.timespan->get_end(), sample_rate);
463 
464  silence_trimmer->add_silence_to_beginning (sb);
465  silence_trimmer->add_silence_to_end (se);
466 
467  add_child (new_config);
468 }
469 
472 {
473  return silence_trimmer;
474 }
475 
476 void
478 {
479  for (boost::ptr_list<SRC>::iterator it = children.begin(); it != children.end(); ++it) {
480  if (*it == new_config) {
481  it->add_child (new_config);
482  return;
483  }
484  }
485 
486  children.push_back (new SRC (parent, new_config, max_frames_in));
487  silence_trimmer->add_output (children.back().sink());
488 }
489 
490 bool
492 {
493  ExportFormatSpecification & format = *config.format;
494  ExportFormatSpecification & other_format = *other_config.format;
495  return (format.trim_beginning() == other_format.trim_beginning()) &&
496  (format.trim_end() == other_format.trim_end()) &&
497  (format.silence_beginning_time() == other_format.silence_beginning_time()) &&
498  (format.silence_end_time() == other_format.silence_end_time());
499 }
500 
501 /* ChannelConfig */
502 
504  : parent (parent)
505 {
506  typedef ExportChannelConfiguration::ChannelList ChannelList;
507 
508  config = new_config;
509 
510  framecnt_t max_frames = parent.session.engine().samples_per_cycle();
512  interleaver->init (new_config.channel_config->get_n_chans(), max_frames);
513 
514  // Make the chunk size divisible by the channel count
515  int chan_count = new_config.channel_config->get_n_chans();
516  max_frames_out = 8192;
517  if (chan_count > 0) {
518  max_frames_out -= max_frames_out % chan_count;
519  }
521  interleaver->add_output(chunker);
522 
523  ChannelList const & channel_list = config.channel_config->get_channels();
524  unsigned chan = 0;
525  for (ChannelList::const_iterator it = channel_list.begin(); it != channel_list.end(); ++it, ++chan) {
526  ChannelMap::iterator map_it = channel_map.find (*it);
527  if (map_it == channel_map.end()) {
528  std::pair<ChannelMap::iterator, bool> result_pair =
529  channel_map.insert (std::make_pair (*it, IdentityVertexPtr (new IdentityVertex<Sample> ())));
530  assert (result_pair.second);
531  map_it = result_pair.first;
532  }
533  map_it->second->add_output (interleaver->input (chan));
534  }
535 
536  add_child (new_config);
537 }
538 
539 void
541 {
542  assert (*this == new_config);
543 
544  for (boost::ptr_list<SilenceHandler>::iterator it = children.begin(); it != children.end(); ++it) {
545  if (*it == new_config) {
546  it->add_child (new_config);
547  return;
548  }
549  }
550 
551  children.push_back (new SilenceHandler (parent, new_config, max_frames_out));
552  chunker->add_output (children.back().sink ());
553 }
554 
555 bool
557 {
558  return config.channel_config == other_config.channel_config;
559 }
560 
561 } // namespace ARDOUR
framecnt_t nominal_frame_rate() const
Definition: session.h:367
bool operator==(FileSpec const &other_config) const
static SampleRate nearest_sample_rate(framecnt_t sample_rate)
pframes_t samples_per_cycle() const
Definition: audioengine.cc:983
boost::shared_ptr< AudioGrapher::SampleFormatConverter< int > > IntConverterPtr
void add_child(FileSpec const &new_config)
std::map< ExportChannelPtr, IdentityVertexPtr > ChannelMap
void add_split_config(FileSpec const &config)
void add_child(FileSpec const &new_config)
boost::shared_ptr< ExportTimespan > timespan
ExportChannelConfigPtr channel_config
bool copy_file(const std::string &from_path, const std::string &to_path)
Definition: file_utils.cc:282
void set_current_timespan(boost::shared_ptr< ExportTimespan > span)
int64_t framecnt_t
Definition: types.h:76
float Sample
Definition: types.h:54
SRC(ExportGraphBuilder &parent, FileSpec const &new_config, framecnt_t max_frames)
unsigned get_normalize_cycle_count() const
bool process()
Returns true when finished.
bool operator==(FileSpec const &other_config) const
void add_child(FileSpec const &new_config)
bool operator==(FileSpec const &other_config) const
SilenceHandler(ExportGraphBuilder &parent, FileSpec const &new_config, framecnt_t max_frames)
Definition: amp.h:29
uint32_t hardware_concurrency()
Definition: cpus.cc:42
Normalizer(ExportGraphBuilder &parent, FileSpec const &new_config, framecnt_t max_frames)
int process(framecnt_t frames, bool last_cycle)
boost::shared_ptr< AudioGrapher::SampleFormatConverter< short > > ShortConverterPtr
const std::string export_path() const
void copy_files(std::string orig_path)
ChannelConfig(ExportGraphBuilder &parent, FileSpec const &new_config, ChannelMap &channel_map)
bool operator==(FileSpec const &other_config) const
boost::shared_ptr< AudioGrapher::Sink< T > > init(FileSpec const &new_config)
void add_child(FileSpec const &new_config)
bool operator==(FileSpec const &other_config) const
const SessionDirectory & session_directory() const
Definition: session.h:182
void add_config(FileSpec const &config)
std::list< Normalizer * > normalizers
void add_child_to_list(FileSpec const &new_config, boost::ptr_list< T > &list)
SFC(ExportGraphBuilder &, FileSpec const &new_config, framecnt_t max_frames)
static int get_real_format(FileSpec const &config)
void add_child(FileSpec const &new_config)
AudioEngine & engine()
Definition: session.h:546
void init_writer(boost::shared_ptr< AudioGrapher::SndfileWriter< T > > &writer)
boost::shared_ptr< AudioGrapher::SampleFormatConverter< Sample > > FloatConverterPtr
bool operator==(FileSpec const &other_config) const
int sndfile_data_width(int format)
void add_child(FileSpec const &new_config)