13
0
livetrax/libs/ardour/export_profile_manager.cc
Sakari Bergen b01ff11b04 Return null if deserializing an export timespan doesn't succeed with at least one range.
This should fix an issue with loading old sessions using the old way of
handling session ranges specially.
2013-05-26 21:25:22 +03:00

953 lines
25 KiB
C++

/*
Copyright (C) 2008 Paul Davis
Author: Sakari Bergen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cassert>
#include <stdexcept>
#include <cerrno>
#include <glib.h>
#include <glib/gstdio.h>
#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
#include "pbd/enumwriter.h"
#include "pbd/xml++.h"
#include "pbd/convert.h"
#include "ardour/export_profile_manager.h"
#include "ardour/export_format_specification.h"
#include "ardour/export_formats_search_path.h"
#include "ardour/export_timespan.h"
#include "ardour/export_channel_configuration.h"
#include "ardour/export_filename.h"
#include "ardour/export_preset.h"
#include "ardour/export_handler.h"
#include "ardour/export_failed.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/broadcast_info.h"
#include "i18n.h"
using namespace std;
using namespace Glib;
using namespace PBD;
namespace ARDOUR
{
ExportProfileManager::ExportProfileManager (Session & s, ExportType type)
: type(type)
, handler (s.get_export_handler())
, session (s)
, ranges (new LocationList ())
, single_range_mode (false)
, format_list (new FormatList ())
{
switch(type) {
case RegularExport:
xml_node_name = X_("ExportProfile");
break;
case RangeExport:
xml_node_name = X_("RangeExportProfile");
break;
case SelectionExport:
xml_node_name = X_("SelectionExportProfile");
break;
case RegionExport:
xml_node_name = X_("RegionExportProfile");
break;
case StemExport:
xml_node_name = X_("StemExportProfile");
break;
}
/* Initialize path variables */
export_config_dir = Glib::build_filename (user_config_directory(), export_dir_name);
search_path += export_formats_search_path();
info << string_compose (_("Searching for export formats in %1"), search_path.to_string()) << endmsg;
/* create export config directory if necessary */
if (!Glib::file_test (export_config_dir, Glib::FILE_TEST_EXISTS)) {
if (g_mkdir_with_parents (export_config_dir.c_str(), 0755) != 0) {
error << string_compose (_("Unable to create export format directory %1: %2"), export_config_dir, g_strerror(errno)) << endmsg;
}
}
load_presets ();
load_formats ();
/* Initialize all lists with an empty config */
XMLNodeList dummy;
init_timespans (dummy);
init_channel_configs (dummy);
init_formats (dummy);
init_filenames (dummy);
}
ExportProfileManager::~ExportProfileManager ()
{
XMLNode * instant_xml (new XMLNode (xml_node_name));
serialize_profile (*instant_xml);
session.add_instant_xml (*instant_xml, false);
}
void
ExportProfileManager::load_profile ()
{
XMLNode * instant_node = session.instant_xml (xml_node_name);
if (instant_node) {
set_state (*instant_node);
} else {
XMLNode empty_node (xml_node_name);
set_state (empty_node);
}
}
void
ExportProfileManager::prepare_for_export ()
{
TimespanListPtr ts_list = timespans.front()->timespans;
FormatStateList::const_iterator format_it;
FilenameStateList::const_iterator filename_it;
// For each timespan
for (TimespanList::iterator ts_it = ts_list->begin(); ts_it != ts_list->end(); ++ts_it) {
// ..., each format-filename pair
for (format_it = formats.begin(), filename_it = filenames.begin();
format_it != formats.end() && filename_it != filenames.end();
++format_it, ++filename_it) {
ExportFilenamePtr filename = (*filename_it)->filename;
// filename->include_timespan = (ts_list->size() > 1); Disabled for now...
boost::shared_ptr<BroadcastInfo> b;
if ((*format_it)->format->has_broadcast_info()) {
b.reset (new BroadcastInfo);
b->set_from_session (session, (*ts_it)->get_start());
}
// ...and each channel config
filename->include_channel_config = (type == StemExport) ||
(channel_configs.size() > 1);
for(ChannelConfigStateList::iterator cc_it = channel_configs.begin(); cc_it != channel_configs.end(); ++cc_it) {
handler->add_export_config (*ts_it, (*cc_it)->config, (*format_it)->format, filename, b);
}
}
}
}
bool
ExportProfileManager::load_preset (ExportPresetPtr preset)
{
bool ok = true;
current_preset = preset;
if (!preset) { return false; }
XMLNode const * state;
if ((state = preset->get_local_state())) {
set_local_state (*state);
} else { ok = false; }
if ((state = preset->get_global_state())) {
if (!set_global_state (*state)) {
ok = false;
}
} else { ok = false; }
return ok;
}
void
ExportProfileManager::load_presets ()
{
vector<std::string> found = find_file (string_compose (X_("*%1"),export_preset_suffix));
for (vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
load_preset_from_disk (*it);
}
}
std::string
ExportProfileManager::preset_filename (std::string const & preset_name)
{
string safe_name = legalize_for_path (preset_name);
return Glib::build_filename (export_config_dir, safe_name + export_preset_suffix);
}
ExportPresetPtr
ExportProfileManager::new_preset (string const & name)
{
// Generate new ID and do regular save
string filename = preset_filename (name);
current_preset.reset (new ExportPreset (filename, session));
preset_list.push_back (current_preset);
return save_preset (name);
}
ExportPresetPtr
ExportProfileManager::save_preset (string const & name)
{
string filename = preset_filename (name);
if (!current_preset) {
current_preset.reset (new ExportPreset (filename, session));
preset_list.push_back (current_preset);
}
XMLNode * global_preset = new XMLNode ("ExportPreset");
XMLNode * local_preset = new XMLNode ("ExportPreset");
serialize_global_profile (*global_preset);
serialize_local_profile (*local_preset);
current_preset->set_name (name);
current_preset->set_global_state (*global_preset);
current_preset->set_local_state (*local_preset);
current_preset->save (filename);
return current_preset;
}
void
ExportProfileManager::remove_preset ()
{
if (!current_preset) { return; }
for (PresetList::iterator it = preset_list.begin(); it != preset_list.end(); ++it) {
if (*it == current_preset) {
preset_list.erase (it);
break;
}
}
FileMap::iterator it = preset_file_map.find (current_preset->id());
if (it != preset_file_map.end()) {
if (g_remove (it->second.c_str()) != 0) {
error << string_compose (_("Unable to remove export preset %1: %2"), it->second, g_strerror(errno)) << endmsg;
}
preset_file_map.erase (it);
}
current_preset->remove_local();
current_preset.reset();
}
void
ExportProfileManager::load_preset_from_disk (std::string const & path)
{
ExportPresetPtr preset (new ExportPreset (path, session));
/* Handle id to filename mapping and don't add duplicates to list */
FilePair pair (preset->id(), path);
if (preset_file_map.insert (pair).second) {
preset_list.push_back (preset);
}
}
bool
ExportProfileManager::set_state (XMLNode const & root)
{
return set_global_state (root) & set_local_state (root);
}
bool
ExportProfileManager::set_global_state (XMLNode const & root)
{
return init_filenames (root.children ("ExportFilename")) &
init_formats (root.children ("ExportFormat"));
}
bool
ExportProfileManager::set_local_state (XMLNode const & root)
{
return init_timespans (root.children ("ExportTimespan")) &
init_channel_configs (root.children ("ExportChannelConfiguration"));
}
void
ExportProfileManager::serialize_profile (XMLNode & root)
{
serialize_local_profile (root);
serialize_global_profile (root);
}
void
ExportProfileManager::serialize_global_profile (XMLNode & root)
{
for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
root.add_child_nocopy (serialize_format (*it));
}
for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
root.add_child_nocopy ((*it)->filename->get_state());
}
}
void
ExportProfileManager::serialize_local_profile (XMLNode & root)
{
for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
root.add_child_nocopy (serialize_timespan (*it));
}
for (ChannelConfigStateList::iterator it = channel_configs.begin(); it != channel_configs.end(); ++it) {
root.add_child_nocopy ((*it)->config->get_state());
}
}
std::vector<std::string>
ExportProfileManager::find_file (std::string const & pattern)
{
vector<std::string> found;
Glib::PatternSpec pattern_spec (pattern);
find_matching_files_in_search_path (search_path, pattern_spec, found);
return found;
}
void
ExportProfileManager::set_selection_range (framepos_t start, framepos_t end)
{
if (start || end) {
selection_range.reset (new Location (session));
selection_range->set_name (_("Selection"));
selection_range->set (start, end);
} else {
selection_range.reset();
}
for (TimespanStateList::iterator it = timespans.begin(); it != timespans.end(); ++it) {
(*it)->selection_range = selection_range;
}
}
std::string
ExportProfileManager::set_single_range (framepos_t start, framepos_t end, string name)
{
single_range_mode = true;
single_range.reset (new Location (session));
single_range->set_name (name);
single_range->set (start, end);
update_ranges ();
return single_range->id().to_s();
}
bool
ExportProfileManager::init_timespans (XMLNodeList nodes)
{
timespans.clear ();
update_ranges ();
bool ok = true;
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
TimespanStatePtr span = deserialize_timespan (**it);
if (span) {
timespans.push_back (span);
} else { ok = false; }
}
if (timespans.empty()) {
TimespanStatePtr state (new TimespanState (selection_range, ranges));
timespans.push_back (state);
// Add session as default selection
Location * session_range = session.locations()->session_range_location();
if (!session_range) { return false; }
ExportTimespanPtr timespan = handler->add_timespan();
timespan->set_name (session_range->name());
timespan->set_range_id (session_range->id().to_s());
timespan->set_range (session_range->start(), session_range->end());
state->timespans->push_back (timespan);
return false;
}
return ok;
}
ExportProfileManager::TimespanStatePtr
ExportProfileManager::deserialize_timespan (XMLNode & root)
{
TimespanStatePtr state (new TimespanState (selection_range, ranges));
XMLProperty const * prop;
XMLNodeList spans = root.children ("Range");
for (XMLNodeList::iterator node_it = spans.begin(); node_it != spans.end(); ++node_it) {
prop = (*node_it)->property ("id");
if (!prop) { continue; }
string id = prop->value();
Location * location = 0;
for (LocationList::iterator it = ranges->begin(); it != ranges->end(); ++it) {
if ((id == "selection" && *it == selection_range.get()) ||
(id == (*it)->id().to_s())) {
location = *it;
break;
}
}
if (!location) { continue; }
ExportTimespanPtr timespan = handler->add_timespan();
timespan->set_name (location->name());
timespan->set_range_id (location->id().to_s());
timespan->set_range (location->start(), location->end());
state->timespans->push_back (timespan);
}
if ((prop = root.property ("format"))) {
state->time_format = (TimeFormat) string_2_enum (prop->value(), TimeFormat);
}
if (state->timespans->empty()) {
return TimespanStatePtr();
}
return state;
}
XMLNode &
ExportProfileManager::serialize_timespan (TimespanStatePtr state)
{
XMLNode & root = *(new XMLNode ("ExportTimespan"));
XMLNode * span;
update_ranges ();
for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
if ((span = root.add_child ("Range"))) {
span->add_property ("id", (*it)->range_id());
}
}
root.add_property ("format", enum_2_string (state->time_format));
return root;
}
void
ExportProfileManager::update_ranges () {
ranges->clear();
if (single_range_mode) {
ranges->push_back (single_range.get());
return;
}
/* Session */
Location * session_range = session.locations()->session_range_location();
if (session_range) {
ranges->push_back (session_range);
}
/* Selection */
if (selection_range) {
ranges->push_back (selection_range.get());
}
/* ranges */
LocationList const & list (session.locations()->list());
for (LocationList::const_iterator it = list.begin(); it != list.end(); ++it) {
if ((*it)->is_range_marker()) {
ranges->push_back (*it);
}
}
}
ExportProfileManager::ChannelConfigStatePtr
ExportProfileManager::add_channel_config ()
{
ChannelConfigStatePtr ptr(new ChannelConfigState(handler->add_channel_config()));
channel_configs.push_back(ptr);
return ptr;
}
bool
ExportProfileManager::init_channel_configs (XMLNodeList nodes)
{
channel_configs.clear();
if (nodes.empty()) {
ChannelConfigStatePtr config (new ChannelConfigState (handler->add_channel_config()));
channel_configs.push_back (config);
// Add master outs as default
if (!session.master_out()) { return false; }
IO* master_out = session.master_out()->output().get();
if (!master_out) { return false; }
for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
PortExportChannel * channel = new PortExportChannel ();
channel->add_port (master_out->audio (n));
ExportChannelPtr chan_ptr (channel);
config->config->register_channel (chan_ptr);
}
return false;
}
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
ChannelConfigStatePtr config (new ChannelConfigState (handler->add_channel_config()));
config->config->set_state (**it);
channel_configs.push_back (config);
}
return true;
}
ExportProfileManager::FormatStatePtr
ExportProfileManager::duplicate_format_state (FormatStatePtr state)
{
/* Note: The pointer in the new FormatState should point to the same format spec
as the original state's pointer. The spec itself should not be copied! */
FormatStatePtr format (new FormatState (format_list, state->format));
formats.push_back (format);
return format;
}
void
ExportProfileManager::remove_format_state (FormatStatePtr state)
{
for (FormatStateList::iterator it = formats.begin(); it != formats.end(); ++it) {
if (*it == state) {
formats.erase (it);
return;
}
}
}
std::string
ExportProfileManager::save_format_to_disk (ExportFormatSpecPtr format)
{
// TODO filename character stripping
/* Get filename for file */
string new_name = format->name();
new_name += export_format_suffix;
/* make sure its legal for the filesystem */
new_name = legalize_for_path (new_name);
std::string new_path = Glib::build_filename (export_config_dir, new_name);
/* Check if format is on disk already */
FileMap::iterator it;
if ((it = format_file_map.find (format->id())) != format_file_map.end()) {
/* Check if config is not in user config dir */
if (Glib::path_get_dirname (it->second) != export_config_dir) {
/* Write new file */
XMLTree tree (new_path);
tree.set_root (&format->get_state());
tree.write();
} else {
/* Update file and rename if necessary */
XMLTree tree (it->second);
tree.set_root (&format->get_state());
tree.write();
if (new_name != Glib::path_get_basename (it->second)) {
if (g_rename (it->second.c_str(), new_path.c_str()) != 0) {
error << string_compose (_("Unable to rename export format %1 to %2: %3"), it->second, new_path, g_strerror(errno)) << endmsg;
};
}
}
it->second = new_path;
} else {
/* Write new file */
XMLTree tree (new_path);
tree.set_root (&format->get_state());
tree.write();
}
FormatListChanged ();
return new_path;
}
void
ExportProfileManager::remove_format_profile (ExportFormatSpecPtr format)
{
for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
if (*it == format) {
format_list->erase (it);
break;
}
}
FileMap::iterator it = format_file_map.find (format->id());
if (it != format_file_map.end()) {
if (g_remove (it->second.c_str()) != 0) {
error << string_compose (_("Unable to remove export profile %1: %2"), it->second, g_strerror(errno)) << endmsg;
return;
}
format_file_map.erase (it);
}
FormatListChanged ();
}
ExportFormatSpecPtr
ExportProfileManager::get_new_format (ExportFormatSpecPtr original)
{
ExportFormatSpecPtr format;
if (original) {
format.reset (new ExportFormatSpecification (*original));
} else {
format = handler->add_format();
format->set_name (_("empty format"));
}
std::string path = save_format_to_disk (format);
FilePair pair (format->id(), path);
format_file_map.insert (pair);
format_list->push_back (format);
FormatListChanged ();
return format;
}
bool
ExportProfileManager::init_formats (XMLNodeList nodes)
{
formats.clear();
bool ok = true;
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
FormatStatePtr format = deserialize_format (**it);
if (format) {
formats.push_back (format);
} else { ok = false; }
}
if (formats.empty ()) {
FormatStatePtr format (new FormatState (format_list, ExportFormatSpecPtr ()));
formats.push_back (format);
return false;
}
return ok;
}
ExportProfileManager::FormatStatePtr
ExportProfileManager::deserialize_format (XMLNode & root)
{
XMLProperty * prop;
UUID id;
if ((prop = root.property ("id"))) {
id = prop->value();
}
for (FormatList::iterator it = format_list->begin(); it != format_list->end(); ++it) {
if ((*it)->id() == id) {
return FormatStatePtr (new FormatState (format_list, *it));
}
}
return FormatStatePtr ();
}
XMLNode &
ExportProfileManager::serialize_format (FormatStatePtr state)
{
XMLNode * root = new XMLNode ("ExportFormat");
string id = state->format ? state->format->id().to_s() : "";
root->add_property ("id", id);
return *root;
}
void
ExportProfileManager::load_formats ()
{
vector<std::string> found = find_file (string_compose ("*%1", export_format_suffix));
for (vector<std::string>::iterator it = found.begin(); it != found.end(); ++it) {
load_format_from_disk (*it);
}
}
void
ExportProfileManager::load_format_from_disk (std::string const & path)
{
XMLTree const tree (path);
ExportFormatSpecPtr format = handler->add_format (*tree.root());
/* Handle id to filename mapping and don't add duplicates to list */
FilePair pair (format->id(), path);
if (format_file_map.insert (pair).second) {
format_list->push_back (format);
}
FormatListChanged ();
}
ExportProfileManager::FilenameStatePtr
ExportProfileManager::duplicate_filename_state (FilenameStatePtr state)
{
FilenameStatePtr filename (new FilenameState (handler->add_filename_copy (state->filename)));
filenames.push_back (filename);
return filename;
}
void
ExportProfileManager::remove_filename_state (FilenameStatePtr state)
{
for (FilenameStateList::iterator it = filenames.begin(); it != filenames.end(); ++it) {
if (*it == state) {
filenames.erase (it);
return;
}
}
}
std::string
ExportProfileManager::get_sample_filename_for_format (ExportFilenamePtr filename, ExportFormatSpecPtr format)
{
assert (format);
if (channel_configs.empty()) { return ""; }
std::list<string> filenames;
build_filenames (filenames, filename, timespans.front()->timespans,
channel_configs.front()->config, format);
if (filenames.empty()) { return ""; }
return filenames.front();
}
bool
ExportProfileManager::init_filenames (XMLNodeList nodes)
{
filenames.clear ();
for (XMLNodeList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
ExportFilenamePtr filename = handler->add_filename();
filename->set_state (**it);
filenames.push_back (FilenameStatePtr (new FilenameState (filename)));
}
if (filenames.empty()) {
FilenameStatePtr filename (new FilenameState (handler->add_filename()));
filenames.push_back (filename);
return false;
}
return true;
}
boost::shared_ptr<ExportProfileManager::Warnings>
ExportProfileManager::get_warnings ()
{
boost::shared_ptr<Warnings> warnings (new Warnings ());
ChannelConfigStatePtr channel_config_state;
if (!channel_configs.empty ()) {
channel_config_state = channel_configs.front();
}
TimespanStatePtr timespan_state = timespans.front();
/*** Check "global" config ***/
TimespanListPtr timespans = timespan_state->timespans;
ExportChannelConfigPtr channel_config;
if (channel_config_state) {
channel_config = channel_config_state->config;
}
/* Check Timespans are not empty */
if (timespans->empty()) {
warnings->errors.push_back (_("No timespan has been selected!"));
}
if (channel_config_state == 0) {
warnings->errors.push_back (_("No channels have been selected!"));
} else {
/* Check channel config ports */
if (!channel_config->all_channels_have_ports ()) {
warnings->warnings.push_back (_("Some channels are empty"));
}
}
/*** Check files ***/
if (channel_config_state) {
FormatStateList::const_iterator format_it;
FilenameStateList::const_iterator filename_it;
for (format_it = formats.begin(), filename_it = filenames.begin();
format_it != formats.end() && filename_it != filenames.end();
++format_it, ++filename_it) {
check_config (warnings, timespan_state, channel_config_state, *format_it, *filename_it);
}
}
return warnings;
}
void
ExportProfileManager::check_config (boost::shared_ptr<Warnings> warnings,
TimespanStatePtr timespan_state,
ChannelConfigStatePtr channel_config_state,
FormatStatePtr format_state,
FilenameStatePtr filename_state)
{
TimespanListPtr timespans = timespan_state->timespans;
ExportChannelConfigPtr channel_config = channel_config_state->config;
ExportFormatSpecPtr format = format_state->format;
ExportFilenamePtr filename = filename_state->filename;
/* Check format and maximum channel count */
if (!format || !format->type()) {
warnings->errors.push_back (_("No format selected!"));
} else if (!channel_config->get_n_chans()) {
warnings->errors.push_back (_("All channels are empty!"));
} else if (!check_format (format, channel_config->get_n_chans())) {
warnings->errors.push_back (_("One or more of the selected formats is not compatible with this system!"));
} else if (format->channel_limit() < channel_config->get_n_chans()) {
warnings->errors.push_back
(string_compose (_("%1 supports only %2 channels, but you have %3 channels in your channel configuration"),
format->format_name(),
format->channel_limit(),
channel_config->get_n_chans()));
}
if (!warnings->errors.empty()) { return; }
/* Check filenames */
// filename->include_timespan = (timespans->size() > 1); Disabled for now...
std::list<string> paths;
build_filenames(paths, filename, timespans, channel_config, format);
for (std::list<string>::const_iterator path_it = paths.begin(); path_it != paths.end(); ++path_it) {
string path = *path_it;
if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
warnings->conflicting_filenames.push_back (path);
}
if (format->with_toc()) {
string marker_file = handler->get_cd_marker_filename(path, CDMarkerTOC);
if (Glib::file_test (marker_file, Glib::FILE_TEST_EXISTS)) {
warnings->conflicting_filenames.push_back (marker_file);
}
}
if (format->with_cue()) {
string marker_file = handler->get_cd_marker_filename(path, CDMarkerCUE);
if (Glib::file_test (marker_file, Glib::FILE_TEST_EXISTS)) {
warnings->conflicting_filenames.push_back (marker_file);
}
}
}
}
bool
ExportProfileManager::check_format (ExportFormatSpecPtr format, uint32_t channels)
{
switch (format->type()) {
case ExportFormatBase::T_Sndfile:
return check_sndfile_format (format, channels);
default:
throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!"));
}
}
bool
ExportProfileManager::check_sndfile_format (ExportFormatSpecPtr format, unsigned int channels)
{
SF_INFO sf_info;
sf_info.channels = channels;
sf_info.samplerate = format->sample_rate ();
sf_info.format = format->format_id () | format->sample_format ();
return (sf_format_check (&sf_info) == SF_TRUE ? true : false);
}
void
ExportProfileManager::build_filenames(std::list<std::string> & result, ExportFilenamePtr filename,
TimespanListPtr timespans, ExportChannelConfigPtr channel_config,
ExportFormatSpecPtr format)
{
for (std::list<ExportTimespanPtr>::iterator timespan_it = timespans->begin();
timespan_it != timespans->end(); ++timespan_it) {
filename->set_timespan (*timespan_it);
if (channel_config->get_split()) {
filename->include_channel = true;
for (uint32_t chan = 1; chan <= channel_config->get_n_chans(); ++chan) {
filename->set_channel (chan);
result.push_back(filename->get_path (format));
}
} else {
filename->include_channel = false;
result.push_back(filename->get_path (format));
}
}
}
}; // namespace ARDOUR