ardour
export_channel_selector.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 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 <algorithm>
24 
25 #include "pbd/convert.h"
26 
27 #include "ardour/audio_track.h"
28 #include "ardour/audioregion.h"
30 #include "ardour/io.h"
31 #include "ardour/route.h"
32 #include "ardour/session.h"
33 
34 #include <sstream>
35 
36 #include "i18n.h"
37 
38 using namespace std;
39 using namespace Glib;
40 using namespace ARDOUR;
41 using namespace PBD;
42 
44  ExportChannelSelector (session, manager),
45  channels_label (_("Channels:"), Gtk::ALIGN_LEFT),
46  split_checkbox (_("Split to mono files")),
47  max_channels (20),
48  channel_view (max_channels)
49 {
50  channels_hbox.pack_start (channels_label, false, false, 0);
51  channels_hbox.pack_end (channels_spinbutton, false, false, 0);
52 
53  channels_vbox.pack_start (channels_hbox, false, false, 0);
54  channels_vbox.pack_start (split_checkbox, false, false, 6);
55 
57  channel_alignment.set_padding (0, 0, 12, 0);
59  channel_scroller.set_size_request (-1, 130);
60  channel_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
61 
62  pack_start (channels_vbox, false, false, 0);
63  pack_start (channel_alignment, true, true, 0);
64 
65  /* Channels spinbutton */
66 
67  channels_spinbutton.set_digits (0);
68  channels_spinbutton.set_increments (1, 2);
69  channels_spinbutton.set_range (1, max_channels);
70  channels_spinbutton.set_value (2);
71 
72  channels_spinbutton.signal_value_changed().connect (sigc::mem_fun (*this, &PortExportChannelSelector::update_channel_count));
73 
74  /* Other signals */
75 
76  split_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &PortExportChannelSelector::update_split_state));
78 
79  /* Finalize */
80 
82  show_all_children ();
83 
84 }
85 
87 {
88 // if (session) {
89 // session->add_instant_xml (get_state(), false);
90 // }
91 }
92 
93 void
95 {
96  state = manager->get_channel_configs().front();
97 
98  split_checkbox.set_active (state->config->get_split());
99  channels_spinbutton.set_value (state->config->get_n_chans());
100 
101  fill_route_list ();
102  channel_view.set_config (state->config);
103 }
104 
105 void
107 {
109  RouteList routes = *_session->get_routes();
110 
111  /* Add master bus and then everything else */
112 
113  if (_session->master_out()) {
114  ARDOUR::IO* master = _session->master_out()->output().get();
115  channel_view.add_route (master);
116  }
117 
118  for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
119  if ((*it)->is_master () || (*it)->is_monitor ()) {
120  continue;
121  }
122  channel_view.add_route ((*it)->output().get());
123  }
124 
126 }
127 
128 void
130 {
131  uint32_t chans = static_cast<uint32_t> (channels_spinbutton.get_value());
134 }
135 
136 void
138 {
139  state->config->set_split (split_checkbox.get_active());
141 }
142 
143 void
145 {
146  while (chans > 0) {
147  channels.push_back (Channel (*this));
148  ++n_channels;
149  --chans;
150  }
151 }
152 
155 {
156  if (channel > n_channels) {
157  std::cout << "Invalid channel cout for get_channel!" << std::endl;
158  }
159 
160  std::list<Channel>::iterator it = channels.begin();
161 
162  while (channel > 1) { // Channel count starts from one!
163  ++it;
164  --channel;
165  }
166 
167  return *it;
168 }
169 
171  n_channels (0)
172 {
173  /* Main columns */
174 
175  route_cols.add_channels (max_channels);
176 
177  route_list = Gtk::ListStore::create(route_cols);
178  set_model (route_list);
179 
180  /* Add column with toggle and text */
181 
182  append_column_editable (_("Export"), route_cols.selected);
183 
184  Gtk::CellRendererText* text_renderer = Gtk::manage (new Gtk::CellRendererText);
185  text_renderer->property_editable() = false;
186  text_renderer->set_alignment (0.0, 0.5);
187 
188  Gtk::TreeView::Column* column = Gtk::manage (new Gtk::TreeView::Column);
189  column->set_title (_("Bus or Track"));
190  column->pack_start (*text_renderer);
191  column->set_expand (true);
192  column->add_attribute (text_renderer->property_text(), route_cols.name);
193  append_column (*column);
194 
195  Gtk::CellRendererToggle *toggle = dynamic_cast<Gtk::CellRendererToggle *>(get_column_cell_renderer (0));
196  toggle->set_alignment (0.0, 0.5);
197  toggle->signal_toggled().connect (sigc::mem_fun (*this, &PortExportChannelSelector::ChannelTreeView::update_toggle_selection));
198 
199  static_columns = get_columns().size();
200 }
201 
202 void
204 {
205  /* TODO Without the following line, the state might get reset.
206  * Pointing to the same address does not mean the state of the configuration hasn't changed.
207  * In the current code this is safe, but the actual cause of the problem would be good to fix
208  */
209 
210  if (config == c) { return; }
211  config = c;
212 
213  uint32_t i = 1;
214  ExportChannelConfiguration::ChannelList chan_list = config->get_channels();
215  for (ExportChannelConfiguration::ChannelList::iterator c_it = chan_list.begin(); c_it != chan_list.end(); ++c_it) {
216 
217  for (Gtk::ListStore::Children::iterator r_it = route_list->children().begin(); r_it != route_list->children().end(); ++r_it) {
218 
220  if (!(pec = dynamic_cast<ARDOUR::PortExportChannel *> (c_it->get()))) {
221  continue;
222  }
223 
224  Glib::RefPtr<Gtk::ListStore> port_list = r_it->get_value (route_cols.port_list_col);
225  std::set<boost::weak_ptr<AudioPort> > route_ports;
226  std::set<boost::weak_ptr<AudioPort> > intersection;
227  std::map<boost::weak_ptr<AudioPort>, string> port_labels;
228 
229  for (Gtk::ListStore::Children::const_iterator p_it = port_list->children().begin(); p_it != port_list->children().end(); ++p_it) {
230  route_ports.insert ((*p_it)->get_value (route_cols.port_cols.port));
231  port_labels.insert (make_pair ((*p_it)->get_value (route_cols.port_cols.port),
232  (*p_it)->get_value (route_cols.port_cols.label)));
233  }
234 
235  std::set_intersection (pec->get_ports().begin(), pec->get_ports().end(),
236  route_ports.begin(), route_ports.end(),
237  std::insert_iterator<std::set<boost::weak_ptr<AudioPort> > > (intersection, intersection.begin()));
238 
239  intersection.erase (boost::weak_ptr<AudioPort> ()); // Remove "none" selection
240 
241  if (intersection.empty()) {
242  continue;
243  }
244 
245  if (!r_it->get_value (route_cols.selected)) {
246  r_it->set_value (route_cols.selected, true);
247 
248  /* Set previous channels (if any) to none */
249 
250  for (uint32_t chn = 1; chn < i; ++chn) {
251  r_it->set_value (route_cols.get_channel (chn).port, boost::weak_ptr<AudioPort> ());
252  r_it->set_value (route_cols.get_channel (chn).label, string ("(none)"));
253  }
254  }
255 
256  boost::weak_ptr<AudioPort> port = *intersection.begin();
257  std::map<boost::weak_ptr<AudioPort>, string>::iterator label_it = port_labels.find (port);
258  string label = label_it != port_labels.end() ? label_it->second : "error";
259 
260  r_it->set_value (route_cols.get_channel (i).port, port);
261  r_it->set_value (route_cols.get_channel (i).label, label);
262  }
263 
264  ++i;
265  }
266 }
267 
268 void
270 {
271  Gtk::TreeModel::iterator iter = route_list->append();
272  Gtk::TreeModel::Row row = *iter;
273 
274  row[route_cols.selected] = false;
275  row[route_cols.name] = io->name();
276  row[route_cols.io] = io;
277 
278  /* Initialize port list */
279 
280  Glib::RefPtr<Gtk::ListStore> port_list = Gtk::ListStore::create (route_cols.port_cols);
281  row[route_cols.port_list_col] = port_list;
282 
283  uint32_t outs = io->n_ports().n_audio();
284  for (uint32_t i = 0; i < outs; ++i) {
285  iter = port_list->append();
286  row = *iter;
287 
288  row[route_cols.port_cols.selected] = false;
289  row[route_cols.port_cols.port] = io->audio (i);
290 
291  std::ostringstream oss;
292  oss << "Out-" << (i + 1);
293 
294  row[route_cols.port_cols.label] = oss.str();
295  }
296 
297  iter = port_list->append();
298  row = *iter;
299 
300  row[route_cols.port_cols.selected] = false;
301  row[route_cols.port_cols.port] = boost::weak_ptr<AudioPort> ();
302  row[route_cols.port_cols.label] = "(none)";
303 
304 }
305 
306 void
308 {
309  int offset = channels - n_channels;
310 
311  while (offset > 0) {
312  ++n_channels;
313 
314  std::ostringstream oss;
315  oss << n_channels;
316 
317  /* New column */
318 
319  Gtk::TreeView::Column* column = Gtk::manage (new Gtk::TreeView::Column (oss.str()));
320 
321  Gtk::CellRendererCombo* combo_renderer = Gtk::manage (new Gtk::CellRendererCombo);
322  combo_renderer->property_text_column() = 2;
323  combo_renderer->property_has_entry() = false;
324  column->pack_start (*combo_renderer);
325 
326  append_column (*column);
327 
328  column->add_attribute (combo_renderer->property_text(), route_cols.get_channel(n_channels).label);
329  column->add_attribute (combo_renderer->property_model(), route_cols.port_list_col);
330  column->add_attribute (combo_renderer->property_editable(), route_cols.selected);
331 
332  combo_renderer->signal_edited().connect (sigc::bind (sigc::mem_fun (*this, &PortExportChannelSelector::ChannelTreeView::update_selection_text), n_channels));
333 
334  /* put data into view */
335 
336  for (Gtk::ListStore::Children::iterator it = route_list->children().begin(); it != route_list->children().end(); ++it) {
337  std::string label = it->get_value(route_cols.selected) ? "(none)" : "";
338  it->set_value (route_cols.get_channel (n_channels).label, label);
339  it->set_value (route_cols.get_channel (n_channels).port, boost::weak_ptr<AudioPort> ());
340  }
341 
342  /* set column width */
343 
344  get_column (static_columns + n_channels - 1)->set_min_width (80);
345 
346  --offset;
347  }
348 
349  while (offset < 0) {
350  --n_channels;
351 
352  remove_column (*get_column (n_channels + static_columns));
353 
354  ++offset;
355  }
356 
357  update_config ();
358 }
359 
360 void
362 {
363  if (!config) { return; }
364 
365  config->clear_channels();
366 
367  for (uint32_t i = 1; i <= n_channels; ++i) {
368 
369  ExportChannelPtr channel (new PortExportChannel ());
370  PortExportChannel * pec = static_cast<PortExportChannel *> (channel.get());
371 
372  for (Gtk::ListStore::Children::iterator it = route_list->children().begin(); it != route_list->children().end(); ++it) {
373  Gtk::TreeModel::Row row = *it;
374 
375  if (!row[route_cols.selected]) {
376  continue;
377  }
378 
379  boost::weak_ptr<AudioPort> weak_port = row[route_cols.get_channel (i).port];
380  boost::shared_ptr<AudioPort> port = weak_port.lock ();
381  if (port) {
382  pec->add_port (port);
383  }
384  }
385 
386  config->register_channel (channel);
387  }
388 
390 }
391 
392 void
394 {
395  Gtk::TreeModel::iterator iter = get_model ()->get_iter (path);
396  bool selected = iter->get_value (route_cols.selected);
397 
398  for (uint32_t i = 1; i <= n_channels; ++i) {
399 
400  if (!selected) {
401  iter->set_value (route_cols.get_channel (i).label, std::string (""));
402  continue;
403  }
404 
405  iter->set_value (route_cols.get_channel (i).label, std::string("(none)"));
406  iter->set_value (route_cols.get_channel (i).port, boost::weak_ptr<AudioPort> ());
407 
408  Glib::RefPtr<Gtk::ListStore> port_list = iter->get_value (route_cols.port_list_col);
409  Gtk::ListStore::Children::iterator port_it;
410  uint32_t port_number = 1;
411 
412  for (port_it = port_list->children().begin(); port_it != port_list->children().end(); ++port_it) {
413  if (port_number == i) {
414  iter->set_value (route_cols.get_channel (i).label, (std::string) (*port_it)->get_value (route_cols.port_cols.label));
415  iter->set_value (route_cols.get_channel (i).port, (*port_it)->get_value (route_cols.port_cols.port));
416  }
417 
418  ++port_number;
419  }
420  }
421 
422  update_config ();
423 }
424 
425 void
426 PortExportChannelSelector::ChannelTreeView::update_selection_text (std::string const & path, std::string const & new_text, uint32_t channel)
427 {
428  Gtk::TreeModel::iterator iter = get_model ()->get_iter (path);
429  iter->set_value (route_cols.get_channel (channel).label, new_text);
430 
431  Glib::RefPtr<Gtk::ListStore> port_list = iter->get_value (route_cols.port_list_col);
432  Gtk::ListStore::Children::iterator port_it;
433 
434  for (port_it = port_list->children().begin(); port_it != port_list->children().end(); ++port_it) {
435  std::string label = port_it->get_value (route_cols.port_cols.label);
436  if (label == new_text) {
437  boost::weak_ptr<AudioPort> w = (*port_it)[route_cols.port_cols.port];
438  iter->set_value (route_cols.get_channel (channel).port, w);
439  }
440  }
441 
442  update_config ();
443 }
444 
447  ARDOUR::AudioRegion const & region,
448  ARDOUR::AudioTrack & track) :
449  ExportChannelSelector (_session, manager),
450  region (region),
451  track (track),
452  region_chans (region.n_channels()),
453  track_chans (track.n_outputs().n_audio()),
454 
455  raw_button (type_group),
456  fades_button (type_group),
457  processed_button (type_group)
458 {
459  pack_start (vbox);
460 
461  /* make fades+region gain be the default */
462 
463  fades_button.set_active ();
464 
465  raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans));
466  raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
467  vbox.pack_start (raw_button, false, false);
468 
469  fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans));
470  fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
471  vbox.pack_start (fades_button, false, false);
472 
473  processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
474  processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
475  vbox.pack_start (processed_button, false, false);
476 
478  vbox.show_all_children ();
479  show_all_children ();
480 }
481 
482 void
484 {
485  state = manager->get_channel_configs().front();
486 
487  if (!state) { return; }
488 
489  switch (state->config->region_processing_type()) {
491  // Do nothing
492  break;
493  case RegionExportChannelFactory::Raw:
494  raw_button.set_active (true);
495  break;
496  case RegionExportChannelFactory::Fades:
497  fades_button.set_active (true);
498  break;
499  case RegionExportChannelFactory::Processed:
500  processed_button.set_active (true);
501  break;
502  }
503 
504  handle_selection ();
505 }
506 
507 void
509 {
510  if (!state) {
511  return;
512  }
513 
514  state->config->clear_channels ();
515 
517  if (raw_button.get_active ()) {
518  type = RegionExportChannelFactory::Raw;
519  } else if (fades_button.get_active ()) {
520  type = RegionExportChannelFactory::Fades;
521  } else if (processed_button.get_active ()) {
522  type = RegionExportChannelFactory::Processed;
523  } else {
525  return;
526  }
527 
529  state->config->set_region_processing_type (type);
530 
531  for (size_t chan = 0; chan < region_chans; ++chan) {
532  state->config->register_channel (factory->create (chan));
533  }
534 
536 }
537 
538 /* Track export channel selector */
539 
541  : ExportChannelSelector(session, manager)
542  , region_contents_button(source_group, _("Export region contents"))
543  , track_output_button(source_group, _("Export track output"))
544 {
545  pack_start(main_layout);
546 
547  // Options
549  options_box.pack_start(track_output_button);
550  main_layout.pack_start(options_box, false, false);
551 
552  // Track scroller
554  track_scroller.set_size_request (-1, 130);
555  track_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
556  main_layout.pack_start(track_scroller);
557 
558  // Track list
559  track_list = Gtk::ListStore::create (track_cols);
560  track_list->set_sort_column (track_cols.order_key, Gtk::SORT_ASCENDING);
561  track_view.set_model (track_list);
562  track_view.set_headers_visible (true);
563 
564  track_view.append_column_editable (_("Export"), track_cols.selected);
565  Gtk::CellRendererToggle *toggle = dynamic_cast<Gtk::CellRendererToggle *>(track_view.get_column_cell_renderer (0));
566  toggle->set_alignment (0.0, 0.5);
567 
568  toggle->signal_toggled().connect (sigc::hide (sigc::mem_fun (*this, &TrackExportChannelSelector::update_config)));
569 
570  Gtk::CellRendererText* text_renderer = Gtk::manage (new Gtk::CellRendererText);
571  text_renderer->property_editable() = false;
572  text_renderer->set_alignment (0.0, 0.5);
573 
574  Gtk::TreeView::Column* column = Gtk::manage (new Gtk::TreeView::Column);
575  column->set_title (_("Track name"));
576 
577  track_view.append_column (*column);
578  column->pack_start (*text_renderer, false);
579  column->add_attribute (text_renderer->property_text(), track_cols.label);
580 
581  fill_list();
582 
583  show_all_children ();
584 }
585 
586 void
588 {
589  // TODO implement properly
590  update_config();
591 }
592 
593 void
595 {
596  track_list->clear();
597  RouteList routes = *_session->get_routes();
598 
599  for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
600  if (!boost::dynamic_pointer_cast<Track>(*it)) {
601  // not a track, must be a bus
602  if ((*it)->is_master () || (*it)->is_monitor ()) {
603  continue;
604  }
605  // not monitor or master bus
606  add_track (*it);
607  }
608  }
609  for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
610  if (boost::dynamic_pointer_cast<AudioTrack>(*it)) {
611  add_track (*it);
612  }
613  }
614 }
615 
616 void
618 {
619  Gtk::TreeModel::iterator iter = track_list->append();
620  Gtk::TreeModel::Row row = *iter;
621 
622  row[track_cols.selected] = true;
623  row[track_cols.label] = route->name();
624  row[track_cols.route] = route;
625  row[track_cols.order_key] = route->order_key();
626 }
627 
628 void
630 {
632 
633  for (Gtk::ListStore::Children::iterator it = track_list->children().begin(); it != track_list->children().end(); ++it) {
634  Gtk::TreeModel::Row row = *it;
635 
636  if (!row[track_cols.selected]) {
637  continue;
638  }
639 
641 
643 
644  if (track_output_button.get_active()) {
645  uint32_t outs = route->n_outputs().n_audio();
646  for (uint32_t i = 0; i < outs; ++i) {
647  boost::shared_ptr<AudioPort> port = route->output()->audio (i);
648  if (port) {
649  ExportChannelPtr channel (new PortExportChannel ());
650  PortExportChannel * pec = static_cast<PortExportChannel *> (channel.get());
651  pec->add_port(port);
652  state->config->register_channel(channel);
653  }
654  }
655  } else {
656  std::list<ExportChannelPtr> list;
657  RouteExportChannel::create_from_route (list, route);
658  state->config->register_channels (list);
659  }
660 
661  state->config->set_name (route->name());
662  }
663 
665 }
Handles RegionExportChannels and does actual reading from region.
void update_selection_text(std::string const &path, std::string const &new_text, uint32_t channel)
Glib::RefPtr< Gtk::ListStore > track_list
ARDOUR::AudioRegion const & region
Definition: ardour_ui.h:130
TrackExportChannelSelector(ARDOUR::Session *session, ProfileManagerPtr manager)
uint32_t n_audio() const
Definition: chan_count.h:63
Basic export channel that reads from AudioPorts.
Definition: Beats.hpp:239
ChannelConfigStateList const & get_channel_configs()
ARDOUR::ExportProfileManager::ChannelConfigStatePtr state
Gtk::TreeModelColumn< std::string > name
boost::shared_ptr< AudioPort > audio(uint32_t n) const
Definition: io.cc:1430
#define _(Text)
Definition: i18n.h:11
Gtk::ScrolledWindow channel_scroller
const ChanCount & n_ports() const
Definition: io.h:135
uint32_t order_key() const
Definition: route.cc:306
ChannelConfigStatePtr add_channel_config()
PortSet const & get_ports()
Gtk::TreeModelColumn< uint32_t > order_key
Definition: amp.h:29
boost::shared_ptr< Route > master_out() const
Definition: session.h:718
ChanCount n_outputs() const
Definition: route.h:93
Gtk::TreeModelColumn< boost::shared_ptr< ARDOUR::Route > > route
void add_port(boost::weak_ptr< AudioPort > port)
boost::shared_ptr< RouteList > get_routes() const
Definition: session.h:229
void update_toggle_selection(std::string const &path)
T * get() const
Definition: shared_ptr.hpp:268
ARDOUR::ExportProfileManager::ChannelConfigStatePtr state
RegionExportChannelSelector(ARDOUR::Session *session, ProfileManagerPtr manager, ARDOUR::AudioRegion const &region, ARDOUR::AudioTrack &track)
std::string name() const
Definition: debug.h:30
PortExportChannelSelector(ARDOUR::Session *session, ProfileManagerPtr manager)
boost::shared_ptr< ARDOUR::RegionExportChannelFactory > factory
boost::shared_ptr< IO > output() const
Definition: route.h:90
std::list< boost::shared_ptr< Route > > RouteList
Definition: types.h:532
sigc::signal< void > CriticalSelectionChanged
void add_track(boost::shared_ptr< ARDOUR::Route > route)
ARDOUR::Session * _session
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
ExportChannelPtr create(uint32_t channel)
Definition: io.h:67
Gtk::TreeModelColumn< std::string > label