#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
-#include <pbd/error.h>
+#include "pbd/error.h"
#include <glibmm/thread.h>
-#include <pbd/pathscanner.h>
-#include <pbd/stl_delete.h>
-#include <pbd/basename.h>
-#include <pbd/stacktrace.h>
-#include <pbd/file_utils.h>
-
-#include <ardour/audioengine.h>
-#include <ardour/configuration.h>
-#include <ardour/session.h>
-#include <ardour/session_directory.h>
-#include <ardour/session_metadata.h>
-#include <ardour/utils.h>
-#include <ardour/audio_diskstream.h>
-#include <ardour/audioplaylist.h>
-#include <ardour/audioregion.h>
-#include <ardour/audiofilesource.h>
-#include <ardour/midi_diskstream.h>
-#include <ardour/midi_playlist.h>
-#include <ardour/midi_region.h>
-#include <ardour/smf_source.h>
-#include <ardour/auditioner.h>
-#include <ardour/recent_sessions.h>
-#include <ardour/io_processor.h>
-#include <ardour/send.h>
-#include <ardour/processor.h>
-#include <ardour/plugin_insert.h>
-#include <ardour/port_insert.h>
-#include <ardour/slave.h>
-#include <ardour/tempo.h>
-#include <ardour/audio_track.h>
-#include <ardour/midi_track.h>
-#include <ardour/cycle_timer.h>
-#include <ardour/named_selection.h>
-#include <ardour/crossfade.h>
-#include <ardour/playlist.h>
-#include <ardour/click.h>
-#include <ardour/data_type.h>
-#include <ardour/buffer_set.h>
-#include <ardour/source_factory.h>
-#include <ardour/region_factory.h>
-#include <ardour/filename_extensions.h>
-#include <ardour/session_directory.h>
-#include <ardour/tape_file_matcher.h>
-#include <ardour/analyser.h>
-#include <ardour/audio_buffer.h>
-#include <ardour/bundle.h>
+#include "pbd/pathscanner.h"
+#include "pbd/stl_delete.h"
+#include "pbd/basename.h"
+#include "pbd/stacktrace.h"
+#include "pbd/file_utils.h"
+
+#include "ardour/analyser.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/audio_diskstream.h"
+#include "ardour/audio_track.h"
+#include "ardour/audioengine.h"
+#include "ardour/audiofilesource.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/auditioner.h"
+#include "ardour/buffer_set.h"
+#include "ardour/bundle.h"
+#include "ardour/click.h"
+#include "ardour/configuration.h"
+#include "ardour/crossfade.h"
+#include "ardour/cycle_timer.h"
+#include "ardour/data_type.h"
+#include "ardour/filename_extensions.h"
+#include "ardour/io_processor.h"
+#include "ardour/midi_diskstream.h"
+#include "ardour/midi_playlist.h"
+#include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
+#include "ardour/named_selection.h"
+#include "ardour/playlist.h"
+#include "ardour/plugin_insert.h"
+#include "ardour/port_insert.h"
+#include "ardour/processor.h"
+#include "ardour/recent_sessions.h"
+#include "ardour/region_factory.h"
+#include "ardour/route_group.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
+#include "ardour/session_directory.h"
+#include "ardour/session_directory.h"
+#include "ardour/session_metadata.h"
+#include "ardour/slave.h"
+#include "ardour/smf_source.h"
+#include "ardour/source_factory.h"
+#include "ardour/tape_file_matcher.h"
+#include "ardour/tempo.h"
+#include "ardour/utils.h"
#include "i18n.h"
string mix_template)
: _engine (eng),
+ _requested_return_frame (-1),
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
_mix_buffers(new BufferSet()),
routes (new RouteList),
auditioner ((Auditioner*) 0),
_total_free_4k_blocks (0),
+ _bundles (new BundleList),
_bundle_xml_node (0),
_click_io ((IO*) 0),
click_data (0),
nframes_t initial_length)
: _engine (eng),
+ _requested_return_frame (-1),
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
_mix_buffers(new BufferSet()),
routes (new RouteList),
auditioner ((Auditioner *) 0),
_total_free_4k_blocks (0),
+ _bundles (new BundleList),
_bundle_xml_node (0),
_click_io ((IO *) 0),
click_data (0),
BootMessage (_("Set up standard connections"));
/* Create a set of Bundle objects that map
- to the physical I/O currently available */
+ to the physical I/O currently available. We create both
+ mono and stereo bundles, so that the common cases of mono
+ and stereo tracks get bundles to put in their mixer strip
+ in / out menus. There may be a nicer way of achieving that;
+ it doesn't really scale that well to higher channel counts */
for (uint32_t np = 0; np < n_physical_outputs; ++np) {
char buf[32];
add_bundle (c);
}
+ for (uint32_t np = 0; np < n_physical_outputs; np += 2) {
+ if (np + 1 < n_physical_outputs) {
+ char buf[32];
+ snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
+ shared_ptr<Bundle> c (new Bundle (buf, true));
+ c->add_channel (_("L"));
+ c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ c->add_channel (_("R"));
+ c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
+
+ add_bundle (c);
+ }
+ }
+
for (uint32_t np = 0; np < n_physical_inputs; ++np) {
char buf[32];
snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
add_bundle (c);
}
+ for (uint32_t np = 0; np < n_physical_inputs; np += 2) {
+ if (np + 1 < n_physical_inputs) {
+ char buf[32];
+ snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2);
+
+ shared_ptr<Bundle> c (new Bundle (buf, false));
+ c->add_channel (_("L"));
+ c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ c->add_channel (_("R"));
+ c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
+
+ add_bundle (c);
+ }
+ }
+
if (_master_out) {
/* create master/control ports */
nframes_t offset;
nframes_t tf;
- if (_transport_speed == 0.0f && non_realtime_work_pending()) {
- return last_stop_frame;
- }
-
/* the first of these two possible settings for "offset"
mean that the audible frame is stationary until
audio emerges from the latency compensation
/* MOVING */
/* check to see if we have passed the first guaranteed
- audible frame past our last stopping position. if not,
- the return that last stopping point because in terms
+ audible frame past our last start position. if not,
+ return that last start point because in terms
of audible frames, we have not moved yet.
*/
if (_transport_speed > 0.0f) {
if (!play_loop || !have_looped) {
- if (tf < last_stop_frame + offset) {
- return last_stop_frame;
-
+ if (tf < _last_roll_location + offset) {
+ return _last_roll_location;
}
}
/* XXX wot? no backward looping? */
- if (tf > last_stop_frame - offset) {
- return last_stop_frame;
+ if (tf > _last_roll_location - offset) {
+ return _last_roll_location;
} else {
/* backwards */
ret += offset;
}
-Session::RouteList
+RouteList
Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many)
{
char bus_name[32];
char buf[16];
string subbase;
- assert(base.find("/") == string::npos);
+ if (base.find("/") != string::npos) {
+ base = base.substr(base.find_last_of("/") + 1);
+ }
if (base == "") {
}
/* Source Management */
+
void
Session::add_source (boost::shared_ptr<Source> source)
{
return source;
}
-
boost::shared_ptr<Source>
Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
{
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
cerr << "comparing " << path << " with " << i->second->name() << endl;
- boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
+ boost::shared_ptr<AudioFileSource> afs
+ = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
if (afs && afs->path() == path && chn == afs->channel()) {
return afs;
}
-
}
return boost::shared_ptr<Source>();
}
-Glib::ustring
-Session::peak_path (Glib::ustring base) const
-{
- sys::path peakfile_path(_session_dir->peak_path());
- peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
- return peakfile_path.to_string();
-}
string
-Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive)
+Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive)
{
string look_for;
string old_basename = PBD::basename_nosuffix (oldname);
/* non-destructive file sources have a name of the form:
- /path/to/NAME-nnnnn(%[LR])?.wav
+ /path/to/NAME-nnnnn(%[LR])?.ext
the task here is to replace NAME with the new name.
*/
if (postfix != string::npos) {
suffix = suffix.substr (postfix);
} else {
- error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl;
+ error << "Logic error in Session::change_source_path_by_name(), please report" << endl;
return "";
}
return path;
}
+/** Return the full path (in some session directory) for a new embedded source.
+ * \a name must be a session-unique name that does not contain slashes
+ * (e.g. as returned by new_*_source_name)
+ */
string
-Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive)
+Session::new_source_path_from_name (DataType type, const string& name)
+{
+ assert(name.find("/") == string::npos);
+
+ SessionDirectory sdir(get_best_session_directory_for_new_source());
+
+ sys::path p;
+ if (type == DataType::AUDIO) {
+ p = sdir.sound_path();
+ } else if (type == DataType::MIDI) {
+ p = sdir.midi_path();
+ } else {
+ error << "Unknown source type, unable to create file path" << endmsg;
+ return "";
+ }
+
+ p /= name;
+ return p.to_string();
+}
+
+Glib::ustring
+Session::peak_path (Glib::ustring base) const
+{
+ sys::path peakfile_path(_session_dir->peak_path());
+ peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
+ return peakfile_path.to_string();
+}
+
+/** Return a unique name based on \a base for a new internal audio source */
+string
+Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
{
string spath;
uint32_t cnt;
string legalized;
buf[0] = '\0';
- legalized = legalize_for_path (name);
-
- /* find a "version" of the file name that doesn't exist in
- any of the possible directories.
- */
+ legalized = legalize_for_path (base);
+ // Find a "version" of the base name that doesn't exist in any of the possible directories.
for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
vector<space_and_path>::iterator i;
spath = sdir.sound_path().to_string();
if (destructive) {
+
if (nchan < 2) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
+ spath.c_str(), cnt, legalized.c_str());
} else if (nchan == 2) {
if (chan == 0) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav",
+ spath.c_str(), cnt, legalized.c_str());
} else {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav",
+ spath.c_str(), cnt, legalized.c_str());
}
} else if (nchan < 26) {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav",
+ spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
} else {
- snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
+ spath.c_str(), cnt, legalized.c_str());
}
} else {
}
if (cnt > limit) {
- error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
+ error << string_compose(
+ _("There are already %1 recordings for %2, which I consider too many."),
+ limit, base) << endmsg;
destroy ();
throw failed_constructor();
}
}
- /* we now have a unique name for the file, but figure out where to
- actually put it.
- */
-
- string foo = buf;
-
- SessionDirectory sdir(get_best_session_directory_for_new_source ());
-
- spath = sdir.sound_path().to_string();
- spath += '/';
-
- string::size_type pos = foo.find_last_of ('/');
-
- if (pos == string::npos) {
- spath += foo;
- } else {
- spath += foo.substr (pos + 1);
- }
-
- return spath;
+ return Glib::path_get_basename(buf);
}
+/** Create a new embedded audio source */
boost::shared_ptr<AudioFileSource>
Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
{
- string spath = audio_path_from_name (ds.name(), ds.n_channels().n_audio(), chan, destructive);
+ const size_t n_chans = ds.n_channels().n_audio();
+ const string name = new_audio_source_name (ds.name(), n_chans, chan, destructive);
+ const string path = new_source_path_from_name(DataType::AUDIO, name);
return boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate()));
+ SourceFactory::createWritable (
+ DataType::AUDIO, *this, path, true, destructive, frame_rate()));
}
-// FIXME: _terrible_ code duplication
+/** Return a unique name based on \a base for a new internal MIDI source */
string
-Session::change_midi_path_by_name (string path, string oldname, string newname, bool destructive)
+Session::new_midi_source_name (const string& base)
{
- string look_for;
- string old_basename = PBD::basename_nosuffix (oldname);
- string new_legalized = legalize_for_path (newname);
-
- /* note: we know (or assume) the old path is already valid */
-
- if (destructive) {
-
- /* destructive file sources have a name of the form:
-
- /path/to/Tnnnn-NAME(%[LR])?.wav
-
- the task here is to replace NAME with the new name.
- */
-
- /* find last slash */
-
- string dir;
- string prefix;
- string::size_type slash;
- string::size_type dash;
-
- if ((slash = path.find_last_of ('/')) == string::npos) {
- return "";
- }
-
- dir = path.substr (0, slash+1);
-
- /* '-' is not a legal character for the NAME part of the path */
-
- if ((dash = path.find_last_of ('-')) == string::npos) {
- return "";
- }
-
- prefix = path.substr (slash+1, dash-(slash+1));
-
- path = dir;
- path += prefix;
- path += '-';
- path += new_legalized;
- path += ".mid"; /* XXX gag me with a spoon */
-
- } else {
-
- /* non-destructive file sources have a name of the form:
-
- /path/to/NAME-nnnnn(%[LR])?.wav
-
- the task here is to replace NAME with the new name.
- */
-
- string dir;
- string suffix;
- string::size_type slash;
- string::size_type dash;
- string::size_type postfix;
-
- /* find last slash */
-
- if ((slash = path.find_last_of ('/')) == string::npos) {
- return "";
- }
-
- dir = path.substr (0, slash+1);
-
- /* '-' is not a legal character for the NAME part of the path */
-
- if ((dash = path.find_last_of ('-')) == string::npos) {
- return "";
- }
-
- suffix = path.substr (dash+1);
-
- // Suffix is now everything after the dash. Now we need to eliminate
- // the nnnnn part, which is done by either finding a '%' or a '.'
-
- postfix = suffix.find_last_of ("%");
- if (postfix == string::npos) {
- postfix = suffix.find_last_of ('.');
- }
-
- if (postfix != string::npos) {
- suffix = suffix.substr (postfix);
- } else {
- error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl;
- return "";
- }
-
- const uint32_t limit = 10000;
- char buf[PATH_MAX+1];
-
- for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
-
- snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
-
- if (access (buf, F_OK) != 0) {
- path = buf;
- break;
- }
- path = "";
- }
-
- if (path == "") {
- error << "FATAL ERROR! Could not find a " << endl;
- }
-
- }
-
- return path;
-}
-
-string
-Session::midi_path_from_name (string name)
-{
- string spath;
uint32_t cnt;
char buf[PATH_MAX+1];
const uint32_t limit = 10000;
string legalized;
buf[0] = '\0';
- legalized = legalize_for_path (name);
-
- /* find a "version" of the file name that doesn't exist in
- any of the possible directories.
- */
+ legalized = legalize_for_path (base);
+ // Find a "version" of the file name that doesn't exist in any of the possible directories.
for (cnt = 1; cnt <= limit; ++cnt) {
vector<space_and_path>::iterator i;
SessionDirectory sdir((*i).path);
sys::path p = sdir.midi_path();
-
p /= legalized;
- spath = p.to_string();
-
- snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt);
+ snprintf (buf, sizeof(buf), "%s-%u.mid", p.to_string().c_str(), cnt);
if (sys::exists (buf)) {
existing++;
}
if (cnt > limit) {
- error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
+ error << string_compose(
+ _("There are already %1 recordings for %2, which I consider too many."),
+ limit, base) << endmsg;
+ destroy ();
throw failed_constructor();
}
}
- /* we now have a unique name for the file, but figure out where to
- actually put it.
- */
-
- string foo = buf;
-
- SessionDirectory sdir(get_best_session_directory_for_new_source ());
-
- spath = sdir.midi_path().to_string();
- spath += '/';
-
- string::size_type pos = foo.find_last_of ('/');
-
- if (pos == string::npos) {
- spath += foo;
- } else {
- spath += foo.substr (pos + 1);
- }
-
- return spath;
+ return Glib::path_get_basename(buf);
}
+
+/** Create a new embedded MIDI source */
boost::shared_ptr<MidiSource>
Session::create_midi_source_for_session (MidiDiskstream& ds)
{
- string mpath = midi_path_from_name (ds.name());
+ const string name = new_midi_source_name (ds.name());
+ const string path = new_source_path_from_name (DataType::MIDI, name);
- return boost::dynamic_pointer_cast<SMFSource> (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate()));
+ return boost::dynamic_pointer_cast<SMFSource> (
+ SourceFactory::createWritable (
+ DataType::MIDI, *this, path, true, false, frame_rate()));
}
Session::add_bundle (shared_ptr<Bundle> bundle)
{
{
- Glib::Mutex::Lock guard (bundle_lock);
- _bundles.push_back (bundle);
+ RCUWriter<BundleList> writer (_bundles);
+ boost::shared_ptr<BundleList> b = writer.get_copy ();
+ b->push_back (bundle);
}
BundleAdded (bundle); /* EMIT SIGNAL */
bool removed = false;
{
- Glib::Mutex::Lock guard (bundle_lock);
- BundleList::iterator i = find (_bundles.begin(), _bundles.end(), bundle);
+ RCUWriter<BundleList> writer (_bundles);
+ boost::shared_ptr<BundleList> b = writer.get_copy ();
+ BundleList::iterator i = find (b->begin(), b->end(), bundle);
- if (i != _bundles.end()) {
- _bundles.erase (i);
+ if (i != b->end()) {
+ b->erase (i);
removed = true;
}
}
shared_ptr<Bundle>
Session::bundle_by_name (string name) const
{
- Glib::Mutex::Lock lm (bundle_lock);
-
- for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
+ boost::shared_ptr<BundleList> b = _bundles.reader ();
+
+ for (BundleList::const_iterator i = b->begin(); i != b->end(); ++i) {
if ((*i)->name() == name) {
return* i;
}
try {
fsource = boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+ SourceFactory::createWritable (DataType::AUDIO, *this, buf, true, false, frame_rate()));
}
catch (failed_constructor& err) {
/* construct a region to represent the bounced material */
- result = RegionFactory::create (srcs, 0, srcs.front()->length(),
- region_name_from_path (srcs.front()->name(), true));
+ result = RegionFactory::create (srcs, 0,
+ srcs.front()->length(srcs.front()->timeline_position()),
+ region_name_from_path (srcs.front()->name(), true));
}
out:
Route::SyncOrderKeys (base); // EMIT SIGNAL
}
-void
-Session::foreach_bundle (sigc::slot<void, boost::shared_ptr<Bundle> > sl)
-{
- Glib::Mutex::Lock lm (bundle_lock);
- for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
- sl (*i);
- }
-}