/*
- Copyright (C) 1999-2010 Paul Davis
-
- 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.
-
-*/
+ * Copyright (C) 1999-2019 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2006-2007 Jesse Chappell <jesse@essej.net>
+ * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
+ * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2006-2016 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2015 GZharun <grygoriiz@wavesglobal.com>
+ * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
#include <stdint.h>
if (np + 1 < outputs[DataType::AUDIO].size()) {
char buf[32];
snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
- boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+ boost::shared_ptr<Bundle> c (new Bundle (buf, true));
c->add_channel (_("L"), DataType::AUDIO);
c->set_port (0, outputs[DataType::AUDIO][np]);
c->add_channel (_("R"), DataType::AUDIO);
bool
Session::record_enabling_legal () const
{
- /* this used to be in here, but survey says.... we don't need to restrict it */
- // if (record_status() == Recording) {
- // return false;
- // }
-
if (Config->get_all_safe()) {
return false;
}
Locations::LocationList skips;
- if (consolidate) {
- PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
- consolidate_skips (loc);
- }
+ if (consolidate) {
+ PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
+ consolidate_skips (loc);
+ }
sync_locations_to_skips ();
void
Session::consolidate_skips (Location* loc)
{
- Locations::LocationList all_locations = _locations->list ();
+ Locations::LocationList all_locations = _locations->list ();
- for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
+ for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
- if (!(*l)->is_skip ()) {
- ++l;
- continue;
- }
+ if (!(*l)->is_skip ()) {
+ ++l;
+ continue;
+ }
- /* don't test against self */
+ /* don't test against self */
- if (*l == loc) {
- ++l;
- continue;
- }
+ if (*l == loc) {
+ ++l;
+ continue;
+ }
- switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
- case Evoral::OverlapInternal:
- case Evoral::OverlapExternal:
- case Evoral::OverlapStart:
- case Evoral::OverlapEnd:
- /* adjust new location to cover existing one */
- loc->set_start (min (loc->start(), (*l)->start()));
- loc->set_end (max (loc->end(), (*l)->end()));
- /* we don't need this one any more */
- _locations->remove (*l);
- /* the location has been deleted, so remove reference to it in our local list */
- l = all_locations.erase (l);
- break;
+ switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
+ case Evoral::OverlapInternal:
+ case Evoral::OverlapExternal:
+ case Evoral::OverlapStart:
+ case Evoral::OverlapEnd:
+ /* adjust new location to cover existing one */
+ loc->set_start (min (loc->start(), (*l)->start()));
+ loc->set_end (max (loc->end(), (*l)->end()));
+ /* we don't need this one any more */
+ _locations->remove (*l);
+ /* the location has been deleted, so remove reference to it in our local list */
+ l = all_locations.erase (l);
+ break;
- case Evoral::OverlapNone:
- ++l;
- break;
- }
- }
+ case Evoral::OverlapNone:
+ ++l;
+ break;
+ }
+ }
}
void
void
Session::location_added (Location *location)
{
- if (location->is_auto_punch()) {
- set_auto_punch_location (location);
- }
+ if (location->is_auto_punch()) {
+ set_auto_punch_location (location);
+ }
- if (location->is_auto_loop()) {
- set_auto_loop_location (location);
- }
+ if (location->is_auto_loop()) {
+ set_auto_loop_location (location);
+ }
- if (location->is_session_range()) {
- /* no need for any signal handling or event setting with the session range,
- because we keep a direct reference to it and use its start/end directly.
- */
- _session_range_location = location;
- }
+ if (location->is_session_range()) {
+ /* no need for any signal handling or event setting with the session range,
+ because we keep a direct reference to it and use its start/end directly.
+ */
+ _session_range_location = location;
+ }
- if (location->is_mark()) {
- /* listen for per-location signals that require us to do any * global updates for marks */
+ if (location->is_mark()) {
+ /* listen for per-location signals that require us to do any * global updates for marks */
- location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- }
+ }
if (location->is_range_marker()) {
- /* listen for per-location signals that require us to do any * global updates for marks */
+ /* listen for per-location signals that require us to do any * global updates for marks */
- location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+ location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- }
+ }
- if (location->is_skip()) {
- /* listen for per-location signals that require us to update skip-locate events */
+ if (location->is_skip()) {
+ /* listen for per-location signals that require us to update skip-locate events */
- location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
- location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
- location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
- location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
+ location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+ location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+ location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+ location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
- update_skips (location, true);
- }
+ update_skips (location, true);
+ }
set_dirty ();
}
void
Session::locations_changed ()
{
- _locations->apply (*this, &Session::_locations_changed);
+ _locations->apply (*this, &Session::_locations_changed);
}
void
Session::_locations_changed (const Locations::LocationList& locations)
{
- /* There was some mass-change in the Locations object.
-
- We might be re-adding a location here but it doesn't actually matter
- for all the locations that the Session takes an interest in.
- */
+ /* There was some mass-change in the Locations object.
+ *
+ * We might be re-adding a location here but it doesn't actually matter
+ * for all the locations that the Session takes an interest in.
+ */
{
PBD::Unwinder<bool> protect_ignore_skip_updates (_ignore_skips_updates, true);
}
RecordStateChanged (); /* emit signal */
-
- if (!rt_context) {
- remove_pending_capture_state ();
- }
}
}
static void
-trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
+trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase, bool sends_only)
{
boost::shared_ptr<Route> r2;
base as being fed by r2
*/
- rbase->add_fed_by (r2, i->sends_only);
+ rbase->add_fed_by (r2, i->sends_only || sends_only);
if (r2 != rbase) {
all routes that feed r2
*/
- trace_terminal (r2, rbase);
+ trace_terminal (r2, rbase, i->sends_only || sends_only);
}
}
or indirectly feeds them.
*/
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- trace_terminal (*i, *i);
+ trace_terminal (*i, *i, false);
}
*r = *sorted_routes;
--how_many;
}
- failed:
+ failed:
if (!new_routes.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
--how_many;
}
- failure:
+ failure:
if (!ret.empty()) {
StateProtector sp (this);
add_routes (ret, false, false, false, order);
void
Session::reconnect_midi_scene_ports(bool inputs)
{
- if (inputs ) {
+ if (inputs ) {
- boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
- if (scene_in_ptr) {
- scene_in_ptr->disconnect_all ();
+ boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
+ if (scene_in_ptr) {
+ scene_in_ptr->disconnect_all ();
- std::vector<EngineStateController::MidiPortState> midi_port_states;
- EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
+ std::vector<EngineStateController::MidiPortState> midi_port_states;
+ EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
- std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+ std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
- for (; state_iter != midi_port_states.end(); ++state_iter) {
- if (state_iter->active && state_iter->available && state_iter->scene_connected) {
- scene_in_ptr->connect (state_iter->name);
- }
- }
- }
+ for (; state_iter != midi_port_states.end(); ++state_iter) {
+ if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+ scene_in_ptr->connect (state_iter->name);
+ }
+ }
+ }
- } else {
+ } else {
- boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
+ boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
- if (scene_out_ptr ) {
- scene_out_ptr->disconnect_all ();
+ if (scene_out_ptr ) {
+ scene_out_ptr->disconnect_all ();
- std::vector<EngineStateController::MidiPortState> midi_port_states;
- EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
+ std::vector<EngineStateController::MidiPortState> midi_port_states;
+ EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
- std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+ std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
- for (; state_iter != midi_port_states.end(); ++state_iter) {
- if (state_iter->active && state_iter->available && state_iter->scene_connected) {
- scene_out_ptr->connect (state_iter->name);
- }
- }
- }
- }
+ for (; state_iter != midi_port_states.end(); ++state_iter) {
+ if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+ scene_out_ptr->connect (state_iter->name);
+ }
+ }
+ }
+ }
}
void
--how_many;
}
- failed:
+ failed:
if (!new_routes.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
--how_many;
}
- failure:
+ failure:
if (!ret.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
(*i)->remove_property (X_("name"));
(*i)->set_property ("bitslot", bitslot);
(*i)->set_property ("name", name);
+ XMLNodeList io_kids = (*i)->children ();
+ for (XMLNodeList::iterator j = io_kids.begin(); j != io_kids.end(); ++j) {
+ if ((*j)->name() != X_("IO")) {
+ continue;
+ }
+ (*j)->remove_property (X_("name"));
+ (*j)->set_property ("name", name);
+ }
}
else if (type && type->value() == X_("intreturn")) {
(*i)->remove_property (X_("bitslot"));
--how_many;
}
- out:
+ out:
if (!ret.empty()) {
StateProtector sp (this);
if (Profile->get_trx()) {
}
in_signal_flow = true;
} else {
- DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n");
+ DEBUG_TRACE (DEBUG::Solo, string_compose("\tno feed to %1\n", (*i)->name()) );
}
if (!in_signal_flow) {
(*s)->mark_for_remove ();
(*s)->drop_references ();
+ SourceRemoved(*s);
s = srcs.erase (s);
}
return 0;
}
+void
+Session::get_last_capture_sources (std::list<boost::shared_ptr<Source> >& srcs)
+{
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (!tr) {
+ continue;
+ }
+
+ list<boost::shared_ptr<Source> >& l = tr->last_capture_sources();
+
+ if (!l.empty()) {
+ srcs.insert (srcs.end(), l.begin(), l.end());
+ l.clear ();
+ }
+ }
+}
+
/* Source Management */
void
}
source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr<Source> (source)));
+
+ SourceAdded(source);
}
}
if ((i = sources.find (source->id())) != sources.end()) {
sources.erase (i);
+ SourceRemoved(source);
}
}
DataType::MIDI, *this, path, false, sample_rate()));
}
+bool
+Session::playlist_is_active (boost::shared_ptr<Playlist> playlist)
+{
+ Glib::Threads::Mutex::Lock lm (_playlists->lock);
+ for (SessionPlaylists::List::iterator i = _playlists->playlists.begin(); i != _playlists->playlists.end(); i++) {
+ if ( (*i) == playlist ) {
+ return true;
+ }
+ }
+ return false;
+}
void
Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
string legal_playlist_name;
string possible_path;
+ DataType data_type = track.data_type();
+
if (end <= start) {
error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
end, start) << endmsg;
diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
include_endpoint, for_export, for_freeze);
- if (diskstream_channels.n(track.data_type()) < 1) {
+ if (data_type == DataType::MIDI && endpoint && !for_export && !for_freeze && diskstream_channels.n(DataType::AUDIO) > 0) {
+ data_type = DataType::AUDIO;
+ }
+
+ if (diskstream_channels.n(data_type) < 1) {
error << _("Cannot write a range with no data.") << endmsg;
return result;
}
goto out;
}
- legal_playlist_name = legalize_for_path (playlist->name());
+ legal_playlist_name = "(bounce)" + legalize_for_path (playlist->name());
- for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(track.data_type()); ++chan_n) {
+ for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(data_type); ++chan_n) {
string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
- string path = ((track.data_type() == DataType::AUDIO)
+ string path = ((data_type == DataType::AUDIO)
? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true)
: new_midi_source_path (legal_playlist_name));
}
try {
- source = SourceFactory::createWritable (track.data_type(), *this, path, false, sample_rate());
+ source = SourceFactory::createWritable (data_type, *this, path, false, sample_rate());
}
catch (failed_constructor& err) {
const MidiBuffer& buf = buffers.get_midi(0);
for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
Evoral::Event<samplepos_t> ev = *i;
- ev.set_time(ev.time() - position);
- ms->append_event_samples(lock, ev, ms->timeline_position());
+ if (!endpoint || for_export) {
+ ev.set_time(ev.time() - position);
+ }
+ ms->append_event_samples(lock, ev, ms->natural_position());
}
}
}
PropertyList plist;
plist.add (Properties::start, 0);
- plist.add (Properties::length, srcs.front()->length(srcs.front()->timeline_position()));
+ plist.add (Properties::whole_file, true);
+ plist.add (Properties::length, srcs.front()->length(srcs.front()->natural_position()));
plist.add (Properties::name, region_name_from_path (srcs.front()->name(), true));
- result = RegionFactory::create (srcs, plist);
+ result = RegionFactory::create (srcs, plist, true);
}
- out:
+ out:
if (!result) {
for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
(*src)->mark_for_remove ();
return rv;
}
+bool
+Session::plot_process_graph (std::string const& file_name) const {
+ return _process_graph ? _process_graph->plot (file_name) : false;
+}
+
void
Session::add_automation_list(AutomationList *al)
{
bool
Session::have_rec_disabled_track () const
{
- return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
+ return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
}
/** Update the state of our rec-enabled tracks flag */