2021-02-09 20:30:17 -05:00
/*
2019-08-02 22:01:25 -04:00
* 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 .
*/
2008-06-02 17:41:35 -04:00
2010-05-01 11:09:19 -04:00
# include <stdint.h>
2008-06-02 17:41:35 -04:00
# include <algorithm>
# include <string>
# include <vector>
# include <sstream>
# include <cstdio> /* sprintf(3) ... grrr */
# include <cmath>
# include <cerrno>
# include <unistd.h>
# include <limits.h>
2020-07-14 10:31:22 -04:00
# include <glibmm/datetime.h>
2012-07-25 13:48:55 -04:00
# include <glibmm/threads.h>
2008-06-02 17:41:35 -04:00
# include <glibmm/miscutils.h>
# include <glibmm/fileutils.h>
2010-07-27 20:43:15 -04:00
# include <boost/algorithm/string/erase.hpp>
2014-07-08 12:25:36 -04:00
# include "pbd/basename.h"
# include "pbd/convert.h"
2009-02-25 13:26:51 -05:00
# include "pbd/error.h"
# include "pbd/file_utils.h"
2014-07-08 00:53:06 -04:00
# include "pbd/md5.h"
2016-04-23 16:11:48 -04:00
# include "pbd/pthread_utils.h"
2013-07-15 08:05:37 -04:00
# include "pbd/search_path.h"
2014-07-08 12:25:36 -04:00
# include "pbd/stl_delete.h"
2015-05-09 19:34:50 -04:00
# include "pbd/replace_all.h"
2019-03-20 23:09:17 -04:00
# include "pbd/types_convert.h"
2014-07-08 12:25:36 -04:00
# include "pbd/unwind.h"
2009-02-25 13:26:51 -05:00
2009-07-21 08:05:44 -04:00
# include "ardour/amp.h"
2009-02-25 13:26:51 -05:00
# include "ardour/analyser.h"
2013-08-07 22:22:11 -04:00
# include "ardour/async_midi_port.h"
2009-02-25 13:26:51 -05:00
# include "ardour/audio_buffer.h"
2009-07-01 09:36:50 -04:00
# include "ardour/audio_port.h"
2009-02-25 13:26:51 -05:00
# include "ardour/audio_track.h"
# include "ardour/audioengine.h"
# include "ardour/audiofilesource.h"
# include "ardour/auditioner.h"
2016-05-07 13:32:31 -04:00
# include "ardour/boost_debug.h"
2010-04-13 16:48:33 -04:00
# include "ardour/buffer_manager.h"
2009-02-25 13:26:51 -05:00
# include "ardour/buffer_set.h"
# include "ardour/bundle.h"
2009-10-23 19:23:00 -04:00
# include "ardour/butler.h"
2009-02-25 13:26:51 -05:00
# include "ardour/click.h"
2011-05-04 11:00:43 -04:00
# include "ardour/control_protocol_manager.h"
2009-02-25 13:26:51 -05:00
# include "ardour/data_type.h"
2009-11-25 09:37:20 -05:00
# include "ardour/debug.h"
2017-04-10 05:25:00 -04:00
# include "ardour/disk_reader.h"
2015-05-28 19:08:47 -04:00
# include "ardour/directory_names.h"
2009-02-25 13:26:51 -05:00
# include "ardour/filename_extensions.h"
2016-01-12 22:04:18 -05:00
# include "ardour/gain_control.h"
2012-05-24 02:09:29 -04:00
# include "ardour/graph.h"
2016-02-23 09:41:21 -05:00
# include "ardour/luabindings.h"
2013-08-07 22:22:11 -04:00
# include "ardour/midiport_manager.h"
2014-04-28 19:58:24 -04:00
# include "ardour/scene_changer.h"
2015-10-19 00:52:48 -04:00
# include "ardour/midi_patch_manager.h"
2009-02-25 13:26:51 -05:00
# include "ardour/midi_track.h"
2009-12-08 22:05:14 -05:00
# include "ardour/midi_ui.h"
2012-05-24 02:09:29 -04:00
# include "ardour/operations.h"
2009-02-25 13:26:51 -05:00
# include "ardour/playlist.h"
2016-11-27 21:30:38 -05:00
# include "ardour/playlist_factory.h"
2012-02-01 23:12:23 -05:00
# include "ardour/plugin.h"
2009-02-25 13:26:51 -05:00
# include "ardour/plugin_insert.h"
2021-07-17 18:07:06 -04:00
# include "ardour/polarity_processor.h"
2021-05-06 15:16:55 -04:00
# include "ardour/presentation_info.h"
2012-05-24 02:09:29 -04:00
# include "ardour/process_thread.h"
2015-05-08 13:44:24 -04:00
# include "ardour/profile.h"
2009-10-28 17:36:40 -04:00
# include "ardour/rc_configuration.h"
2009-02-25 13:26:51 -05:00
# include "ardour/recent_sessions.h"
2012-05-24 02:09:29 -04:00
# include "ardour/region.h"
2009-02-25 13:26:51 -05:00
# include "ardour/region_factory.h"
2016-06-01 16:27:55 -04:00
# include "ardour/revision.h"
2009-02-25 13:26:51 -05:00
# include "ardour/route_group.h"
2017-10-30 10:58:36 -04:00
# include "ardour/rt_tasklist.h"
2022-05-03 20:10:46 -04:00
# include "ardour/rt_safe_delete.h"
2020-02-17 19:26:20 -05:00
# include "ardour/silentfilesource.h"
2009-02-25 13:26:51 -05:00
# include "ardour/send.h"
2017-05-05 07:31:21 -04:00
# include "ardour/selection.h"
2009-02-25 13:26:51 -05:00
# include "ardour/session.h"
# include "ardour/session_directory.h"
2009-12-03 16:52:10 -05:00
# include "ardour/session_playlists.h"
2020-05-07 14:38:09 -04:00
# include "ardour/session_route.h"
2009-02-25 13:26:51 -05:00
# include "ardour/smf_source.h"
2016-04-08 16:49:47 -04:00
# include "ardour/solo_isolate_control.h"
2009-02-25 13:26:51 -05:00
# include "ardour/source_factory.h"
2013-09-09 22:40:54 -04:00
# include "ardour/speakers.h"
2015-04-01 11:56:59 -04:00
# include "ardour/tempo.h"
2016-04-24 08:41:07 -04:00
# include "ardour/ticker.h"
2019-09-17 20:26:03 -04:00
# include "ardour/transport_fsm.h"
2018-09-18 18:51:59 -04:00
# include "ardour/transport_master.h"
2019-09-17 20:26:03 -04:00
# include "ardour/transport_master_manager.h"
2014-04-10 08:58:04 -04:00
# include "ardour/track.h"
2021-07-17 18:07:06 -04:00
# include "ardour/triggerbox.h"
2016-08-24 08:32:51 -04:00
# include "ardour/types_convert.h"
2015-03-08 17:40:27 -04:00
# include "ardour/user_bundle.h"
2009-02-25 13:26:51 -05:00
# include "ardour/utils.h"
2016-01-26 00:18:03 -05:00
# include "ardour/vca_manager.h"
2016-04-21 11:08:47 -04:00
# include "ardour/vca.h"
2008-06-02 17:41:35 -04:00
2010-07-05 20:16:36 -04:00
# include "midi++/port.h"
2010-06-29 09:47:53 -04:00
# include "midi++/mmc.h"
2009-12-29 16:31:14 -05:00
2016-02-23 09:41:21 -05:00
# include "LuaBridge/LuaBridge.h"
2015-08-29 07:48:05 -04:00
# include <glibmm/checksum.h>
2020-08-06 15:38:00 -04:00
# include "pbd/i18n.h"
2012-05-24 02:09:29 -04:00
namespace ARDOUR {
class MidiSource ;
class Processor ;
class Speakers ;
}
2008-06-02 17:41:35 -04:00
using namespace std ;
using namespace ARDOUR ;
using namespace PBD ;
2020-11-11 11:31:52 -05:00
using namespace Temporal ;
2008-06-02 17:41:35 -04:00
bool Session : : _disable_all_loaded_plugins = false ;
2015-09-01 07:26:31 -04:00
bool Session : : _bypass_all_loaded_plugins = false ;
2016-04-12 07:49:50 -04:00
guint Session : : _name_id_counter = 0 ;
2008-06-02 17:41:35 -04:00
2009-12-19 15:26:31 -05:00
PBD : : Signal1 < void , std : : string > Session : : Dialog ;
PBD : : Signal0 < int > Session : : AskAboutPendingState ;
2017-09-18 12:39:17 -04:00
PBD : : Signal2 < int , samplecnt_t , samplecnt_t > Session : : AskAboutSampleRateMismatch ;
PBD : : Signal2 < void , samplecnt_t , samplecnt_t > Session : : NotifyAboutSampleRateMismatch ;
2009-12-19 15:26:31 -05:00
PBD : : Signal0 < void > Session : : SendFeedback ;
2010-11-09 01:03:51 -05:00
PBD : : Signal3 < int , Session * , std : : string , DataType > Session : : MissingFile ;
2009-12-19 15:26:31 -05:00
2017-09-18 12:39:17 -04:00
PBD : : Signal1 < void , samplepos_t > Session : : StartTimeChanged ;
PBD : : Signal1 < void , samplepos_t > Session : : EndTimeChanged ;
2022-03-15 16:51:55 -04:00
PBD : : Signal4 < void , std : : string , std : : string , bool , samplepos_t > Session : : Exported ;
2009-12-20 11:50:41 -05:00
PBD : : Signal1 < int , boost : : shared_ptr < Playlist > > Session : : AskAboutPlaylistDeletion ;
2010-10-07 08:12:16 -04:00
PBD : : Signal0 < void > Session : : Quit ;
2011-11-09 12:44:39 -05:00
PBD : : Signal0 < void > Session : : FeedbackDetected ;
2011-11-11 08:52:27 -05:00
PBD : : Signal0 < void > Session : : SuccessfulGraphSort ;
2013-03-04 16:57:29 -05:00
PBD : : Signal2 < void , std : : string , std : : string > Session : : VersionMismatch ;
2021-11-03 17:18:59 -04:00
PBD : : Signal0 < void > Session : : AfterConnect ;
2008-06-02 17:41:35 -04:00
2017-09-18 12:39:17 -04:00
const samplecnt_t Session : : bounce_chunk_size = 8192 ;
2009-12-10 12:45:18 -05:00
static void clean_up_session_event ( SessionEvent * ev ) { delete ev ; }
2009-12-11 18:30:48 -05:00
const SessionEvent : : RTeventCallback Session : : rt_cleanup ( clean_up_session_event ) ;
2015-05-13 10:39:24 -04:00
const uint32_t Session : : session_end_shift = 0 ;
2012-06-18 14:28:26 -04:00
/** @param snapshot_name Snapshot name, without .ardour suffix */
2008-06-02 17:41:35 -04:00
Session : : Session ( AudioEngine & eng ,
2011-03-14 17:53:10 -04:00
const string & fullpath ,
const string & snapshot_name ,
2019-10-10 16:34:20 -04:00
BusProfile const * bus_profile ,
2020-03-24 12:38:03 -04:00
string mix_template ,
bool unnamed )
2019-03-19 00:14:00 -04:00
: _playlists ( new SessionPlaylists )
2013-09-10 15:41:19 -04:00
, _engine ( eng )
, process_function ( & Session : : process_with_events )
2014-05-26 00:17:49 -04:00
, _bounce_processing_active ( false )
2013-09-10 15:41:19 -04:00
, waiting_for_sync_offset ( false )
2017-09-18 12:39:17 -04:00
, _base_sample_rate ( 0 )
, _nominal_sample_rate ( 0 )
, _current_sample_rate ( 0 )
, _transport_sample ( 0 )
2013-09-10 15:41:19 -04:00
, _session_range_location ( 0 )
2019-02-06 10:54:13 -05:00
, _session_range_is_free ( true )
2013-09-10 15:41:19 -04:00
, _silent ( false )
2017-09-28 00:31:12 -04:00
, _remaining_latency_preroll ( 0 )
2017-10-29 13:30:18 -04:00
, _engine_speed ( 1.0 )
2017-07-30 19:48:28 -04:00
, _signalled_varispeed ( 0 )
2013-09-10 15:41:19 -04:00
, auto_play_legal ( false )
2017-09-18 12:39:17 -04:00
, _requested_return_sample ( - 1 )
2013-09-10 15:41:19 -04:00
, current_block_size ( 0 )
, _worst_output_latency ( 0 )
, _worst_input_latency ( 0 )
2017-09-28 22:17:16 -04:00
, _worst_route_latency ( 0 )
2021-09-08 15:45:16 -04:00
, _io_latency ( 0 )
2017-09-28 22:17:16 -04:00
, _send_latency_changes ( 0 )
2022-02-06 22:30:48 -05:00
, _update_send_delaylines ( false )
2013-09-10 15:41:19 -04:00
, _have_captured ( false )
2021-02-05 16:18:21 -05:00
, _capture_duration ( 0 )
, _capture_xruns ( 0 )
2021-04-29 11:50:28 -04:00
, _export_xruns ( 0 )
2013-09-10 15:41:19 -04:00
, _non_soloed_outs_muted ( false )
2016-01-16 17:40:03 -05:00
, _listening ( false )
2013-09-10 15:41:19 -04:00
, _listen_cnt ( 0 )
, _solo_isolated_cnt ( 0 )
, _writable ( false )
2013-03-30 14:12:31 -04:00
, _under_nsm_control ( false )
2015-04-28 21:09:17 -04:00
, _xrun_count ( 0 )
2018-09-18 18:51:59 -04:00
, master_wait_end ( 0 )
2013-09-10 15:41:19 -04:00
, post_export_sync ( false )
, post_export_position ( 0 )
, _exporting ( false )
, _export_rolling ( false )
2016-07-13 22:34:18 -04:00
, _realtime_export ( false )
2016-10-17 15:34:40 -04:00
, _region_export ( false )
2016-02-29 07:50:56 -05:00
, _export_preroll ( 0 )
2013-09-10 15:41:19 -04:00
, _pre_export_mmc_enabled ( false )
, _name ( snapshot_name )
, _is_new ( true )
, _send_qf_mtc ( false )
, _pframes_since_last_mtc ( 0 )
, play_loop ( false )
2020-05-13 14:34:22 -04:00
, loop_changing ( false )
2013-09-10 15:41:19 -04:00
, last_loopend ( 0 )
2013-09-09 22:40:54 -04:00
, _session_dir ( new SessionDirectory ( fullpath ) )
2015-10-05 10:17:49 -04:00
, _current_snapshot_name ( snapshot_name )
2010-11-26 14:57:03 -05:00
, state_tree ( 0 )
2013-09-10 15:41:19 -04:00
, state_was_pending ( false )
2019-03-18 10:33:05 -04:00
, _state_of_the_state ( StateOfTheState ( CannotSave | InitialConnecting | Loading ) )
2014-06-28 15:27:36 -04:00
, _save_queued ( false )
2020-02-02 14:49:25 -05:00
, _save_queued_pending ( false )
2013-09-10 15:41:19 -04:00
, _last_roll_location ( 0 )
, _last_roll_or_reversal_location ( 0 )
, _last_record_location ( 0 )
, pending_auto_loop ( false )
2017-03-23 14:05:53 -04:00
, _mempool ( " Session " , 3145728 )
2016-02-23 09:41:21 -05:00
, lua ( lua_newstate ( & PBD : : ReallocPool : : lalloc , & _mempool ) )
2016-02-29 09:17:12 -05:00
, _n_lua_scripts ( 0 )
2010-11-26 14:57:03 -05:00
, _butler ( new Butler ( * this ) )
2019-09-20 00:33:43 -04:00
, _transport_fsm ( new TransportFSM ( * this ) )
2013-09-10 15:41:19 -04:00
, _locations ( new Locations ( * this ) )
2015-02-18 12:00:28 -05:00
, _ignore_skips_updates ( false )
2015-04-28 16:18:30 -04:00
, _rt_thread_active ( false )
, _rt_emit_pending ( false )
2016-12-19 10:33:54 -05:00
, _ac_thread_active ( 0 )
2013-09-10 15:41:19 -04:00
, step_speed ( 0 )
, outbound_mtc_timecode_frame ( 0 )
, next_quarter_frame_to_send ( - 1 )
2016-12-08 06:26:41 -05:00
, _samples_per_timecode_frame ( 0 )
2013-09-10 15:41:19 -04:00
, _frames_per_hour ( 0 )
, _timecode_frames_per_hour ( 0 )
, last_timecode_valid ( false )
, last_timecode_when ( 0 )
2010-11-26 14:57:03 -05:00
, _send_timecode_update ( false )
2013-09-10 15:41:19 -04:00
, ltc_encoder ( 0 )
2013-05-14 14:01:20 -04:00
, ltc_enc_buf ( 0 )
2013-09-10 15:41:19 -04:00
, ltc_buf_off ( 0 )
, ltc_buf_len ( 0 )
, ltc_speed ( 0 )
, ltc_enc_byte ( 0 )
, ltc_enc_pos ( 0 )
, ltc_enc_cnt ( 0 )
, ltc_enc_off ( 0 )
, restarting ( false )
, ltc_prev_cycle ( 0 )
, ltc_timecode_offset ( 0 )
, ltc_timecode_negative_offset ( false )
, midi_control_ui ( 0 )
2020-11-27 14:40:58 -05:00
, _punch_or_loop ( NoConstraint )
2010-11-26 14:57:03 -05:00
, _all_route_group ( new RouteGroup ( * this , " all " ) )
, routes ( new RouteList )
2013-09-10 15:41:19 -04:00
, _adding_routes_in_progress ( false )
2015-05-08 20:30:30 -04:00
, _reconnecting_routes_in_progress ( false )
2015-05-08 16:04:15 -04:00
, _route_deletion_in_progress ( false )
2022-01-25 22:17:22 -05:00
, _route_reorder_in_progress ( false )
2014-06-25 15:16:09 -04:00
, _track_number_decimals ( 1 )
2013-09-10 15:41:19 -04:00
, default_fade_steepness ( 0 )
, default_fade_msecs ( 0 )
2010-11-26 14:57:03 -05:00
, _total_free_4k_blocks ( 0 )
2012-06-12 12:41:29 -04:00
, _total_free_4k_blocks_uncertain ( false )
2013-09-10 15:41:19 -04:00
, no_questions_about_missing_files ( false )
2010-11-26 14:57:03 -05:00
, _bundles ( new BundleList )
, _bundle_xml_node ( 0 )
2011-01-19 12:38:46 -05:00
, _current_trans ( 0 )
2013-09-10 15:41:19 -04:00
, _clicking ( false )
2017-04-01 17:52:43 -04:00
, _click_rec_only ( false )
2010-11-26 14:57:03 -05:00
, click_data ( 0 )
, click_emphasis_data ( 0 )
2013-09-10 15:41:19 -04:00
, click_length ( 0 )
, click_emphasis_length ( 0 )
, _clicks_cleared ( 0 )
2017-01-17 14:43:55 -05:00
, _count_in_samples ( 0 )
2013-09-09 22:40:54 -04:00
, _play_range ( false )
2020-09-20 18:34:09 -04:00
, _range_selection ( timepos_t : : max ( Temporal : : AudioTime ) , timepos_t : : max ( Temporal : : AudioTime ) )
, _object_selection ( timepos_t : : max ( Temporal : : AudioTime ) , timepos_t : : max ( Temporal : : AudioTime ) )
2017-01-19 07:03:57 -05:00
, _preroll_record_trim_len ( 0 )
2017-02-13 16:59:20 -05:00
, _count_in_once ( false )
2013-09-10 15:41:19 -04:00
, main_outs ( 0 )
2013-09-09 22:40:54 -04:00
, first_file_data_format_reset ( true )
, first_file_header_format_reset ( true )
2013-09-10 15:41:19 -04:00
, have_looped ( false )
2013-09-09 22:40:54 -04:00
, _step_editors ( 0 )
, _speakers ( new Speakers )
2016-11-25 12:07:43 -05:00
, _ignore_route_processor_changes ( 0 )
2020-04-18 17:54:24 -04:00
, _ignored_a_processor_change ( 0 )
2016-04-24 08:41:07 -04:00
, midi_clock ( 0 )
2014-04-28 19:58:24 -04:00
, _scene_changer ( 0 )
2013-09-16 10:23:37 -04:00
, _midi_ports ( 0 )
, _mmc ( 0 )
2016-02-27 22:16:37 -05:00
, _vca_manager ( new VCAManager ( * this ) )
2017-05-05 07:31:21 -04:00
, _selection ( new CoreSelection ( * this ) )
2017-07-05 17:24:02 -04:00
, _global_locate_pending ( false )
2020-02-29 01:33:33 -05:00
, _had_destructive_tracks ( false )
2022-01-06 02:23:27 -05:00
, _pending_cue ( - 1 )
, _active_cue ( - 1 )
2022-03-14 14:18:20 -04:00
, tb_with_filled_slots ( 0 )
2008-06-02 17:41:35 -04:00
{
2021-03-19 01:12:11 -04:00
g_atomic_int_set ( & _suspend_save , 0 ) ;
g_atomic_int_set ( & _playback_load , 0 ) ;
g_atomic_int_set ( & _capture_load , 0 ) ;
g_atomic_int_set ( & _post_transport_work , 0 ) ;
g_atomic_int_set ( & _processing_prohibited , Disabled ) ;
g_atomic_int_set ( & _record_status , Disabled ) ;
g_atomic_int_set ( & _punch_or_loop , NoConstraint ) ;
g_atomic_int_set ( & _current_usecs_per_track , 1000 ) ;
g_atomic_int_set ( & _have_rec_enabled_track , 0 ) ;
g_atomic_int_set ( & _have_rec_disabled_track , 1 ) ;
g_atomic_int_set ( & _latency_recompute_pending , 0 ) ;
g_atomic_int_set ( & _suspend_timecode_transmission , 0 ) ;
g_atomic_int_set ( & _update_pretty_names , 0 ) ;
g_atomic_int_set ( & _seek_counter , 0 ) ;
g_atomic_int_set ( & _butler_seek_counter , 0 ) ;
2016-06-01 16:27:55 -04:00
created_with = string_compose ( " %1 %2 " , PROGRAM_NAME , revision ) ;
2015-04-28 16:18:30 -04:00
pthread_mutex_init ( & _rt_emit_mutex , 0 ) ;
pthread_cond_init ( & _rt_emit_cond , 0 ) ;
2016-04-23 16:11:48 -04:00
pthread_mutex_init ( & _auto_connect_mutex , 0 ) ;
pthread_cond_init ( & _auto_connect_cond , 0 ) ;
2016-04-12 07:49:50 -04:00
init_name_id_counter ( 1 ) ; // reset for new sessions, start at 1
2016-04-21 11:08:47 -04:00
VCA : : set_next_vca_number ( 1 ) ; // reset for new sessions, start at 1
2016-04-12 07:49:50 -04:00
2022-01-06 02:23:27 -05:00
_cue_events . reserve ( 1024 ) ;
2016-12-27 16:24:24 -05:00
pre_engine_init ( fullpath ) ; // sets _is_new
2015-10-05 10:17:49 -04:00
2016-02-23 09:41:21 -05:00
setup_lua ( ) ;
2019-10-11 12:25:08 -04:00
assert ( AudioEngine : : instance ( ) - > running ( ) ) ;
immediately_post_engine ( ) ;
2020-04-01 19:01:31 -04:00
bool need_template_resave = false ;
std : : string template_description ;
2010-03-22 17:35:35 -04:00
if ( _is_new ) {
2015-05-11 18:51:14 -04:00
2015-08-28 08:07:48 -04:00
Stateful : : loading_state_version = CURRENT_SESSION_FILE_VERSION ;
2020-03-24 12:38:03 -04:00
if ( create ( mix_template , bus_profile , unnamed ) ) {
2008-06-02 17:41:35 -04:00
destroy ( ) ;
2015-09-05 07:16:26 -04:00
throw SessionException ( _ ( " Session initialization failed " ) ) ;
2008-06-02 17:41:35 -04:00
}
2013-09-16 09:57:22 -04:00
2013-09-21 12:50:45 -04:00
/* if a mix template was provided, then ::create() will
* have copied it into the session and we need to load it
* so that we have the state ready for : : set_state ( )
* after the engine is started .
*
* Note that we do NOT try to get the sample rate from
* the template at this time , though doing so would
* be easy if we decided this was an appropriate part
* of a template .
*/
2015-10-04 14:51:05 -04:00
if ( ! mix_template . empty ( ) ) {
2017-08-17 13:28:14 -04:00
try {
2020-04-01 16:22:37 -04:00
if ( load_state ( _current_snapshot_name , /* from_template = */ true ) ) {
2017-08-17 13:28:14 -04:00
throw SessionException ( _ ( " Failed to load template/snapshot state " ) ) ;
}
} catch ( PBD : : unknown_enumeration & e ) {
throw SessionException ( _ ( " Failed to parse template/snapshot state " ) ) ;
2015-05-08 12:07:33 -04:00
}
2020-04-01 19:01:31 -04:00
if ( state_tree & & Stateful : : loading_state_version < CURRENT_SESSION_FILE_VERSION ) {
need_template_resave = true ;
XMLNode const & root ( * state_tree - > root ( ) ) ;
XMLNode * desc_nd = root . child ( X_ ( " description " ) ) ;
if ( desc_nd ) {
template_description = desc_nd - > attribute_value ( ) ;
}
}
2015-05-08 12:07:33 -04:00
store_recent_templates ( mix_template ) ;
2013-09-21 12:50:45 -04:00
}
2014-06-29 09:45:08 -04:00
/* load default session properties - if any */
config . load_state ( ) ;
2013-09-09 22:40:54 -04:00
} else {
2013-09-16 09:57:22 -04:00
2013-09-09 22:40:54 -04:00
if ( load_state ( _current_snapshot_name ) ) {
2015-09-05 07:16:26 -04:00
throw SessionException ( _ ( " Failed to load state " ) ) ;
2013-09-09 22:40:54 -04:00
}
2013-09-10 15:41:19 -04:00
}
2020-11-26 18:11:41 -05:00
/* apply the loaded state_tree */
2017-06-27 14:28:45 -04:00
int err = post_engine_init ( ) ;
2019-10-11 12:25:08 -04:00
2017-06-27 14:28:45 -04:00
if ( err ) {
2008-06-02 17:41:35 -04:00
destroy ( ) ;
2017-06-27 14:28:45 -04:00
switch ( err ) {
case - 1 :
throw SessionException ( string_compose ( _ ( " Cannot initialize session/engine: %1 " ) , _ ( " Failed to create background threads. " ) ) ) ;
break ;
case - 2 :
case - 3 :
throw SessionException ( string_compose ( _ ( " Cannot initialize session/engine: %1 " ) , _ ( " Invalid TempoMap in session-file. " ) ) ) ;
break ;
case - 4 :
throw SessionException ( string_compose ( _ ( " Cannot initialize session/engine: %1 " ) , _ ( " Invalid or corrupt session state. " ) ) ) ;
break ;
case - 5 :
throw SessionException ( string_compose ( _ ( " Cannot initialize session/engine: %1 " ) , _ ( " Port registration failed. " ) ) ) ;
break ;
default :
throw SessionException ( string_compose ( _ ( " Cannot initialize session/engine: %1 " ) , _ ( " Unexpected exception during session setup, possibly invalid audio/midi engine parameters. Please see stdout/stderr for details " ) ) ) ;
break ;
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2020-11-26 18:11:41 -05:00
if ( ! mix_template . empty ( ) ) {
/* fixup monitor-sends */
if ( Config - > get_use_monitor_bus ( ) ) {
/* Session::config_changed will have set use-monitor-bus to match the template.
* search for want_ms , have_ms
*/
assert ( _monitor_out ) ;
/* ..but sends do not exist, since templated track bitslots are unset */
setup_route_monitor_sends ( true , true ) ;
} else {
/* remove any monitor-sends that may be in the template */
assert ( ! _monitor_out ) ;
setup_route_monitor_sends ( false , true ) ;
}
}
2020-04-19 12:20:18 -04:00
if ( ! unnamed ) {
store_recent_sessions ( _name , _path ) ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
bool was_dirty = dirty ( ) ;
2019-03-18 10:33:05 -04:00
unset_dirty ( ) ;
2008-06-02 17:41:35 -04:00
2022-01-25 22:17:22 -05:00
PresentationInfo : : Change . connect_same_thread ( * this , boost : : bind ( & Session : : notify_presentation_info_change , this , _1 ) ) ;
2017-01-26 13:20:36 -05:00
2009-12-21 13:23:07 -05:00
Config - > ParameterChanged . connect_same_thread ( * this , boost : : bind ( & Session : : config_changed , this , _1 , false ) ) ;
config . ParameterChanged . connect_same_thread ( * this , boost : : bind ( & Session : : config_changed , this , _1 , true ) ) ;
2008-06-02 17:41:35 -04:00
if ( was_dirty ) {
DirtyChanged ( ) ; /* EMIT SIGNAL */
}
2010-03-23 13:58:40 -04:00
2010-10-07 14:33:20 -04:00
StartTimeChanged . connect_same_thread ( * this , boost : : bind ( & Session : : start_time_changed , this , _1 ) ) ;
EndTimeChanged . connect_same_thread ( * this , boost : : bind ( & Session : : end_time_changed , this , _1 ) ) ;
2019-09-20 14:25:04 -04:00
LatentSend : : ChangedLatency . connect_same_thread ( * this , boost : : bind ( & Session : : send_latency_compensation_change , this ) ) ;
2022-02-06 22:30:48 -05:00
LatentSend : : QueueUpdate . connect_same_thread ( * this , boost : : bind ( & Session : : update_send_delaylines , this ) ) ;
2019-09-28 14:53:50 -04:00
Latent : : DisableSwitchChanged . connect_same_thread ( * this , boost : : bind ( & Session : : queue_latency_recompute , this ) ) ;
2017-09-28 22:17:16 -04:00
2021-02-08 08:28:39 -05:00
Controllable : : ControlTouched . connect_same_thread ( * this , boost : : bind ( & Session : : controllable_touched , this , _1 ) ) ;
2022-01-04 20:03:28 -05:00
Location : : cue_change . connect_same_thread ( * this , boost : : bind ( & Session : : cue_marker_change , this , _1 ) ) ;
2015-04-28 16:18:30 -04:00
emit_thread_start ( ) ;
2016-04-23 16:11:48 -04:00
auto_connect_thread_start ( ) ;
2015-04-28 16:18:30 -04:00
2013-09-11 16:33:40 -04:00
/* hook us up to the engine since we are now completely constructed */
BootMessage ( _ ( " Connect to engine " ) ) ;
_engine . set_session ( this ) ;
_engine . reset_timebase ( ) ;
2016-12-27 16:24:24 -05:00
ensure_subdirs ( ) ; // archived or zipped sessions may lack peaks/ analysis/ etc
2017-08-09 20:21:52 -04:00
if ( ! mix_template . empty ( ) ) {
/* ::create() unsets _is_new after creating the session.
* But for templated sessions , the sample - rate is initially unset
* ( not read from template ) , so we need to save it ( again ) .
*/
_is_new = true ;
}
2015-05-08 20:56:10 -04:00
session_loaded ( ) ;
2017-08-09 20:21:52 -04:00
_is_new = false ;
2016-09-22 13:39:05 -04:00
2020-04-01 19:01:31 -04:00
if ( need_template_resave ) {
save_template ( mix_template , template_description , true ) ;
}
2013-09-11 16:33:40 -04:00
BootMessage ( _ ( " Session loading complete " ) ) ;
2008-06-02 17:41:35 -04:00
}
Session : : ~ Session ( )
{
2015-10-05 10:17:49 -04:00
# ifdef PT_TIMING
2012-01-14 17:02:59 -05:00
ST . dump ( " ST.dump " ) ;
2015-10-05 10:17:49 -04:00
# endif
2008-06-02 17:41:35 -04:00
destroy ( ) ;
}
2016-04-12 07:49:50 -04:00
unsigned int
Session : : next_name_id ( )
{
return g_atomic_int_add ( & _name_id_counter , 1 ) ;
}
unsigned int
Session : : name_id_counter ( )
{
return g_atomic_int_get ( & _name_id_counter ) ;
}
void
Session : : init_name_id_counter ( guint n )
{
g_atomic_int_set ( & _name_id_counter , n ) ;
}
2013-09-16 12:08:19 -04:00
int
Session : : immediately_post_engine ( )
{
/* Do various initializations that should take place directly after we
* know that the engine is running , but before we either create a
* session or set state for an existing one .
*/
2015-10-05 10:17:49 -04:00
2017-10-30 10:58:36 -04:00
_rt_tasklist . reset ( new RTTaskList ( ) ) ;
2013-09-16 09:57:22 -04:00
if ( how_many_dsp_threads ( ) > 1 ) {
/* For now, only create the graph if we are using >1 DSP threads, as
it is a bit slower than the old code with 1 thread .
*/
_process_graph . reset ( new Graph ( * this ) ) ;
}
2019-12-16 18:13:27 -05:00
/* every time we reconnect, recompute worst case output latencies */
_engine . Running . connect_same_thread ( * this , boost : : bind ( & Session : : initialize_latencies , this ) ) ;
2019-09-17 20:26:03 -04:00
/* Restart transport FSM */
2013-09-16 12:08:19 -04:00
2019-09-20 00:33:43 -04:00
_transport_fsm - > start ( ) ;
2019-09-17 20:26:03 -04:00
/* every time we reconnect, do stuff ... */
_engine . Running . connect_same_thread ( * this , boost : : bind ( & Session : : engine_running , this ) ) ;
2013-09-16 12:08:19 -04:00
try {
BootMessage ( _ ( " Set up LTC " ) ) ;
setup_ltc ( ) ;
BootMessage ( _ ( " Set up Click " ) ) ;
setup_click ( ) ;
BootMessage ( _ ( " Set up standard connections " ) ) ;
setup_bundles ( ) ;
}
catch ( failed_constructor & err ) {
return - 1 ;
}
2015-03-08 17:40:27 -04:00
/* TODO, connect in different thread. (PortRegisteredOrUnregistered may be in RT context)
* can we do that ? */
_engine . PortRegisteredOrUnregistered . connect_same_thread ( * this , boost : : bind ( & Session : : setup_bundles , this ) ) ;
2021-03-14 13:14:56 -04:00
_engine . PortPrettyNameChanged . connect_same_thread ( * this , boost : : bind ( & Session : : setup_bundles , this ) ) ;
2015-03-08 13:39:19 -04:00
2019-10-11 12:25:08 -04:00
// set samplerate for plugins added early
// e.g from templates or MB channelstrip
set_block_size ( _engine . samples_per_cycle ( ) ) ;
set_sample_rate ( _engine . sample_rate ( ) ) ;
2013-09-12 17:06:21 -04:00
return 0 ;
}
2008-06-02 17:41:35 -04:00
void
Session : : destroy ( )
{
2009-11-25 09:37:20 -05:00
vector < void * > debug_pointers ;
2008-06-02 17:41:35 -04:00
/* if we got to here, leaving pending capture state around
is a mistake .
*/
remove_pending_capture_state ( ) ;
2015-11-24 18:55:41 -05:00
Analyser : : flush ( ) ;
2019-03-18 10:33:05 -04:00
_state_of_the_state = StateOfTheState ( CannotSave | Deletion ) ;
2008-06-02 17:41:35 -04:00
2017-07-30 19:48:28 -04:00
{
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
ltc_tx_cleanup ( ) ;
2020-05-07 13:49:11 -04:00
if ( _ltc_output_port ) {
AudioEngine : : instance ( ) - > unregister_port ( _ltc_output_port ) ;
}
2017-07-30 19:48:28 -04:00
}
2012-12-05 11:07:30 -05:00
/* disconnect from any and all signals that we are connected to */
2016-04-23 20:51:23 -04:00
Port : : PortSignalDrop ( ) ; /* EMIT SIGNAL */
2012-12-05 11:07:30 -05:00
drop_connections ( ) ;
2021-11-19 17:36:13 -05:00
/* stop auto dis/connecting */
auto_connect_thread_terminate ( ) ;
2015-10-02 19:22:16 -04:00
/* shutdown control surface protocols while we still have ports
2017-08-03 12:52:39 -04:00
* and the engine to move data to any devices .
*/
2015-10-02 19:22:16 -04:00
ControlProtocolManager : : instance ( ) . drop_protocols ( ) ;
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
_engine . remove_session ( ) ;
2012-06-21 11:00:10 -04:00
/* deregister all ports - there will be no process or any other
* callbacks from the engine any more .
*/
Port : : PortDrop ( ) ; /* EMIT SIGNAL */
2019-10-28 13:15:07 -04:00
/* remove I/O objects that we (the session) own */
_click_io . reset ( ) ;
2020-09-22 15:42:52 -04:00
_click_io_connection . disconnect ( ) ;
2019-10-28 13:15:07 -04:00
2019-03-23 10:52:36 -04:00
{
Glib : : Threads : : Mutex : : Lock lm ( controllables_lock ) ;
for ( Controllables : : iterator i = controllables . begin ( ) ; i ! = controllables . end ( ) ; + + i ) {
( * i ) - > DropReferences ( ) ; /* EMIT SIGNAL */
}
controllables . clear ( ) ;
}
2008-06-02 17:41:35 -04:00
/* clear history so that no references to objects are held any more */
_history . clear ( ) ;
/* clear state tree so that no references to objects are held any more */
2008-08-04 18:37:24 -04:00
2008-12-12 09:43:24 -05:00
delete state_tree ;
2014-09-10 20:34:09 -04:00
state_tree = 0 ;
2008-06-02 17:41:35 -04:00
2017-07-30 19:48:28 -04:00
{
/* unregister all lua functions, drop held references (if any) */
Glib : : Threads : : Mutex : : Lock tm ( lua_lock , Glib : : Threads : : TRY_LOCK ) ;
( * _lua_cleanup ) ( ) ;
lua . do_command ( " Session = nil " ) ;
delete _lua_run ;
delete _lua_add ;
delete _lua_del ;
delete _lua_list ;
delete _lua_save ;
delete _lua_load ;
delete _lua_cleanup ;
lua . collect_garbage ( ) ;
}
2009-10-23 19:23:00 -04:00
2016-02-23 09:41:21 -05:00
/* reset dynamic state version back to default */
2009-10-15 14:56:11 -04:00
Stateful : : loading_state_version = 0 ;
2010-04-14 19:58:20 -04:00
_butler - > drop_references ( ) ;
2009-12-23 23:04:01 -05:00
delete _butler ;
2012-06-14 07:45:10 -04:00
_butler = 0 ;
2015-10-05 10:17:49 -04:00
2011-03-14 17:53:10 -04:00
delete _all_route_group ;
2008-08-04 18:37:24 -04:00
2014-09-10 14:43:30 -04:00
DEBUG_TRACE ( DEBUG : : Destruction , " delete route groups \n " ) ;
for ( list < RouteGroup * > : : iterator i = _route_groups . begin ( ) ; i ! = _route_groups . end ( ) ; + + i ) {
delete * i ;
}
2008-12-12 09:43:24 -05:00
if ( click_data ! = default_click ) {
2008-06-02 17:41:35 -04:00
delete [ ] click_data ;
}
2008-12-12 09:43:24 -05:00
if ( click_emphasis_data ! = default_click_emphasis ) {
2008-06-02 17:41:35 -04:00
delete [ ] click_emphasis_data ;
}
clear_clicks ( ) ;
2014-06-29 22:04:35 -04:00
/* need to remove auditioner before monitoring section
2017-09-21 11:35:52 -04:00
* otherwise it is re - connected .
* Note : If a session was never successfully loaded , there
* may not yet be an auditioner .
*/
if ( auditioner ) {
auditioner - > drop_references ( ) ;
}
2014-06-29 22:04:35 -04:00
auditioner . reset ( ) ;
/* drop references to routes held by the monitoring section
* specifically _monitor_out aux / listen references */
remove_monitor_section ( ) ;
2009-11-27 12:29:27 -05:00
/* clear out any pending dead wood from RCU managed objects */
routes . flush ( ) ;
_bundles . flush ( ) ;
2011-06-01 12:50:12 -04:00
2017-04-10 05:25:00 -04:00
DiskReader : : free_working_buffers ( ) ;
2008-08-04 18:37:24 -04:00
2009-12-22 15:21:43 -05:00
/* tell everyone who is still standing that we're about to die */
drop_references ( ) ;
2008-06-02 17:41:35 -04:00
2009-12-22 15:21:43 -05:00
/* tell everyone to drop references and delete objects as we go */
2008-06-02 17:41:35 -04:00
2009-11-25 09:37:20 -05:00
DEBUG_TRACE ( DEBUG : : Destruction , " delete regions \n " ) ;
2010-03-06 10:40:42 -05:00
RegionFactory : : delete_all_regions ( ) ;
2008-06-02 17:41:35 -04:00
2016-02-29 16:10:31 -05:00
/* Do this early so that VCAs no longer hold references to routes */
DEBUG_TRACE ( DEBUG : : Destruction , " delete vcas \n " ) ;
delete _vca_manager ;
2009-11-25 09:37:20 -05:00
DEBUG_TRACE ( DEBUG : : Destruction , " delete routes \n " ) ;
2010-07-13 22:23:37 -04:00
2009-11-27 19:49:04 -05:00
/* reset these three references to special routes before we do the usual route delete thing */
_master_out . reset ( ) ;
2010-03-22 17:35:35 -04:00
_monitor_out . reset ( ) ;
2009-11-27 19:49:04 -05:00
2008-06-02 17:41:35 -04:00
{
RCUWriter < RouteList > writer ( routes ) ;
boost : : shared_ptr < RouteList > r = writer . get_copy ( ) ;
2009-12-22 15:21:43 -05:00
2008-06-02 17:41:35 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2009-11-25 09:37:20 -05:00
DEBUG_TRACE ( DEBUG : : Destruction , string_compose ( " Dropping for route %1 ; pre-ref = %2 \n " , ( * i ) - > name ( ) , ( * i ) . use_count ( ) ) ) ;
2008-06-02 17:41:35 -04:00
( * i ) - > drop_references ( ) ;
}
2009-12-22 15:21:43 -05:00
2008-06-02 17:41:35 -04:00
r - > clear ( ) ;
/* writer goes out of scope and updates master */
}
routes . flush ( ) ;
2009-12-25 16:06:52 -05:00
2014-01-19 16:37:01 -05:00
{
DEBUG_TRACE ( DEBUG : : Destruction , " delete sources \n " ) ;
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
for ( SourceMap : : iterator i = sources . begin ( ) ; i ! = sources . end ( ) ; + + i ) {
DEBUG_TRACE ( DEBUG : : Destruction , string_compose ( " Dropping for source %1 ; pre-ref = %2 \n " , i - > second - > name ( ) , i - > second . use_count ( ) ) ) ;
i - > second - > drop_references ( ) ;
}
2009-11-27 19:49:04 -05:00
2014-01-19 16:37:01 -05:00
sources . clear ( ) ;
}
2009-11-27 19:49:04 -05:00
2009-12-04 14:09:08 -05:00
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
2019-03-19 00:14:00 -04:00
_playlists . reset ( ) ;
2009-12-04 14:09:08 -05:00
2015-04-28 16:18:30 -04:00
emit_thread_terminate ( ) ;
pthread_cond_destroy ( & _rt_emit_cond ) ;
pthread_mutex_destroy ( & _rt_emit_mutex ) ;
2016-04-23 16:11:48 -04:00
pthread_cond_destroy ( & _auto_connect_cond ) ;
pthread_mutex_destroy ( & _auto_connect_mutex ) ;
2014-04-28 19:58:24 -04:00
delete _scene_changer ; _scene_changer = 0 ;
2014-09-11 13:29:55 -04:00
delete midi_control_ui ; midi_control_ui = 0 ;
2014-04-28 19:58:24 -04:00
2013-09-16 10:23:37 -04:00
delete _mmc ; _mmc = 0 ;
delete _midi_ports ; _midi_ports = 0 ;
delete _locations ; _locations = 0 ;
2010-08-09 12:40:31 -04:00
2016-04-24 08:41:07 -04:00
delete midi_clock ;
2015-10-05 10:17:49 -04:00
2016-04-28 15:37:18 -04:00
/* clear event queue, the session is gone, nobody is interested in
* those anymore , but they do leak memory if not removed
*/
while ( ! immediate_events . empty ( ) ) {
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
SessionEvent * ev = immediate_events . front ( ) ;
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " Drop event: %1 \n " , enum_2_string ( ev - > type ) ) ) ;
immediate_events . pop_front ( ) ;
bool remove = true ;
bool del = true ;
switch ( ev - > type ) {
2021-11-04 00:58:17 -04:00
case SessionEvent : : AutoLoop :
case SessionEvent : : Skip :
case SessionEvent : : PunchIn :
case SessionEvent : : PunchOut :
case SessionEvent : : RangeStop :
case SessionEvent : : RangeLocate :
case SessionEvent : : RealTimeOperation :
process_rtop ( ev ) ;
del = false ;
break ;
default :
break ;
2016-04-28 15:37:18 -04:00
}
if ( remove ) {
del = del & & ! _remove_event ( ev ) ;
}
if ( del ) {
delete ev ;
}
}
2017-04-01 07:14:17 -04:00
{
/* unregister all dropped ports, process pending port deletion. */
// this may call ARDOUR::Port::drop ... jack_port_unregister ()
// jack1 cannot cope with removing ports while processing
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
AudioEngine : : instance ( ) - > clear_pending_port_deletions ( ) ;
}
2017-07-28 14:11:55 -04:00
DEBUG_TRACE ( DEBUG : : Destruction , " delete selection \n " ) ;
delete _selection ;
_selection = 0 ;
2019-09-20 00:33:43 -04:00
_transport_fsm - > stop ( ) ;
2019-09-17 20:26:03 -04:00
2009-11-27 19:49:04 -05:00
DEBUG_TRACE ( DEBUG : : Destruction , " Session::destroy() done \n " ) ;
2011-02-10 13:13:15 -05:00
2019-03-23 10:52:36 -04:00
# ifndef NDEBUG
Controllable : : dump_registry ( ) ;
# endif
2016-05-07 13:32:31 -04:00
BOOST_SHOW_POINTERS ( ) ;
2008-06-02 17:41:35 -04:00
}
2022-02-02 08:16:33 -05:00
void
Session : : block_processing ( )
{
g_atomic_int_set ( & _processing_prohibited , 1 ) ;
/* processing_blocked() is only checked at the beginning
* of the next cycle . So wait until any ongoing
* process - callback returns .
*/
Glib : : Threads : : Mutex : : Lock lm ( _engine . process_lock ( ) ) ;
/* latency callback may be in process, wait until it completed */
Glib : : Threads : : Mutex : : Lock lx ( _engine . latency_lock ( ) ) ;
}
2008-06-02 17:41:35 -04:00
void
2013-09-10 15:41:19 -04:00
Session : : setup_ltc ( )
2008-06-02 17:41:35 -04:00
{
2020-05-07 13:49:11 -04:00
_ltc_output_port = AudioEngine : : instance ( ) - > register_output_port ( DataType : : AUDIO , X_ ( " LTC-Out " ) , false , TransportGenerator ) ;
2015-10-05 10:17:49 -04:00
2020-05-07 13:49:11 -04:00
{
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
/* TODO use auto-connect thread */
reconnect_ltc_output ( ) ;
2008-06-02 17:41:35 -04:00
}
2013-09-10 15:41:19 -04:00
}
2008-06-02 17:41:35 -04:00
2013-09-10 15:41:19 -04:00
void
Session : : setup_click ( )
{
_clicking = false ;
2016-01-12 14:09:24 -05:00
2020-09-20 18:34:09 -04:00
boost : : shared_ptr < AutomationList > gl ( new AutomationList ( Evoral : : Parameter ( GainAutomation ) , Temporal : : AudioTime ) ) ;
2016-01-25 11:47:21 -05:00
boost : : shared_ptr < GainControl > gain_control = boost : : shared_ptr < GainControl > ( new GainControl ( * this , Evoral : : Parameter ( GainAutomation ) , gl ) ) ;
2016-01-12 14:09:24 -05:00
2013-10-23 12:02:49 -04:00
_click_io . reset ( new ClickIO ( * this , X_ ( " Click " ) ) ) ;
2016-01-12 14:09:24 -05:00
_click_gain . reset ( new Amp ( * this , _ ( " Fader " ) , gain_control , true ) ) ;
2013-09-10 15:41:19 -04:00
_click_gain - > activate ( ) ;
2013-10-02 16:17:22 -04:00
if ( state_tree ) {
2013-10-18 15:20:19 -04:00
setup_click_state ( state_tree - > root ( ) ) ;
} else {
setup_click_state ( 0 ) ;
2013-10-02 16:17:22 -04:00
}
2020-09-22 15:42:52 -04:00
click_io_resync_latency ( true ) ;
LatencyUpdated . connect_same_thread ( _click_io_connection , boost : : bind ( & Session : : click_io_resync_latency , this , _1 ) ) ;
2013-10-02 16:17:22 -04:00
}
void
2013-10-18 15:20:19 -04:00
Session : : setup_click_state ( const XMLNode * node )
2015-10-05 10:17:49 -04:00
{
2013-10-02 16:17:22 -04:00
const XMLNode * child = 0 ;
2015-10-05 10:17:49 -04:00
2013-10-18 15:20:19 -04:00
if ( node & & ( child = find_named_node ( * node , " Click " ) ) ! = 0 ) {
2015-10-05 10:17:49 -04:00
2013-09-10 15:41:19 -04:00
/* existing state for Click */
int c = 0 ;
2008-08-04 18:37:24 -04:00
2013-09-10 15:41:19 -04:00
if ( Stateful : : loading_state_version < 3000 ) {
c = _click_io - > set_state_2X ( * child - > children ( ) . front ( ) , Stateful : : loading_state_version , false ) ;
2012-10-25 15:46:23 -04:00
} else {
2013-09-10 15:41:19 -04:00
const XMLNodeList & children ( child - > children ( ) ) ;
XMLNodeList : : const_iterator i = children . begin ( ) ;
if ( ( c = _click_io - > set_state ( * * i , Stateful : : loading_state_version ) ) = = 0 ) {
+ + i ;
if ( i ! = children . end ( ) ) {
c = _click_gain - > set_state ( * * i , Stateful : : loading_state_version ) ;
2012-01-27 17:47:16 -05:00
}
2009-10-15 15:36:48 -04:00
}
2013-09-10 15:41:19 -04:00
}
2015-10-05 10:17:49 -04:00
2013-09-10 15:41:19 -04:00
if ( c = = 0 ) {
_clicking = Config - > get_clicking ( ) ;
2011-06-01 12:50:12 -04:00
2013-09-10 15:41:19 -04:00
} else {
2011-06-01 12:50:12 -04:00
2013-09-10 15:41:19 -04:00
error < < _ ( " could not setup Click I/O " ) < < endmsg ;
_clicking = false ;
}
2008-06-02 17:41:35 -04:00
2009-10-15 15:36:48 -04:00
2013-09-10 15:41:19 -04:00
} else {
2008-08-04 18:37:24 -04:00
2013-09-10 15:41:19 -04:00
/* default state for Click: dual-mono to first 2 physical outputs */
2008-06-02 17:41:35 -04:00
2013-09-10 15:41:19 -04:00
vector < string > outs ;
_engine . get_physical_outputs ( DataType : : AUDIO , outs ) ;
2010-07-27 21:40:36 -04:00
2013-09-10 15:41:19 -04:00
for ( uint32_t physport = 0 ; physport < 2 ; + + physport ) {
if ( outs . size ( ) > physport ) {
if ( _click_io - > add_port ( outs [ physport ] , this ) ) {
// relax, even though its an error
2010-07-27 21:40:36 -04:00
}
}
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2013-09-10 15:41:19 -04:00
if ( _click_io - > n_ports ( ) > ChanCount : : ZERO ) {
_clicking = Config - > get_clicking ( ) ;
}
2008-06-02 17:41:35 -04:00
}
2013-09-10 15:41:19 -04:00
}
2008-06-02 17:41:35 -04:00
2016-07-09 12:21:32 -04:00
void
2016-10-21 16:32:46 -04:00
Session : : get_physical_ports ( vector < string > & inputs , vector < string > & outputs , DataType type ,
MidiPortFlags include , MidiPortFlags exclude )
2016-07-09 12:21:32 -04:00
{
2016-10-21 16:32:46 -04:00
_engine . get_physical_inputs ( type , inputs , include , exclude ) ;
_engine . get_physical_outputs ( type , outputs , include , exclude ) ;
2016-07-09 12:21:32 -04:00
}
2013-09-10 15:41:19 -04:00
2012-01-17 20:30:44 -05:00
void
Session : : auto_connect_master_bus ( )
{
if ( ! _master_out | | ! Config - > get_auto_connect_standard_busses ( ) | | _monitor_out ) {
return ;
}
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
/* if requested auto-connect the outputs to the first N physical ports.
*/
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
uint32_t limit = _master_out - > n_outputs ( ) . n_total ( ) ;
vector < string > outputs [ DataType : : num_types ] ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
for ( uint32_t i = 0 ; i < DataType : : num_types ; + + i ) {
_engine . get_physical_outputs ( DataType ( DataType : : Symbol ( i ) ) , outputs [ i ] ) ;
}
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
for ( uint32_t n = 0 ; n < limit ; + + n ) {
boost : : shared_ptr < Port > p = _master_out - > output ( ) - > nth ( n ) ;
string connect_to ;
if ( outputs [ p - > type ( ) ] . size ( ) > n ) {
connect_to = outputs [ p - > type ( ) ] [ n ] ;
}
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( ! connect_to . empty ( ) & & p - > connected_to ( connect_to ) = = false ) {
if ( _master_out - > output ( ) - > connect ( p , connect_to , this ) ) {
error < < string_compose ( _ ( " cannot connect master output %1 to %2 " ) , n , connect_to )
< < endmsg ;
break ;
2009-05-13 17:34:09 -04:00
}
2008-06-02 17:41:35 -04:00
}
2012-01-17 20:30:44 -05:00
}
}
2008-06-02 17:41:35 -04:00
2020-07-20 16:54:54 -04:00
boost : : shared_ptr < GainControl >
Session : : master_volume ( ) const
{
if ( _master_out ) {
return _master_out - > volume_control ( ) ;
}
return boost : : shared_ptr < GainControl > ( ) ;
}
2012-01-17 20:30:44 -05:00
void
Session : : remove_monitor_section ( )
{
2019-09-25 15:02:31 -04:00
if ( ! _monitor_out ) {
2012-01-17 20:30:44 -05:00
return ;
}
2008-06-02 17:41:35 -04:00
2018-11-25 21:06:22 -05:00
/* allow deletion when session is unloaded */
2019-03-18 10:33:05 -04:00
if ( ! _engine . running ( ) & & ! deletion_in_progress ( ) ) {
2018-11-25 21:06:22 -05:00
error < < _ ( " Cannot remove monitor section while the engine is offline. " ) < < endmsg ;
return ;
}
2012-03-25 15:38:17 -04:00
/* force reversion to Solo-In-Place */
2012-01-17 20:30:44 -05:00
Config - > set_solo_control_is_listen_control ( false ) ;
2009-07-01 09:36:50 -04:00
2014-01-16 17:22:19 -05:00
/* if we are auditioning, cancel it ... this is a workaround
to a problem ( auditioning does not execute the process graph ,
which is needed to remove routes when using > 1 core for processing )
*/
cancel_audition ( ) ;
2019-03-17 12:35:10 -04:00
if ( ! deletion_in_progress ( ) ) {
2020-11-26 17:57:32 -05:00
setup_route_monitor_sends ( false , true ) ;
2021-02-07 08:02:50 -05:00
_engine . monitor_port ( ) . clear_ports ( true ) ;
2012-01-17 20:30:44 -05:00
}
2009-07-01 09:36:50 -04:00
2012-01-17 20:30:44 -05:00
remove_route ( _monitor_out ) ;
2019-03-17 12:04:45 -04:00
if ( deletion_in_progress ( ) ) {
2016-04-23 20:51:23 -04:00
return ;
}
2012-01-17 20:30:44 -05:00
auto_connect_master_bus ( ) ;
2014-01-16 17:22:19 -05:00
if ( auditioner ) {
auditioner - > connect ( ) ;
}
2016-04-23 20:51:23 -04:00
2019-03-01 10:45:42 -05:00
MonitorBusAddedOrRemoved ( ) ; /* EMIT SIGNAL */
2012-01-17 20:30:44 -05:00
}
2009-10-14 12:10:01 -04:00
2012-01-17 20:30:44 -05:00
void
Session : : add_monitor_section ( )
{
RouteList rl ;
2009-10-14 12:10:01 -04:00
2018-11-25 21:06:22 -05:00
if ( ! _engine . running ( ) ) {
error < < _ ( " Cannot create monitor section while the engine is offline. " ) < < endmsg ;
return ;
}
2019-09-25 15:02:31 -04:00
if ( _monitor_out | | ! _master_out ) {
2012-01-17 20:30:44 -05:00
return ;
}
2009-07-01 09:36:50 -04:00
2016-05-16 07:30:28 -04:00
boost : : shared_ptr < Route > r ( new Route ( * this , _ ( " Monitor " ) , PresentationInfo : : MonitorOut , DataType : : AUDIO ) ) ;
2009-07-01 09:36:50 -04:00
2012-01-17 20:30:44 -05:00
if ( r - > init ( ) ) {
return ;
}
2009-07-01 09:36:50 -04:00
2016-05-07 13:32:31 -04:00
BOOST_MARK_ROUTE ( r ) ;
2015-09-11 07:14:27 -04:00
try {
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2012-01-17 20:30:44 -05:00
r - > input ( ) - > ensure_io ( _master_out - > output ( ) - > n_ports ( ) , false , this ) ;
r - > output ( ) - > ensure_io ( _master_out - > output ( ) - > n_ports ( ) , false , this ) ;
2015-09-11 07:14:27 -04:00
} catch ( . . . ) {
error < < _ ( " Cannot create monitor section. 'Monitor' Port name is not unique. " ) < < endmsg ;
return ;
2012-01-17 20:30:44 -05:00
}
2009-07-01 09:36:50 -04:00
2012-01-17 20:30:44 -05:00
rl . push_back ( r ) ;
2020-05-19 12:02:23 -04:00
add_routes ( rl , false , false , 0 ) ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
assert ( _monitor_out ) ;
2009-07-01 09:36:50 -04:00
2012-01-17 20:30:44 -05:00
/* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else
are undefined , at best .
*/
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
uint32_t limit = _monitor_out - > n_inputs ( ) . n_audio ( ) ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( _master_out ) {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
/* connect the inputs to the master bus outputs. this
* represents a separate data feed from the internal sends from
* each route . as of jan 2011 , it allows the monitor section to
* conditionally ignore either the internal sends or the normal
* input feed , but we should really find a better way to do
* this , i think .
*/
_master_out - > output ( ) - > disconnect ( this ) ;
for ( uint32_t n = 0 ; n < limit ; + + n ) {
boost : : shared_ptr < AudioPort > p = _monitor_out - > input ( ) - > ports ( ) . nth_audio_port ( n ) ;
boost : : shared_ptr < AudioPort > o = _master_out - > output ( ) - > ports ( ) . nth_audio_port ( n ) ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( o ) {
string connect_to = o - > name ( ) ;
if ( _monitor_out - > input ( ) - > connect ( p , connect_to , this ) ) {
error < < string_compose ( _ ( " cannot connect control input %1 to %2 " ) , n , connect_to )
< < endmsg ;
break ;
}
}
}
}
2015-10-05 10:17:49 -04:00
2020-11-26 17:57:32 -05:00
/* if monitor section is not connected, connect it to physical outs */
2015-10-05 10:17:49 -04:00
2016-04-20 10:17:22 -04:00
if ( ( Config - > get_auto_connect_standard_busses ( ) | | Profile - > get_mixbus ( ) ) & & ! _monitor_out - > output ( ) - > connected ( ) ) {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( ! Config - > get_monitor_bus_preferred_bundle ( ) . empty ( ) ) {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
boost : : shared_ptr < Bundle > b = bundle_by_name ( Config - > get_monitor_bus_preferred_bundle ( ) ) ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( b ) {
2012-10-18 12:15:11 -04:00
_monitor_out - > output ( ) - > connect_ports_to_bundle ( b , true , this ) ;
2012-01-17 20:30:44 -05:00
} else {
warning < < string_compose ( _ ( " The preferred I/O for the monitor bus (%1) cannot be found " ) ,
Config - > get_monitor_bus_preferred_bundle ( ) )
< < endmsg ;
}
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
} else {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
/* Monitor bus is audio only */
2009-07-01 19:14:27 -04:00
2012-01-17 20:30:44 -05:00
vector < string > outputs [ DataType : : num_types ] ;
2011-03-14 17:53:10 -04:00
2012-01-17 20:30:44 -05:00
for ( uint32_t i = 0 ; i < DataType : : num_types ; + + i ) {
_engine . get_physical_outputs ( DataType ( DataType : : Symbol ( i ) ) , outputs [ i ] ) ;
}
2013-09-16 12:08:19 -04:00
uint32_t mod = outputs [ DataType : : AUDIO ] . size ( ) ;
uint32_t limit = _monitor_out - > n_outputs ( ) . get ( DataType : : AUDIO ) ;
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( mod ! = 0 ) {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
for ( uint32_t n = 0 ; n < limit ; + + n ) {
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
boost : : shared_ptr < Port > p = _monitor_out - > output ( ) - > ports ( ) . port ( DataType : : AUDIO , n ) ;
string connect_to ;
if ( outputs [ DataType : : AUDIO ] . size ( ) > ( n % mod ) ) {
connect_to = outputs [ DataType : : AUDIO ] [ n % mod ] ;
}
2015-10-05 10:17:49 -04:00
2012-01-17 20:30:44 -05:00
if ( ! connect_to . empty ( ) ) {
if ( _monitor_out - > output ( ) - > connect ( p , connect_to , this ) ) {
error < < string_compose (
_ ( " cannot connect control output %1 to %2 " ) ,
n , connect_to )
< < endmsg ;
break ;
2009-07-01 19:14:27 -04:00
}
2009-06-16 10:58:33 -04:00
}
}
2008-06-02 17:41:35 -04:00
}
}
}
2012-01-17 20:30:44 -05:00
/* Hold process lock while doing this so that we don't hear bits and
* pieces of audio as we work on each route .
*/
2015-10-05 10:17:49 -04:00
2020-11-26 17:57:32 -05:00
setup_route_monitor_sends ( true , true ) ;
2008-06-02 17:41:35 -04:00
2020-11-26 17:57:32 -05:00
MonitorBusAddedOrRemoved ( ) ; /* EMIT SIGNAL */
}
2011-09-21 13:28:13 -04:00
2020-11-26 17:57:32 -05:00
void
Session : : setup_route_monitor_sends ( bool enable , bool need_process_lock )
{
Glib : : Threads : : Mutex : : Lock lx ( AudioEngine : : instance ( ) - > process_lock ( ) , Glib : : Threads : : NOT_LOCK ) ;
if ( need_process_lock ) {
/* Hold process lock while doing this so that we don't hear bits and
* pieces of audio as we work on each route .
*/
lx . acquire ( ) ;
}
2011-09-21 13:28:13 -04:00
2012-01-17 20:30:44 -05:00
boost : : shared_ptr < RouteList > rls = routes . reader ( ) ;
2016-11-25 12:07:43 -05:00
ProcessorChangeBlocker pcb ( this , false /* XXX */ ) ;
2012-01-17 20:30:44 -05:00
for ( RouteList : : iterator x = rls - > begin ( ) ; x ! = rls - > end ( ) ; + + x ) {
2021-03-24 12:47:53 -04:00
if ( ( * x ) - > can_monitor ( ) ) {
2020-11-26 17:57:32 -05:00
if ( enable ) {
( * x ) - > enable_monitor_send ( ) ;
} else {
( * x ) - > remove_monitor_send ( ) ;
}
2012-01-17 20:30:44 -05:00
}
}
2014-01-16 17:22:19 -05:00
if ( auditioner ) {
auditioner - > connect ( ) ;
}
2008-06-02 17:41:35 -04:00
}
2015-04-21 09:18:10 -04:00
2020-11-26 17:57:32 -05:00
2015-04-21 09:18:10 -04:00
void
Session : : reset_monitor_section ( )
{
/* Process lock should be held by the caller.*/
2019-09-25 15:02:31 -04:00
if ( ! _monitor_out ) {
2015-04-21 09:18:10 -04:00
return ;
}
uint32_t limit = _master_out - > n_outputs ( ) . n_audio ( ) ;
/* connect the inputs to the master bus outputs. this
* represents a separate data feed from the internal sends from
* each route . as of jan 2011 , it allows the monitor section to
* conditionally ignore either the internal sends or the normal
* input feed , but we should really find a better way to do
* this , i think .
*/
_master_out - > output ( ) - > disconnect ( this ) ;
_monitor_out - > output ( ) - > disconnect ( this ) ;
2016-06-12 14:16:16 -04:00
// monitor section follow master bus - except midi
ChanCount mon_chn ( _master_out - > output ( ) - > n_ports ( ) ) ;
mon_chn . set_midi ( 0 ) ;
_monitor_out - > input ( ) - > ensure_io ( mon_chn , false , this ) ;
_monitor_out - > output ( ) - > ensure_io ( mon_chn , false , this ) ;
2015-04-21 09:18:10 -04:00
for ( uint32_t n = 0 ; n < limit ; + + n ) {
boost : : shared_ptr < AudioPort > p = _monitor_out - > input ( ) - > ports ( ) . nth_audio_port ( n ) ;
boost : : shared_ptr < AudioPort > o = _master_out - > output ( ) - > ports ( ) . nth_audio_port ( n ) ;
if ( o ) {
string connect_to = o - > name ( ) ;
if ( _monitor_out - > input ( ) - > connect ( p , connect_to , this ) ) {
error < < string_compose ( _ ( " cannot connect control input %1 to %2 " ) , n , connect_to )
< < endmsg ;
break ;
}
}
}
2020-11-26 17:57:32 -05:00
/* connect monitor section to physical outs */
2015-04-21 09:18:10 -04:00
if ( Config - > get_auto_connect_standard_busses ( ) ) {
if ( ! Config - > get_monitor_bus_preferred_bundle ( ) . empty ( ) ) {
boost : : shared_ptr < Bundle > b = bundle_by_name ( Config - > get_monitor_bus_preferred_bundle ( ) ) ;
if ( b ) {
_monitor_out - > output ( ) - > connect_ports_to_bundle ( b , true , this ) ;
} else {
warning < < string_compose ( _ ( " The preferred I/O for the monitor bus (%1) cannot be found " ) ,
Config - > get_monitor_bus_preferred_bundle ( ) )
< < endmsg ;
}
} else {
/* Monitor bus is audio only */
vector < string > outputs [ DataType : : num_types ] ;
for ( uint32_t i = 0 ; i < DataType : : num_types ; + + i ) {
_engine . get_physical_outputs ( DataType ( DataType : : Symbol ( i ) ) , outputs [ i ] ) ;
}
uint32_t mod = outputs [ DataType : : AUDIO ] . size ( ) ;
uint32_t limit = _monitor_out - > n_outputs ( ) . get ( DataType : : AUDIO ) ;
if ( mod ! = 0 ) {
for ( uint32_t n = 0 ; n < limit ; + + n ) {
boost : : shared_ptr < Port > p = _monitor_out - > output ( ) - > ports ( ) . port ( DataType : : AUDIO , n ) ;
string connect_to ;
if ( outputs [ DataType : : AUDIO ] . size ( ) > ( n % mod ) ) {
connect_to = outputs [ DataType : : AUDIO ] [ n % mod ] ;
}
if ( ! connect_to . empty ( ) ) {
if ( _monitor_out - > output ( ) - > connect ( p , connect_to , this ) ) {
error < < string_compose (
_ ( " cannot connect control output %1 to %2 " ) ,
n , connect_to )
< < endmsg ;
break ;
}
}
}
}
}
}
2020-11-26 17:57:32 -05:00
setup_route_monitor_sends ( true , false ) ;
2015-04-21 09:18:10 -04:00
}
2008-06-02 17:41:35 -04:00
2017-08-15 17:17:08 -04:00
int
Session : : add_master_bus ( ChanCount const & count )
{
if ( master_out ( ) ) {
return - 1 ;
}
RouteList rl ;
boost : : shared_ptr < Route > r ( new Route ( * this , _ ( " Master " ) , PresentationInfo : : MasterOut , DataType : : AUDIO ) ) ;
if ( r - > init ( ) ) {
return - 1 ;
}
BOOST_MARK_ROUTE ( r ) ;
{
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
r - > input ( ) - > ensure_io ( count , false , this ) ;
r - > output ( ) - > ensure_io ( count , false , this ) ;
}
rl . push_back ( r ) ;
2020-05-19 12:02:23 -04:00
add_routes ( rl , false , false , PresentationInfo : : max_order ) ;
2017-08-15 17:17:08 -04:00
return 0 ;
}
2008-06-02 17:41:35 -04:00
void
2010-03-22 17:35:35 -04:00
Session : : hookup_io ( )
2008-06-02 17:41:35 -04:00
{
/* stop graph reordering notifications from
causing resorts , etc .
*/
_state_of_the_state = StateOfTheState ( _state_of_the_state | InitialConnecting ) ;
2009-05-13 17:34:09 -04:00
if ( ! auditioner ) {
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
/* we delay creating the auditioner till now because
it makes its own connections to ports .
*/
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
try {
2011-02-22 16:15:42 -05:00
boost : : shared_ptr < Auditioner > a ( new Auditioner ( * this ) ) ;
2010-11-26 14:57:03 -05:00
if ( a - > init ( ) ) {
2011-02-22 16:15:42 -05:00
throw failed_constructor ( ) ;
2010-11-26 14:57:03 -05:00
}
2011-02-22 16:15:42 -05:00
auditioner = a ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
catch ( failed_constructor & err ) {
warning < < _ ( " cannot create Auditioner: no auditioning of regions possible " ) < < endmsg ;
}
}
/* load bundles, which we may have postponed earlier on */
if ( _bundle_xml_node ) {
load_bundles ( * _bundle_xml_node ) ;
delete _bundle_xml_node ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2021-11-03 17:06:16 -04:00
/* Get everything connected
2013-08-07 22:22:11 -04:00
*/
AudioEngine : : instance ( ) - > reconnect_ports ( ) ;
2021-11-03 17:15:54 -04:00
2021-11-03 17:03:40 -04:00
AfterConnect ( ) ; /* EMIT SIGNAL */
2008-06-02 17:41:35 -04:00
/* Anyone who cares about input state, wake up and do something */
IOConnectionsComplete ( ) ; /* EMIT SIGNAL */
_state_of_the_state = StateOfTheState ( _state_of_the_state & ~ InitialConnecting ) ;
/* now handle the whole enchilada as if it was one
graph reorder event .
*/
2019-10-28 19:23:54 -04:00
graph_reordered ( false ) ;
2008-06-02 17:41:35 -04:00
2009-07-01 09:36:50 -04:00
/* update the full solo state, which can't be
correctly determined on a per - route basis , but
needs the global overview that only the session
has .
*/
2008-06-02 17:41:35 -04:00
2009-07-01 09:36:50 -04:00
update_route_solo_state ( ) ;
2008-06-02 17:41:35 -04:00
}
void
2010-04-21 16:42:22 -04:00
Session : : track_playlist_changed ( boost : : weak_ptr < Track > wp )
2008-06-02 17:41:35 -04:00
{
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < Track > track = wp . lock ( ) ;
if ( ! track ) {
2009-11-10 18:54:04 -05:00
return ;
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < Playlist > playlist ;
2010-04-21 16:42:22 -04:00
if ( ( playlist = track - > playlist ( ) ) ! = 0 ) {
2011-01-19 12:38:56 -05:00
playlist - > RegionAdded . connect_same_thread ( * this , boost : : bind ( & Session : : playlist_region_added , this , _1 ) ) ;
playlist - > RangesMoved . connect_same_thread ( * this , boost : : bind ( & Session : : playlist_ranges_moved , this , _1 ) ) ;
2011-03-01 11:23:31 -05:00
playlist - > RegionsExtended . connect_same_thread ( * this , boost : : bind ( & Session : : playlist_regions_extended , this , _1 ) ) ;
2008-06-02 17:41:35 -04:00
}
}
bool
Session : : record_enabling_legal ( ) const
{
if ( Config - > get_all_safe ( ) ) {
return false ;
}
return true ;
}
2011-03-05 18:16:32 -05:00
void
Session : : set_track_monitor_input_status ( bool yn )
{
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2021-05-04 13:28:28 -04:00
2011-03-14 17:53:10 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < AudioTrack > tr = boost : : dynamic_pointer_cast < AudioTrack > ( * i ) ;
2016-04-08 16:49:47 -04:00
if ( tr & & tr - > rec_enable_control ( ) - > get_value ( ) ) {
2013-08-09 13:56:23 -04:00
tr - > request_input_monitoring ( yn ) ;
2011-03-14 17:53:10 -04:00
}
}
2011-03-05 18:16:32 -05:00
}
2008-06-02 17:41:35 -04:00
void
Session : : auto_punch_start_changed ( Location * location )
{
2020-09-20 18:34:09 -04:00
replace_event ( SessionEvent : : PunchIn , location - > start_sample ( ) ) ;
2008-06-02 17:41:35 -04:00
2017-09-29 20:45:13 -04:00
if ( get_record_enabled ( ) & & config . get_punch_in ( ) & & ! actively_recording ( ) ) {
2008-06-02 17:41:35 -04:00
/* capture start has been changed, so save new pending state */
save_state ( " " , true ) ;
}
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2020-02-26 20:36:16 -05:00
bool
Session : : punch_active ( ) const
{
if ( ! get_record_enabled ( ) ) {
return false ;
}
if ( ! _locations - > auto_punch_location ( ) ) {
return false ;
}
return config . get_punch_in ( ) | | config . get_punch_out ( ) ;
}
bool
Session : : punch_is_possible ( ) const
{
return g_atomic_int_get ( & _punch_or_loop ) ! = OnlyLoop ;
}
bool
Session : : loop_is_possible ( ) const
{
#if 0 /* maybe prevent looping even when not rolling ? */
if ( get_record_enabled ( ) & & punch_active ( ) ) {
return false ;
}
}
# endif
return g_atomic_int_get ( & _punch_or_loop ) ! = OnlyPunch ;
}
2020-02-27 16:16:12 -05:00
void
Session : : reset_punch_loop_constraint ( )
{
if ( g_atomic_int_get ( & _punch_or_loop ) = = NoConstraint ) {
return ;
}
g_atomic_int_set ( & _punch_or_loop , NoConstraint ) ;
PunchLoopConstraintChange ( ) ; /* EMIT SIGNAL */
}
2020-02-26 20:36:16 -05:00
bool
Session : : maybe_allow_only_loop ( bool play_loop ) {
if ( ! ( get_play_loop ( ) | | play_loop ) ) {
return false ;
}
bool rv = g_atomic_int_compare_and_exchange ( & _punch_or_loop , NoConstraint , OnlyLoop ) ;
2020-02-27 16:16:12 -05:00
if ( rv ) {
PunchLoopConstraintChange ( ) ; /* EMIT SIGNAL */
}
2020-02-26 20:36:16 -05:00
if ( rv | | loop_is_possible ( ) ) {
unset_punch ( ) ;
return true ;
}
return false ;
}
bool
Session : : maybe_allow_only_punch ( ) {
if ( ! punch_active ( ) ) {
return false ;
}
bool rv = g_atomic_int_compare_and_exchange ( & _punch_or_loop , NoConstraint , OnlyPunch ) ;
2020-02-27 16:16:12 -05:00
if ( rv ) {
PunchLoopConstraintChange ( ) ; /* EMIT SIGNAL */
}
2020-02-26 20:36:16 -05:00
return rv | | punch_is_possible ( ) ;
}
void
Session : : unset_punch ( )
{
/* used when enabling looping
* - > _punch_or_loop = OnlyLoop ;
*/
if ( config . get_punch_in ( ) ) {
config . set_punch_in ( false ) ;
}
if ( config . get_punch_out ( ) ) {
config . set_punch_out ( false ) ;
}
}
2008-06-02 17:41:35 -04:00
void
Session : : auto_punch_end_changed ( Location * location )
{
2020-09-20 18:34:09 -04:00
replace_event ( SessionEvent : : PunchOut , location - > end_sample ( ) ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
void
Session : : auto_punch_changed ( Location * location )
{
2017-09-29 20:45:13 -04:00
auto_punch_start_changed ( location ) ;
auto_punch_end_changed ( location ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
void
Session : : auto_loop_changed ( Location * location )
{
2019-11-23 15:37:00 -05:00
if ( ! location ) {
return ;
}
2020-09-20 18:34:09 -04:00
replace_event ( SessionEvent : : AutoLoop , location - > end_sample ( ) , location - > start_sample ( ) ) ;
2008-06-02 17:41:35 -04:00
2020-05-12 12:03:49 -04:00
if ( transport_rolling ( ) ) {
2019-11-23 01:41:56 -05:00
2020-02-26 14:15:00 -05:00
if ( get_play_loop ( ) ) {
2019-11-23 01:41:56 -05:00
2020-09-20 18:34:09 -04:00
if ( _transport_sample < location - > start_sample ( ) | | _transport_sample > location - > end_sample ( ) ) {
2019-12-05 15:00:24 -05:00
2019-12-08 19:00:30 -05:00
/* new loop range excludes current transport
* sample = > relocate to beginning of loop and roll .
*/
2019-12-05 15:00:24 -05:00
2019-12-08 19:00:30 -05:00
/* Set this so that when/if we have to stop the
* transport for a locate , we know that it is triggered
* by loop - changing , and we do not cancel play loop
*/
2019-11-01 23:14:40 -04:00
2020-05-13 14:34:22 -04:00
loop_changing = true ;
2020-09-20 18:34:09 -04:00
request_locate ( location - > start_sample ( ) , MustRoll ) ;
2019-11-01 23:14:40 -04:00
2019-12-08 19:00:30 -05:00
} else {
2019-11-01 23:14:40 -04:00
2020-05-12 13:30:12 -04:00
// schedule a locate-roll to refill the diskstreams at the
// previous loop end
2020-04-12 07:35:21 -04:00
/* schedule a buffer overwrite to refill buffers with the new loop. */
SessionEvent * ev = new SessionEvent ( SessionEvent : : OverwriteAll , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0 , 0.0 ) ;
ev - > overwrite = LoopChanged ;
queue_event ( ev ) ;
2019-12-08 19:00:30 -05:00
}
2008-06-02 17:41:35 -04:00
}
2019-11-01 23:14:40 -04:00
2015-06-11 09:05:46 -04:00
} else {
2008-06-02 17:41:35 -04:00
2019-11-01 23:14:40 -04:00
/* possibly move playhead if not rolling; if we are rolling we'll move
to the loop start on stop if that is appropriate .
*/
2015-01-16 17:50:10 -05:00
2019-11-01 23:14:40 -04:00
samplepos_t pos ;
2015-01-16 17:50:10 -05:00
2019-11-01 23:14:40 -04:00
if ( select_playhead_priority_target ( pos ) ) {
2020-09-20 18:34:09 -04:00
if ( pos = = location - > start_sample ( ) ) {
2019-11-01 23:14:40 -04:00
request_locate ( pos ) ;
}
2015-01-16 17:50:10 -05:00
}
}
2020-09-20 18:34:09 -04:00
last_loopend = location - > end_sample ( ) ;
2015-02-17 10:50:54 -05:00
set_dirty ( ) ;
2008-06-02 17:41:35 -04:00
}
void
Session : : set_auto_punch_location ( Location * location )
{
Location * existing ;
2010-08-09 12:40:31 -04:00
if ( ( existing = _locations - > auto_punch_location ( ) ) ! = 0 & & existing ! = location ) {
2009-12-17 13:24:23 -05:00
punch_connections . drop_connections ( ) ;
2008-06-02 17:41:35 -04:00
existing - > set_auto_punch ( false , this ) ;
2017-09-29 20:45:13 -04:00
clear_events ( SessionEvent : : PunchIn ) ;
2009-12-03 21:15:12 -05:00
clear_events ( SessionEvent : : PunchOut ) ;
2008-06-02 17:41:35 -04:00
auto_punch_location_changed ( 0 ) ;
}
set_dirty ( ) ;
if ( location = = 0 ) {
return ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( location - > end ( ) < = location - > start ( ) ) {
error < < _ ( " Session: you can't use that location for auto punch (start <= end) " ) < < endmsg ;
return ;
}
2009-12-17 13:24:23 -05:00
punch_connections . drop_connections ( ) ;
2008-08-04 18:37:24 -04:00
2014-10-24 12:18:40 -04:00
location - > StartChanged . connect_same_thread ( punch_connections , boost : : bind ( & Session : : auto_punch_start_changed , this , location ) ) ;
location - > EndChanged . connect_same_thread ( punch_connections , boost : : bind ( & Session : : auto_punch_end_changed , this , location ) ) ;
location - > Changed . connect_same_thread ( punch_connections , boost : : bind ( & Session : : auto_punch_changed , this , location ) ) ;
2008-06-02 17:41:35 -04:00
location - > set_auto_punch ( true , this ) ;
auto_punch_changed ( location ) ;
auto_punch_location_changed ( location ) ;
}
2014-12-09 17:17:47 -05:00
void
2020-09-20 18:34:09 -04:00
Session : : set_session_extents ( timepos_t const & start , timepos_t const & end )
2014-12-09 17:17:47 -05:00
{
if ( end < = start ) {
error < < _ ( " Session: you can't use that location for session start/end) " ) < < endmsg ;
return ;
}
2020-01-25 12:27:31 -05:00
Location * existing ;
if ( ( existing = _locations - > session_range_location ( ) ) = = 0 ) {
2020-09-20 18:34:09 -04:00
_session_range_location = new Location ( * this , start , end , _ ( " session " ) , Location : : IsSessionRange ) ;
2020-01-25 12:27:31 -05:00
_locations - > add ( _session_range_location ) ;
} else {
existing - > set ( start , end ) ;
}
2015-10-05 10:17:49 -04:00
2014-12-09 17:17:47 -05:00
set_dirty ( ) ;
}
2008-06-02 17:41:35 -04:00
void
Session : : set_auto_loop_location ( Location * location )
{
Location * existing ;
2010-08-09 12:40:31 -04:00
if ( ( existing = _locations - > auto_loop_location ( ) ) ! = 0 & & existing ! = location ) {
2009-12-17 13:24:23 -05:00
loop_connections . drop_connections ( ) ;
2008-06-02 17:41:35 -04:00
existing - > set_auto_loop ( false , this ) ;
2020-09-20 18:34:09 -04:00
remove_event ( existing - > end_sample ( ) , SessionEvent : : AutoLoop ) ;
2008-06-02 17:41:35 -04:00
auto_loop_location_changed ( 0 ) ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
if ( location = = 0 ) {
return ;
}
if ( location - > end ( ) < = location - > start ( ) ) {
2013-01-15 09:15:01 -05:00
error < < _ ( " You cannot use this location for auto-loop because it has zero or negative length " ) < < endmsg ;
2008-06-02 17:41:35 -04:00
return ;
}
2020-09-20 18:34:09 -04:00
last_loopend = location - > end_sample ( ) ;
2008-08-04 18:37:24 -04:00
2009-12-17 13:24:23 -05:00
loop_connections . drop_connections ( ) ;
2008-08-04 18:37:24 -04:00
2014-10-24 12:18:40 -04:00
location - > StartChanged . connect_same_thread ( loop_connections , boost : : bind ( & Session : : auto_loop_changed , this , location ) ) ;
location - > EndChanged . connect_same_thread ( loop_connections , boost : : bind ( & Session : : auto_loop_changed , this , location ) ) ;
location - > Changed . connect_same_thread ( loop_connections , boost : : bind ( & Session : : auto_loop_changed , this , location ) ) ;
2015-05-10 13:15:46 -04:00
location - > FlagsChanged . connect_same_thread ( loop_connections , boost : : bind ( & Session : : auto_loop_changed , this , location ) ) ;
2008-06-02 17:41:35 -04:00
location - > set_auto_loop ( true , this ) ;
2020-02-26 14:15:00 -05:00
if ( Config - > get_loop_is_mode ( ) & & get_play_loop ( ) ) {
/* set all tracks to use internal looping */
set_track_loop ( true ) ;
2015-05-10 13:15:46 -04:00
}
2015-10-04 14:51:05 -04:00
2008-06-02 17:41:35 -04:00
/* take care of our stuff first */
auto_loop_changed ( location ) ;
/* now tell everyone else */
auto_loop_location_changed ( location ) ;
}
2015-02-05 16:21:01 -05:00
void
Session : : update_marks ( Location * )
{
set_dirty ( ) ;
}
2014-10-24 12:18:40 -04:00
2015-02-05 16:21:01 -05:00
void
Session : : update_skips ( Location * loc , bool consolidate )
{
2015-05-10 13:19:02 -04:00
if ( _ignore_skips_updates ) {
return ;
}
2015-10-05 10:17:49 -04:00
2015-02-05 16:21:01 -05:00
Locations : : LocationList skips ;
2014-10-24 12:18:40 -04:00
2019-04-13 13:19:29 -04:00
if ( consolidate ) {
PBD : : Unwinder < bool > uw ( _ignore_skips_updates , true ) ;
consolidate_skips ( loc ) ;
}
2014-10-24 12:18:40 -04:00
2015-02-05 16:21:01 -05:00
sync_locations_to_skips ( ) ;
2015-10-04 14:51:05 -04:00
2015-02-05 16:21:01 -05:00
set_dirty ( ) ;
2008-06-02 17:41:35 -04:00
}
2015-02-05 16:21:01 -05:00
void
2014-10-24 12:18:40 -04:00
Session : : consolidate_skips ( Location * loc )
2008-06-02 17:41:35 -04:00
{
2019-04-13 13:19:29 -04:00
Locations : : LocationList all_locations = _locations - > list ( ) ;
2014-10-24 12:18:40 -04:00
2019-04-13 13:19:29 -04:00
for ( Locations : : LocationList : : iterator l = all_locations . begin ( ) ; l ! = all_locations . end ( ) ; ) {
2014-10-24 12:18:40 -04:00
2019-04-13 13:19:29 -04:00
if ( ! ( * l ) - > is_skip ( ) ) {
+ + l ;
continue ;
}
2014-10-24 12:18:40 -04:00
2019-04-13 13:19:29 -04:00
/* don't test against self */
2014-10-24 12:18:40 -04:00
2019-04-13 13:19:29 -04:00
if ( * l = = loc ) {
+ + l ;
continue ;
}
2015-10-04 14:51:05 -04:00
2020-09-20 18:34:09 -04:00
switch ( Temporal : : coverage_exclusive_ends ( ( * l ) - > start ( ) , ( * l ) - > end ( ) , loc - > start ( ) , loc - > end ( ) ) ) {
case Temporal : : OverlapInternal :
case Temporal : : OverlapExternal :
case Temporal : : OverlapStart :
case Temporal : : OverlapEnd :
2019-04-13 13:19:29 -04:00
/* 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 ;
2014-10-24 12:18:40 -04:00
2020-09-20 18:34:09 -04:00
case Temporal : : OverlapNone :
2019-04-13 13:19:29 -04:00
+ + l ;
break ;
}
}
2015-02-05 16:21:01 -05:00
}
2014-10-24 12:18:40 -04:00
2015-02-05 16:21:01 -05:00
void
Session : : sync_locations_to_skips ( )
{
/* This happens asynchronously (in the audioengine thread). After the clear is done, we will call
* Session : : _sync_locations_to_skips ( ) from the audioengine thread .
*/
clear_events ( SessionEvent : : Skip , boost : : bind ( & Session : : _sync_locations_to_skips , this ) ) ;
2008-06-02 17:41:35 -04:00
}
void
2015-02-05 16:21:01 -05:00
Session : : _sync_locations_to_skips ( )
2008-06-02 17:41:35 -04:00
{
2015-02-05 16:21:01 -05:00
/* called as a callback after existing Skip events have been cleared from a realtime audioengine thread */
Locations : : LocationList const & locs ( _locations - > list ( ) ) ;
2008-06-02 17:41:35 -04:00
2015-02-05 16:21:01 -05:00
for ( Locations : : LocationList : : const_iterator i = locs . begin ( ) ; i ! = locs . end ( ) ; + + i ) {
2015-10-05 10:17:49 -04:00
2014-10-24 12:18:40 -04:00
Location * location = * i ;
2015-10-05 10:17:49 -04:00
2015-02-05 16:21:01 -05:00
if ( location - > is_skip ( ) & & location - > is_skipping ( ) ) {
2020-09-20 18:34:09 -04:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : Skip , SessionEvent : : Add , location - > start_sample ( ) , location - > end_sample ( ) , 1.0 ) ;
2014-10-24 12:18:40 -04:00
queue_event ( ev ) ;
}
}
}
2008-06-02 17:41:35 -04:00
2015-02-05 16:21:01 -05:00
2014-10-24 12:18:40 -04:00
void
Session : : location_added ( Location * location )
{
2019-04-13 13:19:29 -04:00
if ( location - > is_auto_punch ( ) ) {
set_auto_punch_location ( location ) ;
}
2008-06-02 17:41:35 -04:00
2019-04-13 13:19:29 -04:00
if ( location - > is_auto_loop ( ) ) {
set_auto_loop_location ( location ) ;
}
2015-10-04 14:51:05 -04:00
2019-04-13 13:19:29 -04:00
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 ;
}
2008-08-04 18:37:24 -04:00
2019-04-13 13:19:29 -04:00
if ( location - > is_mark ( ) ) {
/* listen for per-location signals that require us to do any * global updates for marks */
2015-05-08 12:20:25 -04:00
2019-04-13 13:19:29 -04:00
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 ) ) ;
2021-01-25 13:40:48 -05:00
location - > TimeDomainChanged . connect_same_thread ( skip_update_connections , boost : : bind ( & Session : : update_marks , this , location ) ) ;
2019-04-13 13:19:29 -04:00
}
2017-01-26 11:07:29 -05:00
if ( location - > is_range_marker ( ) ) {
2019-04-13 13:19:29 -04:00
/* listen for per-location signals that require us to do any * global updates for marks */
2017-01-26 11:07:29 -05:00
2019-04-13 13:19:29 -04:00
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 ) ) ;
2021-01-25 13:40:48 -05:00
location - > TimeDomainChanged . connect_same_thread ( skip_update_connections , boost : : bind ( & Session : : update_marks , this , location ) ) ;
2019-04-13 13:19:29 -04:00
}
2015-05-08 12:20:25 -04:00
2019-04-13 13:19:29 -04:00
if ( location - > is_skip ( ) ) {
/* listen for per-location signals that require us to update skip-locate events */
2008-06-02 17:41:35 -04:00
2019-04-13 13:19:29 -04:00
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 ) ) ;
2021-01-25 13:40:48 -05:00
location - > TimeDomainChanged . connect_same_thread ( skip_update_connections , boost : : bind ( & Session : : update_marks , this , location ) ) ;
2014-09-16 21:32:58 -04:00
2019-04-13 13:19:29 -04:00
update_skips ( location , true ) ;
}
2015-10-04 14:51:05 -04:00
2014-10-24 12:18:40 -04:00
set_dirty ( ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2014-09-16 21:32:58 -04:00
void
2014-10-24 12:18:40 -04:00
Session : : location_removed ( Location * location )
2014-09-16 21:32:58 -04:00
{
2018-10-05 15:47:43 -04:00
if ( location - > is_auto_loop ( ) ) {
set_auto_loop_location ( 0 ) ;
2020-02-26 14:15:00 -05:00
if ( ! get_play_loop ( ) ) {
2018-10-05 15:48:41 -04:00
set_track_loop ( false ) ;
}
unset_play_loop ( ) ;
2018-10-05 15:47:43 -04:00
}
2015-10-04 14:51:05 -04:00
2018-10-05 15:47:43 -04:00
if ( location - > is_auto_punch ( ) ) {
set_auto_punch_location ( 0 ) ;
}
2014-09-16 21:32:58 -04:00
2018-10-05 15:47:43 -04:00
if ( location - > is_session_range ( ) ) {
/* this is never supposed to happen */
error < < _ ( " programming error: session range removed! " ) < < endl ;
}
2014-09-16 21:32:58 -04:00
2018-10-05 15:47:43 -04:00
if ( location - > is_skip ( ) ) {
2015-10-04 14:51:05 -04:00
2018-10-05 15:47:43 -04:00
update_skips ( location , false ) ;
}
2014-09-16 21:32:58 -04:00
2014-10-24 12:18:40 -04:00
set_dirty ( ) ;
}
2014-09-16 21:32:58 -04:00
2014-10-24 12:18:40 -04:00
void
Session : : locations_changed ( )
{
2019-04-13 13:19:29 -04:00
_locations - > apply ( * this , & Session : : _locations_changed ) ;
2014-10-24 12:18:40 -04:00
}
void
Session : : _locations_changed ( const Locations : : LocationList & locations )
{
2019-04-13 13:19:29 -04:00
/* There was some mass-change in the Locations object.
2019-09-17 20:26:03 -04:00
*
2019-04-13 13:19:29 -04:00
* 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 .
*/
2015-10-05 10:17:49 -04:00
2015-05-10 13:44:23 -04:00
{
PBD : : Unwinder < bool > protect_ignore_skip_updates ( _ignore_skips_updates , true ) ;
for ( Locations : : LocationList : : const_iterator i = locations . begin ( ) ; i ! = locations . end ( ) ; + + i ) {
location_added ( * i ) ;
}
}
2015-10-05 10:17:49 -04:00
2015-05-10 13:44:23 -04:00
update_skips ( NULL , false ) ;
2014-09-16 21:32:58 -04:00
}
2008-06-02 17:41:35 -04:00
void
Session : : enable_record ( )
{
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 0.0 & & _transport_fsm - > transport_speed ( ) ! = 1.0 ) {
2011-08-10 16:21:18 -04:00
/* no recording at anything except normal speed */
2011-08-09 17:17:55 -04:00
return ;
}
2010-11-26 14:57:03 -05:00
while ( 1 ) {
RecordState rs = ( RecordState ) g_atomic_int_get ( & _record_status ) ;
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
if ( rs = = Recording ) {
break ;
}
2015-10-05 10:17:49 -04:00
2010-11-26 14:57:03 -05:00
if ( g_atomic_int_compare_and_exchange ( & _record_status , rs , Recording ) ) {
2011-06-01 12:50:12 -04:00
2017-09-18 12:39:17 -04:00
_last_record_location = _transport_sample ;
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( MIDI : : MachineControl : : cmdRecordStrobe ) ) ;
2011-06-01 12:50:12 -04:00
2020-04-07 17:13:57 -04:00
if ( Config - > get_recording_resets_xrun_count ( ) ) {
reset_xrun_count ( ) ;
}
2010-11-26 14:57:03 -05:00
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring & & config . get_auto_input ( ) ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( true ) ;
2010-11-26 14:57:03 -05:00
}
2011-06-01 12:50:12 -04:00
2021-02-05 16:18:21 -05:00
_capture_duration = 0 ;
_capture_xruns = 0 ;
2010-11-26 14:57:03 -05:00
RecordStateChanged ( ) ;
break ;
}
}
2008-06-02 17:41:35 -04:00
}
2016-01-19 15:00:03 -05:00
void
Session : : set_all_tracks_record_enabled ( bool enable )
{
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2016-05-27 16:11:47 -04:00
set_controls ( route_list_to_control_list ( rl , & Stripable : : rec_enable_control ) , enable , Controllable : : NoGroup ) ;
2016-01-19 15:00:03 -05:00
}
2008-06-02 17:41:35 -04:00
void
Session : : disable_record ( bool rt_context , bool force )
{
RecordState rs ;
if ( ( rs = ( RecordState ) g_atomic_int_get ( & _record_status ) ) ! = Disabled ) {
2015-09-22 13:18:08 -04:00
if ( ! Config - > get_latched_record_enable ( ) | | force ) {
2008-06-02 17:41:35 -04:00
g_atomic_int_set ( & _record_status , Disabled ) ;
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( MIDI : : MachineControl : : cmdRecordExit ) ) ;
2008-06-02 17:41:35 -04:00
} else {
if ( rs = = Recording ) {
g_atomic_int_set ( & _record_status , Enabled ) ;
}
}
2009-05-13 20:13:27 -04:00
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring & & config . get_auto_input ( ) ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( false ) ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
RecordStateChanged ( ) ; /* emit signal */
}
}
void
Session : : step_back_from_record ( )
{
2010-04-12 18:35:06 -04:00
if ( g_atomic_int_compare_and_exchange ( & _record_status , Recording , Enabled ) ) {
2008-06-02 17:41:35 -04:00
2009-05-13 20:13:27 -04:00
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring & & config . get_auto_input ( ) ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( false ) ;
2008-06-02 17:41:35 -04:00
}
2011-10-21 19:12:19 -04:00
RecordStateChanged ( ) ; /* emit signal */
2008-06-02 17:41:35 -04:00
}
}
void
2017-01-01 18:26:21 -05:00
Session : : maybe_enable_record ( bool rt_context )
2008-06-02 17:41:35 -04:00
{
2010-11-26 14:57:03 -05:00
if ( _step_editors > 0 ) {
return ;
}
2010-07-24 12:40:56 -04:00
2008-06-02 17:41:35 -04:00
g_atomic_int_set ( & _record_status , Enabled ) ;
2020-07-17 11:08:59 -04:00
// TODO make configurable, perhaps capture-buffer-seconds dependnet?
bool quick_start = true ;
/* Save pending state of which sources the next record will use,
2017-01-01 18:26:21 -05:00
* which gives us some chance of recovering from a crash during the record .
*/
2021-05-03 19:35:34 -04:00
if ( ! rt_context & & ( ! quick_start | | _transport_fsm - > transport_speed ( ) = = 0 ) ) {
2017-01-01 18:26:21 -05:00
save_state ( " " , true ) ;
}
2011-06-01 12:50:12 -04:00
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 0 ) {
2020-02-26 20:36:16 -05:00
maybe_allow_only_punch ( ) ;
2017-09-29 15:01:50 -04:00
if ( ! config . get_punch_in ( ) ) {
2008-06-02 17:41:35 -04:00
enable_record ( ) ;
2008-08-04 18:37:24 -04:00
}
2020-07-17 11:08:59 -04:00
/* When rolling, start recording immediately.
* Do not wait for . pending state save to complete
* because that may take some time ( up to a second
* for huge sessions ) .
*
* This is potentially dangerous ! ! If a crash happens
* while recording before the . pending save completed ,
* the data until then may be lost or overwritten .
* ( However disk - writer buffers are usually longer ,
* compared to the time it takes to save a session .
* disk I / O may not be a bottleneck either . Except
* perhaps plugin - state saves taking a lock .
*/
if ( ! rt_context & & quick_start ) {
save_state ( " " , true ) ;
}
2008-06-02 17:41:35 -04:00
} else {
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( MIDI : : MachineControl : : cmdRecordPause ) ) ;
2008-06-02 17:41:35 -04:00
RecordStateChanged ( ) ; /* EMIT SIGNAL */
}
set_dirty ( ) ;
}
2017-09-18 12:39:17 -04:00
samplepos_t
Session : : audible_sample ( bool * latent_locate ) const
2008-06-02 17:41:35 -04:00
{
2017-03-31 06:59:44 -04:00
if ( latent_locate ) {
* latent_locate = false ;
}
2008-06-02 17:41:35 -04:00
2017-09-28 00:31:12 -04:00
samplepos_t ret ;
2013-09-19 17:34:23 -04:00
if ( synced_to_engine ( ) ) {
2014-10-12 23:00:14 -04:00
/* Note: this is basically just sync-to-JACK */
2017-09-18 12:39:17 -04:00
ret = _engine . transport_sample ( ) ;
2008-06-02 17:41:35 -04:00
} else {
2017-09-18 12:39:17 -04:00
ret = _transport_sample ;
2008-06-02 17:41:35 -04:00
}
2009-10-14 12:10:01 -04:00
2017-09-28 00:31:12 -04:00
assert ( ret > = 0 ) ;
2008-12-12 09:43:24 -05:00
2017-09-28 00:31:12 -04:00
if ( ! transport_rolling ( ) ) {
return ret ;
}
2008-12-12 09:43:24 -05:00
2017-09-28 00:31:12 -04:00
#if 0 // TODO looping
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) > 0.0f ) {
2017-09-28 00:31:12 -04:00
if ( play_loop & & have_looped ) {
/* the play-position wrapped at the loop-point
* ardour is already playing the beginning of the loop ,
* but due to playback latency , the " audible frame "
* is still at the end of the loop .
*/
Location * location = _locations - > auto_loop_location ( ) ;
sampleoffset_t lo = location - > start ( ) - ret ;
if ( lo > 0 ) {
ret = location - > end ( ) - lo ;
if ( latent_locate ) {
* latent_locate = true ;
2016-07-25 13:46:19 -04:00
}
2009-10-14 12:10:01 -04:00
}
2008-12-12 09:43:24 -05:00
}
2021-05-03 19:35:34 -04:00
} else if ( _transport_fsm - > transport_speed ( ) < 0.0f ) {
2017-09-28 00:31:12 -04:00
/* XXX wot? no backward looping? */
2008-06-02 17:41:35 -04:00
}
2017-09-28 00:31:12 -04:00
# endif
2008-06-02 17:41:35 -04:00
2017-09-18 12:39:17 -04:00
return std : : max ( ( samplepos_t ) 0 , ret ) ;
2008-06-02 17:41:35 -04:00
}
2017-09-18 12:39:17 -04:00
samplecnt_t
Session : : preroll_samples ( samplepos_t pos ) const
2017-01-19 06:39:15 -05:00
{
const float pr = Config - > get_preroll_seconds ( ) ;
if ( pos > = 0 & & pr < 0 ) {
2020-11-27 14:40:58 -05:00
Temporal : : TempoMetric const & metric ( TempoMap : : use ( ) - > metric_at ( pos ) ) ;
2020-11-11 11:31:52 -05:00
return metric . samples_per_bar ( sample_rate ( ) ) * - pr ;
2017-01-19 06:39:15 -05:00
}
if ( pr < 0 ) {
return 0 ;
}
2017-09-18 12:39:17 -04:00
return pr * sample_rate ( ) ;
2017-01-19 06:39:15 -05:00
}
2008-06-02 17:41:35 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : set_sample_rate ( samplecnt_t frames_per_second )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
/** \fn void Session::set_sample_size(samplecnt_t)
2008-08-04 18:37:24 -04:00
the AudioEngine object that calls this guarantees
2008-06-02 17:41:35 -04:00
that it will not be called while we are also in
: : process ( ) . Its fine to do things that block
here .
*/
2017-09-18 12:39:17 -04:00
if ( _base_sample_rate = = 0 ) {
_base_sample_rate = frames_per_second ;
2016-04-18 08:50:09 -04:00
}
2017-09-18 12:39:17 -04:00
else if ( _base_sample_rate ! = frames_per_second & & frames_per_second ! = _nominal_sample_rate ) {
NotifyAboutSampleRateMismatch ( _base_sample_rate , frames_per_second ) ;
2016-04-18 09:16:27 -04:00
}
2017-09-18 12:39:17 -04:00
_nominal_sample_rate = frames_per_second ;
2008-06-02 17:41:35 -04:00
sync_time_vars ( ) ;
clear_clicks ( ) ;
2015-05-12 10:16:26 -04:00
reset_write_sources ( false ) ;
2015-10-05 10:17:49 -04:00
2019-11-23 01:41:56 -05:00
DiskReader : : alloc_loop_declick ( nominal_sample_rate ( ) ) ;
Location * loc = _locations - > auto_loop_location ( ) ;
DiskReader : : reset_loop_declick ( loc , nominal_sample_rate ( ) ) ;
2008-06-02 17:41:35 -04:00
// XXX we need some equivalent to this, somehow
// SndFileSource::setup_standard_crossfades (frames_per_second);
set_dirty ( ) ;
/* XXX need to reset/reinstantiate all LADSPA plugins */
}
void
2010-12-03 17:26:29 -05:00
Session : : set_block_size ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2008-08-04 18:37:24 -04:00
/* the AudioEngine guarantees
2020-04-26 15:34:26 -04:00
* that it will not be called while we are also in
* : : process ( ) . It is therefore fine to do things that block
* here .
*/
current_block_size = nframes ;
2008-06-02 17:41:35 -04:00
2020-04-26 15:34:26 -04:00
ensure_buffers ( ) ;
2008-06-02 17:41:35 -04:00
2020-05-07 14:38:09 -04:00
foreach_route ( & Route : : set_block_size , nframes ) ;
2020-04-26 15:34:26 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " Session::set_block_size -> update worst i/o latency \n " ) ;
/* when this is called from the auto-connect thread, the process-lock is held */
Glib : : Threads : : Mutex : : Lock lx ( _update_latency_lock ) ;
set_worst_output_latency ( ) ;
set_worst_input_latency ( ) ;
2008-06-02 17:41:35 -04:00
}
void
Session : : resort_routes ( )
{
/* don't do anything here with signals emitted
2011-10-26 11:14:53 -04:00
by Routes during initial setup or while we
are being destroyed .
2008-06-02 17:41:35 -04:00
*/
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) ) {
2021-08-27 00:42:56 -04:00
/* drop any references during delete */
GraphEdges edges ;
_current_route_graph = edges ;
2008-06-02 17:41:35 -04:00
return ;
}
2015-10-15 07:49:35 -04:00
if ( _route_deletion_in_progress ) {
return ;
}
2008-06-02 17:41:35 -04:00
{
RCUWriter < RouteList > writer ( routes ) ;
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < RouteList > r = writer . get_copy ( ) ;
2008-06-02 17:41:35 -04:00
resort_routes_using ( r ) ;
/* writer goes out of scope and forces update */
}
2010-04-26 23:10:53 -04:00
# ifndef NDEBUG
2016-04-24 11:35:21 -04:00
if ( DEBUG_ENABLED ( DEBUG : : Graph ) ) {
2022-04-27 20:44:08 -04:00
DEBUG_TRACE ( DEBUG : : Graph , " ---- Session::resort_routes ---- \n " ) ;
for ( auto const & i : * routes . reader ( ) ) {
DEBUG_TRACE ( DEBUG : : Graph , string_compose ( " %1 fed by ... \n " , i - > name ( ) ) ) ;
for ( auto const & f : i - > signal_sources ( ) ) {
DEBUG_TRACE ( DEBUG : : Graph , string_compose ( " \t %1 \n " , f - > graph_node_name ( ) ) ) ;
2010-11-26 14:57:03 -05:00
}
}
2022-04-27 20:44:08 -04:00
DEBUG_TRACE ( DEBUG : : Graph , " ---- EOF ---- \n " ) ;
2010-11-26 14:57:03 -05:00
}
2010-04-26 23:10:53 -04:00
# endif
2008-06-02 17:41:35 -04:00
}
2011-11-07 11:01:46 -05:00
2011-11-09 12:44:39 -05:00
/** This is called whenever we need to rebuild the graph of how we will process
* routes .
* @ param r List of routes , in any order .
*/
2008-06-02 17:41:35 -04:00
void
2010-11-29 17:07:42 -05:00
Session : : resort_routes_using ( boost : : shared_ptr < RouteList > r )
2008-06-02 17:41:35 -04:00
{
2022-04-27 20:44:08 -04:00
GraphNodeList gnl ;
for ( auto const & rt : * r ) {
gnl . push_back ( rt ) ;
}
2010-04-26 23:10:53 -04:00
2022-04-27 20:44:08 -04:00
if ( rechain_process_graph ( gnl ) ) {
r - > clear ( ) ;
for ( auto const & nd : gnl ) {
r - > push_back ( boost : : dynamic_pointer_cast < Route > ( nd ) ) ;
2008-06-02 17:41:35 -04:00
}
}
2008-08-04 18:37:24 -04:00
2022-04-27 20:44:08 -04:00
# ifndef NDEBUG
DEBUG_TRACE ( DEBUG : : Graph , " Routes resorted, order follows: \n " ) ;
for ( auto const & i : * r ) {
DEBUG_TRACE ( DEBUG : : Graph , string_compose ( " \t %1 (presentation order %2) \n " , i - > name ( ) , i - > presentation_info ( ) . order ( ) ) ) ;
}
# endif
}
2015-10-05 10:17:49 -04:00
2022-04-27 20:44:08 -04:00
bool
Session : : rechain_process_graph ( GraphNodeList & g )
{
/* This may be called from the GUI thread (concurrrently with processing),
* when a user adds / removes routes .
*
* Or it may be called from the engine when connections are changed .
* In that case processing is blocked until the graph change is handled .
*/
GraphEdges edges ;
if ( topological_sort ( g , edges ) ) {
2011-11-09 12:44:39 -05:00
/* We got a satisfactory topological sort, so there is no feedback;
2022-04-27 20:44:08 -04:00
* use this new graph .
*
* Note : the process graph chain does not require a
* topologically - sorted list , but hey ho .
*/
2012-01-22 07:28:49 -05:00
if ( _process_graph ) {
2022-04-27 20:44:08 -04:00
_process_graph - > rechain ( g , edges ) ;
2012-01-22 07:28:49 -05:00
}
2015-10-05 10:17:49 -04:00
2011-11-09 12:44:39 -05:00
_current_route_graph = edges ;
2011-11-11 08:52:27 -05:00
SuccessfulGraphSort ( ) ; /* EMIT SIGNAL */
2022-04-27 20:44:08 -04:00
return true ;
2011-11-09 12:44:39 -05:00
}
2022-04-27 20:44:08 -04:00
/* The topological sort failed, so we have a problem. Tell everyone
* and stick to the old graph ; this will continue to be processed , so
* until the feedback is fixed , what is played back will not quite
* reflect what is actually connected .
*/
FeedbackDetected ( ) ; /* EMIT SIGNAL */
return false ;
2008-06-02 17:41:35 -04:00
}
2011-02-26 20:59:04 -05:00
/** Find a route name starting with \a base, maybe followed by the
* lowest \ a id . \ a id will always be added if \ a definitely_add_number
* is true on entry ; otherwise it will only be added if required
* to make the name unique .
2010-02-23 17:45:07 -05:00
*
2011-02-26 20:59:04 -05:00
* Names are constructed like e . g . " Audio 3 " for base = " Audio " and id = 3.
* The available route name with the lowest ID will be used , and \ a id
* will be set to the ID .
2010-02-23 17:45:07 -05:00
*
2011-02-26 20:59:04 -05:00
* \ return false if a route name could not be found , and \ a track_name
* and \ a id do not reflect a free route name .
2010-02-23 17:45:07 -05:00
*/
bool
2015-05-12 10:43:37 -04:00
Session : : find_route_name ( string const & base , uint32_t & id , string & name , bool definitely_add_number )
2010-02-23 17:45:07 -05:00
{
2015-09-10 16:27:14 -04:00
/* the base may conflict with ports that do not belong to existing
routes , but hidden objects like the click track . So check port names
before anything else .
*/
2015-10-05 10:17:49 -04:00
2016-12-10 18:03:44 -05:00
for ( map < string , bool > : : const_iterator reserved = reserved_io_names . begin ( ) ; reserved ! = reserved_io_names . end ( ) ; + + reserved ) {
if ( base = = reserved - > first ) {
2015-11-07 22:15:59 -05:00
/* Check if this reserved name already exists, and if
so , disallow it without a numeric suffix .
*/
2016-12-10 18:03:44 -05:00
if ( ! reserved - > second | | route_by_name ( reserved - > first ) ) {
2015-11-07 22:15:59 -05:00
definitely_add_number = true ;
if ( id < 1 ) {
id = 1 ;
}
2015-09-10 16:27:14 -04:00
}
2015-09-14 10:47:21 -04:00
break ;
}
2015-09-10 16:27:14 -04:00
}
2015-10-05 10:17:49 -04:00
2016-11-07 14:08:00 -05:00
/* if we have "base 1" already, it doesn't make sense to add "base"
* if " base 1 " has been deleted , adding " base " is no worse than " base 1 "
*/
if ( ! definitely_add_number & & route_by_name ( base ) = = 0 & & ( route_by_name ( string_compose ( " %1 1 " , base ) ) = = 0 ) ) {
/* just use the base */
2015-09-14 10:47:21 -04:00
name = base ;
2011-02-26 20:59:04 -05:00
return true ;
}
2011-06-01 12:50:12 -04:00
2010-02-23 17:45:07 -05:00
do {
2015-09-14 10:47:21 -04:00
name = string_compose ( " %1 %2 " , base , id ) ;
2010-02-23 17:45:07 -05:00
if ( route_by_name ( name ) = = 0 ) {
return true ;
}
+ + id ;
2015-10-05 10:17:49 -04:00
2010-02-23 17:45:07 -05:00
} while ( id < ( UINT_MAX - 1 ) ) ;
return false ;
}
2011-03-15 15:32:21 -04:00
/** Count the total ins and outs of all non-hidden tracks in the session and return them in in and out */
2010-02-23 17:45:07 -05:00
void
2011-03-15 15:32:21 -04:00
Session : : count_existing_track_channels ( ChanCount & in , ChanCount & out )
2010-02-23 17:45:07 -05:00
{
in = ChanCount : : ZERO ;
out = ChanCount : : ZERO ;
2011-03-14 16:33:47 -04:00
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2011-03-14 16:33:47 -04:00
2010-02-23 17:45:07 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2017-08-15 17:24:32 -04:00
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( ! tr ) {
continue ;
2010-02-23 17:45:07 -05:00
}
2017-08-15 17:24:32 -04:00
assert ( ! tr - > is_auditioner ( ) ) ; // XXX remove me
in + = tr - > n_inputs ( ) ;
out + = tr - > n_outputs ( ) ;
2010-02-23 17:45:07 -05:00
}
}
2015-05-19 15:54:52 -04:00
string
Session : : default_track_name_pattern ( DataType t )
{
switch ( t ) {
case DataType : : AUDIO :
2021-01-08 16:25:13 -05:00
return _ ( " Audio " ) ;
2015-05-19 15:54:52 -04:00
break ;
case DataType : : MIDI :
2021-01-08 16:25:13 -05:00
return _ ( " MIDI " ) ;
2015-05-19 15:54:52 -04:00
}
return " " ;
}
2011-02-26 20:59:04 -05:00
/** Caller must not hold process lock
2011-11-07 19:31:17 -05:00
* @ param name_template string to use for the start of the name , or " " to use " MIDI " .
2012-02-01 23:12:23 -05:00
* @ param instrument plugin info for the instrument to insert pre - fader , if any
2011-02-26 20:59:04 -05:00
*/
2008-06-02 17:41:35 -04:00
list < boost : : shared_ptr < MidiTrack > >
2017-01-20 15:46:47 -05:00
Session : : new_midi_track ( const ChanCount & input , const ChanCount & output , bool strict_io ,
2016-05-17 08:21:05 -04:00
boost : : shared_ptr < PluginInfo > instrument , Plugin : : PresetRecord * pset ,
2017-01-20 15:46:47 -05:00
RouteGroup * route_group , uint32_t how_many ,
string name_template , PresentationInfo : : order_t order ,
2021-07-17 17:52:10 -04:00
TrackMode mode , bool input_auto_connect ,
2022-01-14 18:06:26 -05:00
bool trigger_visibility )
2008-06-02 17:41:35 -04:00
{
2015-05-12 10:43:37 -04:00
string track_name ;
2010-02-26 18:35:58 -05:00
uint32_t track_id = 0 ;
2008-06-02 17:41:35 -04:00
string port ;
RouteList new_routes ;
list < boost : : shared_ptr < MidiTrack > > ret ;
2008-08-04 18:37:24 -04:00
2015-05-19 15:54:52 -04:00
const string name_pattern = default_track_name_pattern ( DataType : : MIDI ) ;
bool const use_number = ( how_many ! = 1 ) | | name_template . empty ( ) | | ( name_template = = name_pattern ) ;
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
while ( how_many ) {
2015-05-12 10:43:37 -04:00
if ( ! find_route_name ( name_template . empty ( ) ? _ ( " MIDI " ) : name_template , + + track_id , track_name , use_number ) ) {
2010-02-23 17:45:07 -05:00
error < < " cannot find name for new midi track " < < endmsg ;
goto failed ;
}
2008-06-02 17:41:35 -04:00
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < MidiTrack > track ;
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
try {
2016-05-16 07:30:28 -04:00
track . reset ( new MidiTrack ( * this , track_name , mode ) ) ;
2010-03-24 23:40:07 -04:00
2011-02-22 16:15:42 -05:00
if ( track - > init ( ) ) {
2010-11-26 14:57:03 -05:00
goto failed ;
}
2010-03-24 23:40:07 -04:00
2017-01-20 15:46:47 -05:00
if ( strict_io ) {
2016-04-15 08:52:19 -04:00
track - > set_strict_io ( true ) ;
}
2016-05-07 13:32:31 -04:00
BOOST_MARK_TRACK ( track ) ;
2010-11-25 18:46:24 -05:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2012-06-21 21:45:16 -04:00
if ( track - > input ( ) - > ensure_io ( input , false , this ) ) {
2015-10-05 10:17:49 -04:00
error < < " cannot configure " < < input < < " out configuration for new midi track " < < endmsg ;
2010-11-25 18:46:24 -05:00
goto failed ;
}
2009-06-09 16:21:19 -04:00
2012-06-21 21:45:16 -04:00
if ( track - > output ( ) - > ensure_io ( output , false , this ) ) {
error < < " cannot configure " < < output < < " out configuration for new midi track " < < endmsg ;
2010-11-25 18:46:24 -05:00
goto failed ;
}
2008-06-02 17:41:35 -04:00
}
2009-12-10 13:33:54 -05:00
if ( route_group ) {
route_group - > add ( track ) ;
}
2008-08-04 18:37:24 -04:00
2022-01-14 18:06:26 -05:00
track - > presentation_info ( ) . set_trigger_track ( trigger_visibility ) ;
2021-07-17 18:07:06 -04:00
2008-06-02 17:41:35 -04:00
new_routes . push_back ( track ) ;
ret . push_back ( track ) ;
}
catch ( failed_constructor & err ) {
error < < _ ( " Session: could not create new midi track. " ) < < endmsg ;
goto failed ;
}
catch ( AudioEngine : : PortRegistrationFailure & pfe ) {
2013-01-09 10:05:04 -05:00
error < < string_compose ( _ ( " No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks. " ) , PROGRAM_NAME ) < < endmsg ;
2008-06-02 17:41:35 -04:00
goto failed ;
}
- - how_many ;
}
2019-04-13 13:19:29 -04:00
failed :
2008-06-02 17:41:35 -04:00
if ( ! new_routes . empty ( ) ) {
2020-07-18 17:27:11 -04:00
ChanCount existing_inputs ;
ChanCount existing_outputs ;
count_existing_track_channels ( existing_inputs , existing_outputs ) ;
2012-02-01 23:12:23 -05:00
2021-01-08 16:24:40 -05:00
add_routes ( new_routes , input_auto_connect , ! instrument , order ) ;
2020-07-18 17:27:11 -04:00
load_and_connect_instruments ( new_routes , strict_io , instrument , pset , existing_outputs ) ;
2008-06-02 17:41:35 -04:00
}
return ret ;
}
2016-03-15 23:41:19 -04:00
RouteList
2017-01-20 15:46:47 -05:00
Session : : new_midi_route ( RouteGroup * route_group , uint32_t how_many , string name_template , bool strict_io ,
boost : : shared_ptr < PluginInfo > instrument , Plugin : : PresetRecord * pset ,
2016-05-16 07:30:28 -04:00
PresentationInfo : : Flag flag , PresentationInfo : : order_t order )
2016-03-15 23:41:19 -04:00
{
string bus_name ;
uint32_t bus_id = 0 ;
string port ;
RouteList ret ;
bool const use_number = ( how_many ! = 1 ) | | name_template . empty ( ) | | name_template = = _ ( " Midi Bus " ) ;
while ( how_many ) {
if ( ! find_route_name ( name_template . empty ( ) ? _ ( " Midi Bus " ) : name_template , + + bus_id , bus_name , use_number ) ) {
error < < " cannot find name for new midi bus " < < endmsg ;
goto failure ;
}
2016-05-17 09:36:28 -04:00
2016-03-15 23:41:19 -04:00
try {
2016-05-16 07:30:28 -04:00
boost : : shared_ptr < Route > bus ( new Route ( * this , bus_name , flag , DataType : : AUDIO ) ) ; // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
2016-03-15 23:41:19 -04:00
if ( bus - > init ( ) ) {
goto failure ;
}
2017-01-20 15:46:47 -05:00
if ( strict_io ) {
2016-04-15 08:52:19 -04:00
bus - > set_strict_io ( true ) ;
}
2016-05-07 13:32:31 -04:00
BOOST_MARK_ROUTE ( bus ) ;
2016-03-15 23:41:19 -04:00
{
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
if ( bus - > input ( ) - > ensure_io ( ChanCount ( DataType : : MIDI , 1 ) , false , this ) ) {
error < < _ ( " cannot configure new midi bus input " ) < < endmsg ;
goto failure ;
}
if ( bus - > output ( ) - > ensure_io ( ChanCount ( DataType : : MIDI , 1 ) , false , this ) ) {
error < < _ ( " cannot configure new midi bus output " ) < < endmsg ;
goto failure ;
}
}
if ( route_group ) {
route_group - > add ( bus ) ;
}
2016-07-08 20:17:00 -04:00
bus - > add_internal_return ( ) ;
2016-03-15 23:41:19 -04:00
ret . push_back ( bus ) ;
}
catch ( failed_constructor & err ) {
2020-05-24 13:38:04 -04:00
error < < _ ( " Session: could not create new MIDI bus. " ) < < endmsg ;
2016-03-15 23:41:19 -04:00
goto failure ;
}
catch ( AudioEngine : : PortRegistrationFailure & pfe ) {
error < < pfe . what ( ) < < endmsg ;
goto failure ;
}
- - how_many ;
}
2019-04-13 13:19:29 -04:00
failure :
2016-03-15 23:41:19 -04:00
if ( ! ret . empty ( ) ) {
2020-07-18 17:27:11 -04:00
ChanCount existing_inputs ;
ChanCount existing_outputs ;
count_existing_track_channels ( existing_inputs , existing_outputs ) ;
2020-07-18 15:59:49 -04:00
2020-07-18 17:27:11 -04:00
add_routes ( ret , false , ! instrument , order ) ;
load_and_connect_instruments ( ret , strict_io , instrument , pset , existing_outputs ) ;
2016-03-15 23:41:19 -04:00
}
return ret ;
}
2011-03-20 18:09:46 -04:00
void
2020-07-18 17:27:11 -04:00
Session : : midi_output_change_handler ( IOChange change , void * /*src*/ , boost : : weak_ptr < Route > wr )
2011-03-20 18:09:46 -04:00
{
2020-07-18 17:27:11 -04:00
boost : : shared_ptr < Route > midi_route ( wr . lock ( ) ) ;
2011-03-14 16:33:47 -04:00
2020-07-18 19:07:57 -04:00
if ( ! midi_route ) {
return ;
}
2016-04-23 16:11:48 -04:00
if ( ( change . type & IOChange : : ConfigurationChanged ) & & Config - > get_output_auto_connect ( ) ! = ManualConnect ) {
2011-03-20 18:09:46 -04:00
2016-04-23 16:11:48 -04:00
if ( change . after . n_audio ( ) < = change . before . n_audio ( ) ) {
return ;
2008-06-02 17:41:35 -04:00
}
2016-04-23 16:11:48 -04:00
/* new audio ports: make sure the audio goes somewhere useful,
* unless the user has no - auto - connect selected .
*
* The existing ChanCounts don ' t matter for this call as they are only
* to do with matching input and output indices , and we are only changing
* outputs here .
*/
2020-07-18 17:27:11 -04:00
auto_connect_route ( midi_route , false , ! midi_route - > instrument_fanned_out ( ) , ChanCount ( ) , change . before ) ;
2010-02-23 17:45:07 -05:00
}
}
2008-06-02 17:41:35 -04:00
2017-06-16 20:31:03 -04:00
bool
Session : : ensure_stripable_sort_order ( )
{
StripableList sl ;
get_stripables ( sl ) ;
sl . sort ( Stripable : : Sorter ( ) ) ;
bool change = false ;
PresentationInfo : : order_t order = 0 ;
for ( StripableList : : iterator si = sl . begin ( ) ; si ! = sl . end ( ) ; + + si ) {
boost : : shared_ptr < Stripable > s ( * si ) ;
2017-08-15 17:24:32 -04:00
assert ( ! s - > is_auditioner ( ) ) ; // XXX remove me
if ( s - > is_monitor ( ) ) {
2017-06-16 20:31:03 -04:00
continue ;
}
if ( order ! = s - > presentation_info ( ) . order ( ) ) {
s - > set_presentation_order ( order ) ;
change = true ;
}
+ + order ;
}
return change ;
}
2016-05-16 07:30:28 -04:00
void
2016-06-02 11:47:19 -04:00
Session : : ensure_route_presentation_info_gap ( PresentationInfo : : order_t first_new_order , uint32_t how_many )
2016-05-16 07:30:28 -04:00
{
2017-06-16 22:23:55 -04:00
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " ensure order gap starting at %1 for %2 \n " , first_new_order , how_many ) ) ;
2016-05-16 07:30:28 -04:00
if ( first_new_order = = PresentationInfo : : max_order ) {
/* adding at end, no worries */
return ;
}
2016-06-02 11:47:19 -04:00
/* create a gap in the presentation info to accomodate @param how_many
* new objects .
*/
2016-07-03 13:22:47 -04:00
StripableList sl ;
get_stripables ( sl ) ;
2016-06-02 11:47:19 -04:00
2016-07-03 13:22:47 -04:00
for ( StripableList : : iterator si = sl . begin ( ) ; si ! = sl . end ( ) ; + + si ) {
boost : : shared_ptr < Stripable > s ( * si ) ;
2016-05-16 07:30:28 -04:00
2017-06-16 22:23:55 -04:00
if ( s - > presentation_info ( ) . special ( false ) ) {
continue ;
}
if ( ! s - > presentation_info ( ) . order_set ( ) ) {
2016-05-16 07:30:28 -04:00
continue ;
}
2016-07-03 13:22:47 -04:00
if ( s - > presentation_info ( ) . order ( ) > = first_new_order ) {
s - > set_presentation_order ( s - > presentation_info ( ) . order ( ) + how_many ) ;
2016-05-16 07:30:28 -04:00
}
}
}
2011-02-26 20:59:04 -05:00
/** Caller must not hold process lock
* @ param name_template string to use for the start of the name , or " " to use " Audio " .
*/
2010-02-26 18:35:58 -05:00
list < boost : : shared_ptr < AudioTrack > >
2016-05-16 07:30:28 -04:00
Session : : new_audio_track ( int input_channels , int output_channels , RouteGroup * route_group ,
uint32_t how_many , string name_template , PresentationInfo : : order_t order ,
2021-10-08 15:37:29 -04:00
TrackMode mode , bool input_auto_connect ,
2022-01-14 18:06:26 -05:00
bool trigger_visibility )
2010-02-23 17:45:07 -05:00
{
2015-05-12 10:43:37 -04:00
string track_name ;
2010-02-26 18:35:58 -05:00
uint32_t track_id = 0 ;
2010-02-23 17:45:07 -05:00
string port ;
RouteList new_routes ;
list < boost : : shared_ptr < AudioTrack > > ret ;
2008-08-04 18:37:24 -04:00
2015-05-19 15:54:52 -04:00
const string name_pattern = default_track_name_pattern ( DataType : : AUDIO ) ;
bool const use_number = ( how_many ! = 1 ) | | name_template . empty ( ) | | ( name_template = = name_pattern ) ;
2015-10-05 10:17:49 -04:00
2010-02-23 17:45:07 -05:00
while ( how_many ) {
2015-05-08 15:29:19 -04:00
2015-05-12 10:43:37 -04:00
if ( ! find_route_name ( name_template . empty ( ) ? _ ( name_pattern . c_str ( ) ) : name_template , + + track_id , track_name , use_number ) ) {
2010-02-23 17:45:07 -05:00
error < < " cannot find name for new audio track " < < endmsg ;
goto failed ;
}
2008-06-02 17:41:35 -04:00
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < AudioTrack > track ;
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
try {
2016-05-16 07:30:28 -04:00
track . reset ( new AudioTrack ( * this , track_name , mode ) ) ;
2010-03-24 23:40:07 -04:00
2011-02-22 16:15:42 -05:00
if ( track - > init ( ) ) {
2010-11-26 14:57:03 -05:00
goto failed ;
}
2010-03-24 23:40:07 -04:00
2016-04-15 08:52:19 -04:00
if ( Profile - > get_mixbus ( ) ) {
track - > set_strict_io ( true ) ;
}
2016-05-07 13:32:31 -04:00
BOOST_MARK_TRACK ( track ) ;
2010-11-25 18:46:24 -05:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2009-10-14 12:10:01 -04:00
2010-11-25 18:46:24 -05:00
if ( track - > input ( ) - > ensure_io ( ChanCount ( DataType : : AUDIO , input_channels ) , false , this ) ) {
error < < string_compose (
_ ( " cannot configure %1 in/%2 out configuration for new audio track " ) ,
input_channels , output_channels )
< < endmsg ;
goto failed ;
}
2011-06-01 12:50:12 -04:00
2010-11-25 18:46:24 -05:00
if ( track - > output ( ) - > ensure_io ( ChanCount ( DataType : : AUDIO , output_channels ) , false , this ) ) {
error < < string_compose (
_ ( " cannot configure %1 in/%2 out configuration for new audio track " ) ,
input_channels , output_channels )
< < endmsg ;
goto failed ;
}
}
2008-06-02 17:41:35 -04:00
2009-12-10 13:33:54 -05:00
if ( route_group ) {
route_group - > add ( track ) ;
}
2009-06-20 11:40:26 -04:00
2022-01-14 18:06:26 -05:00
track - > presentation_info ( ) . set_trigger_track ( trigger_visibility ) ;
2021-10-08 15:37:29 -04:00
2008-06-02 17:41:35 -04:00
new_routes . push_back ( track ) ;
ret . push_back ( track ) ;
}
catch ( failed_constructor & err ) {
error < < _ ( " Session: could not create new audio track. " ) < < endmsg ;
goto failed ;
}
catch ( AudioEngine : : PortRegistrationFailure & pfe ) {
2009-04-15 14:04:23 -04:00
error < < pfe . what ( ) < < endmsg ;
2008-06-02 17:41:35 -04:00
goto failed ;
}
- - how_many ;
}
2019-04-13 13:19:29 -04:00
failed :
2008-06-02 17:41:35 -04:00
if ( ! new_routes . empty ( ) ) {
2020-05-19 12:02:23 -04:00
add_routes ( new_routes , input_auto_connect , true , order ) ;
2008-06-02 17:41:35 -04:00
}
return ret ;
}
2011-02-26 20:59:04 -05:00
/** Caller must not hold process lock.
* @ param name_template string to use for the start of the name , or " " to use " Bus " .
*/
2009-01-30 02:40:13 -05:00
RouteList
2016-05-16 07:30:28 -04:00
Session : : new_audio_route ( int input_channels , int output_channels , RouteGroup * route_group , uint32_t how_many , string name_template ,
PresentationInfo : : Flag flags , PresentationInfo : : order_t order )
2008-06-02 17:41:35 -04:00
{
2015-05-12 10:43:37 -04:00
string bus_name ;
2010-02-26 18:35:58 -05:00
uint32_t bus_id = 0 ;
2008-06-02 17:41:35 -04:00
string port ;
RouteList ret ;
2011-06-22 19:37:02 -04:00
bool const use_number = ( how_many ! = 1 ) | | name_template . empty ( ) | | name_template = = _ ( " Bus " ) ;
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
while ( how_many ) {
2015-05-12 10:43:37 -04:00
if ( ! find_route_name ( name_template . empty ( ) ? _ ( " Bus " ) : name_template , + + bus_id , bus_name , use_number ) ) {
2010-02-23 17:45:07 -05:00
error < < " cannot find name for new audio bus " < < endmsg ;
goto failure ;
}
2008-06-02 17:41:35 -04:00
try {
2016-05-16 07:30:28 -04:00
boost : : shared_ptr < Route > bus ( new Route ( * this , bus_name , flags , DataType : : AUDIO ) ) ;
2010-03-24 23:40:07 -04:00
2011-02-22 16:15:42 -05:00
if ( bus - > init ( ) ) {
2010-11-26 14:57:03 -05:00
goto failure ;
}
2010-03-24 23:40:07 -04:00
2016-04-15 08:52:19 -04:00
if ( Profile - > get_mixbus ( ) ) {
bus - > set_strict_io ( true ) ;
}
2016-05-07 13:32:31 -04:00
BOOST_MARK_ROUTE ( bus ) ;
2010-11-25 18:46:24 -05:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2009-06-09 16:21:19 -04:00
2010-11-25 18:46:24 -05:00
if ( bus - > input ( ) - > ensure_io ( ChanCount ( DataType : : AUDIO , input_channels ) , false , this ) ) {
error < < string_compose ( _ ( " cannot configure %1 in/%2 out configuration for new audio track " ) ,
input_channels , output_channels )
< < endmsg ;
goto failure ;
}
2011-06-01 12:50:12 -04:00
2010-11-25 18:46:24 -05:00
if ( bus - > output ( ) - > ensure_io ( ChanCount ( DataType : : AUDIO , output_channels ) , false , this ) ) {
error < < string_compose ( _ ( " cannot configure %1 in/%2 out configuration for new audio track " ) ,
input_channels , output_channels )
< < endmsg ;
goto failure ;
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2009-12-10 13:33:54 -05:00
if ( route_group ) {
route_group - > add ( bus ) ;
}
2008-06-02 17:41:35 -04:00
2011-01-05 10:52:27 -05:00
bus - > add_internal_return ( ) ;
2008-06-02 17:41:35 -04:00
ret . push_back ( bus ) ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
catch ( failed_constructor & err ) {
2020-05-24 13:38:04 -04:00
error < < _ ( " Session: could not create new audio bus. " ) < < endmsg ;
2008-06-02 17:41:35 -04:00
goto failure ;
}
catch ( AudioEngine : : PortRegistrationFailure & pfe ) {
2009-04-15 14:04:23 -04:00
error < < pfe . what ( ) < < endmsg ;
2008-06-02 17:41:35 -04:00
goto failure ;
}
- - how_many ;
}
2019-04-13 13:19:29 -04:00
failure :
2008-06-02 17:41:35 -04:00
if ( ! ret . empty ( ) ) {
2019-09-25 15:02:31 -04:00
if ( flags = = PresentationInfo : : FoldbackBus ) {
2020-05-19 12:02:23 -04:00
add_routes ( ret , false , false , order ) ; // no autoconnect
2015-05-08 12:48:07 -04:00
} else {
2020-05-19 12:02:23 -04:00
add_routes ( ret , false , true , order ) ; // autoconnect // outputs only
2015-05-08 12:48:07 -04:00
}
2008-06-02 17:41:35 -04:00
}
return ret ;
}
2009-03-02 11:52:40 -05:00
RouteList
2016-08-22 09:41:08 -04:00
Session : : new_route_from_template ( uint32_t how_many , PresentationInfo : : order_t insert_at , const std : : string & template_path , const std : : string & name_base ,
PlaylistDisposition pd )
2009-03-02 11:52:40 -05:00
{
XMLTree tree ;
if ( ! tree . read ( template_path . c_str ( ) ) ) {
2015-11-13 16:14:09 -05:00
return RouteList ( ) ;
2009-03-02 11:52:40 -05:00
}
2016-08-22 09:41:08 -04:00
return new_route_from_template ( how_many , insert_at , * tree . root ( ) , name_base , pd ) ;
2015-11-13 16:14:09 -05:00
}
2009-03-02 11:52:40 -05:00
2015-11-13 16:14:09 -05:00
RouteList
2016-08-22 09:41:08 -04:00
Session : : new_route_from_template ( uint32_t how_many , PresentationInfo : : order_t insert_at , XMLNode & node , const std : : string & name_base , PlaylistDisposition pd )
2015-11-13 16:14:09 -05:00
{
RouteList ret ;
uint32_t number = 0 ;
const uint32_t being_added = how_many ;
/* This will prevent the use of any existing XML-provided PBD::ID
values by Stateful .
*/
Stateful : : ForceIDRegeneration force_ids ;
2012-01-24 08:43:48 -05:00
2020-04-05 12:55:09 -04:00
/* New v6 templates do have a version in the Route-Template,
* we assume that all older , unversioned templates are
* from Ardour 5. x
* when Stateful : : loading_state_version was 3002
*/
int version = 3002 ;
node . get_property ( X_ ( " version " ) , version ) ;
2009-03-02 11:52:40 -05:00
while ( how_many ) {
2015-11-13 16:14:09 -05:00
/* We're going to modify the node contents a bit so take a
* copy . The node may be re - used when duplicating more than once .
*/
2009-10-14 12:10:01 -04:00
2015-11-13 16:14:09 -05:00
XMLNode node_copy ( node ) ;
2019-03-21 00:16:57 -04:00
std : : vector < boost : : shared_ptr < Playlist > > shared_playlists ;
2009-03-02 11:52:40 -05:00
2011-11-16 18:03:59 -05:00
try {
2015-05-12 10:43:37 -04:00
string name ;
2013-04-01 20:45:57 -04:00
if ( ! name_base . empty ( ) ) {
/* if we're adding more than one routes, force
* all the names of the new routes to be
* numbered , via the final parameter .
*/
2015-05-12 10:43:37 -04:00
if ( ! find_route_name ( name_base . c_str ( ) , + + number , name , ( being_added > 1 ) ) ) {
2020-05-24 13:20:45 -04:00
fatal < < _ ( " Session: Failed to create unique ID for track from template. " ) < < endmsg ;
2019-09-17 23:57:26 -04:00
abort ( ) ; /*NOTREACHED*/
2013-04-01 20:45:57 -04:00
}
} else {
string const route_name = node_copy . property ( X_ ( " name " ) ) - > value ( ) ;
2015-10-05 10:17:49 -04:00
2013-04-01 20:45:57 -04:00
/* generate a new name by adding a number to the end of the template name */
2022-01-24 15:24:16 -05:00
if ( ! find_route_name ( route_name , + + number , name , true ) ) {
2020-05-24 13:20:45 -04:00
fatal < < _ ( " Session: Failed to generate unique name and ID for track from template. " ) < < endmsg ;
2014-11-14 04:47:43 -05:00
abort ( ) ; /*NOTREACHED*/
2013-04-01 20:45:57 -04:00
}
2010-05-08 20:47:16 -04:00
}
2009-03-02 11:52:40 -05:00
2019-03-20 23:09:17 -04:00
/* figure out the appropriate playlist setup. The track
* ( if the Route we ' re creating is a track ) will find
* playlists via ID .
*/
2016-11-27 21:30:38 -05:00
if ( pd = = CopyPlaylist ) {
2019-03-20 23:09:17 -04:00
PBD : : ID playlist_id ;
if ( node_copy . get_property ( X_ ( " audio-playlist " ) , playlist_id ) ) {
boost : : shared_ptr < Playlist > playlist = _playlists - > by_id ( playlist_id ) ;
playlist = PlaylistFactory : : create ( playlist , string_compose ( " %1.1 " , name ) ) ;
playlist - > reset_shares ( ) ;
node_copy . set_property ( X_ ( " audio-playlist " ) , playlist - > id ( ) ) ;
}
if ( node_copy . get_property ( X_ ( " midi-playlist " ) , playlist_id ) ) {
boost : : shared_ptr < Playlist > playlist = _playlists - > by_id ( playlist_id ) ;
2016-11-30 11:02:20 -05:00
playlist = PlaylistFactory : : create ( playlist , string_compose ( " %1.1 " , name ) ) ;
playlist - > reset_shares ( ) ;
2019-03-20 23:09:17 -04:00
node_copy . set_property ( X_ ( " midi-playlist " ) , playlist - > id ( ) ) ;
2016-11-30 11:02:20 -05:00
}
2019-03-20 23:09:17 -04:00
2016-11-30 11:02:20 -05:00
} else if ( pd = = SharePlaylist ) {
2019-03-20 23:09:17 -04:00
PBD : : ID playlist_id ;
if ( node_copy . get_property ( X_ ( " audio-playlist " ) , playlist_id ) ) {
boost : : shared_ptr < Playlist > playlist = _playlists - > by_id ( playlist_id ) ;
2019-03-21 00:16:57 -04:00
shared_playlists . push_back ( playlist ) ;
2016-11-27 21:30:38 -05:00
}
2019-03-20 23:09:17 -04:00
if ( node_copy . get_property ( X_ ( " midi-playlist " ) , playlist_id ) ) {
boost : : shared_ptr < Playlist > playlist = _playlists - > by_id ( playlist_id ) ;
2019-03-21 00:16:57 -04:00
shared_playlists . push_back ( playlist ) ;
2019-03-20 23:09:17 -04:00
}
} else { /* NewPlaylist */
PBD : : ID pid ;
2020-04-05 12:55:09 -04:00
std : : string default_type ;
node . get_property ( X_ ( " default-type " ) , default_type ) ;
2019-03-20 23:09:17 -04:00
2020-04-05 12:55:09 -04:00
if ( node_copy . get_property ( X_ ( " audio-playlist " ) , pid ) | | ( version < 5000 & & default_type = = " audio " ) ) {
2019-03-20 23:09:17 -04:00
boost : : shared_ptr < Playlist > playlist = PlaylistFactory : : create ( DataType : : AUDIO , * this , name , false ) ;
node_copy . set_property ( X_ ( " audio-playlist " ) , playlist - > id ( ) ) ;
}
2020-04-05 12:55:09 -04:00
if ( node_copy . get_property ( X_ ( " midi-playlist " ) , pid ) | | ( version < 5000 & & default_type = = " midi " ) ) {
2019-03-20 23:09:17 -04:00
boost : : shared_ptr < Playlist > playlist = PlaylistFactory : : create ( DataType : : MIDI , * this , name , false ) ;
node_copy . set_property ( X_ ( " midi-playlist " ) , playlist - > id ( ) ) ;
}
2015-11-13 16:14:09 -05:00
}
2019-03-20 23:09:17 -04:00
/* Fix up new name in the XML node */
2016-11-27 21:30:38 -05:00
2019-03-20 23:09:17 -04:00
Route : : set_name_in_state ( node_copy , name ) ;
2009-03-05 12:27:05 -05:00
2011-11-16 18:03:59 -05:00
/* trim bitslots from listen sends so that new ones are used */
XMLNodeList children = node_copy . children ( ) ;
for ( XMLNodeList : : iterator i = children . begin ( ) ; i ! = children . end ( ) ; + + i ) {
if ( ( * i ) - > name ( ) = = X_ ( " Processor " ) ) {
2016-04-26 12:15:53 -04:00
/* ForceIDRegeneration does not catch the following */
2016-05-04 23:09:37 -04:00
XMLProperty const * role = ( * i ) - > property ( X_ ( " role " ) ) ;
XMLProperty const * type = ( * i ) - > property ( X_ ( " type " ) ) ;
2020-06-07 15:39:50 -04:00
2016-04-26 12:15:53 -04:00
if ( role & & role - > value ( ) = = X_ ( " Aux " ) ) {
2020-06-07 15:39:50 -04:00
/* Check if the target bus exists.
* This is mainly useful when duplicating tracks
* ( aux - sends should not be saved in templates ) .
2016-04-26 12:15:53 -04:00
*/
2016-05-04 23:09:37 -04:00
XMLProperty const * target = ( * i ) - > property ( X_ ( " target " ) ) ;
2016-04-26 12:15:53 -04:00
if ( ! target ) {
2016-08-28 07:12:16 -04:00
( * i ) - > set_property ( " type " , " dangling-aux-send " ) ;
2016-04-26 12:15:53 -04:00
continue ;
}
boost : : shared_ptr < Route > r = route_by_id ( target - > value ( ) ) ;
if ( ! r | | boost : : dynamic_pointer_cast < Track > ( r ) ) {
2016-08-28 07:12:16 -04:00
( * i ) - > set_property ( " type " , " dangling-aux-send " ) ;
2016-04-26 12:15:53 -04:00
continue ;
}
}
2020-06-07 15:39:50 -04:00
2011-11-16 18:03:59 -05:00
if ( role & & role - > value ( ) = = X_ ( " Listen " ) ) {
( * i ) - > remove_property ( X_ ( " bitslot " ) ) ;
}
2016-04-26 12:15:53 -04:00
else if ( role & & ( role - > value ( ) = = X_ ( " Send " ) | | role - > value ( ) = = X_ ( " Aux " ) ) ) {
Delivery : : Role xrole ;
uint32_t bitslot = 0 ;
xrole = Delivery : : Role ( string_2_enum ( role - > value ( ) , xrole ) ) ;
2020-06-07 15:39:50 -04:00
/* generate new bitslot ID */
2016-04-26 12:15:53 -04:00
std : : string name = Send : : name_and_id_new_send ( * this , xrole , bitslot , false ) ;
( * i ) - > remove_property ( X_ ( " bitslot " ) ) ;
2016-08-28 07:12:16 -04:00
( * i ) - > set_property ( " bitslot " , bitslot ) ;
2020-06-07 15:39:50 -04:00
/* external sends need unique names */
if ( role - > value ( ) = = X_ ( " Send " ) ) {
( * i ) - > remove_property ( X_ ( " name " ) ) ;
( * 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 ) ;
2019-03-27 22:01:53 -04:00
}
}
2016-04-26 12:15:53 -04:00
}
2016-06-02 16:01:36 -04:00
else if ( type & & type - > value ( ) = = X_ ( " intreturn " ) ) {
( * i ) - > remove_property ( X_ ( " bitslot " ) ) ;
2016-08-28 07:12:16 -04:00
( * i ) - > set_property ( " ignore-bitslot " , " 1 " ) ;
2016-06-02 16:01:36 -04:00
}
2016-04-26 12:15:53 -04:00
else if ( type & & type - > value ( ) = = X_ ( " return " ) ) {
// Return::set_state() generates a new one
( * i ) - > remove_property ( X_ ( " bitslot " ) ) ;
}
else if ( type & & type - > value ( ) = = X_ ( " port " ) ) {
2018-06-28 19:14:37 -04:00
IOProcessor : : prepare_for_reset ( * * i , name ) ;
2016-04-26 12:15:53 -04:00
}
2011-11-16 18:03:59 -05:00
}
}
2015-10-05 10:17:49 -04:00
2017-05-28 16:46:22 -04:00
/* new routes start off unsoloed to avoid issues related to
2019-03-20 23:09:17 -04:00
upstream / downstream buses .
*/
2017-05-29 17:45:16 -04:00
node_copy . remove_node_and_delete ( X_ ( " Controllable " ) , X_ ( " name " ) , X_ ( " solo " ) ) ;
2017-05-28 16:46:22 -04:00
2020-01-29 19:08:57 -05:00
boost : : shared_ptr < Route > route ;
if ( version < 3000 ) {
route = XMLRouteFactory_2X ( node_copy , version ) ;
} else if ( version < 5000 ) {
route = XMLRouteFactory_3X ( node_copy , version ) ;
} else {
route = XMLRouteFactory ( node_copy , version ) ;
}
2010-05-08 20:47:16 -04:00
2009-03-02 11:52:40 -05:00
if ( route = = 0 ) {
error < < _ ( " Session: cannot create track/bus from template description " ) < < endmsg ;
goto out ;
}
2021-05-06 15:16:55 -04:00
{
PresentationInfo & rpi = route - > presentation_info ( ) ;
rpi . set_flags ( PresentationInfo : : Flag ( rpi . flags ( ) & ~ PresentationInfo : : OrderSet ) ) ;
}
2019-03-21 00:16:57 -04:00
/* Fix up sharing of playlists with the new Route/Track */
for ( vector < boost : : shared_ptr < Playlist > > : : iterator sp = shared_playlists . begin ( ) ; sp ! = shared_playlists . end ( ) ; + + sp ) {
( * sp ) - > share_with ( route - > id ( ) ) ;
}
2009-03-05 12:27:05 -05:00
if ( boost : : dynamic_pointer_cast < Track > ( route ) ) {
/* force input/output change signals so that the new diskstream
picks up the configuration of the route . During session
loading this normally happens in a different way .
*/
2011-06-01 12:50:12 -04:00
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2011-06-01 12:50:12 -04:00
2010-08-30 18:34:21 -04:00
IOChange change ( IOChange : : Type ( IOChange : : ConfigurationChanged | IOChange : : ConnectionsChanged ) ) ;
change . after = route - > input ( ) - > n_ports ( ) ;
route - > input ( ) - > changed ( change , this ) ;
change . after = route - > output ( ) - > n_ports ( ) ;
route - > output ( ) - > changed ( change , this ) ;
2009-03-05 12:27:05 -05:00
}
2009-10-14 12:10:01 -04:00
2009-03-02 11:52:40 -05:00
ret . push_back ( route ) ;
}
2009-10-14 12:10:01 -04:00
2009-03-02 11:52:40 -05:00
catch ( failed_constructor & err ) {
2020-05-24 13:38:04 -04:00
error < < _ ( " Session: could not create new track/bus from template " ) < < endmsg ;
2009-03-02 11:52:40 -05:00
goto out ;
}
2009-10-14 12:10:01 -04:00
2009-03-02 11:52:40 -05:00
catch ( AudioEngine : : PortRegistrationFailure & pfe ) {
2009-04-15 14:04:23 -04:00
error < < pfe . what ( ) < < endmsg ;
2009-03-02 11:52:40 -05:00
goto out ;
}
2009-10-14 12:10:01 -04:00
2018-11-26 15:45:06 -05:00
catch ( . . . ) {
throw ;
}
2009-03-02 11:52:40 -05:00
- - how_many ;
}
2019-04-13 13:19:29 -04:00
out :
2009-03-02 11:52:40 -05:00
if ( ! ret . empty ( ) ) {
2022-04-26 09:06:40 -04:00
add_routes ( ret , false , false , insert_at ) ;
2009-03-02 11:52:40 -05:00
}
2020-11-26 18:11:41 -05:00
if ( ! ret . empty ( ) ) {
/* set/unset monitor-send */
Glib : : Threads : : Mutex : : Lock lm ( _engine . process_lock ( ) ) ;
for ( RouteList : : iterator x = ret . begin ( ) ; x ! = ret . end ( ) ; + + x ) {
2021-03-24 12:47:53 -04:00
if ( ( * x ) - > can_monitor ( ) ) {
2020-11-26 18:11:41 -05:00
if ( _monitor_out ) {
( * x ) - > enable_monitor_send ( ) ;
} else {
/* this may happen with old templates */
( * x ) - > remove_monitor_send ( ) ;
}
}
2022-04-26 09:06:40 -04:00
/* reconnect ports using information from state */
for ( auto const & wio : ( * x ) - > all_inputs ( ) ) {
boost : : shared_ptr < IO > io = wio . lock ( ) ;
if ( ! io ) {
continue ;
}
for ( auto const & p : io - > ports ( ) ) {
p - > reconnect ( ) ;
}
}
for ( auto const & wio : ( * x ) - > all_outputs ( ) ) {
boost : : shared_ptr < IO > io = wio . lock ( ) ;
if ( ! io ) {
continue ;
}
for ( auto const & p : io - > ports ( ) ) {
p - > reconnect ( ) ;
}
}
2020-11-26 18:11:41 -05:00
}
}
2009-03-02 11:52:40 -05:00
return ret ;
}
2008-06-02 17:41:35 -04:00
void
2020-05-19 12:02:23 -04:00
Session : : add_routes ( RouteList & new_routes , bool input_auto_connect , bool output_auto_connect , PresentationInfo : : order_t order )
2012-12-04 09:32:28 -05:00
{
try {
PBD : : Unwinder < bool > aip ( _adding_routes_in_progress , true ) ;
2016-05-16 07:30:28 -04:00
add_routes_inner ( new_routes , input_auto_connect , output_auto_connect , order ) ;
2012-12-04 09:32:28 -05:00
} catch ( . . . ) {
error < < _ ( " Adding new tracks/busses failed " ) < < endmsg ;
}
2019-10-28 15:36:38 -04:00
/* During the route additions there will have been potentially several
* signals emitted to indicate the new graph . : : graph_reordered ( ) will
* have ignored all of them because _adding_routes_in_progress was
* true .
*
* We still need the effects of : : graph_reordered ( ) , but we didn ' t want
* it called multiple times during the addition of multiple routes . Now
* that the addition is done , call it explicitly .
*/
2019-10-28 19:23:54 -04:00
graph_reordered ( false ) ;
2012-12-04 09:32:28 -05:00
set_dirty ( ) ;
2015-10-05 10:17:49 -04:00
2014-09-19 09:45:01 -04:00
update_route_record_state ( ) ;
2015-10-04 14:51:05 -04:00
2012-12-04 09:32:28 -05:00
RouteAdded ( new_routes ) ; /* EMIT SIGNAL */
}
void
2016-05-16 07:30:28 -04:00
Session : : add_routes_inner ( RouteList & new_routes , bool input_auto_connect , bool output_auto_connect , PresentationInfo : : order_t order )
2008-06-02 17:41:35 -04:00
{
2016-04-23 16:11:48 -04:00
ChanCount existing_inputs ;
ChanCount existing_outputs ;
2016-05-16 07:30:28 -04:00
uint32_t n_routes ;
uint32_t added = 0 ;
2013-10-23 10:27:13 -04:00
2016-04-23 16:11:48 -04:00
count_existing_track_channels ( existing_inputs , existing_outputs ) ;
2011-03-15 15:32:21 -04:00
2008-08-04 18:37:24 -04:00
{
2008-06-02 17:41:35 -04:00
RCUWriter < RouteList > writer ( routes ) ;
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < RouteList > r = writer . get_copy ( ) ;
2016-05-16 07:30:28 -04:00
n_routes = r - > size ( ) ;
2017-06-16 22:23:55 -04:00
r - > insert ( r - > end ( ) , new_routes . begin ( ) , new_routes . end ( ) ) ;
2009-06-16 10:58:33 -04:00
2009-06-25 16:46:39 -04:00
/* if there is no control out and we're not in the middle of loading,
2016-04-23 16:11:48 -04:00
* resort the graph here . if there is a control out , we will resort
* toward the end of this method . if we are in the middle of loading ,
* we will resort when done .
*/
2009-06-25 16:46:39 -04:00
2021-11-03 17:06:16 -04:00
if ( ! _monitor_out & & ! loading ( ) ) {
2009-06-16 10:58:33 -04:00
resort_routes_using ( r ) ;
}
2008-06-02 17:41:35 -04:00
}
2017-08-15 17:24:32 -04:00
/* monitor is not part of the order */
2017-02-16 18:50:52 -05:00
if ( _monitor_out ) {
assert ( n_routes > 0 ) ;
- - n_routes ;
}
2017-01-26 13:20:36 -05:00
{
PresentationInfo : : ChangeSuspender cs ;
2017-06-16 22:23:55 -04:00
ensure_route_presentation_info_gap ( order , new_routes . size ( ) ) ;
2008-08-04 18:37:24 -04:00
2017-01-26 13:20:36 -05:00
for ( RouteList : : iterator x = new_routes . begin ( ) ; x ! = new_routes . end ( ) ; + + x , + + added ) {
2008-06-02 17:41:35 -04:00
2017-01-26 13:20:36 -05:00
boost : : weak_ptr < Route > wpr ( * x ) ;
boost : : shared_ptr < Route > r ( * x ) ;
2016-03-11 10:34:51 -05:00
2017-01-26 13:20:36 -05:00
r - > solo_control ( ) - > Changed . connect_same_thread ( * this , boost : : bind ( & Session : : route_solo_changed , this , _1 , _2 , wpr ) ) ;
r - > solo_isolate_control ( ) - > Changed . connect_same_thread ( * this , boost : : bind ( & Session : : route_solo_isolated_changed , this , wpr ) ) ;
r - > mute_control ( ) - > Changed . connect_same_thread ( * this , boost : : bind ( & Session : : route_mute_changed , this ) ) ;
2008-08-04 18:37:24 -04:00
2017-01-26 13:20:36 -05:00
r - > processors_changed . connect_same_thread ( * this , boost : : bind ( & Session : : route_processors_changed , this , _1 ) ) ;
r - > processor_latency_changed . connect_same_thread ( * this , boost : : bind ( & Session : : queue_latency_recompute , this ) ) ;
2008-08-04 18:37:24 -04:00
2017-01-26 13:20:36 -05:00
if ( r - > is_master ( ) ) {
_master_out = r ;
}
2010-04-21 16:42:22 -04:00
2017-01-26 13:20:36 -05:00
if ( r - > is_monitor ( ) ) {
_monitor_out = r ;
}
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( r ) ;
if ( tr ) {
tr - > PlaylistChanged . connect_same_thread ( * this , boost : : bind ( & Session : : track_playlist_changed , this , boost : : weak_ptr < Track > ( tr ) ) ) ;
track_playlist_changed ( boost : : weak_ptr < Track > ( tr ) ) ;
tr - > rec_enable_control ( ) - > Changed . connect_same_thread ( * this , boost : : bind ( & Session : : update_route_record_state , this ) ) ;
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( tr ) ;
if ( mt ) {
mt - > StepEditStatusChange . connect_same_thread ( * this , boost : : bind ( & Session : : step_edit_status_change , this , _1 ) ) ;
mt - > presentation_info ( ) . PropertyChanged . connect_same_thread ( * this , boost : : bind ( & Session : : midi_track_presentation_info_changed , this , _1 , boost : : weak_ptr < MidiTrack > ( mt ) ) ) ;
}
2010-11-26 14:57:03 -05:00
}
2011-06-01 12:50:12 -04:00
2022-03-14 14:18:20 -04:00
if ( r - > triggerbox ( ) ) {
r - > triggerbox ( ) - > EmptyStatusChanged . connect_same_thread ( * this , boost : : bind ( & Session : : handle_slots_empty_status , this , wpr ) ) ;
2022-03-14 16:10:29 -04:00
if ( ! r - > triggerbox ( ) - > empty ( ) ) {
tb_with_filled_slots + + ;
}
2022-03-14 14:18:20 -04:00
}
2017-06-16 22:23:55 -04:00
if ( ! r - > presentation_info ( ) . special ( false ) ) {
2012-06-27 18:57:06 -04:00
2017-01-26 13:20:36 -05:00
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " checking PI state for %1 \n " , r - > name ( ) ) ) ;
2016-05-16 07:30:28 -04:00
2017-01-26 13:20:36 -05:00
/* presentation info order may already have been set from XML */
2016-05-16 07:30:28 -04:00
2017-01-26 13:20:36 -05:00
if ( ! r - > presentation_info ( ) . order_set ( ) ) {
if ( order = = PresentationInfo : : max_order ) {
/* just add to the end */
r - > set_presentation_order ( n_routes + added ) ;
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " group order not set, set to NR %1 + %2 = %3 \n " , n_routes , added , n_routes + added ) ) ;
} else {
r - > set_presentation_order ( order + added ) ;
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " group order not set, set to %1 + %2 = %3 \n " , order , added , order + added ) ) ;
}
2016-05-16 07:30:28 -04:00
} else {
2017-01-26 13:20:36 -05:00
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " group order already set to %1 \n " , r - > presentation_info ( ) . order ( ) ) ) ;
2016-05-16 07:30:28 -04:00
}
2012-06-27 18:57:06 -04:00
}
2012-12-13 08:44:11 -05:00
2022-04-09 20:35:16 -04:00
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " added route %1, pi %2 \n " , r - > name ( ) , r - > presentation_info ( ) ) ) ;
2016-05-16 07:30:28 -04:00
2017-01-26 13:20:36 -05:00
if ( input_auto_connect | | output_auto_connect ) {
2020-07-18 17:27:11 -04:00
auto_connect_route ( r , input_auto_connect , output_auto_connect , ChanCount ( ) , ChanCount ( ) , existing_inputs , existing_outputs ) ;
if ( input_auto_connect ) {
existing_inputs + = r - > n_inputs ( ) ;
}
if ( output_auto_connect ) {
existing_outputs + = r - > n_outputs ( ) ;
}
2017-01-26 13:20:36 -05:00
}
2016-05-16 07:30:28 -04:00
2017-01-26 13:20:36 -05:00
ARDOUR : : GUIIdle ( ) ;
}
2017-06-16 22:23:55 -04:00
ensure_stripable_sort_order ( ) ;
2011-03-14 17:53:10 -04:00
}
2008-06-02 17:41:35 -04:00
2021-11-03 17:06:16 -04:00
if ( _monitor_out & & ! loading ( ) ) {
2015-10-05 10:17:49 -04:00
Glib : : Threads : : Mutex : : Lock lm ( _engine . process_lock ( ) ) ;
2012-12-04 09:32:28 -05:00
for ( RouteList : : iterator x = new_routes . begin ( ) ; x ! = new_routes . end ( ) ; + + x ) {
2021-03-24 12:47:53 -04:00
if ( ( * x ) - > can_monitor ( ) ) {
2012-12-04 09:32:28 -05:00
( * x ) - > enable_monitor_send ( ) ;
2010-11-26 14:57:03 -05:00
}
2008-06-02 17:41:35 -04:00
}
}
2016-05-17 09:36:28 -04:00
reassign_track_numbers ( ) ;
2008-06-02 17:41:35 -04:00
}
2020-07-18 17:27:11 -04:00
void
Session : : load_and_connect_instruments ( RouteList & new_routes , bool strict_io , boost : : shared_ptr < PluginInfo > instrument , Plugin : : PresetRecord * pset , ChanCount & existing_outputs )
{
if ( instrument ) {
for ( RouteList : : iterator r = new_routes . begin ( ) ; r ! = new_routes . end ( ) ; + + r ) {
PluginPtr plugin = instrument - > load ( * this ) ;
if ( ! plugin ) {
warning < < " Failed to add Synth Plugin to newly created track. " < < endmsg ;
continue ;
}
if ( pset ) {
plugin - > load_preset ( * pset ) ;
}
2021-01-13 12:47:08 -05:00
boost : : shared_ptr < PluginInsert > pi ( new PluginInsert ( * this , ( * r ) - > time_domain ( ) , plugin ) ) ;
2020-07-18 17:27:11 -04:00
if ( strict_io ) {
pi - > set_strict_io ( true ) ;
}
( * r ) - > add_processor ( pi , PreFader ) ;
if ( Profile - > get_mixbus ( ) & & pi - > configured ( ) & & pi - > output_streams ( ) . n_audio ( ) > 2 ) {
( * r ) - > move_instrument_down ( false ) ;
}
/* Route::add_processors -> Delivery::configure_io -> IO::ensure_ports
* should have registered the ports , so now we can call . . */
if ( ! ( * r ) - > instrument_fanned_out ( ) ) {
auto_connect_route ( * r , false , true , ChanCount ( ) , ChanCount ( ) , ChanCount ( ) , existing_outputs ) ;
existing_outputs + = ( * r ) - > n_outputs ( ) ;
}
}
}
for ( RouteList : : iterator r = new_routes . begin ( ) ; r ! = new_routes . end ( ) ; + + r ) {
( * r ) - > output ( ) - > changed . connect_same_thread ( * this , boost : : bind ( & Session : : midi_output_change_handler , this , _1 , _2 , boost : : weak_ptr < Route > ( * r ) ) ) ;
}
}
2009-07-21 08:05:44 -04:00
void
Session : : globally_set_send_gains_to_zero ( boost : : shared_ptr < Route > dest )
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
boost : : shared_ptr < Send > s ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2010-12-29 16:07:22 -05:00
if ( ( s = ( * i ) - > internal_send_for ( dest ) ) ! = 0 ) {
2016-01-02 04:58:23 -05:00
s - > amp ( ) - > gain_control ( ) - > set_value ( GAIN_COEFF_ZERO , Controllable : : NoGroup ) ;
2009-07-21 08:05:44 -04:00
}
}
}
void
Session : : globally_set_send_gains_to_unity ( boost : : shared_ptr < Route > dest )
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
boost : : shared_ptr < Send > s ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2010-12-29 16:07:22 -05:00
if ( ( s = ( * i ) - > internal_send_for ( dest ) ) ! = 0 ) {
2016-01-02 04:58:23 -05:00
s - > amp ( ) - > gain_control ( ) - > set_value ( GAIN_COEFF_UNITY , Controllable : : NoGroup ) ;
2009-07-21 08:05:44 -04:00
}
}
}
void
Session : : globally_set_send_gains_from_track ( boost : : shared_ptr < Route > dest )
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
boost : : shared_ptr < Send > s ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2010-12-29 16:07:22 -05:00
if ( ( s = ( * i ) - > internal_send_for ( dest ) ) ! = 0 ) {
2016-01-02 04:58:23 -05:00
s - > amp ( ) - > gain_control ( ) - > set_value ( ( * i ) - > gain_control ( ) - > get_value ( ) , Controllable : : NoGroup ) ;
2009-07-21 08:05:44 -04:00
}
}
}
2010-12-29 17:07:34 -05:00
/** @param include_buses true to add sends to buses and tracks, false for just tracks */
2009-06-25 16:46:39 -04:00
void
2010-12-29 17:07:34 -05:00
Session : : globally_add_internal_sends ( boost : : shared_ptr < Route > dest , Placement p , bool include_buses )
2009-06-25 16:46:39 -04:00
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
boost : : shared_ptr < RouteList > t ( new RouteList ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2011-08-10 16:21:18 -04:00
/* no MIDI sends because there are no MIDI busses yet */
if ( include_buses | | boost : : dynamic_pointer_cast < AudioTrack > ( * i ) ) {
2009-06-25 16:46:39 -04:00
t - > push_back ( * i ) ;
}
}
2009-07-14 20:47:34 -04:00
add_internal_sends ( dest , p , t ) ;
2009-06-25 16:46:39 -04:00
}
void
2009-07-14 20:47:34 -04:00
Session : : add_internal_sends ( boost : : shared_ptr < Route > dest , Placement p , boost : : shared_ptr < RouteList > senders )
2009-06-25 16:46:39 -04:00
{
2012-01-30 13:09:54 -05:00
for ( RouteList : : iterator i = senders - > begin ( ) ; i ! = senders - > end ( ) ; + + i ) {
add_internal_send ( dest , ( * i ) - > before_processor_for_placement ( p ) , * i ) ;
}
}
void
Session : : add_internal_send ( boost : : shared_ptr < Route > dest , int index , boost : : shared_ptr < Route > sender )
{
add_internal_send ( dest , sender - > before_processor_for_index ( index ) , sender ) ;
}
void
Session : : add_internal_send ( boost : : shared_ptr < Route > dest , boost : : shared_ptr < Processor > before , boost : : shared_ptr < Route > sender )
{
if ( sender - > is_monitor ( ) | | sender - > is_master ( ) | | sender = = dest | | dest - > is_monitor ( ) | | dest - > is_master ( ) ) {
2009-06-25 16:46:39 -04:00
return ;
}
if ( ! dest - > internal_return ( ) ) {
2012-01-30 13:09:54 -05:00
dest - > add_internal_return ( ) ;
2009-06-25 16:46:39 -04:00
}
2009-11-19 12:06:00 -05:00
2012-01-30 13:09:54 -05:00
sender - > add_aux_send ( dest , before ) ;
2019-10-28 19:23:54 -04:00
graph_reordered ( false ) ;
2009-06-25 16:46:39 -04:00
}
2008-06-02 17:41:35 -04:00
void
2015-05-08 16:04:15 -04:00
Session : : remove_routes ( boost : : shared_ptr < RouteList > routes_to_remove )
2008-06-02 17:41:35 -04:00
{
2017-04-08 17:44:18 -04:00
bool mute_changed = false ;
2017-05-08 05:36:46 -04:00
bool send_selected = false ;
2017-04-08 17:44:18 -04:00
2015-05-08 16:04:15 -04:00
{ // RCU Writer scope
2015-10-15 07:49:35 -04:00
PBD : : Unwinder < bool > uw_flag ( _route_deletion_in_progress , true ) ;
2008-06-02 17:41:35 -04:00
RCUWriter < RouteList > writer ( routes ) ;
2011-03-14 17:53:10 -04:00
boost : : shared_ptr < RouteList > rs = writer . get_copy ( ) ;
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
for ( RouteList : : iterator iter = routes_to_remove - > begin ( ) ; iter ! = routes_to_remove - > end ( ) ; + + iter ) {
2015-10-04 14:51:05 -04:00
2017-05-08 05:36:46 -04:00
if ( _selection - > selected ( * iter ) ) {
send_selected = true ;
}
2015-05-08 16:04:15 -04:00
if ( * iter = = _master_out ) {
continue ;
}
2015-10-04 14:51:05 -04:00
2016-12-07 06:50:35 -05:00
/* speed up session deletion, don't do the solo dance */
2019-03-17 12:04:45 -04:00
if ( ! deletion_in_progress ( ) ) {
2016-12-07 06:50:35 -05:00
( * iter ) - > solo_control ( ) - > set_value ( 0.0 , Controllable : : NoGroup ) ;
}
2015-10-04 14:51:05 -04:00
2017-04-08 17:44:18 -04:00
if ( ( * iter ) - > mute_control ( ) - > muted ( ) ) {
mute_changed = true ;
}
2015-05-08 16:04:15 -04:00
rs - > remove ( * iter ) ;
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
/* deleting the master out seems like a dumb
idea , but its more of a UI policy issue
than our concern .
*/
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
if ( * iter = = _master_out ) {
_master_out = boost : : shared_ptr < Route > ( ) ;
}
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
if ( * iter = = _monitor_out ) {
_monitor_out . reset ( ) ;
2009-11-30 18:34:48 -05:00
}
2015-05-08 16:04:15 -04:00
// We need to disconnect the route's inputs and outputs
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
( * iter ) - > input ( ) - > disconnect ( 0 ) ;
( * iter ) - > output ( ) - > disconnect ( 0 ) ;
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
/* if the route had internal sends sending to it, remove them */
2019-03-17 12:35:10 -04:00
if ( ! deletion_in_progress ( ) & & ( * iter ) - > internal_return ( ) ) {
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < Send > s = ( * i ) - > internal_send_for ( * iter ) ;
if ( s ) {
( * i ) - > remove_processor ( s ) ;
}
}
}
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
/* if the monitoring section had a pointer to this route, remove it */
2021-03-24 12:47:53 -04:00
if ( ! deletion_in_progress ( ) & & _monitor_out & & ( * iter ) - > can_monitor ( ) ) {
2015-05-08 16:04:15 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2016-11-25 12:07:43 -05:00
ProcessorChangeBlocker pcb ( this , false ) ;
2020-11-26 17:57:32 -05:00
( * iter ) - > remove_monitor_send ( ) ;
2015-05-08 16:04:15 -04:00
}
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( * iter ) ;
if ( mt & & mt - > step_editing ( ) ) {
if ( _step_editors > 0 ) {
_step_editors - - ;
}
}
2010-11-26 14:57:03 -05:00
}
2015-10-04 14:51:05 -04:00
2015-05-08 16:04:15 -04:00
/* writer goes out of scope, forces route list update */
2010-07-24 12:40:56 -04:00
2015-05-08 16:04:15 -04:00
} // end of RCU Writer scope
2015-10-04 14:51:05 -04:00
2017-04-08 17:44:18 -04:00
if ( mute_changed ) {
MuteChanged ( ) ; /* EMIT SIGNAL */
}
2015-09-10 11:35:25 -04:00
update_route_solo_state ( ) ;
2019-10-28 19:23:54 -04:00
update_latency_compensation ( false , false ) ;
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
2015-10-04 14:51:05 -04:00
2010-11-26 14:57:03 -05:00
/* Re-sort routes to remove the graph's current references to the one that is
2010-07-13 22:23:37 -04:00
* going away , then flush old references out of the graph .
2010-11-26 14:57:03 -05:00
*/
2010-06-24 12:20:32 -04:00
2019-09-25 14:16:13 -04:00
routes . flush ( ) ; // maybe unsafe, see below.
resort_routes ( ) ;
2015-10-04 14:51:05 -04:00
2019-03-18 10:33:05 -04:00
if ( _process_graph & & ! deletion_in_progress ( ) & & _engine . running ( ) ) {
2012-01-22 07:28:49 -05:00
_process_graph - > clear_other_chain ( ) ;
}
2015-10-04 14:51:05 -04:00
2008-06-02 17:41:35 -04:00
/* get rid of it from the dead wood collection in the route list manager */
/* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
2015-10-04 14:51:05 -04:00
2008-06-02 17:41:35 -04:00
routes . flush ( ) ;
2015-10-04 14:51:05 -04:00
2017-05-08 05:56:24 -04:00
/* remove these routes from the selection if appropriate, and signal
* the change * before * we call DropReferences for them .
2015-05-08 16:04:15 -04:00
*/
2008-06-02 17:41:35 -04:00
2017-05-08 05:36:46 -04:00
if ( send_selected & & ! deletion_in_progress ( ) ) {
for ( RouteList : : iterator iter = routes_to_remove - > begin ( ) ; iter ! = routes_to_remove - > end ( ) ; + + iter ) {
_selection - > remove_stripable_by_id ( ( * iter ) - > id ( ) ) ;
}
2017-05-08 05:56:24 -04:00
PropertyChange pc ;
pc . add ( Properties : : selected ) ;
PresentationInfo : : Change ( pc ) ;
2017-05-08 05:36:46 -04:00
}
2017-05-08 05:56:24 -04:00
/* try to cause everyone to drop their references
* and unregister ports from the backend
*/
2015-05-08 16:04:15 -04:00
for ( RouteList : : iterator iter = routes_to_remove - > begin ( ) ; iter ! = routes_to_remove - > end ( ) ; + + iter ) {
( * iter ) - > drop_references ( ) ;
}
2015-10-04 14:51:05 -04:00
2017-05-08 05:36:46 -04:00
if ( deletion_in_progress ( ) ) {
2016-04-23 20:51:23 -04:00
return ;
}
2017-05-08 05:56:24 -04:00
PropertyChange pc ;
2017-05-08 06:03:35 -04:00
pc . add ( Properties : : order ) ;
2017-05-08 05:56:24 -04:00
PresentationInfo : : Change ( pc ) ;
2015-10-04 14:51:05 -04:00
2014-09-19 09:45:01 -04:00
update_route_record_state ( ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2015-05-08 16:04:15 -04:00
void
Session : : remove_route ( boost : : shared_ptr < Route > route )
{
boost : : shared_ptr < RouteList > rl ( new RouteList ) ;
rl - > push_back ( route ) ;
remove_routes ( rl ) ;
}
2008-06-02 17:41:35 -04:00
void
2016-03-11 10:34:51 -05:00
Session : : route_mute_changed ( )
2008-06-02 17:41:35 -04:00
{
2017-04-07 17:26:59 -04:00
MuteChanged ( ) ; /* EMIT SIGNAL */
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
}
2009-07-01 09:36:50 -04:00
void
2016-03-11 10:34:51 -05:00
Session : : route_listen_changed ( Controllable : : GroupControlDisposition group_override , boost : : weak_ptr < Route > wpr )
2009-07-01 09:36:50 -04:00
{
2016-03-11 10:34:51 -05:00
boost : : shared_ptr < Route > route ( wpr . lock ( ) ) ;
if ( ! route ) {
return ;
}
2016-04-21 02:02:13 -04:00
assert ( Config - > get_solo_control_is_listen_control ( ) ) ;
if ( route - > solo_control ( ) - > soloed_by_self_or_masters ( ) ) {
2010-05-05 11:47:51 -04:00
2010-11-26 14:57:03 -05:00
if ( Config - > get_exclusive_solo ( ) ) {
2016-01-21 11:03:14 -05:00
2021-02-07 08:02:50 -05:00
_engine . monitor_port ( ) . clear_ports ( false ) ;
2015-10-04 18:27:48 -04:00
RouteGroup * rg = route - > route_group ( ) ;
2016-04-21 01:45:09 -04:00
const bool group_already_accounted_for = ( group_override = = Controllable : : ForGroup ) ;
2016-01-21 11:03:14 -05:00
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2016-01-21 11:03:14 -05:00
2010-11-26 14:57:03 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2016-01-21 11:03:14 -05:00
if ( ( * i ) = = route ) {
/* already changed */
continue ;
}
2021-03-24 12:47:53 -04:00
if ( ( * i ) - > solo_isolate_control ( ) - > solo_isolated ( ) | | ! ( * i ) - > can_monitor ( ) ) {
2016-01-21 11:03:14 -05:00
/* route does not get solo propagated to it */
continue ;
}
if ( ( group_already_accounted_for & & ( * i ) - > route_group ( ) & & ( * i ) - > route_group ( ) = = rg ) ) {
/* this route is a part of the same solo group as the route
* that was changed . Changing that route did change or will
* change all group members appropriately , so we can ignore it
* here
*/
2010-11-26 14:57:03 -05:00
continue ;
2011-06-01 12:50:12 -04:00
}
2016-04-08 16:49:47 -04:00
( * i ) - > solo_control ( ) - > set_value ( 0.0 , Controllable : : NoGroup ) ;
2010-11-26 14:57:03 -05:00
}
}
2010-05-05 11:47:51 -04:00
2009-07-01 09:36:50 -04:00
_listen_cnt + + ;
2010-05-05 11:47:51 -04:00
2009-07-01 09:36:50 -04:00
} else if ( _listen_cnt > 0 ) {
2010-05-05 11:47:51 -04:00
2009-07-01 09:36:50 -04:00
_listen_cnt - - ;
}
}
2016-03-09 13:11:53 -05:00
2010-05-06 14:40:37 -04:00
void
2016-03-11 10:34:51 -05:00
Session : : route_solo_isolated_changed ( boost : : weak_ptr < Route > wpr )
2010-05-06 14:40:37 -04:00
{
2016-03-11 10:34:51 -05:00
boost : : shared_ptr < Route > route ( wpr . lock ( ) ) ;
if ( ! route ) {
return ;
}
2011-03-14 17:53:10 -04:00
bool send_changed = false ;
2016-04-08 16:49:47 -04:00
if ( route - > solo_isolate_control ( ) - > solo_isolated ( ) ) {
2011-03-14 17:53:10 -04:00
if ( _solo_isolated_cnt = = 0 ) {
send_changed = true ;
}
_solo_isolated_cnt + + ;
} else if ( _solo_isolated_cnt > 0 ) {
_solo_isolated_cnt - - ;
if ( _solo_isolated_cnt = = 0 ) {
send_changed = true ;
}
}
if ( send_changed ) {
IsolatedChanged ( ) ; /* EMIT SIGNAL */
}
2010-05-06 14:40:37 -04:00
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
void
2016-04-20 23:22:29 -04:00
Session : : route_solo_changed ( bool self_solo_changed , Controllable : : GroupControlDisposition group_override , boost : : weak_ptr < Route > wpr )
2008-08-04 18:37:24 -04:00
{
2016-11-25 05:29:16 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " route solo change, self = %1, update \n " , self_solo_changed ) ) ;
2011-08-10 11:13:15 -04:00
2016-03-11 10:34:51 -05:00
boost : : shared_ptr < Route > route ( wpr . lock ( ) ) ;
if ( ! route ) {
return ;
}
if ( Config - > get_solo_control_is_listen_control ( ) ) {
route_listen_changed ( group_override , wpr ) ;
return ;
}
2016-04-20 23:22:29 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " %1: self %2 masters %3 transition %4 \n " , route - > name ( ) , route - > self_soloed ( ) , route - > solo_control ( ) - > get_masters_value ( ) , route - > solo_control ( ) - > transitioned_into_solo ( ) ) ) ;
if ( route - > solo_control ( ) - > transitioned_into_solo ( ) = = 0 ) {
2016-11-25 06:50:08 -05:00
/* route solo changed by upstream/downstream or clear all solo state; not interesting
2016-04-20 23:22:29 -04:00
to Session .
*/
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " %1 not self-soloed nor soloed by master (%2), ignoring \n " , route - > name ( ) , route - > solo_control ( ) - > get_masters_value ( ) ) ) ;
2010-11-26 14:57:03 -05:00
return ;
}
2010-04-26 23:10:53 -04:00
2016-04-20 23:22:29 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
int32_t delta = route - > solo_control ( ) - > transitioned_into_solo ( ) ;
2016-01-21 11:03:14 -05:00
/* the route may be a member of a group that has shared-solo
* semantics . If so , then all members of that group should follow the
* solo of the changed route . But . . . this is optional , controlled by a
* Controllable : : GroupControlDisposition .
*
* The first argument to the signal that this method is connected to is the
* GroupControlDisposition value that was used to change solo .
*
2016-01-21 12:04:20 -05:00
* If the solo change was done with group semantics ( either InverseGroup
2016-01-21 11:03:14 -05:00
* ( force the entire group to change even if the group shared solo is
* disabled ) or UseGroup ( use the group , which may or may not have the
* shared solo property enabled ) ) then as we propagate the change to
* the entire session we should IGNORE THE GROUP that the changed route
* belongs to .
*/
2011-07-25 12:10:11 -04:00
RouteGroup * rg = route - > route_group ( ) ;
2016-03-14 10:42:01 -04:00
const bool group_already_accounted_for = ( group_override = = Controllable : : ForGroup ) ;
2016-01-21 11:03:14 -05:00
2016-11-25 05:29:16 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " propagate to session, group accounted for ? %1 \n " , group_already_accounted_for ) ) ;
2010-11-26 14:57:03 -05:00
if ( delta = = 1 & & Config - > get_exclusive_solo ( ) ) {
2015-10-05 10:17:49 -04:00
2011-07-25 12:10:11 -04:00
/* new solo: disable all other solos, but not the group if its solo-enabled */
2021-02-07 08:02:50 -05:00
_engine . monitor_port ( ) . clear_ports ( false ) ;
2011-07-25 12:10:11 -04:00
2010-11-26 14:57:03 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2016-01-21 11:03:14 -05:00
if ( ( * i ) = = route ) {
/* already changed */
continue ;
}
2016-04-08 16:49:47 -04:00
if ( ( * i ) - > solo_isolate_control ( ) - > solo_isolated ( ) | | ! ( * i ) - > can_solo ( ) ) {
2016-01-21 11:03:14 -05:00
/* route does not get solo propagated to it */
continue ;
}
if ( ( group_already_accounted_for & & ( * i ) - > route_group ( ) & & ( * i ) - > route_group ( ) = = rg ) ) {
/* this route is a part of the same solo group as the route
* that was changed . Changing that route did change or will
* change all group members appropriately , so we can ignore it
* here
*/
2010-11-26 14:57:03 -05:00
continue ;
2011-06-01 12:50:12 -04:00
}
2016-01-21 11:03:14 -05:00
2016-04-08 16:49:47 -04:00
( * i ) - > solo_control ( ) - > set_value ( 0.0 , group_override ) ;
2010-11-26 14:57:03 -05:00
}
}
2008-08-04 18:37:24 -04:00
2011-08-10 11:13:15 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " propagate solo change, delta = %1 \n " , delta ) ) ;
2010-11-26 14:57:03 -05:00
RouteList uninvolved ;
2011-06-01 12:50:12 -04:00
2011-08-10 11:13:15 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " %1 \n " , route - > name ( ) ) ) ;
2009-06-09 16:21:19 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2010-11-26 14:57:03 -05:00
bool in_signal_flow ;
2009-06-10 09:59:06 -04:00
2016-01-21 11:03:14 -05:00
if ( ( * i ) = = route ) {
/* already changed */
continue ;
}
2016-04-08 16:49:47 -04:00
if ( ( * i ) - > solo_isolate_control ( ) - > solo_isolated ( ) | | ! ( * i ) - > can_solo ( ) ) {
2016-01-21 11:03:14 -05:00
/* route does not get solo propagated to it */
2016-07-20 16:10:11 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " %1 excluded from solo because iso = %2 can_solo = %3 \n " , ( * i ) - > name ( ) , ( * i ) - > solo_isolate_control ( ) - > solo_isolated ( ) ,
( * i ) - > can_solo ( ) ) ) ;
2016-01-21 11:03:14 -05:00
continue ;
}
if ( ( group_already_accounted_for & & ( * i ) - > route_group ( ) & & ( * i ) - > route_group ( ) = = rg ) ) {
/* this route is a part of the same solo group as the route
* that was changed . Changing that route did change or will
* change all group members appropriately , so we can ignore it
* here
*/
2009-11-21 12:20:57 -05:00
continue ;
2011-06-01 12:50:12 -04:00
}
2010-04-26 23:10:53 -04:00
2010-11-26 14:57:03 -05:00
in_signal_flow = false ;
2010-04-27 13:10:04 -04:00
2011-08-10 11:13:15 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " check feed from %1 \n " , ( * i ) - > name ( ) ) ) ;
2015-10-05 10:17:49 -04:00
2020-10-22 17:56:50 -04:00
if ( ( * i ) - > feeds ( route ) ) {
2012-11-21 14:56:55 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " \t there is a feed from %1 \n " , ( * i ) - > name ( ) ) ) ;
2020-10-22 17:56:50 -04:00
if ( ! route - > soloed_by_others_upstream ( ) ) {
( * i ) - > solo_control ( ) - > mod_solo_by_others_downstream ( delta ) ;
2012-11-21 14:56:55 -05:00
} else {
2020-10-22 17:56:50 -04:00
DEBUG_TRACE ( DEBUG : : Solo , " \t already soloed by others upstream \n " ) ;
2009-11-21 12:20:57 -05:00
}
2011-08-10 11:13:15 -04:00
in_signal_flow = true ;
} else {
2012-11-21 14:56:55 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " \t no feed from %1 \n " , ( * i ) - > name ( ) ) ) ;
2011-06-01 12:50:12 -04:00
}
2015-10-05 10:17:49 -04:00
2011-08-10 11:13:15 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " check feed to %1 \n " , ( * i ) - > name ( ) ) ) ;
2011-06-01 12:50:12 -04:00
2020-10-22 17:56:50 -04:00
if ( route - > feeds ( * i ) ) {
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " %1 feeds %2 sboD %3 sboU %4 \n " ,
2016-04-20 23:22:29 -04:00
route - > name ( ) ,
( * i ) - > name ( ) ,
route - > soloed_by_others_downstream ( ) ,
route - > soloed_by_others_upstream ( ) ) ) ;
2020-10-22 17:56:50 -04:00
//NB. Triggers Invert Push, which handles soloed by downstream
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " \t mod %1 by %2 \n " , ( * i ) - > name ( ) , delta ) ) ;
( * i ) - > solo_control ( ) - > mod_solo_by_others_upstream ( delta ) ;
2010-11-26 14:57:03 -05:00
in_signal_flow = true ;
2011-08-10 11:13:15 -04:00
} else {
2019-04-12 13:22:59 -04:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " \t no feed to %1 \n " , ( * i ) - > name ( ) ) ) ;
2010-11-26 14:57:03 -05:00
}
2010-05-03 18:07:47 -04:00
2010-11-26 14:57:03 -05:00
if ( ! in_signal_flow ) {
uninvolved . push_back ( * i ) ;
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2011-08-10 11:13:15 -04:00
DEBUG_TRACE ( DEBUG : : Solo , " propagation complete \n " ) ;
2010-11-26 14:57:03 -05:00
/* now notify that the mute state of the routes not involved in the signal
pathway of the just - solo - changed route may have altered .
*/
2010-05-03 18:07:47 -04:00
2010-11-26 14:57:03 -05:00
for ( RouteList : : iterator i = uninvolved . begin ( ) ; i ! = uninvolved . end ( ) ; + + i ) {
2012-11-21 14:56:55 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " mute change for %1, which neither feeds or is fed by %2 \n " , ( * i ) - > name ( ) , route - > name ( ) ) ) ;
2015-03-24 13:47:37 -04:00
( * i ) - > act_on_mute ( ) ;
2016-11-25 05:29:16 -05:00
/* Session will emit SoloChanged() after all solo changes are
* complete , which should be used by UIs to update mute status
*/
2010-11-26 14:57:03 -05:00
}
2008-06-02 17:41:35 -04:00
}
void
2009-06-10 14:10:07 -04:00
Session : : update_route_solo_state ( boost : : shared_ptr < RouteList > r )
2008-06-02 17:41:35 -04:00
{
2010-03-10 12:31:16 -05:00
/* now figure out if anything that matters is soloed (or is "listening")*/
2008-06-02 17:41:35 -04:00
2009-06-10 14:10:07 -04:00
bool something_soloed = false ;
2016-01-16 17:40:03 -05:00
bool something_listening = false ;
2010-11-26 14:57:03 -05:00
uint32_t listeners = 0 ;
uint32_t isolated = 0 ;
2008-08-04 18:37:24 -04:00
2009-06-10 14:10:07 -04:00
if ( ! r ) {
r = routes . reader ( ) ;
}
2008-06-02 17:41:35 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2021-03-24 12:47:53 -04:00
if ( ( * i ) - > can_monitor ( ) & & Config - > get_solo_control_is_listen_control ( ) ) {
if ( ( * i ) - > solo_control ( ) - > soloed_by_self_or_masters ( ) ) {
listeners + + ;
something_listening = true ;
}
} else if ( ( * i ) - > can_solo ( ) ) {
( * i ) - > set_listen ( false ) ;
if ( ( * i ) - > can_solo ( ) & & ( * i ) - > solo_control ( ) - > soloed_by_self_or_masters ( ) ) {
something_soloed = true ;
2010-11-26 14:57:03 -05:00
}
}
2010-05-06 14:40:37 -04:00
2016-04-08 16:49:47 -04:00
if ( ( * i ) - > solo_isolate_control ( ) - > solo_isolated ( ) ) {
2010-11-26 14:57:03 -05:00
isolated + + ;
}
2008-06-02 17:41:35 -04:00
}
2010-11-26 14:57:03 -05:00
if ( something_soloed ! = _non_soloed_outs_muted ) {
_non_soloed_outs_muted = something_soloed ;
SoloActive ( _non_soloed_outs_muted ) ; /* EMIT SIGNAL */
}
2010-03-06 14:47:34 -05:00
2016-01-16 17:40:03 -05:00
if ( something_listening ! = _listening ) {
_listening = something_listening ;
SoloActive ( _listening ) ;
}
2010-11-26 14:57:03 -05:00
_listen_cnt = listeners ;
2010-05-06 14:40:37 -04:00
2010-11-26 14:57:03 -05:00
if ( isolated ! = _solo_isolated_cnt ) {
_solo_isolated_cnt = isolated ;
IsolatedChanged ( ) ; /* EMIT SIGNAL */
}
2012-11-21 14:56:55 -05:00
DEBUG_TRACE ( DEBUG : : Solo , string_compose ( " solo state updated by session, soloed? %1 listeners %2 isolated %3 \n " ,
something_soloed , listeners , isolated ) ) ;
2016-11-25 05:29:16 -05:00
SoloChanged ( ) ; /* EMIT SIGNAL */
set_dirty ( ) ;
2008-06-02 17:41:35 -04:00
}
2017-05-05 10:47:25 -04:00
bool
Session : : muted ( ) const
{
// TODO consider caching the value on every MuteChanged signal,
// Note that API users may also subscribe to MuteChanged and hence
// this method needs to be called first.
bool muted = false ;
StripableList all ;
get_stripables ( all ) ;
for ( StripableList : : const_iterator i = all . begin ( ) ; i ! = all . end ( ) ; + + i ) {
2017-08-15 17:24:32 -04:00
assert ( ! ( * i ) - > is_auditioner ( ) ) ; // XXX remove me
if ( ( * i ) - > is_monitor ( ) ) {
2017-05-05 10:47:25 -04:00
continue ;
}
boost : : shared_ptr < Route > r = boost : : dynamic_pointer_cast < Route > ( * i ) ;
if ( r & & ! r - > active ( ) ) {
continue ;
}
boost : : shared_ptr < MuteControl > mc = ( * i ) - > mute_control ( ) ;
if ( mc & & mc - > muted ( ) ) {
muted = true ;
break ;
}
}
return muted ;
}
std : : vector < boost : : weak_ptr < AutomationControl > >
Session : : cancel_all_mute ( )
{
StripableList all ;
get_stripables ( all ) ;
std : : vector < boost : : weak_ptr < AutomationControl > > muted ;
boost : : shared_ptr < ControlList > cl ( new ControlList ) ;
for ( StripableList : : const_iterator i = all . begin ( ) ; i ! = all . end ( ) ; + + i ) {
2017-08-15 17:24:32 -04:00
assert ( ! ( * i ) - > is_auditioner ( ) ) ;
if ( ( * i ) - > is_monitor ( ) ) {
2017-05-05 10:47:25 -04:00
continue ;
}
boost : : shared_ptr < Route > r = boost : : dynamic_pointer_cast < Route > ( * i ) ;
if ( r & & ! r - > active ( ) ) {
continue ;
}
boost : : shared_ptr < AutomationControl > ac = ( * i ) - > mute_control ( ) ;
if ( ac & & ac - > get_value ( ) > 0 ) {
cl - > push_back ( ac ) ;
muted . push_back ( boost : : weak_ptr < AutomationControl > ( ac ) ) ;
}
}
if ( ! cl - > empty ( ) ) {
set_controls ( cl , 0.0 , PBD : : Controllable : : UseGroup ) ;
}
return muted ;
}
2016-05-16 16:43:00 -04:00
void
2018-11-07 16:02:49 -05:00
Session : : get_stripables ( StripableList & sl , PresentationInfo : : Flag fl ) const
2016-05-16 16:43:00 -04:00
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2018-11-07 16:02:49 -05:00
for ( RouteList : : iterator it = r - > begin ( ) ; it ! = r - > end ( ) ; + + it ) {
if ( ( * it ) - > presentation_info ( ) . flags ( ) & fl ) {
sl . push_back ( * it ) ;
}
}
2016-05-16 16:43:00 -04:00
2018-11-07 16:02:49 -05:00
if ( fl & PresentationInfo : : VCA ) {
VCAList v = _vca_manager - > vcas ( ) ;
sl . insert ( sl . end ( ) , v . begin ( ) , v . end ( ) ) ;
}
2016-05-16 16:43:00 -04:00
}
2017-06-16 22:35:48 -04:00
StripableList
Session : : get_stripables ( ) const
{
2018-11-07 16:02:49 -05:00
PresentationInfo : : Flag fl = PresentationInfo : : AllStripables ;
2017-06-16 22:35:48 -04:00
StripableList rv ;
2018-11-07 16:02:49 -05:00
Session : : get_stripables ( rv , fl ) ;
2017-06-16 22:35:48 -04:00
rv . sort ( Stripable : : Sorter ( ) ) ;
return rv ;
}
2018-08-02 15:02:31 -04:00
RouteList
2018-11-07 16:02:49 -05:00
Session : : get_routelist ( bool mixer_order , PresentationInfo : : Flag fl ) const
2018-08-02 15:02:31 -04:00
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
RouteList rv ;
2018-11-07 16:02:49 -05:00
for ( RouteList : : iterator it = r - > begin ( ) ; it ! = r - > end ( ) ; + + it ) {
if ( ( * it ) - > presentation_info ( ) . flags ( ) & fl ) {
rv . push_back ( * it ) ;
}
}
2018-08-02 15:02:31 -04:00
rv . sort ( Stripable : : Sorter ( mixer_order ) ) ;
return rv ;
}
2011-06-01 12:50:12 -04:00
boost : : shared_ptr < RouteList >
2009-11-18 23:25:46 -05:00
Session : : get_routes_with_internal_returns ( ) const
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2009-11-18 23:25:46 -05:00
boost : : shared_ptr < RouteList > rl ( new RouteList ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > internal_return ( ) ) {
rl - > push_back ( * i ) ;
}
}
return rl ;
}
2010-04-20 22:24:38 -04:00
bool
2016-05-16 07:30:28 -04:00
Session : : io_name_is_legal ( const std : : string & name ) const
2010-04-20 22:24:38 -04:00
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2011-06-01 12:50:12 -04:00
2016-12-10 18:03:44 -05:00
for ( map < string , bool > : : const_iterator reserved = reserved_io_names . begin ( ) ; reserved ! = reserved_io_names . end ( ) ; + + reserved ) {
if ( name = = reserved - > first ) {
if ( ! route_by_name ( reserved - > first ) ) {
2017-05-10 14:28:47 -04:00
/* first instance of a reserved name is allowed for some */
return reserved - > second ;
2015-11-07 22:15:59 -05:00
}
2015-11-09 08:24:35 -05:00
/* all other instances of a reserved name are not allowed */
return false ;
2015-09-28 14:49:49 -04:00
}
}
2015-10-05 10:17:49 -04:00
2010-11-26 14:57:03 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > name ( ) = = name ) {
return false ;
}
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
if ( ( * i ) - > has_io_processor_named ( name ) ) {
return false ;
}
}
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
return true ;
2010-04-20 22:24:38 -04:00
}
2011-07-03 11:01:21 -04:00
void
2012-11-12 21:19:04 -05:00
Session : : set_exclusive_input_active ( boost : : shared_ptr < RouteList > rl , bool onoff , bool flip_others )
2011-07-03 11:01:21 -04:00
{
2012-11-12 21:19:04 -05:00
RouteList rl2 ;
2011-07-03 11:01:21 -04:00
vector < string > connections ;
2012-11-12 21:19:04 -05:00
/* if we are passed only a single route and we're not told to turn
* others off , then just do the simple thing .
*/
2011-07-03 11:01:21 -04:00
2012-11-12 21:19:04 -05:00
if ( flip_others = = false & & rl - > size ( ) = = 1 ) {
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( rl - > front ( ) ) ;
if ( mt ) {
mt - > set_input_active ( onoff ) ;
return ;
}
2011-07-03 11:01:21 -04:00
}
2012-11-12 21:19:04 -05:00
for ( RouteList : : iterator rt = rl - > begin ( ) ; rt ! = rl - > end ( ) ; + + rt ) {
2011-07-03 11:01:21 -04:00
2012-11-12 21:19:04 -05:00
PortSet & ps ( ( * rt ) - > input ( ) - > ports ( ) ) ;
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
for ( PortSet : : iterator p = ps . begin ( ) ; p ! = ps . end ( ) ; + + p ) {
p - > get_connections ( connections ) ;
}
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
for ( vector < string > : : iterator s = connections . begin ( ) ; s ! = connections . end ( ) ; + + s ) {
routes_using_input_from ( * s , rl2 ) ;
}
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
/* scan all relevant routes to see if others are on or off */
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
bool others_are_already_on = false ;
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
for ( RouteList : : iterator r = rl2 . begin ( ) ; r ! = rl2 . end ( ) ; + + r ) {
2011-07-03 11:01:21 -04:00
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( * r ) ;
2012-11-12 21:19:04 -05:00
if ( ! mt ) {
continue ;
}
if ( ( * r ) ! = ( * rt ) ) {
2011-07-03 11:01:21 -04:00
if ( mt - > input_active ( ) ) {
others_are_already_on = true ;
}
2012-11-12 21:19:04 -05:00
} else {
/* this one needs changing */
mt - > set_input_active ( onoff ) ;
2011-07-03 11:01:21 -04:00
}
}
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
if ( flip_others ) {
2011-07-03 11:01:21 -04:00
2012-11-12 21:19:04 -05:00
/* globally reverse other routes */
2015-10-05 10:17:49 -04:00
2012-11-12 21:19:04 -05:00
for ( RouteList : : iterator r = rl2 . begin ( ) ; r ! = rl2 . end ( ) ; + + r ) {
if ( ( * r ) ! = ( * rt ) ) {
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( * r ) ;
if ( mt ) {
mt - > set_input_active ( ! others_are_already_on ) ;
}
}
2011-07-03 11:01:21 -04:00
}
}
}
}
void
Session : : routes_using_input_from ( const string & str , RouteList & rl )
{
2012-11-12 21:19:04 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2011-07-03 11:01:21 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > input ( ) - > connected_to ( str ) ) {
rl . push_back ( * i ) ;
}
}
}
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < Route >
2016-05-16 07:30:28 -04:00
Session : : route_by_name ( string name ) const
2008-06-02 17:41:35 -04:00
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-06-02 17:41:35 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > name ( ) = = name ) {
return * i ;
}
}
2010-11-29 17:07:42 -05:00
return boost : : shared_ptr < Route > ( ( Route * ) 0 ) ;
2008-06-02 17:41:35 -04:00
}
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < Route >
2016-05-16 07:30:28 -04:00
Session : : route_by_id ( PBD : : ID id ) const
2008-06-02 17:41:35 -04:00
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-06-02 17:41:35 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > id ( ) = = id ) {
return * i ;
}
}
2010-11-29 17:07:42 -05:00
return boost : : shared_ptr < Route > ( ( Route * ) 0 ) ;
2008-06-02 17:41:35 -04:00
}
2017-05-05 07:31:21 -04:00
boost : : shared_ptr < Stripable >
Session : : stripable_by_id ( PBD : : ID id ) const
{
StripableList sl ;
get_stripables ( sl ) ;
for ( StripableList : : const_iterator s = sl . begin ( ) ; s ! = sl . end ( ) ; + + s ) {
if ( ( * s ) - > id ( ) = = id ) {
return * s ;
}
}
return boost : : shared_ptr < Stripable > ( ) ;
}
2022-01-27 13:40:01 -05:00
boost : : shared_ptr < Trigger >
Session : : trigger_by_id ( PBD : : ID id ) const
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < TriggerBox > box = ( * i ) - > triggerbox ( ) ;
if ( box ) {
TriggerPtr trigger = box - > trigger_by_id ( id ) ;
if ( trigger ) {
return trigger ;
}
}
}
return boost : : shared_ptr < Trigger > ( ) ;
}
2016-02-20 14:22:40 -05:00
boost : : shared_ptr < Processor >
Session : : processor_by_id ( PBD : : ID id ) const
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < Processor > p = ( * i ) - > Route : : processor_by_id ( id ) ;
if ( p ) {
return p ;
}
}
return boost : : shared_ptr < Processor > ( ) ;
}
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < Route >
2016-05-17 15:26:35 -04:00
Session : : get_remote_nth_route ( PresentationInfo : : order_t n ) const
2016-03-07 14:59:40 -05:00
{
2016-05-16 07:30:28 -04:00
return boost : : dynamic_pointer_cast < Route > ( get_remote_nth_stripable ( n , PresentationInfo : : Route ) ) ;
2016-03-07 14:59:40 -05:00
}
boost : : shared_ptr < Stripable >
2016-05-17 15:26:35 -04:00
Session : : get_remote_nth_stripable ( PresentationInfo : : order_t n , PresentationInfo : : Flag flags ) const
2008-06-02 17:41:35 -04:00
{
2016-05-17 15:05:36 -04:00
StripableList sl ;
2016-05-17 15:26:35 -04:00
PresentationInfo : : order_t match_cnt = 0 ;
2016-05-16 07:30:28 -04:00
2016-05-17 15:05:36 -04:00
get_stripables ( sl ) ;
2017-06-16 17:44:29 -04:00
sl . sort ( Stripable : : Sorter ( ) ) ;
2008-06-02 17:41:35 -04:00
2016-05-17 15:05:36 -04:00
for ( StripableList : : const_iterator s = sl . begin ( ) ; s ! = sl . end ( ) ; + + s ) {
2016-08-01 12:02:30 -04:00
if ( ( * s ) - > presentation_info ( ) . hidden ( ) ) {
/* if the caller didn't explicitly ask for hidden
stripables , ignore hidden ones . This matches
the semantics of the pre - PresentationOrder
" get by RID " logic of Ardour 4. x and earlier .
XXX at some point we should likely reverse
the logic of the flags , because asking for " the
hidden stripables " is not going to be common,
whereas asking for visible ones is normal .
*/
if ( ! ( flags & PresentationInfo : : Hidden ) ) {
continue ;
}
}
2016-05-17 15:05:36 -04:00
if ( ( * s ) - > presentation_info ( ) . flag_match ( flags ) ) {
if ( match_cnt + + = = n ) {
return * s ;
}
2008-06-02 17:41:35 -04:00
}
}
2016-05-17 15:05:36 -04:00
/* there is no nth stripable that matches the given flags */
return boost : : shared_ptr < Stripable > ( ) ;
2008-06-02 17:41:35 -04:00
}
2017-01-22 13:48:10 -05:00
boost : : shared_ptr < Route >
Session : : route_by_selected_count ( uint32_t id ) const
{
RouteList r ( * ( routes . reader ( ) ) ) ;
2017-06-16 17:44:29 -04:00
r . sort ( Stripable : : Sorter ( ) ) ;
2017-01-22 13:48:10 -05:00
RouteList : : iterator i ;
for ( i = r . begin ( ) ; i ! = r . end ( ) ; + + i ) {
2017-05-05 07:31:21 -04:00
if ( ( * i ) - > is_selected ( ) ) {
2017-01-22 13:48:10 -05:00
if ( id = = 0 ) {
return * i ;
}
- - id ;
}
}
return boost : : shared_ptr < Route > ( ) ;
}
2014-06-25 15:16:09 -04:00
void
Session : : reassign_track_numbers ( )
{
int64_t tn = 0 ;
int64_t bn = 0 ;
2021-10-13 11:07:07 -04:00
uint32_t trigger_order = 0 ;
2014-06-25 15:16:09 -04:00
RouteList r ( * ( routes . reader ( ) ) ) ;
2017-06-16 17:44:29 -04:00
r . sort ( Stripable : : Sorter ( ) ) ;
2014-06-25 15:16:09 -04:00
2014-06-28 15:36:13 -04:00
StateProtector sp ( this ) ;
2014-06-25 15:16:09 -04:00
for ( RouteList : : iterator i = r . begin ( ) ; i ! = r . end ( ) ; + + i ) {
2017-08-15 17:24:32 -04:00
assert ( ! ( * i ) - > is_auditioner ( ) ) ;
2014-06-25 15:16:09 -04:00
if ( boost : : dynamic_pointer_cast < Track > ( * i ) ) {
( * i ) - > set_track_number ( + + tn ) ;
2021-10-13 11:07:07 -04:00
} else if ( ! ( * i ) - > is_master ( ) & & ! ( * i ) - > is_monitor ( ) ) {
2014-06-25 15:16:09 -04:00
( * i ) - > set_track_number ( - - bn ) ;
}
2021-10-13 11:07:07 -04:00
boost : : shared_ptr < TriggerBox > tb = ( * i ) - > triggerbox ( ) ;
if ( tb ) {
tb - > set_order ( trigger_order ) ;
trigger_order + + ;
}
2014-06-25 15:16:09 -04:00
}
const uint32_t decimals = ceilf ( log10f ( tn + 1 ) ) ;
const bool decimals_changed = _track_number_decimals ! = decimals ;
_track_number_decimals = decimals ;
if ( decimals_changed & & config . get_track_name_number ( ) ) {
for ( RouteList : : iterator i = r . begin ( ) ; i ! = r . end ( ) ; + + i ) {
boost : : shared_ptr < Track > t = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( t ) {
2021-03-15 17:36:42 -04:00
t - > resync_take_name ( ) ;
2014-06-25 15:16:09 -04:00
}
}
// trigger GUI re-layout
config . ParameterChanged ( " track-name-number " ) ;
}
2016-05-17 09:36:28 -04:00
# ifndef NDEBUG
if ( DEBUG_ENABLED ( DEBUG : : OrderKeys ) ) {
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
DEBUG_TRACE ( DEBUG : : OrderKeys , string_compose ( " %1 numbered %2 \n " , ( * i ) - > name ( ) , ( * i ) - > track_number ( ) ) ) ;
}
}
# endif /* NDEBUG */
2014-06-25 15:16:09 -04:00
}
2011-01-19 12:38:56 -05:00
void
Session : : playlist_region_added ( boost : : weak_ptr < Region > w )
{
boost : : shared_ptr < Region > r = w . lock ( ) ;
if ( ! r ) {
return ;
}
/* These are the operations that are currently in progress... */
list < GQuark > curr = _current_trans_quarks ;
curr . sort ( ) ;
/* ...and these are the operations during which we want to update
the session range location markers .
*/
list < GQuark > ops ;
ops . push_back ( Operations : : capture ) ;
ops . push_back ( Operations : : paste ) ;
ops . push_back ( Operations : : duplicate_region ) ;
ops . push_back ( Operations : : insert_file ) ;
ops . push_back ( Operations : : insert_region ) ;
ops . push_back ( Operations : : drag_region_brush ) ;
ops . push_back ( Operations : : region_drag ) ;
ops . push_back ( Operations : : selection_grab ) ;
ops . push_back ( Operations : : region_fill ) ;
ops . push_back ( Operations : : fill_selection ) ;
ops . push_back ( Operations : : create_region ) ;
2011-05-12 17:29:27 -04:00
ops . push_back ( Operations : : region_copy ) ;
ops . push_back ( Operations : : fixed_time_region_copy ) ;
2011-01-19 12:38:56 -05:00
ops . sort ( ) ;
/* See if any of the current operations match the ones that we want */
list < GQuark > in ;
set_intersection ( _current_trans_quarks . begin ( ) , _current_trans_quarks . end ( ) , ops . begin ( ) , ops . end ( ) , back_inserter ( in ) ) ;
/* If so, update the session range markers */
if ( ! in . empty ( ) ) {
2020-11-30 12:59:08 -05:00
maybe_update_session_range ( r - > position ( ) , r - > end ( ) ) ;
2011-01-19 12:38:56 -05:00
}
}
/** Update the session range markers if a is before the current start or
* b is after the current end .
2010-05-09 16:48:21 -04:00
*/
2008-06-02 17:41:35 -04:00
void
2020-09-20 18:34:09 -04:00
Session : : maybe_update_session_range ( timepos_t const & a , timepos_t const & b )
2008-06-02 17:41:35 -04:00
{
2019-03-18 10:33:05 -04:00
if ( loading ( ) ) {
2008-06-02 17:41:35 -04:00
return ;
}
2017-09-18 12:39:17 -04:00
samplepos_t session_end_marker_shift_samples = session_end_shift * _nominal_sample_rate ;
2015-05-13 10:39:24 -04:00
2010-05-09 16:48:21 -04:00
if ( _session_range_location = = 0 ) {
2011-06-01 12:50:12 -04:00
2020-09-20 18:34:09 -04:00
set_session_extents ( a , b + timepos_t ( session_end_marker_shift_samples ) ) ;
2011-06-01 12:50:12 -04:00
2010-05-09 16:48:21 -04:00
} else {
2011-06-01 12:50:12 -04:00
2019-02-06 10:54:13 -05:00
if ( _session_range_is_free & & ( a < _session_range_location - > start ( ) ) ) {
2011-01-19 12:38:56 -05:00
_session_range_location - > set_start ( a ) ;
2010-05-09 16:48:21 -04:00
}
2011-06-01 12:50:12 -04:00
2019-02-06 10:54:13 -05:00
if ( _session_range_is_free & & ( b > _session_range_location - > end ( ) ) ) {
2011-01-19 12:38:56 -05:00
_session_range_location - > set_end ( b ) ;
}
2008-06-02 17:41:35 -04:00
}
}
2016-07-12 11:19:15 -04:00
void
2019-02-06 10:54:13 -05:00
Session : : set_session_range_is_free ( bool yn )
2016-07-12 11:19:15 -04:00
{
2019-02-06 10:54:13 -05:00
_session_range_is_free = yn ;
2016-07-12 11:19:15 -04:00
}
2011-01-19 12:38:56 -05:00
void
2020-09-20 18:34:09 -04:00
Session : : playlist_ranges_moved ( list < Temporal : : RangeMove > const & ranges )
2008-06-02 17:41:35 -04:00
{
2020-09-20 18:34:09 -04:00
for ( list < Temporal : : RangeMove > : : const_iterator i = ranges . begin ( ) ; i ! = ranges . end ( ) ; + + i ) {
maybe_update_session_range ( i - > from , i - > to ) ;
2008-06-02 17:41:35 -04:00
}
}
2011-03-01 11:23:31 -05:00
void
2020-09-20 18:34:09 -04:00
Session : : playlist_regions_extended ( list < Temporal : : Range > const & ranges )
2011-03-01 11:23:31 -05:00
{
2020-09-20 18:34:09 -04:00
for ( list < Temporal : : Range > : : const_iterator i = ranges . begin ( ) ; i ! = ranges . end ( ) ; + + i ) {
maybe_update_session_range ( i - > start ( ) , i - > end ( ) ) ;
2011-03-01 11:23:31 -05:00
}
}
2008-06-02 17:41:35 -04:00
/* Region management */
boost : : shared_ptr < Region >
2010-03-01 19:00:00 -05:00
Session : : find_whole_file_parent ( boost : : shared_ptr < Region const > child ) const
2008-06-02 17:41:35 -04:00
{
2010-11-26 14:57:03 -05:00
const RegionFactory : : RegionMap & regions ( RegionFactory : : regions ( ) ) ;
2010-03-06 10:40:42 -05:00
RegionFactory : : RegionMap : : const_iterator i ;
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < Region > region ;
2008-08-04 18:37:24 -04:00
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( region_lock ) ;
2008-06-02 17:41:35 -04:00
for ( i = regions . begin ( ) ; i ! = regions . end ( ) ; + + i ) {
region = i - > second ;
if ( region - > whole_file ( ) ) {
if ( child - > source_equivalent ( region ) ) {
return region ;
}
}
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
return boost : : shared_ptr < Region > ( ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
int
2019-12-25 11:57:10 -05:00
Session : : destroy_sources ( list < boost : : shared_ptr < Source > > const & srcs )
2008-06-02 17:41:35 -04:00
{
2010-11-26 14:57:03 -05:00
set < boost : : shared_ptr < Region > > relevant_regions ;
2008-08-04 18:37:24 -04:00
2019-12-25 11:57:10 -05:00
for ( list < boost : : shared_ptr < Source > > : : const_iterator s = srcs . begin ( ) ; s ! = srcs . end ( ) ; + + s ) {
2010-11-26 14:57:03 -05:00
RegionFactory : : get_regions_using_source ( * s , relevant_regions ) ;
2008-06-02 17:41:35 -04:00
}
2010-11-26 14:57:03 -05:00
for ( set < boost : : shared_ptr < Region > > : : iterator r = relevant_regions . begin ( ) ; r ! = relevant_regions . end ( ) ; ) {
set < boost : : shared_ptr < Region > > : : iterator tmp ;
2010-06-23 16:14:07 -04:00
2010-11-26 14:57:03 -05:00
tmp = r ;
+ + tmp ;
2010-06-23 16:14:07 -04:00
2019-03-19 00:14:00 -04:00
_playlists - > destroy_region ( * r ) ;
2010-11-26 14:57:03 -05:00
RegionFactory : : map_remove ( * r ) ;
2008-06-02 17:41:35 -04:00
2010-11-26 14:57:03 -05:00
( * r ) - > drop_sources ( ) ;
( * r ) - > drop_references ( ) ;
2010-06-23 16:14:07 -04:00
2010-11-26 14:57:03 -05:00
relevant_regions . erase ( r ) ;
2010-06-23 16:14:07 -04:00
2010-11-26 14:57:03 -05:00
r = tmp ;
}
2010-06-23 16:14:07 -04:00
2019-12-25 11:57:10 -05:00
for ( list < boost : : shared_ptr < Source > > : : const_iterator s = srcs . begin ( ) ; s ! = srcs . end ( ) ; + + s ) {
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock ls ( source_lock ) ;
2010-11-26 14:57:03 -05:00
/* remove from the main source list */
sources . erase ( ( * s ) - > id ( ) ) ;
}
2008-06-02 17:41:35 -04:00
2010-11-26 14:57:03 -05:00
( * s ) - > mark_for_remove ( ) ;
( * s ) - > drop_references ( ) ;
2019-12-25 11:59:38 -05:00
SourceRemoved ( boost : : weak_ptr < Source > ( * s ) ) ; /* EMIT SIGNAL */
2010-11-26 14:57:03 -05:00
}
2008-06-02 17:41:35 -04:00
return 0 ;
}
int
Session : : remove_last_capture ( )
{
2010-06-23 16:14:07 -04:00
list < boost : : shared_ptr < Source > > srcs ;
2008-08-04 18:37:24 -04:00
2010-04-21 16:42:22 -04:00
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 ;
}
2011-06-01 12:50:12 -04:00
2010-06-23 16:14:07 -04:00
list < boost : : shared_ptr < Source > > & l = tr - > last_capture_sources ( ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( ! l . empty ( ) ) {
2010-06-23 16:14:07 -04:00
srcs . insert ( srcs . end ( ) , l . begin ( ) , l . end ( ) ) ;
2008-06-02 17:41:35 -04:00
l . clear ( ) ;
}
}
2010-06-23 16:14:07 -04:00
destroy_sources ( srcs ) ;
2008-06-02 17:41:35 -04:00
2020-05-19 11:13:39 -04:00
/* save state so we don't end up with a session file
* referring to non - existent sources .
*/
save_state ( ) ;
2008-06-02 17:41:35 -04:00
return 0 ;
}
2018-10-19 21:02:44 -04:00
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 ( ) ;
}
}
}
2008-06-02 17:41:35 -04:00
/* Source Management */
2009-02-16 21:11:49 -05:00
2008-06-02 17:41:35 -04:00
void
Session : : add_source ( boost : : shared_ptr < Source > source )
{
pair < SourceMap : : key_type , SourceMap : : mapped_type > entry ;
pair < SourceMap : : iterator , bool > result ;
entry . first = source - > id ( ) ;
entry . second = source ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
2008-06-02 17:41:35 -04:00
result = sources . insert ( entry ) ;
}
if ( result . second ) {
2008-08-04 18:37:24 -04:00
2010-11-26 14:57:03 -05:00
/* yay, new source */
2008-06-02 17:41:35 -04:00
2012-05-27 14:07:30 -04:00
boost : : shared_ptr < FileSource > fs = boost : : dynamic_pointer_cast < FileSource > ( source ) ;
2015-10-05 10:17:49 -04:00
2012-05-27 14:07:30 -04:00
if ( fs ) {
if ( ! fs - > within_session ( ) ) {
ensure_search_path_includes ( Glib : : path_get_dirname ( fs - > path ( ) ) , fs - > type ( ) ) ;
}
}
2015-10-05 10:17:49 -04:00
2010-06-23 16:14:07 -04:00
set_dirty ( ) ;
2010-11-26 14:57:03 -05:00
boost : : shared_ptr < AudioFileSource > afs ;
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
if ( ( afs = boost : : dynamic_pointer_cast < AudioFileSource > ( source ) ) ! = 0 ) {
if ( Config - > get_auto_analyse_audio ( ) ) {
Analyser : : queue_source_for_analysis ( source , false ) ;
}
}
2011-02-28 21:04:50 -05:00
source - > DropReferences . connect_same_thread ( * this , boost : : bind ( & Session : : remove_source , this , boost : : weak_ptr < Source > ( source ) ) ) ;
2018-11-15 10:21:31 -05:00
2019-12-25 11:59:38 -05:00
SourceAdded ( boost : : weak_ptr < Source > ( source ) ) ; /* EMIT SIGNAL */
2011-03-14 17:53:10 -04:00
}
2008-06-02 17:41:35 -04:00
}
void
Session : : remove_source ( boost : : weak_ptr < Source > src )
{
2019-03-18 10:33:05 -04:00
if ( deletion_in_progress ( ) ) {
2011-03-02 06:30:37 -05:00
return ;
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
SourceMap : : iterator i ;
boost : : shared_ptr < Source > source = src . lock ( ) ;
if ( ! source ) {
return ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2008-08-04 18:37:24 -04:00
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
2008-06-02 17:41:35 -04:00
if ( ( i = sources . find ( source - > id ( ) ) ) ! = sources . end ( ) ) {
sources . erase ( i ) ;
2019-12-25 11:59:38 -05:00
SourceRemoved ( src ) ; /* EMIT SIGNAL */
2020-05-19 12:04:40 -04:00
} else {
return ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2020-05-19 12:04:40 -04:00
if ( source - > empty ( ) ) {
/* No need to save when empty sources are removed.
* This is likely due to disk - writer initial dummies
* where files don ' t even exist on disk .
*/
return ;
}
2008-08-04 18:37:24 -04:00
2020-05-19 12:04:40 -04:00
if ( ! in_cleanup ( ) & & ! loading ( ) ) {
2008-06-02 17:41:35 -04:00
/* save state so we don't end up with a session file
2019-03-18 10:33:05 -04:00
* referring to non - existent sources .
*/
2020-05-19 11:13:39 -04:00
save_state ( ) ;
2008-06-02 17:41:35 -04:00
}
}
boost : : shared_ptr < Source >
Session : : source_by_id ( const PBD : : ID & id )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
2008-06-02 17:41:35 -04:00
SourceMap : : iterator i ;
boost : : shared_ptr < Source > source ;
if ( ( i = sources . find ( id ) ) ! = sources . end ( ) ) {
source = i - > second ;
}
return source ;
}
2014-04-13 10:29:07 -04:00
boost : : shared_ptr < AudioFileSource >
2014-05-29 16:27:33 -04:00
Session : : audio_source_by_path_and_channel ( const string & path , uint16_t chn ) const
2008-06-02 17:41:35 -04:00
{
2014-04-13 10:29:07 -04:00
/* Restricted to audio files because only audio sources have channel
as a property .
*/
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
2008-06-02 17:41:35 -04:00
2014-04-13 10:29:07 -04:00
for ( SourceMap : : const_iterator i = sources . begin ( ) ; i ! = sources . end ( ) ; + + i ) {
2009-02-16 21:11:49 -05:00
boost : : shared_ptr < AudioFileSource > afs
= boost : : dynamic_pointer_cast < AudioFileSource > ( i - > second ) ;
2008-06-02 17:41:35 -04:00
if ( afs & & afs - > path ( ) = = path & & chn = = afs - > channel ( ) ) {
return afs ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
}
2014-04-13 10:29:07 -04:00
return boost : : shared_ptr < AudioFileSource > ( ) ;
}
boost : : shared_ptr < MidiSource >
2017-06-24 19:23:03 -04:00
Session : : midi_source_by_path ( const std : : string & path , bool need_source_lock ) const
2014-04-13 10:29:07 -04:00
{
/* Restricted to MIDI files because audio sources require a channel
for unique identification , in addition to a path .
*/
2017-06-24 19:23:03 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock , Glib : : Threads : : NOT_LOCK ) ;
if ( need_source_lock ) {
lm . acquire ( ) ;
}
2014-04-13 10:29:07 -04:00
for ( SourceMap : : const_iterator s = sources . begin ( ) ; s ! = sources . end ( ) ; + + s ) {
boost : : shared_ptr < MidiSource > ms
= boost : : dynamic_pointer_cast < MidiSource > ( s - > second ) ;
boost : : shared_ptr < FileSource > fs
= boost : : dynamic_pointer_cast < FileSource > ( s - > second ) ;
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
if ( ms & & fs & & fs - > path ( ) = = path ) {
return ms ;
}
}
return boost : : shared_ptr < MidiSource > ( ) ;
2008-06-02 17:41:35 -04:00
}
2010-11-09 17:18:52 -05:00
uint32_t
Session : : count_sources_by_origin ( const string & path )
{
2010-11-26 14:57:03 -05:00
uint32_t cnt = 0 ;
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( source_lock ) ;
2010-11-09 17:18:52 -05:00
2020-02-24 22:15:30 -05:00
for ( SourceMap : : const_iterator i = sources . begin ( ) ; i ! = sources . end ( ) ; + + i ) {
2010-11-09 17:18:52 -05:00
boost : : shared_ptr < FileSource > fs
= boost : : dynamic_pointer_cast < FileSource > ( i - > second ) ;
2010-11-26 14:57:03 -05:00
if ( fs & & fs - > origin ( ) = = path ) {
+ + cnt ;
}
2010-11-09 17:18:52 -05:00
}
return cnt ;
}
2015-09-09 18:48:10 -04:00
static string
peak_file_helper ( const string & peak_path , const string & file_path , const string & file_base , bool hash ) {
if ( hash ) {
std : : string checksum = Glib : : Checksum : : compute_checksum ( Glib : : Checksum : : CHECKSUM_SHA1 , file_path + G_DIR_SEPARATOR + file_base ) ;
return Glib : : build_filename ( peak_path , checksum + peakfile_suffix ) ;
} else {
return Glib : : build_filename ( peak_path , file_base + peakfile_suffix ) ;
}
}
2010-09-14 11:45:21 -04:00
string
2015-09-10 10:39:35 -04:00
Session : : construct_peak_filepath ( const string & filepath , const bool in_session , const bool old_peak_name ) const
2009-02-16 21:11:49 -05:00
{
2015-08-29 07:48:05 -04:00
string interchange_dir_string = string ( interchange_dir_name ) + G_DIR_SEPARATOR ;
if ( Glib : : path_is_absolute ( filepath ) ) {
2015-05-28 19:08:47 -04:00
/* rip the session dir from the audiofile source */
string session_path ;
bool in_another_session = true ;
2015-10-05 10:17:49 -04:00
2015-08-29 07:48:05 -04:00
if ( filepath . find ( interchange_dir_string ) ! = string : : npos ) {
2015-10-05 10:17:49 -04:00
2015-08-29 07:48:05 -04:00
session_path = Glib : : path_get_dirname ( filepath ) ; /* now ends in audiofiles */
2015-05-28 19:08:47 -04:00
session_path = Glib : : path_get_dirname ( session_path ) ; /* now ends in session name */
session_path = Glib : : path_get_dirname ( session_path ) ; /* now ends in interchange */
session_path = Glib : : path_get_dirname ( session_path ) ; /* now has session path */
/* see if it is within our session */
for ( vector < space_and_path > : : const_iterator i = session_dirs . begin ( ) ; i ! = session_dirs . end ( ) ; + + i ) {
if ( i - > path = = session_path ) {
in_another_session = false ;
break ;
}
}
} else {
in_another_session = false ;
}
2015-10-05 10:17:49 -04:00
2015-05-28 19:08:47 -04:00
if ( in_another_session ) {
SessionDirectory sd ( session_path ) ;
2015-09-10 10:39:35 -04:00
return peak_file_helper ( sd . peak_path ( ) , " " , Glib : : path_get_basename ( filepath ) , ! old_peak_name ) ;
2015-05-28 19:08:47 -04:00
}
}
2015-09-09 18:48:10 -04:00
/* 1) if file belongs to this session
* it may be a relative path ( interchange / . . . )
* or just basename ( session_state , remove source )
* - > just use the basename
*/
2015-08-29 07:48:05 -04:00
std : : string filename = Glib : : path_get_basename ( filepath ) ;
std : : string path ;
2015-09-09 18:48:10 -04:00
/* 2) if the file is outside our session dir:
* ( imported but not copied ) add the path for check - summming */
2015-09-10 10:39:35 -04:00
if ( ! in_session ) {
2015-08-29 07:48:05 -04:00
path = Glib : : path_get_dirname ( filepath ) ;
}
2015-10-05 10:17:49 -04:00
2015-09-10 10:39:35 -04:00
return peak_file_helper ( _session_dir - > peak_path ( ) , path , Glib : : path_get_basename ( filepath ) , ! old_peak_name ) ;
2009-02-16 21:11:49 -05:00
}
string
2014-07-08 00:53:06 -04:00
Session : : new_audio_source_path_for_embedded ( const std : : string & path )
2008-06-02 17:41:35 -04:00
{
2015-10-04 14:51:05 -04:00
/* embedded source:
2014-07-08 00:53:06 -04:00
*
* we know that the filename is already unique because it exists
2015-10-04 14:51:05 -04:00
* out in the filesystem .
2014-07-08 00:53:06 -04:00
*
* However , when we bring it into the session , we could get a
* collision .
*
* Eg . two embedded files :
2015-10-04 14:51:05 -04:00
*
2014-07-08 00:53:06 -04:00
* / foo / bar / baz . wav
* / frob / nic / baz . wav
*
2015-10-04 14:51:05 -04:00
* When merged into session , these collide .
2014-07-08 00:53:06 -04:00
*
* There will not be a conflict with in - memory sources
* because when the source was created we already picked
* a unique name for it .
*
* This collision is not likely to be common , but we have to guard
* against it . So , if there is a collision , take the md5 hash of the
* the path , and use that as the filename instead .
*/
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
SessionDirectory sdir ( get_best_session_directory_for_new_audio ( ) ) ;
string base = Glib : : path_get_basename ( path ) ;
string newpath = Glib : : build_filename ( sdir . sound_path ( ) , base ) ;
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
if ( Glib : : file_test ( newpath , Glib : : FILE_TEST_EXISTS ) ) {
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
MD5 md5 ;
2014-07-02 17:35:48 -04:00
2014-07-08 00:53:06 -04:00
md5 . digestString ( path . c_str ( ) ) ;
md5 . writeToString ( ) ;
base = md5 . digestChars ;
2015-10-05 10:17:49 -04:00
2014-07-08 12:25:36 -04:00
string ext = get_suffix ( path ) ;
2014-06-02 11:20:37 -04:00
2014-07-08 12:25:36 -04:00
if ( ! ext . empty ( ) ) {
base + = ' . ' ;
base + = ext ;
}
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
newpath = Glib : : build_filename ( sdir . sound_path ( ) , base ) ;
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
/* if this collides, we're screwed */
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
if ( Glib : : file_test ( newpath , Glib : : FILE_TEST_EXISTS ) ) {
error < < string_compose ( _ ( " Merging embedded file %1: name collision AND md5 hash collision! " ) , path ) < < endmsg ;
return string ( ) ;
}
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
}
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
return newpath ;
}
2008-06-02 17:41:35 -04:00
2015-10-04 14:51:05 -04:00
/** Return true if there are no audio file sources that use @param name as
* the filename component of their path .
2015-02-20 14:12:12 -05:00
*
* Return false otherwise .
*
2015-10-04 14:51:05 -04:00
* This method MUST ONLY be used to check in - session , mono files since it
2015-02-20 14:12:12 -05:00
* hard - codes the channel of the audio file source we are looking for as zero .
2015-10-04 14:51:05 -04:00
*
2015-02-20 14:12:12 -05:00
* If / when Ardour supports native files in non - mono formats , the logic here
* will need to be revisited .
*/
2014-07-08 00:53:06 -04:00
bool
2015-02-20 14:12:12 -05:00
Session : : audio_source_name_is_unique ( const string & name )
2014-07-08 00:53:06 -04:00
{
std : : vector < string > sdirs = source_search_path ( DataType : : AUDIO ) ;
uint32_t existing = 0 ;
2010-07-22 10:52:05 -04:00
2014-07-08 00:53:06 -04:00
for ( vector < string > : : const_iterator i = sdirs . begin ( ) ; i ! = sdirs . end ( ) ; + + i ) {
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
/* note that we search *without* the extension so that
we don ' t end up both " Audio 1-1.wav " and " Audio 1-1.caf "
in the event that this new name is required for
a file format change .
*/
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
const string spath = * i ;
2015-10-05 10:17:49 -04:00
2014-07-10 08:16:27 -04:00
if ( matching_unsuffixed_filename_exists_in ( spath , name ) ) {
2014-07-08 00:53:06 -04:00
existing + + ;
break ;
}
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
/* it is possible that we have the path already
* assigned to a source that has not yet been written
* ( ie . the write source for a diskstream ) . we have to
* check this in order to make sure that our candidate
* path isn ' t used again , because that can lead to
* two Sources point to the same file with different
* notions of their removability .
*/
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
string possible_path = Glib : : build_filename ( spath , name ) ;
2010-07-22 10:52:05 -04:00
2015-02-20 14:12:12 -05:00
if ( audio_source_by_path_and_channel ( possible_path , 0 ) ) {
2014-07-08 00:53:06 -04:00
existing + + ;
break ;
}
}
2014-07-10 08:16:27 -04:00
2014-07-08 00:53:06 -04:00
return ( existing = = 0 ) ;
}
2014-04-13 11:35:49 -04:00
2014-07-08 00:53:06 -04:00
string
2020-02-25 23:35:07 -05:00
Session : : format_audio_source_name ( const string & legalized_base , uint32_t nchan , uint32_t chan , bool take_required , uint32_t cnt , bool related_exists )
2014-07-08 00:53:06 -04:00
{
ostringstream sstr ;
const string ext = native_header_format_extension ( config . get_native_file_header_format ( ) , DataType : : AUDIO ) ;
2015-10-05 10:17:49 -04:00
2019-09-25 15:02:31 -04:00
sstr < < legalized_base ;
2015-10-05 10:17:49 -04:00
2019-09-25 15:02:31 -04:00
if ( take_required | | related_exists ) {
sstr < < ' - ' ;
sstr < < cnt ;
2014-07-08 00:53:06 -04:00
}
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
if ( nchan = = 2 ) {
if ( chan = = 0 ) {
sstr < < " %L " ;
} else {
sstr < < " %R " ;
}
} else if ( nchan > 2 ) {
if ( nchan < 26 ) {
sstr < < ' % ' ;
sstr < < ' a ' + chan ;
} else {
/* XXX what? more than 26 channels! */
sstr < < ' % ' ;
sstr < < chan + 1 ;
}
}
2015-10-05 10:17:49 -04:00
2014-07-08 00:53:06 -04:00
sstr < < ext ;
2014-04-14 03:03:35 -04:00
2014-07-08 00:53:06 -04:00
return sstr . str ( ) ;
}
2014-04-13 11:35:49 -04:00
2014-07-08 00:53:06 -04:00
/** Return a unique name based on \a base for a new internal audio source */
string
2020-02-25 23:35:07 -05:00
Session : : new_audio_source_path ( const string & base , uint32_t nchan , uint32_t chan , bool take_required )
2014-07-08 00:53:06 -04:00
{
uint32_t cnt ;
string possible_name ;
const uint32_t limit = 9999 ; // arbitrary limit on number of files with the same basic name
string legalized ;
bool some_related_source_name_exists = false ;
2008-06-02 17:41:35 -04:00
2014-07-08 00:53:06 -04:00
legalized = legalize_for_path ( base ) ;
// Find a "version" of the base name that doesn't exist in any of the possible directories.
2020-02-25 23:35:07 -05:00
for ( cnt = 1 ; cnt < = limit ; + + cnt ) {
2014-07-08 00:53:06 -04:00
2020-02-25 23:35:07 -05:00
possible_name = format_audio_source_name ( legalized , nchan , chan , take_required , cnt , some_related_source_name_exists ) ;
2015-10-05 10:17:49 -04:00
2015-02-20 14:12:12 -05:00
if ( audio_source_name_is_unique ( possible_name ) ) {
2008-06-02 17:41:35 -04:00
break ;
}
2015-10-05 10:17:49 -04:00
2014-06-02 11:20:37 -04:00
some_related_source_name_exists = true ;
2008-06-02 17:41:35 -04:00
if ( cnt > limit ) {
2009-02-16 21:11:49 -05:00
error < < string_compose (
_ ( " There are already %1 recordings for %2, which I consider too many. " ) ,
limit , base ) < < endmsg ;
2008-06-02 17:41:35 -04:00
destroy ( ) ;
throw failed_constructor ( ) ;
}
}
2011-06-01 12:50:12 -04:00
2014-06-02 11:20:37 -04:00
/* We've established that the new name does not exist in any session
* directory , so now find out which one we should use for this new
* audio source .
*/
2008-06-02 17:41:35 -04:00
2014-06-02 11:20:37 -04:00
SessionDirectory sdir ( get_best_session_directory_for_new_audio ( ) ) ;
std : : string s = Glib : : build_filename ( sdir . sound_path ( ) , possible_name ) ;
2009-11-25 09:37:20 -05:00
2014-06-02 11:20:37 -04:00
return s ;
2008-06-02 17:41:35 -04:00
}
2014-11-14 23:53:59 -05:00
/** Return a unique name based on `base` for a new internal MIDI source */
2008-06-02 17:41:35 -04:00
string
2017-06-24 19:23:03 -04:00
Session : : new_midi_source_path ( const string & base , bool need_lock )
2008-06-02 17:41:35 -04:00
{
2014-06-02 11:20:37 -04:00
string possible_path ;
2014-04-13 10:29:07 -04:00
string possible_name ;
2008-06-02 17:41:35 -04:00
2017-09-07 00:05:26 -04:00
possible_name = legalize_for_path ( base ) ;
2008-06-02 17:41:35 -04:00
2009-02-16 21:11:49 -05:00
// Find a "version" of the file name that doesn't exist in any of the possible directories.
2014-07-02 17:35:48 -04:00
std : : vector < string > sdirs = source_search_path ( DataType : : MIDI ) ;
/* - the main session folder is the first in the vector.
* - after checking all locations for file - name uniqueness ,
* we keep the one from the last iteration as new file name
* - midi files are small and should just be kept in the main session - folder
*
* - > reverse the array , check main session folder last and use that as location
* for MIDI files .
*/
std : : reverse ( sdirs . begin ( ) , sdirs . end ( ) ) ;
2014-04-10 08:58:04 -04:00
2017-09-07 00:05:26 -04:00
while ( true ) {
possible_name = bump_name_once ( possible_name , ' - ' ) ;
2008-06-02 17:41:35 -04:00
uint32_t existing = 0 ;
2015-10-05 10:17:49 -04:00
2014-07-02 17:35:48 -04:00
for ( vector < string > : : const_iterator i = sdirs . begin ( ) ; i ! = sdirs . end ( ) ; + + i ) {
2008-06-02 17:41:35 -04:00
2017-09-07 00:05:26 -04:00
possible_path = Glib : : build_filename ( * i , possible_name + " .mid " ) ;
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
if ( Glib : : file_test ( possible_path , Glib : : FILE_TEST_EXISTS ) ) {
existing + + ;
}
2008-06-02 17:41:35 -04:00
2017-06-24 19:23:03 -04:00
if ( midi_source_by_path ( possible_path , need_lock ) ) {
2008-06-02 17:41:35 -04:00
existing + + ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
}
2017-09-07 00:05:26 -04:00
if ( possible_path . size ( ) > = PATH_MAX ) {
2009-02-16 21:11:49 -05:00
error < < string_compose (
2017-09-07 00:05:26 -04:00
_ ( " There are already many recordings for %1, resulting in a too long file-path %2. " ) ,
base , possible_path ) < < endmsg ;
2009-02-16 21:11:49 -05:00
destroy ( ) ;
2014-06-02 11:20:37 -04:00
return 0 ;
2008-06-02 17:41:35 -04:00
}
2017-09-07 00:05:26 -04:00
if ( existing = = 0 ) {
break ;
}
2008-06-02 17:41:35 -04:00
}
2014-06-02 11:20:37 -04:00
/* No need to "find best location" for software/app-based RAID, because
MIDI is so small that we always put it in the same place .
*/
return possible_path ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2009-02-16 21:11:49 -05:00
2014-06-02 11:20:37 -04:00
/** Create a new within-session audio source */
boost : : shared_ptr < AudioFileSource >
2020-02-25 23:35:07 -05:00
Session : : create_audio_source_for_session ( size_t n_chans , string const & base , uint32_t chan )
2014-06-02 11:20:37 -04:00
{
2020-02-25 23:35:07 -05:00
const string path = new_audio_source_path ( base , n_chans , chan , true ) ;
2014-06-02 11:20:37 -04:00
if ( ! path . empty ( ) ) {
2020-03-23 23:47:13 -04:00
return boost : : dynamic_pointer_cast < AudioFileSource > ( SourceFactory : : createWritable ( DataType : : AUDIO , * this , path , sample_rate ( ) , true , true ) ) ;
2014-06-02 11:20:37 -04:00
} else {
throw failed_constructor ( ) ;
}
}
2009-11-30 08:16:38 -05:00
/** Create a new within-session MIDI source */
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < MidiSource >
2014-04-13 10:29:07 -04:00
Session : : create_midi_source_for_session ( string const & basic_name )
2008-06-02 17:41:35 -04:00
{
2014-06-02 11:20:37 -04:00
const string path = new_midi_source_path ( basic_name ) ;
2015-10-05 10:17:49 -04:00
2014-06-02 11:20:37 -04:00
if ( ! path . empty ( ) ) {
2020-03-23 23:47:13 -04:00
return boost : : dynamic_pointer_cast < SMFSource > ( SourceFactory : : createWritable ( DataType : : MIDI , * this , path , sample_rate ( ) ) ) ;
2014-06-02 11:20:37 -04:00
} else {
throw failed_constructor ( ) ;
2014-04-13 10:29:07 -04:00
}
}
2010-06-23 21:37:24 -04:00
2014-04-13 10:29:07 -04:00
/** Create a new within-session MIDI source */
boost : : shared_ptr < MidiSource >
Session : : create_midi_source_by_stealing_name ( boost : : shared_ptr < Track > track )
{
/* the caller passes in the track the source will be used in,
2015-10-04 14:51:05 -04:00
so that we can keep the numbering sane .
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
Rationale : a track with the name " Foo " that has had N
captures carried out so far will ALREADY have a write source
named " Foo-N+1.mid " waiting to be used for the next capture .
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
If we call new_midi_source_name ( ) we will get " Foo-N+2 " . But
there is no region corresponding to " Foo-N+1 " , so when
" Foo-N+2 " appears in the track , the gap presents the user
with odd behaviour - why did it skip past Foo - N + 1 ?
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
We could explain this to the user in some odd way , but
instead we rename " Foo-N+1.mid " as " Foo-N+2.mid " , and then
use " Foo-N+1 " here .
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
If that attempted rename fails , we get " Foo-N+2.mid " anyway .
*/
2015-10-05 10:17:49 -04:00
2014-04-13 10:29:07 -04:00
boost : : shared_ptr < MidiTrack > mt = boost : : dynamic_pointer_cast < MidiTrack > ( track ) ;
assert ( mt ) ;
std : : string name = track - > steal_write_source_name ( ) ;
2011-06-01 12:50:12 -04:00
2014-04-10 08:58:04 -04:00
if ( name . empty ( ) ) {
2014-04-13 10:29:07 -04:00
return boost : : shared_ptr < MidiSource > ( ) ;
2010-11-26 14:57:03 -05:00
}
2010-06-23 21:37:24 -04:00
2014-08-28 14:01:52 -04:00
/* MIDI files are small, just put them in the first location of the
session source search path .
*/
const string path = Glib : : build_filename ( source_search_path ( DataType : : MIDI ) . front ( ) , name ) ;
2008-08-04 18:37:24 -04:00
2020-03-23 23:47:13 -04:00
return boost : : dynamic_pointer_cast < SMFSource > ( SourceFactory : : createWritable ( DataType : : MIDI , * this , path , sample_rate ( ) ) ) ;
2008-06-02 17:41:35 -04:00
}
2018-10-16 18:44:11 -04:00
bool
Session : : playlist_is_active ( boost : : shared_ptr < Playlist > playlist )
{
2019-05-29 17:18:23 -04:00
Glib : : Threads : : Mutex : : Lock lm ( _playlists - > lock ) ;
for ( SessionPlaylists : : List : : iterator i = _playlists - > playlists . begin ( ) ; i ! = _playlists - > playlists . end ( ) ; i + + ) {
2018-10-16 18:44:11 -04:00
if ( ( * i ) = = playlist ) {
return true ;
}
}
return false ;
}
2008-06-02 17:41:35 -04:00
2008-09-26 04:29:30 -04:00
void
Session : : add_playlist ( boost : : shared_ptr < Playlist > playlist , bool unused )
2008-06-02 17:41:35 -04:00
{
if ( playlist - > hidden ( ) ) {
return ;
}
2019-03-19 00:14:00 -04:00
_playlists - > add ( playlist ) ;
2008-06-02 17:41:35 -04:00
2008-09-26 04:29:30 -04:00
if ( unused ) {
playlist - > release ( ) ;
}
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
}
void
Session : : remove_playlist ( boost : : weak_ptr < Playlist > weak_playlist )
{
2019-03-18 10:33:05 -04:00
if ( deletion_in_progress ( ) ) {
2008-06-02 17:41:35 -04:00
return ;
}
boost : : shared_ptr < Playlist > playlist ( weak_playlist . lock ( ) ) ;
if ( ! playlist ) {
return ;
}
2019-03-19 00:14:00 -04:00
_playlists - > remove ( playlist ) ;
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
}
2008-08-04 18:37:24 -04:00
void
2008-06-02 17:41:35 -04:00
Session : : set_audition ( boost : : shared_ptr < Region > r )
{
pending_audition_region = r ;
2009-11-08 11:28:21 -05:00
add_post_transport_work ( PostTransportAudition ) ;
2009-10-23 20:39:28 -04:00
_butler - > schedule_transport_work ( ) ;
2008-06-02 17:41:35 -04:00
}
void
Session : : audition_playlist ( )
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : Audition , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0.0 ) ;
2008-06-02 17:41:35 -04:00
ev - > region . reset ( ) ;
queue_event ( ev ) ;
}
2016-02-23 09:41:21 -05:00
void
Session : : register_lua_function (
const std : : string & name ,
const std : : string & script ,
const LuaScriptParamList & args
)
{
Glib : : Threads : : Mutex : : Lock lm ( lua_lock ) ;
lua_State * L = lua . getState ( ) ;
const std : : string & bytecode = LuaScripting : : get_factory_bytecode ( script ) ;
luabridge : : LuaRef tbl_arg ( luabridge : : newTable ( L ) ) ;
for ( LuaScriptParamList : : const_iterator i = args . begin ( ) ; i ! = args . end ( ) ; + + i ) {
if ( ( * i ) - > optional & & ! ( * i ) - > is_set ) { continue ; }
tbl_arg [ ( * i ) - > name ] = ( * i ) - > value ;
}
( * _lua_add ) ( name , bytecode , tbl_arg ) ; // throws luabridge::LuaException
2017-02-18 18:06:01 -05:00
lm . release ( ) ;
LuaScriptsChanged ( ) ; /* EMIT SIGNAL */
2016-02-23 09:41:21 -05:00
set_dirty ( ) ;
}
void
Session : : unregister_lua_function ( const std : : string & name )
{
Glib : : Threads : : Mutex : : Lock lm ( lua_lock ) ;
( * _lua_del ) ( name ) ; // throws luabridge::LuaException
lua . collect_garbage ( ) ;
2017-02-18 18:06:01 -05:00
lm . release ( ) ;
LuaScriptsChanged ( ) ; /* EMIT SIGNAL */
2016-02-23 09:41:21 -05:00
set_dirty ( ) ;
}
std : : vector < std : : string >
Session : : registered_lua_functions ( )
{
Glib : : Threads : : Mutex : : Lock lm ( lua_lock ) ;
std : : vector < std : : string > rv ;
try {
luabridge : : LuaRef list ( ( * _lua_list ) ( ) ) ;
for ( luabridge : : Iterator i ( list ) ; ! i . isNil ( ) ; + + i ) {
if ( ! i . key ( ) . isString ( ) ) { assert ( 0 ) ; continue ; }
rv . push_back ( i . key ( ) . cast < std : : string > ( ) ) ;
}
2017-08-18 19:05:18 -04:00
} catch ( . . . ) { }
2016-02-23 09:41:21 -05:00
return rv ;
}
static void _lua_print ( std : : string s ) {
2020-04-18 12:13:14 -04:00
# ifndef NDEBUG
std : : cout < < " LuaSession: " < < s < < " \n " ;
2016-02-23 09:41:21 -05:00
# endif
2020-04-18 12:13:14 -04:00
PBD : : info < < " LuaSession: " < < s < < endmsg ;
}
2016-02-23 09:41:21 -05:00
void
Session : : try_run_lua ( pframes_t nframes )
{
if ( _n_lua_scripts = = 0 ) return ;
Glib : : Threads : : Mutex : : Lock tm ( lua_lock , Glib : : Threads : : TRY_LOCK ) ;
if ( tm . locked ( ) ) {
2017-08-18 19:05:18 -04:00
try { ( * _lua_run ) ( nframes ) ; } catch ( . . . ) { }
2018-03-18 21:26:24 -04:00
lua . collect_garbage_step ( ) ;
2016-02-23 09:41:21 -05:00
}
}
void
Session : : setup_lua ( )
{
lua . Print . connect ( & _lua_print ) ;
2017-08-08 22:07:23 -04:00
lua . sandbox ( true ) ;
2016-02-23 09:41:21 -05:00
lua . do_command (
" function ArdourSession () "
" local self = { scripts = {}, instances = {} } "
" "
" local remove = function (n) "
" self.scripts[n] = nil "
" self.instances[n] = nil "
" Session:scripts_changed() " // call back
" end "
" "
" local addinternal = function (n, f, a) "
" assert(type(n) == 'string', 'function-name must be string') "
" assert(type(f) == 'function', 'Given script is a not a function') "
" assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid') "
" assert(self.scripts[n] == nil, 'Callback \" '.. n ..' \" already exists.') "
" self.scripts[n] = { ['f'] = f, ['a'] = a } "
2017-01-01 18:46:01 -05:00
" local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, bit32=bit32, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR } "
2016-02-23 09:41:21 -05:00
" self.instances[n] = load (string.dump(f, true), nil, nil, env)(a) "
" Session:scripts_changed() " // call back
" end "
" "
" local add = function (n, b, a) "
" assert(type(b) == 'string', 'ByteCode must be string') "
" load (b)() " // assigns f
" assert(type(f) == 'string', 'Assigned ByteCode must be string') "
" addinternal (n, load(f), a) "
" end "
" "
" local run = function (...) "
" for n, s in pairs (self.instances) do "
" local status, err = pcall (s, ...) "
" if not status then "
" print ('fn \" '.. n .. ' \" : ', err) "
" remove (n) "
" end "
" end "
2020-01-29 10:51:14 -05:00
" collectgarbage( \" step \" ) "
2016-02-23 09:41:21 -05:00
" end "
" "
" local cleanup = function () "
" self.scripts = nil "
" self.instances = nil "
" end "
" "
" local list = function () "
" local rv = {} "
" for n, _ in pairs (self.scripts) do "
" rv[n] = true "
" end "
" return rv "
" end "
" "
" local function basic_serialize (o) "
" if type(o) == \" number \" then "
" return tostring(o) "
" else "
" return string.format( \" %q \" , o) "
" end "
" end "
" "
" local function serialize (name, value) "
" local rv = name .. ' = ' "
" collectgarbage() "
" if type(value) == \" number \" or type(value) == \" string \" or type(value) == \" nil \" then "
" return rv .. basic_serialize(value) .. ' ' "
" elseif type(value) == \" table \" then "
" rv = rv .. '{} ' "
" for k,v in pairs(value) do "
" local fieldname = string.format( \" %s[%s] \" , name, basic_serialize(k)) "
" rv = rv .. serialize(fieldname, v) .. ' ' "
" collectgarbage() " // string concatenation allocates a new string :(
" end "
" return rv; "
" elseif type(value) == \" function \" then "
" return rv .. string.format( \" %q \" , string.dump(value, true)) "
" else "
" error('cannot save a ' .. type(value)) "
" end "
" end "
" "
" "
" local save = function () "
" return (serialize('scripts', self.scripts)) "
" end "
" "
" local restore = function (state) "
" self.scripts = {} "
" load (state)() "
" for n, s in pairs (scripts) do "
" addinternal (n, load(s['f']), s['a']) "
" end "
" end "
" "
" return { run = run, add = add, remove = remove, "
" list = list, restore = restore, save = save, cleanup = cleanup} "
" end "
" "
" sess = ArdourSession () "
" ArdourSession = nil "
" "
" function ardour () end "
) ;
lua_State * L = lua . getState ( ) ;
try {
luabridge : : LuaRef lua_sess = luabridge : : getGlobal ( L , " sess " ) ;
lua . do_command ( " sess = nil " ) ; // hide it.
lua . do_command ( " collectgarbage() " ) ;
_lua_run = new luabridge : : LuaRef ( lua_sess [ " run " ] ) ;
_lua_add = new luabridge : : LuaRef ( lua_sess [ " add " ] ) ;
_lua_del = new luabridge : : LuaRef ( lua_sess [ " remove " ] ) ;
_lua_list = new luabridge : : LuaRef ( lua_sess [ " list " ] ) ;
_lua_save = new luabridge : : LuaRef ( lua_sess [ " save " ] ) ;
_lua_load = new luabridge : : LuaRef ( lua_sess [ " restore " ] ) ;
_lua_cleanup = new luabridge : : LuaRef ( lua_sess [ " cleanup " ] ) ;
} catch ( luabridge : : LuaException const & e ) {
fatal < < string_compose ( _ ( " programming error: %1 " ) ,
2017-08-18 19:05:18 -04:00
std : : string ( " Failed to setup session Lua interpreter " ) + e . what ( ) )
< < endmsg ;
abort ( ) ; /*NOTREACHED*/
} catch ( . . . ) {
fatal < < string_compose ( _ ( " programming error: %1 " ) ,
X_ ( " Failed to setup session Lua interpreter " ) )
2016-02-23 09:41:21 -05:00
< < endmsg ;
abort ( ) ; /*NOTREACHED*/
}
2018-03-18 21:26:24 -04:00
lua_mlock ( L , 1 ) ;
2016-02-23 09:41:21 -05:00
LuaBindings : : stddef ( L ) ;
LuaBindings : : common ( L ) ;
LuaBindings : : dsp ( L ) ;
2018-03-18 21:26:24 -04:00
lua_mlock ( L , 0 ) ;
2016-02-23 09:41:21 -05:00
luabridge : : push < Session * > ( L , this ) ;
lua_setglobal ( L , " Session " ) ;
}
void
Session : : scripts_changed ( )
{
assert ( ! lua_lock . trylock ( ) ) ; // must hold lua_lock
try {
luabridge : : LuaRef list ( ( * _lua_list ) ( ) ) ;
int cnt = 0 ;
for ( luabridge : : Iterator i ( list ) ; ! i . isNil ( ) ; + + i ) {
if ( ! i . key ( ) . isString ( ) ) { assert ( 0 ) ; continue ; }
+ + cnt ;
}
_n_lua_scripts = cnt ;
} catch ( luabridge : : LuaException const & e ) {
2017-08-18 19:05:18 -04:00
fatal < < string_compose ( _ ( " programming error: %1 " ) ,
std : : string ( " Indexing Lua Session Scripts failed. " ) + e . what ( ) )
< < endmsg ;
abort ( ) ; /*NOTREACHED*/
} catch ( . . . ) {
2016-02-23 09:41:21 -05:00
fatal < < string_compose ( _ ( " programming error: %1 " ) ,
X_ ( " Indexing Lua Session Scripts failed. " ) )
< < endmsg ;
abort ( ) ; /*NOTREACHED*/
}
}
2008-06-02 17:41:35 -04:00
void
Session : : non_realtime_set_audition ( )
{
2012-04-11 11:10:12 -04:00
assert ( pending_audition_region ) ;
auditioner - > audition_region ( pending_audition_region ) ;
pending_audition_region . reset ( ) ;
2008-06-02 17:41:35 -04:00
AuditionActive ( true ) ; /* EMIT SIGNAL */
}
void
Session : : audition_region ( boost : : shared_ptr < Region > r )
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : Audition , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0.0 ) ;
2008-06-02 17:41:35 -04:00
ev - > region = r ;
queue_event ( ev ) ;
}
void
Session : : cancel_audition ( )
{
2014-06-29 22:04:35 -04:00
if ( ! auditioner ) {
return ;
}
2010-03-24 23:40:07 -04:00
if ( auditioner - > auditioning ( ) ) {
2008-06-02 17:41:35 -04:00
auditioner - > cancel_audition ( ) ;
AuditionActive ( false ) ; /* EMIT SIGNAL */
}
}
bool
Session : : is_auditioning ( ) const
{
/* can be called before we have an auditioner object */
if ( auditioner ) {
2010-03-24 23:40:07 -04:00
return auditioner - > auditioning ( ) ;
2008-06-02 17:41:35 -04:00
} else {
return false ;
}
}
void
2019-10-28 19:23:54 -04:00
Session : : graph_reordered ( bool called_from_backend )
2008-06-02 17:41:35 -04:00
{
/* don't do this stuff if we are setting up connections
2009-11-27 19:49:04 -05:00
from a set_state ( ) call or creating new tracks . Ditto for deletion .
2008-06-02 17:41:35 -04:00
*/
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) | | _adding_routes_in_progress | | _reconnecting_routes_in_progress | | _route_deletion_in_progress ) {
2008-06-02 17:41:35 -04:00
return ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
resort_routes ( ) ;
2008-08-04 18:37:24 -04:00
/* force all diskstreams to update their capture offset values to
2017-09-28 22:17:16 -04:00
* reflect any changes in latencies within the graph .
2022-04-27 20:44:08 -04:00
*
* XXX : Is this required ? When the graph - order callback
* is initiated by the backend , it is always followed by
* a latency callback .
2017-09-28 22:17:16 -04:00
*/
2019-10-28 19:23:54 -04:00
update_latency_compensation ( true , called_from_backend ) ;
2008-06-02 17:41:35 -04:00
}
2017-09-18 12:39:17 -04:00
/** @return Number of samples that there is disk space available to write,
2012-06-12 12:41:29 -04:00
* if known .
*/
2017-09-18 12:39:17 -04:00
boost : : optional < samplecnt_t >
2008-06-02 17:41:35 -04:00
Session : : available_capture_duration ( )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( space_lock ) ;
2012-06-28 12:08:48 -04:00
2012-06-12 12:41:29 -04:00
if ( _total_free_4k_blocks_uncertain ) {
2017-09-18 12:39:17 -04:00
return boost : : optional < samplecnt_t > ( ) ;
2012-06-12 12:41:29 -04:00
}
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
float sample_bytes_on_disk = 4.0 ; // keep gcc happy
2009-05-13 20:13:27 -04:00
switch ( config . get_native_file_data_format ( ) ) {
2008-06-02 17:41:35 -04:00
case FormatFloat :
sample_bytes_on_disk = 4.0 ;
break ;
case FormatInt24 :
sample_bytes_on_disk = 3.0 ;
break ;
case FormatInt16 :
sample_bytes_on_disk = 2.0 ;
break ;
2008-08-04 18:37:24 -04:00
default :
2008-06-02 17:41:35 -04:00
/* impossible, but keep some gcc versions happy */
fatal < < string_compose ( _ ( " programming error: %1 " ) ,
X_ ( " illegal native file data format " ) )
< < endmsg ;
2014-11-14 04:47:43 -05:00
abort ( ) ; /*NOTREACHED*/
2008-06-02 17:41:35 -04:00
}
double scale = 4096.0 / sample_bytes_on_disk ;
2017-09-18 12:39:17 -04:00
if ( _total_free_4k_blocks * scale > ( double ) max_samplecnt ) {
return max_samplecnt ;
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2017-09-18 12:39:17 -04:00
return ( samplecnt_t ) floor ( _total_free_4k_blocks * scale ) ;
2008-06-02 17:41:35 -04:00
}
void
2020-11-11 11:31:52 -05:00
Session : : tempo_map_changed ( )
2008-06-02 17:41:35 -04:00
{
clear_clicks ( ) ;
2022-03-14 16:02:50 -04:00
sync_cues ( ) ;
2008-08-04 18:37:24 -04:00
2019-03-19 00:14:00 -04:00
_playlists - > update_after_tempo_map_change ( ) ;
2008-06-02 17:41:35 -04:00
set_dirty ( ) ;
}
/** Ensures that all buffers (scratch, send, silent, etc) are allocated for
* the given count with the current block size .
*/
void
Session : : ensure_buffers ( ChanCount howmany )
{
2014-05-26 00:58:44 -04:00
BufferManager : : ensure_buffers ( howmany , bounce_processing ( ) ? bounce_chunk_size : 0 ) ;
2008-06-02 17:41:35 -04:00
}
2009-05-04 11:50:51 -04:00
void
Session : : ensure_buffer_set ( BufferSet & buffers , const ChanCount & count )
{
for ( DataType : : iterator t = DataType : : begin ( ) ; t ! = DataType : : end ( ) ; + + t ) {
buffers . ensure_buffers ( * t , count . get ( * t ) , _engine . raw_buffer_size ( * t ) ) ;
}
}
2008-06-02 17:41:35 -04:00
uint32_t
Session : : next_insert_id ( )
{
/* this doesn't really loop forever. just think about it */
while ( true ) {
2016-04-22 17:36:41 -04:00
for ( boost : : dynamic_bitset < uint32_t > : : size_type n = 1 ; n < insert_bitset . size ( ) ; + + n ) {
2008-06-02 17:41:35 -04:00
if ( ! insert_bitset [ n ] ) {
insert_bitset [ n ] = true ;
return n ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
}
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
/* none available, so resize and try again */
insert_bitset . resize ( insert_bitset . size ( ) + 16 , false ) ;
}
}
uint32_t
Session : : next_send_id ( )
{
/* this doesn't really loop forever. just think about it */
while ( true ) {
2016-04-22 17:36:41 -04:00
for ( boost : : dynamic_bitset < uint32_t > : : size_type n = 1 ; n < send_bitset . size ( ) ; + + n ) {
2008-06-02 17:41:35 -04:00
if ( ! send_bitset [ n ] ) {
send_bitset [ n ] = true ;
return n ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
}
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
/* none available, so resize and try again */
send_bitset . resize ( send_bitset . size ( ) + 16 , false ) ;
}
}
2012-01-18 12:51:57 -05:00
uint32_t
Session : : next_aux_send_id ( )
{
/* this doesn't really loop forever. just think about it */
while ( true ) {
2016-04-22 17:36:41 -04:00
for ( boost : : dynamic_bitset < uint32_t > : : size_type n = 1 ; n < aux_send_bitset . size ( ) ; + + n ) {
2012-01-18 12:51:57 -05:00
if ( ! aux_send_bitset [ n ] ) {
aux_send_bitset [ n ] = true ;
return n ;
}
}
/* none available, so resize and try again */
aux_send_bitset . resize ( aux_send_bitset . size ( ) + 16 , false ) ;
}
}
2009-05-07 13:31:18 -04:00
uint32_t
Session : : next_return_id ( )
{
/* this doesn't really loop forever. just think about it */
while ( true ) {
2016-04-22 17:36:41 -04:00
for ( boost : : dynamic_bitset < uint32_t > : : size_type n = 1 ; n < return_bitset . size ( ) ; + + n ) {
2009-05-07 13:31:18 -04:00
if ( ! return_bitset [ n ] ) {
return_bitset [ n ] = true ;
return n ;
}
}
/* none available, so resize and try again */
return_bitset . resize ( return_bitset . size ( ) + 16 , false ) ;
}
}
2008-06-02 17:41:35 -04:00
void
Session : : mark_send_id ( uint32_t id )
{
if ( id > = send_bitset . size ( ) ) {
send_bitset . resize ( id + 16 , false ) ;
}
if ( send_bitset [ id ] ) {
warning < < string_compose ( _ ( " send ID %1 appears to be in use already " ) , id ) < < endmsg ;
}
send_bitset [ id ] = true ;
}
2012-01-18 12:51:57 -05:00
void
Session : : mark_aux_send_id ( uint32_t id )
{
if ( id > = aux_send_bitset . size ( ) ) {
aux_send_bitset . resize ( id + 16 , false ) ;
}
if ( aux_send_bitset [ id ] ) {
warning < < string_compose ( _ ( " aux send ID %1 appears to be in use already " ) , id ) < < endmsg ;
}
aux_send_bitset [ id ] = true ;
}
2009-05-07 13:31:18 -04:00
void
Session : : mark_return_id ( uint32_t id )
{
if ( id > = return_bitset . size ( ) ) {
return_bitset . resize ( id + 16 , false ) ;
}
if ( return_bitset [ id ] ) {
warning < < string_compose ( _ ( " return ID %1 appears to be in use already " ) , id ) < < endmsg ;
}
return_bitset [ id ] = true ;
}
2008-06-02 17:41:35 -04:00
void
Session : : mark_insert_id ( uint32_t id )
{
if ( id > = insert_bitset . size ( ) ) {
insert_bitset . resize ( id + 16 , false ) ;
}
if ( insert_bitset [ id ] ) {
warning < < string_compose ( _ ( " insert ID %1 appears to be in use already " ) , id ) < < endmsg ;
}
insert_bitset [ id ] = true ;
}
2010-03-31 21:24:13 -04:00
void
Session : : unmark_send_id ( uint32_t id )
{
2020-02-25 19:34:36 -05:00
if ( deletion_in_progress ( ) ) {
return ;
}
2010-03-31 21:24:13 -04:00
if ( id < send_bitset . size ( ) ) {
2010-11-26 14:57:03 -05:00
send_bitset [ id ] = false ;
}
2010-03-31 21:24:13 -04:00
}
2012-01-18 12:51:57 -05:00
void
Session : : unmark_aux_send_id ( uint32_t id )
{
2020-02-25 19:34:36 -05:00
if ( deletion_in_progress ( ) ) {
return ;
}
2012-01-18 12:51:57 -05:00
if ( id < aux_send_bitset . size ( ) ) {
aux_send_bitset [ id ] = false ;
}
}
2010-03-31 21:24:13 -04:00
void
Session : : unmark_return_id ( uint32_t id )
{
2019-03-18 10:33:05 -04:00
if ( deletion_in_progress ( ) ) {
return ;
}
2010-03-31 21:24:13 -04:00
if ( id < return_bitset . size ( ) ) {
2010-11-26 14:57:03 -05:00
return_bitset [ id ] = false ;
}
2010-03-31 21:24:13 -04:00
}
void
Session : : unmark_insert_id ( uint32_t id )
{
2020-02-25 19:34:36 -05:00
if ( deletion_in_progress ( ) ) {
return ;
}
2010-03-31 21:24:13 -04:00
if ( id < insert_bitset . size ( ) ) {
2010-11-26 14:57:03 -05:00
insert_bitset [ id ] = false ;
}
2010-03-31 21:24:13 -04:00
}
2008-06-02 17:41:35 -04:00
void
Session : : reset_native_file_format ( )
{
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2015-05-12 10:16:26 -04:00
2010-04-21 16:42:22 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
2019-03-18 10:33:05 -04:00
/* don't save state as we do this, there's no point */
_state_of_the_state = StateOfTheState ( _state_of_the_state | InCleanup ) ;
2010-04-21 16:42:22 -04:00
tr - > reset_write_sources ( false ) ;
2010-11-26 14:57:03 -05:00
_state_of_the_state = StateOfTheState ( _state_of_the_state & ~ InCleanup ) ;
2010-04-21 16:42:22 -04:00
}
2008-06-02 17:41:35 -04:00
}
}
bool
Session : : route_name_unique ( string n ) const
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > name ( ) = = n ) {
return false ;
}
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
return true ;
}
2009-04-29 12:15:33 -04:00
bool
Session : : route_name_internal ( string n ) const
{
if ( auditioner & & auditioner - > name ( ) = = n ) {
return true ;
}
if ( _click_io & & _click_io - > name ( ) = = n ) {
return true ;
}
return false ;
}
2008-06-02 17:41:35 -04:00
int
2010-03-02 13:05:26 -05:00
Session : : freeze_all ( InterThreadInfo & itt )
2008-06-02 17:41:35 -04:00
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-06-02 17:41:35 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2009-04-15 14:04:23 -04:00
boost : : shared_ptr < Track > t ;
2008-06-02 17:41:35 -04:00
2009-04-15 14:04:23 -04:00
if ( ( t = boost : : dynamic_pointer_cast < Track > ( * i ) ) ! = 0 ) {
2008-06-02 17:41:35 -04:00
/* XXX this is wrong because itt.progress will keep returning to zero at the start
of every track .
*/
2010-03-02 13:05:26 -05:00
t - > freeze_me ( itt ) ;
2008-06-02 17:41:35 -04:00
}
}
return 0 ;
}
2020-05-04 20:48:01 -04:00
struct MidiSourceLockMap
{
boost : : shared_ptr < MidiSource > src ;
2022-03-30 14:56:04 -04:00
Source : : WriterLock lock ;
2020-05-04 20:48:01 -04:00
MidiSourceLockMap ( boost : : shared_ptr < MidiSource > midi_source ) : src ( midi_source ) , lock ( src - > mutex ( ) ) { }
} ;
2008-09-10 11:03:30 -04:00
boost : : shared_ptr < Region >
2017-09-18 12:39:17 -04:00
Session : : write_one_track ( Track & track , samplepos_t start , samplepos_t end ,
2009-10-14 12:10:01 -04:00
bool /*overwrite*/ , vector < boost : : shared_ptr < Source > > & srcs ,
2015-10-04 14:51:05 -04:00
InterThreadInfo & itt ,
2012-03-15 17:40:17 -04:00
boost : : shared_ptr < Processor > endpoint , bool include_endpoint ,
2020-07-26 11:15:15 -04:00
bool for_export , bool for_freeze , std : : string const & name )
2008-06-02 17:41:35 -04:00
{
2008-09-10 11:03:30 -04:00
boost : : shared_ptr < Region > result ;
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < Playlist > playlist ;
2014-12-17 21:48:09 -05:00
boost : : shared_ptr < Source > source ;
2010-12-10 17:28:29 -05:00
ChanCount diskstream_channels ( track . n_channels ( ) ) ;
2017-09-18 12:39:17 -04:00
samplepos_t position ;
samplecnt_t this_chunk ;
samplepos_t to_do ;
samplepos_t latency_skip ;
2021-06-11 20:33:09 -04:00
samplepos_t out_pos ;
2008-06-02 17:41:35 -04:00
BufferSet buffers ;
2017-09-18 12:39:17 -04:00
samplepos_t len = end - start ;
2010-11-26 14:57:03 -05:00
bool need_block_size_reset = false ;
2010-12-10 17:28:29 -05:00
ChanCount const max_proc = track . max_processor_streams ( ) ;
2014-06-22 21:58:07 -04:00
string legal_playlist_name ;
string possible_path ;
2020-05-04 20:48:01 -04:00
MidiBuffer resolved ( 256 ) ;
2022-02-01 18:20:28 -05:00
MidiNoteTracker tracker ;
2019-04-07 13:34:37 -04:00
DataType data_type = track . data_type ( ) ;
2020-05-04 20:48:01 -04:00
std : : vector < MidiSourceLockMap * > midi_source_locks ;
2019-04-07 13:34:37 -04:00
2008-09-10 11:03:30 -04:00
if ( end < = start ) {
error < < string_compose ( _ ( " Cannot write a range where end <= start (e.g. %1 <= %2) " ) ,
end , start ) < < endmsg ;
return result ;
}
2008-06-02 17:41:35 -04:00
2014-06-03 15:08:45 -04:00
diskstream_channels = track . bounce_get_output_streams ( diskstream_channels , endpoint ,
include_endpoint , for_export , for_freeze ) ;
2014-05-25 12:10:01 -04:00
2019-04-07 13:51:08 -04:00
if ( data_type = = DataType : : MIDI & & endpoint & & ! for_export & & ! for_freeze & & diskstream_channels . n ( DataType : : AUDIO ) > 0 ) {
data_type = DataType : : AUDIO ;
}
2019-04-07 13:34:37 -04:00
if ( diskstream_channels . n ( data_type ) < 1 ) {
2014-12-17 21:48:09 -05:00
error < < _ ( " Cannot write a range with no data. " ) < < endmsg ;
2014-05-25 12:10:01 -04:00
return result ;
}
2022-02-02 08:16:33 -05:00
/* block all process callback handling, so that thread-buffers
* are available here .
*/
2009-04-16 12:02:25 -04:00
block_processing ( ) ;
2008-08-04 18:37:24 -04:00
2014-05-26 00:17:49 -04:00
_bounce_processing_active = true ;
2008-06-02 17:41:35 -04:00
/* call tree *MUST* hold route_lock */
2008-08-04 18:37:24 -04:00
2010-04-21 16:42:22 -04:00
if ( ( playlist = track . playlist ( ) ) = = 0 ) {
2008-06-02 17:41:35 -04:00
goto out ;
}
2020-07-13 15:30:10 -04:00
if ( name . length ( ) > 0 ) {
/*if the user passed in a name, we will use it, and also prepend the resulting sources with that name*/
legal_playlist_name . append ( legalize_for_path ( name ) + " - " ) ;
}
legal_playlist_name . append ( legalize_for_path ( playlist - > name ( ) ) ) ;
2014-06-22 21:58:07 -04:00
2019-04-07 13:34:37 -04:00
for ( uint32_t chan_n = 0 ; chan_n < diskstream_channels . n ( data_type ) ; + + chan_n ) {
2008-06-02 17:41:35 -04:00
2019-04-07 13:34:37 -04:00
string path = ( ( data_type = = DataType : : AUDIO )
2020-02-25 23:35:07 -05:00
? new_audio_source_path ( legal_playlist_name , diskstream_channels . n_audio ( ) , chan_n , true )
2014-12-17 21:48:09 -05:00
: new_midi_source_path ( legal_playlist_name ) ) ;
2015-10-05 10:17:49 -04:00
2014-06-02 11:20:37 -04:00
if ( path . empty ( ) ) {
2008-06-02 17:41:35 -04:00
goto out ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
try {
2020-03-23 23:47:13 -04:00
source = SourceFactory : : createWritable ( data_type , * this , path , sample_rate ( ) ) ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
catch ( failed_constructor & err ) {
2014-12-17 21:48:09 -05:00
error < < string_compose ( _ ( " cannot create new file \" %1 \" for %2 " ) , path , track . name ( ) ) < < endmsg ;
2008-06-02 17:41:35 -04:00
goto out ;
}
2020-07-14 08:21:13 -04:00
source - > set_captured_for ( track . name ( ) ) ;
2020-07-14 10:31:22 -04:00
time_t now ;
time ( & now ) ;
Glib : : DateTime tm ( Glib : : DateTime : : create_now_local ( now ) ) ;
source - > set_take_id ( tm . format ( " %F %H.%M.%S " ) ) ;
2014-12-17 21:48:09 -05:00
srcs . push_back ( source ) ;
2008-06-02 17:41:35 -04:00
}
2012-03-16 12:51:54 -04:00
/* tell redirects that care that we are about to use a much larger
* blocksize . this will flush all plugins too , so that they are ready
* to be used for this process .
*/
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
need_block_size_reset = true ;
2014-05-26 00:58:44 -04:00
track . set_block_size ( bounce_chunk_size ) ;
_engine . main_thread ( ) - > get_buffers ( ) ;
2010-08-16 15:58:34 -04:00
2008-06-02 17:41:35 -04:00
position = start ;
to_do = len ;
2014-06-03 15:08:45 -04:00
latency_skip = track . bounce_get_latency ( endpoint , include_endpoint , for_export , for_freeze ) ;
2008-06-02 17:41:35 -04:00
/* create a set of reasonably-sized buffers */
2014-05-25 13:43:37 -04:00
for ( DataType : : iterator t = DataType : : begin ( ) ; t ! = DataType : : end ( ) ; + + t ) {
2014-05-26 00:58:44 -04:00
buffers . ensure_buffers ( * t , max_proc . get ( * t ) , bounce_chunk_size ) ;
2014-05-25 13:43:37 -04:00
}
2010-12-10 17:28:29 -05:00
buffers . set_count ( max_proc ) ;
2008-06-02 17:41:35 -04:00
2020-05-04 20:48:01 -04:00
/* prepare MIDI files */
for ( vector < boost : : shared_ptr < Source > > : : iterator src = srcs . begin ( ) ; src ! = srcs . end ( ) ; + + src ) {
boost : : shared_ptr < MidiSource > ms = boost : : dynamic_pointer_cast < MidiSource > ( * src ) ;
if ( ms ) {
midi_source_locks . push_back ( new MidiSourceLockMap ( ms ) ) ;
ms - > mark_streaming_write_started ( midi_source_locks . back ( ) - > lock ) ;
}
}
/* prepare audio files */
2010-12-10 17:28:29 -05:00
for ( vector < boost : : shared_ptr < Source > > : : iterator src = srcs . begin ( ) ; src ! = srcs . end ( ) ; + + src ) {
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < AudioFileSource > afs = boost : : dynamic_pointer_cast < AudioFileSource > ( * src ) ;
2015-01-17 22:19:57 -05:00
if ( afs ) {
2008-06-02 17:41:35 -04:00
afs - > prepare_for_peakfile_writes ( ) ;
2015-01-17 22:19:57 -05:00
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2021-06-11 20:33:09 -04:00
/* process */
out_pos = start ;
2008-06-02 17:41:35 -04:00
while ( to_do & & ! itt . cancel ) {
2008-08-04 18:37:24 -04:00
2014-05-26 00:58:44 -04:00
this_chunk = min ( to_do , bounce_chunk_size ) ;
2008-08-04 18:37:24 -04:00
2020-05-04 20:48:01 -04:00
if ( track . export_stuff ( buffers , start , this_chunk , endpoint , include_endpoint , for_export , for_freeze , tracker ) ) {
2008-06-02 17:41:35 -04:00
goto out ;
}
2014-05-25 13:43:37 -04:00
start + = this_chunk ;
to_do - = this_chunk ;
itt . progress = ( float ) ( 1.0 - ( ( double ) to_do / len ) ) ;
2014-05-26 00:58:44 -04:00
if ( latency_skip > = bounce_chunk_size ) {
latency_skip - = bounce_chunk_size ;
2014-05-25 13:43:37 -04:00
continue ;
}
2017-09-18 12:39:17 -04:00
const samplecnt_t current_chunk = this_chunk - latency_skip ;
2014-05-25 13:43:37 -04:00
2008-06-02 17:41:35 -04:00
uint32_t n = 0 ;
for ( vector < boost : : shared_ptr < Source > > : : iterator src = srcs . begin ( ) ; src ! = srcs . end ( ) ; + + src , + + n ) {
boost : : shared_ptr < AudioFileSource > afs = boost : : dynamic_pointer_cast < AudioFileSource > ( * src ) ;
2014-12-17 21:48:09 -05:00
boost : : shared_ptr < MidiSource > ms ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( afs ) {
2014-05-25 13:43:37 -04:00
if ( afs - > write ( buffers . get_audio ( n ) . data ( latency_skip ) , current_chunk ) ! = current_chunk ) {
2008-06-02 17:41:35 -04:00
goto out ;
}
2020-05-04 20:48:01 -04:00
}
}
2014-12-17 21:48:09 -05:00
2021-06-11 21:13:13 -04:00
/* XXX NUTEMPO fix this to not use samples */
2020-05-04 20:48:01 -04:00
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
2014-12-17 21:48:09 -05:00
const MidiBuffer & buf = buffers . get_midi ( 0 ) ;
for ( MidiBuffer : : const_iterator i = buf . begin ( ) ; i ! = buf . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
Evoral : : Event < samplepos_t > ev = * i ;
2019-04-07 13:18:28 -04:00
if ( ! endpoint | | for_export ) {
ev . set_time ( ev . time ( ) - position ) ;
2021-06-11 20:33:09 -04:00
} else {
/* MidiTrack::export_stuff moves event to the current cycle */
ev . set_time ( ev . time ( ) + out_pos - position ) ;
2019-04-07 13:18:28 -04:00
}
2020-09-20 18:34:09 -04:00
( * m ) - > src - > append_event_samples ( ( * m ) - > lock , ev , ( * m ) - > src - > natural_position ( ) . samples ( ) ) ;
2014-12-17 21:48:09 -05:00
}
2008-06-02 17:41:35 -04:00
}
2021-06-11 20:33:09 -04:00
out_pos + = current_chunk ;
2014-05-25 13:43:37 -04:00
latency_skip = 0 ;
}
2008-08-04 18:37:24 -04:00
2020-09-20 18:34:09 -04:00
tracker . resolve_notes ( resolved , end - 1 ) ;
if ( ! resolved . empty ( ) ) {
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
for ( MidiBuffer : : iterator i = resolved . begin ( ) ; i ! = resolved . end ( ) ; + + i ) {
Evoral : : Event < samplepos_t > ev = * i ;
if ( ! endpoint | | for_export ) {
ev . set_time ( ev . time ( ) - position ) ;
}
( * m ) - > src - > append_event_samples ( ( * m ) - > lock , ev , ( * m ) - > src - > natural_position ( ) . samples ( ) ) ;
}
}
}
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
delete * m ;
}
midi_source_locks . clear ( ) ;
2014-05-25 13:43:37 -04:00
/* post-roll, pick up delayed processor output */
2014-06-03 15:08:45 -04:00
latency_skip = track . bounce_get_latency ( endpoint , include_endpoint , for_export , for_freeze ) ;
2008-08-04 18:37:24 -04:00
2014-05-25 13:43:37 -04:00
while ( latency_skip & & ! itt . cancel ) {
2014-05-26 00:58:44 -04:00
this_chunk = min ( latency_skip , bounce_chunk_size ) ;
2014-05-25 13:43:37 -04:00
latency_skip - = this_chunk ;
buffers . silence ( this_chunk , 0 ) ;
2014-06-03 15:08:45 -04:00
track . bounce_process ( buffers , start , this_chunk , endpoint , include_endpoint , for_export , for_freeze ) ;
2014-05-25 13:43:37 -04:00
2021-06-11 20:33:09 -04:00
start + = this_chunk ;
2014-05-25 13:43:37 -04:00
uint32_t n = 0 ;
for ( vector < boost : : shared_ptr < Source > > : : iterator src = srcs . begin ( ) ; src ! = srcs . end ( ) ; + + src , + + n ) {
boost : : shared_ptr < AudioFileSource > afs = boost : : dynamic_pointer_cast < AudioFileSource > ( * src ) ;
2008-06-02 17:41:35 -04:00
2014-05-25 13:43:37 -04:00
if ( afs ) {
if ( afs - > write ( buffers . get_audio ( n ) . data ( ) , this_chunk ) ! = this_chunk ) {
goto out ;
}
}
}
2021-06-11 20:33:09 -04:00
2021-06-11 21:13:13 -04:00
/* XXX NUTEMPO fix this to not use samples */
2021-06-11 20:33:09 -04:00
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
const MidiBuffer & buf = buffers . get_midi ( 0 ) ;
for ( MidiBuffer : : const_iterator i = buf . begin ( ) ; i ! = buf . end ( ) ; + + i ) {
Evoral : : Event < samplepos_t > ev = * i ;
if ( ! endpoint | | for_export ) {
ev . set_time ( ev . time ( ) - position ) ;
} else {
ev . set_time ( ev . time ( ) + out_pos - position ) ;
}
2021-07-03 21:05:19 -04:00
( * m ) - > src - > append_event_samples ( ( * m ) - > lock , ev , ( * m ) - > src - > natural_position ( ) . samples ( ) ) ;
2021-06-11 20:33:09 -04:00
}
}
out_pos + = this_chunk ;
2008-06-02 17:41:35 -04:00
}
2021-06-11 20:33:09 -04:00
tracker . resolve_notes ( resolved , end - 1 ) ;
if ( ! resolved . empty ( ) ) {
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
for ( MidiBuffer : : iterator i = resolved . begin ( ) ; i ! = resolved . end ( ) ; + + i ) {
Evoral : : Event < samplepos_t > ev = * i ;
if ( ! endpoint | | for_export ) {
ev . set_time ( ev . time ( ) - position ) ;
} else {
ev . set_time ( ev . time ( ) + out_pos - position ) ;
}
2021-07-03 21:05:19 -04:00
( * m ) - > src - > append_event_samples ( ( * m ) - > lock , ev , ( * m ) - > src - > natural_position ( ) . samples ( ) ) ;
2021-06-11 20:33:09 -04:00
}
}
}
for ( vector < MidiSourceLockMap * > : : iterator m = midi_source_locks . begin ( ) ; m ! = midi_source_locks . end ( ) ; + + m ) {
delete * m ;
}
midi_source_locks . clear ( ) ;
2008-06-02 17:41:35 -04:00
if ( ! itt . cancel ) {
2008-08-04 18:37:24 -04:00
2020-12-18 15:02:35 -05:00
PropertyList plist ;
2008-06-02 17:41:35 -04:00
time_t now ;
struct tm * xnow ;
time ( & now ) ;
xnow = localtime ( & now ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
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 ) ;
2014-12-17 21:48:09 -05:00
boost : : shared_ptr < MidiSource > ms ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( afs ) {
afs - > update_header ( position , * xnow , now ) ;
afs - > flush_header ( ) ;
2020-12-18 15:02:35 -05:00
plist . add ( Properties : : start , timepos_t ( 0 ) ) ;
2014-12-17 21:48:09 -05:00
} else if ( ( ms = boost : : dynamic_pointer_cast < MidiSource > ( * src ) ) ) {
2022-03-30 14:56:04 -04:00
Source : : WriterLock lock ( ms - > mutex ( ) ) ;
2014-12-17 21:48:09 -05:00
ms - > mark_streaming_write_completed ( lock ) ;
2020-12-18 15:02:35 -05:00
plist . add ( Properties : : start , timepos_t ( Beats ( ) ) ) ;
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2020-07-28 13:29:11 -04:00
/* construct a whole-file region to represent the bounced material */
2008-06-02 17:41:35 -04:00
2019-07-30 16:43:22 -04:00
plist . add ( Properties : : whole_file , true ) ;
2020-07-28 13:29:11 -04:00
plist . add ( Properties : : length , len ) ; //ToDo: in nutempo, if the Range is snapped to bbt, this should be in bbt (?)
2010-02-18 08:59:49 -05:00
plist . add ( Properties : : name , region_name_from_path ( srcs . front ( ) - > name ( ) , true ) ) ;
2020-07-12 13:39:52 -04:00
plist . add ( Properties : : tags , " (bounce) " ) ;
2011-06-01 12:50:12 -04:00
2019-07-30 16:43:22 -04:00
result = RegionFactory : : create ( srcs , plist , true ) ;
2011-06-01 12:50:12 -04:00
2021-05-20 12:30:39 -04:00
result - > set_name ( legal_playlist_name ) ; /*setting name in the properties didn't seem to work, but this does*/
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2019-04-13 13:19:29 -04:00
out :
2008-09-10 11:03:30 -04:00
if ( ! result ) {
2008-06-02 17:41:35 -04:00
for ( vector < boost : : shared_ptr < Source > > : : iterator src = srcs . begin ( ) ; src ! = srcs . end ( ) ; + + src ) {
2014-12-17 21:48:09 -05:00
( * src ) - > mark_for_remove ( ) ;
2008-06-02 17:41:35 -04:00
( * src ) - > drop_references ( ) ;
}
} else {
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 ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( afs )
afs - > done_with_peakfile_writes ( ) ;
}
}
2014-05-26 00:17:49 -04:00
_bounce_processing_active = false ;
2010-08-16 15:58:34 -04:00
2010-11-26 14:57:03 -05:00
if ( need_block_size_reset ) {
2014-05-26 00:58:44 -04:00
_engine . main_thread ( ) - > drop_buffers ( ) ;
2010-11-26 14:57:03 -05:00
track . set_block_size ( get_block_size ( ) ) ;
}
2011-06-01 12:50:12 -04:00
2009-04-16 12:02:25 -04:00
unblock_processing ( ) ;
2008-06-02 17:41:35 -04:00
2008-09-10 11:03:30 -04:00
return result ;
2008-06-02 17:41:35 -04:00
}
2010-04-13 16:48:33 -04:00
gain_t *
Session : : gain_automation_buffer ( ) const
{
2010-11-26 14:57:03 -05:00
return ProcessThread : : gain_automation_buffer ( ) ;
2010-04-13 16:48:33 -04:00
}
2015-04-25 15:24:58 -04:00
gain_t *
Session : : trim_automation_buffer ( ) const
{
return ProcessThread : : trim_automation_buffer ( ) ;
}
2012-06-11 06:42:30 -04:00
gain_t *
Session : : send_gain_automation_buffer ( ) const
{
return ProcessThread : : send_gain_automation_buffer ( ) ;
}
2017-06-03 06:26:33 -04:00
gain_t *
Session : : scratch_automation_buffer ( ) const
{
return ProcessThread : : scratch_automation_buffer ( ) ;
}
2010-04-13 16:48:33 -04:00
pan_t * *
Session : : pan_automation_buffer ( ) const
{
2010-11-26 14:57:03 -05:00
return ProcessThread : : pan_automation_buffer ( ) ;
2010-04-13 16:48:33 -04:00
}
2008-06-02 17:41:35 -04:00
BufferSet &
Session : : get_silent_buffers ( ChanCount count )
{
2010-11-26 14:57:03 -05:00
return ProcessThread : : get_silent_buffers ( count ) ;
2008-06-02 17:41:35 -04:00
}
BufferSet &
2013-07-31 18:35:24 -04:00
Session : : get_scratch_buffers ( ChanCount count , bool silence )
2008-06-02 17:41:35 -04:00
{
2013-07-31 18:35:24 -04:00
return ProcessThread : : get_scratch_buffers ( count , silence ) ;
2008-06-02 17:41:35 -04:00
}
2016-03-25 19:40:51 -04:00
BufferSet &
Session : : get_noinplace_buffers ( ChanCount count )
{
return ProcessThread : : get_noinplace_buffers ( count ) ;
}
2013-07-30 10:55:33 -04:00
BufferSet &
Session : : get_route_buffers ( ChanCount count , bool silence )
{
return ProcessThread : : get_route_buffers ( count , silence ) ;
}
2008-06-02 17:41:35 -04:00
BufferSet &
Session : : get_mix_buffers ( ChanCount count )
{
2010-11-26 14:57:03 -05:00
return ProcessThread : : get_mix_buffers ( count ) ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
uint32_t
2008-06-02 17:41:35 -04:00
Session : : ntracks ( ) const
{
2020-07-18 19:29:53 -04:00
/* XXX Could be optimized by caching */
2008-06-02 17:41:35 -04:00
uint32_t n = 0 ;
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-06-02 17:41:35 -04:00
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2009-04-15 14:04:23 -04:00
if ( boost : : dynamic_pointer_cast < Track > ( * i ) ) {
2008-06-02 17:41:35 -04:00
+ + n ;
}
}
return n ;
}
2020-07-18 19:29:53 -04:00
uint32_t
Session : : naudiotracks ( ) const
{
/* XXX Could be optimized by caching */
uint32_t n = 0 ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( boost : : dynamic_pointer_cast < AudioTrack > ( * i ) ) {
+ + n ;
}
}
return n ;
}
2008-08-04 18:37:24 -04:00
uint32_t
2008-06-02 17:41:35 -04:00
Session : : nbusses ( ) const
{
uint32_t n = 0 ;
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2008-06-02 17:41:35 -04:00
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2009-04-15 14:04:23 -04:00
if ( boost : : dynamic_pointer_cast < Track > ( * i ) = = 0 ) {
2008-06-02 17:41:35 -04:00
+ + n ;
}
}
return n ;
}
2017-06-16 22:23:55 -04:00
uint32_t
2017-08-15 17:24:32 -04:00
Session : : nstripables ( bool with_monitor ) const
2017-06-16 22:23:55 -04:00
{
uint32_t rv = routes . reader ( ) - > size ( ) ;
rv + = _vca_manager - > vcas ( ) . size ( ) ;
2017-08-15 17:24:32 -04:00
if ( with_monitor ) {
2017-06-16 22:23:55 -04:00
return rv ;
}
if ( _monitor_out ) {
assert ( rv > 0 ) ;
- - rv ;
}
return rv ;
}
2019-08-07 11:27:01 -04:00
bool
Session : : plot_process_graph ( std : : string const & file_name ) const {
return _process_graph ? _process_graph - > plot ( file_name ) : false ;
}
2008-06-02 17:41:35 -04:00
void
Session : : add_automation_list ( AutomationList * al )
{
automation_lists [ al - > id ( ) ] = al ;
}
2010-04-21 16:42:22 -04:00
/** @return true if there is at least one record-enabled track, otherwise false */
2009-04-29 13:30:35 -04:00
bool
2010-04-21 16:42:22 -04:00
Session : : have_rec_enabled_track ( ) const
2009-04-29 13:30:35 -04:00
{
2021-03-19 01:12:11 -04:00
return g_atomic_int_get ( & _have_rec_enabled_track ) = = 1 ;
2009-04-29 13:30:35 -04:00
}
2015-05-08 13:46:42 -04:00
bool
Session : : have_rec_disabled_track ( ) const
{
2021-03-19 01:12:11 -04:00
return g_atomic_int_get ( & _have_rec_disabled_track ) = = 1 ;
2015-05-08 13:46:42 -04:00
}
2010-04-21 16:42:22 -04:00
/** Update the state of our rec-enabled tracks flag */
2009-04-29 13:30:35 -04:00
void
2014-09-19 09:45:01 -04:00
Session : : update_route_record_state ( )
2009-04-29 13:30:35 -04:00
{
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
RouteList : : iterator i = rl - > begin ( ) ;
while ( i ! = rl - > end ( ) ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2016-04-08 16:49:47 -04:00
if ( tr & & tr - > rec_enable_control ( ) - > get_value ( ) ) {
2010-04-21 16:42:22 -04:00
break ;
}
2011-06-01 12:50:12 -04:00
2009-04-29 13:30:35 -04:00
+ + i ;
}
2010-04-21 16:42:22 -04:00
int const old = g_atomic_int_get ( & _have_rec_enabled_track ) ;
2009-04-29 13:30:35 -04:00
2010-04-21 16:42:22 -04:00
g_atomic_int_set ( & _have_rec_enabled_track , i ! = rl - > end ( ) ? 1 : 0 ) ;
2009-04-29 13:30:35 -04:00
2010-04-21 16:42:22 -04:00
if ( g_atomic_int_get ( & _have_rec_enabled_track ) ! = old ) {
2009-04-29 13:30:35 -04:00
RecordStateChanged ( ) ; /* EMIT SIGNAL */
}
2015-05-08 13:46:42 -04:00
2015-05-08 16:26:52 -04:00
for ( i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2015-05-08 13:46:42 -04:00
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2016-04-08 16:49:47 -04:00
if ( tr & & ! tr - > rec_enable_control ( ) - > get_value ( ) ) {
2015-05-08 13:46:42 -04:00
break ;
}
}
2015-10-04 14:51:05 -04:00
2015-05-08 16:26:52 -04:00
g_atomic_int_set ( & _have_rec_disabled_track , i ! = rl - > end ( ) ? 1 : 0 ) ;
bool record_arm_state_changed = ( old ! = g_atomic_int_get ( & _have_rec_enabled_track ) ) ;
2015-10-04 14:51:05 -04:00
2015-05-08 16:26:52 -04:00
if ( record_status ( ) = = Recording & & record_arm_state_changed ) {
RecordArmStateChanged ( ) ;
}
2015-10-05 10:17:49 -04:00
2021-01-29 01:21:37 -05:00
UpdateRouteRecordState ( ) ;
2009-04-29 13:30:35 -04:00
}
2009-06-17 15:30:54 -04:00
void
2009-07-01 09:36:50 -04:00
Session : : listen_position_changed ( )
2009-06-17 15:30:54 -04:00
{
2019-03-17 12:04:45 -04:00
if ( loading ( ) ) {
2019-03-05 19:01:03 -05:00
/* skip duing session restore (already taken care of) */
return ;
}
2016-11-25 12:07:43 -05:00
ProcessorChangeBlocker pcb ( this ) ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > listen_position_changed ( ) ;
2009-06-17 15:30:54 -04:00
}
}
2009-06-20 09:41:55 -04:00
2009-07-01 09:36:50 -04:00
void
Session : : solo_control_mode_changed ( )
{
2016-04-11 09:47:13 -04:00
if ( soloing ( ) | | listening ( ) ) {
2016-11-25 06:50:39 -05:00
if ( loading ( ) ) {
/* We can't use ::clear_all_solo_state() here because during
session loading at program startup , that will queue a call
to rt_clear_all_solo_state ( ) that will not execute until
AFTER solo states have been established ( thus throwing away
the session ' s saved solo state ) . So just explicitly turn
them all off .
*/
set_controls ( route_list_to_control_list ( get_routes ( ) , & Stripable : : solo_control ) , 0.0 , Controllable : : NoGroup ) ;
} else {
clear_all_solo_state ( get_routes ( ) ) ;
}
2016-04-10 18:20:11 -04:00
}
2009-07-01 09:36:50 -04:00
}
2011-09-07 07:56:23 -04:00
/** Called when a property of one of our route groups changes */
2009-06-20 09:41:55 -04:00
void
2016-01-19 14:16:49 -05:00
Session : : route_group_property_changed ( RouteGroup * rg )
2009-06-20 13:15:33 -04:00
{
2016-01-19 14:16:49 -05:00
RouteGroupPropertyChanged ( rg ) ; /* EMIT SIGNAL */
2011-09-07 07:56:23 -04:00
}
/** Called when a route is added to one of our route groups */
void
Session : : route_added_to_route_group ( RouteGroup * rg , boost : : weak_ptr < Route > r )
{
RouteAddedToRouteGroup ( rg , r ) ;
}
/** Called when a route is removed from one of our route groups */
void
Session : : route_removed_from_route_group ( RouteGroup * rg , boost : : weak_ptr < Route > r )
{
2014-09-19 09:45:01 -04:00
update_route_record_state ( ) ;
RouteRemovedFromRouteGroup ( rg , r ) ; /* EMIT SIGNAL */
2016-11-10 17:57:47 -05:00
if ( ! rg - > has_control_master ( ) & & ! rg - > has_subgroup ( ) & & rg - > empty ( ) ) {
remove_route_group ( * rg ) ;
}
2009-06-20 13:15:33 -04:00
}
2009-11-09 15:05:18 -05:00
2018-10-10 05:17:57 -04:00
boost : : shared_ptr < AudioTrack >
2021-05-25 20:33:31 -04:00
Session : : get_nth_audio_track ( uint32_t nth ) const
2018-10-10 05:17:57 -04:00
{
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
rl - > sort ( Stripable : : Sorter ( ) ) ;
for ( RouteList : : const_iterator r = rl - > begin ( ) ; r ! = rl - > end ( ) ; + + r ) {
2021-05-25 20:33:31 -04:00
boost : : shared_ptr < AudioTrack > at = boost : : dynamic_pointer_cast < AudioTrack > ( * r ) ;
if ( ! at ) {
2018-10-10 05:17:57 -04:00
continue ;
}
2021-05-25 20:33:31 -04:00
if ( nth - - = = 0 ) {
return at ;
2018-10-10 05:17:57 -04:00
}
}
return boost : : shared_ptr < AudioTrack > ( ) ;
}
2014-07-09 11:29:26 -04:00
boost : : shared_ptr < RouteList >
Session : : get_tracks ( ) const
{
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
boost : : shared_ptr < RouteList > tl ( new RouteList ) ;
for ( RouteList : : const_iterator r = rl - > begin ( ) ; r ! = rl - > end ( ) ; + + r ) {
if ( boost : : dynamic_pointer_cast < Track > ( * r ) ) {
2017-08-15 17:24:32 -04:00
assert ( ! ( * r ) - > is_auditioner ( ) ) ; // XXX remove me
tl - > push_back ( * r ) ;
2014-07-09 11:29:26 -04:00
}
}
return tl ;
}
2009-11-29 07:47:59 -05:00
boost : : shared_ptr < RouteList >
2020-09-20 18:34:09 -04:00
Session : : get_routes_with_regions_at ( timepos_t const & p ) const
2009-11-29 07:47:59 -05:00
{
2010-11-29 17:07:42 -05:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
boost : : shared_ptr < RouteList > rl ( new RouteList ) ;
2009-11-29 07:47:59 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( ! tr ) {
continue ;
}
2011-06-01 12:50:12 -04:00
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < Playlist > pl = tr - > playlist ( ) ;
2009-11-29 07:47:59 -05:00
if ( ! pl ) {
continue ;
}
2011-06-01 12:50:12 -04:00
2009-11-29 07:47:59 -05:00
if ( pl - > has_region_at ( p ) ) {
rl - > push_back ( * i ) ;
}
}
return rl ;
}
2010-05-09 16:48:21 -04:00
void
Session : : goto_end ( )
{
if ( _session_range_location ) {
2020-09-20 18:34:09 -04:00
request_locate ( _session_range_location - > end ( ) . samples ( ) , MustStop ) ;
2010-05-09 16:48:21 -04:00
} else {
2020-01-17 17:26:01 -05:00
request_locate ( 0 , MustStop ) ;
2010-05-09 16:48:21 -04:00
}
}
void
2016-06-17 15:41:28 -04:00
Session : : goto_start ( bool and_roll )
2010-05-09 16:48:21 -04:00
{
if ( _session_range_location ) {
2020-09-20 18:34:09 -04:00
request_locate ( _session_range_location - > start ( ) . samples ( ) , and_roll ? MustRoll : RollIfAppropriate ) ;
2010-05-09 16:48:21 -04:00
} else {
2020-01-17 18:35:28 -05:00
request_locate ( 0 , and_roll ? MustRoll : RollIfAppropriate ) ;
2010-05-09 16:48:21 -04:00
}
}
2017-09-18 12:39:17 -04:00
samplepos_t
Session : : current_start_sample ( ) const
2010-05-09 16:48:21 -04:00
{
2020-09-20 18:34:09 -04:00
return _session_range_location ? _session_range_location - > start ( ) . samples ( ) : 0 ;
2010-05-09 16:48:21 -04:00
}
2017-09-18 12:39:17 -04:00
samplepos_t
Session : : current_end_sample ( ) const
2010-05-09 16:48:21 -04:00
{
2020-09-20 18:34:09 -04:00
return _session_range_location ? _session_range_location - > end ( ) . samples ( ) : 0 ;
2010-05-09 16:48:21 -04:00
}
2020-10-15 01:09:02 -04:00
timepos_t
Session : : current_start ( ) const
{
return _session_range_location ? _session_range_location - > start ( ) : timepos_t : : max ( Temporal : : AudioTime ) ;
}
timepos_t
Session : : current_end ( ) const
{
return _session_range_location ? _session_range_location - > end ( ) : timepos_t : : max ( Temporal : : AudioTime ) ;
}
2010-07-24 12:40:56 -04:00
void
Session : : step_edit_status_change ( bool yn )
{
2010-11-26 14:57:03 -05:00
bool send = false ;
2010-07-24 12:40:56 -04:00
2010-11-26 14:57:03 -05:00
bool val = false ;
if ( yn ) {
send = ( _step_editors = = 0 ) ;
val = true ;
2010-07-24 12:40:56 -04:00
2010-11-26 14:57:03 -05:00
_step_editors + + ;
} else {
send = ( _step_editors = = 1 ) ;
val = false ;
2010-07-24 12:40:56 -04:00
2010-11-26 14:57:03 -05:00
if ( _step_editors > 0 ) {
_step_editors - - ;
}
}
2010-07-24 12:40:56 -04:00
2010-11-26 14:57:03 -05:00
if ( send ) {
StepEditStatusChange ( val ) ;
}
2010-07-24 12:40:56 -04:00
}
2010-08-01 21:59:34 -04:00
2011-06-01 12:50:12 -04:00
2010-10-07 14:33:20 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : start_time_changed ( samplepos_t old )
2010-10-07 14:33:20 -04:00
{
/* Update the auto loop range to match the session range
( unless the auto loop range has been changed by the user )
*/
2010-12-29 17:36:03 -05:00
2010-10-07 14:33:20 -04:00
Location * s = _locations - > session_range_location ( ) ;
2010-12-29 17:36:03 -05:00
if ( s = = 0 ) {
return ;
}
2011-06-01 12:50:12 -04:00
2010-10-07 14:33:20 -04:00
Location * l = _locations - > auto_loop_location ( ) ;
2012-04-07 06:05:17 -04:00
if ( l & & l - > start ( ) = = old ) {
2010-10-07 14:33:20 -04:00
l - > set_start ( s - > start ( ) , true ) ;
}
2016-09-09 08:04:26 -04:00
set_dirty ( ) ;
2010-10-07 14:33:20 -04:00
}
void
2017-09-18 12:39:17 -04:00
Session : : end_time_changed ( samplepos_t old )
2010-10-07 14:33:20 -04:00
{
/* Update the auto loop range to match the session range
( unless the auto loop range has been changed by the user )
*/
Location * s = _locations - > session_range_location ( ) ;
2010-12-29 17:36:03 -05:00
if ( s = = 0 ) {
return ;
}
2011-06-01 12:50:12 -04:00
2010-10-07 14:33:20 -04:00
Location * l = _locations - > auto_loop_location ( ) ;
2011-10-07 15:49:21 -04:00
if ( l & & l - > end ( ) = = old ) {
2010-10-07 14:33:20 -04:00
l - > set_end ( s - > end ( ) , true ) ;
}
2016-09-09 08:04:26 -04:00
set_dirty ( ) ;
2010-10-07 14:33:20 -04:00
}
2010-11-09 01:03:51 -05:00
2013-07-15 08:05:37 -04:00
std : : vector < std : : string >
2010-11-09 01:03:51 -05:00
Session : : source_search_path ( DataType type ) const
{
2013-08-15 06:04:08 -04:00
Searchpath sp ;
2011-12-07 13:52:14 -05:00
if ( session_dirs . size ( ) = = 1 ) {
switch ( type ) {
case DataType : : AUDIO :
2013-07-15 08:05:37 -04:00
sp . push_back ( _session_dir - > sound_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
case DataType : : MIDI :
2013-07-15 08:05:37 -04:00
sp . push_back ( _session_dir - > midi_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
}
} else {
for ( vector < space_and_path > : : const_iterator i = session_dirs . begin ( ) ; i ! = session_dirs . end ( ) ; + + i ) {
SessionDirectory sdir ( i - > path ) ;
switch ( type ) {
case DataType : : AUDIO :
2013-07-15 08:05:37 -04:00
sp . push_back ( sdir . sound_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
case DataType : : MIDI :
2013-07-15 08:05:37 -04:00
sp . push_back ( sdir . midi_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
}
}
}
2010-11-09 01:03:51 -05:00
2012-10-23 10:52:26 -04:00
if ( type = = DataType : : AUDIO ) {
const string sound_path_2X = _session_dir - > sound_path_2X ( ) ;
if ( Glib : : file_test ( sound_path_2X , Glib : : FILE_TEST_EXISTS | Glib : : FILE_TEST_IS_DIR ) ) {
2013-07-15 08:05:37 -04:00
if ( find ( sp . begin ( ) , sp . end ( ) , sound_path_2X ) = = sp . end ( ) ) {
sp . push_back ( sound_path_2X ) ;
2012-11-14 16:54:04 -05:00
}
2012-10-23 10:52:26 -04:00
}
}
2013-07-15 08:05:37 -04:00
// now check the explicit (possibly user-specified) search path
2011-12-07 12:45:18 -05:00
switch ( type ) {
case DataType : : AUDIO :
2013-08-15 06:04:08 -04:00
sp + = Searchpath ( config . get_audio_search_path ( ) ) ;
2011-12-07 12:45:18 -05:00
break ;
case DataType : : MIDI :
2013-08-15 06:04:08 -04:00
sp + = Searchpath ( config . get_midi_search_path ( ) ) ;
2011-12-07 12:45:18 -05:00
break ;
}
2013-07-15 08:05:37 -04:00
return sp ;
2010-11-09 01:03:51 -05:00
}
2010-11-09 12:24:17 -05:00
void
Session : : ensure_search_path_includes ( const string & path , DataType type )
{
2013-08-15 06:04:08 -04:00
Searchpath sp ;
2010-11-09 12:24:17 -05:00
2010-11-26 14:57:03 -05:00
if ( path = = " . " ) {
return ;
}
2010-11-09 18:50:20 -05:00
2011-12-07 13:52:14 -05:00
switch ( type ) {
case DataType : : AUDIO :
2013-08-15 06:04:08 -04:00
sp + = Searchpath ( config . get_audio_search_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
case DataType : : MIDI :
2013-08-15 06:04:08 -04:00
sp + = Searchpath ( config . get_midi_search_path ( ) ) ;
2011-12-07 13:52:14 -05:00
break ;
}
2012-05-28 12:32:41 -04:00
2013-07-15 08:05:37 -04:00
for ( vector < std : : string > : : iterator i = sp . begin ( ) ; i ! = sp . end ( ) ; + + i ) {
2012-03-04 19:34:45 -05:00
/* No need to add this new directory if it has the same inode as
an existing one ; checking inode rather than name prevents duplicated
directories when we are using symlinks .
On Windows , I think we could just do if ( * i = = path ) here .
*/
2012-06-23 01:08:14 -04:00
if ( PBD : : equivalent_paths ( * i , path ) ) {
2010-11-26 14:57:03 -05:00
return ;
}
}
2010-11-09 12:24:17 -05:00
2013-07-15 08:05:37 -04:00
sp + = path ;
2011-06-01 12:50:12 -04:00
2010-11-26 14:57:03 -05:00
switch ( type ) {
case DataType : : AUDIO :
2013-07-15 08:05:37 -04:00
config . set_audio_search_path ( sp . to_string ( ) ) ;
2010-11-26 14:57:03 -05:00
break ;
case DataType : : MIDI :
2013-07-15 08:05:37 -04:00
config . set_midi_search_path ( sp . to_string ( ) ) ;
2010-11-26 14:57:03 -05:00
break ;
}
2010-11-09 12:24:17 -05:00
}
2010-11-18 19:58:57 -05:00
2014-07-08 00:53:06 -04:00
void
Session : : remove_dir_from_search_path ( const string & dir , DataType type )
{
Searchpath sp ;
switch ( type ) {
case DataType : : AUDIO :
sp = Searchpath ( config . get_audio_search_path ( ) ) ;
break ;
case DataType : : MIDI :
sp = Searchpath ( config . get_midi_search_path ( ) ) ;
break ;
}
sp - = dir ;
switch ( type ) {
case DataType : : AUDIO :
config . set_audio_search_path ( sp . to_string ( ) ) ;
break ;
case DataType : : MIDI :
config . set_midi_search_path ( sp . to_string ( ) ) ;
break ;
}
}
2011-02-17 11:43:55 -05:00
boost : : shared_ptr < Speakers >
2011-06-01 12:50:12 -04:00
Session : : get_speakers ( )
2010-11-18 19:58:57 -05:00
{
2011-03-14 17:53:10 -04:00
return _speakers ;
2010-11-18 19:58:57 -05:00
}
2010-11-26 18:30:48 -05:00
list < string >
Session : : unknown_processors ( ) const
{
list < string > p ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
list < string > t = ( * i ) - > unknown_processors ( ) ;
copy ( t . begin ( ) , t . end ( ) , back_inserter ( p ) ) ;
}
p . sort ( ) ;
p . unique ( ) ;
return p ;
}
2011-02-11 11:14:54 -05:00
2020-02-17 19:26:20 -05:00
list < string >
Session : : missing_filesources ( DataType dt ) const
{
list < string > p ;
for ( SourceMap : : const_iterator i = sources . begin ( ) ; i ! = sources . end ( ) ; + + i ) {
if ( dt = = DataType : : AUDIO & & 0 ! = boost : : dynamic_pointer_cast < SilentFileSource > ( i - > second ) ) {
p . push_back ( i - > second - > name ( ) ) ;
}
else if ( dt = = DataType : : MIDI & & 0 ! = boost : : dynamic_pointer_cast < SMFSource > ( i - > second ) & & ( i - > second - > flags ( ) & Source : : Missing ) ! = 0 ) {
p . push_back ( i - > second - > name ( ) ) ;
}
}
p . sort ( ) ;
return p ;
}
2019-12-16 18:13:27 -05:00
void
Session : : initialize_latencies ( )
{
2022-02-05 18:45:49 -05:00
block_processing ( ) ;
2020-04-25 11:52:54 -04:00
update_latency ( false ) ;
update_latency ( true ) ;
2022-02-05 18:45:49 -05:00
unblock_processing ( ) ;
2017-09-28 22:17:16 -04:00
}
2016-05-03 07:56:08 -04:00
2017-09-28 22:17:16 -04:00
void
Session : : send_latency_compensation_change ( )
{
/* As a result of Send::set_output_latency()
* or InternalReturn : : set_playback_offset ( )
* the send ' s own latency can change ( source track
* is aligned with target bus ) .
*
* This can only happen be triggered by
* Route : : update_signal_latency ( )
* when updating the processor latency .
*
* We need to walk the graph again to take those changes into account
* ( we should probably recurse or process the graph in a 2 step process ) .
*/
+ + _send_latency_changes ;
}
2022-02-06 22:30:48 -05:00
void
Session : : update_send_delaylines ( )
{
/* called in rt-thread, if send latency changed */
_update_send_delaylines = true ;
}
2017-09-28 22:17:16 -04:00
bool
2020-04-27 01:25:32 -04:00
Session : : update_route_latency ( bool playback , bool apply_to_delayline , bool * delayline_update_needed )
2017-09-28 22:17:16 -04:00
{
2020-04-25 23:55:43 -04:00
/* apply_to_delayline can no be called concurrently with processing
* caller must hold process lock when apply_to_delayline = = true */
assert ( ! apply_to_delayline | | ! AudioEngine : : instance ( ) - > process_lock ( ) . trylock ( ) ) ;
2020-04-25 11:52:54 -04:00
2020-04-26 14:47:43 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " update_route_latency: %1 apply_to_delayline? %2) \n " , ( playback ? " PLAYBACK " : " CAPTURE " ) , ( apply_to_delayline ? " yes " : " no " ) ) ) ;
2017-09-28 22:17:16 -04:00
/* Note: RouteList is process-graph sorted */
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
if ( playback ) {
/* reverse the list so that we work backwards from the last route to run to the first,
* this is not needed , but can help to reduce the iterations for aux - sends .
*/
RouteList * rl = routes . reader ( ) . get ( ) ;
r . reset ( new RouteList ( * rl ) ) ;
reverse ( r - > begin ( ) , r - > end ( ) ) ;
}
bool changed = false ;
int bailout = 0 ;
restart :
_send_latency_changes = 0 ;
_worst_route_latency = 0 ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
// if (!(*i)->active()) { continue ; } // TODO
samplecnt_t l ;
2020-04-27 01:25:32 -04:00
if ( ( * i ) - > signal_latency ( ) ! = ( l = ( * i ) - > update_signal_latency ( apply_to_delayline , delayline_update_needed ) ) ) {
2017-09-28 22:17:16 -04:00
changed = true ;
}
_worst_route_latency = std : : max ( l , _worst_route_latency ) ;
}
if ( _send_latency_changes > 0 ) {
2020-04-26 14:47:43 -04:00
/* One extra iteration might be needed since we allow u level of aux-sends.
* Except mixbus that allows up to 3 ( aux - sends , sends to mixbusses 1 - 8 , sends to mixbusses 9 - 12 ,
* and then there ' s JACK */
2017-09-28 22:17:16 -04:00
if ( + + bailout < 5 ) {
cerr < < " restarting Session::update_latency. # of send changes: " < < _send_latency_changes < < " iteration: " < < bailout < < endl ;
goto restart ;
}
}
2020-04-26 14:47:43 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " update_route_latency: worst proc latency: %1 (changed? %2) recursions: %3 \n " , _worst_route_latency , ( changed ? " yes " : " no " ) , bailout ) ) ;
2017-09-28 22:17:16 -04:00
return changed ;
}
2021-09-08 15:43:23 -04:00
void
Session : : set_owned_port_public_latency ( bool playback )
{
/* special routes or IO or ports owned by the session */
if ( auditioner ) {
samplecnt_t latency = auditioner - > set_private_port_latencies ( playback ) ;
auditioner - > set_public_port_latencies ( latency , playback , true ) ;
}
_click_io - > set_public_port_latencies ( _click_io - > connected_latency ( playback ) , playback ) ;
if ( _midi_ports ) {
_midi_ports - > set_public_latency ( playback ) ;
}
}
2017-09-28 22:17:16 -04:00
void
Session : : update_latency ( bool playback )
{
2019-10-25 19:06:04 -04:00
/* called only from AudioEngine::latency_callback.
* but may indirectly be triggered from
* Session : : update_latency_compensation - > _engine . update_latencies
*/
2020-07-18 17:27:11 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " Engine latency callback: %1 (initial/deletion: %2 adding: %3 deletion: %4) \n " ,
2020-07-11 17:38:44 -04:00
( playback ? " PLAYBACK " : " CAPTURE " ) ,
inital_connect_or_deletion_in_progress ( ) ,
_adding_routes_in_progress ,
_route_deletion_in_progress
) ) ;
2011-02-15 22:25:23 -05:00
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) | | _adding_routes_in_progress | | _route_deletion_in_progress ) {
2020-07-11 18:40:14 -04:00
_engine . queue_latency_update ( playback ) ;
2011-03-14 17:53:10 -04:00
return ;
}
2020-03-05 15:17:25 -05:00
if ( ! _engine . running ( ) | | _exporting ) {
2017-07-20 10:45:14 -04:00
return ;
}
2011-03-14 16:33:47 -04:00
2020-04-26 16:17:18 -04:00
/* Session::new_midi_track -> Route::add_processors -> Delivery::configure_io
* - > IO : : ensure_ports - > PortManager : : register_output_port
* may run currently ( adding many ports ) while the backend
* already emits AudioEngine : : latency_callback ( ) for previously
* added ports .
*
* Route : : set_public_port_latencies ( ) - > IO : : latency may try
* to lookup ports that don ' t yet exist .
* IO : : * uses BLOCK_PROCESS_CALLBACK to prevent concurrency ,
* so the same has to be done here to prevent a race .
*/
2020-04-26 22:10:18 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) , Glib : : Threads : : TRY_LOCK ) ;
if ( ! lm . locked ( ) ) {
/* IO::ensure_ports() calls jack_port_register() while holding the process-lock,
* JACK2 may block and call JACKAudioBackend : : _latency_callback ( ) which
* ends up here . https : //pastebin.com/mitGBwpq
*
* This is a stopgap to be able to use 6.0 with JACK2 ' s insane threading .
* Yes JACK can also concurrently process ( using the old graph ) yet emit
* a latency - callback ( for which we do need the lock ) .
*
* One alternative is to use _adding_routes_in_progress and
* call graph_reordered ( false ) ; however various entry - points
* to ensure_io don ' t originate from Session .
*
* Eventually Ardour will probably need to be changed to
* register ports lock - free , and mark those ports as " pending " ,
* and skip them during process and all other callbacks .
*
* Then clear the pending flags in the rt - process context after
* a port - registraion callback .
*/
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " Engine latency callback: called with process-lock held. queue for later. \n " ) ;
queue_latency_recompute ( ) ;
return ;
}
/* Note; RouteList is sorted as process-graph */
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
if ( playback ) {
/* reverse the list so that we work backwards from the last route to run to the first */
RouteList * rl = routes . reader ( ) . get ( ) ;
r . reset ( new RouteList ( * rl ) ) ;
reverse ( r - > begin ( ) , r - > end ( ) ) ;
}
2011-02-15 22:25:23 -05:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2021-09-08 15:43:23 -04:00
/* private port latency includes plugin and I/O delay,
* but no latency compensation delaylines .
*/
2017-09-28 00:31:12 -04:00
samplecnt_t latency = ( * i ) - > set_private_port_latencies ( playback ) ;
2021-09-08 15:43:23 -04:00
/* However we also need to reset the latency of connected external
* ports , since those includes latency compensation delaylines .
*/
( * i ) - > set_public_port_latencies ( latency , playback , false ) ;
2017-09-21 22:15:56 -04:00
}
2011-06-01 12:50:12 -04:00
2021-09-08 15:43:23 -04:00
set_owned_port_public_latency ( playback ) ;
2011-03-14 17:53:10 -04:00
if ( playback ) {
2020-04-26 14:25:31 -04:00
/* Processing needs to be blocked while re-configuring delaylines.
2020-04-25 11:52:54 -04:00
*
2020-04-26 14:25:31 -04:00
* With internal backends , AudioEngine : : latency_callback ( ) - > this method
* is called from the main_process_thread ( so the lock is not contended ) .
2020-04-25 11:52:54 -04:00
* However jack2 can concurrently process and reconfigure port latencies .
2020-04-26 16:17:18 -04:00
* - > keep the process - lock .
2020-04-25 11:52:54 -04:00
*/
2020-04-26 14:25:31 -04:00
/* prevent any concurrent latency updates */
Glib : : Threads : : Mutex : : Lock lx ( _update_latency_lock ) ;
set_worst_output_latency ( ) ;
2020-04-27 01:25:32 -04:00
update_route_latency ( true , /*apply_to_delayline*/ true , NULL ) ;
2020-04-25 11:52:54 -04:00
2020-04-26 16:17:18 -04:00
/* relese before emiting signals */
lm . release ( ) ;
2011-03-14 17:53:10 -04:00
} else {
2020-04-26 16:17:18 -04:00
/* process lock is not needed to update worst-case latency */
lm . release ( ) ;
2019-10-25 19:06:04 -04:00
Glib : : Threads : : Mutex : : Lock lx ( _update_latency_lock ) ;
2017-09-28 22:17:16 -04:00
set_worst_input_latency ( ) ;
2020-04-27 01:25:32 -04:00
update_route_latency ( false , false , NULL ) ;
2011-03-14 17:53:10 -04:00
}
2011-03-12 15:25:09 -05:00
2021-09-08 15:43:23 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
/* Publish port latency. This includes latency-compensation
* delaylines in the direction of signal flow .
*/
samplecnt_t latency = ( * i ) - > set_private_port_latencies ( playback ) ;
( * i ) - > set_public_port_latencies ( latency , playback , true ) ;
}
/* now handle non-route ports that we are responsible for */
set_owned_port_public_latency ( playback ) ;
2019-10-25 19:06:04 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " Engine latency callback: DONE \n " ) ;
2020-04-06 18:10:29 -04:00
LatencyUpdated ( playback ) ; /* EMIT SIGNAL */
2011-03-10 21:55:52 -05:00
}
2011-03-14 16:33:47 -04:00
void
2017-09-28 22:17:16 -04:00
Session : : set_worst_output_latency ( )
2011-03-14 16:33:47 -04:00
{
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) ) {
2011-03-14 17:53:10 -04:00
return ;
}
2011-03-14 16:33:47 -04:00
2011-03-14 17:53:10 -04:00
_worst_output_latency = 0 ;
2021-09-08 15:45:16 -04:00
_io_latency = 0 ;
2011-03-14 16:33:47 -04:00
2018-11-28 09:24:47 -05:00
if ( ! _engine . running ( ) ) {
2011-03-14 16:33:47 -04:00
return ;
}
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2011-03-14 17:53:10 -04:00
_worst_output_latency = max ( _worst_output_latency , ( * i ) - > output ( ) - > latency ( ) ) ;
2021-09-08 15:45:16 -04:00
_io_latency = max ( _io_latency , ( * i ) - > output ( ) - > latency ( ) + ( * i ) - > input ( ) - > latency ( ) ) ;
2011-03-14 16:33:47 -04:00
}
2017-09-28 00:31:12 -04:00
_worst_output_latency = max ( _worst_output_latency , _click_io - > latency ( ) ) ;
2019-10-25 19:06:04 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " Worst output latency: %1 \n " , _worst_output_latency ) ) ;
2011-03-14 16:33:47 -04:00
}
void
2017-09-28 22:17:16 -04:00
Session : : set_worst_input_latency ( )
2011-03-14 16:33:47 -04:00
{
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) ) {
2011-03-14 17:53:10 -04:00
return ;
}
2011-03-14 16:33:47 -04:00
2011-03-14 17:53:10 -04:00
_worst_input_latency = 0 ;
2011-03-14 16:33:47 -04:00
2018-11-28 09:24:47 -05:00
if ( ! _engine . running ( ) ) {
2011-03-14 16:33:47 -04:00
return ;
}
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2011-03-14 17:53:10 -04:00
_worst_input_latency = max ( _worst_input_latency , ( * i ) - > input ( ) - > latency ( ) ) ;
2011-03-14 16:33:47 -04:00
}
2019-10-25 19:06:04 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " Worst input latency: %1 \n " , _worst_input_latency ) ) ;
2011-03-14 16:33:47 -04:00
}
void
2019-10-28 19:23:54 -04:00
Session : : update_latency_compensation ( bool force_whole_graph , bool called_from_backend )
2011-03-14 16:33:47 -04:00
{
2019-10-28 18:53:00 -04:00
/* Called to update Ardour's internal latency values and compensation
* planning . Typically case is from within : : graph_reordered ( )
*/
2019-03-18 10:33:05 -04:00
if ( inital_connect_or_deletion_in_progress ( ) ) {
2011-03-14 16:33:47 -04:00
return ;
}
2019-10-28 18:53:00 -04:00
2018-10-24 20:00:08 -04:00
/* this lock is not usually contended, but under certain conditions,
* update_latency_compensation may be called concurrently .
* e . g . drag / drop copy a latent plugin while rolling .
* GUI thread ( via route_processors_changed ) and
* auto_connect_thread_run may race .
*/
Glib : : Threads : : Mutex : : Lock lx ( _update_latency_lock , Glib : : Threads : : TRY_LOCK ) ;
if ( ! lx . locked ( ) ) {
/* no need to do this twice */
return ;
}
2011-03-14 16:33:47 -04:00
2020-04-26 14:47:43 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , string_compose ( " update_latency_compensation%1. \n " , ( force_whole_graph ? " of whole graph " : " " ) ) ) ;
2019-10-25 19:06:04 -04:00
2020-04-27 01:25:32 -04:00
bool delayline_update_needed = false ;
bool some_track_latency_changed = update_route_latency ( false , false , & delayline_update_needed ) ;
2015-10-05 10:17:49 -04:00
2011-09-21 13:28:13 -04:00
if ( some_track_latency_changed | | force_whole_graph ) {
2019-10-28 19:23:54 -04:00
2019-10-28 15:39:59 -04:00
/* cannot hold lock while engine initiates a full latency callback */
2019-10-28 19:23:54 -04:00
2019-10-28 15:39:59 -04:00
lx . release ( ) ;
2019-10-28 19:23:54 -04:00
2019-10-28 18:53:00 -04:00
/* next call will ask the backend up update its latencies.
*
* The semantics of how the backend does this are not well
* defined ( Oct 2019 ) .
*
* In all cases , eventually AudioEngine : : latency_callback ( ) is
* invoked , which will call Session : : update_latency ( ) .
*
* Some backends will do that asynchronously with respect to
* this call . Others ( JACK1 ) will do so synchronously , and in
* those cases this call will return until the backend latency
* callback is complete .
2019-10-28 19:23:54 -04:00
*
* Further , if this is called as part of a backend callback ,
* then we have to follow the JACK1 rule that we cannot call
* back into the backend during such a callback ( otherwise
* deadlock ensues ) .
2017-09-28 22:17:16 -04:00
*/
2019-10-28 19:23:54 -04:00
if ( ! called_from_backend ) {
2019-10-28 19:34:38 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " update_latency_compensation: delegate to engine \n " ) ;
2019-10-28 18:55:29 -04:00
_engine . update_latencies ( ) ;
2019-10-28 19:34:38 -04:00
} else {
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " update_latency_compensation called from engine, don't call back into engine \n " ) ;
2019-10-28 18:55:29 -04:00
}
2020-04-27 01:25:32 -04:00
} else if ( delayline_update_needed ) {
2020-04-26 14:47:43 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " update_latency_compensation: directly apply to routes \n " ) ;
2020-04-27 01:25:32 -04:00
lx . release ( ) ; // XXX cannot hold this lock when acquiring process_lock ?!
2020-04-25 23:55:43 -04:00
# ifndef MIXBUS
2020-04-25 11:52:54 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) , Glib : : Threads : : NOT_LOCK ) ;
# endif
2020-04-25 23:55:43 -04:00
lm . acquire ( ) ;
2020-04-25 11:52:54 -04:00
2017-09-28 22:17:16 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > apply_latency_compensation ( ) ;
}
2011-09-21 13:28:13 -04:00
}
2020-04-26 14:47:43 -04:00
DEBUG_TRACE ( DEBUG : : LatencyCompensation , " update_latency_compensation: complete \n " ) ;
2011-03-14 16:33:47 -04:00
}
2020-06-09 14:18:02 -04:00
const std : : string
2011-12-08 22:06:58 -05:00
Session : : session_name_is_legal ( const string & path )
{
2020-06-23 16:05:40 -04:00
static const char illegal_chars [ ] = { ' / ' , ' \\ ' , ' : ' , ' ; ' } ;
2011-12-08 22:06:58 -05:00
2020-06-23 16:05:40 -04:00
for ( size_t i = 0 ; i < sizeof ( illegal_chars ) ; + + i ) {
2011-12-08 22:06:58 -05:00
if ( path . find ( illegal_chars [ i ] ) ! = string : : npos ) {
2020-06-09 14:18:02 -04:00
return std : : string ( 1 , illegal_chars [ i ] ) ;
2011-12-08 22:06:58 -05:00
}
}
2020-06-09 14:18:02 -04:00
for ( size_t i = 0 ; i < path . length ( ) ; + + i ) {
if ( iscntrl ( path [ i ] ) ) {
return _ ( " Control Char " ) ;
}
}
return std : : string ( ) ;
2011-12-08 22:06:58 -05:00
}
2012-01-17 20:30:44 -05:00
2012-06-27 18:57:06 -04:00
void
2022-01-25 22:17:22 -05:00
Session : : notify_presentation_info_change ( PropertyChange const & what_changed )
2012-06-27 18:57:06 -04:00
{
2022-01-25 22:17:22 -05:00
if ( deletion_in_progress ( ) | | _route_reorder_in_progress ) {
2012-06-27 18:57:06 -04:00
return ;
2012-06-25 08:46:13 -04:00
}
2022-01-25 22:17:22 -05:00
if ( what_changed . contains ( Properties : : order ) ) {
PBD : : Unwinder < bool > uw ( _route_reorder_in_progress , true ) ;
ensure_stripable_sort_order ( ) ;
reassign_track_numbers ( ) ;
set_dirty ( ) ;
}
2012-01-17 20:30:44 -05:00
}
2012-06-13 12:46:59 -04:00
2021-02-08 08:28:39 -05:00
void
Session : : controllable_touched ( boost : : weak_ptr < PBD : : Controllable > c )
{
_recently_touched_controllable = c ;
}
boost : : shared_ptr < PBD : : Controllable >
Session : : recently_touched_controllable ( ) const
{
return _recently_touched_controllable . lock ( ) ;
}
2012-06-13 12:46:59 -04:00
bool
Session : : operation_in_progress ( GQuark op ) const
{
return ( find ( _current_trans_quarks . begin ( ) , _current_trans_quarks . end ( ) , op ) ! = _current_trans_quarks . end ( ) ) ;
}
2012-10-25 15:46:23 -04:00
void
Session : : reconnect_ltc_output ( )
{
2020-05-07 13:49:11 -04:00
if ( _ltc_output_port ) {
2015-05-25 20:56:03 -04:00
string src = Config - > get_ltc_output_port ( ) ;
2012-10-25 15:46:23 -04:00
2020-05-07 13:49:11 -04:00
_ltc_output_port - > disconnect_all ( ) ;
2012-10-25 15:46:23 -04:00
if ( src ! = _ ( " None " ) & & ! src . empty ( ) ) {
2020-05-07 13:49:11 -04:00
_ltc_output_port - > connect ( src ) ;
2012-10-25 15:46:23 -04:00
}
}
}
2015-01-16 12:17:09 -05:00
void
2020-10-15 01:09:02 -04:00
Session : : set_range_selection ( timepos_t const & start , timepos_t const & end )
2015-01-16 12:17:09 -05:00
{
2020-10-15 01:09:02 -04:00
_range_selection = Temporal : : Range ( start , end ) ;
2015-01-16 12:17:09 -05:00
}
void
2020-10-15 01:09:02 -04:00
Session : : set_object_selection ( timepos_t const & start , timepos_t const & end )
2015-01-16 12:17:09 -05:00
{
2020-10-15 01:09:02 -04:00
_object_selection = Temporal : : Range ( start , end ) ;
2015-01-16 12:17:09 -05:00
}
void
Session : : clear_range_selection ( )
{
2020-09-20 18:34:09 -04:00
_range_selection = Temporal : : Range ( timepos_t : : max ( Temporal : : AudioTime ) , timepos_t : : max ( Temporal : : AudioTime ) ) ;
2015-01-16 12:17:09 -05:00
}
void
Session : : clear_object_selection ( )
{
2020-09-20 18:34:09 -04:00
_object_selection = Temporal : : Range ( timepos_t : : max ( Temporal : : AudioTime ) , timepos_t : : max ( Temporal : : AudioTime ) ) ;
2015-01-16 12:17:09 -05:00
}
2016-04-23 16:11:48 -04:00
void
2020-07-18 17:27:11 -04:00
Session : : auto_connect_route ( boost : : shared_ptr < Route > route ,
bool connect_inputs ,
bool connect_outputs ,
2016-04-23 16:11:48 -04:00
const ChanCount & input_start ,
const ChanCount & output_start ,
const ChanCount & input_offset ,
const ChanCount & output_offset )
{
Glib : : Threads : : Mutex : : Lock lx ( _auto_connect_queue_lock ) ;
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto ,
2020-07-18 17:27:11 -04:00
string_compose ( " Session::auto_connect_route '%1' ci: %2 co: %3 is=(%4) os=(%5) io=(%6) oo=(%7) \n " ,
route - > name ( ) , connect_inputs , connect_outputs ,
2020-07-18 14:09:14 -04:00
input_start , output_start , input_offset , output_offset ) ) ;
2020-07-18 17:27:11 -04:00
_auto_connect_queue . push ( AutoConnectRequest ( route ,
connect_inputs , connect_outputs ,
2016-04-23 16:11:48 -04:00
input_start , output_start ,
input_offset , output_offset ) ) ;
2020-07-18 19:00:16 -04:00
lx . release ( ) ; // XXX check try-lock + pthread_cond_wait
2016-10-13 17:18:42 -04:00
auto_connect_thread_wakeup ( ) ;
}
void
Session : : auto_connect_thread_wakeup ( )
{
2016-04-23 16:11:48 -04:00
if ( pthread_mutex_trylock ( & _auto_connect_mutex ) = = 0 ) {
pthread_cond_signal ( & _auto_connect_cond ) ;
pthread_mutex_unlock ( & _auto_connect_mutex ) ;
}
}
2016-07-09 11:42:58 -04:00
void
Session : : queue_latency_recompute ( )
{
g_atomic_int_inc ( & _latency_recompute_pending ) ;
2016-10-13 17:18:42 -04:00
auto_connect_thread_wakeup ( ) ;
2016-07-09 11:42:58 -04:00
}
2016-04-23 16:11:48 -04:00
void
Session : : auto_connect ( const AutoConnectRequest & ar )
{
boost : : shared_ptr < Route > route = ar . route . lock ( ) ;
if ( ! route ) { return ; }
2021-11-03 17:06:16 -04:00
if ( loading ( ) ) {
2016-04-23 16:11:48 -04:00
return ;
}
/* If both inputs and outputs are auto-connected to physical ports,
* use the max of input and output offsets to ensure auto - connected
* port numbers always match up ( e . g . the first audio input and the
* first audio output of the route will have the same physical
* port number ) . Otherwise just use the lowest input or output
* offset possible .
*/
const bool in_out_physical =
( Config - > get_input_auto_connect ( ) & AutoConnectPhysical )
& & ( Config - > get_output_auto_connect ( ) & AutoConnectPhysical )
& & ar . connect_inputs ;
const ChanCount in_offset = in_out_physical
? ChanCount : : max ( ar . input_offset , ar . output_offset )
: ar . input_offset ;
const ChanCount out_offset = in_out_physical
? ChanCount : : max ( ar . input_offset , ar . output_offset )
: ar . output_offset ;
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto ,
string_compose ( " Session::auto_connect '%1' iop: %2 is=(%3) os=(%4) Eio=(%5) Eoo=(%6) \n " ,
route - > name ( ) , in_out_physical , ar . input_start , ar . output_start , in_offset , out_offset ) ) ;
2016-04-23 16:11:48 -04:00
for ( DataType : : iterator t = DataType : : begin ( ) ; t ! = DataType : : end ( ) ; + + t ) {
vector < string > physinputs ;
vector < string > physoutputs ;
2016-10-21 16:32:46 -04:00
/* for connecting track inputs we only want MIDI ports marked
* for " music " .
*/
get_physical_ports ( physinputs , physoutputs , * t , MidiPortMusic ) ;
2016-04-23 16:11:48 -04:00
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto ,
string_compose ( " Physical MidiPortMusic %1 Ports count in: %2 out %3 \n " ,
( * t ) . to_string ( ) , physinputs . size ( ) , physoutputs . size ( ) ) ) ;
2016-04-23 16:11:48 -04:00
if ( ! physinputs . empty ( ) & & ar . connect_inputs ) {
uint32_t nphysical_in = physinputs . size ( ) ;
for ( uint32_t i = ar . input_start . get ( * t ) ; i < route - > n_inputs ( ) . get ( * t ) & & i < nphysical_in ; + + i ) {
string port ;
if ( Config - > get_input_auto_connect ( ) & AutoConnectPhysical ) {
port = physinputs [ ( in_offset . get ( * t ) + i ) % nphysical_in ] ;
}
if ( ! port . empty ( ) & & route - > input ( ) - > connect ( route - > input ( ) - > ports ( ) . port ( * t , i ) , port , this ) ) {
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto , " Failed to auto-connect input. " ) ;
2016-04-23 16:11:48 -04:00
break ;
}
}
}
2020-07-18 17:27:11 -04:00
if ( ! physoutputs . empty ( ) & & ar . connect_outputs ) {
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto ,
string_compose ( " Connect %1 outputs # %2 .. %3 \n " ,
( * t ) . to_string ( ) , ar . output_start . get ( * t ) , route - > n_outputs ( ) . get ( * t ) ) ) ;
2016-04-23 16:11:48 -04:00
uint32_t nphysical_out = physoutputs . size ( ) ;
for ( uint32_t i = ar . output_start . get ( * t ) ; i < route - > n_outputs ( ) . get ( * t ) ; + + i ) {
string port ;
if ( ( * t ) = = DataType : : MIDI & & ( Config - > get_output_auto_connect ( ) & AutoConnectPhysical ) ) {
port = physoutputs [ ( out_offset . get ( * t ) + i ) % nphysical_out ] ;
} else if ( ( * t ) = = DataType : : AUDIO & & ( Config - > get_output_auto_connect ( ) & AutoConnectMaster ) ) {
/* master bus is audio only */
if ( _master_out & & _master_out - > n_inputs ( ) . get ( * t ) > 0 ) {
port = _master_out - > input ( ) - > ports ( ) . port ( * t ,
i % _master_out - > input ( ) - > n_ports ( ) . get ( * t ) ) - > name ( ) ;
}
}
if ( ! port . empty ( ) & & route - > output ( ) - > connect ( route - > output ( ) - > ports ( ) . port ( * t , i ) , port , this ) ) {
2020-07-18 14:09:14 -04:00
DEBUG_TRACE ( DEBUG : : PortConnectAuto , " Failed to auto-connect ouput. " ) ;
2016-04-23 16:11:48 -04:00
break ;
}
}
}
}
}
void
Session : : auto_connect_thread_start ( )
{
2016-12-19 10:33:54 -05:00
if ( g_atomic_int_get ( & _ac_thread_active ) ) {
2016-04-23 16:11:48 -04:00
return ;
}
2016-04-23 18:24:43 -04:00
2020-07-18 18:40:34 -04:00
Glib : : Threads : : Mutex : : Lock lx ( _auto_connect_queue_lock ) ;
2016-04-23 16:11:48 -04:00
while ( ! _auto_connect_queue . empty ( ) ) {
_auto_connect_queue . pop ( ) ;
}
2020-07-18 18:40:34 -04:00
lx . release ( ) ;
2016-04-23 16:11:48 -04:00
2016-12-19 10:33:54 -05:00
g_atomic_int_set ( & _ac_thread_active , 1 ) ;
2016-04-23 16:11:48 -04:00
if ( pthread_create ( & _auto_connect_thread , NULL , auto_connect_thread , this ) ) {
2016-12-19 10:33:54 -05:00
g_atomic_int_set ( & _ac_thread_active , 0 ) ;
2016-04-23 16:11:48 -04:00
}
}
void
Session : : auto_connect_thread_terminate ( )
{
2016-12-19 10:33:54 -05:00
if ( ! g_atomic_int_get ( & _ac_thread_active ) ) {
2016-04-23 16:11:48 -04:00
return ;
}
2016-04-23 18:24:43 -04:00
{
Glib : : Threads : : Mutex : : Lock lx ( _auto_connect_queue_lock ) ;
while ( ! _auto_connect_queue . empty ( ) ) {
_auto_connect_queue . pop ( ) ;
}
}
2016-12-19 09:35:29 -05:00
/* cannot use auto_connect_thread_wakeup() because that is allowed to
* fail to wakeup the thread .
*/
pthread_mutex_lock ( & _auto_connect_mutex ) ;
2016-12-19 10:33:54 -05:00
g_atomic_int_set ( & _ac_thread_active , 0 ) ;
2016-12-19 09:35:29 -05:00
pthread_cond_signal ( & _auto_connect_cond ) ;
pthread_mutex_unlock ( & _auto_connect_mutex ) ;
2016-04-23 16:11:48 -04:00
void * status ;
pthread_join ( _auto_connect_thread , & status ) ;
}
void *
Session : : auto_connect_thread ( void * arg )
{
Session * s = static_cast < Session * > ( arg ) ;
2020-03-29 08:56:22 -04:00
pthread_set_name ( X_ ( " autoconnect " ) ) ;
2016-04-23 16:11:48 -04:00
s - > auto_connect_thread_run ( ) ;
pthread_exit ( 0 ) ;
return 0 ;
}
void
Session : : auto_connect_thread_run ( )
{
2016-04-24 10:06:38 -04:00
SessionEvent : : create_per_thread_pool ( X_ ( " autoconnect " ) , 1024 ) ;
PBD : : notify_event_loops_about_thread_creation ( pthread_self ( ) , X_ ( " autoconnect " ) , 1024 ) ;
2016-04-23 16:11:48 -04:00
pthread_mutex_lock ( & _auto_connect_mutex ) ;
2020-07-18 19:00:16 -04:00
Glib : : Threads : : Mutex : : Lock lx ( _auto_connect_queue_lock ) ;
2016-12-19 10:33:54 -05:00
while ( g_atomic_int_get ( & _ac_thread_active ) ) {
2016-04-23 16:11:48 -04:00
2016-04-24 10:06:38 -04:00
if ( ! _auto_connect_queue . empty ( ) ) {
2020-04-26 14:25:31 -04:00
/* Why would we need the process lock?
*
* A : if ports are added while connections change ,
* the backend ' s iterator may be invalidated :
* graph_order_callback ( ) - > resort_routes ( ) - > direct_feeds_according_to_reality ( ) - > backend : : connected_to ( )
* Ardour : : IO uses the process - lock to avoid concurrency , too
*/
2016-04-24 10:06:38 -04:00
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
while ( ! _auto_connect_queue . empty ( ) ) {
const AutoConnectRequest ar ( _auto_connect_queue . front ( ) ) ;
_auto_connect_queue . pop ( ) ;
lx . release ( ) ;
auto_connect ( ar ) ;
lx . acquire ( ) ;
}
2016-04-23 16:11:48 -04:00
}
2020-07-18 18:40:34 -04:00
lx . release ( ) ;
2016-04-23 16:11:48 -04:00
2016-07-09 12:31:40 -04:00
if ( ! actively_recording ( ) ) { // might not be needed,
/* this is only used for updating plugin latencies, the
* graph does not change . so it ' s safe in general .
* BUT . .
2017-09-28 00:31:12 -04:00
* update_latency_compensation ( )
* calls DiskWriter : : set_capture_offset ( ) which
* modifies the capture - offset , which can be a problem .
2016-07-09 12:31:40 -04:00
*/
while ( g_atomic_int_and ( & _latency_recompute_pending , 0 ) ) {
2020-04-27 11:28:26 -04:00
update_latency_compensation ( false , false ) ;
2020-04-27 10:31:40 -04:00
if ( g_atomic_int_get ( & _latency_recompute_pending ) ) {
Glib : : usleep ( 1000 ) ;
}
2016-07-09 12:31:40 -04:00
}
2016-07-09 11:42:58 -04:00
}
2021-02-09 17:42:05 -05:00
if ( _midi_ports & & g_atomic_int_get ( & _update_pretty_names ) ) {
boost : : shared_ptr < Port > ap = boost : : dynamic_pointer_cast < Port > ( vkbd_output_port ( ) ) ;
if ( ap - > pretty_name ( ) ! = _ ( " Virtual Keyboard " ) ) {
ap - > set_pretty_name ( _ ( " Virtual Keyboard " ) ) ;
}
g_atomic_int_set ( & _update_pretty_names , 0 ) ;
}
2021-02-09 16:46:05 -05:00
if ( _engine . port_deletions_pending ( ) . read_space ( ) > 0 ) {
2016-12-12 16:47:16 -05:00
// this may call ARDOUR::Port::drop ... jack_port_unregister ()
// jack1 cannot cope with removing ports while processing
Glib : : Threads : : Mutex : : Lock lm ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2021-02-09 16:46:05 -05:00
_engine . clear_pending_port_deletions ( ) ;
2016-12-12 16:47:16 -05:00
}
2016-10-13 17:18:42 -04:00
2020-07-18 19:00:16 -04:00
lx . acquire ( ) ;
if ( _auto_connect_queue . empty ( ) ) {
lx . release ( ) ;
pthread_cond_wait ( & _auto_connect_cond , & _auto_connect_mutex ) ;
lx . acquire ( ) ;
}
2016-04-23 16:11:48 -04:00
}
2020-07-18 19:00:16 -04:00
lx . release ( ) ;
2016-04-23 16:11:48 -04:00
pthread_mutex_unlock ( & _auto_connect_mutex ) ;
}
2016-07-13 14:33:23 -04:00
void
Session : : cancel_all_solo ( )
{
StripableList sl ;
get_stripables ( sl ) ;
set_controls ( stripable_list_to_control_list ( sl , & Stripable : : solo_control ) , 0.0 , Controllable : : NoGroup ) ;
clear_all_solo_state ( routes . reader ( ) ) ;
2021-02-07 08:02:50 -05:00
_engine . monitor_port ( ) . clear_ports ( false ) ;
}
bool
Session : : listening ( ) const
{
if ( _listen_cnt > 0 ) {
return true ;
}
if ( _monitor_out & & _engine . monitor_port ( ) . monitoring ( ) ) {
return true ;
}
return false ;
2016-07-13 14:33:23 -04:00
}
2018-10-08 12:59:51 -04:00
void
Session : : maybe_update_tempo_from_midiclock_tempo ( float bpm )
{
2022-04-08 11:23:19 -04:00
TempoMap : : WritableSharedPtr tmap ( TempoMap : : write_copy ( ) ) ;
2020-11-27 14:40:58 -05:00
if ( tmap - > n_tempos ( ) = = 1 ) {
Temporal : : TempoMetric const & metric ( tmap - > metric_at ( 0 ) ) ;
2020-11-11 11:31:52 -05:00
if ( fabs ( metric . tempo ( ) . note_types_per_minute ( ) - bpm ) > ( 0.01 * metric . tempo ( ) . note_types_per_minute ( ) ) ) {
2021-03-26 23:15:10 -04:00
tmap - > change_tempo ( metric . get_editable_tempo ( ) , Tempo ( bpm , 4.0 , bpm ) ) ;
2022-04-08 11:23:19 -04:00
TempoMap : : update ( tmap ) ;
2018-10-08 12:59:51 -04:00
}
}
}
2020-02-29 01:33:33 -05:00
2020-05-29 21:45:46 -04:00
void
Session : : send_mclk_for_cycle ( samplepos_t start_sample , samplepos_t end_sample , pframes_t n_samples , samplecnt_t pre_roll )
{
midi_clock - > tick ( start_sample , end_sample , n_samples , pre_roll ) ;
}
2020-02-29 01:33:33 -05:00
void
Session : : set_had_destructive_tracks ( bool yn )
{
_had_destructive_tracks = yn ;
}
bool
Session : : had_destructive_tracks ( ) const
{
return _had_destructive_tracks ;
}
2022-01-04 20:03:28 -05:00