do not add in ticks in the "at" position when computing BBT duration somewhere on...
[ardour.git] / libs / ardour / export_profile_manager.cc
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
21 #include <cassert>
22 #include <stdexcept>
23 #include <cerrno>
24
25 #include <glib.h>
26 #include <glib/gstdio.h>
27
28 #include <glibmm/fileutils.h>
29 #include <glibmm/miscutils.h>
30
31 #include "pbd/enumwriter.h"
32 #include "pbd/xml++.h"
33 #include "pbd/convert.h"
34
35 #include "ardour/export_profile_manager.h"
36 #include "ardour/export_format_specification.h"
37 #include "ardour/export_formats_search_path.h"
38 #include "ardour/export_timespan.h"
39 #include "ardour/export_channel_configuration.h"
40 #include "ardour/export_filename.h"
41 #include "ardour/export_preset.h"
42 #include "ardour/export_handler.h"
43 #include "ardour/export_failed.h"
44 #include "ardour/directory_names.h"
45 #include "ardour/filename_extensions.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/broadcast_info.h"
49
50 #include "i18n.h"
51
52 using namespace std;
53 using namespace Glib;
54 using namespace PBD;
55
56 namespace ARDOUR
57 {
58
59 ExportProfileManager::ExportProfileManager (Session & s, std::string xml_node_name)
60   : xml_node_name (xml_node_name)
61   , handler (s.get_export_handler())
62   , session (s)
63
64   , session_range (new Location (s))
65   , ranges (new LocationList ())
66   , single_range_mode (false)
67
68   , format_list (new FormatList ())
69 {
70         /* Initialize path variables */
71
72         export_config_dir = Glib::build_filename (user_config_directory(), export_dir_name);
73
74         search_path += export_formats_search_path();
75
76         info << string_compose (_("Searching for export formats in %1"), search_path.to_string()) << endmsg;
77
78         /* create export config directory if necessary */
79
80         if (!Glib::file_test (export_config_dir, Glib::FILE_TEST_EXISTS)) {
81                 if (g_mkdir_with_parents (export_config_dir.c_str(), 0755) != 0) {
82                         error << string_compose (_("Unable to create export format directory %1: %2"), export_config_dir, g_strerror(errno)) << endmsg;
83                 }
84         }
85
86         load_presets ();
87         load_formats ();
88
89         /* Initialize all lists with an empty config */
90
91         XMLNodeList dummy;
92         init_timespans (dummy);
93         init_channel_configs (dummy);
94         init_formats (dummy);
95         init_filenames (dummy);
96 }
97
98 ExportProfileManager::~ExportProfileManager ()
99 {
100         XMLNode * instant_xml (new XMLNode (xml_node_name));
101         serialize_profile (*instant_xml);
102         session.add_instant_xml (*instant_xml, false);
103 }
104
105 void
106 ExportProfileManager::load_profile ()
107 {
108         XMLNode * instant_node = session.instant_xml (xml_node_name);
109         if (instant_node) {
110                 set_state (*instant_node);
111         } else {
112                 XMLNode empty_node (xml_node_name);
113                 set_state (empty_node);
114         }
115 }
116
117 void
118 ExportProfileManager::prepare_for_export ()
119 {
120         TimespanListPtr ts_list = timespans.front()->timespans;
121
122         FormatStateList::const_iterator format_it;
123         FilenameStateList::const_iterator filename_it;
124
125         // For each timespan
126         for (TimespanList::iterator ts_it = ts_list->begin(); ts_it != ts_list->end(); ++ts_it) {
127                 // ..., each format-filename pair
128                 for (format_it = formats.begin(), filename_it = filenames.begin();
129                      format_it != formats.end() && filename_it != filenames.end();
130                      ++format_it, ++filename_it) {
131
132                         ExportFilenamePtr filename = (*filename_it)->filename;
133 //                      filename->include_timespan = (ts_list->size() > 1); Disabled for now...
134
135                         boost::shared_ptr<BroadcastInfo> b;
136                         if ((*format_it)->format->has_broadcast_info()) {
137                                 b.reset (new BroadcastInfo);
138                                 b->set_from_session (session, (*ts_it)->get_start());
139                         }
140
141                         // ...and each channel config
142                         filename->include_channel_config = (channel_configs.size() > 1);
143                         for(ChannelConfigStateList::iterator cc_it = channel_configs.begin(); cc_it != channel_configs.end(); ++cc_it) {
144                                 handler->add_export_config (*ts_it, (*cc_it)->config, (*format_it)->format, filename, b);
145                         }
146                 }
147         }
148 }
149
150 bool
151 ExportProfileManager::load_preset (ExportPresetPtr preset)
152 {
153         bool ok = true;
154
155         current_preset = preset;
156         if (!preset) { return false; }
157
158         XMLNode const * state;
159         if ((state = preset->get_local_state())) {
160                 set_local_state (*state);
161         } else { ok = false; }
162
163         if ((state = preset->get_global_state())) {
164                 if (!set_global_state (*state)) {
165                         ok = false;
166                 }
167         } else { ok = false; }
168
169         return ok;
170 }
171
172 void
173 ExportProfileManager::load_presets ()
174 {
175         vector<std::string> found = find_file (string_compose (X_("*%1"),export_preset_suffix));
176
177         for (vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
178                 load_preset_from_disk (*it);
179         }
180 }
181
182 std::string
183 ExportProfileManager::preset_filename (std::string const & preset_name)
184 {
185         string safe_name = legalize_for_path (preset_name);
186         return Glib::build_filename (export_config_dir, safe_name + export_preset_suffix);
187 }
188
189 ExportPresetPtr
190 ExportProfileManager::new_preset (string const & name)
191 {
192         // Generate new ID and do regular save
193         string filename = preset_filename (name);
194         current_preset.reset (new ExportPreset (filename, session));
195         preset_list.push_back (current_preset);
196         return save_preset (name);
197 }
198
199 ExportPresetPtr
200 ExportProfileManager::save_preset (string const & name)
201 {
202         string filename = preset_filename (name);
203
204         if (!current_preset) {
205                 current_preset.reset (new ExportPreset (filename, session));
206                 preset_list.push_back (current_preset);
207         }
208
209         XMLNode * global_preset = new XMLNode ("ExportPreset");
210         XMLNode * local_preset = new XMLNode ("ExportPreset");
211
212         serialize_global_profile (*global_preset);
213         serialize_local_profile (*local_preset);
214
215         current_preset->set_name (name);
216         current_preset->set_global_state (*global_preset);
217         current_preset->set_local_state (*local_preset);
218
219         current_preset->save (filename);
220
221         return current_preset;
222 }
223
224 void
225 ExportProfileManager::remove_preset ()
226 {
227         if (!current_preset) { return; }
228
229         for (PresetList::iterator it = preset_list.begin(); it != preset_list.end(); ++it) {
230                 if (*it == current_preset) {
231                         preset_list.erase (it);
232                         break;
233                 }
234         }
235
236         FileMap::iterator it = preset_file_map.find (current_preset->id());
237         if (it != preset_file_map.end()) {
238                 if (g_remove (it->second.c_str()) != 0) {
239                         error << string_compose (_("Unable to remove export preset %1: %2"), it->second, g_strerror(errno)) << endmsg;
240                 }
241                 preset_file_map.erase (it);
242         }
243
244         current_preset->remove_local();
245         current_preset.reset();
246 }
247
248 void
249 ExportProfileManager::load_preset_from_disk (std::string const & path)
250 {
251         ExportPresetPtr preset (new ExportPreset (path, session));
252
253         /* Handle id to filename mapping and don't add duplicates to list */
254
255         FilePair pair (preset->id(), path);
256         if (preset_file_map.insert (pair).second) {
257                 preset_list.push_back (preset);
258         }
259 }
260
261 bool
262 ExportProfileManager::set_state (XMLNode const & root)
263 {
264         return set_global_state (root) & set_local_state (root);
265 }
266
267 bool
268 ExportProfileManager::set_global_state (XMLNode const & root)
269 {
270         return init_filenames (root.children ("ExportFilename")) &
271                init_formats (root.children ("ExportFormat"));
272 }
273
274 bool
275 ExportProfileManager::set_local_state (XMLNode const & root)
276 {
277         return init_timespans (root.children ("ExportTimespan")) &
278                init_channel_configs (root.children ("ExportChannelConfiguration"));
279 }
280
281 void
282 ExportProfileManager::serialize_profile (XMLNode & root)
283 {
284         serialize_local_profile (root);
285         serialize_global_profile (root);
286 }
287
288 void
289 ExportProfileManager::serialize_global_profile (XMLNode & root)
290 {
291         for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
292                 root.add_child_nocopy (serialize_format (*it));
293         }
294
295         for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
296                 root.add_child_nocopy ((*it)->filename->get_state());
297         }
298 }
299
300 void
301 ExportProfileManager::serialize_local_profile (XMLNode & root)
302 {
303         for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
304                 root.add_child_nocopy (serialize_timespan (*it));
305         }
306
307         for (ChannelConfigStateList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
308                 root.add_child_nocopy ((*it)->config->get_state());
309         }
310 }
311
312 std::vector<std::string>
313 ExportProfileManager::find_file (std::string const & pattern)
314 {
315         vector<std::string> found;
316
317         Glib::PatternSpec pattern_spec (pattern);
318         find_matching_files_in_search_path (search_path, pattern_spec, found);
319
320         return found;
321 }
322
323 void
324 ExportProfileManager::set_selection_range (framepos_t start, framepos_t end)
325 {
326
327         if (start || end) {
328                 selection_range.reset (new Location (session));
329                 selection_range->set_name (_("Selection"));
330                 selection_range->set (start, end);
331         } else {
332                 selection_range.reset();
333         }
334
335         for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
336                 (*it)->selection_range = selection_range;
337         }
338 }
339
340 std::string
341 ExportProfileManager::set_single_range (framepos_t start, framepos_t end, string name)
342 {
343         single_range_mode = true;
344
345         single_range.reset (new Location (session));
346         single_range->set_name (name);
347         single_range->set (start, end);
348
349         update_ranges ();
350
351         return single_range->id().to_s();
352 }
353
354 bool
355 ExportProfileManager::init_timespans (XMLNodeList nodes)
356 {
357         timespans.clear ();
358         update_ranges ();
359
360         bool ok = true;
361         for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
362                 TimespanStatePtr span = deserialize_timespan (**it);
363                 if (span) {
364                         timespans.push_back (span);
365                 } else { ok = false; }
366         }
367
368         if (timespans.empty()) {
369                 TimespanStatePtr state (new TimespanState (session_range, selection_range, ranges));
370                 timespans.push_back (state);
371
372                 // Add session as default selection
373                 ExportTimespanPtr timespan = handler->add_timespan();
374                 timespan->set_name (session_range->name());
375                 timespan->set_range_id ("session");
376                 timespan->set_range (session_range->start(), session_range->end());
377                 state->timespans->push_back (timespan);
378                 return false;
379         }
380
381         return ok;
382 }
383
384 ExportProfileManager::TimespanStatePtr
385 ExportProfileManager::deserialize_timespan (XMLNode & root)
386 {
387         TimespanStatePtr state (new TimespanState (session_range, selection_range, ranges));
388         XMLProperty const * prop;
389
390         XMLNodeList spans = root.children ("Range");
391         for (XMLNodeList::iterator node_it = spans.begin(); node_it != spans.end(); ++node_it) {
392
393                 prop = (*node_it)->property ("id");
394                 if (!prop) { continue; }
395                 string id = prop->value();
396
397                 for (LocationList::iterator it = ranges->begin(); it != ranges->end(); ++it) {
398                         if ((!id.compare ("session") && *it == session_range.get()) ||
399                             (!id.compare ("selection") && *it == selection_range.get()) ||
400                             (!id.compare ((*it)->id().to_s()))) {
401                                 ExportTimespanPtr timespan = handler->add_timespan();
402                                 timespan->set_name ((*it)->name());
403                                 timespan->set_range_id (id);
404                                 timespan->set_range ((*it)->start(), (*it)->end());
405                                 state->timespans->push_back (timespan);
406                         }
407                 }
408         }
409
410         if ((prop = root.property ("format"))) {
411                 state->time_format = (TimeFormat) string_2_enum (prop->value(), TimeFormat);
412         }
413
414         return state;
415 }
416
417 XMLNode &
418 ExportProfileManager::serialize_timespan (TimespanStatePtr state)
419 {
420         XMLNode & root = *(new XMLNode ("ExportTimespan"));
421         XMLNode * span;
422
423         update_ranges ();
424
425         for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
426                 if ((span = root.add_child ("Range"))) {
427                         span->add_property ("id", (*it)->range_id());
428                 }
429         }
430
431         root.add_property ("format", enum_2_string (state->time_format));
432
433         return root;
434 }
435
436 void
437 ExportProfileManager::update_ranges () {
438         ranges->clear();
439
440         if (single_range_mode) {
441                 ranges->push_back (single_range.get());
442                 return;
443         }
444
445         /* Session */
446
447         session_range->set_name (_("Session"));
448         session_range->set (session.current_start_frame(), session.current_end_frame());
449         ranges->push_back (session_range.get());
450
451         /* Selection */
452
453         if (selection_range) {
454                 ranges->push_back (selection_range.get());
455         }
456
457         /* ranges */
458
459         LocationList const & list (session.locations()->list());
460         for (LocationList::const_iterator it = list.begin(); it != list.end(); ++it) {
461                 if ((*it)->is_range_marker()) {
462                         ranges->push_back (*it);
463                 }
464         }
465 }
466
467 ExportProfileManager::ChannelConfigStatePtr
468 ExportProfileManager::add_channel_config ()
469 {
470         ChannelConfigStatePtr ptr(new ChannelConfigState(handler->add_channel_config()));
471         channel_configs.push_back(ptr);
472         return ptr;
473 }
474
475 bool
476 ExportProfileManager::init_channel_configs (XMLNodeList nodes)
477 {
478         channel_configs.clear();
479
480         if (nodes.empty()) {
481                 ChannelConfigStatePtr config (new ChannelConfigState (handler->add_channel_config()));
482                 channel_configs.push_back (config);
483
484                 // Add master outs as default
485                 IO* master_out = session.master_out()->output().get();
486                 if (!master_out) { return false; }
487
488                 for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
489                         PortExportChannel * channel = new PortExportChannel ();
490                         channel->add_port (master_out->audio (n));
491
492                         ExportChannelPtr chan_ptr (channel);
493                         config->config->register_channel (chan_ptr);
494                 }
495                 return false;
496         }
497
498         for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
499                 ChannelConfigStatePtr config (new ChannelConfigState (handler->add_channel_config()));
500                 config->config->set_state (**it);
501                 channel_configs.push_back (config);
502         }
503
504         return true;
505 }
506
507 ExportProfileManager::FormatStatePtr
508 ExportProfileManager::duplicate_format_state (FormatStatePtr state)
509 {
510         /* Note: The pointer in the new FormatState should point to the same format spec
511                  as the original state's pointer. The spec itself should not be copied!   */
512
513         FormatStatePtr format (new FormatState (format_list, state->format));
514         formats.push_back (format);
515         return format;
516 }
517
518 void
519 ExportProfileManager::remove_format_state (FormatStatePtr state)
520 {
521         for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
522                 if (*it == state) {
523                         formats.erase (it);
524                         return;
525                 }
526         }
527 }
528
529 std::string
530 ExportProfileManager::save_format_to_disk (ExportFormatSpecPtr format)
531 {
532         // TODO filename character stripping
533
534         /* Get filename for file */
535
536         string new_name = format->name();
537         new_name += export_format_suffix;
538
539         /* make sure its legal for the filesystem */
540
541         new_name = legalize_for_path (new_name);
542
543         std::string new_path = Glib::build_filename (export_config_dir, new_name);
544
545         /* Check if format is on disk already */
546         FileMap::iterator it;
547         if ((it = format_file_map.find (format->id())) != format_file_map.end()) {
548
549                 /* Check if config is not in user config dir */
550                 if (Glib::path_get_dirname (it->second) != export_config_dir) {
551
552                         /* Write new file */
553
554                         XMLTree tree (new_path);
555                         tree.set_root (&format->get_state());
556                         tree.write();
557
558                 } else {
559
560                         /* Update file and rename if necessary */
561
562                         XMLTree tree (it->second);
563                         tree.set_root (&format->get_state());
564                         tree.write();
565
566                         if (new_name != Glib::path_get_basename (it->second)) {
567                                 if (g_rename (it->second.c_str(), new_path.c_str()) != 0) {
568                                         error << string_compose (_("Unable to rename export format %1 to %2: %3"), it->second, new_path, g_strerror(errno)) << endmsg;
569                                 };
570                         }
571                 }
572
573                 it->second = new_path;
574
575         } else {
576                 /* Write new file */
577
578                 XMLTree tree (new_path);
579                 tree.set_root (&format->get_state());
580                 tree.write();
581         }
582
583         FormatListChanged ();
584         return new_path;
585 }
586
587 void
588 ExportProfileManager::remove_format_profile (ExportFormatSpecPtr format)
589 {
590         for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
591                 if (*it == format) {
592                         format_list->erase (it);
593                         break;
594                 }
595         }
596
597         FileMap::iterator it = format_file_map.find (format->id());
598         if (it != format_file_map.end()) {
599                 if (g_remove (it->second.c_str()) != 0) {
600                         error << string_compose (_("Unable to remove export profile %1: %2"), it->second, g_strerror(errno)) << endmsg;
601                         return;
602                 }
603                 format_file_map.erase (it);
604         }
605
606         FormatListChanged ();
607 }
608
609 ExportFormatSpecPtr
610 ExportProfileManager::get_new_format (ExportFormatSpecPtr original)
611 {
612         ExportFormatSpecPtr format;
613         if (original) {
614                 format.reset (new ExportFormatSpecification (*original));
615         } else {
616                 format = handler->add_format();
617                 format->set_name (_("empty format"));
618         }
619
620         std::string path = save_format_to_disk (format);
621         FilePair pair (format->id(), path);
622         format_file_map.insert (pair);
623
624         format_list->push_back (format);
625         FormatListChanged ();
626
627         return format;
628 }
629
630 bool
631 ExportProfileManager::init_formats (XMLNodeList nodes)
632 {
633         formats.clear();
634
635         bool ok = true;
636         for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
637                 FormatStatePtr format = deserialize_format (**it);
638                 if (format) {
639                         formats.push_back (format);
640                 } else { ok = false; }
641         }
642
643         if (formats.empty ()) {
644                 FormatStatePtr format (new FormatState (format_list, ExportFormatSpecPtr ()));
645                 formats.push_back (format);
646                 return false;
647         }
648
649         return ok;
650 }
651
652 ExportProfileManager::FormatStatePtr
653 ExportProfileManager::deserialize_format (XMLNode & root)
654 {
655         XMLProperty * prop;
656         UUID id;
657
658         if ((prop = root.property ("id"))) {
659                 id = prop->value();
660         }
661
662         for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
663                 if ((*it)->id() == id) {
664                         return FormatStatePtr (new FormatState (format_list, *it));
665                 }
666         }
667
668         return FormatStatePtr ();
669 }
670
671 XMLNode &
672 ExportProfileManager::serialize_format (FormatStatePtr state)
673 {
674         XMLNode * root = new XMLNode ("ExportFormat");
675
676         string id = state->format ? state->format->id().to_s() : "";
677         root->add_property ("id", id);
678
679         return *root;
680 }
681
682 void
683 ExportProfileManager::load_formats ()
684 {
685         vector<std::string> found = find_file (string_compose ("*%1", export_format_suffix));
686
687         for (vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
688                 load_format_from_disk (*it);
689         }
690 }
691
692 void
693 ExportProfileManager::load_format_from_disk (std::string const & path)
694 {
695         XMLTree const tree (path);
696         ExportFormatSpecPtr format = handler->add_format (*tree.root());
697
698         /* Handle id to filename mapping and don't add duplicates to list */
699
700         FilePair pair (format->id(), path);
701         if (format_file_map.insert (pair).second) {
702                 format_list->push_back (format);
703         }
704
705         FormatListChanged ();
706 }
707
708 ExportProfileManager::FilenameStatePtr
709 ExportProfileManager::duplicate_filename_state (FilenameStatePtr state)
710 {
711         FilenameStatePtr filename (new FilenameState (handler->add_filename_copy (state->filename)));
712         filenames.push_back (filename);
713         return filename;
714 }
715
716 void
717 ExportProfileManager::remove_filename_state (FilenameStatePtr state)
718 {
719         for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
720                 if (*it == state) {
721                         filenames.erase (it);
722                         return;
723                 }
724         }
725 }
726
727 std::string
728 ExportProfileManager::get_sample_filename_for_format (ExportFilenamePtr filename, ExportFormatSpecPtr format)
729 {
730         assert (format);
731         
732         if (channel_configs.empty()) { return ""; }
733
734         std::list<string> filenames;
735         build_filenames (filenames, filename, timespans.front()->timespans,
736                          channel_configs.front()->config, format);
737
738         if (filenames.empty()) { return ""; }
739         return filenames.front();
740 }
741
742 bool
743 ExportProfileManager::init_filenames (XMLNodeList nodes)
744 {
745         filenames.clear ();
746
747         for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
748                 ExportFilenamePtr filename = handler->add_filename();
749                 filename->set_state (**it);
750                 filenames.push_back (FilenameStatePtr (new FilenameState (filename)));
751         }
752
753         if (filenames.empty()) {
754                 FilenameStatePtr filename (new FilenameState (handler->add_filename()));
755                 filenames.push_back (filename);
756                 return false;
757         }
758
759         return true;
760 }
761
762 boost::shared_ptr<ExportProfileManager::Warnings>
763 ExportProfileManager::get_warnings ()
764 {
765         boost::shared_ptr<Warnings> warnings (new Warnings ());
766
767         ChannelConfigStatePtr channel_config_state;
768         if (!channel_configs.empty ()) {
769                 channel_config_state = channel_configs.front();
770         }
771         
772         TimespanStatePtr timespan_state = timespans.front();
773
774         /*** Check "global" config ***/
775
776         TimespanListPtr timespans = timespan_state->timespans;
777
778         ExportChannelConfigPtr channel_config;
779         if (channel_config_state) {
780                 channel_config = channel_config_state->config;
781         }
782
783         /* Check Timespans are not empty */
784
785         if (timespans->empty()) {
786                 warnings->errors.push_back (_("No timespan has been selected!"));
787         }
788
789         if (channel_config_state == 0) {
790                 warnings->errors.push_back (_("No channels have been selected!"));
791         } else {
792                 /* Check channel config ports */
793                 if (!channel_config->all_channels_have_ports ()) {
794                         warnings->warnings.push_back (_("Some channels are empty"));
795                 }
796         }
797
798         /*** Check files ***/
799
800         if (channel_config_state) {
801                 FormatStateList::const_iterator format_it;
802                 FilenameStateList::const_iterator filename_it;
803                 for (format_it = formats.begin(), filename_it = filenames.begin();
804                      format_it != formats.end() && filename_it != filenames.end();
805                      ++format_it, ++filename_it) {
806                         check_config (warnings, timespan_state, channel_config_state, *format_it, *filename_it);
807                 }
808         }
809         
810         return warnings;
811 }
812
813 void
814 ExportProfileManager::check_config (boost::shared_ptr<Warnings> warnings,
815                                     TimespanStatePtr timespan_state,
816                                     ChannelConfigStatePtr channel_config_state,
817                                     FormatStatePtr format_state,
818                                     FilenameStatePtr filename_state)
819 {
820         TimespanListPtr timespans = timespan_state->timespans;
821         ExportChannelConfigPtr channel_config = channel_config_state->config;
822         ExportFormatSpecPtr format = format_state->format;
823         ExportFilenamePtr filename = filename_state->filename;
824
825         /* Check format and maximum channel count */
826         if (!format || !format->type()) {
827                 warnings->errors.push_back (_("No format selected!"));
828         } else if (!channel_config->get_n_chans()) {
829                 warnings->errors.push_back (_("All channels are empty!"));
830         } else if (!check_format (format, channel_config->get_n_chans())) {
831                 warnings->errors.push_back (_("One or more of the selected formats is not compatible with this system!"));
832         } else if (format->channel_limit() < channel_config->get_n_chans()) {
833                 warnings->errors.push_back
834                   (string_compose (_("%1 supports only %2 channels, but you have %3 channels in your channel configuration"),
835                                      format->format_name(),
836                                      format->channel_limit(),
837                                      channel_config->get_n_chans()));
838         }
839
840         if (!warnings->errors.empty()) { return; }
841
842         /* Check filenames */
843
844 //      filename->include_timespan = (timespans->size() > 1); Disabled for now...
845
846         std::list<string> paths;
847         build_filenames(paths, filename, timespans, channel_config, format);
848
849         for (std::list<string>::const_iterator path_it = paths.begin(); path_it != paths.end(); ++path_it) {
850
851                 string path = *path_it;
852
853                 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
854                         warnings->conflicting_filenames.push_back (path);
855                 }
856
857                 if (format->with_toc()) {
858                         string marker_file = handler->get_cd_marker_filename(path, CDMarkerTOC);
859                         if (Glib::file_test (marker_file, Glib::FILE_TEST_EXISTS)) {
860                                 warnings->conflicting_filenames.push_back (marker_file);
861                         }
862                 }
863
864                 if (format->with_cue()) {
865                         string marker_file = handler->get_cd_marker_filename(path, CDMarkerCUE);
866                         if (Glib::file_test (marker_file, Glib::FILE_TEST_EXISTS)) {
867                                 warnings->conflicting_filenames.push_back (marker_file);
868                         }
869                 }
870         }
871 }
872
873 bool
874 ExportProfileManager::check_format (ExportFormatSpecPtr format, uint32_t channels)
875 {
876         switch (format->type()) {
877           case ExportFormatBase::T_Sndfile:
878                 return check_sndfile_format (format, channels);
879
880           default:
881                 throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!"));
882         }
883 }
884
885 bool
886 ExportProfileManager::check_sndfile_format (ExportFormatSpecPtr format, unsigned int channels)
887 {
888         SF_INFO sf_info;
889         sf_info.channels = channels;
890         sf_info.samplerate = format->sample_rate ();
891         sf_info.format = format->format_id () | format->sample_format ();
892
893         return (sf_format_check (&sf_info) == SF_TRUE ? true : false);
894 }
895
896 void
897 ExportProfileManager::build_filenames(std::list<std::string> & result, ExportFilenamePtr filename,
898                                       TimespanListPtr timespans, ExportChannelConfigPtr channel_config,
899                                       ExportFormatSpecPtr format)
900 {
901         for (std::list<ExportTimespanPtr>::iterator timespan_it = timespans->begin();
902              timespan_it != timespans->end(); ++timespan_it) {
903                 filename->set_timespan (*timespan_it);
904
905                 if (channel_config->get_split()) {
906                         filename->include_channel = true;
907
908                         for (uint32_t chan = 1; chan <= channel_config->get_n_chans(); ++chan) {
909                                 filename->set_channel (chan);
910                                 result.push_back(filename->get_path (format));
911                         }
912
913                 } else {
914                         filename->include_channel = false;
915                         result.push_back(filename->get_path (format));
916                 }
917         }
918 }
919
920 }; // namespace ARDOUR