#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>
bool Session::_disable_all_loaded_plugins = false;
-Session::compute_peak_t Session::compute_peak = 0;
-Session::find_peaks_t Session::find_peaks = 0;
-Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0;
-Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0;
-Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0;
-
sigc::signal<void,std::string> Session::Dialog;
sigc::signal<int> Session::AskAboutPendingState;
sigc::signal<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
sigc::signal<void> Session::SMPTEOffsetChanged;
sigc::signal<void> Session::StartTimeChanged;
sigc::signal<void> Session::EndTimeChanged;
-
sigc::signal<void> Session::AutoBindingOn;
sigc::signal<void> Session::AutoBindingOff;
+sigc::signal<void, std::string, std::string> Session::Exported;
Session::Session (AudioEngine &eng,
const string& fullpath,
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
_mix_buffers(new BufferSet()),
+ mmc (0),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
_midi_clock_port (default_midi_clock_port),
_session_dir (new SessionDirectory(fullpath)),
pending_events (2048),
+ state_tree (0),
+ butler_mixdown_buffer (0),
+ butler_gain_buffer (0),
post_transport_work((PostTransportWork)0),
_send_smpte_update (false),
- midi_requests (128),
+ midi_thread (pthread_t (0)),
+ midi_requests (128), // the size of this should match the midi request pool size
diskstreams (new DiskstreamList),
routes (new RouteList),
auditioner ((Auditioner*) 0),
+ _total_free_4k_blocks (0),
_bundle_xml_node (0),
_click_io ((IO*) 0),
- main_outs (0)
+ click_data (0),
+ click_emphasis_data (0),
+ main_outs (0),
+ _metadata (new SessionMetadata())
+
{
bool new_session;
cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
- n_physical_outputs = _engine.n_physical_outputs();
- n_physical_inputs = _engine.n_physical_inputs();
+ n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO);
+ n_physical_inputs = _engine.n_physical_inputs(DataType::AUDIO);
first_stage_init (fullpath, snapshot_name);
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
_mix_buffers(new BufferSet()),
+ mmc (0),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
_midi_clock_port (default_midi_clock_port),
_session_dir ( new SessionDirectory(fullpath)),
pending_events (2048),
+ state_tree (0),
+ butler_mixdown_buffer (0),
+ butler_gain_buffer (0),
post_transport_work((PostTransportWork)0),
_send_smpte_update (false),
+ midi_thread (pthread_t (0)),
midi_requests (16),
diskstreams (new DiskstreamList),
routes (new RouteList),
+ auditioner ((Auditioner *) 0),
+ _total_free_4k_blocks (0),
_bundle_xml_node (0),
- main_outs (0)
-
+ _click_io ((IO *) 0),
+ click_data (0),
+ click_emphasis_data (0),
+ main_outs (0),
+ _metadata (new SessionMetadata())
{
bool new_session;
cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
- n_physical_outputs = _engine.n_physical_outputs();
- n_physical_inputs = _engine.n_physical_inputs();
+ n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO);
+ n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO);
if (n_physical_inputs) {
n_physical_inputs = max (requested_physical_in, n_physical_inputs);
/* clear state tree so that no references to objects are held any more */
- if (state_tree) {
- delete state_tree;
- }
+ delete state_tree;
terminate_butler_thread ();
//terminate_midi_thread ();
- if (click_data && click_data != default_click) {
+ if (click_data != default_click) {
delete [] click_data;
}
- if (click_emphasis_data && click_emphasis_data != default_click_emphasis) {
+ if (click_emphasis_data != default_click_emphasis) {
delete [] click_emphasis_data;
}
i = tmp;
}
- if (butler_mixdown_buffer) {
- delete [] butler_mixdown_buffer;
- }
-
- if (butler_gain_buffer) {
- delete [] butler_gain_buffer;
- }
+ delete [] butler_mixdown_buffer;
+ delete [] butler_gain_buffer;
Crossfade::set_buffer_size (0);
- if (mmc) {
- delete mmc;
- }
+ delete mmc;
}
void
set_loop = true;
}
+ if (location->is_start()) {
+ start_location = location;
+ }
+ if (location->is_end()) {
+ end_location = location;
+ }
}
if (!set_loop) {
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
} else {
tf = _transport_frame;
}
-
- if (_transport_speed == 0) {
- return tf;
- }
-
- if (tf < offset) {
- return 0;
- }
-
+
ret = tf;
if (!non_realtime_work_pending()) {
/* MOVING */
- /* take latency into account */
+ /* 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
+ 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;
+
+ }
+ }
+
+
+ /* forwards */
+ ret -= offset;
- ret -= offset;
+ } else if (_transport_speed < 0.0f) {
+
+ /* XXX wot? no backward looping? */
+
+ if (tf > last_stop_frame - offset) {
+ return last_stop_frame;
+ } else {
+ /* backwards */
+ ret += offset;
+ }
+ }
}
return ret;
ensure_buffers(_scratch_buffers->available());
- if (_gain_automation_buffer) {
- delete [] _gain_automation_buffer;
- }
+ delete [] _gain_automation_buffer;
_gain_automation_buffer = new gain_t[nframes];
allocate_pan_automation_buffers (nframes, _npan_buffers, true);
}
}
- /*
vector<string> physinputs;
vector<string> physoutputs;
- uint32_t nphysical_in;
- uint32_t nphysical_out;
- _engine.get_physical_outputs (physoutputs);
- _engine.get_physical_inputs (physinputs);
- control_id = ntracks() + nbusses() + 1;
- */
+ _engine.get_physical_outputs (DataType::MIDI, physoutputs);
+ _engine.get_physical_inputs (DataType::MIDI, physinputs);
+
+ // control_id = ntracks() + nbusses();
while (how_many) {
} while (track_id < (UINT_MAX-1));
- /*
- if (Config->get_input_auto_connect() & AutoConnectPhysical) {
- nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
- } else {
- nphysical_in = 0;
- }
-
- if (Config->get_output_auto_connect() & AutoConnectPhysical) {
- nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
- } else {
- nphysical_out = 0;
- }
- */
-
shared_ptr<MidiTrack> track;
try {
vector<string> physinputs;
vector<string> physoutputs;
- uint32_t nphysical_in;
- uint32_t nphysical_out;
- _engine.get_physical_outputs (physoutputs);
- _engine.get_physical_inputs (physinputs);
+ _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
+ _engine.get_physical_inputs (DataType::AUDIO, physinputs);
+
control_id = ntracks() + nbusses() + 1;
while (how_many) {
} while (track_id < (UINT_MAX-1));
- if (Config->get_input_auto_connect() & AutoConnectPhysical) {
- nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
- } else {
- nphysical_in = 0;
- }
-
- if (Config->get_output_auto_connect() & AutoConnectPhysical) {
- nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
- } else {
- nphysical_out = 0;
- }
-
shared_ptr<AudioTrack> track;
try {
goto failed;
}
- if (nphysical_in) {
+ if (!physinputs.empty()) {
+ uint32_t nphysical_in = physinputs.size();
+
for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) {
port = "";
}
}
- for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+ if (!physoutputs.empty()) {
+ uint32_t nphysical_out = physoutputs.size();
- port = "";
-
- if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
- port = physoutputs[(channels_used+x)%nphysical_out];
- } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
- if (_master_out) {
- port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
+ for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) {
+
+ port = "";
+
+ if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+ port = physoutputs[(channels_used+x)%nphysical_out];
+ } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ if (_master_out) {
+ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
+ }
+ }
+
+ if (port.length() && track->connect_output (track->output (x), port, this)) {
+ break;
}
- }
-
- if (port.length() && track->connect_output (track->output (x), port, this)) {
- break;
}
}
char bus_name[32];
uint32_t bus_id = 1;
uint32_t n = 0;
+ uint32_t channels_used = 0;
string port;
RouteList ret;
uint32_t control_id;
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
+ if (boost::dynamic_pointer_cast<Track>(*i) == 0) {
+ /* its a bus ? */
if (!(*i)->is_hidden() && (*i)->name() != _("master")) {
bus_id++;
+ n++;
+ channels_used += (*i)->n_inputs().n_audio();
}
}
}
vector<string> physinputs;
vector<string> physoutputs;
- _engine.get_physical_outputs (physoutputs);
- _engine.get_physical_inputs (physinputs);
+ _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
+ _engine.get_physical_inputs (DataType::AUDIO, physinputs);
+
+ n_physical_audio_outputs = physoutputs.size();
+ n_physical_audio_inputs = physinputs.size();
+
control_id = ntracks() + nbusses() + 1;
while (how_many) {
goto failure;
}
- for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().n_audio(); ++x) {
- port = "";
+ /*
+ for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) {
+
+ port = "";
+
if (Config->get_input_auto_connect() & AutoConnectPhysical) {
- port = physinputs[((n+x)%n_physical_inputs)];
- }
-
+ port = physinputs[((n+x)%n_physical_audio_inputs)];
+ }
+
if (port.length() && bus->connect_input (bus->input (x), port, this)) {
break;
}
}
+ */
- for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().n_audio(); ++x) {
-
+ for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) {
port = "";
if (Config->get_output_auto_connect() & AutoConnectPhysical) {
}
}
+ channels_used += bus->n_inputs ().n_audio();
+
bus->set_remote_control_id (control_id);
++control_id;
_control_out = (*x);
}
- add_bundle ((*x)->bundle_for_inputs());
- add_bundle ((*x)->bundle_for_outputs());
+ /* only busses get automatic bundles formed */
+
+ if (!boost::dynamic_pointer_cast<Track> (*x)) {
+ add_bundle ((*x)->bundle_for_inputs());
+ add_bundle ((*x)->bundle_for_outputs());
+ }
}
if (_control_out && IO::connecting_legal) {
route->drop_references ();
+ sync_order_keys (N_("session"));
+
/* save the new state of the world */
if (save_state (_current_snapshot_name)) {
bool is_track = false;
bool signal = false;
- /* caller must hold RouteLock */
-
/* this is where we actually implement solo by changing
the solo mute setting of each track.
*/
has.
*/
update_route_solo_state();
-}
+}
+
+void
+Session::catch_up_on_solo_mute_override ()
+{
+ if (Config->get_solo_model() != InverseMute) {
+ return;
+ }
+
+ /* this is called whenever the param solo-mute-override is
+ changed.
+ */
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->catch_up_on_solo_mute_override ();
+ }
+}
shared_ptr<Route>
Session::route_by_name (string name)
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->destructive()) //ignore tape tracks when getting max extents
+ continue;
boost::shared_ptr<Playlist> pl = (*i)->playlist();
if ((me = pl->get_maximum_extent()) > max) {
max = me;
}
int
-Session::region_name (string& result, string base, bool newlevel) const
+Session::region_name (string& result, string base, bool newlevel)
{
char buf[16];
string subbase;
Glib::Mutex::Lock lm (region_lock);
snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1);
-
-
result = "region.";
result += buf;
} else {
- /* XXX this is going to be slow. optimize me later */
-
if (newlevel) {
subbase = base;
} else {
}
- bool name_taken = true;
-
{
Glib::Mutex::Lock lm (region_lock);
- for (int n = 1; n < 5000; ++n) {
-
- result = subbase;
- snprintf (buf, sizeof (buf), ".%d", n);
- result += buf;
+ map<string,uint32_t>::iterator x;
- name_taken = false;
+ result = subbase;
- for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
- if (i->second->name() == result) {
- name_taken = true;
- break;
- }
- }
+ if ((x = region_name_map.find (subbase)) == region_name_map.end()) {
+ result += ".1";
+ region_name_map[subbase] = 1;
+ } else {
+ x->second++;
+ snprintf (buf, sizeof (buf), ".%d", x->second);
- if (!name_taken) {
- break;
- }
+ result += buf;
}
}
-
- if (name_taken) {
- fatal << string_compose(_("too many regions with names like %1"), base) << endmsg;
- /*NOTREACHED*/
- }
}
+
return 0;
}
add the region to the region list.
*/
- set_dirty();
+ set_dirty ();
if (added) {
region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region)));
region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region)));
+
+ update_region_name_map (region);
}
if (!v.empty()) {
}
}
+void
+Session::update_region_name_map (boost::shared_ptr<Region> region)
+{
+ string::size_type last_period = region->name().find_last_of ('.');
+
+ if (last_period != string::npos && last_period < region->name().length() - 1) {
+
+ string base = region->name().substr (0, last_period);
+ string number = region->name().substr (last_period+1);
+ map<string,uint32_t>::iterator x;
+
+ /* note that if there is no number, we get zero from atoi,
+ which is just fine
+ */
+
+ region_name_map[base] = atoi (number);
+ }
+}
+
void
Session::region_changed (Change what_changed, boost::weak_ptr<Region> weak_region)
{
/* relay hidden changes */
RegionHiddenChange (region);
}
+
+ if (what_changed & NameChanged) {
+ update_region_name_map (region);
+ }
}
void
}
void
-Session::add_playlist (boost::shared_ptr<Playlist> playlist)
+Session::unassigned_playlists (std::list<boost::shared_ptr<Playlist> > & list)
+{
+ Glib::Mutex::Lock lm (playlist_lock);
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
+ list.push_back (*i);
+ }
+ }
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
+ list.push_back (*i);
+ }
+ }
+}
+
+void
+Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
{
if (playlist->hidden()) {
return;
}
}
+ if (unused) {
+ playlist->release();
+ }
+
set_dirty();
PlaylistAdded (playlist); /* EMIT SIGNAL */
return 0;
}
-int
-Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len,
- bool overwrite, vector<boost::shared_ptr<Source> >& srcs, InterThreadInfo& itt)
+boost::shared_ptr<Region>
+Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
+ bool overwrite, vector<boost::shared_ptr<Source> >& srcs, InterThreadInfo& itt)
{
- int ret = -1;
+ boost::shared_ptr<Region> result;
boost::shared_ptr<Playlist> playlist;
boost::shared_ptr<AudioFileSource> fsource;
uint32_t x;
BufferSet buffers;
SessionDirectory sdir(get_best_session_directory_for_new_source ());
const string sound_dir = sdir.sound_path().to_string();
+ nframes_t len = end - start;
+
+ if (end <= start) {
+ error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
+ end, start) << endmsg;
+ return result;
+ }
// any bigger than this seems to cause stack overflows in called functions
const nframes_t chunk_size = (128 * 1024)/4;
/* construct a region to represent the bounced material */
- boost::shared_ptr<Region> aregion = RegionFactory::create (srcs, 0, srcs.front()->length(),
- region_name_from_path (srcs.front()->name(), true));
-
- ret = 0;
+ result = RegionFactory::create (srcs, 0, srcs.front()->length(),
+ region_name_from_path (srcs.front()->name(), true));
}
out:
- if (ret) {
+ if (!result) {
for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
if (afs) {
afs->mark_for_remove ();
}
-
+
(*src)->drop_references ();
}
g_atomic_int_set (&processing_prohibited, 0);
- return ret;
+ return result;
}
BufferSet&
BufferSet&
Session::get_scratch_buffers (ChanCount count)
{
- assert(_scratch_buffers->available() >= count);
- _scratch_buffers->set_count(count);
+ if (count != ChanCount::ZERO) {
+ assert(_scratch_buffers->available() >= count);
+ _scratch_buffers->set_count(count);
+ } else {
+ _scratch_buffers->set_count (_scratch_buffers->available());
+ }
+
return *_scratch_buffers;
}
}
void
-Session::sync_order_keys ()
+Session::sync_order_keys (const char* base)
{
if (!Config->get_sync_all_route_ordering()) {
/* leave order keys as they are */
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->sync_order_keys ();
+ (*i)->sync_order_keys (base);
}
- Route::SyncOrderKeys (); // EMIT SIGNAL
+ Route::SyncOrderKeys (base); // EMIT SIGNAL
}
void